Skip to content

Commit

Permalink
feat(backend): Expose and use min_confirmations filter to get utxos (#…
Browse files Browse the repository at this point in the history
…2601)

# Motivation

The bitcoin API to get utxos exposes a filter for the mininum
confirmations of the transaction.

In this PR, I expose this filter in `get_all_utxos` and use it in the
`btc_select_user_utxos_fee` endpoint.

# Changes

* Add optional parameter min_confirmations to `get_all_utxos`.
* Change page for filter parameter for helper `get_utxos`.
* Call `get_all_utxos` with 6 minimum confirmations.

# Tests

We can't add integration tests for bitcoin api until pocket-ic supports
bitcoin.
  • Loading branch information
lmuntaner authored Oct 1, 2024
1 parent 24b52bf commit 440aff8
Show file tree
Hide file tree
Showing 9 changed files with 34 additions and 10 deletions.
1 change: 1 addition & 0 deletions src/backend/backend.did
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ type SelectedUtxosFeeRequest = record {
network : BitcoinNetwork;
amount_satoshis : nat64;
source_address : text;
min_confirmations : opt nat32;
};
type SelectedUtxosFeeResponse = record {
fee_satoshis : nat64;
Expand Down
16 changes: 11 additions & 5 deletions src/backend/src/bitcoin_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ use ic_cdk::api::management_canister::bitcoin::{
async fn get_utxos(
network: BitcoinNetwork,
address: String,
maybe_next_page: Option<Vec<u8>>,
filter: Option<UtxoFilter>,
) -> Result<GetUtxosResponse, String> {
let utxos_res = bitcoin_get_utxos(GetUtxosRequest {
address,
network,
filter: maybe_next_page.map(UtxoFilter::Page),
filter,
})
.await
.map_err(|err| err.1)?;
Expand All @@ -25,13 +25,19 @@ async fn get_utxos(
}
/// Returns all the UTXOs of a specific address.
/// API interface returns a paginated view of the utxos but we need to get them all.
pub async fn get_all_utxos(network: BitcoinNetwork, address: String) -> Result<Vec<Utxo>, String> {
let mut utxos_response = get_utxos(network, address.clone(), None).await?;
pub async fn get_all_utxos(
network: BitcoinNetwork,
address: String,
min_confirmations: Option<u32>,
) -> Result<Vec<Utxo>, String> {
let filter = min_confirmations.map(UtxoFilter::MinConfirmations);
let mut utxos_response = get_utxos(network, address.clone(), filter).await?;

let mut all_utxos: Vec<Utxo> = utxos_response.utxos;
let mut next_page: Option<Vec<u8>> = utxos_response.next_page;
while next_page.is_some() {
utxos_response = get_utxos(network, address.clone(), next_page).await?;
utxos_response =
get_utxos(network, address.clone(), next_page.map(UtxoFilter::Page)).await?;
all_utxos.extend(utxos_response.utxos);
next_page = utxos_response.next_page;
}
Expand Down
16 changes: 13 additions & 3 deletions src/backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,23 @@ fn list_custom_tokens() -> Vec<CustomToken> {
read_state(|s| s.custom_token.get(&stored_principal).unwrap_or_default().0)
}

const MIN_CONFIRMATIONS_ACCEPTED_BTC_TX: u32 = 6;

#[update(guard = "may_read_user_data")]
async fn btc_select_user_utxos_fee(
params: SelectedUtxosFeeRequest,
) -> Result<SelectedUtxosFeeResponse, SelectedUtxosFeeError> {
let all_utxos = bitcoin_api::get_all_utxos(params.network, params.source_address)
.await
.map_err(|msg| SelectedUtxosFeeError::InternalError { msg })?;
let all_utxos = bitcoin_api::get_all_utxos(
params.network,
params.source_address,
Some(
params
.min_confirmations
.unwrap_or(MIN_CONFIRMATIONS_ACCEPTED_BTC_TX),
),
)
.await
.map_err(|msg| SelectedUtxosFeeError::InternalError { msg })?;

let median_fee_millisatoshi_per_vbyte = bitcoin_api::get_fee_per_byte(params.network)
.await
Expand Down
2 changes: 2 additions & 0 deletions src/backend/tests/it/bitcoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ fn test_select_user_utxos_fee_returns_zero_when_user_has_insufficient_funds() {
amount_satoshis: 100_000_000u64,
source_address: "bcrt1qpg7udjvq7gx2fp480pgt4hnhj3qc4nhrkstc33".to_string(),
network: BitcoinNetwork::Regtest,
// Until bitcoin is supported in pocket-ic it only works with 1.
min_confirmations: Some(1),
};
let response = pic_setup.update::<Result<SelectedUtxosFeeResponse, SelectedUtxosFeeError>>(
caller,
Expand Down
1 change: 1 addition & 0 deletions src/declarations/backend/backend.did
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ type SelectedUtxosFeeRequest = record {
network : BitcoinNetwork;
amount_satoshis : nat64;
source_address : text;
min_confirmations : opt nat32;
};
type SelectedUtxosFeeResponse = record {
fee_satoshis : nat64;
Expand Down
1 change: 1 addition & 0 deletions src/declarations/backend/backend.did.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export interface SelectedUtxosFeeRequest {
network: BitcoinNetwork;
amount_satoshis: bigint;
source_address: string;
min_confirmations: [] | [number];
}
export interface SelectedUtxosFeeResponse {
fee_satoshis: bigint;
Expand Down
3 changes: 2 additions & 1 deletion src/declarations/backend/backend.factory.certified.did.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ export const idlFactory = ({ IDL }) => {
const SelectedUtxosFeeRequest = IDL.Record({
network: BitcoinNetwork,
amount_satoshis: IDL.Nat64,
source_address: IDL.Text
source_address: IDL.Text,
min_confirmations: IDL.Opt(IDL.Nat32)
});
const Outpoint = IDL.Record({
txid: IDL.Vec(IDL.Nat8),
Expand Down
3 changes: 2 additions & 1 deletion src/declarations/backend/backend.factory.did.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ export const idlFactory = ({ IDL }) => {
const SelectedUtxosFeeRequest = IDL.Record({
network: BitcoinNetwork,
amount_satoshis: IDL.Nat64,
source_address: IDL.Text
source_address: IDL.Text,
min_confirmations: IDL.Opt(IDL.Nat32)
});
const Outpoint = IDL.Record({
txid: IDL.Vec(IDL.Nat8),
Expand Down
1 change: 1 addition & 0 deletions src/shared/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ pub mod bitcoin {
pub amount_satoshis: u64,
pub source_address: String,
pub network: BitcoinNetwork,
pub min_confirmations: Option<u32>,
}

#[derive(CandidType, Deserialize, Clone, Eq, PartialEq, Debug)]
Expand Down

0 comments on commit 440aff8

Please sign in to comment.