Skip to content

Commit fd21b3e

Browse files
marc0oloclaude
andcommitted
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 <noreply@anthropic.com>
1 parent bafb2cf commit fd21b3e

40 files changed

Lines changed: 294 additions & 333 deletions

.github/workflows/basic_bitcoin.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ on:
66
pull_request:
77
paths:
88
- motoko/basic_bitcoin/**
9+
- rust/basic_bitcoin/**
910
- .github/workflows/basic_bitcoin.yml
1011

1112
concurrency:
@@ -41,3 +42,36 @@ jobs:
4142
icp network start -d
4243
icp deploy --cycles 30t
4344
make test
45+
46+
rust-basic_bitcoin:
47+
# Run directly on the host (no container:) so that icp-cli can bind-mount
48+
# the status directory into our custom Docker image. When icp-cli runs inside
49+
# a container, the tmpdir it creates is invisible to the host Docker daemon.
50+
runs-on: ubuntu-24.04
51+
env:
52+
ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
53+
steps:
54+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
55+
- name: Install icp-cli and ic-wasm
56+
run: npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm
57+
- name: Install Rust toolchain
58+
uses: dtolnay/rust-toolchain@stable
59+
with:
60+
targets: wasm32-unknown-unknown
61+
- name: Set up Docker Buildx
62+
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
63+
- name: Build network launcher image
64+
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
65+
with:
66+
context: rust/basic_bitcoin
67+
push: false
68+
load: true
69+
tags: icp-cli-network-launcher-bitcoin:latest
70+
cache-from: type=gha
71+
cache-to: type=gha,mode=max
72+
- name: Deploy and test
73+
working-directory: rust/basic_bitcoin
74+
run: |
75+
icp network start -d
76+
icp deploy --cycles 30t
77+
make test

rust/basic_bitcoin/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/basic_bitcoin/Cargo.toml

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,3 @@
1-
[package]
2-
name = "basic_bitcoin"
3-
version = "0.1.0"
4-
edition = "2018"
5-
6-
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7-
8-
[lib]
9-
crate-type = ["cdylib"]
10-
11-
[dependencies]
12-
hex = "0.4.3"
13-
bitcoin = "0.32.7"
14-
candid = "0.10.19"
15-
ic-cdk = "0.20.0"
16-
ic-cdk-bitcoin-canister = "0.2"
17-
ic-cdk-management-canister = "0.1"
18-
serde = "1.0.132"
19-
serde_bytes = "0.11.15"
20-
leb128 = "0.2.5"
1+
[workspace]
2+
members = ["backend"]
3+
resolver = "2"

rust/basic_bitcoin/Dockerfile

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Always use the latest network launcher image
2+
# Before building we must pull to pick up new releases
3+
# For real world usage, consider pinning the version instead of using :latest
4+
FROM ghcr.io/dfinity/icp-cli-network-launcher:latest
5+
6+
ARG TARGETARCH
7+
ARG BITCOIN_VERSION=27.2
8+
9+
RUN apt-get update && apt-get install -y --no-install-recommends curl && \
10+
case "${TARGETARCH}" in \
11+
"amd64") \
12+
BITCOIN_TARBALL="bitcoin-${BITCOIN_VERSION}-x86_64-linux-gnu.tar.gz" ; \
13+
BITCOIN_SHA256="acc223af46c178064c132b235392476f66d486453ddbd6bca6f1f8411547da78" ;; \
14+
"arm64") \
15+
BITCOIN_TARBALL="bitcoin-${BITCOIN_VERSION}-aarch64-linux-gnu.tar.gz" ; \
16+
BITCOIN_SHA256="154c9b9e6e17136edc8f20fda5d252fb339e727e4a85ef49e7d8facb9085f2d3" ;; \
17+
*) echo "Unsupported architecture: ${TARGETARCH}" && exit 1 ;; \
18+
esac && \
19+
curl -fsSL "https://bitcoincore.org/bin/bitcoin-core-${BITCOIN_VERSION}/${BITCOIN_TARBALL}" \
20+
-o /tmp/bitcoin.tar.gz && \
21+
echo "${BITCOIN_SHA256} /tmp/bitcoin.tar.gz" | sha256sum -c && \
22+
tar xzf /tmp/bitcoin.tar.gz --strip-components=2 \
23+
-C /usr/local/bin \
24+
"bitcoin-${BITCOIN_VERSION}/bin/bitcoind" \
25+
"bitcoin-${BITCOIN_VERSION}/bin/bitcoin-cli" && \
26+
rm /tmp/bitcoin.tar.gz && \
27+
apt-get purge -y curl && apt-get autoremove -y && rm -rf /var/lib/apt/lists/*
28+
29+
COPY docker/start.sh /app/start.sh
30+
RUN chmod +x /app/start.sh
31+
32+
ENTRYPOINT ["/app/start.sh"]

rust/basic_bitcoin/Makefile

Lines changed: 78 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,80 @@
1-
.PHONY: all
2-
all: deploy
3-
4-
.PHONY: deploy
5-
.SILENT: deploy
6-
deploy:
7-
dfx deploy basic_bitcoin --argument '(variant { regtest })'
8-
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_ADDR=$(shell dfx canister call basic_bitcoin get_p2tr_address | tr -d '()') && \
14-
P2TR_KEY_ONLY_ADDR=$(shell dfx canister call basic_bitcoin get_p2tr_key_only_address | tr -d '()') && \
15-
TOPUP_CMD_P2PKH_ADDR="bitcoin-cli -regtest -rpcport=8333 sendtoaddress $${P2PKH_ADDR} 1" && \
16-
TOPUP_CMD_P2TR_ADDR="bitcoin-cli -regtest -rpcport=8333 sendtoaddress $${P2TR_ADDR} 1" && \
17-
TOPUP_CMD_P2TR_KEY_ONLY_ADDR="bitcoin-cli -regtest -rpcport=8333 sendtoaddress $${P2TR_KEY_ONLY_ADDR} 1" && \
18-
eval "$${TOPUP_CMD_P2PKH_ADDR}" && \
19-
eval "$${TOPUP_CMD_P2PKH_ADDR}" && \
20-
eval "$${TOPUP_CMD_P2PKH_ADDR}" && \
21-
eval "$${TOPUP_CMD_P2TR_ADDR}" && \
22-
eval "$${TOPUP_CMD_P2TR_ADDR}" && \
23-
eval "$${TOPUP_CMD_P2TR_ADDR}" && \
24-
eval "$${TOPUP_CMD_P2TR_KEY_ONLY_ADDR}" && \
25-
eval "$${TOPUP_CMD_P2TR_KEY_ONLY_ADDR}" && \
26-
eval "$${TOPUP_CMD_P2TR_KEY_ONLY_ADDR}" && \
27-
bitcoin-cli -regtest -rpcport=8333 -generate 6
28-
29-
.PHONY: test
30-
.SILENT: test
31-
# No tests yet. This target exists so CI doesn't fail when it runs `make test`.
1+
IMAGE_NAME = icp-cli-network-launcher-bitcoin
2+
# Find the running container built from our custom image
3+
BITCOIN_CONTAINER = $(shell docker ps --filter "ancestor=$(IMAGE_NAME)" --format "{{.ID}}" | head -1)
4+
5+
.PHONY: build-image test topup
6+
7+
build-image:
8+
# Because we're building off of :latest, use --pull to fetch the latest image.
9+
# Remove `--pull` if the Dockerfile is updated to pin the base image version.
10+
docker build --pull -t $(IMAGE_NAME) .
11+
12+
topup:
13+
icp canister top-up --amount 30t backend
14+
3215
test:
16+
@echo "=== Test 1: get_p2pkh_address returns a valid Bitcoin address ==="
17+
@result=$$(icp canister call backend get_p2pkh_address '()') && \
18+
echo "$$result" && \
19+
echo "$$result" | grep -q '"' && \
20+
echo "PASS" || (echo "FAIL" && exit 1)
21+
22+
@echo "=== Test 2: get_p2wpkh_address returns a valid Bitcoin address ==="
23+
@result=$$(icp canister call backend get_p2wpkh_address '()') && \
24+
echo "$$result" && \
25+
echo "$$result" | grep -q '"' && \
26+
echo "PASS" || (echo "FAIL" && exit 1)
27+
28+
@echo "=== Test 3: get_p2tr_key_path_only_address returns a valid Bitcoin address ==="
29+
@result=$$(icp canister call backend get_p2tr_key_path_only_address '()') && \
30+
echo "$$result" && \
31+
echo "$$result" | grep -q '"' && \
32+
echo "PASS" || (echo "FAIL" && exit 1)
33+
34+
@echo "=== Test 4: get_p2tr_script_path_enabled_address returns a valid Bitcoin address ==="
35+
@result=$$(icp canister call backend get_p2tr_script_path_enabled_address '()') && \
36+
echo "$$result" && \
37+
echo "$$result" | grep -q '"' && \
38+
echo "PASS" || (echo "FAIL" && exit 1)
39+
40+
@echo "=== Test 5: get_current_fee_percentiles returns a vec ==="
41+
@result=$$(icp canister call backend get_current_fee_percentiles '()') && \
42+
echo "$$result" && \
43+
echo "PASS" || (echo "FAIL" && exit 1)
44+
45+
@echo "=== Mining 101 blocks to fund test address ==="
46+
@[ -n "$(BITCOIN_CONTAINER)" ] || (echo "ERROR: network launcher container not running — run 'icp network start -d' first" && exit 1)
47+
@addr=$$(icp canister call backend get_p2pkh_address '()' | grep -o '"[^"]*"' | tr -d '"') && \
48+
docker exec $(BITCOIN_CONTAINER) bitcoin-cli -regtest \
49+
-rpcuser=ic-btc-integration -rpcpassword=ic-btc-integration \
50+
generatetoaddress 101 "$$addr" > /dev/null && \
51+
echo "mined 101 blocks to $$addr"
52+
53+
@echo "=== Waiting for IC to sync Bitcoin blocks ==="
54+
@sleep 5
55+
56+
@echo "=== Test 6: get_balance returns non-zero after mining ==="
57+
@addr=$$(icp canister call backend get_p2pkh_address '()' | grep -o '"[^"]*"' | tr -d '"') && \
58+
result=$$(icp canister call backend get_balance "(\"$$addr\")") && \
59+
echo "$$result" && \
60+
echo "$$result" | grep -qE '[1-9]' && \
61+
echo "PASS" || (echo "FAIL" && exit 1)
62+
63+
@echo "=== Test 7: get_utxos returns synced chain state after mining ==="
64+
@addr=$$(icp canister call backend get_p2pkh_address '()' | grep -o '"[^"]*"' | tr -d '"') && \
65+
result=$$(icp canister call backend get_utxos "(\"$$addr\")") && \
66+
echo "$$result" && \
67+
echo "$$result" | grep -q 'tip_height = 101' && \
68+
echo "PASS" || (echo "FAIL" && exit 1)
69+
70+
@echo "=== Test 8: get_blockchain_info returns tip_height ==="
71+
@result=$$(icp canister call backend get_blockchain_info '()') && \
72+
echo "$$result" && \
73+
echo "$$result" | grep -q 'height' && \
74+
echo "PASS" || (echo "FAIL" && exit 1)
3375

34-
.PHONY: clean
35-
.SILENT: clean
36-
clean:
37-
rm -rf .dfx
38-
rm -rf dist
39-
rm -rf node_modules
40-
rm -rf src/declarations
41-
rm -f .env
42-
cargo clean
76+
@echo "=== Test 9: get_block_headers returns headers ==="
77+
@result=$$(icp canister call backend get_block_headers '(0: nat32, null)') && \
78+
echo "$$result" && \
79+
echo "$$result" | grep -q 'tip_height' && \
80+
echo "PASS" || (echo "FAIL" && exit 1)

0 commit comments

Comments
 (0)