From 5e0cfc79af1aa68b17de90c7ef65a3d66cbf8ebb Mon Sep 17 00:00:00 2001 From: raph Date: Fri, 22 Mar 2024 21:09:51 +0100 Subject: [PATCH] Fix redundant locking (#3342) --- crates/test-bitcoincore-rpc/src/server.rs | 2 +- src/subcommand/wallet/mint.rs | 10 +-- src/subcommand/wallet/send.rs | 40 +---------- src/wallet.rs | 29 ++++++++ tests/wallet/mint.rs | 87 +++++++++++++++++++++++ 5 files changed, 120 insertions(+), 48 deletions(-) diff --git a/crates/test-bitcoincore-rpc/src/server.rs b/crates/test-bitcoincore-rpc/src/server.rs index c1bbbd34fe..a14ee1c438 100644 --- a/crates/test-bitcoincore-rpc/src/server.rs +++ b/crates/test-bitcoincore-rpc/src/server.rs @@ -827,7 +827,7 @@ impl Api for Server { txid: output.txid, }; assert!(state.utxos.contains_key(&output)); - state.locked.insert(output); + assert!(state.locked.insert(output)); } Ok(true) diff --git a/src/subcommand/wallet/mint.rs b/src/subcommand/wallet/mint.rs index 38f80ab7d3..3c22c6579a 100644 --- a/src/subcommand/wallet/mint.rs +++ b/src/subcommand/wallet/mint.rs @@ -72,15 +72,7 @@ impl Mint { ], }; - let inscriptions = wallet - .inscriptions() - .keys() - .map(|satpoint| satpoint.outpoint) - .collect::>(); - - if !bitcoin_client.lock_unspent(&inscriptions)? { - bail!("failed to lock UTXOs"); - } + wallet.lock_non_cardinal_outputs()?; let unsigned_transaction = fund_raw_transaction(bitcoin_client, self.fee_rate, &unfunded_transaction)?; diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index 4ffe847651..6c5d40b926 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -127,43 +127,13 @@ impl Send { }))) } - fn lock_non_cardinal_outputs( - bitcoin_client: &Client, - inscriptions: &BTreeMap>, - runic_outputs: &BTreeSet, - unspent_outputs: &BTreeMap, - ) -> Result { - let all_inscription_outputs = inscriptions - .keys() - .map(|satpoint| satpoint.outpoint) - .collect::>(); - - let locked_outputs = unspent_outputs - .keys() - .filter(|utxo| all_inscription_outputs.contains(utxo)) - .chain(runic_outputs.iter()) - .cloned() - .collect::>(); - - if !bitcoin_client.lock_unspent(&locked_outputs)? { - bail!("failed to lock UTXOs"); - } - - Ok(()) - } - fn create_unsigned_send_amount_transaction( wallet: &Wallet, destination: Address, amount: Amount, fee_rate: FeeRate, ) -> Result { - Self::lock_non_cardinal_outputs( - wallet.bitcoin_client(), - wallet.inscriptions(), - &wallet.get_runic_outputs()?, - wallet.utxos(), - )?; + wallet.lock_non_cardinal_outputs()?; let unfunded_transaction = Transaction { version: 2, @@ -243,17 +213,11 @@ impl Send { "sending runes with `ord send` requires index created with `--index-runes` flag", ); - let unspent_outputs = wallet.utxos(); let inscriptions = wallet.inscriptions(); let runic_outputs = wallet.get_runic_outputs()?; let bitcoin_client = wallet.bitcoin_client(); - Self::lock_non_cardinal_outputs( - bitcoin_client, - inscriptions, - &runic_outputs, - unspent_outputs, - )?; + wallet.lock_non_cardinal_outputs()?; let (id, entry, _parent) = wallet .get_rune(spaced_rune.rune)? diff --git a/src/wallet.rs b/src/wallet.rs index 1791df2c2f..556e195513 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -337,6 +337,35 @@ impl Wallet { &self.locked_utxos } + pub(crate) fn lock_non_cardinal_outputs(&self) -> Result { + let inscriptions = self + .inscriptions() + .keys() + .map(|satpoint| satpoint.outpoint) + .collect::>(); + + let locked = self + .locked_utxos() + .keys() + .cloned() + .collect::>(); + + let outputs = self + .utxos() + .keys() + .filter(|utxo| inscriptions.contains(utxo)) + .chain(self.get_runic_outputs()?.iter()) + .cloned() + .filter(|utxo| !locked.contains(utxo)) + .collect::>(); + + if !self.bitcoin_client().lock_unspent(&outputs)? { + bail!("failed to lock UTXOs"); + } + + Ok(()) + } + pub(crate) fn inscriptions(&self) -> &BTreeMap> { &self.inscriptions } diff --git a/tests/wallet/mint.rs b/tests/wallet/mint.rs index bb47b8b073..7fd9408a05 100644 --- a/tests/wallet/mint.rs +++ b/tests/wallet/mint.rs @@ -221,3 +221,90 @@ fn minting_rune_with_no_rune_index_fails() { .expected_stderr("error: `ord wallet etch` requires index created with `--index-runes` flag\n") .run_and_extract_stdout(); } + +#[test] +fn minting_rune_and_then_sending_works() { + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() + .network(Network::Regtest) + .build(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-runes", "--regtest"], &[]); + + bitcoin_rpc_server.mine_blocks(1); + + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); + + batch( + &bitcoin_rpc_server, + &ord_rpc_server, + Batchfile { + etch: Some(Etch { + divisibility: 0, + rune: SpacedRune { + rune: Rune(RUNE), + spacers: 0, + }, + premine: "111".parse().unwrap(), + symbol: '¢', + mint: Some(ord::wallet::inscribe::BatchMint { + term: Some(10), + limit: "21".parse().unwrap(), + deadline: None, + }), + }), + inscriptions: vec![BatchEntry { + file: "inscription.jpeg".into(), + ..Default::default() + }], + ..Default::default() + }, + ); + + let balance = CommandBuilder::new("--chain regtest --index-runes wallet balance") + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) + .run_and_deserialize_output::(); + + assert_eq!( + *balance.runes.unwrap().first_key_value().unwrap().1, + 111_u128 + ); + + let output = CommandBuilder::new(format!( + "--chain regtest --index-runes wallet mint --fee-rate 1 --rune {}", + Rune(RUNE) + )) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) + .run_and_deserialize_output::(); + + bitcoin_rpc_server.mine_blocks(1); + + let balance = CommandBuilder::new("--chain regtest --index-runes wallet balance") + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) + .run_and_deserialize_output::(); + + assert_eq!( + *balance.runes.unwrap().first_key_value().unwrap().1, + 132_u128 + ); + + pretty_assert_eq!( + output.pile, + Pile { + amount: 21, + divisibility: 0, + symbol: Some('¢'), + } + ); + + CommandBuilder::new(format!( + "--regtest --index-runes wallet send bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 5{} --fee-rate 1", + Rune(RUNE) + )) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) + .run_and_deserialize_output::(); +}