diff --git a/signer/src/bitcoin/client.rs b/signer/src/bitcoin/client.rs index c36cdd5b..1d8640d6 100644 --- a/signer/src/bitcoin/client.rs +++ b/signer/src/bitcoin/client.rs @@ -62,7 +62,7 @@ impl BitcoinInteract for ApiFallbackClient { .await } - fn get_tx(&self, txid: &Txid) -> Result { + fn get_tx(&self, txid: &Txid) -> Result, Error> { self.get_client().get_tx(txid) } diff --git a/signer/src/bitcoin/mod.rs b/signer/src/bitcoin/mod.rs index dec2e122..797cc28a 100644 --- a/signer/src/bitcoin/mod.rs +++ b/signer/src/bitcoin/mod.rs @@ -28,7 +28,7 @@ pub trait BitcoinInteract { ) -> impl Future, Error>> + Send; /// get tx - fn get_tx(&self, txid: &Txid) -> Result; + fn get_tx(&self, txid: &Txid) -> Result, Error>; /// get tx info fn get_tx_info( diff --git a/signer/src/bitcoin/rpc.rs b/signer/src/bitcoin/rpc.rs index ce2964ad..a92dc9a6 100644 --- a/signer/src/bitcoin/rpc.rs +++ b/signer/src/bitcoin/rpc.rs @@ -214,7 +214,7 @@ impl BitcoinCoreClient { /// mempool. If -txindex is enabled on bitcoin-core and no blockhash /// argument is passed, it will return the transaction if it is in the /// mempool or any block. - pub fn get_tx(&self, txid: &Txid) -> Result { + pub fn get_tx(&self, txid: &Txid) -> Result, Error> { let args = [ serde_json::to_value(txid).map_err(Error::JsonSerialize)?, // This is the verbosity level. The acceptable values are 0, 1, @@ -224,9 +224,15 @@ impl BitcoinCoreClient { serde_json::Value::Null, ]; - self.inner - .call("getrawtransaction", &args) - .map_err(|err| Error::GetTransactionBitcoinCore(err, *txid)) + match self.inner.call::("getrawtransaction", &args) { + Ok(tx_info) => Ok(Some(tx_info)), + // If the transaction is not found in an + // actual block then the message is "No such transaction found + // in the provided block. Use gettransaction for wallet + // transactions." In both cases the code is the same. + Err(BtcRpcError::JsonRpc(JsonRpcError::Rpc(RpcError { code: -5, .. }))) => Ok(None), + Err(err) => Err(Error::GetTransactionBitcoinCore(err, *txid)), + } } /// Fetch and decode raw transaction from bitcoin-core using the @@ -308,14 +314,12 @@ impl BitcoinInteract for BitcoinCoreClient { unimplemented!() } - #[doc = " get tx"] - fn get_tx(&self, _: &Txid) -> Result { - todo!() + fn get_tx(&self, txid: &Txid) -> Result, Error> { + self.get_tx(txid) } - #[doc = " get tx info"] - fn get_tx_info(&self, _: &Txid, _: &BlockHash) -> Result, Error> { - todo!() + fn get_tx_info(&self, txid: &Txid, block_hash: &BlockHash) -> Result, Error> { + self.get_tx_info(txid, block_hash) } #[doc = " Estimate fee rate"] diff --git a/signer/src/block_observer.rs b/signer/src/block_observer.rs index 461d91cd..e3f4edde 100644 --- a/signer/src/block_observer.rs +++ b/signer/src/block_observer.rs @@ -79,7 +79,9 @@ impl DepositRequestValidator for CreateDepositRequest { C: BitcoinInteract, { // Fetch the transaction from either a block or from the mempool - let response = client.get_tx(&self.outpoint.txid)?; + let Some(response) = client.get_tx(&self.outpoint.txid)? else { + return Err(Error::BitcoinTxMissing(self.outpoint.txid)); + }; Ok(Deposit { info: self.validate_tx(&response.tx)?, @@ -772,8 +774,8 @@ mod tests { } impl BitcoinInteract for TestHarness { - fn get_tx(&self, txid: &bitcoin::Txid) -> Result { - self.deposits.get(txid).cloned().ok_or(Error::Encryption) + fn get_tx(&self, txid: &bitcoin::Txid) -> Result, Error> { + Ok(self.deposits.get(txid).cloned()) } fn get_tx_info(&self, _: &Txid, _: &BlockHash) -> Result, Error> { diff --git a/signer/src/error.rs b/signer/src/error.rs index aa6e9b3c..714b8087 100644 --- a/signer/src/error.rs +++ b/signer/src/error.rs @@ -15,6 +15,12 @@ pub enum Error { #[error("could not create RPC client to {1}: {0}")] BitcoinCoreRpcClient(#[source] bitcoincore_rpc::Error, String), + /// The bitcoin tranaction was not found in the mempool or on the + /// bitcoin blockchain. This is thrown when we expect the transaction + /// to exist in bitcoin core but it does not. + #[error("Transaction is missing from mempool")] + BitcoinTxMissing(bitcoin::Txid), + /// Returned when we could not decode the hex into a /// bitcoin::Transaction. #[error("failed to decode the provided hex into a transaction. txid: {1}. {0}")] diff --git a/signer/src/testing/api_clients.rs b/signer/src/testing/api_clients.rs index 1cb13657..ee3e9397 100644 --- a/signer/src/testing/api_clients.rs +++ b/signer/src/testing/api_clients.rs @@ -24,7 +24,7 @@ impl TryFrom<&[Url]> for NoopApiClient { /// Noop implementation of the BitcoinInteract trait. impl BitcoinInteract for NoopApiClient { - fn get_tx(&self, _: &bitcoin::Txid) -> Result { + fn get_tx(&self, _: &bitcoin::Txid) -> Result, Error> { unimplemented!() } fn get_tx_info( diff --git a/signer/tests/integration/bitcoin_rpc.rs b/signer/tests/integration/bitcoin_rpc.rs index 69105e30..6aaf3e76 100644 --- a/signer/tests/integration/bitcoin_rpc.rs +++ b/signer/tests/integration/bitcoin_rpc.rs @@ -33,7 +33,7 @@ fn btc_client_getstransaction() { let outpoint = faucet.send_to(500_000, &signer.address); let vout = outpoint.vout as usize; - let response = client.get_tx(&outpoint.txid).unwrap(); + let response = client.get_tx(&outpoint.txid).unwrap().unwrap(); // Let's make sure we got the right transaction assert_eq!(response.tx.compute_txid(), outpoint.txid); assert_eq!(response.tx.output[vout].value.to_sat(), 500_000); @@ -45,7 +45,7 @@ fn btc_client_getstransaction() { // Now let's confirm it and try again faucet.generate_blocks(1); - let response = client.get_tx(&outpoint.txid).unwrap(); + let response = client.get_tx(&outpoint.txid).unwrap().unwrap(); // Let's make sure we got the right transaction assert_eq!(response.tx.compute_txid(), outpoint.txid); assert_eq!(response.tx.output[vout].value.to_sat(), 500_000); @@ -77,7 +77,7 @@ fn btc_client_gets_transaction_info() { let outpoint = faucet.send_to(500_000, &signer.address); let vout = outpoint.vout as usize; - let response = client.get_tx(&outpoint.txid).unwrap(); + let response = client.get_tx(&outpoint.txid).unwrap().unwrap(); // Let's make sure we got the right transaction assert_eq!(response.tx.compute_txid(), outpoint.txid); assert_eq!(response.tx.output[vout].value.to_sat(), 500_000);