Update to latest sdk_common
This commit is contained in:
parent
42355506bb
commit
b75217ae0f
@ -9,10 +9,8 @@ crate-type = ["lib", "cdylib"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
async-trait = "0.1"
|
||||
serde = { version = "1.0.188", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wasm-bindgen = "0.2.91"
|
||||
getrandom = { version="0.2.12", features = ["js"] }
|
||||
wasm-logger = "0.2.0"
|
||||
rand = "0.8.5"
|
||||
@ -21,6 +19,7 @@ sdk_common = { git = "https://git.4nkweb.com/4nk/sdk_common.git", branch = "dev"
|
||||
serde-wasm-bindgen = "0.6.5"
|
||||
futures-util = { version = "0.3.28", default-features = false, features = ["sink", "std"] }
|
||||
web-time = "1.1.0"
|
||||
wasm-bindgen-futures = "0.4.55"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3"
|
||||
|
||||
72
src/api.rs
72
src/api.rs
@ -17,7 +17,6 @@ use sdk_common::aes_gcm::aes::cipher::ArrayLength;
|
||||
use sdk_common::aes_gcm::Nonce;
|
||||
use sdk_common::hash::AnkPcdHash;
|
||||
use sdk_common::log::{self, debug, info, warn};
|
||||
use sdk_common::backend_blindbit_wasm::wasm_bindgen_futures;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use anyhow::Error as AnyhowError;
|
||||
@ -29,28 +28,28 @@ use sdk_common::crypto::{
|
||||
use sdk_common::process::{Process, ProcessState};
|
||||
use sdk_common::serialization::{OutPointMemberMap, OutPointProcessMap};
|
||||
use sdk_common::signature::{AnkHash, AnkMessageHash, AnkValidationNoHash, AnkValidationYesHash, Proof};
|
||||
use sdk_common::sp_client::bitcoin::blockdata::fee_rate;
|
||||
use sdk_common::sp_client::bitcoin::consensus::{deserialize, serialize};
|
||||
use sdk_common::sp_client::bitcoin::hashes::{sha256, sha256t, Hash};
|
||||
use sdk_common::sp_client::bitcoin::hashes::{FromSliceError, HashEngine};
|
||||
use sdk_common::sp_client::bitcoin::hex::{
|
||||
use sdk_common::spdk_core::bitcoin::blockdata::fee_rate;
|
||||
use sdk_common::spdk_core::bitcoin::consensus::{deserialize, serialize};
|
||||
use sdk_common::spdk_core::bitcoin::hashes::{sha256, sha256t, Hash};
|
||||
use sdk_common::spdk_core::bitcoin::hashes::{FromSliceError, HashEngine};
|
||||
use sdk_common::spdk_core::bitcoin::hex::{
|
||||
self, parse, DisplayHex, FromHex, HexToArrayError, HexToBytesError,
|
||||
};
|
||||
use sdk_common::sp_client::bitcoin::key::{Keypair, Parity, Secp256k1};
|
||||
use sdk_common::sp_client::bitcoin::network::ParseNetworkError;
|
||||
use sdk_common::sp_client::bitcoin::p2p::message::NetworkMessage;
|
||||
use sdk_common::sp_client::bitcoin::psbt::raw;
|
||||
use sdk_common::sp_client::bitcoin::secp256k1::ecdh::shared_secret_point;
|
||||
use sdk_common::sp_client::bitcoin::secp256k1::{PublicKey, Scalar, SecretKey};
|
||||
use sdk_common::sp_client::bitcoin::transaction::ParseOutPointError;
|
||||
use sdk_common::sp_client::bitcoin::{
|
||||
use sdk_common::spdk_core::bitcoin::key::{Keypair, Parity, Secp256k1};
|
||||
use sdk_common::spdk_core::bitcoin::network::ParseNetworkError;
|
||||
use sdk_common::spdk_core::bitcoin::p2p::message::NetworkMessage;
|
||||
use sdk_common::spdk_core::bitcoin::psbt::raw;
|
||||
use sdk_common::spdk_core::bitcoin::secp256k1::ecdh::shared_secret_point;
|
||||
use sdk_common::spdk_core::bitcoin::secp256k1::{PublicKey, Scalar, SecretKey};
|
||||
use sdk_common::spdk_core::bitcoin::transaction::ParseOutPointError;
|
||||
use sdk_common::spdk_core::bitcoin::{
|
||||
Amount, Network, OutPoint, Psbt, Transaction, Txid, XOnlyPublicKey,
|
||||
};
|
||||
use sdk_common::sp_client::constants::{
|
||||
use sdk_common::spdk_core::constants::{
|
||||
DUST_THRESHOLD, PSBT_SP_ADDRESS_KEY, PSBT_SP_PREFIX, PSBT_SP_SUBTYPE,
|
||||
};
|
||||
use sdk_common::sp_client::silentpayments::utils as sp_utils;
|
||||
use sdk_common::sp_client::silentpayments::{
|
||||
use sdk_common::spdk_core::silentpayments::utils as sp_utils;
|
||||
use sdk_common::spdk_core::silentpayments::{
|
||||
SilentPaymentAddress,
|
||||
Error as SpError,
|
||||
};
|
||||
@ -60,8 +59,8 @@ use serde_json::{json, Error as SerdeJsonError, Map, Value};
|
||||
|
||||
use serde::{de, Deserialize, Serialize};
|
||||
use tsify::{JsValueSerdeExt, Tsify};
|
||||
use wasm_bindgen::convert::{FromWasmAbi, VectorFromWasmAbi};
|
||||
use wasm_bindgen::prelude::*;
|
||||
use sdk_common::wasm_bindgen::convert::{FromWasmAbi, VectorFromWasmAbi};
|
||||
use sdk_common::wasm_bindgen::prelude::*;
|
||||
|
||||
use sdk_common::device::Device;
|
||||
use sdk_common::network::{
|
||||
@ -73,8 +72,9 @@ use sdk_common::pcd::{
|
||||
};
|
||||
use sdk_common::prd::{AnkPrdHash, Prd, PrdType};
|
||||
use sdk_common::silentpayments::{create_transaction as internal_create_transaction, sign_transaction as internal_sign_tx, SpWallet, TsUnsignedTransaction};
|
||||
use sdk_common::sp_client::{FeeRate, OutputSpendStatus, OwnedOutput, Recipient, RecipientAddress, SilentPaymentUnsignedTransaction, SpClient, SpendKey};
|
||||
use sdk_common::spdk_core::{FeeRate, OutputSpendStatus, OwnedOutput, Recipient, RecipientAddress, SilentPaymentUnsignedTransaction, SpClient, SpendKey};
|
||||
use sdk_common::secrets::SecretsStore;
|
||||
use sdk_common::wasm_bindgen;
|
||||
|
||||
use crate::user::{lock_local_device, set_new_device, LOCAL_DEVICE};
|
||||
use crate::wallet::{generate_sp_wallet, lock_freezed_utxos};
|
||||
@ -197,26 +197,26 @@ impl From<HexToArrayError> for ApiError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sdk_common::sp_client::bitcoin::psbt::PsbtParseError> for ApiError {
|
||||
fn from(value: sdk_common::sp_client::bitcoin::psbt::PsbtParseError) -> Self {
|
||||
impl From<sdk_common::spdk_core::bitcoin::psbt::PsbtParseError> for ApiError {
|
||||
fn from(value: sdk_common::spdk_core::bitcoin::psbt::PsbtParseError) -> Self {
|
||||
ApiError::new(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sdk_common::sp_client::bitcoin::psbt::ExtractTxError> for ApiError {
|
||||
fn from(value: sdk_common::sp_client::bitcoin::psbt::ExtractTxError) -> Self {
|
||||
impl From<sdk_common::spdk_core::bitcoin::psbt::ExtractTxError> for ApiError {
|
||||
fn from(value: sdk_common::spdk_core::bitcoin::psbt::ExtractTxError) -> Self {
|
||||
ApiError::new(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sdk_common::sp_client::bitcoin::secp256k1::Error> for ApiError {
|
||||
fn from(value: sdk_common::sp_client::bitcoin::secp256k1::Error) -> Self {
|
||||
impl From<sdk_common::spdk_core::bitcoin::secp256k1::Error> for ApiError {
|
||||
fn from(value: sdk_common::spdk_core::bitcoin::secp256k1::Error) -> Self {
|
||||
ApiError::new(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sdk_common::sp_client::bitcoin::consensus::encode::Error> for ApiError {
|
||||
fn from(value: sdk_common::sp_client::bitcoin::consensus::encode::Error) -> Self {
|
||||
impl From<sdk_common::spdk_core::bitcoin::consensus::encode::Error> for ApiError {
|
||||
fn from(value: sdk_common::spdk_core::bitcoin::consensus::encode::Error) -> Self {
|
||||
ApiError::new(value.to_string())
|
||||
}
|
||||
}
|
||||
@ -828,7 +828,7 @@ fn handle_prd(
|
||||
members_list: &OutPointMemberMap,
|
||||
processes: &OutPointProcessMap
|
||||
) -> AnyhowResult<ApiReturn> {
|
||||
debug!("handle_prd: {:#?}", prd);
|
||||
// debug!("handle_prd: {:#?}", prd);
|
||||
// Connect is a bit different here because there's no associated process
|
||||
// Let's handle that case separately
|
||||
if prd.prd_type == PrdType::Connect {
|
||||
@ -847,7 +847,9 @@ fn handle_prd(
|
||||
PrdType::Update => {
|
||||
// Compute the merkle tree root for the proposed new state to see if we already know about it
|
||||
let update_merkle_root = prd.pcd_commitments.create_merkle_tree()?.root().ok_or(AnyhowError::msg("Invalid merkle tree"))?;
|
||||
let updated_state: &ProcessState = if let Ok(existing_state) = relevant_process.get_state_for_id_mut(&update_merkle_root) {
|
||||
|
||||
// First, handle the mutable borrow separately
|
||||
if let Ok(existing_state) = relevant_process.get_state_for_id_mut(&update_merkle_root) {
|
||||
// We already know about that state, if we also have the keys we can just stop here
|
||||
if !existing_state.keys.is_empty() {
|
||||
// We check that the keys are the same, just in case
|
||||
@ -867,7 +869,7 @@ fn handle_prd(
|
||||
// We don't have any keys for this state, we can just update it
|
||||
existing_state.keys = prd.keys.clone();
|
||||
}
|
||||
existing_state
|
||||
// Mutable borrow ends here
|
||||
} else {
|
||||
let commited_in = relevant_process.get_process_tip()?;
|
||||
|
||||
@ -882,9 +884,10 @@ fn handle_prd(
|
||||
};
|
||||
|
||||
relevant_process.insert_concurrent_state(new_state)?;
|
||||
}
|
||||
|
||||
relevant_process.get_state_for_id(&new_state.state_id).expect("New state should be inserted")
|
||||
};
|
||||
// Now we can get an immutable reference to the state
|
||||
let updated_state = relevant_process.get_state_for_id(&update_merkle_root).expect("State should exist");
|
||||
|
||||
// Compute the diffs
|
||||
let diffs = create_diffs(&lock_local_device()?, &relevant_process, &updated_state, members_list)?;
|
||||
@ -1039,6 +1042,7 @@ fn handle_prd(
|
||||
value_commitment: hash.to_lower_hex_string(),
|
||||
field: field.to_owned(),
|
||||
storages: relevant_fields.get(field.as_str()).unwrap().clone(),
|
||||
roles: state.roles.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
diffs.push(diff);
|
||||
@ -1445,7 +1449,7 @@ pub fn request_data(process_id: String, state_ids_str: Vec<String>, roles: JsVal
|
||||
let cipher = encrypt_with_key(secret.as_byte_array(), prd_msg.as_bytes())?;
|
||||
ciphers.push(cipher.to_lower_hex_string());
|
||||
} else {
|
||||
debug!("No shared secret");
|
||||
debug!("No shared secret for {}", address.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
156
src/scanner.rs
156
src/scanner.rs
@ -1,23 +1,24 @@
|
||||
use std::{collections::{HashMap, HashSet}, sync::atomic::AtomicBool};
|
||||
use std::{collections::{HashMap, HashSet}, sync::atomic::AtomicBool, pin::Pin};
|
||||
use web_time::{Duration, Instant};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use futures_util::Stream;
|
||||
use sdk_common::{backend_blindbit_wasm::{ChainBackend, SpScanner}, log::{self, info}, sp_client::{bitcoin::{absolute::Height, bip158::BlockFilter, hashes::{sha256, Hash}, secp256k1::PublicKey, Amount, BlockHash, OutPoint}, BlockData, FilterData, OutputSpendStatus, OwnedOutput, SpClient, Updater}};
|
||||
use sdk_common::{backend_blindbit_wasm::{async_trait, BlindbitBackend, HttpClient}, log::{self, info}, spdk_core::{BlockData, FilterData, OutputSpendStatus, OwnedOutput, SpClient, bitcoin::{Amount, BlockHash, OutPoint, absolute::Height, bip158::BlockFilter, hashes::{Hash, sha256}, secp256k1::PublicKey}, Updater}};
|
||||
use sdk_common::updates::StateUpdater;
|
||||
|
||||
pub struct WasmSpScanner<'a> {
|
||||
updater: Box<dyn Updater + Sync>,
|
||||
backend: Box<dyn ChainBackend + Sync>,
|
||||
pub struct WasmSpScanner<'a, H: HttpClient> {
|
||||
updater: StateUpdater,
|
||||
backend: BlindbitBackend<H>,
|
||||
client: SpClient,
|
||||
keep_scanning: &'a AtomicBool, // used to interrupt scanning
|
||||
owned_outpoints: HashSet<OutPoint>, // used to scan block inputs
|
||||
}
|
||||
|
||||
impl<'a> WasmSpScanner<'a> {
|
||||
impl<'a, H: HttpClient + Clone + 'static> WasmSpScanner<'a, H> {
|
||||
pub fn new(
|
||||
client: SpClient,
|
||||
updater: Box<dyn Updater + Sync>,
|
||||
backend: Box<dyn ChainBackend + Sync>,
|
||||
updater: StateUpdater,
|
||||
backend: BlindbitBackend<H>,
|
||||
owned_outpoints: HashSet<OutPoint>,
|
||||
keep_scanning: &'a AtomicBool,
|
||||
) -> Self {
|
||||
@ -30,21 +31,21 @@ impl<'a> WasmSpScanner<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// Removed trait implementation, keeping methods as standalone async methods
|
||||
pub async fn process_blocks(
|
||||
&mut self,
|
||||
start: Height,
|
||||
end: Height,
|
||||
block_data_stream: impl Stream<Item = Result<BlockData>> + Unpin ,
|
||||
mut block_data_stream: Pin<Box<dyn Stream<Item = Result<BlockData>>>>,
|
||||
) -> Result<()> {
|
||||
use futures_util::StreamExt;
|
||||
|
||||
let mut update_time = Instant::now();
|
||||
let mut stream = block_data_stream;
|
||||
|
||||
let save_interval = 10;
|
||||
let mut blocks_scanned = 1;
|
||||
|
||||
while let Some(blockdata) = stream.next().await {
|
||||
while let Some(blockdata) = block_data_stream.next().await {
|
||||
let blockdata = blockdata?;
|
||||
let blkheight = blockdata.blkheight;
|
||||
let blkhash = blockdata.blkhash;
|
||||
@ -87,11 +88,8 @@ impl<'a> WasmSpScanner<'a> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl<'a> SpScanner for WasmSpScanner<'a> {
|
||||
async fn scan_blocks(
|
||||
pub async fn scan_blocks(
|
||||
&mut self,
|
||||
start: Height,
|
||||
end: Height,
|
||||
@ -121,7 +119,7 @@ impl<'a> SpScanner for WasmSpScanner<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn process_block(
|
||||
pub async fn process_block(
|
||||
&mut self,
|
||||
blockdata: BlockData,
|
||||
) -> Result<(HashMap<OutPoint, OwnedOutput>, HashSet<OutPoint>)> {
|
||||
@ -148,7 +146,7 @@ impl<'a> SpScanner for WasmSpScanner<'a> {
|
||||
Ok((outs, ins))
|
||||
}
|
||||
|
||||
async fn process_block_outputs(
|
||||
pub async fn process_block_outputs(
|
||||
&self,
|
||||
blkheight: Height,
|
||||
tweaks: Vec<PublicKey>,
|
||||
@ -197,7 +195,7 @@ impl<'a> SpScanner for WasmSpScanner<'a> {
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
async fn process_block_inputs(
|
||||
pub async fn process_block_inputs(
|
||||
&self,
|
||||
blkheight: Height,
|
||||
spent_filter: FilterData,
|
||||
@ -207,7 +205,7 @@ impl<'a> SpScanner for WasmSpScanner<'a> {
|
||||
let blkhash = spent_filter.block_hash;
|
||||
|
||||
// first get the 8-byte hashes used to construct the input filter
|
||||
let input_hashes_map = self.get_input_hashes(blkhash)?;
|
||||
let input_hashes_map = self.get_input_hashes(blkhash).await?;
|
||||
|
||||
// check against filter
|
||||
let blkfilter = BlockFilter::new(&spent_filter.data);
|
||||
@ -219,8 +217,9 @@ impl<'a> SpScanner for WasmSpScanner<'a> {
|
||||
|
||||
// if match: download spent data, collect the outpoints that are spent
|
||||
if matched_inputs {
|
||||
use sdk_common::spdk_core::ChainBackend;
|
||||
info!("matched inputs on: {}", blkheight);
|
||||
let spent = self.backend.spent_index(blkheight).await?.data;
|
||||
let spent = self.backend.spent_index(blkheight)?.data;
|
||||
|
||||
for spent in spent {
|
||||
let hex: &[u8] = spent.as_ref();
|
||||
@ -238,9 +237,12 @@ impl<'a> SpScanner for WasmSpScanner<'a> {
|
||||
range: std::ops::RangeInclusive<u32>,
|
||||
dust_limit: Amount,
|
||||
with_cutthrough: bool,
|
||||
) -> std::pin::Pin<Box<dyn Stream<Item = Result<BlockData>>>> {
|
||||
self.backend
|
||||
.get_block_data_for_range(range, dust_limit, with_cutthrough)
|
||||
) -> Pin<Box<dyn Stream<Item = Result<BlockData>>>> {
|
||||
use futures_util::stream;
|
||||
use sdk_common::spdk_core::ChainBackend;
|
||||
|
||||
let iter = self.backend.get_block_data_for_range(range, dust_limit, with_cutthrough);
|
||||
Box::pin(stream::iter(iter))
|
||||
}
|
||||
|
||||
fn should_interrupt(&self) -> bool {
|
||||
@ -250,7 +252,9 @@ impl<'a> SpScanner for WasmSpScanner<'a> {
|
||||
}
|
||||
|
||||
fn save_state(&mut self) -> Result<()> {
|
||||
self.updater.save_to_persistent_storage()
|
||||
// StateUpdater doesn't have persistent storage, it sends updates via sink
|
||||
// The actual saving is handled by the update sink
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn record_outputs(
|
||||
@ -259,8 +263,7 @@ impl<'a> SpScanner for WasmSpScanner<'a> {
|
||||
block_hash: BlockHash,
|
||||
outputs: HashMap<OutPoint, OwnedOutput>,
|
||||
) -> Result<()> {
|
||||
self.updater
|
||||
.record_block_outputs(height, block_hash, outputs)
|
||||
self.updater.record_block_outputs(height, block_hash, outputs)
|
||||
}
|
||||
|
||||
fn record_inputs(
|
||||
@ -280,16 +283,107 @@ impl<'a> SpScanner for WasmSpScanner<'a> {
|
||||
&self.client
|
||||
}
|
||||
|
||||
fn backend(&self) -> &dyn ChainBackend {
|
||||
self.backend.as_ref()
|
||||
// Helper methods from SpScanner trait
|
||||
fn check_block_outputs(
|
||||
created_utxo_filter: BlockFilter,
|
||||
blkhash: BlockHash,
|
||||
candidate_spks: Vec<&[u8; 34]>,
|
||||
) -> Result<bool> {
|
||||
// check output scripts
|
||||
let output_keys: Vec<_> = candidate_spks
|
||||
.into_iter()
|
||||
.map(|spk| spk[2..].as_ref())
|
||||
.collect();
|
||||
|
||||
// note: match will always return true for an empty query!
|
||||
if !output_keys.is_empty() {
|
||||
Ok(created_utxo_filter.match_any(&blkhash, &mut output_keys.into_iter())?)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
fn updater(&mut self) -> &mut dyn Updater {
|
||||
self.updater.as_mut()
|
||||
fn check_block_inputs(
|
||||
&self,
|
||||
spent_filter: BlockFilter,
|
||||
blkhash: BlockHash,
|
||||
input_hashes: Vec<[u8; 8]>,
|
||||
) -> Result<bool> {
|
||||
// note: match will always return true for an empty query!
|
||||
if !input_hashes.is_empty() {
|
||||
Ok(spent_filter.match_any(&blkhash, &mut input_hashes.into_iter())?)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
async fn scan_utxos(
|
||||
&self,
|
||||
blkheight: Height,
|
||||
secrets_map: HashMap<[u8; 34], PublicKey>,
|
||||
) -> Result<Vec<(Option<sdk_common::spdk_core::silentpayments::receiving::Label>, sdk_common::spdk_core::UtxoData, sdk_common::spdk_core::bitcoin::secp256k1::Scalar)>> {
|
||||
use sdk_common::spdk_core::{ChainBackend, UtxoData, bitcoin::{Txid, secp256k1::{Scalar, PublicKey as Secp256k1PublicKey}, XOnlyPublicKey}};
|
||||
|
||||
let utxos = self.backend.utxos(blkheight)?;
|
||||
|
||||
// Group utxos by the txid
|
||||
let mut txmap: std::collections::HashMap<Txid, Vec<UtxoData>> = std::collections::HashMap::new();
|
||||
for utxo in utxos {
|
||||
txmap.entry(utxo.txid).or_default().push(utxo);
|
||||
}
|
||||
|
||||
// Scan transactions for owned outputs
|
||||
let mut res = Vec::new();
|
||||
for (_txid, utxos_in_tx) in txmap {
|
||||
// check if we know the secret to any of the spks
|
||||
let secret = utxos_in_tx.iter().find_map(|utxo| {
|
||||
let spk = utxo.scriptpubkey.as_bytes();
|
||||
if spk.len() == 34 {
|
||||
let mut key = [0u8; 34];
|
||||
key.copy_from_slice(spk);
|
||||
secrets_map.get(&key).copied()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(tweak) = secret {
|
||||
let output_keys: Vec<XOnlyPublicKey> = utxos_in_tx
|
||||
.iter()
|
||||
.filter_map(|x| {
|
||||
if x.scriptpubkey.is_p2tr() {
|
||||
XOnlyPublicKey::from_slice(&x.scriptpubkey.as_bytes()[2..]).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// scan this transaction for our output(s)
|
||||
if let Ok(found) = self.client.sp_receiver.scan_transaction(&tweak, output_keys) {
|
||||
for (label, outputs_map) in found {
|
||||
for (output_pubkey, shared_secret_tweak) in outputs_map {
|
||||
let matching_utxo = utxos_in_tx.iter().find(|utxo| {
|
||||
if let Ok(key) = XOnlyPublicKey::from_slice(&utxo.scriptpubkey.as_bytes()[2..]) {
|
||||
key == output_pubkey
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(utxo) = matching_utxo {
|
||||
res.push((label.clone(), utxo.clone(), shared_secret_tweak));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
// Override the default get_input_hashes implementation to use owned_outpoints
|
||||
fn get_input_hashes(&self, blkhash: BlockHash) -> Result<HashMap<[u8; 8], OutPoint>> {
|
||||
async fn get_input_hashes(&self, blkhash: BlockHash) -> Result<HashMap<[u8; 8], OutPoint>> {
|
||||
let mut map: HashMap<[u8; 8], OutPoint> = HashMap::new();
|
||||
|
||||
for outpoint in &self.owned_outpoints {
|
||||
|
||||
24
src/user.rs
24
src/user.rs
@ -1,19 +1,19 @@
|
||||
use anyhow::{Error, Result};
|
||||
use rand::{self, thread_rng, Rng, RngCore};
|
||||
use sdk_common::sp_client::bitcoin::consensus::{deserialize, serialize};
|
||||
use sdk_common::sp_client::bitcoin::hashes::{Hash, HashEngine};
|
||||
use sdk_common::sp_client::bitcoin::hex::{DisplayHex, FromHex};
|
||||
use sdk_common::sp_client::bitcoin::key::{Parity, Secp256k1};
|
||||
use sdk_common::sp_client::bitcoin::secp256k1::{PublicKey, SecretKey, ThirtyTwoByteHash};
|
||||
use sdk_common::sp_client::bitcoin::{
|
||||
use sdk_common::spdk_core::bitcoin::consensus::{deserialize, serialize};
|
||||
use sdk_common::spdk_core::bitcoin::hashes::{Hash, HashEngine};
|
||||
use sdk_common::spdk_core::bitcoin::hex::{DisplayHex, FromHex};
|
||||
use sdk_common::spdk_core::bitcoin::key::{Parity, Secp256k1};
|
||||
use sdk_common::spdk_core::bitcoin::secp256k1::{PublicKey, SecretKey, ThirtyTwoByteHash};
|
||||
use sdk_common::spdk_core::bitcoin::{
|
||||
Network, OutPoint, ScriptBuf, Transaction, Txid, XOnlyPublicKey,
|
||||
};
|
||||
use sdk_common::sp_client::SpClient;
|
||||
use sdk_common::spdk_core::SpClient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use tsify::Tsify;
|
||||
use wasm_bindgen::convert::VectorFromWasmAbi;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use sdk_common::wasm_bindgen::convert::VectorFromWasmAbi;
|
||||
use sdk_common::wasm_bindgen::prelude::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
@ -22,9 +22,9 @@ use std::str::FromStr;
|
||||
use std::sync::{Mutex, MutexGuard, OnceLock};
|
||||
|
||||
use sdk_common::device::Device;
|
||||
use sdk_common::sp_client::bitcoin::secp256k1::constants::SECRET_KEY_SIZE;
|
||||
use sdk_common::sp_client::silentpayments::bitcoin_hashes::sha256;
|
||||
use sdk_common::sp_client::silentpayments::{
|
||||
use sdk_common::spdk_core::bitcoin::secp256k1::constants::SECRET_KEY_SIZE;
|
||||
use sdk_common::spdk_core::silentpayments::bitcoin_hashes::sha256;
|
||||
use sdk_common::spdk_core::silentpayments::{
|
||||
Network as SpNetwork,
|
||||
SilentPaymentAddress
|
||||
};
|
||||
|
||||
@ -6,11 +6,17 @@ use web_time::Instant;
|
||||
|
||||
use anyhow::Error;
|
||||
use rand::Rng;
|
||||
use sdk_common::{backend_blindbit_wasm::{BlindbitBackend, SpScanner}, log, silentpayments::SpWallet, sp_client::{
|
||||
bitcoin::{absolute::Height, secp256k1::SecretKey, Amount, Network, OutPoint},
|
||||
silentpayments::SilentPaymentAddress,
|
||||
SpClient, SpendKey,
|
||||
}, updates::StateUpdater};
|
||||
use sdk_common::{
|
||||
backend_blindbit_wasm::{BlindbitBackend, ReqwestClient},
|
||||
log,
|
||||
silentpayments::SpWallet,
|
||||
spdk_core::{
|
||||
bitcoin::{absolute::Height, secp256k1::SecretKey, Amount, Network, OutPoint},
|
||||
silentpayments::SilentPaymentAddress,
|
||||
SpClient, SpendKey,
|
||||
},
|
||||
updates::StateUpdater
|
||||
};
|
||||
|
||||
use crate::{scanner::WasmSpScanner, MutexExt, WITH_CUTTHROUGH};
|
||||
|
||||
@ -57,15 +63,16 @@ pub async fn scan_blocks(
|
||||
}
|
||||
|
||||
let updater = StateUpdater::new();
|
||||
let backend = BlindbitBackend::new(blindbit_url.to_string())?;
|
||||
let http_client = ReqwestClient::new();
|
||||
let backend = BlindbitBackend::new(blindbit_url.to_string(), http_client)?;
|
||||
|
||||
let keep_scanning = Arc::new(AtomicBool::new(true));
|
||||
|
||||
let start_time = Instant::now();
|
||||
let mut scanner = WasmSpScanner::new(
|
||||
sp_client,
|
||||
Box::new(updater),
|
||||
Box::new(backend),
|
||||
updater,
|
||||
backend,
|
||||
owned_outpoints,
|
||||
&keep_scanning,
|
||||
);
|
||||
|
||||
@ -5,8 +5,8 @@ use sdk_client::api::{
|
||||
};
|
||||
use sdk_common::log::debug;
|
||||
use sdk_common::secrets::SecretsStore;
|
||||
use sdk_common::sp_client::bitcoin::OutPoint;
|
||||
use sdk_common::sp_client::OwnedOutput;
|
||||
use sdk_common::spdk_core::bitcoin::OutPoint;
|
||||
use sdk_common::spdk_core::OwnedOutput;
|
||||
|
||||
use tsify::JsValueSerdeExt;
|
||||
#[allow(dead_code)]
|
||||
|
||||
163
tests/merkle_proof.rs
Normal file
163
tests/merkle_proof.rs
Normal file
@ -0,0 +1,163 @@
|
||||
use wasm_bindgen_test::*;
|
||||
use sdk_client::api::*;
|
||||
use sdk_common::pcd::{Pcd, PcdCommitments, Roles};
|
||||
use sdk_common::spdk_core::bitcoin::{Network, OutPoint};
|
||||
use std::collections::BTreeMap;
|
||||
use serde_json::json;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_get_merkle_proof() {
|
||||
// This is a basic test to ensure the function compiles and can be called
|
||||
// We'll create a mock process state object and test the function
|
||||
|
||||
// Create a mock process state object (this will fail deserialization which is expected)
|
||||
let mock_process_state = JsValue::from_str("invalid_process_state");
|
||||
|
||||
let result = get_merkle_proof(
|
||||
mock_process_state,
|
||||
"test_attribute".to_string()
|
||||
);
|
||||
|
||||
// This should fail with a deserialization error, which is expected
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_get_merkle_proof_with_valid_state() {
|
||||
// This test would create a valid ProcessState and test the actual merkle proof generation
|
||||
// For now, we'll just test that the function exists and can be called with valid parameters
|
||||
|
||||
// Create a minimal valid process state structure
|
||||
let process_state_json = json!({
|
||||
"state_id": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"pcd_commitment": {
|
||||
"test_attribute": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
"roles": {},
|
||||
"public_data": {},
|
||||
"keys": {},
|
||||
"validation_tokens": [],
|
||||
"commited_in": "0000000000000000000000000000000000000000000000000000000000000000:0"
|
||||
});
|
||||
|
||||
let process_state = serde_wasm_bindgen::to_value(&process_state_json).unwrap();
|
||||
|
||||
let result = get_merkle_proof(
|
||||
process_state,
|
||||
"test_attribute".to_string()
|
||||
);
|
||||
|
||||
// This should work if the ProcessState structure is correct
|
||||
// For now, we just test that the function can be called
|
||||
assert!(result.is_ok() || result.is_err()); // Either outcome is acceptable for this test
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_get_merkle_proof_invalid_state_id() {
|
||||
// Create a mock process state object
|
||||
let mock_process_state = JsValue::from_str("invalid_process_state");
|
||||
|
||||
// Test with invalid process state (should fail deserialization)
|
||||
let result = get_merkle_proof(
|
||||
mock_process_state,
|
||||
"test_attribute".to_string()
|
||||
);
|
||||
|
||||
assert!(result.is_err());
|
||||
let error = result.unwrap_err();
|
||||
assert_eq!(error.message, "Failed to deserialize process state");
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_get_merkle_proof_with_real_process() {
|
||||
// Reset device to start with a clean state
|
||||
reset_device().unwrap();
|
||||
|
||||
// Create a new device
|
||||
create_new_device(0, "signet".to_string()).unwrap();
|
||||
|
||||
// Create some test data
|
||||
let test_data = json!({
|
||||
"name": "test_name",
|
||||
"value": "test_value",
|
||||
"number": 42
|
||||
});
|
||||
|
||||
// Encode the data as PCD
|
||||
let pcd = encode_json(JsValue::from_serde(&test_data).unwrap()).unwrap();
|
||||
|
||||
// Create roles
|
||||
let roles = Roles::new(BTreeMap::new());
|
||||
|
||||
// Create a mock relay address (this would normally be a real address)
|
||||
let relay_address = "sprt1qqfmqt0ngq99y8t4ke6uhtm2a2vc2zxvhj7hjrqu599kn30d4cs9rwqn6n079mdr4dfqg72yrtvuxf43yswscw86nvvl09mc5ljx65vfh75fkza35".to_string();
|
||||
|
||||
// Create an empty members list
|
||||
let members_list = OutPointMemberMap::new();
|
||||
|
||||
// Create a new process
|
||||
let result = create_new_process(
|
||||
pcd.clone(),
|
||||
roles.clone(),
|
||||
pcd.clone(),
|
||||
relay_address,
|
||||
1, // fee_rate
|
||||
members_list
|
||||
);
|
||||
|
||||
// The process creation might fail due to missing relay or other dependencies,
|
||||
// but we can still test the merkle proof function structure
|
||||
if let Ok(api_return) = result {
|
||||
if let Some(updated_process) = api_return.updated_process {
|
||||
let process = updated_process.current_process;
|
||||
let state = process.get_latest_commited_state().unwrap();
|
||||
|
||||
// Convert the process state to JsValue for the function call
|
||||
let state_js = serde_wasm_bindgen::to_value(&state).unwrap();
|
||||
|
||||
// Now test the merkle proof generation
|
||||
let proof_result = get_merkle_proof(
|
||||
state_js,
|
||||
"name".to_string()
|
||||
);
|
||||
|
||||
// The proof should be generated successfully
|
||||
assert!(proof_result.is_ok());
|
||||
|
||||
let proof = proof_result.unwrap();
|
||||
|
||||
// Check that the proof struct has the expected fields
|
||||
assert!(!proof.proof.is_empty());
|
||||
assert!(!proof.root.is_empty());
|
||||
assert_eq!(proof.attribute, "name");
|
||||
assert!(proof.attribute_index >= 0);
|
||||
assert!(proof.total_leaves_count > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_validate_merkle_proof() {
|
||||
// Create a mock MerkleProofResult
|
||||
let mock_proof_result = MerkleProofResult {
|
||||
proof: "0000000000000000000000000000000000000000000000000000000000000000".to_string(),
|
||||
root: "0000000000000000000000000000000000000000000000000000000000000000".to_string(),
|
||||
attribute: "test_attribute".to_string(),
|
||||
attribute_index: 0,
|
||||
total_leaves_count: 1,
|
||||
};
|
||||
|
||||
// Create mock hash data
|
||||
let mock_hash = "0000000000000000000000000000000000000000000000000000000000000000".to_string();
|
||||
|
||||
// Test the function (this will likely fail validation, which is expected)
|
||||
let result = validate_merkle_proof(mock_proof_result, mock_hash);
|
||||
|
||||
// The function should return a boolean result
|
||||
assert!(result.is_ok());
|
||||
let is_valid = result.unwrap();
|
||||
// For now, we expect it to be false since we're using dummy data
|
||||
assert!(!is_valid);
|
||||
}
|
||||
43
tests/scanner.rs
Normal file
43
tests/scanner.rs
Normal file
@ -0,0 +1,43 @@
|
||||
use wasm_bindgen_test::*;
|
||||
use sdk_client::scan::WasmScanner;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
async fn test_scanner_creation() {
|
||||
let scanner = WasmScanner::new();
|
||||
assert!(!scanner.is_scanning());
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
async fn test_scanner_basic_scan() {
|
||||
let scanner = WasmScanner::new();
|
||||
|
||||
// Create an empty processes array
|
||||
let processes = serde_wasm_bindgen::to_value(&Vec::<sdk_common::process::Process>::new()).unwrap();
|
||||
|
||||
let result = scanner.scan_blocks(10, processes).await;
|
||||
assert!(result.is_ok());
|
||||
|
||||
let scan_result = result.unwrap();
|
||||
let scanned_blocks = js_sys::Reflect::get(&scan_result, &"scannedBlocks".into()).unwrap();
|
||||
let scanned_blocks: u32 = scanned_blocks.as_f64().unwrap() as u32;
|
||||
|
||||
assert_eq!(scanned_blocks, 10);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
async fn test_scanner_stop_functionality() {
|
||||
let scanner = WasmScanner::new();
|
||||
|
||||
// Start scanning
|
||||
let processes = serde_wasm_bindgen::to_value(&Vec::<sdk_common::process::Process>::new()).unwrap();
|
||||
let scan_future = scanner.scan_blocks(100, processes);
|
||||
|
||||
// Stop scanning immediately
|
||||
scanner.stop_scanning();
|
||||
|
||||
// The scan should complete quickly since we stopped it
|
||||
let result = scan_future.await;
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
@ -3,14 +3,14 @@ use std::collections::HashMap;
|
||||
|
||||
use sdk_client::api::{parse_new_tx, ApiReturn};
|
||||
use sdk_common::network::NewTxMessage;
|
||||
use sdk_common::sp_client::bitcoin::consensus::{deserialize, serialize};
|
||||
use sdk_common::sp_client::bitcoin::hex::{DisplayHex, FromHex};
|
||||
use sdk_common::sp_client::bitcoin::secp256k1::PublicKey;
|
||||
use sdk_common::sp_client::bitcoin::{OutPoint, Transaction};
|
||||
use sdk_common::sp_client::silentpayments::utils::receiving::{
|
||||
use sdk_common::spdk_core::bitcoin::consensus::{deserialize, serialize};
|
||||
use sdk_common::spdk_core::bitcoin::hex::{DisplayHex, FromHex};
|
||||
use sdk_common::spdk_core::bitcoin::secp256k1::PublicKey;
|
||||
use sdk_common::spdk_core::bitcoin::{OutPoint, Transaction};
|
||||
use sdk_common::spdk_core::silentpayments::utils::receiving::{
|
||||
calculate_tweak_data, get_pubkey_from_input,
|
||||
};
|
||||
use sdk_common::sp_client::{OwnedOutput, SpClient};
|
||||
use sdk_common::spdk_core::{OwnedOutput, SpClient};
|
||||
use sdk_common::serialization::OutPointMemberMap;
|
||||
use serde_json::{self};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user