Skip to content

Commit 97a035f

Browse files
authored
Merge pull request #932 from altkdf/alex/add-taproot-transactions
feat(schnorr,bitcoin): CRP-2533 add taproot transactions and improvements
2 parents 37e2e3b + cc91428 commit 97a035f

File tree

15 files changed

+1040
-316
lines changed

15 files changed

+1040
-316
lines changed

rust/basic_bitcoin/Cargo.lock

Lines changed: 28 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/basic_bitcoin/Makefile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,32 @@ all: deploy
66
deploy:
77
dfx deploy basic_bitcoin --argument '(variant { regtest })'
88

9+
.PHONY: regtest_topup
10+
.SILENT: regtest_topup
11+
regtest_topup:
12+
P2PKH_ADDR=$(shell dfx canister call basic_bitcoin get_p2pkh_address | tr -d '()') && \
13+
P2TR_SCRIPT_SPEND_ADDR=$(shell dfx canister call basic_bitcoin get_p2tr_script_spend_address | tr -d '()') && \
14+
P2TR_SCRIPT_KEY_SPEND_ADDR=$(shell dfx canister call basic_bitcoin get_p2tr_raw_key_spend_address | tr -d '()') && \
15+
TOPUP_CMD_P2PKH="bitcoin-cli -rpcport=8333 sendtoaddress $${P2PKH_ADDR} 1" && \
16+
TOPUP_CMD_P2TR_SCRIPT_SPEND="bitcoin-cli -rpcport=8333 sendtoaddress $${P2TR_SCRIPT_SPEND_ADDR} 1" && \
17+
TOPUP_CMD_P2TR_RAW_KEY_SPEND="bitcoin-cli -rpcport=8333 sendtoaddress $${P2TR_SCRIPT_KEY_SPEND_ADDR} 1" && \
18+
eval "$${TOPUP_CMD_P2PKH}" && \
19+
eval "$${TOPUP_CMD_P2PKH}" && \
20+
eval "$${TOPUP_CMD_P2PKH}" && \
21+
eval "$${TOPUP_CMD_P2TR_SCRIPT_SPEND}" && \
22+
eval "$${TOPUP_CMD_P2TR_SCRIPT_SPEND}" && \
23+
eval "$${TOPUP_CMD_P2TR_SCRIPT_SPEND}" && \
24+
eval "$${TOPUP_CMD_P2TR_RAW_KEY_SPEND}" && \
25+
eval "$${TOPUP_CMD_P2TR_RAW_KEY_SPEND}" && \
26+
eval "$${TOPUP_CMD_P2TR_RAW_KEY_SPEND}" && \
27+
bitcoin-cli -rpcport=8333 -generate 6
28+
929
.PHONY: clean
1030
.SILENT: clean
1131
clean:
1232
rm -rf .dfx
33+
rm -rf dist
34+
rm -rf node_modules
35+
rm -rf src/declarations
36+
rm -f .env
1337
cargo clean

rust/basic_bitcoin/README.md

Lines changed: 104 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,27 @@ This tutorial will walk you through how to deploy a sample [canister smart contr
1111

1212
## Architecture
1313

14-
This example internally leverages the [ECDSA API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-ecdsa_public_key)
15-
and [Bitcoin API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin-api) of the Internet Computer.
14+
This example internally leverages the [ECDSA
15+
API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-ecdsa_public_key),
16+
[Schnorr API](https://org5p-7iaaa-aaaak-qckna-cai.icp0.io/docs#ic-sign_with_schnorr), and [Bitcoin
17+
API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin-api)
18+
of the Internet Computer.
1619

1720
For a deeper understanding of the ICP < > BTC integration, see the [Bitcoin integration documentation](https://wiki.internetcomputer.org/wiki/Bitcoin_Integration).
1821

1922
## Prerequisites
2023

21-
* [x] Install the [IC SDK](https://internetcomputer.org/docs/current/developer-docs/setup/install/index.mdx).
24+
* [x] Install the [IC
25+
SDK](https://internetcomputer.org/docs/current/developer-docs/setup/install/index.mdx).
26+
For local testing, `dfx >= 0.22.0-beta.0` is required.
2227

2328
## Step 1: Building and deploying sample code
2429

2530
### Clone the smart contract
2631

27-
This tutorial has the **same smart contract** written in different programming languages: in [Motoko](https://internetcomputer.org/docs/current/developer-docs/backend/motoko/index.md) and [Rust](https://internetcomputer.org/docs/current/developer-docs/backend/rust/index.md).
32+
This tutorial has the same smart contract written in
33+
[Motoko](https://internetcomputer.org/docs/current/developer-docs/backend/motoko/index.md)
34+
using ECDSA and Bitcoin API but currently not Schnorr API.
2835

2936
You can clone and deploy either one, as they both function in the same way.
3037

@@ -33,11 +40,9 @@ To clone and build the smart contract in **Rust**:
3340
```bash
3441
git clone https://github.com/dfinity/examples
3542
cd examples/rust/basic_bitcoin
36-
git submodule update --init --recursive
43+
cargo build --release --target wasm32-unknown unknown
3744
```
3845

39-
**If you choose Rust and are using MacOS, you'll need to install Homebrew and run `brew install llvm` to be able to compile the example.**
40-
4146
### Acquire cycles to deploy
4247

4348
Deploying to the Internet Computer requires [cycles](https://internetcomputer.org/docs/current/developer-docs/setup/cycles) (the equivalent of "gas" in other blockchains). You can get free cycles from the [cycles faucet](https://internetcomputer.org/docs/current/developer-docs/setup/cycles/cycles-faucet.md).
@@ -53,8 +58,12 @@ dfx deploy --network=ic basic_bitcoin --argument '(variant { testnet })'
5358
- `--network=ic` tells the command line to deploy the smart contract to the mainnet ICP blockchain
5459
- `--argument '(variant { testnet })'` passes the argument `testnet` to initialize the smart contract, telling it to connect to the Bitcoin testnet
5560

56-
**We're initializing the canister with `variant { testnet }` so that the canister connects to the [Bitcoin testnet](https://en.bitcoin.it/wiki/Testnet). To be specific, this connects to `Testnet3`, which is the current Bitcoin test network used by the Bitcoin community.**
57-
61+
**We're initializing the canister with `variant { testnet }` so that the
62+
canister connects to the [Bitcoin testnet](https://en.bitcoin.it/wiki/Testnet).
63+
To be specific, this connects to `Testnet3`, which is the current Bitcoin test
64+
network used by the Bitcoin community. This means that the addresses generated
65+
in the smart contract can only be used to receive or send funds only on Bitcoin
66+
testnet.**
5867

5968
If successful, you should see an output that looks like this:
6069

@@ -70,80 +79,134 @@ Candid:
7079

7180
Your canister is live and ready to use! You can interact with it using either the command line or using the Candid UI, which is the link you see in the output above.
7281

73-
In the output above, to see the Candid Web UI for your bitcoin canister, you would use the URL `https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.icp0.io/?id=<YOUR-CANISTER-ID>`. Here are the two methods you will see:
82+
In the output above, to see the Candid Web UI for your bitcoin canister, you
83+
would use the URL
84+
`https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.icp0.io/?id=<YOUR-CANISTER-ID>`. Candid
85+
Web UI will contain all methods implemented by the canister.
7486

75-
* `public_key`
76-
* `sign`
87+
The `basic_bitcoin` example is deployed on mainnet for illustration purposes and
88+
is interacting with Bitcoin testnet. It has the URL
89+
https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.icp0.io/?id=vvha6-7qaaa-aaaap-ahodq-cai
90+
and serves up the Candid web UI for this particular canister deployed on
91+
mainnet.
7792

7893
## Step 2: Generating a Bitcoin address
7994

80-
Bitcoin has different types of addresses (e.g. P2PKH, P2SH). Most of these
81-
addresses can be generated from an ECDSA public key. The example code
82-
showcases how your canister can generate a [P2PKH address](https://en.bitcoin.it/wiki/Transaction#Pay-to-PubkeyHash) using the [ecdsa_public_key](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-ecdsa_public_key) API.
83-
84-
On the Candid UI of your canister, click the "Call" button under `get_p2pkh_address` to
85-
generate a P2PKH Bitcoin address.
95+
Bitcoin has different types of addresses (e.g. P2PKH, P2SH, P2TR). You may want
96+
to check [this
97+
article](https://bitcoinmagazine.com/technical/bitcoin-address-types-compared-p2pkh-p2sh-p2wpkh-and-more)
98+
if you are interested in a high-level comparison of different address types.
99+
These addresses can be generated from an ECDSA public key or a Schnorr
100+
([BIP340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki))
101+
public key. The example code showcases how your canister can generate and spend
102+
from three types of addresses:
103+
1. A [P2PKH address](https://en.bitcoin.it/wiki/Transaction#Pay-to-PubkeyHash)
104+
using the
105+
[ecdsa_public_key](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-method-ecdsa_public_key)
106+
API.
107+
2. A [P2TR
108+
address](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)
109+
where the funds can be spent using the raw (untweaked) internal key
110+
(so-called P2TR key path spend, but untweaked). The advantage of this
111+
approach compared to the one below is its significantly smaller fee per
112+
transaction because checking the transaction signature is analogous to P2PK
113+
but uses Schnorr instead of ECDSA. IMPORTANT: Note that
114+
[BIP341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-23)
115+
advises against using taproot addresses that can be spent with an untweaked
116+
key. This precaution is to prevent attacks that can occur when creating
117+
taproot multisigner addresses using specific multisignature schemes. However,
118+
the Schnorr API of the internet computer does not support Schnorr
119+
multisignatures.
120+
3. A [P2TR
121+
address](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)
122+
where the funds can be spent using the provided public key with the script
123+
path, where the Merkelized Alternative Script Tree (MAST) consists of a
124+
single script allowing to spend funds by exactly one key.
125+
126+
Note that P2TR *key path* spending with a tweaked key is currently not available
127+
on the IC because the threshold Schnorr signing interface does not allow
128+
applying BIP341 tweaks to the private key. In contrast, the
129+
tweaked public key is used to spend in the script path, which is availble on the
130+
IC. For a technical comparison of different ways of how single-signer P2TR
131+
addresses can be constructed and used, you may want to take a look at [this
132+
post](https://bitcoin.stackexchange.com/a/111100) by Pieter Wuille.
133+
134+
On the Candid UI of your canister, click the "Call" button under
135+
`get_${type}_address` to generate a `${type}` Bitcoin address, where `${type}`
136+
is one of `[p2pkh, p2tr_raw_key_spend, p2tr_script_spend]`.
86137

87138
Or, if you prefer the command line:
88-
89-
dfx canister --network=ic call basic_bitcoin get_p2pkh_address
90-
91-
* The Bitcoin address you see will be different from the one above because the
92-
ECDSA public key your canister retrieves is unique.
93-
94-
* We are generating a Bitcoin testnet address, which can only be
95-
used for sending/receiving Bitcoin on the Bitcoin testnet.
96-
139+
`dfx canister --network=ic call basic_bitcoin get_${type}_address`
97140

98141
## Step 3: Receiving bitcoin
99142

100143
Now that the canister is deployed and you have a Bitcoin address, it's time to receive
101144
some testnet bitcoin. You can use one of the Bitcoin faucets, such as [coinfaucet.eu](https://coinfaucet.eu),
102145
to receive some bitcoin.
103146

104-
Enter your address and click on "Send testnet bitcoins". This example will use the Bitcoin address `n31eU1K11m1r58aJMgTyxGonu7wSMoUYe7`, but you will use your address. The canister will be receiving 0.011 test BTC on the Bitcoin Testnet.
147+
Enter your address and click on "Send testnet bitcoins". This example will use
148+
the Bitcoin P2PHK address `mot21Ef7HNDpDJa4CBzt48WpEX7AxNyaqx`, but you will use
149+
your address. The Bitcoin address you see will be different from the one above
150+
because the ECDSA/Schnorr public key your canister retrieves is unique.
105151

106152
Once the transaction has at least one confirmation, which can take a few minutes,
107153
you'll be able to see it in your canister's balance.
108154

155+
The addresses that have been used for the testing of this canister on Bitcoin
156+
testnet are `mot21Ef7HNDpDJa4CBzt48WpEX7AxNyaqx` (P2PKH,
157+
[transactions](https://blockstream.info/testnet/address/mot21Ef7HNDpDJa4CBzt48WpEX7AxNyaqx)),
158+
`tb1pkkrwg6e9s5zf3jmftu224rc5ppax26g5yzdg03rhmqw84359xgpsv5mn2y` (P2TR raw key
159+
spend,
160+
[transactions](https://blockstream.info/testnet/address/tb1pkkrwg6e9s5zf3jmftu224rc5ppax26g5yzdg03rhmqw84359xgpsv5mn2y)),
161+
and `tb1pnm743sjkw9tq3zf9uyetgqkrx7tauthmxnsl5dtyrwnyz9r7lu8qdtcnnc` (P2TR
162+
script path spend,
163+
[transactions](https://blockstream.info/testnet/address/tb1pnm743sjkw9tq3zf9uyetgqkrx7tauthmxnsl5dtyrwnyz9r7lu8qdtcnnc)).
164+
It may be useful to click on "transactions" links if you are interested in how
165+
they are structured.
166+
109167
## Step 4: Checking your bitcoin balance
110168

111169
You can check a Bitcoin address's balance by using the `get_balance` endpoint on your canister.
112170

113171
In the Candid UI, paste in your canister's address, and click on "Call":
114172

115-
Alternatively, make the call using the command line. Be sure to replace `mheyfRsAQ1XrjtzjfU1cCH2B6G1KmNarNL` with your own generated P2PKH address:
173+
Alternatively, make the call using the command line. Be sure to replace `mot21Ef7HNDpDJa4CBzt48WpEX7AxNyaqx` with your own generated address:
116174

117175
```bash
118-
dfx canister --network=ic call basic_bitcoin get_balance '("mheyfRsAQ1XrjtzjfU1cCH2B6G1KmNarNL")'
176+
dfx canister --network=ic call basic_bitcoin get_balance '("mot21Ef7HNDpDJa4CBzt48WpEX7AxNyaqx")'
119177
```
120178

121179
Checking the balance of a Bitcoin address relies on the [bitcoin_get_balance](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_get_balance) API.
122180

123181
## Step 5: Sending bitcoin
124182

125-
You can send bitcoin using the `send` endpoint on your canister.
183+
You can send bitcoin using the `send_from_${type}` endpoint on your canister, where
184+
`${type}` is on of `[p2pkh, p2tr_raw_key_spend, p2tr_script_spend]`.
126185

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

130189
Via command line, the same call would look like this:
131190

132191
```bash
133-
dfx canister --network=ic call basic_bitcoin send '(record { destination_address = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt"; amount_in_satoshi = 4321; })'
192+
dfx canister --network=ic call basic_bitcoin send_from_p2pkh '(record { destination_address = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt"; amount_in_satoshi = 4321; })'
134193
```
135194

136-
The `send` endpoint can send bitcoin by:
195+
The `send_from_${type}` endpoint can send bitcoin by:
137196

138-
1. Getting the percentiles of the most recent fees on the Bitcoin network using the [bitcoin_get_current_fee_percentiles API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_get_current_fee_percentiles).
139-
2. Fetching your unspent transaction outputs (UTXOs), using the [bitcoin_get_utxos API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_get_utxos).
197+
1. Getting the percentiles of the most recent fees on the Bitcoin network using the [bitcoin_get_current_fee_percentiles API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-method-bitcoin_get_current_fee_percentiles).
198+
2. Fetching your unspent transaction outputs (UTXOs), using the [bitcoin_get_utxos API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-method-bitcoin_get_utxos).
140199
3. Building a transaction, using some of the UTXOs from step 2 as input and the destination address and amount to send as output.
141200
The fee percentiles obtained from step 1 is used to set an appropriate fee.
142-
4. Signing the inputs of the transaction using the [sign_with_ecdsa API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-sign_with_ecdsa).
143-
5. Sending the signed transaction to the Bitcoin network using the [bitcoin_send_transaction API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_send_transaction).
144-
145-
The `send` endpoint returns the ID of the transaction it sent to the network.
146-
You can track the status of this transaction using a block explorer. Once the
201+
4. Signing the inputs of the transaction using the
202+
[sign_with_ecdsa
203+
API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-method-sign_with_ecdsa)/\
204+
[sign_with_schnorr](https://org5p-7iaaa-aaaak-qckna-cai.icp0.io/docs#ic-sign_with_schnorr).
205+
5. Sending the signed transaction to the Bitcoin network using the [bitcoin_send_transaction API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-method-bitcoin_send_transaction).
206+
207+
This canister's `send_from_${type}` endpoint returns the ID of the transaction
208+
it sent to the network. You can track the status of this transaction using a
209+
[block explorer](https://en.bitcoin.it/wiki/Block_chain_browser). Once the
147210
transaction has at least one confirmation, you should be able to see it
148211
reflected in your current balance.
149212

@@ -156,7 +219,7 @@ In this tutorial, you were able to:
156219
* Connect the canister to the Bitcoin testnet.
157220
* Send the canister some testnet BTC.
158221
* Check the testnet BTC balance of the canister.
159-
* Use the canister to send testnet BTC to another BTC address.
222+
* Use the canister to send testnet BTC to another testnet BTC address.
160223

161224
This example is extensively documented in the following tutorials:
162225

0 commit comments

Comments
 (0)