Update to latest sdk_common

This commit is contained in:
Sosthene 2025-11-27 16:59:07 +01:00
parent 42355506bb
commit b75217ae0f
9 changed files with 407 additions and 97 deletions

View File

@ -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"

View File

@ -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());
}
}

View File

@ -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(
@ -279,17 +282,108 @@ impl<'a> SpScanner for WasmSpScanner<'a> {
fn client(&self) -> &SpClient {
&self.client
}
// 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();
fn backend(&self) -> &dyn ChainBackend {
self.backend.as_ref()
// 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 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)?;
fn updater(&mut self) -> &mut dyn Updater {
self.updater.as_mut()
// 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 {

View File

@ -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
};

View File

@ -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,
);

View File

@ -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
View 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
View 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());
}

View File

@ -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};