diff --git a/clients/apps/rust/src/main.rs b/clients/apps/rust/src/main.rs index 9649be1d..7b15138e 100644 --- a/clients/apps/rust/src/main.rs +++ b/clients/apps/rust/src/main.rs @@ -27,7 +27,7 @@ enum Commands { BroadcastBackupTransaction { wallet_name: String, statechain_id: String, - to_address: String, + to_address: Option, /// Transaction fee rate in sats per byte fee_rate: Option }, @@ -115,7 +115,7 @@ async fn main() -> Result<()> { }, Commands::BroadcastBackupTransaction { wallet_name, statechain_id, to_address, fee_rate } => { mercuryrustlib::coin_status::update_coins(&client_config, &wallet_name).await?; - mercuryrustlib::broadcast_backup_tx::execute(&client_config, &wallet_name, &statechain_id, &to_address, fee_rate).await?; + mercuryrustlib::broadcast_backup_tx::execute(&client_config, &wallet_name, &statechain_id, to_address, fee_rate).await?; }, Commands::ListStatecoins { wallet_name } => { mercuryrustlib::coin_status::update_coins(&client_config, &wallet_name).await?; diff --git a/clients/libs/rust/src/broadcast_backup_tx.rs b/clients/libs/rust/src/broadcast_backup_tx.rs index 93f07a31..db544d38 100644 --- a/clients/libs/rust/src/broadcast_backup_tx.rs +++ b/clients/libs/rust/src/broadcast_backup_tx.rs @@ -3,14 +3,17 @@ use anyhow::{anyhow, Result}; use electrum_client::ElectrumApi; use mercurylib::wallet::{cpfp_tx, CoinStatus}; -pub async fn execute(client_config: &ClientConfig, wallet_name: &str, statechain_id: &str, to_address: &str, fee_rate: Option) -> Result<()> { +pub async fn execute(client_config: &ClientConfig, wallet_name: &str, statechain_id: &str, to_address: Option, fee_rate: Option) -> Result<()> { let mut wallet: mercurylib::wallet::Wallet = get_wallet(&client_config.pool, &wallet_name).await?; - let is_address_valid = mercurylib::validate_address(to_address, &wallet.network)?; + if to_address.is_some() { + let to_address = to_address.clone().unwrap(); + let is_address_valid = mercurylib::validate_address(&to_address, &wallet.network)?; - if !is_address_valid { - return Err(anyhow!("Invalid address")); + if !is_address_valid { + return Err(anyhow!("Invalid address")); + } } let backup_txs = get_backup_txs(&client_config.pool, &statechain_id).await?; @@ -34,38 +37,46 @@ pub async fn execute(client_config: &ClientConfig, wallet_name: &str, statechain let backup_tx = cpfp_tx::latest_backup_tx_pays_to_user_pubkey(&backup_txs, &coin, &wallet.network)?; - let fee_rate = match fee_rate { - Some(fee_rate) => fee_rate, - None => { - let mut fee_rate_btc_per_kb = client_config.electrum_client.estimate_fee(1)?; + let cpfp_tx = if to_address.is_some() { - // Why does it happen? - if fee_rate_btc_per_kb <= 0.0 { - fee_rate_btc_per_kb = 0.00001; - } + let to_address = to_address.clone().unwrap(); - let fee_rate_sats_per_byte = fee_rate_btc_per_kb * 100000.0; + let fee_rate = match fee_rate { + Some(fee_rate) => fee_rate, + None => { + let mut fee_rate_btc_per_kb = client_config.electrum_client.estimate_fee(1)?; - if fee_rate_sats_per_byte > client_config.max_fee_rate { - client_config.max_fee_rate - } else { - fee_rate_sats_per_byte - } - }, - }; + // Why does it happen? + if fee_rate_btc_per_kb <= 0.0 { + fee_rate_btc_per_kb = 0.00001; + } - let cpfp_tx = cpfp_tx::create_cpfp_tx(&backup_tx, &coin, to_address, fee_rate, &wallet.network)?; + let fee_rate_sats_per_byte = fee_rate_btc_per_kb * 100000.0; - let tx_bytes = hex::decode(&backup_tx.tx)?; - let txid = client_config.electrum_client.transaction_broadcast_raw(&tx_bytes)?; - println!("Broadcasting backup transaction: {}", txid); + if fee_rate_sats_per_byte > client_config.max_fee_rate { + client_config.max_fee_rate + } else { + fee_rate_sats_per_byte + } + }, + }; - let tx_bytes = hex::decode(&cpfp_tx)?; - let txid = client_config.electrum_client.transaction_broadcast_raw(&tx_bytes)?; - println!("Broadcasting CPFP transaction: {}", txid); + Some(cpfp_tx::create_cpfp_tx(&backup_tx, &coin, &to_address, fee_rate, &wallet.network)?) + } else { + None + }; + let tx_bytes = hex::decode(&backup_tx.tx)?; + let mut txid = client_config.electrum_client.transaction_broadcast_raw(&tx_bytes)?; + + if cpfp_tx.is_some() { + let cpfp_tx = cpfp_tx.unwrap(); + let tx_bytes = hex::decode(&cpfp_tx)?; + txid = client_config.electrum_client.transaction_broadcast_raw(&tx_bytes)?; + } + coin.tx_cpfp = Some(txid.to_string()); - coin.withdrawal_address = Some(to_address.to_string()); + coin.withdrawal_address = if to_address.is_some() { Some(to_address.unwrap()) } else { Some(coin.backup_address.clone()) }; coin.status = CoinStatus::WITHDRAWING; let signed_statechain_id = coin.signed_statechain_id.as_ref().unwrap().to_string(); diff --git a/clients/libs/rust/src/transfer_receiver.rs b/clients/libs/rust/src/transfer_receiver.rs index 2de94b5d..3e4d560e 100644 --- a/clients/libs/rust/src/transfer_receiver.rs +++ b/clients/libs/rust/src/transfer_receiver.rs @@ -59,6 +59,9 @@ pub async fn execute(client_config: &ClientConfig, wallet_name: &str) -> Result< let mut temp_coins = wallet.coins.clone(); let mut temp_activities = wallet.activities.clone(); + let block_header = client_config.electrum_client.block_headers_subscribe_raw()?; + let blockheight = block_header.height as u32; + for (key, values) in &enc_msgs_per_auth_pubkey { let auth_pubkey = key.clone(); @@ -71,7 +74,7 @@ pub async fn execute(client_config: &ClientConfig, wallet_name: &str) -> Result< let mut coin = coin.unwrap(); - let message_result = process_encrypted_message(client_config, &mut coin, enc_message, &wallet.network, &info_config, &mut temp_activities).await; + let message_result = process_encrypted_message(client_config, &mut coin, enc_message, &wallet.network, &info_config, blockheight, &mut temp_activities).await; if message_result.is_err() { println!("Error: {}", message_result.err().unwrap().to_string()); @@ -99,7 +102,7 @@ pub async fn execute(client_config: &ClientConfig, wallet_name: &str) -> Result< let mut new_coin = new_coin.unwrap(); - let message_result = process_encrypted_message(client_config, &mut new_coin, enc_message, &wallet.network, &info_config, &mut temp_activities).await; + let message_result = process_encrypted_message(client_config, &mut new_coin, enc_message, &wallet.network, &info_config, blockheight, &mut temp_activities).await; if message_result.is_err() { println!("Error: {}", message_result.err().unwrap().to_string()); @@ -151,7 +154,7 @@ pub struct MessageResult { pub statechain_id: Option, } -async fn process_encrypted_message(client_config: &ClientConfig, coin: &mut Coin, enc_message: &str, network: &str, info_config: &InfoConfig, activities: &mut Vec) -> Result { +async fn process_encrypted_message(client_config: &ClientConfig, coin: &mut Coin, enc_message: &str, network: &str, info_config: &InfoConfig, blockheight: u32, activities: &mut Vec) -> Result { let client_auth_key = coin.auth_privkey.clone(); let new_user_pubkey = coin.user_pubkey.clone(); @@ -208,8 +211,10 @@ async fn process_encrypted_message(client_config: &ClientConfig, coin: &mut Coin &transfer_msg, &statechain_info, &tx0_hex, + blockheight, client_config.fee_rate_tolerance, current_fee_rate_sats_per_byte, + info_config.initlock, info_config.interval); if previous_lock_time.is_err() { diff --git a/clients/libs/web/transfer_receive.js b/clients/libs/web/transfer_receive.js index 61460d87..d9ba11bf 100644 --- a/clients/libs/web/transfer_receive.js +++ b/clients/libs/web/transfer_receive.js @@ -131,7 +131,7 @@ const sendTransferReceiverRequestPayload = async (clientConfig, transferReceiver } -const processEncryptedMessage = async (clientConfig, coin, encMessage, network, serverInfo, activities) => { +const processEncryptedMessage = async (clientConfig, coin, encMessage, network, serverInfo, blockheight, activities) => { let clientAuthKey = coin.auth_privkey; let newUserPubkey = coin.user_pubkey; @@ -182,8 +182,10 @@ const processEncryptedMessage = async (clientConfig, coin, encMessage, network, transferMsg, statechainInfo, tx0Hex, + blockheight, feeRateTolerance, currentFeeRateSatsPerByte, + serverInfo.initlock, serverInfo.interval ) @@ -283,6 +285,9 @@ const execute = async (clientConfig, walletName) => { let tempCoins = [...wallet.coins]; let tempActivities = [...wallet.activities]; + const response = await axios.get(`${clientConfig.esploraServer}/api/blocks/tip/height`); + const blockheight = response.data; + for (let [authPubkey, encMessages] of encMsgsPerAuthPubkey.entries()) { for (let encMessage of encMessages) { @@ -291,7 +296,7 @@ const execute = async (clientConfig, walletName) => { if (coin) { try { - let messageResult = await processEncryptedMessage(clientConfig, coin, encMessage, wallet.network, serverInfo, tempActivities); + let messageResult = await processEncryptedMessage(clientConfig, coin, encMessage, wallet.network, serverInfo, blockheight, tempActivities); if (messageResult.isBatchLocked) { isThereBatchLocked = true; @@ -310,7 +315,7 @@ const execute = async (clientConfig, walletName) => { let newCoin = await mercury_wasm.duplicateCoinToInitializedState(wallet, authPubkey); if (newCoin) { - let messageResult = await processEncryptedMessage(clientConfig, newCoin, encMessage, wallet.network, serverInfo, tempActivities); + let messageResult = await processEncryptedMessage(clientConfig, newCoin, encMessage, wallet.network, serverInfo, blockheight, tempActivities); if (messageResult.isBatchLocked) { isThereBatchLocked = true; diff --git a/clients/tests/rust/src/electrs.rs b/clients/tests/rust/src/electrs.rs index 2969aef6..05cf6039 100644 --- a/clients/tests/rust/src/electrs.rs +++ b/clients/tests/rust/src/electrs.rs @@ -24,4 +24,9 @@ pub async fn check_address(client_config: &ClientConfig, address: &str, amount: } Ok(true) +} + +pub async fn get_blockheight(client_config: &ClientConfig) -> Result { + let blockheight = client_config.electrum_client.block_headers_subscribe()?.height; + Ok(blockheight) } \ No newline at end of file diff --git a/clients/tests/rust/src/main.rs b/clients/tests/rust/src/main.rs index e9e4df20..692f76b7 100644 --- a/clients/tests/rust/src/main.rs +++ b/clients/tests/rust/src/main.rs @@ -8,6 +8,7 @@ pub mod tb02_transfer_address_reuse; pub mod tb03_simple_atomic_transfer; pub mod tb04_simple_lightning_latch; pub mod tm01_sender_double_spends; +mod tv05; use anyhow::{Result, Ok}; #[tokio::main(flavor = "current_thread")] @@ -20,6 +21,7 @@ async fn main() -> Result<()> { tm01_sender_double_spends::execute().await?; ta01_sign_second_not_called::execute().await?; ta02_duplicate_deposits::execute().await?; + tv05::execute().await?; Ok(()) } diff --git a/clients/tests/rust/src/tv05.rs b/clients/tests/rust/src/tv05.rs new file mode 100644 index 00000000..29bff91f --- /dev/null +++ b/clients/tests/rust/src/tv05.rs @@ -0,0 +1,103 @@ +use std::{env, process::Command, thread, time::Duration}; + +use anyhow::{Result, Ok}; +use mercuryrustlib::{client_config::ClientConfig, CoinStatus, Wallet}; + +use crate::{bitcoin_core, electrs}; + +async fn w1_transfer_to_w2(client_config: &ClientConfig, wallet1: &Wallet, wallet2: &Wallet) -> Result<()> { + + let amount = 1000; + + let token_id = mercuryrustlib::deposit::get_token(client_config).await?; + + let deposit_address = mercuryrustlib::deposit::get_deposit_bitcoin_address(&client_config, &wallet1.name, &token_id, amount).await?; + + let _ = bitcoin_core::sendtoaddress(amount, &deposit_address)?; + + let core_wallet_address = bitcoin_core::getnewaddress()?; + let remaining_blocks = client_config.confirmation_target; + let _ = bitcoin_core::generatetoaddress(remaining_blocks, &core_wallet_address)?; + + // It appears that Electrs takes a few seconds to index the transaction + let mut is_tx_indexed = false; + + while !is_tx_indexed { + is_tx_indexed = electrs::check_address(client_config, &deposit_address, amount).await?; + thread::sleep(Duration::from_secs(1)); + } + + mercuryrustlib::coin_status::update_coins(&client_config, &wallet1.name).await?; + let wallet1: mercuryrustlib::Wallet = mercuryrustlib::sqlite_manager::get_wallet(&client_config.pool, &wallet1.name).await?; + let new_coin = wallet1.coins.iter().find(|&coin| coin.aggregated_address == Some(deposit_address.clone()) && coin.status == CoinStatus::CONFIRMED).unwrap(); + let statechain_id = new_coin.statechain_id.as_ref().unwrap(); + + assert!(new_coin.status == CoinStatus::CONFIRMED); + + let wallet2_transfer_adress = mercuryrustlib::transfer_receiver::new_transfer_address(&client_config, &wallet2.name).await?; + + let batch_id = None; + let force_send = false; + + let result = mercuryrustlib::transfer_sender::execute(&client_config, &wallet2_transfer_adress, &wallet1.name, &statechain_id, force_send, batch_id).await; + assert!(result.is_ok()); + + let transfer_receive_result = mercuryrustlib::transfer_receiver::execute(&client_config, &wallet2.name).await?; + let received_statechain_ids = transfer_receive_result.received_statechain_ids; + + assert!(received_statechain_ids.contains(&statechain_id.to_string())); + assert!(received_statechain_ids.len() == 1); + + mercuryrustlib::coin_status::update_coins(&client_config, &wallet2.name).await?; + let local_wallet_2: mercuryrustlib::Wallet = mercuryrustlib::sqlite_manager::get_wallet(&client_config.pool, &wallet2.name).await?; + let new_w2_coin = local_wallet_2.coins.iter().find(|&coin| coin.statechain_id == Some(statechain_id.clone())).unwrap(); + + assert!(new_coin.status == CoinStatus::CONFIRMED); + assert!(new_w2_coin.status == CoinStatus::CONFIRMED); + + let fee_rate = None; + + let core_wallet_address = Some(core_wallet_address); + + let result = mercuryrustlib::broadcast_backup_tx::execute(&client_config, &wallet1.name, &statechain_id, core_wallet_address.clone(), fee_rate).await; + + assert!(result.is_err()); + + let err = result.err().unwrap(); + + assert!(err.to_string() == "Electrum server error: {\"code\":2,\"message\":\"non-final\"}"); + + let _ = bitcoin_core::generatetoaddress(990, &core_wallet_address.clone().unwrap())?; + + let result = mercuryrustlib::broadcast_backup_tx::execute(&client_config, &wallet2.name, &statechain_id, core_wallet_address, fee_rate).await; + + assert!(result.is_ok()); + + Ok(()) +} + +pub async fn execute() -> Result<()> { + let _ = Command::new("rm").arg("wallet.db").arg("wallet.db-shm").arg("wallet.db-wal").output().expect("failed to execute process"); + + env::set_var("ML_NETWORK", "regtest"); + + let client_config = mercuryrustlib::client_config::load().await; + + let wallet1 = mercuryrustlib::wallet::create_wallet( + "wallet1", + &client_config).await?; + + mercuryrustlib::sqlite_manager::insert_wallet(&client_config.pool, &wallet1).await?; + + let wallet2 = mercuryrustlib::wallet::create_wallet( + "wallet2", + &client_config).await?; + + mercuryrustlib::sqlite_manager::insert_wallet(&client_config.pool, &wallet2).await?; + + w1_transfer_to_w2(&client_config, &wallet1, &wallet2).await?; + + println!("TV05 - Result as reported."); + + Ok(()) +} \ No newline at end of file diff --git a/clients/tests/web/package-lock.json b/clients/tests/web/package-lock.json index 64feedd7..ea1ed337 100644 --- a/clients/tests/web/package-lock.json +++ b/clients/tests/web/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "axios": "^1.7.2", "body-parser": "^1.20.2", + "bolt11": "^1.4.1", "cors": "^2.8.5", "express": "^4.19.2", "install": "^0.13.0", @@ -699,6 +700,17 @@ "node": ">=18" } }, + "node_modules/@noble/hashes": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz", + "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@open-draft/deferred-promise": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", @@ -1027,6 +1039,14 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", @@ -1054,7 +1074,6 @@ "version": "22.0.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.0.0.tgz", "integrity": "sha512-VT7KSYudcPOzP5Q0wfbowyNLaVR8QWUdw+088uFWwfvpY6uCWaXpqV6ieLAu9WBcnTa7H4Z5RLK8I5t2FuOcqw==", - "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~6.11.1" @@ -1381,6 +1400,11 @@ "streamx": "^2.18.0" } }, + "node_modules/base-x": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", + "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==" + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -1408,6 +1432,45 @@ "node": ">=10.0.0" } }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + }, + "node_modules/bip174": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-2.1.1.tgz", + "integrity": "sha512-mdFV5+/v0XyNYXjBS6CQPLo9ekCx4gtKZFnJm5PMto7Fs9hTTDpkkzOB7/FtluRI6JbUUAu+snTYfJRgHLZbZQ==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/bitcoinjs-lib": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.6.tgz", + "integrity": "sha512-Fk8+Vc+e2rMoDU5gXkW9tD+313rhkm5h6N9HfZxXvYU9LedttVvmXKTgd9k5rsQJjkSfsv6XRM8uhJv94SrvcA==", + "dependencies": { + "@noble/hashes": "^1.2.0", + "bech32": "^2.0.0", + "bip174": "^2.1.1", + "bs58check": "^3.0.1", + "typeforce": "^1.11.3", + "varuint-bitcoin": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/bitcoinjs-lib/node_modules/bech32": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", + "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" + }, + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, "node_modules/body-parser": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", @@ -1455,6 +1518,43 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/bolt11": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/bolt11/-/bolt11-1.4.1.tgz", + "integrity": "sha512-jR0Y+MO+CK2at1Cg5mltLJ+6tdOwNKoTS/DJOBDdzVkQ+R9D6UgZMayTWOsuzY7OgV1gEqlyT5Tzk6t6r4XcNQ==", + "dependencies": { + "@types/bn.js": "^4.11.3", + "bech32": "^1.1.2", + "bitcoinjs-lib": "^6.0.0", + "bn.js": "^4.11.8", + "create-hash": "^1.2.0", + "lodash": "^4.17.11", + "safe-buffer": "^5.1.1", + "secp256k1": "^4.0.2" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "node_modules/bs58": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", + "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", + "dependencies": { + "base-x": "^4.0.0" + } + }, + "node_modules/bs58check": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-3.0.1.tgz", + "integrity": "sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ==", + "dependencies": { + "@noble/hashes": "^1.2.0", + "bs58": "^5.0.0" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -1584,6 +1684,15 @@ "devtools-protocol": "*" } }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/cli-spinners": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", @@ -1734,6 +1843,18 @@ } } }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1904,6 +2025,20 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, + "node_modules/elliptic": { + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", + "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -2462,6 +2597,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -2480,6 +2637,16 @@ "dev": true, "license": "MIT" }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", @@ -2753,6 +2920,11 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/loupe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", @@ -2789,6 +2961,16 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -2862,6 +3044,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, "node_modules/mitt": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", @@ -2977,6 +3169,21 @@ "node": ">= 0.4.0" } }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, + "node_modules/node-gyp-build": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", + "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/npm": { "version": "10.8.2", "resolved": "https://registry.npmjs.org/npm/-/npm-10.8.2.tgz", @@ -5812,6 +6019,19 @@ "dev": true, "license": "MIT" }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", @@ -5841,6 +6061,15 @@ "node": ">=4" } }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, "node_modules/rollup": { "version": "4.18.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.1.tgz", @@ -5918,6 +6147,20 @@ "node": ">=v12.22.7" } }, + "node_modules/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "hasInstallScript": true, + "dependencies": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/semver": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", @@ -6014,6 +6257,18 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6183,6 +6438,14 @@ "dev": true, "license": "MIT" }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -6382,6 +6645,11 @@ "node": ">= 0.6" } }, + "node_modules/typeforce": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", + "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" + }, "node_modules/unbzip2-stream": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", @@ -6395,7 +6663,6 @@ "version": "6.11.1", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.11.1.tgz", "integrity": "sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==", - "devOptional": true, "license": "MIT" }, "node_modules/universalify": { @@ -6430,6 +6697,11 @@ "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==" }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -6438,6 +6710,14 @@ "node": ">= 0.4.0" } }, + "node_modules/varuint-bitcoin": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz", + "integrity": "sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==", + "dependencies": { + "safe-buffer": "^5.1.1" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/docs/protocol.md b/docs/protocol.md index 94d5ec7e..a03da4f1 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -19,7 +19,7 @@ A user wants to create a shared key, and they request that the SE initialize the 3. The SE then generates a private key: `s1` (the SE private key share), calculates the corresponding public key and sends it to Owner 1: `S1 = s1.G` along with a new random `statechain_id` (UUID - used for the server to reference this shared key). The SE then stores `s1`, `S1` and `A1` indexed with `statechain_id`. 4. Owner 1 then adds the public key they receive by their own public key to obtain the shared (aggregated) public key `P` (which corresponds to a shared private key of `p = o1 + s1`): `P = O1 + S1` 5. Owner 1 creates and broadcasts a funding transaction (`Tx0`) to pay an amount `v` to the bitcoin address derived from `P`. This defines the UTXO `TxID:vout` (the coin outpoint). -6. Owner 1 creates an unsigned *backup transaction* (`Tx1`) that pays the `P` output of `Tx0` to an address `O1`, and sets the `nLocktime` to the initial future block height `h0` (where `h0 = cheight + hinit`, `cheight` is the current Bitcoin block height and `hinit` is the initial locktime specified by the server). +6. Owner 1 creates an unsigned *backup transaction* (`Tx1`) that pays the `P` output of `Tx0` to an address `O1`, and sets the `nLocktime` to the initial future block height `h0` (where `h0 = cheight + hinit`, `cheight` is the current Bitcoin block height and `hinit` is the initial locktime specified by the server). The `nSequence` number is set to zero. 7. Owner 1 cooperates with SE to generate a valid signature on `Tx1` as follows (MuSig2): > To generate a signature on `Tx1`, the owner first computes the sighash `m1`. @@ -41,7 +41,7 @@ Owner 1 wishes to transfer the value of the UTXO `TxID:vout` `v` to a new owner 1. The receiver (Owner 2) generates a private key `o2`. They then compute the corresponding public key `O2 = o2.G`. They also generate an authentication key `a2` and corresponding public key `A2 = a2.G`. 2. `O2 || A2` is the Owner 2 'mercury address' and is communicated to Owner 1 (or published) in order for them to send the statecoin to it. -3. Owner 1 then creates a new unsigned backup transaction `Tx2` paying the output of `Tx0` to the bitcoin address defined from `O2`, and sets the `nLocktime` to `h0 - (n-1)*c` where `c` is the confirmation interval and `n` is the owner number (i.e. 2). +3. Owner 1 then creates a new unsigned backup transaction `Tx2` paying the output of `Tx0` to the bitcoin address defined from `O2`, and sets the `nLocktime` to `h0 - (n-1)*c` where `c` is the confirmation interval and `n` is the owner number (i.e. 2). The `nSequence` number is set to zero. 4. Owner 1 cooperates with SE to generate a valid signature on `Tx2` as follows: > To generate a signature on `Tx2`, the owner first computes the sighash `m2`. @@ -80,9 +80,13 @@ Owner 1 wishes to transfer the value of the UTXO `TxID:vout` `v` to a new owner b. The `nLocktimes` are decremented correctly (i.e. the latest `TxK` has the lowest). -5. Owner 2 queries SE for 1) The total number of signatures generated for `statechain_id`: `N` and 2) Current SE public key share. 3) The public point `X1 = x1.G` -6. Owner 2 then verifies that `K = N` and that `t1.G = O1 + X1`. This check verifies that the value `t1` has been computed correctly from the sender private key. -7. Owner 2 verifies the signature `SC_sig_1` verifies against `O1` and that `P = S1 + O1`. This check mitigates the key cancellation vulnerability. + c. The transaction has been constructed exactly according to the sender implimentation. + + d. That the latest `nLocktimes` is not expired accordning to the current block height. + +6. Owner 2 queries SE for 1) The total number of signatures generated for `statechain_id`: `N` and 2) Current SE public key share. 3) The public point `X1 = x1.G` +7. Owner 2 then verifies that `K = N` and that `t1.G = O1 + X1`. This check verifies that the value `t1` has been computed correctly from the sender private key. +8. Owner 2 verifies the signature `SC_sig_1` verifies against `O1` and that `P = S1 + O1`. This check mitigates the key cancellation vulnerability. The SE key share update then proceeds as follows: diff --git a/lib/src/error.rs b/lib/src/error.rs index b2b11546..73541fd1 100644 --- a/lib/src/error.rs +++ b/lib/src/error.rs @@ -43,6 +43,11 @@ pub enum MercuryError { Bech32Error, HexError, LocktimeNotBlockHeightError, + LocktimeTooLow, + LocktimeTooHigh, + TransactionReconstructionError, + TransactionVersionError, + TransactionSequenceDifferentThanZeroError, BitcoinConsensusEncodeError, MusigNonceGenError, InvalidStatechainAddressError, @@ -68,6 +73,8 @@ pub enum MercuryError { SecpError, NoBackupTransactionFound, Tx1HasMoreThanOneInput, + TxHasMoreThanOneOutput, + EmptyInput, InvalidSignature, EmptyWitness, EmptyWitnessData, diff --git a/lib/src/transaction.rs b/lib/src/transaction.rs index 196b5fef..21229a6a 100644 --- a/lib/src/transaction.rs +++ b/lib/src/transaction.rs @@ -215,7 +215,7 @@ pub fn get_musig_session( input: vec![TxIn { previous_output: OutPoint { txid: input_txid, vout: input_vout }, script_sig: ScriptBuf::new(), - sequence: bitcoin::Sequence(0xFFFFFFFF), // Ignore nSequence. + sequence: bitcoin::Sequence(0x0), // Ignore nSequence. witness: Witness::default(), }], output: outputs, diff --git a/lib/src/transfer/receiver.rs b/lib/src/transfer/receiver.rs index 9fbbd838..541aa0c4 100644 --- a/lib/src/transfer/receiver.rs +++ b/lib/src/transfer/receiver.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use bitcoin::{PrivateKey, Transaction, hashes::{sha256, Hash}, Txid, Address, sighash::{TapSighashType, SighashCache, self}, TxOut, taproot::TapTweakHash}; +use bitcoin::{hashes::{sha256, Hash}, sighash::{self, SighashCache, TapSighashType}, taproot::TapTweakHash, Address, PrivateKey, Sequence, Transaction, TxOut, Txid}; use secp256k1_zkp::{PublicKey, schnorr::Signature, Secp256k1, Message, XOnlyPublicKey, musig::{MusigPubNonce, BlindingFactor, blinded_musig_pubkey_xonly_tweak_add, MusigAggNonce, MusigSession}, SecretKey, Scalar, KeyPair}; use serde::{Serialize, Deserialize}; @@ -265,8 +265,10 @@ pub fn validate_signature_scheme( transfer_msg: &TransferMsg, statechain_info: &StatechainInfoResponsePayload, tx0_hex: &str, + current_blockheight: u32, fee_rate_tolerance: f64, current_fee_rate_sats_per_byte: f64, + lockheight_init: u32, interval: u32) -> Result { let mut previous_lock_time: Option = None; @@ -291,6 +293,24 @@ pub fn validate_signature_scheme( break; } + if verify_transaction_sequence(&backup_tx.tx).is_err() { + println!("transaction sequence is not correct"); + sig_scheme_validation = false; + break; + } + + if verify_if_locktime_is_reasonable_tx_version_and_output_size(&backup_tx.tx, current_blockheight, lockheight_init).is_err() { + println!("locktime is not reasonable"); + sig_scheme_validation = false; + break; + } + + if reconstruct_transaction(&backup_tx.tx).is_err() { + println!("reconstruction failed"); + sig_scheme_validation = false; + break; + } + if previous_lock_time.is_some() { let prev_lock_time = previous_lock_time.unwrap(); let current_lock_time = crate::utils::get_blockheight(&backup_tx)?; @@ -337,7 +357,7 @@ pub fn verify_transaction_signature(tx_n_hex: &str, tx0_hex: &str, fee_rate_tole let xonly_pubkey = XOnlyPublicKey::from_slice(tx0_output.script_pubkey[2..].as_bytes()).unwrap(); - let sighash_type = TapSighashType::from_consensus_u8(witness_data.last().unwrap().to_owned()).unwrap(); + let sighash_type = TapSighashType::All; let hash = SighashCache::new(tx_n.clone()).taproot_key_spend_signature_hash( 0, @@ -369,6 +389,81 @@ pub fn verify_transaction_signature(tx_n_hex: &str, tx0_hex: &str, fee_rate_tole } +#[cfg_attr(feature = "bindings", uniffi::export)] +pub fn verify_transaction_sequence(tx_n_hex: &str) -> Result<(), MercuryError> { + + let tx_n: Transaction = bitcoin::consensus::encode::deserialize(&hex::decode(&tx_n_hex)?)?; + + if tx_n.input.is_empty() { + return Err(MercuryError::EmptyInput); + } + + if tx_n.input.len() > 1 { + return Err(MercuryError::Tx1HasMoreThanOneInput); + } + + let input = tx_n.input.first().unwrap(); + + if input.sequence != Sequence(0) { + return Err(MercuryError::TransactionSequenceDifferentThanZeroError); + } + + Ok(()) +} + +pub fn verify_if_locktime_is_reasonable_tx_version_and_output_size(tx_n_hex: &str, current_blockheight: u32, lockheight_init:u32) -> Result<(), MercuryError> { + + let tx_n: Transaction = bitcoin::consensus::encode::deserialize(&hex::decode(&tx_n_hex)?)?; + + if tx_n.version != 2 { + return Err(MercuryError::TransactionVersionError); + } + + if tx_n.output.len() > 1 { + return Err(MercuryError::TxHasMoreThanOneOutput); + } + + let lock_time = tx_n.lock_time; + + if !(lock_time.is_block_height()) { + return Err(MercuryError::LocktimeNotBlockHeightError); + } + + if lock_time.to_consensus_u32() <= current_blockheight { + return Err(MercuryError::LocktimeTooLow); + } + + if current_blockheight + lockheight_init < lock_time.to_consensus_u32() { + return Err(MercuryError::LocktimeTooHigh); + } + + Ok(()) +} + +pub fn reconstruct_transaction(tx_n_hex: &str) -> Result<(), MercuryError> { + + let tx_n: Transaction = bitcoin::consensus::encode::deserialize(&hex::decode(&tx_n_hex)?)?; + + // this assumes that the transaction has only one input and one output (suposedly checked before) + let output = tx_n.output[0].clone(); + let input = tx_n.input[0].clone(); + let locktime = tx_n.lock_time; + + let new_tx = Transaction { + version: 2, + lock_time: locktime, + input: [input].to_vec(), + output: [output].to_vec(), + }; + + let serialized_new_tx = hex::encode(bitcoin::consensus::encode::serialize(&new_tx)); + + if tx_n_hex != serialized_new_tx { + return Err(MercuryError::TransactionReconstructionError); + } + + Ok(()) +} fn get_tx_hash(tx_0: &Transaction, tx_n: &Transaction) -> Result { diff --git a/lib/src/unifii_interface.rs b/lib/src/unifii_interface.rs index 59d6421c..76b3d70c 100644 --- a/lib/src/unifii_interface.rs +++ b/lib/src/unifii_interface.rs @@ -101,11 +101,13 @@ pub fn fii_create_transfer_receiver_request_payload(statechain_info: &Statechain pub fn ffi_validate_signature_scheme( ffi_transfer_msg: &FFITransferMsg, statechain_info: &StatechainInfoResponsePayload, - tx0_hex: &str, + tx0_hex: &str, + current_blockheight: u32, fee_rate_tolerance: f64, current_fee_rate_sats_per_byte: f64, + lockheight_init:u32, interval: u32) -> Result { let transfer_msg = ffi_to_transfer_msg(ffi_transfer_msg.clone())?; - crate::transfer::receiver::validate_signature_scheme(&transfer_msg, &statechain_info, tx0_hex, fee_rate_tolerance, current_fee_rate_sats_per_byte, interval) + crate::transfer::receiver::validate_signature_scheme(&transfer_msg, &statechain_info, tx0_hex, current_blockheight, fee_rate_tolerance, current_fee_rate_sats_per_byte, lockheight_init, interval) } diff --git a/server/Cargo.toml b/server/Cargo.toml index 0a7df756..abfb8463 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mercury-server" -version = "0.1.1" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/wasm/node_pkg/debug/mercury_wasm.d.ts b/wasm/node_pkg/debug/mercury_wasm.d.ts index efa4b767..fdfd7877 100644 --- a/wasm/node_pkg/debug/mercury_wasm.d.ts +++ b/wasm/node_pkg/debug/mercury_wasm.d.ts @@ -267,12 +267,14 @@ export function duplicateCoinToInitializedState(walletJson: any, authPubkey: str * @param {any} transfer_msg * @param {any} statechain_info * @param {string} tx0_hex +* @param {number} current_blockheight * @param {number} fee_rate_tolerance * @param {number} current_fee_rate_sats_per_byte +* @param {number} lockheight_init * @param {number} interval * @returns {any} */ -export function validateSignatureScheme(transfer_msg: any, statechain_info: any, tx0_hex: string, fee_rate_tolerance: number, current_fee_rate_sats_per_byte: number, interval: number): any; +export function validateSignatureScheme(transfer_msg: any, statechain_info: any, tx0_hex: string, current_blockheight: number, fee_rate_tolerance: number, current_fee_rate_sats_per_byte: number, lockheight_init: number, interval: number): any; /** * @returns {any} */ diff --git a/wasm/node_pkg/debug/mercury_wasm.js b/wasm/node_pkg/debug/mercury_wasm.js index a16e56e1..fbe9230a 100644 --- a/wasm/node_pkg/debug/mercury_wasm.js +++ b/wasm/node_pkg/debug/mercury_wasm.js @@ -845,16 +845,20 @@ module.exports.duplicateCoinToInitializedState = function(walletJson, authPubkey * @param {any} transfer_msg * @param {any} statechain_info * @param {string} tx0_hex +* @param {number} current_blockheight * @param {number} fee_rate_tolerance * @param {number} current_fee_rate_sats_per_byte +* @param {number} lockheight_init * @param {number} interval * @returns {any} */ -module.exports.validateSignatureScheme = function(transfer_msg, statechain_info, tx0_hex, fee_rate_tolerance, current_fee_rate_sats_per_byte, interval) { +module.exports.validateSignatureScheme = function(transfer_msg, statechain_info, tx0_hex, current_blockheight, fee_rate_tolerance, current_fee_rate_sats_per_byte, lockheight_init, interval) { const ptr0 = passStringToWasm0(tx0_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len0 = WASM_VECTOR_LEN; + _assertNum(current_blockheight); + _assertNum(lockheight_init); _assertNum(interval); - const ret = wasm.validateSignatureScheme(addHeapObject(transfer_msg), addHeapObject(statechain_info), ptr0, len0, fee_rate_tolerance, current_fee_rate_sats_per_byte, interval); + const ret = wasm.validateSignatureScheme(addHeapObject(transfer_msg), addHeapObject(statechain_info), ptr0, len0, current_blockheight, fee_rate_tolerance, current_fee_rate_sats_per_byte, lockheight_init, interval); return takeObject(ret); }; @@ -1176,17 +1180,17 @@ module.exports.__wbg_buffer_085ec1f694018c4f = function() { return logError(func return addHeapObject(ret); }, arguments) }; +module.exports.__wbg_get_97b561fb56f034b5 = function() { return handleError(function (arg0, arg1) { + const ret = Reflect.get(getObject(arg0), getObject(arg1)); + return addHeapObject(ret); +}, arguments) }; + module.exports.__wbindgen_is_function = function(arg0) { const ret = typeof(getObject(arg0)) === 'function'; _assertBoolean(ret); return ret; }; -module.exports.__wbg_get_97b561fb56f034b5 = function() { return handleError(function (arg0, arg1) { - const ret = Reflect.get(getObject(arg0), getObject(arg1)); - return addHeapObject(ret); -}, arguments) }; - module.exports.__wbindgen_debug_string = function(arg0, arg1) { const ret = debugString(getObject(arg1)); const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); diff --git a/wasm/node_pkg/debug/mercury_wasm_bg.wasm b/wasm/node_pkg/debug/mercury_wasm_bg.wasm index 9a6d27ba..06b19695 100644 Binary files a/wasm/node_pkg/debug/mercury_wasm_bg.wasm and b/wasm/node_pkg/debug/mercury_wasm_bg.wasm differ diff --git a/wasm/node_pkg/debug/mercury_wasm_bg.wasm.d.ts b/wasm/node_pkg/debug/mercury_wasm_bg.wasm.d.ts index e36e7657..a40f9970 100644 --- a/wasm/node_pkg/debug/mercury_wasm_bg.wasm.d.ts +++ b/wasm/node_pkg/debug/mercury_wasm_bg.wasm.d.ts @@ -41,7 +41,7 @@ export function signMessage(a: number, b: number, c: number, d: number): void; export function isEnclavePubkeyPartOfCoin(a: number, b: number, c: number): number; export function latestBackuptxPaysToUserpubkey(a: number, b: number, c: number, d: number): number; export function duplicateCoinToInitializedState(a: number, b: number, c: number): number; -export function validateSignatureScheme(a: number, b: number, c: number, d: number, e: number, f: number, g: number): number; +export function validateSignatureScheme(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number): number; export function getMockWallet(): number; export function rustsecp256k1zkp_v0_8_1_default_illegal_callback_fn(a: number, b: number): void; export function rustsecp256k1zkp_v0_8_1_default_error_callback_fn(a: number, b: number): void; diff --git a/wasm/node_pkg/release/mercury_wasm.d.ts b/wasm/node_pkg/release/mercury_wasm.d.ts index efa4b767..fdfd7877 100644 --- a/wasm/node_pkg/release/mercury_wasm.d.ts +++ b/wasm/node_pkg/release/mercury_wasm.d.ts @@ -267,12 +267,14 @@ export function duplicateCoinToInitializedState(walletJson: any, authPubkey: str * @param {any} transfer_msg * @param {any} statechain_info * @param {string} tx0_hex +* @param {number} current_blockheight * @param {number} fee_rate_tolerance * @param {number} current_fee_rate_sats_per_byte +* @param {number} lockheight_init * @param {number} interval * @returns {any} */ -export function validateSignatureScheme(transfer_msg: any, statechain_info: any, tx0_hex: string, fee_rate_tolerance: number, current_fee_rate_sats_per_byte: number, interval: number): any; +export function validateSignatureScheme(transfer_msg: any, statechain_info: any, tx0_hex: string, current_blockheight: number, fee_rate_tolerance: number, current_fee_rate_sats_per_byte: number, lockheight_init: number, interval: number): any; /** * @returns {any} */ diff --git a/wasm/node_pkg/release/mercury_wasm.js b/wasm/node_pkg/release/mercury_wasm.js index 8a34b2c7..4abeaf0d 100644 --- a/wasm/node_pkg/release/mercury_wasm.js +++ b/wasm/node_pkg/release/mercury_wasm.js @@ -9,6 +9,20 @@ heap.push(undefined, null, true, false); function getObject(idx) { return heap[idx]; } +let heap_next = heap.length; + +function dropObject(idx) { + if (idx < 132) return; + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} + let WASM_VECTOR_LEN = 0; let cachedUint8Memory0 = null; @@ -87,20 +101,6 @@ function getInt32Memory0() { return cachedInt32Memory0; } -let heap_next = heap.length; - -function dropObject(idx) { - if (idx < 132) return; - heap[idx] = heap_next; - heap_next = idx; -} - -function takeObject(idx) { - const ret = getObject(idx); - dropObject(idx); - return ret; -} - let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); cachedTextDecoder.decode(); @@ -823,15 +823,17 @@ module.exports.duplicateCoinToInitializedState = function(walletJson, authPubkey * @param {any} transfer_msg * @param {any} statechain_info * @param {string} tx0_hex +* @param {number} current_blockheight * @param {number} fee_rate_tolerance * @param {number} current_fee_rate_sats_per_byte +* @param {number} lockheight_init * @param {number} interval * @returns {any} */ -module.exports.validateSignatureScheme = function(transfer_msg, statechain_info, tx0_hex, fee_rate_tolerance, current_fee_rate_sats_per_byte, interval) { +module.exports.validateSignatureScheme = function(transfer_msg, statechain_info, tx0_hex, current_blockheight, fee_rate_tolerance, current_fee_rate_sats_per_byte, lockheight_init, interval) { const ptr0 = passStringToWasm0(tx0_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len0 = WASM_VECTOR_LEN; - const ret = wasm.validateSignatureScheme(addHeapObject(transfer_msg), addHeapObject(statechain_info), ptr0, len0, fee_rate_tolerance, current_fee_rate_sats_per_byte, interval); + const ret = wasm.validateSignatureScheme(addHeapObject(transfer_msg), addHeapObject(statechain_info), ptr0, len0, current_blockheight, fee_rate_tolerance, current_fee_rate_sats_per_byte, lockheight_init, interval); return takeObject(ret); }; @@ -851,6 +853,10 @@ function handleError(f, args) { } } +module.exports.__wbindgen_object_drop_ref = function(arg0) { + takeObject(arg0); +}; + module.exports.__wbindgen_string_get = function(arg0, arg1) { const obj = getObject(arg1); const ret = typeof(obj) === 'string' ? obj : undefined; @@ -860,10 +866,6 @@ module.exports.__wbindgen_string_get = function(arg0, arg1) { getInt32Memory0()[arg0 / 4 + 0] = ptr1; }; -module.exports.__wbindgen_object_drop_ref = function(arg0) { - takeObject(arg0); -}; - module.exports.__wbindgen_boolean_get = function(arg0) { const v = getObject(arg0); const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2; @@ -896,6 +898,16 @@ module.exports.__wbindgen_error_new = function(arg0, arg1) { return addHeapObject(ret); }; +module.exports.__wbindgen_number_new = function(arg0) { + const ret = arg0; + return addHeapObject(ret); +}; + +module.exports.__wbindgen_string_new = function(arg0, arg1) { + const ret = getStringFromWasm0(arg0, arg1); + return addHeapObject(ret); +}; + module.exports.__wbindgen_jsval_loose_eq = function(arg0, arg1) { const ret = getObject(arg0) == getObject(arg1); return ret; @@ -908,21 +920,11 @@ module.exports.__wbindgen_number_get = function(arg0, arg1) { getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret); }; -module.exports.__wbindgen_number_new = function(arg0) { - const ret = arg0; - return addHeapObject(ret); -}; - module.exports.__wbindgen_object_clone_ref = function(arg0) { const ret = getObject(arg0); return addHeapObject(ret); }; -module.exports.__wbindgen_string_new = function(arg0, arg1) { - const ret = getStringFromWasm0(arg0, arg1); - return addHeapObject(ret); -}; - module.exports.__wbg_getwithrefkey_15c62c2b8546208d = function(arg0, arg1) { const ret = getObject(arg0)[getObject(arg1)]; return addHeapObject(ret); @@ -952,11 +954,6 @@ module.exports.__wbg_node_caaf83d002149bd5 = function(arg0) { return addHeapObject(ret); }; -module.exports.__wbg_msCrypto_0b84745e9245cdf6 = function(arg0) { - const ret = getObject(arg0).msCrypto; - return addHeapObject(ret); -}; - module.exports.__wbg_require_94a9da52636aacbf = function() { return handleError(function () { const ret = module.require; return addHeapObject(ret); @@ -967,6 +964,11 @@ module.exports.__wbindgen_is_function = function(arg0) { return ret; }; +module.exports.__wbg_msCrypto_0b84745e9245cdf6 = function(arg0) { + const ret = getObject(arg0).msCrypto; + return addHeapObject(ret); +}; + module.exports.__wbg_randomFillSync_290977693942bf03 = function() { return handleError(function (arg0, arg1) { getObject(arg0).randomFillSync(takeObject(arg1)); }, arguments) }; diff --git a/wasm/node_pkg/release/mercury_wasm_bg.wasm b/wasm/node_pkg/release/mercury_wasm_bg.wasm index 24d83658..08b3da66 100644 Binary files a/wasm/node_pkg/release/mercury_wasm_bg.wasm and b/wasm/node_pkg/release/mercury_wasm_bg.wasm differ diff --git a/wasm/node_pkg/release/mercury_wasm_bg.wasm.d.ts b/wasm/node_pkg/release/mercury_wasm_bg.wasm.d.ts index e36e7657..a40f9970 100644 --- a/wasm/node_pkg/release/mercury_wasm_bg.wasm.d.ts +++ b/wasm/node_pkg/release/mercury_wasm_bg.wasm.d.ts @@ -41,7 +41,7 @@ export function signMessage(a: number, b: number, c: number, d: number): void; export function isEnclavePubkeyPartOfCoin(a: number, b: number, c: number): number; export function latestBackuptxPaysToUserpubkey(a: number, b: number, c: number, d: number): number; export function duplicateCoinToInitializedState(a: number, b: number, c: number): number; -export function validateSignatureScheme(a: number, b: number, c: number, d: number, e: number, f: number, g: number): number; +export function validateSignatureScheme(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number): number; export function getMockWallet(): number; export function rustsecp256k1zkp_v0_8_1_default_illegal_callback_fn(a: number, b: number): void; export function rustsecp256k1zkp_v0_8_1_default_error_callback_fn(a: number, b: number): void; diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index b2d93147..7feafe55 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -468,12 +468,12 @@ pub fn duplicateCoinToInitializedState(walletJson: JsValue, authPubkey: String) } #[wasm_bindgen] -pub fn validateSignatureScheme(transfer_msg: JsValue, statechain_info: JsValue, tx0_hex: String, fee_rate_tolerance: f32, current_fee_rate_sats_per_byte: f32, interval: u32) -> JsValue { +pub fn validateSignatureScheme(transfer_msg: JsValue, statechain_info: JsValue, tx0_hex: String, current_blockheight: u32, fee_rate_tolerance: f32, current_fee_rate_sats_per_byte: f32, lockheight_init: u32, interval: u32) -> JsValue { let statechain_info: StatechainInfoResponsePayload = serde_wasm_bindgen::from_value(statechain_info).unwrap(); let transfer_msg: TransferMsg = serde_wasm_bindgen::from_value(transfer_msg).unwrap(); - let result = mercurylib::transfer::receiver::validate_signature_scheme(&transfer_msg, &statechain_info, &tx0_hex, fee_rate_tolerance as f64, current_fee_rate_sats_per_byte as f64, interval); + let result = mercurylib::transfer::receiver::validate_signature_scheme(&transfer_msg, &statechain_info, &tx0_hex, current_blockheight, fee_rate_tolerance as f64, current_fee_rate_sats_per_byte as f64, lockheight_init, interval); #[derive(Serialize, Deserialize)] struct ValidationResult { diff --git a/wasm/web_pkg/debug/mercury_wasm.d.ts b/wasm/web_pkg/debug/mercury_wasm.d.ts index 95fe6817..b11793ae 100644 --- a/wasm/web_pkg/debug/mercury_wasm.d.ts +++ b/wasm/web_pkg/debug/mercury_wasm.d.ts @@ -267,12 +267,14 @@ export function duplicateCoinToInitializedState(walletJson: any, authPubkey: str * @param {any} transfer_msg * @param {any} statechain_info * @param {string} tx0_hex +* @param {number} current_blockheight * @param {number} fee_rate_tolerance * @param {number} current_fee_rate_sats_per_byte +* @param {number} lockheight_init * @param {number} interval * @returns {any} */ -export function validateSignatureScheme(transfer_msg: any, statechain_info: any, tx0_hex: string, fee_rate_tolerance: number, current_fee_rate_sats_per_byte: number, interval: number): any; +export function validateSignatureScheme(transfer_msg: any, statechain_info: any, tx0_hex: string, current_blockheight: number, fee_rate_tolerance: number, current_fee_rate_sats_per_byte: number, lockheight_init: number, interval: number): any; /** * @returns {any} */ @@ -322,7 +324,7 @@ export interface InitOutput { readonly isEnclavePubkeyPartOfCoin: (a: number, b: number, c: number) => number; readonly latestBackuptxPaysToUserpubkey: (a: number, b: number, c: number, d: number) => number; readonly duplicateCoinToInitializedState: (a: number, b: number, c: number) => number; - readonly validateSignatureScheme: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number; + readonly validateSignatureScheme: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => number; readonly getMockWallet: () => number; readonly rustsecp256k1zkp_v0_8_1_default_illegal_callback_fn: (a: number, b: number) => void; readonly rustsecp256k1zkp_v0_8_1_default_error_callback_fn: (a: number, b: number) => void; diff --git a/wasm/web_pkg/debug/mercury_wasm.js b/wasm/web_pkg/debug/mercury_wasm.js index 99aee9af..4608b54d 100644 --- a/wasm/web_pkg/debug/mercury_wasm.js +++ b/wasm/web_pkg/debug/mercury_wasm.js @@ -842,16 +842,20 @@ export function duplicateCoinToInitializedState(walletJson, authPubkey) { * @param {any} transfer_msg * @param {any} statechain_info * @param {string} tx0_hex +* @param {number} current_blockheight * @param {number} fee_rate_tolerance * @param {number} current_fee_rate_sats_per_byte +* @param {number} lockheight_init * @param {number} interval * @returns {any} */ -export function validateSignatureScheme(transfer_msg, statechain_info, tx0_hex, fee_rate_tolerance, current_fee_rate_sats_per_byte, interval) { +export function validateSignatureScheme(transfer_msg, statechain_info, tx0_hex, current_blockheight, fee_rate_tolerance, current_fee_rate_sats_per_byte, lockheight_init, interval) { const ptr0 = passStringToWasm0(tx0_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len0 = WASM_VECTOR_LEN; + _assertNum(current_blockheight); + _assertNum(lockheight_init); _assertNum(interval); - const ret = wasm.validateSignatureScheme(addHeapObject(transfer_msg), addHeapObject(statechain_info), ptr0, len0, fee_rate_tolerance, current_fee_rate_sats_per_byte, interval); + const ret = wasm.validateSignatureScheme(addHeapObject(transfer_msg), addHeapObject(statechain_info), ptr0, len0, current_blockheight, fee_rate_tolerance, current_fee_rate_sats_per_byte, lockheight_init, interval); return takeObject(ret); } @@ -1156,15 +1160,15 @@ function __wbg_get_imports() { const ret = getObject(arg0).buffer; return addHeapObject(ret); }, arguments) }; + imports.wbg.__wbg_get_97b561fb56f034b5 = function() { return handleError(function (arg0, arg1) { + const ret = Reflect.get(getObject(arg0), getObject(arg1)); + return addHeapObject(ret); + }, arguments) }; imports.wbg.__wbindgen_is_function = function(arg0) { const ret = typeof(getObject(arg0)) === 'function'; _assertBoolean(ret); return ret; }; - imports.wbg.__wbg_get_97b561fb56f034b5 = function() { return handleError(function (arg0, arg1) { - const ret = Reflect.get(getObject(arg0), getObject(arg1)); - return addHeapObject(ret); - }, arguments) }; imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { const ret = debugString(getObject(arg1)); const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); diff --git a/wasm/web_pkg/debug/mercury_wasm_bg.wasm b/wasm/web_pkg/debug/mercury_wasm_bg.wasm index 13d86e2a..f53ed4ed 100644 Binary files a/wasm/web_pkg/debug/mercury_wasm_bg.wasm and b/wasm/web_pkg/debug/mercury_wasm_bg.wasm differ diff --git a/wasm/web_pkg/debug/mercury_wasm_bg.wasm.d.ts b/wasm/web_pkg/debug/mercury_wasm_bg.wasm.d.ts index e36e7657..a40f9970 100644 --- a/wasm/web_pkg/debug/mercury_wasm_bg.wasm.d.ts +++ b/wasm/web_pkg/debug/mercury_wasm_bg.wasm.d.ts @@ -41,7 +41,7 @@ export function signMessage(a: number, b: number, c: number, d: number): void; export function isEnclavePubkeyPartOfCoin(a: number, b: number, c: number): number; export function latestBackuptxPaysToUserpubkey(a: number, b: number, c: number, d: number): number; export function duplicateCoinToInitializedState(a: number, b: number, c: number): number; -export function validateSignatureScheme(a: number, b: number, c: number, d: number, e: number, f: number, g: number): number; +export function validateSignatureScheme(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number): number; export function getMockWallet(): number; export function rustsecp256k1zkp_v0_8_1_default_illegal_callback_fn(a: number, b: number): void; export function rustsecp256k1zkp_v0_8_1_default_error_callback_fn(a: number, b: number): void; diff --git a/wasm/web_pkg/release/mercury_wasm.d.ts b/wasm/web_pkg/release/mercury_wasm.d.ts index 95fe6817..b11793ae 100644 --- a/wasm/web_pkg/release/mercury_wasm.d.ts +++ b/wasm/web_pkg/release/mercury_wasm.d.ts @@ -267,12 +267,14 @@ export function duplicateCoinToInitializedState(walletJson: any, authPubkey: str * @param {any} transfer_msg * @param {any} statechain_info * @param {string} tx0_hex +* @param {number} current_blockheight * @param {number} fee_rate_tolerance * @param {number} current_fee_rate_sats_per_byte +* @param {number} lockheight_init * @param {number} interval * @returns {any} */ -export function validateSignatureScheme(transfer_msg: any, statechain_info: any, tx0_hex: string, fee_rate_tolerance: number, current_fee_rate_sats_per_byte: number, interval: number): any; +export function validateSignatureScheme(transfer_msg: any, statechain_info: any, tx0_hex: string, current_blockheight: number, fee_rate_tolerance: number, current_fee_rate_sats_per_byte: number, lockheight_init: number, interval: number): any; /** * @returns {any} */ @@ -322,7 +324,7 @@ export interface InitOutput { readonly isEnclavePubkeyPartOfCoin: (a: number, b: number, c: number) => number; readonly latestBackuptxPaysToUserpubkey: (a: number, b: number, c: number, d: number) => number; readonly duplicateCoinToInitializedState: (a: number, b: number, c: number) => number; - readonly validateSignatureScheme: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number; + readonly validateSignatureScheme: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => number; readonly getMockWallet: () => number; readonly rustsecp256k1zkp_v0_8_1_default_illegal_callback_fn: (a: number, b: number) => void; readonly rustsecp256k1zkp_v0_8_1_default_error_callback_fn: (a: number, b: number) => void; diff --git a/wasm/web_pkg/release/mercury_wasm.js b/wasm/web_pkg/release/mercury_wasm.js index 08db576d..9152e74e 100644 --- a/wasm/web_pkg/release/mercury_wasm.js +++ b/wasm/web_pkg/release/mercury_wasm.js @@ -6,6 +6,20 @@ heap.push(undefined, null, true, false); function getObject(idx) { return heap[idx]; } +let heap_next = heap.length; + +function dropObject(idx) { + if (idx < 132) return; + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} + let WASM_VECTOR_LEN = 0; let cachedUint8Memory0 = null; @@ -84,20 +98,6 @@ function getInt32Memory0() { return cachedInt32Memory0; } -let heap_next = heap.length; - -function dropObject(idx) { - if (idx < 132) return; - heap[idx] = heap_next; - heap_next = idx; -} - -function takeObject(idx) { - const ret = getObject(idx); - dropObject(idx); - return ret; -} - const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; @@ -820,15 +820,17 @@ export function duplicateCoinToInitializedState(walletJson, authPubkey) { * @param {any} transfer_msg * @param {any} statechain_info * @param {string} tx0_hex +* @param {number} current_blockheight * @param {number} fee_rate_tolerance * @param {number} current_fee_rate_sats_per_byte +* @param {number} lockheight_init * @param {number} interval * @returns {any} */ -export function validateSignatureScheme(transfer_msg, statechain_info, tx0_hex, fee_rate_tolerance, current_fee_rate_sats_per_byte, interval) { +export function validateSignatureScheme(transfer_msg, statechain_info, tx0_hex, current_blockheight, fee_rate_tolerance, current_fee_rate_sats_per_byte, lockheight_init, interval) { const ptr0 = passStringToWasm0(tx0_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len0 = WASM_VECTOR_LEN; - const ret = wasm.validateSignatureScheme(addHeapObject(transfer_msg), addHeapObject(statechain_info), ptr0, len0, fee_rate_tolerance, current_fee_rate_sats_per_byte, interval); + const ret = wasm.validateSignatureScheme(addHeapObject(transfer_msg), addHeapObject(statechain_info), ptr0, len0, current_blockheight, fee_rate_tolerance, current_fee_rate_sats_per_byte, lockheight_init, interval); return takeObject(ret); } @@ -882,6 +884,9 @@ async function __wbg_load(module, imports) { function __wbg_get_imports() { const imports = {}; imports.wbg = {}; + imports.wbg.__wbindgen_object_drop_ref = function(arg0) { + takeObject(arg0); + }; imports.wbg.__wbindgen_string_get = function(arg0, arg1) { const obj = getObject(arg1); const ret = typeof(obj) === 'string' ? obj : undefined; @@ -890,9 +895,6 @@ function __wbg_get_imports() { getInt32Memory0()[arg0 / 4 + 1] = len1; getInt32Memory0()[arg0 / 4 + 0] = ptr1; }; - imports.wbg.__wbindgen_object_drop_ref = function(arg0) { - takeObject(arg0); - }; imports.wbg.__wbindgen_boolean_get = function(arg0) { const v = getObject(arg0); const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2; @@ -919,6 +921,14 @@ function __wbg_get_imports() { const ret = new Error(getStringFromWasm0(arg0, arg1)); return addHeapObject(ret); }; + imports.wbg.__wbindgen_number_new = function(arg0) { + const ret = arg0; + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_string_new = function(arg0, arg1) { + const ret = getStringFromWasm0(arg0, arg1); + return addHeapObject(ret); + }; imports.wbg.__wbindgen_jsval_loose_eq = function(arg0, arg1) { const ret = getObject(arg0) == getObject(arg1); return ret; @@ -929,18 +939,10 @@ function __wbg_get_imports() { getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret; getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret); }; - imports.wbg.__wbindgen_number_new = function(arg0) { - const ret = arg0; - return addHeapObject(ret); - }; imports.wbg.__wbindgen_object_clone_ref = function(arg0) { const ret = getObject(arg0); return addHeapObject(ret); }; - imports.wbg.__wbindgen_string_new = function(arg0, arg1) { - const ret = getStringFromWasm0(arg0, arg1); - return addHeapObject(ret); - }; imports.wbg.__wbg_getwithrefkey_15c62c2b8546208d = function(arg0, arg1) { const ret = getObject(arg0)[getObject(arg1)]; return addHeapObject(ret); @@ -964,10 +966,6 @@ function __wbg_get_imports() { const ret = getObject(arg0).node; return addHeapObject(ret); }; - imports.wbg.__wbg_msCrypto_0b84745e9245cdf6 = function(arg0) { - const ret = getObject(arg0).msCrypto; - return addHeapObject(ret); - }; imports.wbg.__wbg_require_94a9da52636aacbf = function() { return handleError(function () { const ret = module.require; return addHeapObject(ret); @@ -976,6 +974,10 @@ function __wbg_get_imports() { const ret = typeof(getObject(arg0)) === 'function'; return ret; }; + imports.wbg.__wbg_msCrypto_0b84745e9245cdf6 = function(arg0) { + const ret = getObject(arg0).msCrypto; + return addHeapObject(ret); + }; imports.wbg.__wbg_randomFillSync_290977693942bf03 = function() { return handleError(function (arg0, arg1) { getObject(arg0).randomFillSync(takeObject(arg1)); }, arguments) }; diff --git a/wasm/web_pkg/release/mercury_wasm_bg.wasm b/wasm/web_pkg/release/mercury_wasm_bg.wasm index a7bb04af..5cf0f170 100644 Binary files a/wasm/web_pkg/release/mercury_wasm_bg.wasm and b/wasm/web_pkg/release/mercury_wasm_bg.wasm differ diff --git a/wasm/web_pkg/release/mercury_wasm_bg.wasm.d.ts b/wasm/web_pkg/release/mercury_wasm_bg.wasm.d.ts index e36e7657..a40f9970 100644 --- a/wasm/web_pkg/release/mercury_wasm_bg.wasm.d.ts +++ b/wasm/web_pkg/release/mercury_wasm_bg.wasm.d.ts @@ -41,7 +41,7 @@ export function signMessage(a: number, b: number, c: number, d: number): void; export function isEnclavePubkeyPartOfCoin(a: number, b: number, c: number): number; export function latestBackuptxPaysToUserpubkey(a: number, b: number, c: number, d: number): number; export function duplicateCoinToInitializedState(a: number, b: number, c: number): number; -export function validateSignatureScheme(a: number, b: number, c: number, d: number, e: number, f: number, g: number): number; +export function validateSignatureScheme(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number): number; export function getMockWallet(): number; export function rustsecp256k1zkp_v0_8_1_default_illegal_callback_fn(a: number, b: number): void; export function rustsecp256k1zkp_v0_8_1_default_error_callback_fn(a: number, b: number): void;