From f55087f6cfcb34e3e33453c51dc853b4508808b7 Mon Sep 17 00:00:00 2001 From: Louis Pahlavi Date: Mon, 2 Dec 2024 16:10:47 +0100 Subject: [PATCH 1/5] XC-229: Use IC CDK to interact with Bitcoin canister --- rs/bitcoin/ckbtc/minter/src/lib.rs | 10 +- rs/bitcoin/ckbtc/minter/src/management.rs | 141 ++++++++++++------- rs/bitcoin/ckbtc/minter/src/test_fixtures.rs | 2 +- 3 files changed, 99 insertions(+), 54 deletions(-) diff --git a/rs/bitcoin/ckbtc/minter/src/lib.rs b/rs/bitcoin/ckbtc/minter/src/lib.rs index 21c55c36232..a5097baeb49 100644 --- a/rs/bitcoin/ckbtc/minter/src/lib.rs +++ b/rs/bitcoin/ckbtc/minter/src/lib.rs @@ -849,7 +849,7 @@ pub async fn sign_transaction( ecdsa_public_key: &ECDSAPublicKey, output_account: &BTreeMap, unsigned_tx: tx::UnsignedTransaction, -) -> Result { +) -> Result { use crate::address::{derivation_path, derive_public_key}; let mut signed_inputs = Vec::with_capacity(unsigned_tx.inputs.len()); @@ -1272,8 +1272,7 @@ pub trait CanisterRuntime { /// Fetches all unspent transaction outputs (UTXOs) associated with the provided address in the specified Bitcoin network. async fn bitcoin_get_utxos( &self, - request: &GetUtxosRequest, - cycles: u64, + request: GetUtxosRequest, ) -> Result; async fn check_transaction( @@ -1314,10 +1313,9 @@ impl CanisterRuntime for IcCanisterRuntime { async fn bitcoin_get_utxos( &self, - request: &GetUtxosRequest, - cycles: u64, + request: GetUtxosRequest, ) -> Result { - management::call("bitcoin_get_utxos", cycles, &request).await + management::bitcoin_get_utxos(request).await } async fn check_transaction( diff --git a/rs/bitcoin/ckbtc/minter/src/management.rs b/rs/bitcoin/ckbtc/minter/src/management.rs index b8005ef819b..64436ffbb48 100644 --- a/rs/bitcoin/ckbtc/minter/src/management.rs +++ b/rs/bitcoin/ckbtc/minter/src/management.rs @@ -1,22 +1,24 @@ //! This module contains async functions for interacting with the management canister. - use crate::logs::P0; use crate::ECDSAPublicKey; use crate::{tx, CanisterRuntime}; use candid::{CandidType, Principal}; +use ic_btc_interface::NetworkInRequest::{mainnet, regtest, testnet, Mainnet, Regtest, Testnet}; use ic_btc_interface::{ - Address, GetCurrentFeePercentilesRequest, GetUtxosRequest, GetUtxosResponse, - MillisatoshiPerByte, Network, Utxo, UtxosFilterInRequest, + Address, GetUtxosRequest, GetUtxosResponse, MillisatoshiPerByte, Network, OutPoint, Txid, Utxo, + UtxosFilterInRequest, }; use ic_btc_kyt::{ CheckAddressArgs, CheckAddressResponse, CheckTransactionArgs, CheckTransactionResponse, }; use ic_canister_log::log; use ic_cdk::api::call::RejectionCode; +use ic_cdk::api::management_canister::bitcoin::{BitcoinNetwork, UtxoFilter}; use ic_management_canister_types::{ DerivationPath, ECDSAPublicKeyArgs, ECDSAPublicKeyResponse, EcdsaCurve, EcdsaKeyId, }; use serde::de::DeserializeOwned; +use serde_bytes::ByteBuf; use std::fmt; /// Represents an error from a management canister call, such as @@ -37,6 +39,13 @@ impl CallError { pub fn reason(&self) -> &Reason { &self.reason } + + pub fn from_cdk_error(method: &str, (code, msg): (RejectionCode, String)) -> CallError { + CallError { + method: String::from(method), + reason: Reason::from_reject(code, msg), + } + } } impl fmt::Display for CallError { @@ -142,19 +151,8 @@ pub async fn get_utxos( source: CallSource, runtime: &R, ) -> Result { - // NB. The minimum number of cycles that need to be sent with the call is 10B (4B) for - // Bitcoin mainnet (Bitcoin testnet): - // https://internetcomputer.org/docs/current/developer-docs/integrations/bitcoin/bitcoin-how-it-works#api-fees--pricing - let get_utxos_cost_cycles = match network { - Network::Mainnet => 10_000_000_000, - Network::Testnet | Network::Regtest => 4_000_000_000, - }; - - // Calls "bitcoin_get_utxos" method with the specified argument on the - // management canister. async fn bitcoin_get_utxos( - req: &GetUtxosRequest, - cycles: u64, + req: GetUtxosRequest, source: CallSource, runtime: &R, ) -> Result { @@ -163,16 +161,15 @@ pub async fn get_utxos( CallSource::Minter => &crate::metrics::GET_UTXOS_MINTER_CALLS, } .with(|cell| cell.set(cell.get() + 1)); - runtime.bitcoin_get_utxos(req, cycles).await + runtime.bitcoin_get_utxos(req).await } let mut response = bitcoin_get_utxos( - &GetUtxosRequest { + GetUtxosRequest { address: address.to_string(), network: network.into(), filter: Some(UtxosFilterInRequest::MinConfirmations(min_confirmations)), }, - get_utxos_cost_cycles, source, runtime, ) @@ -183,12 +180,11 @@ pub async fn get_utxos( // Continue fetching until there are no more pages. while let Some(page) = response.next_page { response = bitcoin_get_utxos( - &GetUtxosRequest { + GetUtxosRequest { address: address.to_string(), network: network.into(), filter: Some(UtxosFilterInRequest::Page(page)), }, - get_utxos_cost_cycles, source, runtime, ) @@ -202,22 +198,24 @@ pub async fn get_utxos( Ok(response) } +/// Fetches a subset of UTXOs for the specified address. +pub async fn bitcoin_get_utxos(request: GetUtxosRequest) -> Result { + ic_cdk::api::management_canister::bitcoin::bitcoin_get_utxos(cdk_get_utxos_request(request)) + .await + .map(|(response,)| parse_cdk_get_utxos_response(response)) + .map_err(|err| CallError::from_cdk_error("bitcoin_get_utxos", err)) +} + /// Returns the current fee percentiles on the bitcoin network. pub async fn get_current_fees(network: Network) -> Result, CallError> { - let cost_cycles = match network { - Network::Mainnet => 100_000_000, - Network::Testnet => 40_000_000, - Network::Regtest => 0, - }; - - call( - "bitcoin_get_current_fee_percentiles", - cost_cycles, - &GetCurrentFeePercentilesRequest { - network: network.into(), + ic_cdk::api::management_canister::bitcoin::bitcoin_get_current_fee_percentiles( + ic_cdk::api::management_canister::bitcoin::GetCurrentFeePercentilesRequest { + network: cdk_network(network), }, ) .await + .map(|(result,)| result) + .map_err(|err| CallError::from_cdk_error("bitcoin_get_current_fee_percentiles", err)) } /// Sends the transaction to the network the management canister interacts with. @@ -225,27 +223,14 @@ pub async fn send_transaction( transaction: &tx::SignedTransaction, network: Network, ) -> Result<(), CallError> { - use ic_cdk::api::management_canister::bitcoin::BitcoinNetwork; - - let cdk_network = match network { - Network::Mainnet => BitcoinNetwork::Mainnet, - Network::Testnet => BitcoinNetwork::Testnet, - Network::Regtest => BitcoinNetwork::Regtest, - }; - - let tx_bytes = transaction.serialize(); - ic_cdk::api::management_canister::bitcoin::bitcoin_send_transaction( ic_cdk::api::management_canister::bitcoin::SendTransactionRequest { - transaction: tx_bytes, - network: cdk_network, + transaction: transaction.serialize(), + network: cdk_network(network), }, ) .await - .map_err(|(code, msg)| CallError { - method: "bitcoin_send_transaction".to_string(), - reason: Reason::from_reject(code, msg), - }) + .map_err(|err| CallError::from_cdk_error("bitcoin_send_transaction", err)) } /// Fetches the ECDSA public key of the canister. @@ -342,3 +327,65 @@ pub async fn check_transaction( })?; Ok(res) } + +fn cdk_network(network: Network) -> BitcoinNetwork { + match network { + Network::Mainnet => BitcoinNetwork::Mainnet, + Network::Testnet => BitcoinNetwork::Testnet, + Network::Regtest => BitcoinNetwork::Regtest, + } +} + +fn cdk_get_utxos_request( + request: GetUtxosRequest, +) -> ic_cdk::api::management_canister::bitcoin::GetUtxosRequest { + ic_cdk::api::management_canister::bitcoin::GetUtxosRequest { + address: request.address, + network: match request.network { + Mainnet | mainnet => BitcoinNetwork::Mainnet, + Testnet | testnet => BitcoinNetwork::Testnet, + Regtest | regtest => BitcoinNetwork::Regtest, + }, + filter: request.filter.map(|filter| match filter { + UtxosFilterInRequest::MinConfirmations(confirmations) + | UtxosFilterInRequest::min_confirmations(confirmations) => { + UtxoFilter::MinConfirmations(confirmations) + } + UtxosFilterInRequest::Page(bytes) | UtxosFilterInRequest::page(bytes) => { + UtxoFilter::Page(bytes.into_vec()) + } + }), + } +} + +fn parse_cdk_get_utxos_response( + response: ic_cdk::api::management_canister::bitcoin::GetUtxosResponse, +) -> GetUtxosResponse { + GetUtxosResponse { + utxos: response.utxos.into_iter().map(parse_cdk_utxo).collect(), + tip_block_hash: response.tip_block_hash, + tip_height: response.tip_height, + next_page: response.next_page.map(ByteBuf::from), + } +} + +fn parse_cdk_utxo(utxo: ic_cdk::api::management_canister::bitcoin::Utxo) -> Utxo { + Utxo { + outpoint: OutPoint { + txid: parse_cdk_txid(utxo.outpoint.txid), + vout: utxo.outpoint.vout, + }, + value: utxo.value, + height: utxo.height, + } +} + +fn parse_cdk_txid(txid: Vec) -> Txid { + let bytes: [u8; 32] = txid.try_into().unwrap_or_else(|v: Vec| { + panic!( + "Expected transaction ID to be length 32, but was length {}", + v.len() + ) + }); + Txid::from(bytes) +} diff --git a/rs/bitcoin/ckbtc/minter/src/test_fixtures.rs b/rs/bitcoin/ckbtc/minter/src/test_fixtures.rs index e599571075a..36712a4644a 100644 --- a/rs/bitcoin/ckbtc/minter/src/test_fixtures.rs +++ b/rs/bitcoin/ckbtc/minter/src/test_fixtures.rs @@ -128,7 +128,7 @@ pub mod mock { fn id(&self) -> Principal; fn time(&self) -> u64; fn global_timer_set(&self, timestamp: u64); - async fn bitcoin_get_utxos(&self, request: &GetUtxosRequest, cycles: u64) -> Result; + async fn bitcoin_get_utxos(&self, request: GetUtxosRequest) -> Result; async fn check_transaction(&self, kyt_principal: Principal, utxo: &Utxo, cycle_payment: u128, ) -> Result; async fn mint_ckbtc(&self, amount: u64, to: Account, memo: Memo) -> Result; } From 38ec6a7a5d42ec96a01d90fe0bc273ee90f01297 Mon Sep 17 00:00:00 2001 From: Louis Pahlavi Date: Tue, 3 Dec 2024 11:06:31 +0100 Subject: [PATCH 2/5] XC-229: Fix bitcoin_get_utxos mock network assert --- rs/bitcoin/mock/src/main.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/rs/bitcoin/mock/src/main.rs b/rs/bitcoin/mock/src/main.rs index 03307ab51b1..adb3edb8925 100644 --- a/rs/bitcoin/mock/src/main.rs +++ b/rs/bitcoin/mock/src/main.rs @@ -1,7 +1,7 @@ use candid::candid_method; use ic_btc_interface::{ Address, GetCurrentFeePercentilesRequest, GetUtxosRequest, GetUtxosResponse, - MillisatoshiPerByte, Network, Utxo, UtxosFilterInRequest, + MillisatoshiPerByte, Network, NetworkInRequest, Utxo, UtxosFilterInRequest, }; use ic_cdk::api::management_canister::bitcoin::{BitcoinNetwork, SendTransactionRequest}; use ic_cdk_macros::{init, update}; @@ -87,7 +87,7 @@ fn set_tip_height(tip_height: u32) { #[update] fn bitcoin_get_utxos(utxos_request: GetUtxosRequest) -> GetUtxosResponse { read_state(|s| { - assert_eq!(utxos_request.network, s.network.into()); + assert_network_equivalent(&utxos_request.network, &s.network); let mut utxos = s .address_to_utxos @@ -238,3 +238,17 @@ fn check_candid_interface_compatibility() { candid_parser::utils::CandidSource::File(old_interface.as_path()), ); } + +fn assert_network_equivalent(lhs: &NetworkInRequest, rhs: &Network) { + match lhs { + &NetworkInRequest::Mainnet | &NetworkInRequest::mainnet => { + assert_eq!(rhs, &Network::Mainnet) + } + &NetworkInRequest::Testnet | &NetworkInRequest::testnet => { + assert_eq!(rhs, &Network::Testnet) + } + &NetworkInRequest::Regtest | &NetworkInRequest::regtest => { + assert_eq!(rhs, &Network::Regtest) + } + } +} From 90798748e1467a09bc491366f59314f83b7aeede Mon Sep 17 00:00:00 2001 From: Louis Pahlavi Date: Tue, 3 Dec 2024 14:59:56 +0100 Subject: [PATCH 3/5] XC-229: Handle UtxosFilterInRequest::min_confirmation in Bitcoin canister mock --- rs/bitcoin/ckbtc/minter/src/management.rs | 24 +++++++++++++---------- rs/bitcoin/mock/src/main.rs | 12 ++++++++---- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/rs/bitcoin/ckbtc/minter/src/management.rs b/rs/bitcoin/ckbtc/minter/src/management.rs index 64436ffbb48..92ee18303cb 100644 --- a/rs/bitcoin/ckbtc/minter/src/management.rs +++ b/rs/bitcoin/ckbtc/minter/src/management.rs @@ -3,7 +3,6 @@ use crate::logs::P0; use crate::ECDSAPublicKey; use crate::{tx, CanisterRuntime}; use candid::{CandidType, Principal}; -use ic_btc_interface::NetworkInRequest::{mainnet, regtest, testnet, Mainnet, Regtest, Testnet}; use ic_btc_interface::{ Address, GetUtxosRequest, GetUtxosResponse, MillisatoshiPerByte, Network, OutPoint, Txid, Utxo, UtxosFilterInRequest, @@ -339,6 +338,9 @@ fn cdk_network(network: Network) -> BitcoinNetwork { fn cdk_get_utxos_request( request: GetUtxosRequest, ) -> ic_cdk::api::management_canister::bitcoin::GetUtxosRequest { + use ic_btc_interface::NetworkInRequest::{ + mainnet, regtest, testnet, Mainnet, Regtest, Testnet, + }; ic_cdk::api::management_canister::bitcoin::GetUtxosRequest { address: request.address, network: match request.network { @@ -346,15 +348,17 @@ fn cdk_get_utxos_request( Testnet | testnet => BitcoinNetwork::Testnet, Regtest | regtest => BitcoinNetwork::Regtest, }, - filter: request.filter.map(|filter| match filter { - UtxosFilterInRequest::MinConfirmations(confirmations) - | UtxosFilterInRequest::min_confirmations(confirmations) => { - UtxoFilter::MinConfirmations(confirmations) - } - UtxosFilterInRequest::Page(bytes) | UtxosFilterInRequest::page(bytes) => { - UtxoFilter::Page(bytes.into_vec()) - } - }), + filter: request.filter.map(cdk_utxo_filter), + } +} + +fn cdk_utxo_filter(filter: UtxosFilterInRequest) -> UtxoFilter { + use UtxosFilterInRequest::{min_confirmations, page, MinConfirmations, Page}; + match filter { + MinConfirmations(confirmations) | min_confirmations(confirmations) => { + UtxoFilter::MinConfirmations(confirmations) + } + Page(bytes) | page(bytes) => UtxoFilter::Page(bytes.into_vec()), } } diff --git a/rs/bitcoin/mock/src/main.rs b/rs/bitcoin/mock/src/main.rs index adb3edb8925..f5e61e7ec05 100644 --- a/rs/bitcoin/mock/src/main.rs +++ b/rs/bitcoin/mock/src/main.rs @@ -98,10 +98,14 @@ fn bitcoin_get_utxos(utxos_request: GetUtxosRequest) -> GetUtxosResponse { .cloned() .collect::>(); - if let Some(UtxosFilterInRequest::MinConfirmations(min_confirmations)) = - utxos_request.filter - { - utxos.retain(|u| s.tip_height + 1 >= u.height + min_confirmations); + if let Some(filter) = utxos_request.filter { + match filter { + UtxosFilterInRequest::MinConfirmations(min_confirmations) + | UtxosFilterInRequest::min_confirmations(min_confirmations) => { + utxos.retain(|u| s.tip_height + 1 >= u.height + min_confirmations) + } + UtxosFilterInRequest::Page(_) | UtxosFilterInRequest::page(_) => {} + } } GetUtxosResponse { From 884a7b463c25c60db5e594de77caf17ff67a2a51 Mon Sep 17 00:00:00 2001 From: Louis Pahlavi Date: Wed, 4 Dec 2024 18:33:04 +0100 Subject: [PATCH 4/5] XC-229: Address review feedback --- rs/bitcoin/ckbtc/minter/src/management.rs | 116 ++++++++++------------ 1 file changed, 55 insertions(+), 61 deletions(-) diff --git a/rs/bitcoin/ckbtc/minter/src/management.rs b/rs/bitcoin/ckbtc/minter/src/management.rs index 92ee18303cb..9640e32faa4 100644 --- a/rs/bitcoin/ckbtc/minter/src/management.rs +++ b/rs/bitcoin/ckbtc/minter/src/management.rs @@ -11,8 +11,10 @@ use ic_btc_kyt::{ CheckAddressArgs, CheckAddressResponse, CheckTransactionArgs, CheckTransactionResponse, }; use ic_canister_log::log; -use ic_cdk::api::call::RejectionCode; -use ic_cdk::api::management_canister::bitcoin::{BitcoinNetwork, UtxoFilter}; +use ic_cdk::api::{ + call::RejectionCode, + management_canister::bitcoin::{BitcoinNetwork, UtxoFilter}, +}; use ic_management_canister_types::{ DerivationPath, ECDSAPublicKeyArgs, ECDSAPublicKeyResponse, EcdsaCurve, EcdsaKeyId, }; @@ -199,6 +201,57 @@ pub async fn get_utxos( /// Fetches a subset of UTXOs for the specified address. pub async fn bitcoin_get_utxos(request: GetUtxosRequest) -> Result { + fn cdk_get_utxos_request( + request: GetUtxosRequest, + ) -> ic_cdk::api::management_canister::bitcoin::GetUtxosRequest { + use ic_btc_interface::NetworkInRequest; + ic_cdk::api::management_canister::bitcoin::GetUtxosRequest { + address: request.address, + network: match request.network { + NetworkInRequest::Mainnet | NetworkInRequest::mainnet => BitcoinNetwork::Mainnet, + NetworkInRequest::Testnet | NetworkInRequest::testnet => BitcoinNetwork::Testnet, + NetworkInRequest::Regtest | NetworkInRequest::regtest => BitcoinNetwork::Regtest, + }, + filter: request.filter.map(|filter| match filter { + UtxosFilterInRequest::MinConfirmations(confirmations) + | UtxosFilterInRequest::min_confirmations(confirmations) => { + UtxoFilter::MinConfirmations(confirmations) + } + UtxosFilterInRequest::Page(bytes) | UtxosFilterInRequest::page(bytes) => { + UtxoFilter::Page(bytes.into_vec()) + } + }), + } + } + + fn parse_cdk_get_utxos_response( + response: ic_cdk::api::management_canister::bitcoin::GetUtxosResponse, + ) -> GetUtxosResponse { + GetUtxosResponse { + utxos: response + .utxos + .into_iter() + .map(|utxo| { + let txid_bytes: [u8; 32] = + utxo.outpoint.txid.try_into().unwrap_or_else(|v: Vec| { + panic!("Expected TXID to be length 32, but was length {}", v.len()) + }); + Utxo { + outpoint: OutPoint { + txid: Txid::from(txid_bytes), + vout: utxo.outpoint.vout, + }, + value: utxo.value, + height: utxo.height, + } + }) + .collect(), + tip_block_hash: response.tip_block_hash, + tip_height: response.tip_height, + next_page: response.next_page.map(ByteBuf::from), + } + } + ic_cdk::api::management_canister::bitcoin::bitcoin_get_utxos(cdk_get_utxos_request(request)) .await .map(|(response,)| parse_cdk_get_utxos_response(response)) @@ -334,62 +387,3 @@ fn cdk_network(network: Network) -> BitcoinNetwork { Network::Regtest => BitcoinNetwork::Regtest, } } - -fn cdk_get_utxos_request( - request: GetUtxosRequest, -) -> ic_cdk::api::management_canister::bitcoin::GetUtxosRequest { - use ic_btc_interface::NetworkInRequest::{ - mainnet, regtest, testnet, Mainnet, Regtest, Testnet, - }; - ic_cdk::api::management_canister::bitcoin::GetUtxosRequest { - address: request.address, - network: match request.network { - Mainnet | mainnet => BitcoinNetwork::Mainnet, - Testnet | testnet => BitcoinNetwork::Testnet, - Regtest | regtest => BitcoinNetwork::Regtest, - }, - filter: request.filter.map(cdk_utxo_filter), - } -} - -fn cdk_utxo_filter(filter: UtxosFilterInRequest) -> UtxoFilter { - use UtxosFilterInRequest::{min_confirmations, page, MinConfirmations, Page}; - match filter { - MinConfirmations(confirmations) | min_confirmations(confirmations) => { - UtxoFilter::MinConfirmations(confirmations) - } - Page(bytes) | page(bytes) => UtxoFilter::Page(bytes.into_vec()), - } -} - -fn parse_cdk_get_utxos_response( - response: ic_cdk::api::management_canister::bitcoin::GetUtxosResponse, -) -> GetUtxosResponse { - GetUtxosResponse { - utxos: response.utxos.into_iter().map(parse_cdk_utxo).collect(), - tip_block_hash: response.tip_block_hash, - tip_height: response.tip_height, - next_page: response.next_page.map(ByteBuf::from), - } -} - -fn parse_cdk_utxo(utxo: ic_cdk::api::management_canister::bitcoin::Utxo) -> Utxo { - Utxo { - outpoint: OutPoint { - txid: parse_cdk_txid(utxo.outpoint.txid), - vout: utxo.outpoint.vout, - }, - value: utxo.value, - height: utxo.height, - } -} - -fn parse_cdk_txid(txid: Vec) -> Txid { - let bytes: [u8; 32] = txid.try_into().unwrap_or_else(|v: Vec| { - panic!( - "Expected transaction ID to be length 32, but was length {}", - v.len() - ) - }); - Txid::from(bytes) -} From 0a41a58c408a7a758c31eba2de2b3f2c93eac79b Mon Sep 17 00:00:00 2001 From: Louis Pahlavi Date: Thu, 5 Dec 2024 11:53:03 +0100 Subject: [PATCH 5/5] XC-229: Streamline some type conversions --- rs/bitcoin/ckbtc/minter/src/management.rs | 28 ++++++++--------------- rs/bitcoin/mock/src/main.rs | 18 ++------------- 2 files changed, 11 insertions(+), 35 deletions(-) diff --git a/rs/bitcoin/ckbtc/minter/src/management.rs b/rs/bitcoin/ckbtc/minter/src/management.rs index 9640e32faa4..11fe27ff6e3 100644 --- a/rs/bitcoin/ckbtc/minter/src/management.rs +++ b/rs/bitcoin/ckbtc/minter/src/management.rs @@ -204,14 +204,9 @@ pub async fn bitcoin_get_utxos(request: GetUtxosRequest) -> Result ic_cdk::api::management_canister::bitcoin::GetUtxosRequest { - use ic_btc_interface::NetworkInRequest; ic_cdk::api::management_canister::bitcoin::GetUtxosRequest { address: request.address, - network: match request.network { - NetworkInRequest::Mainnet | NetworkInRequest::mainnet => BitcoinNetwork::Mainnet, - NetworkInRequest::Testnet | NetworkInRequest::testnet => BitcoinNetwork::Testnet, - NetworkInRequest::Regtest | NetworkInRequest::regtest => BitcoinNetwork::Regtest, - }, + network: cdk_network(request.network.into()), filter: request.filter.map(|filter| match filter { UtxosFilterInRequest::MinConfirmations(confirmations) | UtxosFilterInRequest::min_confirmations(confirmations) => { @@ -231,19 +226,14 @@ pub async fn bitcoin_get_utxos(request: GetUtxosRequest) -> Result| { - panic!("Expected TXID to be length 32, but was length {}", v.len()) - }); - Utxo { - outpoint: OutPoint { - txid: Txid::from(txid_bytes), - vout: utxo.outpoint.vout, - }, - value: utxo.value, - height: utxo.height, - } + .map(|utxo| Utxo { + outpoint: OutPoint { + txid: Txid::try_from(utxo.outpoint.txid.as_slice()) + .unwrap_or_else(|_| panic!("Unable to parse TXID")), + vout: utxo.outpoint.vout, + }, + value: utxo.value, + height: utxo.height, }) .collect(), tip_block_hash: response.tip_block_hash, diff --git a/rs/bitcoin/mock/src/main.rs b/rs/bitcoin/mock/src/main.rs index f5e61e7ec05..ea5c8dbbeca 100644 --- a/rs/bitcoin/mock/src/main.rs +++ b/rs/bitcoin/mock/src/main.rs @@ -1,7 +1,7 @@ use candid::candid_method; use ic_btc_interface::{ Address, GetCurrentFeePercentilesRequest, GetUtxosRequest, GetUtxosResponse, - MillisatoshiPerByte, Network, NetworkInRequest, Utxo, UtxosFilterInRequest, + MillisatoshiPerByte, Network, Utxo, UtxosFilterInRequest, }; use ic_cdk::api::management_canister::bitcoin::{BitcoinNetwork, SendTransactionRequest}; use ic_cdk_macros::{init, update}; @@ -87,7 +87,7 @@ fn set_tip_height(tip_height: u32) { #[update] fn bitcoin_get_utxos(utxos_request: GetUtxosRequest) -> GetUtxosResponse { read_state(|s| { - assert_network_equivalent(&utxos_request.network, &s.network); + assert_eq!(Network::from(utxos_request.network), s.network); let mut utxos = s .address_to_utxos @@ -242,17 +242,3 @@ fn check_candid_interface_compatibility() { candid_parser::utils::CandidSource::File(old_interface.as_path()), ); } - -fn assert_network_equivalent(lhs: &NetworkInRequest, rhs: &Network) { - match lhs { - &NetworkInRequest::Mainnet | &NetworkInRequest::mainnet => { - assert_eq!(rhs, &Network::Mainnet) - } - &NetworkInRequest::Testnet | &NetworkInRequest::testnet => { - assert_eq!(rhs, &Network::Testnet) - } - &NetworkInRequest::Regtest | &NetworkInRequest::regtest => { - assert_eq!(rhs, &Network::Regtest) - } - } -}