Skip to content

Commit 6a51c37

Browse files
authored
Merge pull request #909 from dfinity/tl/basic_bitcoin_using_cdk
chore: Use ECDSA and Bitcoin IC CDK Functionality
2 parents 0f0ca7d + 6fd82b1 commit 6a51c37

File tree

3 files changed

+57
-92
lines changed

3 files changed

+57
-92
lines changed

rust/basic_bitcoin/src/basic_bitcoin/src/bitcoin_api.rs

Lines changed: 21 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,20 @@
1-
use candid::Principal;
2-
use ic_cdk::api::call::call_with_payment;
31
use ic_cdk::api::management_canister::bitcoin::{
4-
BitcoinNetwork, GetBalanceRequest, GetCurrentFeePercentilesRequest, GetUtxosRequest,
5-
GetUtxosResponse, MillisatoshiPerByte, Satoshi, SendTransactionRequest,
2+
bitcoin_get_balance, bitcoin_get_current_fee_percentiles, bitcoin_get_utxos,
3+
bitcoin_send_transaction, BitcoinNetwork, GetBalanceRequest, GetCurrentFeePercentilesRequest,
4+
GetUtxosRequest, GetUtxosResponse, MillisatoshiPerByte, SendTransactionRequest,
65
};
76

8-
// The fees for the various bitcoin endpoints.
9-
const GET_BALANCE_COST_CYCLES: u64 = 100_000_000;
10-
const GET_UTXOS_COST_CYCLES: u64 = 10_000_000_000;
11-
const GET_CURRENT_FEE_PERCENTILES_CYCLES: u64 = 100_000_000;
12-
const SEND_TRANSACTION_BASE_CYCLES: u64 = 5_000_000_000;
13-
const SEND_TRANSACTION_PER_BYTE_CYCLES: u64 = 20_000_000;
14-
157
/// Returns the balance of the given bitcoin address.
168
///
179
/// Relies on the `bitcoin_get_balance` endpoint.
1810
/// See https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_get_balance
1911
pub async fn get_balance(network: BitcoinNetwork, address: String) -> u64 {
20-
let balance_res: Result<(Satoshi,), _> = call_with_payment(
21-
Principal::management_canister(),
22-
"bitcoin_get_balance",
23-
(GetBalanceRequest {
24-
address,
25-
network: network.into(),
26-
min_confirmations: None,
27-
},),
28-
GET_BALANCE_COST_CYCLES,
29-
)
12+
let min_confirmations = None;
13+
let balance_res = bitcoin_get_balance(GetBalanceRequest {
14+
address,
15+
network,
16+
min_confirmations,
17+
})
3018
.await;
3119

3220
balance_res.unwrap().0
@@ -37,16 +25,12 @@ pub async fn get_balance(network: BitcoinNetwork, address: String) -> u64 {
3725
/// NOTE: Relies on the `bitcoin_get_utxos` endpoint.
3826
/// See https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_get_utxos
3927
pub async fn get_utxos(network: BitcoinNetwork, address: String) -> GetUtxosResponse {
40-
let utxos_res: Result<(GetUtxosResponse,), _> = call_with_payment(
41-
Principal::management_canister(),
42-
"bitcoin_get_utxos",
43-
(GetUtxosRequest {
44-
address,
45-
network: network.into(),
46-
filter: None,
47-
},),
48-
GET_UTXOS_COST_CYCLES,
49-
)
28+
let filter = None;
29+
let utxos_res = bitcoin_get_utxos(GetUtxosRequest {
30+
address,
31+
network,
32+
filter,
33+
})
5034
.await;
5135

5236
utxos_res.unwrap().0
@@ -58,15 +42,8 @@ pub async fn get_utxos(network: BitcoinNetwork, address: String) -> GetUtxosResp
5842
/// Relies on the `bitcoin_get_current_fee_percentiles` endpoint.
5943
/// See https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_get_current_fee_percentiles
6044
pub async fn get_current_fee_percentiles(network: BitcoinNetwork) -> Vec<MillisatoshiPerByte> {
61-
let res: Result<(Vec<MillisatoshiPerByte>,), _> = call_with_payment(
62-
Principal::management_canister(),
63-
"bitcoin_get_current_fee_percentiles",
64-
(GetCurrentFeePercentilesRequest {
65-
network: network.into(),
66-
},),
67-
GET_CURRENT_FEE_PERCENTILES_CYCLES,
68-
)
69-
.await;
45+
let res =
46+
bitcoin_get_current_fee_percentiles(GetCurrentFeePercentilesRequest { network }).await;
7047

7148
res.unwrap().0
7249
}
@@ -76,18 +53,10 @@ pub async fn get_current_fee_percentiles(network: BitcoinNetwork) -> Vec<Millisa
7653
/// Relies on the `bitcoin_send_transaction` endpoint.
7754
/// See https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_send_transaction
7855
pub async fn send_transaction(network: BitcoinNetwork, transaction: Vec<u8>) {
79-
let transaction_fee = SEND_TRANSACTION_BASE_CYCLES
80-
+ (transaction.len() as u64) * SEND_TRANSACTION_PER_BYTE_CYCLES;
81-
82-
let res: Result<(), _> = call_with_payment(
83-
Principal::management_canister(),
84-
"bitcoin_send_transaction",
85-
(SendTransactionRequest {
86-
network: network.into(),
87-
transaction,
88-
},),
89-
transaction_fee,
90-
)
56+
let res = bitcoin_send_transaction(SendTransactionRequest {
57+
network,
58+
transaction,
59+
})
9160
.await;
9261

9362
res.unwrap();

rust/basic_bitcoin/src/basic_bitcoin/src/bitcoin_wallet.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ use bitcoin::{
1414
hashes::Hash,
1515
Address, AddressType, EcdsaSighashType, OutPoint, Script, Transaction, TxIn, TxOut, Txid,
1616
};
17-
use ic_cdk::api::management_canister::bitcoin::{MillisatoshiPerByte, BitcoinNetwork, Satoshi, Utxo};
17+
use ic_cdk::api::management_canister::bitcoin::{
18+
BitcoinNetwork, MillisatoshiPerByte, Satoshi, Utxo,
19+
};
1820
use ic_cdk::print;
1921
use sha2::Digest;
2022
use std::str::FromStr;
@@ -28,7 +30,7 @@ pub async fn get_p2pkh_address(
2830
derivation_path: Vec<Vec<u8>>,
2931
) -> String {
3032
// Fetch the public key of the given derivation path.
31-
let public_key = ecdsa_api::ecdsa_public_key(key_name, derivation_path).await;
33+
let public_key = ecdsa_api::get_ecdsa_public_key(key_name, derivation_path).await;
3234

3335
// Compute the address.
3436
public_key_to_p2pkh_address(network, &public_key)
@@ -59,7 +61,7 @@ pub async fn send(
5961

6062
// Fetch our public key, P2PKH address, and UTXOs.
6163
let own_public_key =
62-
ecdsa_api::ecdsa_public_key(key_name.clone(), derivation_path.clone()).await;
64+
ecdsa_api::get_ecdsa_public_key(key_name.clone(), derivation_path.clone()).await;
6365
let own_address = public_key_to_p2pkh_address(network, &own_public_key);
6466

6567
print("Fetching UTXOs...");
@@ -85,7 +87,7 @@ pub async fn send(
8587
.await;
8688

8789
let tx_bytes = transaction.serialize();
88-
print(&format!("Transaction to sign: {}", hex::encode(tx_bytes)));
90+
print(format!("Transaction to sign: {}", hex::encode(tx_bytes)));
8991

9092
// Sign the transaction.
9193
let signed_transaction = sign_transaction(
@@ -94,12 +96,12 @@ pub async fn send(
9496
transaction,
9597
key_name,
9698
derivation_path,
97-
ecdsa_api::sign_with_ecdsa,
99+
ecdsa_api::get_ecdsa_signature,
98100
)
99101
.await;
100102

101103
let signed_transaction_bytes = signed_transaction.serialize();
102-
print(&format!(
104+
print(format!(
103105
"Signed transaction: {}",
104106
hex::encode(&signed_transaction_bytes)
105107
));
@@ -151,7 +153,7 @@ async fn build_transaction(
151153
let signed_tx_bytes_len = signed_transaction.serialize().len() as u64;
152154

153155
if (signed_tx_bytes_len * fee_per_byte) / 1000 == total_fee {
154-
print(&format!("Transaction built with fee {}.", total_fee));
156+
print(format!("Transaction built with fee {}.", total_fee));
155157
return transaction;
156158
} else {
157159
total_fee = (signed_tx_bytes_len * fee_per_byte) / 1000;

rust/basic_bitcoin/src/basic_bitcoin/src/ecdsa_api.rs

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,43 @@
1-
use crate::types::*;
2-
use candid::Principal;
3-
use ic_cdk::{api::call::call_with_payment, call};
4-
5-
// The fee for the `sign_with_ecdsa` endpoint using the test key.
6-
const SIGN_WITH_ECDSA_COST_CYCLES: u64 = 10_000_000_000;
1+
use ic_cdk::api::management_canister::ecdsa::{
2+
ecdsa_public_key, sign_with_ecdsa, EcdsaCurve, EcdsaKeyId, EcdsaPublicKeyArgument,
3+
SignWithEcdsaArgument,
4+
};
75

86
/// Returns the ECDSA public key of this canister at the given derivation path.
9-
pub async fn ecdsa_public_key(key_name: String, derivation_path: Vec<Vec<u8>>) -> Vec<u8> {
7+
pub async fn get_ecdsa_public_key(key_name: String, derivation_path: Vec<Vec<u8>>) -> Vec<u8> {
108
// Retrieve the public key of this canister at the given derivation path
119
// from the ECDSA API.
12-
let res: Result<(ECDSAPublicKeyReply,), _> = call(
13-
Principal::management_canister(),
14-
"ecdsa_public_key",
15-
(ECDSAPublicKey {
16-
canister_id: None,
17-
derivation_path,
18-
key_id: EcdsaKeyId {
19-
curve: EcdsaCurve::Secp256k1,
20-
name: key_name,
21-
},
22-
},),
23-
)
10+
let canister_id = None;
11+
let key_id = EcdsaKeyId {
12+
curve: EcdsaCurve::Secp256k1,
13+
name: key_name,
14+
};
15+
16+
let res = ecdsa_public_key(EcdsaPublicKeyArgument {
17+
canister_id,
18+
derivation_path,
19+
key_id,
20+
})
2421
.await;
2522

2623
res.unwrap().0.public_key
2724
}
2825

29-
pub async fn sign_with_ecdsa(
26+
pub async fn get_ecdsa_signature(
3027
key_name: String,
3128
derivation_path: Vec<Vec<u8>>,
3229
message_hash: Vec<u8>,
3330
) -> Vec<u8> {
34-
let res: Result<(SignWithECDSAReply,), _> = call_with_payment(
35-
Principal::management_canister(),
36-
"sign_with_ecdsa",
37-
(SignWithECDSA {
38-
message_hash,
39-
derivation_path,
40-
key_id: EcdsaKeyId {
41-
curve: EcdsaCurve::Secp256k1,
42-
name: key_name,
43-
},
44-
},),
45-
SIGN_WITH_ECDSA_COST_CYCLES,
46-
)
31+
let key_id = EcdsaKeyId {
32+
curve: EcdsaCurve::Secp256k1,
33+
name: key_name,
34+
};
35+
36+
let res = sign_with_ecdsa(SignWithEcdsaArgument {
37+
message_hash,
38+
derivation_path,
39+
key_id,
40+
})
4741
.await;
4842

4943
res.unwrap().0.signature

0 commit comments

Comments
 (0)