From 7ca108838b30f31614c9c150a57d4ff7ecbbab38 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 14 Oct 2024 13:48:31 +0000 Subject: [PATCH] Switch to `submitpackage` for broadcasting transactions --- src/bitcoind_client.rs | 60 ++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/src/bitcoind_client.rs b/src/bitcoind_client.rs index 85b1217..31b328c 100644 --- a/src/bitcoind_client.rs +++ b/src/bitcoind_client.rs @@ -314,32 +314,42 @@ impl FeeEstimator for BitcoindClient { impl BroadcasterInterface for BitcoindClient { fn broadcast_transactions(&self, txs: &[&Transaction]) { - // TODO: Rather than calling `sendrawtransaction` in a a loop, we should probably use - // `submitpackage` once it becomes available. - for tx in txs { - let bitcoind_rpc_client = Arc::clone(&self.bitcoind_rpc_client); - let tx_serialized = encode::serialize_hex(tx); - let tx_json = serde_json::json!(tx_serialized); - let logger = Arc::clone(&self.logger); - self.handle.spawn(async move { - // This may error due to RL calling `broadcast_transactions` with the same transaction - // multiple times, but the error is safe to ignore. - match bitcoind_rpc_client - .call_method::("sendrawtransaction", &vec![tx_json]) + // As of Bitcoin Core 28, using `submitpackage` allows us to broadcast multiple + // transactions at once and have them propagate through the network as a whole, avoiding + // some pitfalls with anchor channels where the first transaction doesn't make it into the + // mempool at all. Several older versions of Bitcoin Core also support `submitpackage`, + // however, so we just use it unconditionally here. + // Sadly, Bitcoin Core has an arbitrary restriction on `submitpackage` - it must actually + // contain a package (see https://github.com/bitcoin/bitcoin/issues/31085). + let txn = txs.iter().map(|tx| encode::serialize_hex(tx)).collect::>(); + let bitcoind_rpc_client = Arc::clone(&self.bitcoind_rpc_client); + let logger = Arc::clone(&self.logger); + self.handle.spawn(async move { + let res = if txn.len() == 1 { + let tx_json = serde_json::json!(txn[0]); + bitcoind_rpc_client + .call_method::("sendrawtransaction", &[tx_json]) .await - { - Ok(_) => {} - Err(e) => { - let err_str = e.get_ref().unwrap().to_string(); - log_error!(logger, - "Warning, failed to broadcast a transaction, this is likely okay but may indicate an error: {}\nTransaction: {}", - err_str, - tx_serialized); - print!("Warning, failed to broadcast a transaction, this is likely okay but may indicate an error: {}\n> ", err_str); - } - } - }); - } + } else { + let tx_json = serde_json::json!(txn); + bitcoind_rpc_client + .call_method::("submitpackage", &[tx_json]) + .await + }; + // This may error due to RL calling `broadcast_transactions` with the same transaction + // multiple times, but the error is safe to ignore. + match res { + Ok(_) => {} + Err(e) => { + let err_str = e.get_ref().unwrap().to_string(); + log_error!(logger, + "Warning, failed to broadcast a transaction, this is likely okay but may indicate an error: {}\nTransactions: {:?}", + err_str, + txn); + print!("Warning, failed to broadcast a transaction, this is likely okay but may indicate an error: {}\n> ", err_str); + } + } + }); } } -- 2.39.5