From fd21b3ee4048ecb7c0eea62b31393c720cf0f7dd Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Tue, 16 Jun 2026 19:50:00 +0200 Subject: [PATCH] chore: migrate rust/basic_bitcoin to icp-cli - Replace dfx.json with icp.yaml (Bitcoin integration via custom Docker image bundling bitcoind, matching motoko/basic_bitcoin pattern) - Move Rust source from src/ into backend/ subdirectory - Replace root Cargo.toml with workspace + backend/Cargo.toml (name=backend) - Copy Dockerfile and docker/start.sh from motoko/basic_bitcoin - Rewrite Makefile with icp-cli commands and 9 numbered tests - Update README with icp-cli instructions, preserve all domain content - Add rust-basic_bitcoin CI job to .github/workflows/basic_bitcoin.yml - Delete dfx.json, build.sh, basic_bitcoin.did, old src/ Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/basic_bitcoin.yml | 34 ++++ rust/basic_bitcoin/Cargo.lock | 2 +- rust/basic_bitcoin/Cargo.toml | 23 +-- rust/basic_bitcoin/Dockerfile | 32 +++ rust/basic_bitcoin/Makefile | 118 +++++++---- rust/basic_bitcoin/README.md | 184 +++++++----------- rust/basic_bitcoin/backend/Cargo.toml | 18 ++ rust/basic_bitcoin/{ => backend}/src/brc20.rs | 0 .../basic_bitcoin/{ => backend}/src/common.rs | 0 rust/basic_bitcoin/{ => backend}/src/ecdsa.rs | 0 rust/basic_bitcoin/{ => backend}/src/lib.rs | 0 .../{ => backend}/src/ordinals.rs | 0 rust/basic_bitcoin/{ => backend}/src/p2pkh.rs | 0 rust/basic_bitcoin/{ => backend}/src/p2tr.rs | 0 .../basic_bitcoin/{ => backend}/src/p2wpkh.rs | 0 rust/basic_bitcoin/{ => backend}/src/runes.rs | 0 .../{ => backend}/src/schnorr.rs | 0 .../{ => backend}/src/service.rs | 0 .../{ => backend}/src/service/etch_rune.rs | 0 .../{ => backend}/src/service/get_balance.rs | 0 .../src/service/get_block_headers.rs | 0 .../src/service/get_blockchain_info.rs | 0 .../service/get_current_fee_percentiles.rs | 0 .../src/service/get_p2pkh_address.rs | 0 .../service/get_p2tr_key_path_only_address.rs | 0 .../get_p2tr_script_path_enabled_address.rs | 0 .../src/service/get_p2wpkh_address.rs | 0 .../{ => backend}/src/service/get_utxos.rs | 0 .../src/service/inscribe_brc20.rs | 0 .../src/service/inscribe_ordinal.rs | 0 .../src/service/send_from_p2pkh_address.rs | 0 .../send_from_p2tr_key_path_only_address.rs | 0 ...r_script_path_enabled_address_key_spend.rs | 0 ...cript_path_enabled_address_script_spend.rs | 0 .../src/service/send_from_p2wpkh_address.rs | 0 rust/basic_bitcoin/basic_bitcoin.did | 110 ----------- rust/basic_bitcoin/build.sh | 17 -- rust/basic_bitcoin/dfx.json | 32 --- rust/basic_bitcoin/docker/start.sh | 30 +++ rust/basic_bitcoin/icp.yaml | 27 +++ 40 files changed, 294 insertions(+), 333 deletions(-) create mode 100644 rust/basic_bitcoin/Dockerfile create mode 100644 rust/basic_bitcoin/backend/Cargo.toml rename rust/basic_bitcoin/{ => backend}/src/brc20.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/common.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/ecdsa.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/lib.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/ordinals.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/p2pkh.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/p2tr.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/p2wpkh.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/runes.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/schnorr.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/etch_rune.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/get_balance.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/get_block_headers.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/get_blockchain_info.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/get_current_fee_percentiles.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/get_p2pkh_address.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/get_p2tr_key_path_only_address.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/get_p2tr_script_path_enabled_address.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/get_p2wpkh_address.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/get_utxos.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/inscribe_brc20.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/inscribe_ordinal.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/send_from_p2pkh_address.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/send_from_p2tr_key_path_only_address.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/send_from_p2tr_script_path_enabled_address_key_spend.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/send_from_p2tr_script_path_enabled_address_script_spend.rs (100%) rename rust/basic_bitcoin/{ => backend}/src/service/send_from_p2wpkh_address.rs (100%) delete mode 100644 rust/basic_bitcoin/basic_bitcoin.did delete mode 100755 rust/basic_bitcoin/build.sh delete mode 100644 rust/basic_bitcoin/dfx.json create mode 100644 rust/basic_bitcoin/docker/start.sh create mode 100644 rust/basic_bitcoin/icp.yaml diff --git a/.github/workflows/basic_bitcoin.yml b/.github/workflows/basic_bitcoin.yml index 4733e1fa08..3f43fab4da 100644 --- a/.github/workflows/basic_bitcoin.yml +++ b/.github/workflows/basic_bitcoin.yml @@ -6,6 +6,7 @@ on: pull_request: paths: - motoko/basic_bitcoin/** + - rust/basic_bitcoin/** - .github/workflows/basic_bitcoin.yml concurrency: @@ -41,3 +42,36 @@ jobs: icp network start -d icp deploy --cycles 30t make test + + rust-basic_bitcoin: + # Run directly on the host (no container:) so that icp-cli can bind-mount + # the status directory into our custom Docker image. When icp-cli runs inside + # a container, the tmpdir it creates is invisible to the host Docker daemon. + runs-on: ubuntu-24.04 + env: + ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Install icp-cli and ic-wasm + run: npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-unknown-unknown + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 + - name: Build network launcher image + uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0 + with: + context: rust/basic_bitcoin + push: false + load: true + tags: icp-cli-network-launcher-bitcoin:latest + cache-from: type=gha + cache-to: type=gha,mode=max + - name: Deploy and test + working-directory: rust/basic_bitcoin + run: | + icp network start -d + icp deploy --cycles 30t + make test diff --git a/rust/basic_bitcoin/Cargo.lock b/rust/basic_bitcoin/Cargo.lock index 4c20c411bd..d4bea5ad2c 100644 --- a/rust/basic_bitcoin/Cargo.lock +++ b/rust/basic_bitcoin/Cargo.lock @@ -37,7 +37,7 @@ dependencies = [ ] [[package]] -name = "basic_bitcoin" +name = "backend" version = "0.1.0" dependencies = [ "bitcoin", diff --git a/rust/basic_bitcoin/Cargo.toml b/rust/basic_bitcoin/Cargo.toml index fe2838e81d..d1e49e317a 100644 --- a/rust/basic_bitcoin/Cargo.toml +++ b/rust/basic_bitcoin/Cargo.toml @@ -1,20 +1,3 @@ -[package] -name = "basic_bitcoin" -version = "0.1.0" -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -crate-type = ["cdylib"] - -[dependencies] -hex = "0.4.3" -bitcoin = "0.32.7" -candid = "0.10.19" -ic-cdk = "0.20.0" -ic-cdk-bitcoin-canister = "0.2" -ic-cdk-management-canister = "0.1" -serde = "1.0.132" -serde_bytes = "0.11.15" -leb128 = "0.2.5" +[workspace] +members = ["backend"] +resolver = "2" diff --git a/rust/basic_bitcoin/Dockerfile b/rust/basic_bitcoin/Dockerfile new file mode 100644 index 0000000000..47bd891983 --- /dev/null +++ b/rust/basic_bitcoin/Dockerfile @@ -0,0 +1,32 @@ +# Always use the latest network launcher image +# Before building we must pull to pick up new releases +# For real world usage, consider pinning the version instead of using :latest +FROM ghcr.io/dfinity/icp-cli-network-launcher:latest + +ARG TARGETARCH +ARG BITCOIN_VERSION=27.2 + +RUN apt-get update && apt-get install -y --no-install-recommends curl && \ + case "${TARGETARCH}" in \ + "amd64") \ + BITCOIN_TARBALL="bitcoin-${BITCOIN_VERSION}-x86_64-linux-gnu.tar.gz" ; \ + BITCOIN_SHA256="acc223af46c178064c132b235392476f66d486453ddbd6bca6f1f8411547da78" ;; \ + "arm64") \ + BITCOIN_TARBALL="bitcoin-${BITCOIN_VERSION}-aarch64-linux-gnu.tar.gz" ; \ + BITCOIN_SHA256="154c9b9e6e17136edc8f20fda5d252fb339e727e4a85ef49e7d8facb9085f2d3" ;; \ + *) echo "Unsupported architecture: ${TARGETARCH}" && exit 1 ;; \ + esac && \ + curl -fsSL "https://bitcoincore.org/bin/bitcoin-core-${BITCOIN_VERSION}/${BITCOIN_TARBALL}" \ + -o /tmp/bitcoin.tar.gz && \ + echo "${BITCOIN_SHA256} /tmp/bitcoin.tar.gz" | sha256sum -c && \ + tar xzf /tmp/bitcoin.tar.gz --strip-components=2 \ + -C /usr/local/bin \ + "bitcoin-${BITCOIN_VERSION}/bin/bitcoind" \ + "bitcoin-${BITCOIN_VERSION}/bin/bitcoin-cli" && \ + rm /tmp/bitcoin.tar.gz && \ + apt-get purge -y curl && apt-get autoremove -y && rm -rf /var/lib/apt/lists/* + +COPY docker/start.sh /app/start.sh +RUN chmod +x /app/start.sh + +ENTRYPOINT ["/app/start.sh"] diff --git a/rust/basic_bitcoin/Makefile b/rust/basic_bitcoin/Makefile index da0e4d3a86..99a65f4402 100644 --- a/rust/basic_bitcoin/Makefile +++ b/rust/basic_bitcoin/Makefile @@ -1,42 +1,80 @@ -.PHONY: all -all: deploy - -.PHONY: deploy -.SILENT: deploy -deploy: - dfx deploy basic_bitcoin --argument '(variant { regtest })' - -.PHONY: regtest_topup -.SILENT: regtest_topup -regtest_topup: - P2PKH_ADDR=$(shell dfx canister call basic_bitcoin get_p2pkh_address | tr -d '()') && \ - P2TR_ADDR=$(shell dfx canister call basic_bitcoin get_p2tr_address | tr -d '()') && \ - P2TR_KEY_ONLY_ADDR=$(shell dfx canister call basic_bitcoin get_p2tr_key_only_address | tr -d '()') && \ - TOPUP_CMD_P2PKH_ADDR="bitcoin-cli -regtest -rpcport=8333 sendtoaddress $${P2PKH_ADDR} 1" && \ - TOPUP_CMD_P2TR_ADDR="bitcoin-cli -regtest -rpcport=8333 sendtoaddress $${P2TR_ADDR} 1" && \ - TOPUP_CMD_P2TR_KEY_ONLY_ADDR="bitcoin-cli -regtest -rpcport=8333 sendtoaddress $${P2TR_KEY_ONLY_ADDR} 1" && \ - eval "$${TOPUP_CMD_P2PKH_ADDR}" && \ - eval "$${TOPUP_CMD_P2PKH_ADDR}" && \ - eval "$${TOPUP_CMD_P2PKH_ADDR}" && \ - eval "$${TOPUP_CMD_P2TR_ADDR}" && \ - eval "$${TOPUP_CMD_P2TR_ADDR}" && \ - eval "$${TOPUP_CMD_P2TR_ADDR}" && \ - eval "$${TOPUP_CMD_P2TR_KEY_ONLY_ADDR}" && \ - eval "$${TOPUP_CMD_P2TR_KEY_ONLY_ADDR}" && \ - eval "$${TOPUP_CMD_P2TR_KEY_ONLY_ADDR}" && \ - bitcoin-cli -regtest -rpcport=8333 -generate 6 - -.PHONY: test -.SILENT: test -# No tests yet. This target exists so CI doesn't fail when it runs `make test`. +IMAGE_NAME = icp-cli-network-launcher-bitcoin +# Find the running container built from our custom image +BITCOIN_CONTAINER = $(shell docker ps --filter "ancestor=$(IMAGE_NAME)" --format "{{.ID}}" | head -1) + +.PHONY: build-image test topup + +build-image: + # Because we're building off of :latest, use --pull to fetch the latest image. + # Remove `--pull` if the Dockerfile is updated to pin the base image version. + docker build --pull -t $(IMAGE_NAME) . + +topup: + icp canister top-up --amount 30t backend + test: + @echo "=== Test 1: get_p2pkh_address returns a valid Bitcoin address ===" + @result=$$(icp canister call backend get_p2pkh_address '()') && \ + echo "$$result" && \ + echo "$$result" | grep -q '"' && \ + echo "PASS" || (echo "FAIL" && exit 1) + + @echo "=== Test 2: get_p2wpkh_address returns a valid Bitcoin address ===" + @result=$$(icp canister call backend get_p2wpkh_address '()') && \ + echo "$$result" && \ + echo "$$result" | grep -q '"' && \ + echo "PASS" || (echo "FAIL" && exit 1) + + @echo "=== Test 3: get_p2tr_key_path_only_address returns a valid Bitcoin address ===" + @result=$$(icp canister call backend get_p2tr_key_path_only_address '()') && \ + echo "$$result" && \ + echo "$$result" | grep -q '"' && \ + echo "PASS" || (echo "FAIL" && exit 1) + + @echo "=== Test 4: get_p2tr_script_path_enabled_address returns a valid Bitcoin address ===" + @result=$$(icp canister call backend get_p2tr_script_path_enabled_address '()') && \ + echo "$$result" && \ + echo "$$result" | grep -q '"' && \ + echo "PASS" || (echo "FAIL" && exit 1) + + @echo "=== Test 5: get_current_fee_percentiles returns a vec ===" + @result=$$(icp canister call backend get_current_fee_percentiles '()') && \ + echo "$$result" && \ + echo "PASS" || (echo "FAIL" && exit 1) + + @echo "=== Mining 101 blocks to fund test address ===" + @[ -n "$(BITCOIN_CONTAINER)" ] || (echo "ERROR: network launcher container not running — run 'icp network start -d' first" && exit 1) + @addr=$$(icp canister call backend get_p2pkh_address '()' | grep -o '"[^"]*"' | tr -d '"') && \ + docker exec $(BITCOIN_CONTAINER) bitcoin-cli -regtest \ + -rpcuser=ic-btc-integration -rpcpassword=ic-btc-integration \ + generatetoaddress 101 "$$addr" > /dev/null && \ + echo "mined 101 blocks to $$addr" + + @echo "=== Waiting for IC to sync Bitcoin blocks ===" + @sleep 5 + + @echo "=== Test 6: get_balance returns non-zero after mining ===" + @addr=$$(icp canister call backend get_p2pkh_address '()' | grep -o '"[^"]*"' | tr -d '"') && \ + result=$$(icp canister call backend get_balance "(\"$$addr\")") && \ + echo "$$result" && \ + echo "$$result" | grep -qE '[1-9]' && \ + echo "PASS" || (echo "FAIL" && exit 1) + + @echo "=== Test 7: get_utxos returns synced chain state after mining ===" + @addr=$$(icp canister call backend get_p2pkh_address '()' | grep -o '"[^"]*"' | tr -d '"') && \ + result=$$(icp canister call backend get_utxos "(\"$$addr\")") && \ + echo "$$result" && \ + echo "$$result" | grep -q 'tip_height = 101' && \ + echo "PASS" || (echo "FAIL" && exit 1) + + @echo "=== Test 8: get_blockchain_info returns tip_height ===" + @result=$$(icp canister call backend get_blockchain_info '()') && \ + echo "$$result" && \ + echo "$$result" | grep -q 'height' && \ + echo "PASS" || (echo "FAIL" && exit 1) -.PHONY: clean -.SILENT: clean -clean: - rm -rf .dfx - rm -rf dist - rm -rf node_modules - rm -rf src/declarations - rm -f .env - cargo clean + @echo "=== Test 9: get_block_headers returns headers ===" + @result=$$(icp canister call backend get_block_headers '(0: nat32, null)') && \ + echo "$$result" && \ + echo "$$result" | grep -q 'tip_height' && \ + echo "PASS" || (echo "FAIL" && exit 1) diff --git a/rust/basic_bitcoin/README.md b/rust/basic_bitcoin/README.md index eb22617a98..b35f90f197 100644 --- a/rust/basic_bitcoin/README.md +++ b/rust/basic_bitcoin/README.md @@ -4,31 +4,7 @@ This example demonstrates how to deploy a smart contract on the Internet Compute This example also includes how to work with Bitcoin assets such as Ordinals, Runes, and BRC-20 tokens. -## Table of contents - -* [Architecture](#architecture) -* [Deploying from ICP Ninja](#deploying-from-icp-ninja) -* [Building and deploying the smart contract locally](#building-and-deploying-the-smart-contract-locally) - * [1. Prerequisites](#1-prerequisites) - * [2. Clone the examples repo](#2-clone-the-examples-repo) - * [3. Start the ICP execution environment](#3-start-the-icp-execution-environment) - * [4. Start Bitcoin regtest](#4-start-bitcoin-regtest) - * [5. Deploy the smart contract](#4-deploy-the-smart-contract) -* [Generating Bitcoin addresses](#generating-bitcoin-addresses) -* [Receiving bitcoin](#receiving-bitcoin) -* [Prerequisites](#prerequisites) -* [Checking balance](#checking-balance) -* [Sending bitcoin](#sending-bitcoin) -* [Retrieving blockchain info](#retrieving-blockchain-info) -* [Retrieving block headers](#retrieving-block-headers) -* [Bitcoin assets](#bitcoin-assets) - - * [Prerequisites for Bitcoin assets](#prerequisites-for-bitcoin-assets) -* [Inscribe an Ordinal](#inscribe-an-ordinal) -* [Etch a Rune](#etch-a-rune) -* [Deploy a BRC-20 token](#deploy-a-brc-20-token) -* [Notes on implementation](#notes-on-implementation) -* [Security considerations and best practices](#security-considerations-and-best-practices) +See also the [Motoko version](../../motoko/basic_bitcoin). ## Architecture @@ -40,61 +16,41 @@ This example integrates with the Internet Computer's built-in: For background on the ICP<>BTC integration, refer to the [Learn Hub](https://learn.internetcomputer.org/hc/en-us/articles/34211154520084-Bitcoin-Integration). +## Build and deploy from the command line -## Deploying from ICP Ninja +### Prerequisites -This example can be deployed directly to the Internet Computer using ICP Ninja, where it connects to Bitcoin **testnet4**. Note: Canisters deployed via ICP Ninja remain live for 50 minutes after signing in with your Internet Identity. +- [Rust toolchain](https://www.rust-lang.org/tools/install) +- icp-cli: `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm` +- Docker (required to run the custom network launcher image that bundles bitcoind) +- On macOS, an `llvm` version that supports the `wasm32-unknown-unknown` target is required. The Rust `bitcoin` library relies on the `secp256k1-sys` crate, which requires `llvm` to build. The default `llvm` version provided by XCode does not meet this requirement. Install the [Homebrew version](https://formulae.brew.sh/formula/llvm) using `brew install llvm`. -[![](https://icp.ninja/assets/open.svg)](https://icp.ninja/editor?g=https://github.com/dfinity/examples/tree/master/rust/basic_bitcoin) - -## Building and deploying the smart contract locally - -### 1. Prerequisites - -* [x] [Rust toolchain](https://www.rust-lang.org/tools/install) -* [x] [Internet Computer SDK](https://internetcomputer.org/docs/building-apps/getting-started/install) -* [x] [Local Bitcoin testnet (regtest)](https://internetcomputer.org/docs/build-on-btc/btc-dev-env#create-a-local-bitcoin-testnet-regtest-with-bitcoind) -* [x] On macOS, an `llvm` version that supports the `wasm32-unknown-unknown` target is required. The Rust `bitcoin` library relies on the `secp256k1-sys` crate, which requires `llvm` to build. The default `llvm` version provided by XCode does not meet this requirement. Install the [Homebrew version](https://formulae.brew.sh/formula/llvm) using `brew install llvm`. - - -### 2. Clone the examples repo +### Install ```bash git clone https://github.com/dfinity/examples cd examples/rust/basic_bitcoin ``` -### 3. Start the ICP execution environment - - -Open a terminal window (terminal 1) and run the following: -```bash -dfx start --enable-bitcoin --bitcoin-node 127.0.0.1:18444 -``` -This starts a local canister execution environment with Bitcoin support enabled. - -### 4. Start Bitcoin regtest +### Build the network launcher image -Open another terminal window (terminal 2) and run the following to start the local Bitcoin regtest network: +The local network bundles bitcoind inside a custom Docker image. Build it once before starting the network: ```bash -bitcoind -conf=$(pwd)/bitcoin.conf -datadir=$(pwd)/bitcoin_data --port=18444 +make build-image ``` -### 5. Deploy the smart contract - -Open a third terminal (terminal 3) and run the following to deploy the smart contract: +### Deploy and test ```bash -dfx deploy basic_bitcoin --argument '(variant { regtest })' +icp network start -d +icp deploy --cycles 30t +make test +icp network stop ``` -What this does: +> If tests fail with an out-of-cycles error, run `make topup` to add 30 trillion cycles to the backend canister and retry. -- `dfx deploy` tells the command line interface to `deploy` the smart contract. -- `--argument '(variant { regtest })'` passes the argument `regtest` to initialize the smart contract, telling it to connect to the local Bitcoin regtest network. - -Your smart contract is live and ready to use! You can interact with it using either the command line or the Candid UI (the link you see in the terminal). ## Generating Bitcoin addresses The example demonstrates how to generate and use the following address types: @@ -103,32 +59,42 @@ The example demonstrates how to generate and use the following address types: 2. **P2WPKH (SegWit v0)** using ECDSA and `sign_with_ecdsa` 3. **P2TR (Taproot, key-path-only)** using Schnorr keys and `sign_with_schnorr` 4. **P2TR (Taproot, script-path-enabled)** commits to a script allowing both key path and script path spending -Use the Candid UI or CLI to generate: ```bash -dfx canister call basic_bitcoin get_p2pkh_address +icp canister call backend get_p2pkh_address '()' # or: get_p2wpkh_address, get_p2tr_key_path_only_address, get_p2tr_script_path_enabled_address ``` ## Receiving bitcoin -Use the `bitcoin-cli` to mine a Bitcoin block and send the block reward in the form of local testnet bitcoin to one of the smart contract addresses. +Use `bitcoin-cli` inside the running network container to mine blocks and send the block reward to a smart contract address: + ```bash -bitcoin-cli -conf=$(pwd)/bitcoin.conf generatetoaddress 1 +# Get the container ID of the running network launcher +CONTAINER=$(docker ps --filter "ancestor=icp-cli-network-launcher-bitcoin" --format "{{.ID}}" | head -1) + +# Get an address from the canister +ADDR=$(icp canister call backend get_p2pkh_address '()' | grep -o '"[^"]*"' | tr -d '"') + +# Mine 1 block to that address +docker exec $CONTAINER bitcoin-cli -regtest \ + -rpcuser=ic-btc-integration -rpcpassword=ic-btc-integration \ + generatetoaddress 1 "$ADDR" ``` ## Checking balance Check the balance of any Bitcoin address: + ```bash -dfx canister call basic_bitcoin get_balance '("")' +icp canister call backend get_balance '("")' ``` This uses `bitcoin_get_balance` and works for any supported address type. The balance requires at least one confirmation to be reflected. + ## Sending bitcoin You can send bitcoin using the following endpoints: -Endpoints: - `send_from_p2pkh_address` - `send_from_p2wpkh_address` @@ -147,18 +113,13 @@ Each endpoint internally: Example: ```bash -dfx canister call basic_bitcoin send_from_p2pkh_address '(record { +icp canister call backend send_from_p2pkh_address '(record { destination_address = "bcrt1qg8qknn6f3txqg97gt8ca0ctya0vw7ep6d02qmt"; amount_in_satoshi = 4321; })' ``` -> [!IMPORTANT] -> Newly mined bitcoin, like those you created with the above `bitcoin-cli` command, cannot be spent until 100 additional blocks have been added to the chain. To make your bitcoin spendable, create 100 additional blocks. Choose one of the smart contract addresses as receiver of the block reward or use any valid Bitcoin dummy address. -> -> ```bash -> bitcoin-cli -conf=$(pwd)/bitcoin.conf generatetoaddress 100 -> ``` +> **Important:** Newly mined bitcoin cannot be spent until 100 additional blocks have been added to the chain. To make your bitcoin spendable, create 100 additional blocks with any valid address as the recipient. The function returns the transaction ID. When interacting with the contract deployed on IC mainnet, you can track testnet transactions on [mempool.space](https://mempool.space/testnet4/). @@ -167,7 +128,7 @@ The function returns the transaction ID. When interacting with the contract depl You can query the current state of the Bitcoin blockchain: ```bash -dfx canister call basic_bitcoin get_blockchain_info +icp canister call backend get_blockchain_info '()' ``` This calls `get_blockchain_info` on the Bitcoin canister and returns the tip height, block hash, timestamp, difficulty, and total UTXO count. It is useful for monitoring the state of the Bitcoin network from your smart contract. @@ -177,12 +138,13 @@ This calls `get_blockchain_info` on the Bitcoin canister and returns the tip hei You can query historical block headers: ```bash -dfx canister call basic_bitcoin get_block_headers '(10: nat32, null)' +icp canister call backend get_block_headers '(10: nat32, null)' # or a range: -dfx canister call basic_bitcoin get_block_headers '(10: nat32, opt (11: nat32))' +icp canister call backend get_block_headers '(10: nat32, opt (11: nat32))' ``` This calls `bitcoin_get_block_headers`, which is useful for blockchain validation or light client logic. + ## Bitcoin assets Bitcoin's scripting capabilities enable various digital assets beyond simple transfers. This example demonstrates how to create and interact with three major Bitcoin asset protocols from an ICP smart contract: @@ -203,11 +165,9 @@ brew install ord For other platforms, see the [ord repository](https://github.com/ordinals/ord) for installation instructions. -> [!NOTE] -> This repository includes a [default ord config file](./ord.yaml) that matches the also provided [bitcoin config file](./bitcoin.conf). +> **Note:** This repository includes a [default ord config file](./ord.yaml) that matches the also provided [bitcoin config file](./bitcoin.conf). -> [!IMPORTANT] -> **Bitcoin Configuration**: To work with Bitcoin assets, make sure bitcoind is configured to accept non-standard transactions by including this setting in your `bitcoin.conf`: +> **Important — Bitcoin Configuration:** To work with Bitcoin assets, make sure bitcoind is configured to accept non-standard transactions by including this setting in your `bitcoin.conf`: > > ``` > acceptnonstdtxn=1 @@ -226,22 +186,26 @@ For other platforms, see the [ord repository](https://github.com/ordinals/ord) f 2. **Get a Taproot address** for funding the inscription: ```bash - dfx canister call basic_bitcoin get_p2tr_key_path_only_address '()' + icp canister call backend get_p2tr_key_path_only_address '()' ``` -3. **Fund the address** with sufficient bitcoin (100 blocks ensures spendability): +3. **Fund the address** with sufficient bitcoin (101 blocks ensures spendability): ```bash - bitcoin-cli -conf=$(pwd)/bitcoin.conf generatetoaddress 100 + docker exec $CONTAINER bitcoin-cli -regtest \ + -rpcuser=ic-btc-integration -rpcpassword=ic-btc-integration \ + generatetoaddress 101 ``` 4. **Create the inscription** with your desired text: ```bash - dfx canister call basic_bitcoin inscribe_ordinal '("Hello Bitcoin")' + icp canister call backend inscribe_ordinal '("Hello Bitcoin")' ``` 5. **Mine a block** to confirm the transactions: ```bash - bitcoin-cli -conf=$(pwd)/bitcoin.conf generatetoaddress 1 + docker exec $CONTAINER bitcoin-cli -regtest \ + -rpcuser=ic-btc-integration -rpcpassword=ic-btc-integration \ + generatetoaddress 1 ``` The function returns the reveal transaction ID. Your inscription is now permanently stored on Bitcoin and can be viewed using ord or other Ordinals explorers. The default address of the local `ord` server is `http://127.0.0.1:80/`. @@ -259,22 +223,26 @@ The function returns the reveal transaction ID. Your inscription is now permanen 2. **Get a Taproot address** for the Rune etching: ```bash - dfx canister call basic_bitcoin get_p2tr_key_path_only_address '()' + icp canister call backend get_p2tr_key_path_only_address '()' ``` 3. **Fund the address** with bitcoin to pay for the etching: ```bash - bitcoin-cli -conf=$(pwd)/bitcoin.conf generatetoaddress 100 + docker exec $CONTAINER bitcoin-cli -regtest \ + -rpcuser=ic-btc-integration -rpcpassword=ic-btc-integration \ + generatetoaddress 101 ``` 4. **Etch the Rune** with an uppercase name (maximum 28 characters): ```bash - dfx canister call basic_bitcoin etch_rune '("ICPRUNE")' + icp canister call backend etch_rune '("ICPRUNE")' ``` 5. **Mine a block** to confirm the etching: ```bash - bitcoin-cli -conf=$(pwd)/bitcoin.conf generatetoaddress 1 + docker exec $CONTAINER bitcoin-cli -regtest \ + -rpcuser=ic-btc-integration -rpcpassword=ic-btc-integration \ + generatetoaddress 1 ``` 6. **Decode the Runestone** to verify the etching: @@ -282,7 +250,7 @@ The function returns the reveal transaction ID. Your inscription is now permanen ord --config-dir . decode --txid ``` -The Rune is now etched with 1_000_000 tokens minted to your address. The tokens can be transferred using standard Bitcoin transactions with Runestone data. +The Rune is now etched with 1,000,000 tokens minted to your address. The tokens can be transferred using standard Bitcoin transactions with Runestone data. ## Deploy a BRC-20 token @@ -295,30 +263,24 @@ The Rune is now etched with 1_000_000 tokens minted to your address. The tokens ord --config-dir . server ``` -2. **Get a Taproot address** for the token deployment: - ```bash - dfx canister call basic_bitcoin get_p2tr_key_path_only_address '()' - ``` - -3. **Fund the address** with bitcoin: - ```bash - bitcoin-cli -conf=$(pwd)/bitcoin.conf generatetoaddress 100 - ``` +2. **Get a Taproot address** and fund it with bitcoin. -4. **Deploy the BRC-20 token** with a 4-character ticker: +3. **Deploy the BRC-20 token** with a 4-character ticker: ```bash - dfx canister call basic_bitcoin inscribe_brc20 '("DEMO")' + icp canister call backend inscribe_brc20 '("DEMO")' ``` -5. **Mine a block** to confirm the deployment: +4. **Mine a block** to confirm the deployment: ```bash - bitcoin-cli -conf=$(pwd)/bitcoin.conf generatetoaddress 1 + docker exec $CONTAINER bitcoin-cli -regtest \ + -rpcuser=ic-btc-integration -rpcpassword=ic-btc-integration \ + generatetoaddress 1 ``` This creates a BRC-20 token with: - Ticker: "DEMO" -- Max supply: 21_000_000 tokens -- Mint limit: 1_000 tokens per mint +- Max supply: 21,000,000 tokens +- Mint limit: 1,000 tokens per mint The deployment inscription contains JSON metadata that BRC-20 indexers use to track token balances and transfers. Additional mint and transfer operations require separate inscriptions following the BRC-20 protocol. @@ -338,13 +300,9 @@ This example implements several important patterns for Bitcoin integration: This example is provided for educational purposes and is not production-ready. It is important to consider security implications when developing applications that interact with Bitcoin or other cryptocurrencies. The code has **not been audited** and may contain vulnerabilities or security issues. -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. +If you base your application on this example, we recommend you familiarize yourself with and adhere to the [security best practices](https://docs.internetcomputer.org/guides/security/overview) 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/building-apps/security/data-integrity-and-authenticity#using-certified-variables-for-secure-queries), since the app e.g. offers a method to read balances. -- [Use a decentralized governance system like SNS to make a smart contract have a decentralized controller](https://internetcomputer.org/docs/building-apps/security/decentralization), since decentralized control may be essential for smart contracts holding bitcoins on behalf of users. - ---- - -*Last updated: July 2025* +- Certify query responses if they are relevant for security, since the app offers a method to read balances. +- Use a decentralized governance system like SNS to make a smart contract have a decentralized controller, since decentralized control may be essential for smart contracts holding bitcoins on behalf of users. diff --git a/rust/basic_bitcoin/backend/Cargo.toml b/rust/basic_bitcoin/backend/Cargo.toml new file mode 100644 index 0000000000..505af7a408 --- /dev/null +++ b/rust/basic_bitcoin/backend/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "backend" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +hex = "0.4.3" +bitcoin = "0.32.7" +candid = "0.10.19" +ic-cdk = "0.20.0" +ic-cdk-bitcoin-canister = "0.2" +ic-cdk-management-canister = "0.1" +serde = "1.0.132" +serde_bytes = "0.11.15" +leb128 = "0.2.5" diff --git a/rust/basic_bitcoin/src/brc20.rs b/rust/basic_bitcoin/backend/src/brc20.rs similarity index 100% rename from rust/basic_bitcoin/src/brc20.rs rename to rust/basic_bitcoin/backend/src/brc20.rs diff --git a/rust/basic_bitcoin/src/common.rs b/rust/basic_bitcoin/backend/src/common.rs similarity index 100% rename from rust/basic_bitcoin/src/common.rs rename to rust/basic_bitcoin/backend/src/common.rs diff --git a/rust/basic_bitcoin/src/ecdsa.rs b/rust/basic_bitcoin/backend/src/ecdsa.rs similarity index 100% rename from rust/basic_bitcoin/src/ecdsa.rs rename to rust/basic_bitcoin/backend/src/ecdsa.rs diff --git a/rust/basic_bitcoin/src/lib.rs b/rust/basic_bitcoin/backend/src/lib.rs similarity index 100% rename from rust/basic_bitcoin/src/lib.rs rename to rust/basic_bitcoin/backend/src/lib.rs diff --git a/rust/basic_bitcoin/src/ordinals.rs b/rust/basic_bitcoin/backend/src/ordinals.rs similarity index 100% rename from rust/basic_bitcoin/src/ordinals.rs rename to rust/basic_bitcoin/backend/src/ordinals.rs diff --git a/rust/basic_bitcoin/src/p2pkh.rs b/rust/basic_bitcoin/backend/src/p2pkh.rs similarity index 100% rename from rust/basic_bitcoin/src/p2pkh.rs rename to rust/basic_bitcoin/backend/src/p2pkh.rs diff --git a/rust/basic_bitcoin/src/p2tr.rs b/rust/basic_bitcoin/backend/src/p2tr.rs similarity index 100% rename from rust/basic_bitcoin/src/p2tr.rs rename to rust/basic_bitcoin/backend/src/p2tr.rs diff --git a/rust/basic_bitcoin/src/p2wpkh.rs b/rust/basic_bitcoin/backend/src/p2wpkh.rs similarity index 100% rename from rust/basic_bitcoin/src/p2wpkh.rs rename to rust/basic_bitcoin/backend/src/p2wpkh.rs diff --git a/rust/basic_bitcoin/src/runes.rs b/rust/basic_bitcoin/backend/src/runes.rs similarity index 100% rename from rust/basic_bitcoin/src/runes.rs rename to rust/basic_bitcoin/backend/src/runes.rs diff --git a/rust/basic_bitcoin/src/schnorr.rs b/rust/basic_bitcoin/backend/src/schnorr.rs similarity index 100% rename from rust/basic_bitcoin/src/schnorr.rs rename to rust/basic_bitcoin/backend/src/schnorr.rs diff --git a/rust/basic_bitcoin/src/service.rs b/rust/basic_bitcoin/backend/src/service.rs similarity index 100% rename from rust/basic_bitcoin/src/service.rs rename to rust/basic_bitcoin/backend/src/service.rs diff --git a/rust/basic_bitcoin/src/service/etch_rune.rs b/rust/basic_bitcoin/backend/src/service/etch_rune.rs similarity index 100% rename from rust/basic_bitcoin/src/service/etch_rune.rs rename to rust/basic_bitcoin/backend/src/service/etch_rune.rs diff --git a/rust/basic_bitcoin/src/service/get_balance.rs b/rust/basic_bitcoin/backend/src/service/get_balance.rs similarity index 100% rename from rust/basic_bitcoin/src/service/get_balance.rs rename to rust/basic_bitcoin/backend/src/service/get_balance.rs diff --git a/rust/basic_bitcoin/src/service/get_block_headers.rs b/rust/basic_bitcoin/backend/src/service/get_block_headers.rs similarity index 100% rename from rust/basic_bitcoin/src/service/get_block_headers.rs rename to rust/basic_bitcoin/backend/src/service/get_block_headers.rs diff --git a/rust/basic_bitcoin/src/service/get_blockchain_info.rs b/rust/basic_bitcoin/backend/src/service/get_blockchain_info.rs similarity index 100% rename from rust/basic_bitcoin/src/service/get_blockchain_info.rs rename to rust/basic_bitcoin/backend/src/service/get_blockchain_info.rs diff --git a/rust/basic_bitcoin/src/service/get_current_fee_percentiles.rs b/rust/basic_bitcoin/backend/src/service/get_current_fee_percentiles.rs similarity index 100% rename from rust/basic_bitcoin/src/service/get_current_fee_percentiles.rs rename to rust/basic_bitcoin/backend/src/service/get_current_fee_percentiles.rs diff --git a/rust/basic_bitcoin/src/service/get_p2pkh_address.rs b/rust/basic_bitcoin/backend/src/service/get_p2pkh_address.rs similarity index 100% rename from rust/basic_bitcoin/src/service/get_p2pkh_address.rs rename to rust/basic_bitcoin/backend/src/service/get_p2pkh_address.rs diff --git a/rust/basic_bitcoin/src/service/get_p2tr_key_path_only_address.rs b/rust/basic_bitcoin/backend/src/service/get_p2tr_key_path_only_address.rs similarity index 100% rename from rust/basic_bitcoin/src/service/get_p2tr_key_path_only_address.rs rename to rust/basic_bitcoin/backend/src/service/get_p2tr_key_path_only_address.rs diff --git a/rust/basic_bitcoin/src/service/get_p2tr_script_path_enabled_address.rs b/rust/basic_bitcoin/backend/src/service/get_p2tr_script_path_enabled_address.rs similarity index 100% rename from rust/basic_bitcoin/src/service/get_p2tr_script_path_enabled_address.rs rename to rust/basic_bitcoin/backend/src/service/get_p2tr_script_path_enabled_address.rs diff --git a/rust/basic_bitcoin/src/service/get_p2wpkh_address.rs b/rust/basic_bitcoin/backend/src/service/get_p2wpkh_address.rs similarity index 100% rename from rust/basic_bitcoin/src/service/get_p2wpkh_address.rs rename to rust/basic_bitcoin/backend/src/service/get_p2wpkh_address.rs diff --git a/rust/basic_bitcoin/src/service/get_utxos.rs b/rust/basic_bitcoin/backend/src/service/get_utxos.rs similarity index 100% rename from rust/basic_bitcoin/src/service/get_utxos.rs rename to rust/basic_bitcoin/backend/src/service/get_utxos.rs diff --git a/rust/basic_bitcoin/src/service/inscribe_brc20.rs b/rust/basic_bitcoin/backend/src/service/inscribe_brc20.rs similarity index 100% rename from rust/basic_bitcoin/src/service/inscribe_brc20.rs rename to rust/basic_bitcoin/backend/src/service/inscribe_brc20.rs diff --git a/rust/basic_bitcoin/src/service/inscribe_ordinal.rs b/rust/basic_bitcoin/backend/src/service/inscribe_ordinal.rs similarity index 100% rename from rust/basic_bitcoin/src/service/inscribe_ordinal.rs rename to rust/basic_bitcoin/backend/src/service/inscribe_ordinal.rs diff --git a/rust/basic_bitcoin/src/service/send_from_p2pkh_address.rs b/rust/basic_bitcoin/backend/src/service/send_from_p2pkh_address.rs similarity index 100% rename from rust/basic_bitcoin/src/service/send_from_p2pkh_address.rs rename to rust/basic_bitcoin/backend/src/service/send_from_p2pkh_address.rs diff --git a/rust/basic_bitcoin/src/service/send_from_p2tr_key_path_only_address.rs b/rust/basic_bitcoin/backend/src/service/send_from_p2tr_key_path_only_address.rs similarity index 100% rename from rust/basic_bitcoin/src/service/send_from_p2tr_key_path_only_address.rs rename to rust/basic_bitcoin/backend/src/service/send_from_p2tr_key_path_only_address.rs diff --git a/rust/basic_bitcoin/src/service/send_from_p2tr_script_path_enabled_address_key_spend.rs b/rust/basic_bitcoin/backend/src/service/send_from_p2tr_script_path_enabled_address_key_spend.rs similarity index 100% rename from rust/basic_bitcoin/src/service/send_from_p2tr_script_path_enabled_address_key_spend.rs rename to rust/basic_bitcoin/backend/src/service/send_from_p2tr_script_path_enabled_address_key_spend.rs diff --git a/rust/basic_bitcoin/src/service/send_from_p2tr_script_path_enabled_address_script_spend.rs b/rust/basic_bitcoin/backend/src/service/send_from_p2tr_script_path_enabled_address_script_spend.rs similarity index 100% rename from rust/basic_bitcoin/src/service/send_from_p2tr_script_path_enabled_address_script_spend.rs rename to rust/basic_bitcoin/backend/src/service/send_from_p2tr_script_path_enabled_address_script_spend.rs diff --git a/rust/basic_bitcoin/src/service/send_from_p2wpkh_address.rs b/rust/basic_bitcoin/backend/src/service/send_from_p2wpkh_address.rs similarity index 100% rename from rust/basic_bitcoin/src/service/send_from_p2wpkh_address.rs rename to rust/basic_bitcoin/backend/src/service/send_from_p2wpkh_address.rs diff --git a/rust/basic_bitcoin/basic_bitcoin.did b/rust/basic_bitcoin/basic_bitcoin.did deleted file mode 100644 index 7e5fc372cd..0000000000 --- a/rust/basic_bitcoin/basic_bitcoin.did +++ /dev/null @@ -1,110 +0,0 @@ -type satoshi = nat64; - -type millisatoshi_per_vbyte = nat64; - -type bitcoin_address = text; - -type transaction_id = text; - -type block_hash = blob; - -type network = variant { - regtest; - testnet; - mainnet; -}; - -type outpoint = record { - txid : blob; - vout : nat32; -}; - -type utxo = record { - outpoint : outpoint; - value : satoshi; - height : nat32; -}; - -type get_utxos_response = record { - utxos : vec utxo; - tip_block_hash : block_hash; - tip_height : nat32; - next_page : opt blob; -}; - -type block_header = blob; -type block_height = nat32; - -type get_block_headers_response = record { - tip_height : block_height; - block_headers : vec block_header; -}; - -type blockchain_info = record { - height : block_height; - block_hash : block_hash; - timestamp : nat32; - difficulty : nat; - utxos_length : nat64; -}; - -service : (network) -> { - "etch_rune" : (ticker: text) -> (transaction_id); - - "get_p2pkh_address" : () -> (bitcoin_address); - - "get_p2wpkh_address" : () -> (bitcoin_address); - - "get_p2tr_script_path_enabled_address" : () -> (bitcoin_address); - - "get_p2tr_key_path_only_address" : () -> (bitcoin_address); - - "get_balance" : (address : bitcoin_address) -> (satoshi); - - "get_utxos" : (bitcoin_address) -> (get_utxos_response); - - "get_blockchain_info" : () -> (blockchain_info); - - "get_block_headers" : (start_height : block_height, end_height : opt block_height) -> (get_block_headers_response); - - "get_current_fee_percentiles" : () -> (vec millisatoshi_per_vbyte); - - "inscribe_brc20": (ticker: text) -> (transaction_id); - - "inscribe_ordinal": (text: text) -> (transaction_id); - - "send_from_p2pkh_address" : ( - record { - destination_address : bitcoin_address; - amount_in_satoshi : satoshi; - } - ) -> (transaction_id); - - "send_from_p2wpkh_address" : ( - record { - destination_address : bitcoin_address; - amount_in_satoshi : satoshi; - } - ) -> (transaction_id); - - "send_from_p2tr_key_path_only_address" : ( - record { - destination_address : bitcoin_address; - amount_in_satoshi : satoshi; - } - ) -> (transaction_id); - - "send_from_p2tr_script_path_enabled_address_key_spend" : ( - record { - destination_address : bitcoin_address; - amount_in_satoshi : satoshi; - } - ) -> (transaction_id); - - "send_from_p2tr_script_path_enabled_address_script_spend" : ( - record { - destination_address : bitcoin_address; - amount_in_satoshi : satoshi; - } - ) -> (transaction_id); -}; diff --git a/rust/basic_bitcoin/build.sh b/rust/basic_bitcoin/build.sh deleted file mode 100755 index aa67ef942b..0000000000 --- a/rust/basic_bitcoin/build.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -TARGET="wasm32-unknown-unknown" -SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" - -# Change to the script directory -cd "$SCRIPT_DIR" - -# Build based on the platform. On MacOS, use LLVM's clang and llvm-ar -# to avoid issues with the default clang and ar. See more info in the README. -if [ "$(uname)" == "Darwin" ]; then - LLVM_PATH=$(brew --prefix llvm) - AR="${LLVM_PATH}/bin/llvm-ar" CC="${LLVM_PATH}/bin/clang" cargo build --target $TARGET --release -else - cargo build --target $TARGET --release -fi \ No newline at end of file diff --git a/rust/basic_bitcoin/dfx.json b/rust/basic_bitcoin/dfx.json deleted file mode 100644 index ca7f1864a3..0000000000 --- a/rust/basic_bitcoin/dfx.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "version": 1, - "canisters": { - "basic_bitcoin": { - "build": "build.sh", - "candid": "basic_bitcoin.did", - "gzip": true, - "metadata": [ - { - "name": "candid:service", - "path": "basic_bitcoin.did", - "visibility": "public" - } - ], - "package": "basic_bitcoin", - "type": "custom", - "wasm": "target/wasm32-unknown-unknown/release/basic_bitcoin.wasm", - "init_arg": "(variant { testnet })" - } - }, - "defaults": { - "bitcoin": { - "enabled": true, - "nodes": ["127.0.0.1:18444"] - } - }, - "networks": { - "local": { - "bind": "127.0.0.1:4943" - } - } -} diff --git a/rust/basic_bitcoin/docker/start.sh b/rust/basic_bitcoin/docker/start.sh new file mode 100644 index 0000000000..4b9f6a676f --- /dev/null +++ b/rust/basic_bitcoin/docker/start.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# Start bitcoind in regtest mode, then hand off to the IC network launcher. +# bitcoind runs in the background; the launcher becomes PID 1 via exec. + +bitcoind \ + -regtest -server \ + -rpcbind=127.0.0.1 -rpcallowip=127.0.0.1 \ + -rpcuser=ic-btc-integration -rpcpassword=ic-btc-integration \ + -fallbackfee=0.00001 -txindex=1 & + +# Wait for bitcoind to accept RPC connections +until bitcoin-cli -regtest \ + -rpcuser=ic-btc-integration -rpcpassword=ic-btc-integration \ + getblockcount >/dev/null 2>&1; do + sleep 0.5 +done + +echo "bitcoind ready on regtest" + +# Hand off to the IC network launcher. +# --bitcoind-addr wires the IC Bitcoin subnet to our local bitcoind. +# Port 18443 is the RPC port (used by bitcoin-cli inside the container). +# Port 18444 is the P2P port (used by the launcher for block discovery). +exec /app/icp-cli-network-launcher \ + --status-dir=/app/status \ + --config-port 4942 \ + --gateway-port 4943 \ + --bind 0.0.0.0 \ + --bitcoind-addr=127.0.0.1:18444 \ + "$@" diff --git a/rust/basic_bitcoin/icp.yaml b/rust/basic_bitcoin/icp.yaml new file mode 100644 index 0000000000..547ecc917c --- /dev/null +++ b/rust/basic_bitcoin/icp.yaml @@ -0,0 +1,27 @@ +canisters: + - name: backend + recipe: + type: "@dfinity/rust@v3.3.0" + +networks: + - name: local + mode: managed + image: icp-cli-network-launcher-bitcoin + port-mapping: + - 0:4943 # IC gateway (dynamic) + +environments: + - name: local + network: local + init_args: + backend: "(variant { regtest })" + + - name: staging + network: ic + init_args: + backend: "(variant { testnet })" + + - name: production + network: ic + init_args: + backend: "(variant { mainnet })"