diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 3fdbc36a..cbb19195 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -2,7 +2,7 @@ name: CI Checks env: RUST_VERSION: 1.68.0 - DFX_VERSION: 0.13.1 + DFX_VERSION: 0.15.2-beta.0 on: push: @@ -353,6 +353,38 @@ jobs: run: | bash e2e-tests/set_config.sh + cycles_burn: + runs-on: ubuntu-20.04 + needs: cargo-build + + steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-1 + + - name: Install Rust + run: | + rustup update ${{ matrix.rust }} --no-self-update + rustup default ${{ matrix.rust }} + rustup target add wasm32-unknown-unknown + + - name: Install DFX + run: | + wget --output-document install-dfx.sh "https://internetcomputer.org/install.sh" + bash install-dfx.sh < <(yes Y) + rm install-dfx.sh + dfx cache install + echo "$HOME/bin" >> $GITHUB_PATH + + - name: Run cycles_burn test + run: | + bash e2e-tests/cycles_burn.sh + benchmark: runs-on: ubuntu-20.04 needs: cargo-build @@ -529,6 +561,7 @@ jobs: - charge-cycles-on-reject - upgradability - set_config + - cycles_burn - benchmark - watchdog_health_status - watchdog_get_config diff --git a/Cargo.lock b/Cargo.lock index a3807b66..53b14bab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -320,7 +320,7 @@ dependencies = [ "ic-btc-canister", "ic-btc-interface", "ic-btc-types", - "ic-cdk 0.10.0", + "ic-cdk 0.11.3", "ic-cdk-macros 0.7.0", "lazy_static", "maplit", @@ -550,7 +550,7 @@ version = "0.1.0" dependencies = [ "candid 0.9.10", "futures", - "ic-cdk 0.10.0", + "ic-cdk 0.11.3", "ic-cdk-macros 0.7.0", "ic-http", "serde", @@ -862,7 +862,7 @@ dependencies = [ "bitcoin", "candid 0.9.10", "ic-btc-test-utils", - "ic-cdk 0.10.0", + "ic-cdk 0.11.3", "ic-cdk-macros 0.7.0", "serde", ] @@ -1409,7 +1409,7 @@ dependencies = [ "ic-btc-test-utils", "ic-btc-types", "ic-btc-validation", - "ic-cdk 0.10.0", + "ic-cdk 0.11.3", "ic-cdk-macros 0.7.0", "ic-metrics-encoder", "ic-stable-structures", @@ -1472,20 +1472,20 @@ checksum = "9beb0bf1dcd0639c313630e34aa547a2b19450ddf1969c176e13225ef3b29048" dependencies = [ "candid 0.8.4", "ic-cdk-macros 0.6.10", - "ic0", + "ic0 0.18.11", "serde", "serde_bytes", ] [[package]] name = "ic-cdk" -version = "0.10.0" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d4c0b932bf454d5d60e61e13c3c944972fcfd74dc82b9ed5c8b0a75979cf50" +checksum = "c126ac20219abff15c3441282e9da6aa7244319d5a4a42c7260667237e790712" dependencies = [ "candid 0.9.10", - "ic-cdk-macros 0.7.0", - "ic0", + "ic-cdk-macros 0.8.1", + "ic0 0.21.1", "serde", "serde_bytes", ] @@ -1518,6 +1518,20 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ic-cdk-macros" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6295fd7389c198a97dd99b28b846e18487d99303077102d817eebbf6a924cd" +dependencies = [ + "candid 0.9.10", + "proc-macro2", + "quote", + "serde", + "serde_tokenstream", + "syn 1.0.109", +] + [[package]] name = "ic-cdk-timers" version = "0.1.2" @@ -1526,7 +1540,7 @@ checksum = "c739e7c592cb66df4f15c6b6c4859b1195782f63923e2fb1b29553d9c0819bd4" dependencies = [ "futures", "ic-cdk 0.7.4", - "ic0", + "ic0 0.18.11", "serde", "serde_bytes", "slotmap", @@ -1538,7 +1552,7 @@ version = "0.1.0" dependencies = [ "candid 0.9.10", "futures", - "ic-cdk 0.10.0", + "ic-cdk 0.11.3", "ic-cdk-macros 0.7.0", "serde_json", "tokio", @@ -1574,6 +1588,12 @@ version = "0.18.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "576c539151d4769fb4d1a0c25c4108dd18facd04c5695b02cf2d226ab4e43aa5" +[[package]] +name = "ic0" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a54b5297861c651551676e8c43df805dad175cc33bc97dbd992edbbb85dcbcdf" + [[package]] name = "idna" version = "0.4.0" @@ -2638,7 +2658,7 @@ dependencies = [ "bitcoin", "candid 0.9.10", "ic-btc-test-utils", - "ic-cdk 0.10.0", + "ic-cdk 0.11.3", "ic-cdk-macros 0.7.0", "serde", ] @@ -2650,7 +2670,7 @@ dependencies = [ "bitcoin", "candid 0.9.10", "ic-btc-test-utils", - "ic-cdk 0.10.0", + "ic-cdk 0.11.3", "ic-cdk-macros 0.7.0", "serde", ] @@ -2662,7 +2682,7 @@ dependencies = [ "bitcoin", "candid 0.9.10", "ic-btc-test-utils", - "ic-cdk 0.10.0", + "ic-cdk 0.11.3", "ic-cdk-macros 0.7.0", "serde", ] @@ -3375,7 +3395,7 @@ dependencies = [ "clap", "garcon", "ic-agent", - "ic-cdk 0.10.0", + "ic-cdk 0.11.3", "ic-cdk-macros 0.7.0", "serde", "sha256", @@ -3536,7 +3556,7 @@ dependencies = [ "futures", "hex", "ic-btc-interface", - "ic-cdk 0.10.0", + "ic-cdk 0.11.3", "ic-cdk-macros 0.7.0", "ic-cdk-timers", "ic-http", diff --git a/Cargo.toml b/Cargo.toml index 99f5ca04..fba1d055 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ ic-btc-interface = { path = "./interface" } ic-btc-types = { path = "./types" } ic-btc-test-utils = { path = "./test-utils" } ic-btc-validation = { path = "./validation" } -ic-cdk = "0.10.0" +ic-cdk = "0.11.2" ic-cdk-macros = "0.7.0" ic-http = { path = "./ic-http" } ic-metrics-encoder = "1.0.0" @@ -45,4 +45,4 @@ lazy_static = "1.4.0" serde = "1.0.171" serde_bytes = "0.11" serde_json = "1.0.94" -tokio = { version = "1.29.1", features = [ "full" ] } +tokio = { version = "1.29.1", features = ["full"] } diff --git a/canister/src/api/metrics.rs b/canister/src/api/metrics.rs index 8ce9a1d4..ec023a8e 100644 --- a/canister/src/api/metrics.rs +++ b/canister/src/api/metrics.rs @@ -133,6 +133,12 @@ fn encode_metrics(w: &mut MetricsEncoder>) -> std::io::Result<()> { "The total number of (valid) requests to the send_transaction endpoint.", )?; + w.encode_gauge( + "cycles_burnt", + state.metrics.cycles_burnt.unwrap_or_default() as f64, + "The total number of cycles burnt.", + )?; + w.encode_gauge( "cycles_balance", ic_cdk::api::canister_balance() as f64, diff --git a/canister/src/heartbeat.rs b/canister/src/heartbeat.rs index 50f45ca7..8a06681c 100644 --- a/canister/src/heartbeat.rs +++ b/canister/src/heartbeat.rs @@ -1,5 +1,5 @@ use crate::{ - runtime::{call_get_successors, print}, + runtime::{call_get_successors, cycles_burn, print}, state::{self, ResponseToProcess}, types::{ GetSuccessorsCompleteResponse, GetSuccessorsRequest, GetSuccessorsRequestInitial, @@ -17,6 +17,11 @@ use ic_btc_types::{Block, BlockHash}; /// The heartbeat fetches new blocks from the bitcoin network and inserts them into the state. pub async fn heartbeat() { print("Starting heartbeat..."); + + // Burn any cycles in the canister's balance (to count towards the IC's cycles burn rate) + let cycles_burnt = cycles_burn(); + add_cycles_burnt_to_metric(cycles_burnt); + if ingest_stable_blocks_into_utxoset() { // Exit the heartbeat if stable blocks had been ingested. // This is a precaution to not exceed the instructions limit. @@ -228,6 +233,16 @@ fn maybe_get_successors_request() -> Option { }) } +fn add_cycles_burnt_to_metric(cycles_burnt: u128) { + with_state_mut(|s| { + if let Some(metric_cycles_burnt) = &mut s.metrics.cycles_burnt { + *metric_cycles_burnt += cycles_burnt; + } else { + s.metrics.cycles_burnt = Some(cycles_burnt); + } + }); +} + #[cfg(test)] mod test { use super::*; diff --git a/canister/src/metrics.rs b/canister/src/metrics.rs index 208be137..09cf361f 100644 --- a/canister/src/metrics.rs +++ b/canister/src/metrics.rs @@ -26,6 +26,9 @@ pub struct Metrics { /// Instructions needed to insert a block into the pool of unstable blocks. pub block_insertion: InstructionHistogram, + + /// The total number of cycles burnt. + pub cycles_burnt: Option, } impl Default for Metrics { @@ -66,6 +69,8 @@ impl Default for Metrics { "ins_block_insertion", "Instructions needed to insert a block into the pool of unstable blocks.", ), + + cycles_burnt: Some(0), } } } diff --git a/canister/src/runtime.rs b/canister/src/runtime.rs index f9004f48..031f7c26 100644 --- a/canister/src/runtime.rs +++ b/canister/src/runtime.rs @@ -217,3 +217,13 @@ pub fn time() -> u64 { .unwrap() .as_secs() } + +#[cfg(target_arch = "wasm32")] +pub fn cycles_burn() -> u128 { + ic_cdk::api::cycles_burn(ic_cdk::api::canister_balance128()) +} + +#[cfg(not(target_arch = "wasm32"))] +pub fn cycles_burn() -> u128 { + 1_000_000 +} diff --git a/canister/src/tests.rs b/canister/src/tests.rs index 5e4deb34..d7bce91d 100644 --- a/canister/src/tests.rs +++ b/canister/src/tests.rs @@ -716,3 +716,35 @@ async fn test_syncing_with_next_block_headers() { assert!(catch_unwind(verify_synced).is_err()); } + +#[async_std::test] +async fn cycles_burnt_are_tracked_in_metrics() { + crate::init(crate::Config::default()); + + let cycles_burnt_0 = crate::with_state(|state| state.metrics.cycles_burnt); + + assert_eq!(cycles_burnt_0, Some(0)); + + let burn_amount = 1_000_000; + + // Burn cycles. + heartbeat().await; + + let cycles_burnt_1 = crate::with_state(|state| state.metrics.cycles_burnt); + + assert_eq!(cycles_burnt_1, Some(burn_amount)); + + // Burn cycles. + heartbeat().await; + + let cycles_burnt_2 = crate::with_state(|state| state.metrics.cycles_burnt); + + assert_eq!(cycles_burnt_2, Some(2 * burn_amount)); + + // Burn cycles. + heartbeat().await; + + let cycles_burnt_3 = crate::with_state(|state| state.metrics.cycles_burnt); + + assert_eq!(cycles_burnt_3, Some(3 * burn_amount)); +} diff --git a/dfx.json b/dfx.json index 71d12c9c..fb153af8 100644 --- a/dfx.json +++ b/dfx.json @@ -1,5 +1,5 @@ { - "dfx": "0.13.1", + "dfx": "0.15.2-beta.0", "canisters": { "bitcoin": { "type": "custom", @@ -78,9 +78,11 @@ } }, "testnet": { - "providers": ["http://[2a00:fb01:400:42:5000:aaff:fea4:ae46]:8080"], + "providers": [ + "http://[2a00:fb01:400:42:5000:aaff:fea4:ae46]:8080" + ], "type": "persistent" } }, "version": 1 -} +} \ No newline at end of file diff --git a/e2e-tests/cycles_burn.sh b/e2e-tests/cycles_burn.sh new file mode 100755 index 00000000..972e4a75 --- /dev/null +++ b/e2e-tests/cycles_burn.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -Eexuo pipefail + +get_balance() { + dfx canister status bitcoin 2>&1 | grep "Balance: " | awk '{ print $2 }' +} + +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +source "${SCRIPT_DIR}/utils.sh" + +# Run dfx stop if we run into errors. +trap "dfx stop" EXIT SIGINT + +dfx start --background --clean + +INITIAL_BALANCE=100000000000 + +# Deploy the bitcoin canister. +dfx deploy --no-wallet --with-cycles "$INITIAL_BALANCE" bitcoin --argument "(record { + stability_threshold = 0; + network = variant { regtest }; + blocks_source = principal \"aaaaa-aa\"; + syncing = variant { enabled }; + fees = record { + get_utxos_base = 0; + get_utxos_cycles_per_ten_instructions = 0; + get_utxos_maximum = 0; + get_balance = 0; + get_balance_maximum = 0; + get_current_fee_percentiles = 0; + get_current_fee_percentiles_maximum = 0; + send_transaction_base = 0; + send_transaction_per_byte = 0; + }; + api_access = variant { enabled }; + disable_api_if_not_fully_synced = variant { enabled }; + watchdog_canister = null; +})" + +sleep 3 + +# Check that cycles are burnt. +if [ "$(get_balance)" != "0" ]; then + EXIT SIGINT +fi + +echo "SUCCESS" diff --git a/e2e-tests/scenario-1.sh b/e2e-tests/scenario-1.sh index f5c4069a..00e0595f 100755 --- a/e2e-tests/scenario-1.sh +++ b/e2e-tests/scenario-1.sh @@ -130,7 +130,7 @@ GET_UTXOS_QUERY_REPLICATED_CALL=$(dfx canister call --update bitcoin bitcoin_get })' 2>&1) set -e -if [[ $GET_UTXOS_QUERY_REPLICATED_CALL != *"Http Error: status 403 Forbidden"* ]]; then +if [[ $GET_UTXOS_QUERY_REPLICATED_CALL != *"CanisterReject"* ]]; then echo "FAIL" exit 1 fi @@ -153,7 +153,7 @@ GET_BALANCE_QUERY_REPLICATED_CALL=$(dfx canister call --update bitcoin bitcoin_g })' 2>&1) set -e -if [[ $GET_BALANCE_QUERY_REPLICATED_CALL != *"Http Error: status 403 Forbidden"* ]]; then +if [[ $GET_BALANCE_QUERY_REPLICATED_CALL != *"CanisterReject"* ]]; then echo "FAIL" exit 1 fi diff --git a/ic-http/example_canister/src/canister_backend/Cargo.toml b/ic-http/example_canister/src/canister_backend/Cargo.toml index 33042fa5..96deaa57 100644 --- a/ic-http/example_canister/src/canister_backend/Cargo.toml +++ b/ic-http/example_canister/src/canister_backend/Cargo.toml @@ -11,10 +11,10 @@ path = "src/main.rs" [dependencies] candid = "0.9.1" -ic-cdk = "0.10.0" +ic-cdk = "0.11.2" ic-cdk-macros = "0.7.0" -ic-http = {path = "../../../"} -serde = { workspace = true, features = [ "derive" ] } +ic-http = { path = "../../../" } +serde = { workspace = true, features = ["derive"] } serde_json = "1.0.94" [dev-dependencies]