From b14bc602d45cac8191059d8ed6a5391ca7cfc68f Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Mon, 17 Oct 2022 15:48:12 +0200 Subject: [PATCH] bitcoin: interface for broadcasting a transaction --- src/bitcoin/d/mod.rs | 8 ++++++++ src/bitcoin/mod.rs | 41 +++++++++++++++++++++++++++++++++++++++-- src/testutils.rs | 6 +++++- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/bitcoin/d/mod.rs b/src/bitcoin/d/mod.rs index 2bfe3a5e..c3db160e 100644 --- a/src/bitcoin/d/mod.rs +++ b/src/bitcoin/d/mod.rs @@ -679,6 +679,14 @@ impl BitcoinD { blockhash, } } + + pub fn broadcast_tx(&self, tx: &bitcoin::Transaction) -> Result<(), BitcoindError> { + self.make_fallible_node_request( + "sendrawtransaction", + ¶ms!(bitcoin::consensus::encode::serialize_hex(tx)), + )?; + Ok(()) + } } // Bitcoind uses a guess for the value of verificationprogress. It will eventually get to // be 1, and we want to be less conservative. diff --git a/src/bitcoin/mod.rs b/src/bitcoin/mod.rs index 5f9a7289..ec7cf141 100644 --- a/src/bitcoin/mod.rs +++ b/src/bitcoin/mod.rs @@ -4,12 +4,30 @@ pub mod d; pub mod poller; -use d::LSBlockEntry; +use d::{BitcoindError, LSBlockEntry}; -use std::{collections::HashMap, fmt, sync}; +use std::{collections::HashMap, error, fmt, sync}; use miniscript::bitcoin; +/// Error occuring when querying our Bitcoin backend. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BitcoinError { + Broadcast(String), +} + +impl fmt::Display for BitcoinError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + BitcoinError::Broadcast(reason) => { + write!(f, "Failed to broadcast transaction: '{}'", reason) + } + } + } +} + +impl error::Error for BitcoinError {} + /// Information about the best block in the chain #[derive(Debug, Clone, Eq, PartialEq, Copy)] pub struct BlockChainTip { @@ -60,6 +78,9 @@ pub trait BitcoinInterface: Send { /// Get the common ancestor between the Bitcoin backend's tip and the given tip. fn common_ancestor(&self, tip: &BlockChainTip) -> BlockChainTip; + + /// Broadcast this transaction to the Bitcoin P2P network + fn broadcast_tx(&self, tx: &bitcoin::Transaction) -> Result<(), BitcoinError>; } impl BitcoinInterface for d::BitcoinD { @@ -234,6 +255,18 @@ impl BitcoinInterface for d::BitcoinD { ancestor } + + fn broadcast_tx(&self, tx: &bitcoin::Transaction) -> Result<(), BitcoinError> { + match self.broadcast_tx(tx) { + Ok(()) => Ok(()), + Err(BitcoindError::Server(e)) => Err(BitcoinError::Broadcast(e.to_string())), + // We assume the Bitcoin backend doesn't fail, so it must be a JSONRPC error. + Err(e) => panic!( + "Unexpected Bitcoin error when broadcast transaction: '{}'.", + e + ), + } + } } // FIXME: do we need to repeat the entire trait implemenation? Isn't there a nicer way? @@ -282,6 +315,10 @@ impl BitcoinInterface for sync::Arc> fn common_ancestor(&self, tip: &BlockChainTip) -> BlockChainTip { self.lock().unwrap().common_ancestor(tip) } + + fn broadcast_tx(&self, tx: &bitcoin::Transaction) -> Result<(), BitcoinError> { + self.lock().unwrap().broadcast_tx(tx) + } } // FIXME: We could avoid this type (and all the conversions entailing allocations) if bitcoind diff --git a/src/testutils.rs b/src/testutils.rs index 523dc3de..3f5b1e72 100644 --- a/src/testutils.rs +++ b/src/testutils.rs @@ -1,5 +1,5 @@ use crate::{ - bitcoin::{BitcoinInterface, BlockChainTip, UTxO}, + bitcoin::{BitcoinError, BitcoinInterface, BlockChainTip, UTxO}, config::{BitcoinConfig, Config}, database::{Coin, DatabaseConnection, DatabaseInterface, SpendBlock}, DaemonHandle, @@ -66,6 +66,10 @@ impl BitcoinInterface for DummyBitcoind { fn common_ancestor(&self, _: &BlockChainTip) -> BlockChainTip { todo!() } + + fn broadcast_tx(&self, _: &bitcoin::Transaction) -> Result<(), BitcoinError> { + todo!() + } } pub struct DummyDb {