Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: dfinity/examples
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 336d1cc3cc4f4e72ab60a163776e754a26120335
Choose a base ref
..
head repository: dfinity/examples
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 088698b7c80424dc0684a9b4a5024efd1059ba56
Choose a head ref
Showing with 1,635 additions and 1,116 deletions.
  1. +0 −1 .github/CODEOWNERS
  2. +0 −40 .github/workflows/motoko-http_counter-example.yaml
  3. +2 −2 .github/workflows/rust-nft-wallet-example.yml
  4. +1 −1 .ic-commit
  5. +83 −24 motoko/basic_bitcoin/README.md
  6. +1 −3 motoko/basic_bitcoin/mops.toml
  7. +17 −3 motoko/basic_bitcoin/src/basic_bitcoin/basic_bitcoin.did
  8. +4 −4 motoko/basic_bitcoin/src/basic_bitcoin/src/BitcoinApi.mo
  9. +0 −231 motoko/basic_bitcoin/src/basic_bitcoin/src/BitcoinWallet.mo
  10. +1 −1 motoko/basic_bitcoin/src/basic_bitcoin/src/EcdsaApi.mo
  11. +27 −9 motoko/basic_bitcoin/src/basic_bitcoin/src/Main.mo
  12. +222 −0 motoko/basic_bitcoin/src/basic_bitcoin/src/P2pkh.mo
  13. +221 −0 motoko/basic_bitcoin/src/basic_bitcoin/src/P2trRawKeySpend.mo
  14. +277 −0 motoko/basic_bitcoin/src/basic_bitcoin/src/P2trScriptSpend.mo
  15. +52 −0 motoko/basic_bitcoin/src/basic_bitcoin/src/SchnorrApi.mo
  16. +39 −7 motoko/basic_bitcoin/src/basic_bitcoin/src/Types.mo
  17. +11 −5 motoko/basic_bitcoin/src/basic_bitcoin/src/Utils.mo
  18. +0 −31 motoko/http_counter/Makefile
  19. +0 −84 motoko/http_counter/README.md
  20. +0 −8 motoko/http_counter/dfx.json
  21. +0 −149 motoko/http_counter/src/main.mo
  22. +4 −4 motoko/ic-pos/package-lock.json
  23. +1 −1 motoko/ic-pos/package.json
  24. +1 −1 motoko/send_http_get/src/send_http_get_backend/main.mo
  25. +3 −3 motoko/vetkd/src/app_backend/Main.mo
  26. +1 −1 motoko/vetkd/src/app_frontend_js/assets/.ic-assets.json5
  27. +1 −1 motoko/vetkd/src/app_frontend_js/src/.ic-assets.json5
  28. +14 −2 motoko/vetkd/src/app_frontend_js/src/index.js
  29. +2 −2 rust/send_http_get/src/send_http_get_backend/src/lib.rs
  30. +1 −1 rust/vetkd/src/app_frontend_js/assets/.ic-assets.json5
  31. +1 −1 rust/vetkd/src/app_frontend_js/src/.ic-assets.json5
  32. +14 −2 rust/vetkd/src/app_frontend_js/src/index.js
  33. +205 −137 svelte/svelte-motoko-starter/src/frontend/package-lock.json
  34. +428 −356 svelte/svelte-starter/src/frontend/package-lock.json
  35. +1 −1 svelte/svelte-starter/src/frontend/package.json
1 change: 0 additions & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -19,7 +19,6 @@
/motoko/encrypted-notes-dapp-vetkd/ @dfinity/crypto-team
/motoko/encrypted-notes-dapp/ @dfinity/crypto-team
/motoko/hello_cycles/ @dfinity/languages
/motoko/http_counter/ @dfinity/networking
/motoko/ic-pos/ @dfinity/div-Crypto
/motoko/icrc2-swap/ @dfinity/div-Crypto
/motoko/internet_identity_integration/ @dfinity/gix
40 changes: 0 additions & 40 deletions .github/workflows/motoko-http_counter-example.yaml

This file was deleted.

4 changes: 2 additions & 2 deletions .github/workflows/rust-nft-wallet-example.yml
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@ jobs:
- name: Rust NFT Wallet Darwin
run: |
pushd rust/nft-wallet
dfx start --background --host 127.0.0.1:8000
dfx start --background --host 127.0.0.1:4943
bash ./deploy.sh
dfx canister call nftwallet owned_nfts
popd
@@ -44,7 +44,7 @@ jobs:
- name: Rust NFT Wallet Linux
run: |
pushd rust/nft-wallet
dfx start --background --host 127.0.0.1:8000
dfx start --background --host 127.0.0.1:4943
bash ./deploy.sh
dfx canister call nftwallet owned_nfts
popd
2 changes: 1 addition & 1 deletion .ic-commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5b4a6e3a5e984438fa35d80f79ca830cacbf4c70
d6957f09abb7be4698dfc0596956fc2ff1f78994
107 changes: 83 additions & 24 deletions motoko/basic_bitcoin/README.md
Original file line number Diff line number Diff line change
@@ -11,14 +11,18 @@ This tutorial will walk you through how to deploy a sample [canister smart contr

## Architecture

This example internally leverages the [ECDSA API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-ecdsa_public_key)
and [Bitcoin API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin-api) of the Internet Computer.
This example internally leverages the [ECDSA
API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-ecdsa_public_key),
[Schnorr API](https://internetcomputer.org/docs/current/references/ic-interface-spec#ic-sign_with_schnorr), and [Bitcoin
API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin-api)
of the Internet Computer.

For a deeper understanding of the ICP < > BTC integration, see the [Bitcoin integration documentation](/docs/current/developer-docs/multi-chain/bitcoin/overview).

## Prerequisites

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

:::info
This example is designed to be deployed on the mainnet. It will return errors when deployed locally; these errors are expected.
@@ -59,7 +63,12 @@ dfx deploy --network=ic basic_bitcoin --argument '(variant { testnet })'
- `--network=ic` tells the command line to deploy the smart contract to the mainnet ICP blockchain
- `--argument '(variant { Testnet })'` passes the argument `Testnet` to initialize the smart contract, telling it to connect to the Bitcoin testnet

**We're initializing the canister with `variant { Testnet }`, so that the canister connects to the 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.**
**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. This means that the addresses generated
in the smart contract can only be used to receive or send funds only on Bitcoin
testnet.**


If successful, you should see an output that looks like this:
@@ -83,17 +92,48 @@ In the output above, to see the Candid Web UI for your bitcoin canister, you wou

## Step 2: Generating a Bitcoin address

Bitcoin has different types of addresses (e.g. P2PKH, P2SH). Most of these
addresses can be generated from an ECDSA public key. The example code
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.

On the Candid UI of your canister, click the "Call" button under `get_p2pkh_address` to
generate a P2PKH Bitcoin address.
Bitcoin has different types of addresses (e.g. P2PKH, P2SH, P2TR). You may want
to check [this
article](https://bitcoinmagazine.com/technical/bitcoin-address-types-compared-p2pkh-p2sh-p2wpkh-and-more)
if you are interested in a high-level comparison of different address types.
These addresses can be generated from an ECDSA public key or a Schnorr
([BIP340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki))
public key. The example code showcases how your canister can generate and spend
from two types of addresses:
1. 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-method-ecdsa_public_key)
API.
2. A [P2TR
address](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)
where the funds can be spent using the raw (untweaked) internal key
(so-called P2TR key path spend, but untweaked). The advantage of this
approach compared to P2TR script spends is its significantly smaller fee per
transaction because checking the transaction signature is analogous to P2PK
but uses Schnorr instead of ECDSA. IMPORTANT: Note that
[BIP341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-23)
advises against using taproot addresses that can be spent with an untweaked
key. This precaution is to prevent attacks that can occur when creating
taproot multisigner addresses using specific multisignature schemes. However,
the Schnorr API of the internet computer does not support Schnorr
multisignatures.

Note that P2TR *key path* spending with a tweaked key is currently not available
on the IC because the threshold Schnorr signing interface does not allow
applying BIP341 tweaks to the private key. In contrast, the
tweaked public key is used to spend in the script path, which is availble on the
IC. For a technical comparison of different ways of how single-signer P2TR
addresses can be constructed and used, you may want to take a look at [this
post](https://bitcoin.stackexchange.com/a/111100) by Pieter Wuille.

On the Candid UI of your canister, click the "Call" button under
`get_${type}_address` to generate a `${type}` Bitcoin address, where `${type}`
is one of `[p2pkh, p2tr_raw_key_spend]`.

Or, if you prefer the command line:

```bash
dfx canister --network=ic call basic_bitcoin get_p2pkh_address
dfx canister --network=ic call basic_bitcoin get_${type}_address
```

* The Bitcoin address you see will be different from the one above because the
@@ -109,7 +149,8 @@ Now that the canister is deployed and you have a Bitcoin address, it's time to r
some testnet bitcoin. You can use one of the Bitcoin faucets, such as [coinfaucet.eu](https://coinfaucet.eu),
to receive some bitcoin.

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 canister will be receiving 0.011 test BTC on the Bitcoin Testnet.
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
because the ECDSA/Schnorr public key your canister retrieves is unique.


Once the transaction has at least one confirmation, which can take a few minutes,
@@ -121,7 +162,7 @@ You can check a Bitcoin address's balance by using the `get_balance` endpoint on

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

Alternatively, make the call using the command line. Be sure to replace `mheyfRsAQ1XrjtzjfU1cCH2B6G1KmNarNL` with your own generated P2PKH address:
Alternatively, make the call using the command line. Be sure to replace `mheyfRsAQ1XrjtzjfU1cCH2B6G1KmNarNL` with your own generated address:

```bash
dfx canister --network=ic call basic_bitcoin get_balance '("mheyfRsAQ1XrjtzjfU1cCH2B6G1KmNarNL")'
@@ -131,28 +172,33 @@ Checking the balance of a Bitcoin address relies on the [bitcoin_get_balance](ht

## Step 5: Sending bitcoin

You can send bitcoin using the `send` endpoint on your canister.
You can send bitcoin using the `send_from_${type}` endpoint on your canister, where
`${type}` is on of `[p2pkh, p2tr_raw_key_spend]`.

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

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

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

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

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).
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).
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).
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).
3. Building a transaction, using some of the UTXOs from step 2 as input and the destination address and amount to send as output.
The fee percentiles obtained from step 1 are used to set an appropriate fee.
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).
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).

The `send` endpoint returns the ID of the transaction it sent to the network.
You can track the status of this transaction using a block explorer. Once the
4. Signing the inputs of the transaction using the
[sign_with_ecdsa
API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-method-sign_with_ecdsa)/\
[sign_with_schnorr](https://org5p-7iaaa-aaaak-qckna-cai.icp0.io/docs#ic-sign_with_schnorr).
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).

This canister's `send_from_${type}` endpoint returns the ID of the transaction
it sent to the network. You can track the status of this transaction using a
[block explorer](https://en.bitcoin.it/wiki/Block_chain_browser). Once the
transaction has at least one confirmation, you should be able to see it
reflected in your current balance.

@@ -165,7 +211,7 @@ In this tutorial, you were able to:
* Connect the canister to the Bitcoin testnet.
* Send the canister some testnet BTC.
* Check the testnet BTC balance of the canister.
* Use the canister to send testnet BTC to another BTC address.
* Use the canister to send testnet BTC to another testnet BTC address.

This example is extensively documented in the following tutorials:

@@ -179,3 +225,16 @@ If you base your application on this example, we recommend you familiarize yours
For example, the following aspects are particularly relevant for this app:
* [Certify query responses if they are relevant for security](https://internetcomputer.org/docs/current/references/security/general-security-best-practices#certify-query-responses-if-they-are-relevant-for-security), since the app e.g. offers a method to read balances.
* [Use a decentralized governance system like SNS to make a canister have a decentralized controller](https://internetcomputer.org/docs/current/developer-docs/security/security-best-practices/overview)

## Taproot transactions
In addition to P2PKH transactions, this example now also suppots P2TR
transactions, namely the so-called untweaked key path P2TR transactions, which
is the most efficient way of performing a P2TR transaction. The limitation of
this type of transactions is that it cannot be used in combination with scripts.
IMPORTANT: Note that BIP341 advises against using taproot addresses that can be
spent with an untweaked key. This precaution is to prevent attacks that can
occur when creating taproot multisigner addresses using specific multisignature
schemes. However, the Schnorr API of the internet computer does not support
Schnorr multisignatures.

This implementation has only been tested locally with regtest.
4 changes: 1 addition & 3 deletions motoko/basic_bitcoin/mops.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
[dependencies]
base = "0.11.2"
bitcoin = "https://github.com/dfinity/motoko-bitcoin#ad3709363bf980d2cab45cce0dea7eda5c97a7ff"
# sha2 is a transitive dependency and should be removed when `bitcoin` is published on `mops`
sha2 = "0.1.0"
bitcoin = "0.1.0"
20 changes: 17 additions & 3 deletions motoko/basic_bitcoin/src/basic_bitcoin/basic_bitcoin.did
Original file line number Diff line number Diff line change
@@ -33,15 +33,29 @@ type get_utxos_response = record {
};

service : (network) -> {
"get_p2pkh_address": () -> (bitcoin_address);

"get_balance": (address: bitcoin_address) -> (satoshi);

"get_utxos": (bitcoin_address) -> (get_utxos_response);

"get_current_fee_percentiles": () -> (vec millisatoshi_per_vbyte);

"send": (record {
"get_p2pkh_address": () -> (bitcoin_address);

"send_from_p2pkh_address": (record {
destination_address: bitcoin_address;
amount_in_satoshi: satoshi;
}) -> (transaction_id);

"get_p2tr_raw_key_spend_address": () -> (bitcoin_address);

"send_from_p2tr_raw_key_spend_address": (record {
destination_address: bitcoin_address;
amount_in_satoshi: satoshi;
}) -> (transaction_id);

"get_p2tr_script_spend_address": () -> (bitcoin_address);

"send_from_p2tr_script_spend_address": (record {
destination_address: bitcoin_address;
amount_in_satoshi: satoshi;
}) -> (transaction_id);
8 changes: 4 additions & 4 deletions motoko/basic_bitcoin/src/basic_bitcoin/src/BitcoinApi.mo
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ module {
/// Relies on the `bitcoin_get_balance` endpoint.
/// See https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_get_balance
public func get_balance(network : Network, address : BitcoinAddress) : async Satoshi {
ExperimentalCycles.add(GET_BALANCE_COST_CYCLES);
ExperimentalCycles.add<system>(GET_BALANCE_COST_CYCLES);
await management_canister_actor.bitcoin_get_balance({
address;
network;
@@ -49,7 +49,7 @@ module {
/// NOTE: Relies on the `bitcoin_get_utxos` endpoint.
/// See https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_get_utxos
public func get_utxos(network : Network, address : BitcoinAddress) : async GetUtxosResponse {
ExperimentalCycles.add(GET_UTXOS_COST_CYCLES);
ExperimentalCycles.add<system>(GET_UTXOS_COST_CYCLES);
await management_canister_actor.bitcoin_get_utxos({
address;
network;
@@ -63,7 +63,7 @@ module {
/// Relies on the `bitcoin_get_current_fee_percentiles` endpoint.
/// See https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_get_current_fee_percentiles
public func get_current_fee_percentiles(network : Network) : async [MillisatoshiPerVByte] {
ExperimentalCycles.add(GET_CURRENT_FEE_PERCENTILES_COST_CYCLES);
ExperimentalCycles.add<system>(GET_CURRENT_FEE_PERCENTILES_COST_CYCLES);
await management_canister_actor.bitcoin_get_current_fee_percentiles({
network;
})
@@ -77,7 +77,7 @@ module {
let transaction_fee =
SEND_TRANSACTION_BASE_COST_CYCLES + transaction.size() * SEND_TRANSACTION_COST_CYCLES_PER_BYTE;

ExperimentalCycles.add(transaction_fee);
ExperimentalCycles.add<system>(transaction_fee);
await management_canister_actor.bitcoin_send_transaction({
network;
transaction;
Loading