Skip to content

Commit ce3ef7c

Browse files
committed
readme and small improvements
1 parent a0385b1 commit ce3ef7c

File tree

4 files changed

+26
-47
lines changed

4 files changed

+26
-47
lines changed

motoko/basic_bitcoin/README.md

Lines changed: 17 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,9 @@ For a deeper understanding of the ICP < > BTC integration, see the [Bitcoin inte
1515
## Prerequisites
1616

1717
- [x] Install the [IC
18-
SDK](https://internetcomputer.org/docs/current/developer-docs/getting-started/install). For local testing, `dfx >= 0.22.0` is required.
18+
SDK](https://internetcomputer.org/docs/current/developer-docs/getting-started/install).
1919
- [x] Clone the example dapp project: `git clone https://github.com/dfinity/examples`
2020

21-
> [!WARNING]
22-
> This example is designed to be deployed on the mainnet. It will return errors when deployed locally; these errors are expected.
23-
2421
Begin by opening a terminal window.
2522

2623
## Step 1: Setup the project environment
@@ -89,58 +86,40 @@ to check [this
8986
article](https://bitcoinmagazine.com/technical/bitcoin-address-types-compared-p2pkh-p2sh-p2wpkh-and-more)
9087
if you are interested in a high-level comparison of different address types.
9188
These addresses can be generated from an ECDSA public key or a Schnorr
92-
([BIP340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki))
93-
public key. The example code showcases how your canister can generate and spend
94-
from three types of addresses:
89+
([BIP340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki),
90+
[BIP341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)) public
91+
key. The example code showcases how your canister can generate and spend from
92+
three types of addresses:
9593
1. A [P2PKH address](https://en.bitcoin.it/wiki/Transaction#Pay-to-PubkeyHash)
9694
using the
9795
[ecdsa_public_key](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-method-ecdsa_public_key)
9896
API.
9997
2. A [P2TR
10098
address](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)
101-
where the funds can be spent using the raw (untweaked) internal key
102-
(so-called P2TR key path spend, but untweaked). The advantage of this
103-
approach compared to P2TR script spends is its significantly smaller fee per
104-
transaction because checking the transaction signature is analogous to P2PK
105-
but uses Schnorr instead of ECDSA. The limitation of untweaked P2TR addresses
106-
is that they cannot be used with scripts. IMPORTANT: Note that
107-
[BIP341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-23)
108-
advises against using taproot addresses that can be spent with an untweaked
109-
key. This precaution is to prevent attacks that can occur when creating
110-
taproot multisigner addresses using specific multisignature schemes. However,
111-
the Schnorr API of the internet computer does not support Schnorr
112-
multisignatures.
99+
where the funds can be spent using the internal key only ([P2TR key path
100+
spend with unspendable script
101+
tree](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-23)).
113102
3. A [P2TR
114103
address](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)
115-
where the funds can be spent using the provided public key with the script
116-
path, where the Merkelized Alternative Script Tree (MAST) consists of a
117-
single script allowing to spend funds by exactly one key.
118-
119-
Note that P2TR *key path* spending with a tweaked key is currently not available
120-
on the IC because the threshold Schnorr signing interface does not allow
121-
applying BIP341 tweaks to the private key. In contrast, the
122-
tweaked public key is used to spend in the script path, which is availble on the
123-
IC. For a technical comparison of different ways of how single-signer P2TR
124-
addresses can be constructed and used, you may want to take a look at [this
125-
post](https://bitcoin.stackexchange.com/a/111100) by Pieter Wuille.
104+
where the funds can be spent using either 1) the internal key or 2) the
105+
provided public key with the script path, where the Merkelized Alternative
106+
Script Tree (MAST) consists of a single script allowing to spend funds by
107+
exactly one key.
126108

127109
On the Candid UI of your canister, click the "Call" button under
128110
`get_${type}_address` to generate a `${type}` Bitcoin address, where `${type}`
129-
is one of `[p2pkh, p2tr_raw_key_spend, p2tr_script_spend]`.
111+
is one of `[p2pkh, p2tr_key_only, p2tr]` (corresponding to the three types of
112+
addresses described above, in the same order).
130113

131114
Or, if you prefer the command line:
132115

133116
```bash
134117
dfx canister --network=ic call basic_bitcoin get_${type}_address
135118
```
136119

137-
* The Bitcoin address you see will be different from the one above because the
138-
ECDSA public key your canister retrieves is unique.
139-
140120
* We are generating a Bitcoin testnet address, which can only be
141121
used for sending/receiving Bitcoin on the Bitcoin testnet.
142122

143-
144123
## Step 3: Receiving bitcoin
145124

146125
Now that the canister is deployed and you have a Bitcoin address, it's time to receive
@@ -150,7 +129,6 @@ to receive some bitcoin.
150129
Enter your address and click on "Send testnet bitcoins". In the example below we will use Bitcoin address `n31eU1K11m1r58aJMgTyxGonu7wSMoUYe7`, but you will use your address. The Bitcoin address you see will be different from the one above
151130
because the ECDSA/Schnorr public key your canister retrieves is unique.
152131

153-
154132
Once the transaction has at least one confirmation, which can take a few minutes,
155133
you'll be able to see it in your canister's balance.
156134

@@ -171,15 +149,16 @@ Checking the balance of a Bitcoin address relies on the [bitcoin_get_balance](ht
171149
## Step 5: Sending bitcoin
172150

173151
You can send bitcoin using the `send_from_${type}` endpoint on your canister, where
174-
`${type}` is on of `[p2pkh, p2tr_raw_key_spend, p2tr_script_spend]`.
152+
`${type}` is one of
153+
`[p2pkh_address, p2tr_key_only_address, p2tr_address_key_path, p2tr_address_script_path]`.
175154

176155
In the Candid UI, add a destination address and an amount to send. In the example
177156
below, we're sending 4'321 Satoshi (0.00004321 BTC) back to the testnet faucet.
178157

179158
Via the command line, the same call would look like this:
180159

181160
```bash
182-
dfx canister --network=ic call basic_bitcoin send_from_p2pkh '(record { destination_address = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt"; amount_in_satoshi = 4321; })'
161+
dfx canister --network=ic call basic_bitcoin send_from_p2pkh_address '(record { destination_address = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt"; amount_in_satoshi = 4321; })'
183162
```
184163

185164
The `send_from_${type}` endpoint can send bitcoin by:

motoko/basic_bitcoin/src/basic_bitcoin/src/Main.mo

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,12 @@ actor class BasicBitcoin(network : Types.Network) {
8888
await P2tr.get_address(schnorr_canister_actor, NETWORK, KEY_NAME, p2trDerivationPaths());
8989
};
9090

91-
public func send_from_p2tr_address_key_spend(request : SendRequest) : async TransactionId {
92-
Utils.bytesToText(await P2tr.send_key_spend(schnorr_canister_actor, NETWORK, p2trDerivationPaths(), KEY_NAME, request.destination_address, request.amount_in_satoshi));
91+
public func send_from_p2tr_address_key_path(request : SendRequest) : async TransactionId {
92+
Utils.bytesToText(await P2tr.send_key_path(schnorr_canister_actor, NETWORK, p2trDerivationPaths(), KEY_NAME, request.destination_address, request.amount_in_satoshi));
9393
};
9494

95-
public func send_from_p2tr_address_script_spend(request : SendRequest) : async TransactionId {
96-
Utils.bytesToText(await P2tr.send_script_spend(schnorr_canister_actor, NETWORK, p2trDerivationPaths(), KEY_NAME, request.destination_address, request.amount_in_satoshi));
95+
public func send_from_p2tr_address_script_path(request : SendRequest) : async TransactionId {
96+
Utils.bytesToText(await P2tr.send_script_path(schnorr_canister_actor, NETWORK, p2trDerivationPaths(), KEY_NAME, request.destination_address, request.amount_in_satoshi));
9797
};
9898

9999
func p2pkhDerivationPath() : [[Nat8]] {

motoko/basic_bitcoin/src/basic_bitcoin/src/P2tr.mo

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ module {
245245
/// Sends a key spend transaction with a non-empty MAST to the network that
246246
/// transfers the given amount to the given destination, where the source
247247
/// of the funds is the canister itself at the given derivation path.
248-
public func send_key_spend(schnorr_canister_actor : SchnorrCanisterActor, network : Network, derivation_paths : P2trDerivationPaths, key_name : Text, dst_address : BitcoinAddress, amount : Satoshi) : async [Nat8] {
248+
public func send_key_path(schnorr_canister_actor : SchnorrCanisterActor, network : Network, derivation_paths : P2trDerivationPaths, key_name : Text, dst_address : BitcoinAddress, amount : Satoshi) : async [Nat8] {
249249
let internal_bip340_public_key = await fetch_bip340_public_key(schnorr_canister_actor, key_name, derivation_paths.key_path_derivation_path);
250250
let script_bip340_public_key = await fetch_bip340_public_key(schnorr_canister_actor, key_name, derivation_paths.script_path_derivation_path);
251251
let { tweaked_address = own_tweaked_address } = internal_key_and_script_key_to_p2tr_address(internal_bip340_public_key, script_bip340_public_key, network);
@@ -256,13 +256,13 @@ module {
256256
merkle_root_hash = Blob.fromArray(leafHash(leaf_script));
257257
});
258258

259-
await send_key_spend_generic(schnorr_canister_actor, own_tweaked_address, network, derivation_paths.key_path_derivation_path, key_name, ?aux, dst_address, amount);
259+
await send_key_path_generic(schnorr_canister_actor, own_tweaked_address, network, derivation_paths.key_path_derivation_path, key_name, ?aux, dst_address, amount);
260260
};
261261

262262
/// Sends a key spend transaction to the network that transfers the given amount to the
263263
/// given destination, where the source of the funds is the canister itself
264264
/// at the given derivation path.
265-
public func send_key_spend_generic(schnorr_canister_actor : SchnorrCanisterActor, own_address : BitcoinAddress, network : Network, signer_derivation_path : [[Nat8]], key_name : Text, aux : ?Types.SchnorrAux, dst_address : BitcoinAddress, amount : Satoshi) : async [Nat8] {
265+
public func send_key_path_generic(schnorr_canister_actor : SchnorrCanisterActor, own_address : BitcoinAddress, network : Network, signer_derivation_path : [[Nat8]], key_name : Text, aux : ?Types.SchnorrAux, dst_address : BitcoinAddress, amount : Satoshi) : async [Nat8] {
266266
// Get fee percentiles from previous transactions to estimate our own fee.
267267
let fee_percentiles = await BitcoinApi.get_current_fee_percentiles(network);
268268

@@ -313,7 +313,7 @@ module {
313313
/// Sends a script spend transaction to the network that transfers the given amount to the
314314
/// given destination, where the source of the funds is the canister itself
315315
/// at the given derivation path.
316-
public func send_script_spend(schnorr_canister_actor : SchnorrCanisterActor, network : Network, derivation_paths : P2trDerivationPaths, key_name : Text, dst_address : BitcoinAddress, amount : Satoshi) : async [Nat8] {
316+
public func send_script_path(schnorr_canister_actor : SchnorrCanisterActor, network : Network, derivation_paths : P2trDerivationPaths, key_name : Text, dst_address : BitcoinAddress, amount : Satoshi) : async [Nat8] {
317317
// Get fee percentiles from previous transactions to estimate our own fee.
318318
let fee_percentiles = await BitcoinApi.get_current_fee_percentiles(network);
319319

motoko/basic_bitcoin/src/basic_bitcoin/src/P2trKeyOnly.mo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ module {
4141
#bip341({
4242
merkle_root_hash = Blob.fromArray(P2tr.unspendableMerkleRoot(untweaked_bip340_public_key_bytes));
4343
});
44-
await P2tr.send_key_spend_generic(schnorr_canister_actor, own_address, network, derivation_path, key_name, ?aux, dst_address, amount);
44+
await P2tr.send_key_path_generic(schnorr_canister_actor, own_address, network, derivation_path, key_name, ?aux, dst_address, amount);
4545
};
4646

4747
/// Returns the P2TR key-only address of this canister at a specific

0 commit comments

Comments
 (0)