From 65fe6da9c2c70c666365a6c66d2728fe0de96d70 Mon Sep 17 00:00:00 2001 From: Kamal Ahmad Date: Wed, 28 Aug 2024 12:32:29 +0500 Subject: [PATCH] Index all boxes + add notes/byPubkey endpoint --- Cargo.lock | 1 + crates/chaincash_server/Cargo.toml | 1 + crates/chaincash_server/src/notes.rs | 18 +++++-- crates/chaincash_server/src/reserves.rs | 12 +++-- crates/chaincash_services/src/lib.rs | 19 ++++++- crates/chaincash_services/src/scanner.rs | 54 ++++--------------- .../down.sql | 3 ++ .../up.sql | 2 + crates/chaincash_store/src/notes.rs | 8 ++- crates/chaincash_store/src/reserves.rs | 7 ++- 10 files changed, 69 insertions(+), 56 deletions(-) create mode 100644 crates/chaincash_store/migrations/2024-08-28-071415_create_pubkey_indices/down.sql create mode 100644 crates/chaincash_store/migrations/2024-08-28-071415_create_pubkey_indices/up.sql diff --git a/Cargo.lock b/Cargo.lock index 6b81462..f994950 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -456,6 +456,7 @@ dependencies = [ "chaincash_predicate", "chaincash_services", "chaincash_store", + "ergo-lib", "ergo_client", "hyper 0.14.30", "serde", diff --git a/crates/chaincash_server/Cargo.toml b/crates/chaincash_server/Cargo.toml index 975c17e..2d77f61 100644 --- a/crates/chaincash_server/Cargo.toml +++ b/crates/chaincash_server/Cargo.toml @@ -15,6 +15,7 @@ hyper = { version = "0.14.27", features = ["full"] } tracing = { workspace = true } tokio = { workspace = true } thiserror = { workspace = true } +ergo-lib = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } ergo_client = { workspace = true } diff --git a/crates/chaincash_server/src/notes.rs b/crates/chaincash_server/src/notes.rs index 2724fa6..e11cec4 100644 --- a/crates/chaincash_server/src/notes.rs +++ b/crates/chaincash_server/src/notes.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use axum::extract::State; +use axum::extract::{Path, State}; use axum::response::{IntoResponse, Response}; use axum::routing::{get, post}; use axum::{Json, Router}; @@ -9,6 +9,7 @@ use chaincash_offchain::transactions::notes::{ }; use chaincash_services::transaction::SpendNoteRequest; use chaincash_services::ServerState; +use ergo_lib::ergo_chain_types::EcPoint; use serde_json::json; use crate::api::ApiError; @@ -40,15 +41,24 @@ async fn spend_note( Ok(response.into_response()) } -async fn list_notes(State(state): State>) -> Result { - let notes = state.store.notes().notes()?; +async fn list_wallet_notes(State(state): State>) -> Result { + let pubkeys = state.wallet_pubkeys().await?; + let notes = state.store.notes().notes_by_pubkeys(&pubkeys)?; let response = Json(notes); Ok(response.into_response()) } +async fn by_pubkey( + State(state): State>, + Path(pubkey): Path, +) -> Result { + Ok(Json(state.store.notes().notes_by_pubkeys(&[pubkey])?).into_response()) +} + pub fn router() -> Router> { Router::new() - .route("/", get(list_notes)) + .route("/wallet", get(list_wallet_notes)) + .route("/byPubkey/:pubkey", get(by_pubkey)) .route("/spend", post(spend_note)) .route("/mint", post(mint_note)) } diff --git a/crates/chaincash_server/src/reserves.rs b/crates/chaincash_server/src/reserves.rs index f4314c5..b9198a7 100644 --- a/crates/chaincash_server/src/reserves.rs +++ b/crates/chaincash_server/src/reserves.rs @@ -41,13 +41,19 @@ async fn top_up_reserve( Ok(response.into_response()) } -async fn list_reserves(State(state): State>) -> Result { - Ok(Json(state.store.reserves().reserve_boxes()?).into_response()) +async fn list_wallet_reserves(State(state): State>) -> Result { + Ok(Json( + state + .store + .reserves() + .reserve_boxes_by_pubkeys(&state.wallet_pubkeys().await?)?, + ) + .into_response()) } pub fn router() -> Router> { Router::new() .route("/mint", post(mint_reserve)) .route("/topup", post(top_up_reserve)) - .route("/", get(list_reserves)) + .route("/wallet", get(list_wallet_reserves)) } diff --git a/crates/chaincash_services/src/lib.rs b/crates/chaincash_services/src/lib.rs index a6a1bfc..86c3a83 100644 --- a/crates/chaincash_services/src/lib.rs +++ b/crates/chaincash_services/src/lib.rs @@ -2,7 +2,8 @@ use chaincash_predicate::predicates::Predicate; use chaincash_store::ChainCashStore; use compiler::Compiler; use ergo_client::node::NodeClient; -use transaction::TransactionService; +use ergo_lib::{ergo_chain_types::EcPoint, ergotree_ir::chain::address::Address}; +use transaction::{TransactionService, TransactionServiceError}; pub mod compiler; pub mod scanner; @@ -25,6 +26,22 @@ impl ServerState { predicates, } } + + pub async fn wallet_pubkeys(&self) -> Result, TransactionServiceError> { + Ok(self + .node + .endpoints() + .wallet()? + .get_addresses() + .await? + .into_iter() + .filter_map(|addr| match addr.address() { + Address::P2Pk(provedlog) => Some(*provedlog.h), + _ => None, + }) + .collect()) + } + pub fn tx_service(&self) -> TransactionService { TransactionService::new(&self.node, &self.store, &self.compiler) } diff --git a/crates/chaincash_services/src/scanner.rs b/crates/chaincash_services/src/scanner.rs index 3a26601..bd474b3 100644 --- a/crates/chaincash_services/src/scanner.rs +++ b/crates/chaincash_services/src/scanner.rs @@ -11,10 +11,8 @@ use ergo_client::node::{ }; use ergo_lib::{ chain::transaction::{ergo_transaction::ErgoTransaction, Transaction, TxId}, - ergo_chain_types::EcPoint, ergotree_ir::{ chain::{ - address::Address, ergo_box::{ErgoBox, NonMandatoryRegisterId, RegisterId}, token::TokenId, }, @@ -49,12 +47,8 @@ struct ContractScan<'a> { } impl<'a> ContractScan<'a> { - async fn new( - state: &ServerState, - scan_type: ScanType, - public_keys: &[EcPoint], - ) -> Result { - let (contract, register) = match scan_type { + async fn new(state: &ServerState, scan_type: ScanType) -> Result { + let (contract, _) = match scan_type { ScanType::Reserves => ( state.compiler.reserve_contract().await?, NonMandatoryRegisterId::R4, @@ -68,40 +62,22 @@ impl<'a> ContractScan<'a> { NonMandatoryRegisterId::R7, ), }; - let scan = Self::pubkey_scan( - format!("Chaincash {} scan", scan_type.to_str()), - contract, - register, - public_keys, - ); + let scan = Self::contract_scan(format!("Chaincash {} scan", scan_type.to_str()), contract); Ok(Self { scan_type, scan }) } - fn pubkey_scan( + fn contract_scan( scan_name: impl Into>, contract: &ErgoTree, - pubkey_register: NonMandatoryRegisterId, - public_keys: &[EcPoint], ) -> Scan<'a> { Scan { scan_name: scan_name.into(), wallet_interaction: "off".into(), tracking_rule: TrackingRule::And { - args: vec![ - TrackingRule::Contains { - register: Some(RegisterId::R1), - value: contract.sigma_serialize_bytes().unwrap().into(), - }, - TrackingRule::Or { - args: public_keys - .iter() - .map(|pubkey| TrackingRule::Equals { - register: Some(pubkey_register.into()), - value: pubkey.clone().into(), - }) - .collect(), - }, - ], + args: vec![TrackingRule::Contains { + register: Some(RegisterId::R1), + value: contract.sigma_serialize_bytes().unwrap().into(), + }], }, remove_offchain: true, } @@ -148,19 +124,7 @@ async fn load_scan( scan_type: ScanType, node_scans: &[RegisteredScan<'_>], ) -> Result<(bool, i32), ScannerError> { - let addresses = state - .node - .endpoints() - .wallet()? - .get_addresses() - .await? - .into_iter() - .filter_map(|addr| match addr.address() { - Address::P2Pk(pk) => Some((*pk.h).clone()), - _ => None, - }) - .collect::>(); - let contract_scan = ContractScan::new(state, scan_type, &addresses).await?; + let contract_scan = ContractScan::new(state, scan_type).await?; let scan = state.store.scans().scan_by_type(scan_type)?; if let Some(scan) = scan { if node_scans.iter().any(|node_scan| { diff --git a/crates/chaincash_store/migrations/2024-08-28-071415_create_pubkey_indices/down.sql b/crates/chaincash_store/migrations/2024-08-28-071415_create_pubkey_indices/down.sql new file mode 100644 index 0000000..77eace7 --- /dev/null +++ b/crates/chaincash_store/migrations/2024-08-28-071415_create_pubkey_indices/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` +DROP INDEX reserve_pubkey_idx; +DROP INDEX note_pubkey_idx; diff --git a/crates/chaincash_store/migrations/2024-08-28-071415_create_pubkey_indices/up.sql b/crates/chaincash_store/migrations/2024-08-28-071415_create_pubkey_indices/up.sql new file mode 100644 index 0000000..5aacd69 --- /dev/null +++ b/crates/chaincash_store/migrations/2024-08-28-071415_create_pubkey_indices/up.sql @@ -0,0 +1,2 @@ +CREATE INDEX reserve_pubkey_idx ON reserves(owner); +CREATE INDEX note_pubkey_idx ON notes(owner); diff --git a/crates/chaincash_store/src/notes.rs b/crates/chaincash_store/src/notes.rs index c40e33f..dbc2554 100644 --- a/crates/chaincash_store/src/notes.rs +++ b/crates/chaincash_store/src/notes.rs @@ -9,7 +9,10 @@ use diesel::{ BelongingToDsl, Connection, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl, Selectable, SelectableHelper, }; -use ergo_lib::ergotree_ir::chain::{self, ergo_box::BoxId, token::TokenId}; +use ergo_lib::{ + ergo_chain_types::EcPoint, + ergotree_ir::chain::{self, ergo_box::BoxId, token::TokenId}, +}; use serde::Serialize; use crate::{ @@ -170,9 +173,10 @@ impl NoteRepository { .transpose() } - pub fn notes(&self) -> Result, Error> { + pub fn notes_by_pubkeys(&self, pubkeys: &[EcPoint]) -> Result, Error> { let mut conn = self.pool.get()?; let notes = schema::notes::table + .filter(schema::notes::owner.eq_any(pubkeys.into_iter().cloned().map(String::from))) .select(Note::as_select()) .load(conn.borrow_mut())?; Ok(OwnershipEntry::belonging_to(¬es) diff --git a/crates/chaincash_store/src/reserves.rs b/crates/chaincash_store/src/reserves.rs index 6e7f75f..c7cd1ed 100644 --- a/crates/chaincash_store/src/reserves.rs +++ b/crates/chaincash_store/src/reserves.rs @@ -6,6 +6,7 @@ use crate::Error; use chaincash_offchain::boxes::ReserveBoxSpec; use diesel::dsl::delete; use diesel::prelude::*; +use ergo_lib::ergo_chain_types::EcPoint; use ergo_lib::ergotree_ir::chain; use ergo_lib::ergotree_ir::chain::ergo_box::BoxId; use ergo_lib::ergotree_ir::chain::token::TokenId; @@ -82,10 +83,14 @@ impl ReserveRepository { .expect("Failed to parse ReserveBoxSpec from database")) } - pub fn reserve_boxes(&self) -> Result, Error> { + pub fn reserve_boxes_by_pubkeys( + &self, + pubkeys: &[EcPoint], + ) -> Result, Error> { let mut conn = self.pool.get()?; let join = schema::reserves::table .inner_join(schema::ergo_boxes::table) + .filter(schema::reserves::owner.eq_any(pubkeys.into_iter().cloned().map(String::from))) .select((Reserve::as_select(), ErgoBox::as_select())) .load::<(Reserve, ErgoBox)>(&mut conn)?; // Panic here if parsing ReserveBox from database fails