Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update v1.0.0 with latest changes #35

Merged
merged 9 commits into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,16 @@ all-deploy-and-pytest:

cd backend/donation_canister && \
dfx deploy donation_canister --argument '(variant { regtest })' && \
dfx canister update-settings --add-controller be2us-64aaa-aaaaa-qaabq-cai donation_canister && \
donation_canister_id=$$(dfx canister id donation_canister) && \
echo "donation_canister_id: $${donation_canister_id}" && \
argument_string=$$'("'$${donation_canister_id}'")' && \
echo "argument_string: $${argument_string}" && \
cd ../donation_tracker_canister && \
dfx deploy donation_tracker_canister --argument $${argument_string} && \
dfx canister call donation_tracker_canister initRecipients
dfx canister call donation_tracker_canister initRecipients && \
donation_tracker_canister_id=$$(dfx canister id donation_tracker_canister) && \
echo "donation_tracker_canister_id: $${donation_tracker_canister_id}"

pytest

Expand All @@ -95,6 +98,13 @@ all-deploy-and-pytest:
-datadir=$(CURDIR)/bitcoin-$(VERSION_BITCOIN)/data \
stop

.PHONY: bitcoin-core-start
bitcoin-core-start:
bitcoin-$(VERSION_BITCOIN)/bin/bitcoind \
-conf=$(CURDIR)/bitcoin-$(VERSION_BITCOIN)/bitcoin.conf \
-datadir=$(CURDIR)/bitcoin-$(VERSION_BITCOIN)/data \
--port=18444

.PHONY: bitcoin-core-stop
bitcoin-core-stop:
bitcoin-$(VERSION_BITCOIN)/bin/bitcoin-cli \
Expand Down Expand Up @@ -149,7 +159,7 @@ install-bitcoin-core:
# Make sure to source ~/.profile afterwards -> it adds ~/bin to the path if it exists
.PHONY: install-dfx
install-dfx:
sh -ci "$$(curl -fsSL https://sdk.dfinity.org/install.sh)"
DFXVM_INIT_YES=true sh -ci "$$(curl -fsSL https://sdk.dfinity.org/install.sh)"

.PHONY: install-didc
install-didc:
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

![Alt text](frontend/src/donation_frontend/assets/BitcoinDonationApp_banner.png)

[Try it out!](https://5rsod-ciaaa-aaaai-acrdq-cai.icp0.io/)

# The bitcoin-core network

To run locally, you must start up your own, local bitcoin-core network.
See instructions in backend/donation_canister/README.md

# The frontend & backend canisters

The dApp consists out of 3 canister, and each canister is treated as a standalone project.
Expand Down
77 changes: 39 additions & 38 deletions backend/donation_canister/README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
# Donation Canister

## References
- Initial version based on: https://github.com/dfinity/examples/tree/master/motoko/basic_bitcoin
- Hackathon PDF refers to:
- https://internetcomputer.org/how-it-works/bitcoin-integration/
- https://internetcomputer.org/docs/current/tutorials/developer-journey/level-4/4.3-ckbtc-and-bitcoin/

- README of bitcoin_basic example refers to:
- [Deploying your first Bitcoin dapp](https://internetcomputer.org/docs/current/samples/deploying-your-first-bitcoin-dapp).
- [Developing Bitcoin dapps locally](https://internetcomputer.org/docs/current/developer-docs/integrations/bitcoin/local-development).

## Architecture

This example internally leverages the [ECDSA API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-ecdsa_public_key)
This canister is a slightly modified version of [basic_bitcoin](https://github.com/dfinity/examples/tree/master/motoko/basic_bitcoin) smart contract, which 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.

For deeper understanding of the ICP < > BTC integration, see the IC wiki article on [Bitcoin integration](https://wiki.internetcomputer.org/wiki/Bitcoin_Integration).

## Step 0: Set up a local Bitcoin network

- Install the [IC SDK](https://internetcomputer.org/docs/current/developer-docs/setup/install/index.mdx).
- [Set up a local Bitcoin network](https://internetcomputer.org/docs/current/tutorials/developer-journey/level-4/4.3-ckbtc-and-bitcoin/#setting-up-a-local-bitcoin-network):

NOTE: On mac I have not been able to get it to run, due to security checks...

```bash
# On linux, you can run
make install-bitcoin-core

# then start it with:
make bitcoin-core-start
```

## Step 1: Building and deploying donations canister

Expand All @@ -38,6 +34,7 @@ cd backend/donation_canister
```

Note on the submodule:

- The donation_canister depends on [motoko-bitcoin](https://github.com/tgalal/motoko-bitcoin)
- I added it to the repo as a submodule with:
```bash
Expand All @@ -62,32 +59,38 @@ We use multiple canisters that we deploy separately on a shared local network. C

File: ~/.config/dfx/networks.json
Note: log_level options are: "critical", "error", "warning", "info", "debug", "trace"

```json
{
"local": {
"bitcoin": {
"enabled": true,
"log_level": "error",
"nodes": [
"127.0.0.1:18444"
]
"nodes": ["127.0.0.1:18444"]
}
}
}
```
### Deploy local

### Deploy donation_canister locally

[Reference](https://internetcomputer.org/docs/current/tutorials/developer-journey/level-4/4.3-ckbtc-and-bitcoin/#deploying-the-example-canister)

```bash
# From the bitcoin-core/bitcoin-25.0 folder, start the local bitcoin instance
# From the bitcoin-25.0 folder, start the local bitcoin instance
./bin/bitcoind -conf=$(pwd)/bitcoin.conf -datadir=$(pwd)/data --port=18444

# Start the local dfx network
dfx start --clean

# From the backend/donation_canister folder:
dfx deploy donation_canister --argument '(variant { regtest })'

# Add the donation_tracker_canister as a controller, for access to the `send` method
dfx canister update-settings --add-controller be2us-64aaa-aaaaa-qaabq-cai donation_canister

# Generate the bindings used by the frontend
dfx generate
```

### Exercise it with dfx
Expand All @@ -98,7 +101,7 @@ $ dfx canister call donation_canister get_p2pkh_address
("mkkzk2xTQcrrRYv8FPnj22ujHhkZwsETtX")

# receiving BTC
# From your local bitcoin-core folder, issue this command, replacing BTC_ADDRESS with yours
# From bitcoin-25.0 issue this command, replacing BTC_ADDRESS with yours
./bin/bitcoin-cli -conf=$(pwd)/bitcoin.conf generatetoaddress 1 BTC_ADDRESS
# eg.
$ ./bin/bitcoin-cli -conf=$(pwd)/bitcoin.conf generatetoaddress 1 mkkzk2xTQcrrRYv8FPnj22ujHhkZwsETtX
Expand All @@ -115,22 +118,23 @@ $ dfx canister call donation_canister get_balance '("mkkzk2xTQcrrRYv8FPnj22ujHhk

---

### Deploy the smart contract to the Internet Computer

This is done only once.
### Deploy donation_canister to the Internet Computer

```bash
dfx deploy --network=ic -m reinstall donation_canister --argument '(variant { testnet })'

# Add the donation_tracker_canister as a controller, for access to the `send` method
dfx canister --network=ic update-settings --add-controller fj5jn-2qaaa-aaaag-acmfq-cai donation_canister
```

#### What this does

- `dfx deploy` tells the command line interface to `deploy` the smart contract
- `--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.**


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

```bash
Expand All @@ -147,8 +151,8 @@ Your canister is live and ready to use! You can interact with it using either th

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:

* `public_key`
* `sign`
- `public_key`
- `sign`

## Step 2: Generating a Bitcoin address

Expand All @@ -165,12 +169,11 @@ Or, if you prefer the command line:
dfx canister --network=ic call donation_canister get_p2pkh_address
```

* The Bitcoin address you see will be different from the one above, because the
- The Bitcoin address you see will be different from the one above, because the
ECDSA public key your canister retrieves is unique.

* We are generating a Bitcoin testnet address, which can only be
used for sending/receiving Bitcoin on the Bitcoin testnet.

- We are generating a Bitcoin testnet address, which can only be
used for sending/receiving Bitcoin on the Bitcoin testnet.

## Step 3: Receiving bitcoin

Expand All @@ -180,7 +183,6 @@ 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 would use your own address. The canister will be receiving 0.011 test BTC on the Bitcoin Testnet.


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

Expand Down Expand Up @@ -225,11 +227,10 @@ You can track the status of this transaction using a block explorer. Once the
transaction has at least one confirmation, you should be able to see it
reflected in your current balance.

## References

## Security considerations and best practices

If you base your application on this example, we recommend you familiarize yourself with and adhere to the [security best practices](https://internetcomputer.org/docs/current/references/security/) for developing on the Internet Computer. This example may not implement all the best practices.

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 method to read balances.
* [Use a decentralized governance system like SNS to make a canister have a decentralized controller](https://internetcomputer.org/docs/current/references/security/rust-canister-development-security-best-practices#use-a-decentralized-governance-system-like-sns-to-make-a-canister-have-a-decentralized-controller), since decentralized control may be essential for canisters holding Bitcoin on behalf of users.
- [basic_bitcoin](https://github.com/dfinity/examples/tree/master/motoko/basic_bitcoin)
- [bitcoin-integration](https://internetcomputer.org/how-it-works/bitcoin-integration/)
- [ckbtc-and-bitcoin](https://internetcomputer.org/docs/current/tutorials/developer-journey/level-4/4.3-ckbtc-and-bitcoin/)
- [Deploying your first Bitcoin dapp](https://internetcomputer.org/docs/current/samples/deploying-your-first-bitcoin-dapp).
- [Developing Bitcoin dapps locally](https://internetcomputer.org/docs/current/developer-docs/integrations/bitcoin/local-development).
3 changes: 2 additions & 1 deletion backend/donation_canister/canister_ids.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"donation_canister": {
"ic": "ekral-oiaaa-aaaag-acmda-cai"
"ic": "ekral-oiaaa-aaaag-acmda-cai",
"local": "bkyz2-fmaaa-aaaaa-qaaaq-cai"
}
}
14 changes: 11 additions & 3 deletions backend/donation_canister/src/Main.mo
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,16 @@ actor class BasicBitcoin(_network : Types.Network) {
};

/// Sends the given amount of bitcoin from this canister to the given address.
/// Returns the transaction ID.
public func send(request : SendRequest) : async Text {
Utils.bytesToText(await BitcoinWallet.send(NETWORK, DERIVATION_PATH, KEY_NAME, request.destination_address, request.amount_in_satoshi));
/// Only controllers of this canister allowed to call.
/// - Note that the donation_tracker_canister will be a controller.
/// Returns a SendRecordResult, containing the transaction ID
public shared (msg) func send(request : SendRequest) : async Types.SendRecordResult {
if (not Principal.isController(msg.caller)) {
return #Err(#Unauthorized);
};
let txid = Utils.bytesToText(await BitcoinWallet.send(NETWORK, DERIVATION_PATH, KEY_NAME, request.destination_address, request.amount_in_satoshi));

let sendRecord = { txid = txid };
return #Ok(sendRecord);
};
};
8 changes: 8 additions & 0 deletions backend/donation_canister/src/Types.mo
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ module Types {
amount_in_satoshi : Satoshi;
};

//-------------------------------------------------------------------------
public type SendRecord = {
txid : Text;
};

public type SendRecordResult = Result<SendRecord, ApiError>;

//-------------------------------------------------------------------------
public type ECDSAPublicKeyReply = {
public_key : Blob;
chain_code : Blob;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
type Utxo =
record {
height: nat32;
outpoint: OutPoint;
value: Satoshi;
};
type SendRequest =
record {
amount_in_satoshi: Satoshi;
destination_address: text;
};
type SendRecordResult =
variant {
Err: ApiError;
Ok: SendRecord;
};
type SendRecord = record {txid: text;};
type Satoshi__1 = nat64;
type Satoshi = nat64;
type Page = vec nat8;
type OutPoint =
record {
txid: blob;
vout: nat32;
};
type Network =
variant {
mainnet;
regtest;
testnet;
};
type MillisatoshiPerVByte = nat64;
type GetUtxosResponse =
record {
next_page: opt Page;
tip_block_hash: BlockHash;
tip_height: nat32;
utxos: vec Utxo;
};
type BlockHash = vec nat8;
type BitcoinAddress = text;
type BasicBitcoin =
service {
amiController: () -> (AuthRecordResult);
/// Returns the balance of the given Bitcoin address.
get_balance: (BitcoinAddress) -> (Satoshi__1);
/// Returns the 100 fee percentiles measured in millisatoshi/vbyte.
/// Percentiles are computed from the last 10,000 transactions (if available).
get_current_fee_percentiles: () -> (vec MillisatoshiPerVByte);
/// Returns the P2PKH address of this canister at a specific derivation path.
get_p2pkh_address: () -> (BitcoinAddress);
/// Returns the UTXOs of the given Bitcoin address.
get_utxos: (BitcoinAddress) -> (GetUtxosResponse);
/// Sends the given amount of bitcoin from this canister to the given address.
/// Only controllers of this canister allowed to call.
/// - Note that the donation_tracker_canister will be a controller.
/// Returns a SendRecordResult, containing the transaction ID
send: (SendRequest) -> (SendRecordResult);
whoami: () -> (principal);
};
type AuthRecordResult =
variant {
Err: ApiError;
Ok: AuthRecord;
};
type AuthRecord = record {auth: text;};
type ApiError =
variant {
InvalidId;
Other: text;
Unauthorized;
ZeroAddress;
};
service : (Network) -> BasicBitcoin
Loading
Loading