From 1bf71e5b4d3fa866366f80f126714de091251bbb Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Wed, 13 Dec 2023 15:43:40 -0700 Subject: [PATCH 1/9] RBF initial implementation. --- .vscode/settings.json | 2 +- lib/web/bitcoin.ts | 31 +++++++++++++------ src/bitcoin.rs | 50 +++++++++++++++++++++++++++++- src/web.rs | 72 ++++++++++++++++++++++++++++++------------- 4 files changed, 121 insertions(+), 34 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 49add19b..5827acd4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,8 +5,8 @@ "files.autoSave": "onFocusChange", "files.insertFinalNewline": true, "files.trimTrailingWhitespace": true, - "rust-analyzer.cargo.features": "all", // Enable only for desktop "rust-analyzer.check.allTargets": true, + "rust-analyzer.cargo.features": "all", // Enable only for desktop // "rust-analyzer.cargo.target": "wasm32-unknown-unknown", // Enable only for web // "rust-analyzer.check.noDefaultFeatures": true, // Enable for web // "rust-analyzer.runnables.extraArgs": ["--release"], // Enable for web diff --git a/lib/web/bitcoin.ts b/lib/web/bitcoin.ts index 2fc3f892..cb98ff83 100644 --- a/lib/web/bitcoin.ts +++ b/lib/web/bitcoin.ts @@ -57,16 +57,6 @@ export const sendSats = async ( await BMC.send_sats(descriptor, changeDescriptor, address, amount, feeRate) ); -export const drainWallet = async ( - destination: string, - descriptor: string, - changeDescriptor?: string, - feeRate?: number -): Promise => - JSON.parse( - await BMC.drain_wallet(destination, descriptor, changeDescriptor, feeRate) - ); - export const fundVault = async ( descriptor: string, changeDescriptor: string, @@ -92,6 +82,27 @@ export const getAssetsVault = async ( await BMC.get_assets_vault(rgbAssetsDescriptorXpub, rgbUdasDescriptorXpub) ); +export const drainWallet = async ( + destination: string, + descriptor: string, + changeDescriptor?: string, + feeRate?: number +): Promise => + JSON.parse( + await BMC.drain_wallet(destination, descriptor, changeDescriptor, feeRate) + ); + +export const bumpFee = async ( + txid: string, + feeRate: number, + descriptor: string, + changeDescriptor: string, + broadcast: boolean +): Promise => + JSON.parse( + await BMC.bump_fee(txid, feeRate, descriptor, changeDescriptor, broadcast) + ); + // Core type interfaces based on structs defined within the bitmask-core Rust crate: // https://github.com/diba-io/bitmask-core/blob/development/src/structs.rs diff --git a/src/bitcoin.rs b/src/bitcoin.rs index 1b0c955a..c734d3ae 100644 --- a/src/bitcoin.rs +++ b/src/bitcoin.rs @@ -5,7 +5,7 @@ use ::psbt::Psbt; use amplify::hex::ToHex; use argon2::Argon2; use bdk::{wallet::AddressIndex, FeeRate, LocalUtxo, SignOptions, TransactionDetails}; -use bitcoin::{consensus::encode, psbt::PartiallySignedTransaction}; +use bitcoin::{consensus::encode, psbt::PartiallySignedTransaction, Txid}; use rand::{rngs::StdRng, Rng, SeedableRng}; use serde_encrypt::{ serialize::impls::BincodeSerializer, shared_key::SharedKey, traits::SerdeEncryptSharedKey, @@ -113,6 +113,9 @@ pub enum BitcoinError { /// PSBT decode error #[error(transparent)] BitcoinPsbtDecodeError(#[from] bitcoin::consensus::encode::Error), + /// Txid parse error + #[error(transparent)] + TxidParseError(#[from] bitcoin::hashes::hex::Error), } /// Bitcoin Wallet Operations @@ -598,3 +601,48 @@ pub async fn drain_wallet( Err(BitcoinError::DrainWalletNoTxDetails) } } + +pub async fn bump_fee( + txid: String, + fee_rate: f32, + descriptor: &SecretString, + change_descriptor: &SecretString, + broadcast: bool, +) -> Result { + let txid = Txid::from_str(&txid)?; + + let wallet = get_wallet(descriptor, Some(change_descriptor)).await?; + sync_wallet(&wallet).await?; + + let (mut psbt, details) = { + let wallet_lock = wallet.lock().await; + let mut builder = wallet_lock.build_fee_bump(txid)?; + builder.fee_rate(FeeRate::from_sat_per_vb(fee_rate)); + builder.finish()? + }; + + if broadcast { + let _finalized = wallet + .lock() + .await + .sign(&mut psbt, SignOptions::default())?; + let tx = psbt.extract_tx(); + let blockchain = get_blockchain().await; + blockchain.broadcast(&tx).await?; + + let sent = tx.output.iter().fold(0, |sum, output| output.value + sum); + + let details = TransactionDetails { + txid: tx.txid(), + transaction: Some(tx), + received: 0, + sent, + fee: details.fee, + confirmation_time: None, + }; + + Ok(details) + } else { + Ok(details) + } +} diff --git a/src/web.rs b/src/web.rs index de2d03fd..0ebdfb93 100644 --- a/src/web.rs +++ b/src/web.rs @@ -294,21 +294,21 @@ pub mod bitcoin { } #[wasm_bindgen] - pub fn drain_wallet( - destination: String, + pub fn fund_vault( descriptor: String, - change_descriptor: Option, + change_descriptor: String, + asset_address: String, + uda_address: String, fee_rate: Option, ) -> Promise { set_panic_hook(); future_to_promise(async move { - let change_descriptor = change_descriptor.map(SecretString); - - match crate::bitcoin::drain_wallet( - &destination, + match crate::bitcoin::fund_vault( &SecretString(descriptor), - change_descriptor.as_ref(), + &SecretString(change_descriptor), + &asset_address, + &uda_address, fee_rate, ) .await @@ -322,21 +322,43 @@ pub mod bitcoin { } #[wasm_bindgen] - pub fn fund_vault( + pub fn get_assets_vault( + rgb_assets_descriptor_xpub: String, + rgb_udas_descriptor_xpub: String, + ) -> Promise { + set_panic_hook(); + + future_to_promise(async move { + match crate::bitcoin::get_assets_vault( + &SecretString(rgb_assets_descriptor_xpub), + &SecretString(rgb_udas_descriptor_xpub), + ) + .await + { + Ok(result) => Ok(JsValue::from_string( + serde_json::to_string(&result).unwrap(), + )), + Err(err) => Err(JsValue::from_string(err.to_string())), + } + }) + } + + #[wasm_bindgen] + pub fn drain_wallet( + destination: String, descriptor: String, - change_descriptor: String, - asset_address_1: String, - uda_address_1: String, + change_descriptor: Option, fee_rate: Option, ) -> Promise { set_panic_hook(); future_to_promise(async move { - match crate::bitcoin::fund_vault( + let change_descriptor = change_descriptor.map(SecretString); + + match crate::bitcoin::drain_wallet( + &destination, &SecretString(descriptor), - &SecretString(change_descriptor), - &asset_address_1, - &uda_address_1, + change_descriptor.as_ref(), fee_rate, ) .await @@ -350,16 +372,22 @@ pub mod bitcoin { } #[wasm_bindgen] - pub fn get_assets_vault( - rgb_assets_descriptor_xpub: String, - rgb_udas_descriptor_xpub: String, + pub fn bump_fee( + txid: String, + fee_rate: f32, + descriptor: String, + change_descriptor: String, + broadcast: bool, ) -> Promise { set_panic_hook(); future_to_promise(async move { - match crate::bitcoin::get_assets_vault( - &SecretString(rgb_assets_descriptor_xpub), - &SecretString(rgb_udas_descriptor_xpub), + match crate::bitcoin::bump_fee( + txid, + fee_rate, + &SecretString(descriptor), + &SecretString(change_descriptor), + broadcast, ) .await { From 22981bb22b35dda4b3b351c62c4f0148db893473 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Thu, 21 Dec 2023 13:14:37 -0700 Subject: [PATCH 2/9] Return tx vsize and fee rate. --- lib/web/bitcoin.ts | 6 ++++++ lib/web/package-lock.json | 4 ++-- lib/web/package.json | 2 +- src/bitcoin.rs | 34 ++++++++++++++++++++++++++++------ src/structs.rs | 8 ++++++++ tests/rgb/integration/drain.rs | 4 ++-- 6 files changed, 47 insertions(+), 11 deletions(-) diff --git a/lib/web/bitcoin.ts b/lib/web/bitcoin.ts index 2fc3f892..4245911f 100644 --- a/lib/web/bitcoin.ts +++ b/lib/web/bitcoin.ts @@ -162,6 +162,12 @@ export interface TransactionDetails extends Transaction { } export interface TransactionData { + details: TransactionDataDetails; + vsize: number; + feeRate: number; +} + +export interface TransactionDataDetails { transaction?: Transaction; txid: string; received: number; diff --git a/lib/web/package-lock.json b/lib/web/package-lock.json index 88fa2a3f..bcacd74c 100644 --- a/lib/web/package-lock.json +++ b/lib/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitmask-core", - "version": "0.7.0-beta.8", + "version": "0.7.0-beta.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitmask-core", - "version": "0.7.0-beta.8", + "version": "0.7.0-beta.10", "license": "MIT", "devDependencies": { "@types/node": "^20.8.2", diff --git a/lib/web/package.json b/lib/web/package.json index fecd44f0..d6d90191 100644 --- a/lib/web/package.json +++ b/lib/web/package.json @@ -6,7 +6,7 @@ "Francisco Calderón " ], "description": "Core functionality for the BitMask wallet", - "version": "0.7.0-beta.8", + "version": "0.7.0-beta.10", "license": "MIT", "repository": { "type": "git", diff --git a/src/bitcoin.rs b/src/bitcoin.rs index 1b0c955a..7439b768 100644 --- a/src/bitcoin.rs +++ b/src/bitcoin.rs @@ -40,7 +40,7 @@ use crate::{ structs::{ DecryptedWalletData, EncryptedWalletDataV04, FundVaultDetails, PublishPsbtRequest, PublishedPsbtResponse, SatsInvoice, SecretString, SignPsbtRequest, SignedPsbtResponse, - WalletData, WalletTransaction, + TransactionData, WalletData, WalletTransaction, }, trace, }; @@ -341,13 +341,13 @@ pub async fn send_sats( destination: &str, // bip21 uri or address amount: u64, fee_rate: Option, -) -> Result { +) -> Result { use payjoin::UriExt; let wallet = get_wallet(descriptor, Some(change_descriptor)).await?; let fee_rate = fee_rate.map(FeeRate::from_sat_per_vb); - let transaction = match payjoin::Uri::try_from(destination) { + let details = match payjoin::Uri::try_from(destination) { Ok(uri) => { let address = uri.address.clone(); validate_address(&address).await?; @@ -370,7 +370,18 @@ pub async fn send_sats( } }; - Ok(transaction) + let vsize = details + .transaction + .as_ref() + .expect("transaction exists") + .vsize(); + let fee_rate = details.fee.expect("fee is present on tx") as f32 / vsize as f32; + + Ok(TransactionData { + details, + vsize, + fee_rate, + }) } pub async fn fund_vault( @@ -537,7 +548,7 @@ pub async fn drain_wallet( descriptor: &SecretString, change_descriptor: Option<&SecretString>, fee_rate: Option, -) -> Result { +) -> Result { let address = Address::from_str(destination)?; validate_address(&address).await?; debug!(format!("Create drain wallet tx to: {address:#?}")); @@ -593,7 +604,18 @@ pub async fn drain_wallet( "Drain wallet transaction submitted with details: {details:#?}" )); - Ok(details) + let vsize = details + .transaction + .as_ref() + .expect("transaction exists") + .vsize(); + let fee_rate = details.fee.expect("fee is present on tx") as f32 / vsize as f32; + + Ok(TransactionData { + details, + vsize, + fee_rate, + }) } else { Err(BitcoinError::DrainWalletNoTxDetails) } diff --git a/src/structs.rs b/src/structs.rs index 9aa6cd0d..c40baeb0 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -42,6 +42,14 @@ pub struct WalletTransaction { pub confirmation_time: Option, } +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct TransactionData { + pub details: TransactionDetails, + pub vsize: usize, + pub fee_rate: f32, +} + #[derive(Serialize, Deserialize, Clone, Debug, Zeroize, ZeroizeOnDrop, Display, Default)] #[display(inner)] pub struct SecretString(pub String); diff --git a/tests/rgb/integration/drain.rs b/tests/rgb/integration/drain.rs index 305c37ac..b5626197 100644 --- a/tests/rgb/integration/drain.rs +++ b/tests/rgb/integration/drain.rs @@ -49,11 +49,11 @@ pub async fn drain() -> Result<()> { .await?; assert_eq!( - drain_wallet_details.received, 0, + drain_wallet_details.details.received, 0, "received no funds in this transaction" ); assert_eq!( - drain_wallet_details.sent + drain_wallet_details.fee.expect("fee present"), + drain_wallet_details.details.sent + drain_wallet_details.details.fee.expect("fee present"), 30_000_000, "received 0.3 tBTC" ); From 1e3c00c301052b12334a995956b2d459809b6549 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Thu, 21 Dec 2023 13:35:00 -0700 Subject: [PATCH 3/9] Add addl tx data to RBF. Fix test. --- src/bitcoin.rs | 25 +++++++++++++++++++++---- tests/web_wallet.rs | 4 ++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/bitcoin.rs b/src/bitcoin.rs index f4265bf8..5f1b1fbb 100644 --- a/src/bitcoin.rs +++ b/src/bitcoin.rs @@ -630,7 +630,7 @@ pub async fn bump_fee( descriptor: &SecretString, change_descriptor: &SecretString, broadcast: bool, -) -> Result { +) -> Result { let txid = Txid::from_str(&txid)?; let wallet = get_wallet(descriptor, Some(change_descriptor)).await?; @@ -643,6 +643,12 @@ pub async fn bump_fee( builder.finish()? }; + let vsize = details + .transaction + .as_ref() + .expect("transaction exists") + .vsize(); + if broadcast { let _finalized = wallet .lock() @@ -654,8 +660,11 @@ pub async fn bump_fee( let sent = tx.output.iter().fold(0, |sum, output| output.value + sum); + let txid = tx.txid(); + let vsize = tx.vsize(); + let details = TransactionDetails { - txid: tx.txid(), + txid, transaction: Some(tx), received: 0, sent, @@ -663,8 +672,16 @@ pub async fn bump_fee( confirmation_time: None, }; - Ok(details) + Ok(TransactionData { + details, + vsize, + fee_rate, + }) } else { - Ok(details) + Ok(TransactionData { + details, + vsize, + fee_rate, + }) } } diff --git a/tests/web_wallet.rs b/tests/web_wallet.rs index 48d4a02c..3beea03d 100644 --- a/tests/web_wallet.rs +++ b/tests/web_wallet.rs @@ -1,7 +1,7 @@ #![cfg(target_arch = "wasm32")] use bitmask_core::{ debug, info, - structs::{DecryptedWalletData, SecretString, TransactionDetails, WalletData}, + structs::{DecryptedWalletData, SecretString, TransactionData, WalletData}, web::{ bitcoin::{ decrypt_wallet, encrypt_wallet, get_wallet_data, hash_password, new_wallet, send_sats, @@ -186,7 +186,7 @@ async fn import_test_wallet() { .await; info!("Parse tx_details"); - let tx_data: TransactionDetails = json_parse(&tx_details); + let tx_data: TransactionData = json_parse(&tx_details); assert!( tx_data.confirmation_time.is_none(), From b4880a896fb7105fad200e1f9c1feb2374cd492e Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Thu, 21 Dec 2023 13:38:13 -0700 Subject: [PATCH 4/9] Fix web test. --- tests/web_wallet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/web_wallet.rs b/tests/web_wallet.rs index 3beea03d..06ef5c0d 100644 --- a/tests/web_wallet.rs +++ b/tests/web_wallet.rs @@ -189,7 +189,7 @@ async fn import_test_wallet() { let tx_data: TransactionData = json_parse(&tx_details); assert!( - tx_data.confirmation_time.is_none(), + tx_data.details.confirmation_time.is_none(), "latest transaction hasn't been confirmed yet" ); } From 211ad96e76488a8d071c343db636cac91f8573b2 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Thu, 21 Dec 2023 14:01:34 -0700 Subject: [PATCH 5/9] Include vsize and feeRate from getWalletData() --- lib/web/bitcoin.ts | 2 ++ src/bitcoin.rs | 21 ++++++++++++++------- src/structs.rs | 4 +++- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/web/bitcoin.ts b/lib/web/bitcoin.ts index 931e176e..52790887 100644 --- a/lib/web/bitcoin.ts +++ b/lib/web/bitcoin.ts @@ -200,6 +200,8 @@ export interface WalletTransaction { fee: number; confirmed: boolean; confirmationTime: ConfirmationTime; + vsize: number; + feeRate: number; } export interface WalletBalance { diff --git a/src/bitcoin.rs b/src/bitcoin.rs index 5f1b1fbb..4a71c4c7 100644 --- a/src/bitcoin.rs +++ b/src/bitcoin.rs @@ -286,13 +286,20 @@ pub async fn get_wallet_data( let transactions: Vec = transactions .into_iter() - .map(|tx| WalletTransaction { - txid: tx.txid, - received: tx.received, - sent: tx.sent, - fee: tx.fee, - confirmed: tx.confirmation_time.is_some(), - confirmation_time: tx.confirmation_time, + .map(|tx| { + let vsize = tx.transaction.expect("transaction exists").vsize(); + let fee_rate = tx.fee.expect("tx fee exists") as f32 / vsize as f32; + + WalletTransaction { + txid: tx.txid, + received: tx.received, + sent: tx.sent, + fee: tx.fee, + confirmed: tx.confirmation_time.is_some(), + confirmation_time: tx.confirmation_time, + vsize, + fee_rate, + } }) .collect(); diff --git a/src/structs.rs b/src/structs.rs index c40baeb0..9fc03f4f 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -31,7 +31,7 @@ pub struct WalletData { pub utxos: Vec, } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct WalletTransaction { pub txid: Txid, @@ -40,6 +40,8 @@ pub struct WalletTransaction { pub fee: Option, pub confirmed: bool, pub confirmation_time: Option, + pub vsize: usize, + pub fee_rate: f32, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] From 7b002a367e6670962b34c562e21ffacc9dc0b2c4 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Fri, 22 Dec 2023 15:03:13 -0700 Subject: [PATCH 6/9] RBF fixes. --- lib/web/bitcoin.ts | 2 +- src/bitcoin.rs | 85 +++++++++++++++++++++------------------------- 2 files changed, 40 insertions(+), 47 deletions(-) diff --git a/lib/web/bitcoin.ts b/lib/web/bitcoin.ts index 52790887..23a30d01 100644 --- a/lib/web/bitcoin.ts +++ b/lib/web/bitcoin.ts @@ -134,7 +134,7 @@ export interface Vault { public: PublicWalletData; } -export interface Transaction { +export interface Transaction extends WalletTransaction { amount: number; asset?: string; assetType: string; diff --git a/src/bitcoin.rs b/src/bitcoin.rs index 4a71c4c7..eec6eeff 100644 --- a/src/bitcoin.rs +++ b/src/bitcoin.rs @@ -273,7 +273,7 @@ pub async fn get_wallet_data( let mut transactions = wallet .lock() .await - .list_transactions(false) + .list_transactions(true) .unwrap_or_default(); trace!(format!("transactions: {transactions:#?}")); @@ -287,7 +287,10 @@ pub async fn get_wallet_data( let transactions: Vec = transactions .into_iter() .map(|tx| { - let vsize = tx.transaction.expect("transaction exists").vsize(); + let vsize = match &tx.transaction { + Some(tx_details) => tx_details.vsize(), + None => 1, + }; let fee_rate = tx.fee.expect("tx fee exists") as f32 / vsize as f32; WalletTransaction { @@ -380,11 +383,10 @@ pub async fn send_sats( } }; - let vsize = details - .transaction - .as_ref() - .expect("transaction exists") - .vsize(); + let vsize = match &details.transaction { + Some(tx_details) => tx_details.vsize(), + None => 1, + }; let fee_rate = details.fee.expect("fee is present on tx") as f32 / vsize as f32; Ok(TransactionData { @@ -614,11 +616,10 @@ pub async fn drain_wallet( "Drain wallet transaction submitted with details: {details:#?}" )); - let vsize = details - .transaction - .as_ref() - .expect("transaction exists") - .vsize(); + let vsize = match &details.transaction { + Some(tx_details) => tx_details.vsize(), + None => 1, + }; let fee_rate = details.fee.expect("fee is present on tx") as f32 / vsize as f32; Ok(TransactionData { @@ -641,7 +642,10 @@ pub async fn bump_fee( let txid = Txid::from_str(&txid)?; let wallet = get_wallet(descriptor, Some(change_descriptor)).await?; - sync_wallet(&wallet).await?; + + if broadcast { + sync_wallet(&wallet).await?; + } let (mut psbt, details) = { let wallet_lock = wallet.lock().await; @@ -650,45 +654,34 @@ pub async fn bump_fee( builder.finish()? }; - let vsize = details - .transaction - .as_ref() - .expect("transaction exists") - .vsize(); + let _finalized = wallet + .lock() + .await + .sign(&mut psbt, SignOptions::default())?; + let tx = psbt.extract_tx(); if broadcast { - let _finalized = wallet - .lock() - .await - .sign(&mut psbt, SignOptions::default())?; - let tx = psbt.extract_tx(); let blockchain = get_blockchain().await; blockchain.broadcast(&tx).await?; + } - let sent = tx.output.iter().fold(0, |sum, output| output.value + sum); + let sent = tx.output.iter().fold(0, |sum, output| output.value + sum); - let txid = tx.txid(); - let vsize = tx.vsize(); + let txid = tx.txid(); + let vsize = tx.vsize(); - let details = TransactionDetails { - txid, - transaction: Some(tx), - received: 0, - sent, - fee: details.fee, - confirmation_time: None, - }; + let details = TransactionDetails { + txid, + transaction: Some(tx), + received: 0, + sent, + fee: details.fee, + confirmation_time: None, + }; - Ok(TransactionData { - details, - vsize, - fee_rate, - }) - } else { - Ok(TransactionData { - details, - vsize, - fee_rate, - }) - } + Ok(TransactionData { + details, + vsize, + fee_rate, + }) } From 86f7928b8a6619b4b10c88036dc27008b31df927 Mon Sep 17 00:00:00 2001 From: Jose D Robles Date: Tue, 26 Dec 2023 20:18:04 +0100 Subject: [PATCH 7/9] Fix RgbSwapStrategy interface (#444) --- lib/web/rgb.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/web/rgb.ts b/lib/web/rgb.ts index d8bb10d0..03a95ffe 100644 --- a/lib/web/rgb.ts +++ b/lib/web/rgb.ts @@ -812,10 +812,10 @@ export interface RgbOfferRequest { expireAt?: number; } -export interface RgbSwapStrategy { - auction?: string, - p2p?: string, - hotswap?: string, +export enum RgbSwapStrategy { + Auction = "auction", + P2P = "p2p", + HotSwap = "hotswap", } export interface RgbAuctionOfferRequest { signKeys: string[], From 6f065f06edbaee5645b70b1dbb2a2360bb3af439 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Wed, 27 Dec 2023 05:20:29 -0700 Subject: [PATCH 8/9] Bump fee test... --- tests/rgb/integration/rbf.rs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/tests/rgb/integration/rbf.rs b/tests/rgb/integration/rbf.rs index d8328cfe..12fca111 100644 --- a/tests/rgb/integration/rbf.rs +++ b/tests/rgb/integration/rbf.rs @@ -9,8 +9,9 @@ use bdk::{ SignOptions, SyncOptions, }; use bitcoin::{secp256k1::Secp256k1, Network, Txid}; +use bitcoin_hashes::hex::{Case, DisplayHex}; use bitmask_core::{ - bitcoin::{get_blockchain, new_mnemonic, sign_and_publish_psbt_file}, + bitcoin::{bump_fee, get_blockchain, new_mnemonic, sign_and_publish_psbt_file}, rgb::{get_contract, structs::ContractAmount}, structs::{PsbtFeeRequest, PsbtResponse, SecretString, SignPsbtRequest}, }; @@ -131,7 +132,6 @@ pub async fn create_simple_rbf_bitcoin_transfer() -> Result<()> { Ok(()) } -#[ignore = "No longer necessary, this is a simple test to rbf with bdk"] #[tokio::test] pub async fn create_bdk_rbf_transaction() -> Result<()> { // 1. Initial Setup @@ -149,6 +149,7 @@ pub async fn create_bdk_rbf_transaction() -> Result<()> { let user_address = user_wallet_data.get_address(AddressIndex::New)?; send_some_coins(&user_address.address.to_string(), "1").await; + // 2. Send sats user_wallet_data .sync(&blockchain, SyncOptions::default()) .await?; @@ -186,16 +187,14 @@ pub async fn create_bdk_rbf_transaction() -> Result<()> { .map(|u| u.previous_output.to_string()) .collect(); - let (mut psbt, ..) = { - let mut builder = user_wallet_data.build_fee_bump(tx.txid())?; - builder.fee_rate(bdk::FeeRate::from_sat_per_vb(5.0)); - builder.finish()? - }; - - let _ = user_wallet_data.sign(&mut psbt, SignOptions::default())?; - let tx = psbt.extract_tx(); - let blockchain = get_blockchain().await; - blockchain.broadcast(&tx).await?; + bump_fee( + tx.txid().to_hex_string(Case::Lower), + 5.0, + &SecretString(user_keys.private.btc_descriptor_xprv.to_owned()), + &SecretString(user_keys.private.btc_change_descriptor_xprv.to_owned()), + true, + ) + .await?; user_wallet_data .sync(&blockchain, SyncOptions::default()) @@ -212,8 +211,8 @@ pub async fn create_bdk_rbf_transaction() -> Result<()> { .map(|u| u.previous_output.to_string()) .collect(); - // println!("{:#?}", tx_1_utxos); - // println!("{:#?}", tx_2_utxos); + println!("tx 1 utxos: {:#?}", tx_1_utxos); + println!("tx 2 utxos: {:#?}", tx_2_utxos); assert_eq!(tx_1_utxos, tx_2_utxos); Ok(()) From 4e932a9ce8d4f7f12b8f8a4f3a51f0afa5e6862d Mon Sep 17 00:00:00 2001 From: Armando Dutra Date: Wed, 27 Dec 2023 18:24:04 -0300 Subject: [PATCH 9/9] fix: bump fee --- lib/web/bitcoin.ts | 4 ++-- src/bitcoin.rs | 4 ++-- src/web.rs | 5 +++-- tests/rgb/integration/rbf.rs | 18 +++++------------- 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/lib/web/bitcoin.ts b/lib/web/bitcoin.ts index 23a30d01..21401d9a 100644 --- a/lib/web/bitcoin.ts +++ b/lib/web/bitcoin.ts @@ -95,9 +95,9 @@ export const drainWallet = async ( export const bumpFee = async ( txid: string, feeRate: number, + broadcast: boolean, descriptor: string, - changeDescriptor: string, - broadcast: boolean + changeDescriptor?: string, ): Promise => JSON.parse( await BMC.bump_fee(txid, feeRate, descriptor, changeDescriptor, broadcast) diff --git a/src/bitcoin.rs b/src/bitcoin.rs index eec6eeff..7843fe9f 100644 --- a/src/bitcoin.rs +++ b/src/bitcoin.rs @@ -636,12 +636,12 @@ pub async fn bump_fee( txid: String, fee_rate: f32, descriptor: &SecretString, - change_descriptor: &SecretString, + change_descriptor: Option<&SecretString>, broadcast: bool, ) -> Result { let txid = Txid::from_str(&txid)?; - let wallet = get_wallet(descriptor, Some(change_descriptor)).await?; + let wallet = get_wallet(descriptor, change_descriptor).await?; if broadcast { sync_wallet(&wallet).await?; diff --git a/src/web.rs b/src/web.rs index 55163375..c4c2f9ee 100644 --- a/src/web.rs +++ b/src/web.rs @@ -377,17 +377,18 @@ pub mod bitcoin { txid: String, fee_rate: f32, descriptor: String, - change_descriptor: String, + change_descriptor: Option, broadcast: bool, ) -> Promise { set_panic_hook(); future_to_promise(async move { + let change_descriptor = change_descriptor.map(SecretString); match crate::bitcoin::bump_fee( txid, fee_rate, &SecretString(descriptor), - &SecretString(change_descriptor), + change_descriptor.as_ref(), broadcast, ) .await diff --git a/tests/rgb/integration/rbf.rs b/tests/rgb/integration/rbf.rs index 12fca111..9665631a 100644 --- a/tests/rgb/integration/rbf.rs +++ b/tests/rgb/integration/rbf.rs @@ -1,6 +1,4 @@ #![cfg(not(target_arch = "wasm32"))] -use std::str::FromStr; - use anyhow::Result; use bdk::{ database::MemoryDatabase, @@ -9,12 +7,12 @@ use bdk::{ SignOptions, SyncOptions, }; use bitcoin::{secp256k1::Secp256k1, Network, Txid}; -use bitcoin_hashes::hex::{Case, DisplayHex}; use bitmask_core::{ bitcoin::{bump_fee, get_blockchain, new_mnemonic, sign_and_publish_psbt_file}, rgb::{get_contract, structs::ContractAmount}, structs::{PsbtFeeRequest, PsbtResponse, SecretString, SignPsbtRequest}, }; +use std::str::FromStr; use crate::rgb::integration::utils::{ create_new_psbt_v2, issuer_issue_contract_v2, send_some_coins, UtxoFilter, @@ -165,10 +163,8 @@ pub async fn create_bdk_rbf_transaction() -> Result<()> { let (mut psbt, _) = builder.finish()?; let _ = user_wallet_data.sign(&mut psbt, SignOptions::default())?; - // println!("{:#?}", signed); let tx = psbt.extract_tx(); - blockchain.broadcast(&tx).await?; user_wallet_data @@ -176,8 +172,6 @@ pub async fn create_bdk_rbf_transaction() -> Result<()> { .await?; let txs = user_wallet_data.list_transactions(false)?; - // println!("{:#?}", txs); - assert_eq!(2, txs.len()); let tx_1_utxos: Vec = tx @@ -188,10 +182,10 @@ pub async fn create_bdk_rbf_transaction() -> Result<()> { .collect(); bump_fee( - tx.txid().to_hex_string(Case::Lower), + tx.txid().to_string(), 5.0, &SecretString(user_keys.private.btc_descriptor_xprv.to_owned()), - &SecretString(user_keys.private.btc_change_descriptor_xprv.to_owned()), + None, true, ) .await?; @@ -201,8 +195,6 @@ pub async fn create_bdk_rbf_transaction() -> Result<()> { .await?; let txs = user_wallet_data.list_transactions(false)?; - // println!("{:#?}", txs); - assert_eq!(2, txs.len()); let tx_2_utxos: Vec = tx @@ -211,8 +203,8 @@ pub async fn create_bdk_rbf_transaction() -> Result<()> { .map(|u| u.previous_output.to_string()) .collect(); - println!("tx 1 utxos: {:#?}", tx_1_utxos); - println!("tx 2 utxos: {:#?}", tx_2_utxos); + // println!("tx 1 utxos: {:#?}", tx_1_utxos); + // println!("tx 2 utxos: {:#?}", tx_2_utxos); assert_eq!(tx_1_utxos, tx_2_utxos); Ok(())