diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3ffe0400ace..186c9c4b38b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -208,6 +208,7 @@ go_deps.bzl @dfinity/idx /rs/rust_canisters/stable_structures/ @dfinity/execution /rs/rust_canisters/stable_memory_integrity @dfinity/execution /rs/rust_canisters/canister_creator @dfinity/execution +/rs/rust_canisters/load_simulator @dfinity/execution /rs/rust_canisters/xnet_test/ @dfinity/ic-message-routing-owners /rs/rust_canisters/downstream_calls_test/ @dfinity/ic-message-routing-owners /rs/rust_canisters/random_traffic_test/ @dfinity/ic-message-routing-owners diff --git a/Cargo.lock b/Cargo.lock index d8e649842f7..336b8da1b7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6547,7 +6547,9 @@ dependencies = [ "candid_parser", "canister-test", "ciborium", + "flate2", "hex", + "ic-agent", "ic-base-types", "ic-bitcoin-canister-mock", "ic-btc-interface", @@ -6585,6 +6587,7 @@ dependencies = [ "serde_bytes", "serde_json", "simple_asn1", + "tokio", ] [[package]] @@ -15094,6 +15097,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "load-simulator" +version = "0.1.0" +dependencies = [ + "candid", + "ic-cdk 0.16.0", + "ic-cdk-macros 0.9.0", + "ic-cdk-timers", +] + [[package]] name = "local-channel" version = "0.1.5" @@ -16966,7 +16979,7 @@ dependencies = [ [[package]] name = "pocket-ic" -version = "5.0.0" +version = "6.0.0" dependencies = [ "base64 0.13.1", "candid", @@ -17001,7 +17014,7 @@ dependencies = [ [[package]] name = "pocket-ic-server" -version = "6.0.0" +version = "7.0.0" dependencies = [ "aide", "askama", diff --git a/Cargo.toml b/Cargo.toml index 34de9fcdc19..d0872ee9706 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -316,6 +316,7 @@ members = [ "rs/rust_canisters/downstream_calls_test", "rs/rust_canisters/ecdsa", "rs/rust_canisters/http_types", + "rs/rust_canisters/load_simulator", "rs/rust_canisters/memory_test", "rs/rust_canisters/on_wire", "rs/rust_canisters/pmap", diff --git a/ic-os/boundary-guestos/context/docker-base.prod b/ic-os/boundary-guestos/context/docker-base.prod index 4209ece39eb..11e961748a0 100644 --- a/ic-os/boundary-guestos/context/docker-base.prod +++ b/ic-os/boundary-guestos/context/docker-base.prod @@ -1 +1 @@ -ghcr.io/dfinity/boundaryos-base@sha256:451327fb6888c436361465a5734f9838e84c2e394843f1a4400bfa8437f43c9e +ghcr.io/dfinity/boundaryos-base@sha256:a6f3fb0d6c40dc709cd19d473807f64318dc599d79e138243b279bdf13d36150 diff --git a/ic-os/components/ic/generate-ic-config/generate-ic-config.sh b/ic-os/components/ic/generate-ic-config/generate-ic-config.sh index b19d666e475..dc5aa205e65 100755 --- a/ic-os/components/ic/generate-ic-config/generate-ic-config.sh +++ b/ic-os/components/ic/generate-ic-config/generate-ic-config.sh @@ -217,7 +217,6 @@ IPV4_ADDRESS="${ipv4_address:-}" IPV4_GATEWAY="${ipv4_gateway:-}" DOMAIN="${domain:-}" NNS_URLS="${nns_urls:-http://[::1]:8080}" -NODE_INDEX="${node_index:-0}" # Default value is 24h BACKUP_RETENTION_TIME_SECS="${backup_retention_time_secs:-86400}" # Default value is 1h @@ -239,7 +238,6 @@ sed -e "s@{{ ipv6_address }}@${IPV6_ADDRESS}@" \ -e "s@{{ ipv4_gateway }}@${IPV4_GATEWAY}@" \ -e "s@{{ domain }}@${DOMAIN}@" \ -e "s@{{ nns_urls }}@${NNS_URLS}@" \ - -e "s@{{ node_index }}@${NODE_INDEX}@" \ -e "s@{{ backup_retention_time_secs }}@${BACKUP_RETENTION_TIME_SECS}@" \ -e "s@{{ backup_purging_interval_secs }}@${BACKUP_PURGING_INTERVAL_SECS}@" \ -e "s@{{ malicious_behavior }}@${MALICIOUS_BEHAVIOR}@" \ diff --git a/ic-os/components/ic/generate-ic-config/ic.json5.template b/ic-os/components/ic/generate-ic-config/ic.json5.template index 72671ecd83e..e6a506b8683 100644 --- a/ic-os/components/ic/generate-ic-config/ic.json5.template +++ b/ic-os/components/ic/generate-ic-config/ic.json5.template @@ -3,7 +3,7 @@ // Global Replica Configuration // ============================================ - node_id: "{{ node_index }}", + node_id: "0", // ======================================================= // Configuration of transport parameters and node identity @@ -158,7 +158,7 @@ // =================================== logger: { // The node id to append to log lines. - node_id: {{ node_index }}, + node_id: 0, // The log level to use. level: "info", // The format of emitted log lines diff --git a/ic-os/components/setupos-scripts/functions.sh b/ic-os/components/setupos-scripts/functions.sh index 703b64fd2f9..2513ef7c248 100755 --- a/ic-os/components/setupos-scripts/functions.sh +++ b/ic-os/components/setupos-scripts/functions.sh @@ -12,6 +12,11 @@ function log_and_halt_installation_on_error() { if [ "${exit_code}" -ne 0 ]; then { + # Sleep before printing error log to ensure previous log messages + # have time to display on console (helpful for screen recordings). + echo "ERROR DETECTED..." + sleep 5 + echo -e "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" echo "--------------------------------------------------------------------------------" echo " INTERNET COMPUTER - SETUP - FAILED" diff --git a/ic-os/guestos/context/docker-base.dev b/ic-os/guestos/context/docker-base.dev index ed552abbf84..35aeb1973ec 100644 --- a/ic-os/guestos/context/docker-base.dev +++ b/ic-os/guestos/context/docker-base.dev @@ -1 +1 @@ -ghcr.io/dfinity/guestos-base-dev@sha256:8a0b0eb1d92df090f47c653307ccd8a537bf2d83de94f39c3aacca0ff6743074 +ghcr.io/dfinity/guestos-base-dev@sha256:60ceca01eb6ed570bd99025305005e02bf586cf08476c028ef52aeaf56541d00 diff --git a/ic-os/guestos/context/docker-base.prod b/ic-os/guestos/context/docker-base.prod index ab12979a1f3..fe7bdd84ad1 100644 --- a/ic-os/guestos/context/docker-base.prod +++ b/ic-os/guestos/context/docker-base.prod @@ -1 +1 @@ -ghcr.io/dfinity/guestos-base@sha256:618c1c4755af3fa30357350d983c89dbd8999ea61fb1cf31707a44f62d4b3161 +ghcr.io/dfinity/guestos-base@sha256:a22e4f8093866002d324d0583b0a33c15725954cb1d132c6d89971f80a383201 diff --git a/ic-os/hostos/context/docker-base.dev b/ic-os/hostos/context/docker-base.dev index 8c059b9d5d0..6a634a8cfd1 100644 --- a/ic-os/hostos/context/docker-base.dev +++ b/ic-os/hostos/context/docker-base.dev @@ -1 +1 @@ -ghcr.io/dfinity/hostos-base-dev@sha256:37f5f725a1390f55cf6e3e65bef34464c432867fdc14207b66aa1793d6cb0d56 +ghcr.io/dfinity/hostos-base-dev@sha256:8fa9b6f10bd6b1ee7438407db3d10d2e8b22fcff959616986d57bdbd4a884c17 diff --git a/ic-os/hostos/context/docker-base.prod b/ic-os/hostos/context/docker-base.prod index 37d8f551018..4c2ab0863fb 100644 --- a/ic-os/hostos/context/docker-base.prod +++ b/ic-os/hostos/context/docker-base.prod @@ -1 +1 @@ -ghcr.io/dfinity/hostos-base@sha256:425cfbb72c967ea8ea28ee3a23cfe10271ae7b641e54f816a29422a7650d08bd +ghcr.io/dfinity/hostos-base@sha256:b5b7d3f25b3381a2eddcb8942f00c4f46dbe2d9ca4c81d966f9fbc9f72c3024c diff --git a/ic-os/setupos/context/docker-base.dev b/ic-os/setupos/context/docker-base.dev index 7b139ab8b11..081572d4496 100644 --- a/ic-os/setupos/context/docker-base.dev +++ b/ic-os/setupos/context/docker-base.dev @@ -1 +1 @@ -ghcr.io/dfinity/setupos-base-dev@sha256:4a8a66d87bf6de3daf5f080e25add9e2934984a90e8acdfb7f6acfcd6e646895 +ghcr.io/dfinity/setupos-base-dev@sha256:4f0e8cdfae3fd8fab096d29c3ded0fbb18eb9c56ff1aa67bc72a5d3a4692e43e diff --git a/ic-os/setupos/context/docker-base.prod b/ic-os/setupos/context/docker-base.prod index 93da252f914..1a11002421c 100644 --- a/ic-os/setupos/context/docker-base.prod +++ b/ic-os/setupos/context/docker-base.prod @@ -1 +1 @@ -ghcr.io/dfinity/setupos-base@sha256:40ec1d9ad32e1cf8931a188bf6a4a1adb9962788a47956db43a41ed12441832c +ghcr.io/dfinity/setupos-base@sha256:9142679e9fbe9fe98524930745ac2f3cc0e556f472f40ab38036f8ff4d1321f5 diff --git a/packages/pocket-ic/BUILD.bazel b/packages/pocket-ic/BUILD.bazel index 408e82f1352..9133fb86581 100644 --- a/packages/pocket-ic/BUILD.bazel +++ b/packages/pocket-ic/BUILD.bazel @@ -45,7 +45,7 @@ rust_library( name = "pocket-ic", srcs = glob(["src/**/*.rs"]), proc_macro_deps = MACRO_DEPENDENCIES, - version = "5.0.0", + version = "6.0.0", deps = DEPENDENCIES, ) diff --git a/packages/pocket-ic/CHANGELOG.md b/packages/pocket-ic/CHANGELOG.md index 2b206ae3997..50b7faa0f14 100644 --- a/packages/pocket-ic/CHANGELOG.md +++ b/packages/pocket-ic/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased + + +## 6.0.0 - 2024-11-13 + ### Added - The function `PocketIc::get_subnet_metrics` to retrieve metrics of a given subnet. - The function `PocketIcBuilder::with_bitcoind_addr` to specify the address and port at which a `bitcoind` process is listening. diff --git a/packages/pocket-ic/Cargo.toml b/packages/pocket-ic/Cargo.toml index 5b0d6f0dd74..f9d0d8d5fe5 100644 --- a/packages/pocket-ic/Cargo.toml +++ b/packages/pocket-ic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pocket-ic" -version = "5.0.0" +version = "6.0.0" license = "Apache-2.0" description = "PocketIC: A Canister Smart Contract Testing Platform" repository = "https://github.com/dfinity/ic" diff --git a/packages/pocket-ic/src/lib.rs b/packages/pocket-ic/src/lib.rs index c19703db039..91f146c237f 100644 --- a/packages/pocket-ic/src/lib.rs +++ b/packages/pocket-ic/src/lib.rs @@ -76,7 +76,7 @@ pub mod common; pub mod management_canister; pub mod nonblocking; -const EXPECTED_SERVER_VERSION: &str = "pocket-ic-server 6.0.0"; +const EXPECTED_SERVER_VERSION: &str = "pocket-ic-server 7.0.0"; // the default timeout of a PocketIC operation const DEFAULT_MAX_REQUEST_TIME_MS: u64 = 300_000; diff --git a/publish/canisters/BUILD.bazel b/publish/canisters/BUILD.bazel index 2d4068ed466..03d373171cc 100644 --- a/publish/canisters/BUILD.bazel +++ b/publish/canisters/BUILD.bazel @@ -8,6 +8,7 @@ load("//ci/src/artifacts:upload.bzl", "upload_artifacts") # 4. Canisters required for test environments. CANISTERS = { "canister-creator-canister.wasm.gz": "//rs/rust_canisters/canister_creator:canister_creator_canister", + "load_simulator_canister.wasm.gz": "//rs/rust_canisters/load_simulator:load_simulator_canister", "cycles-minting-canister.wasm.gz": "//rs/nns/cmc:cycles-minting-canister", "genesis-token-canister.wasm.gz": "//rs/nns/gtc:genesis-token-canister", "governance-canister.wasm.gz": "//rs/nns/governance:governance-canister", diff --git a/rs/bitcoin/ckbtc/minter/BUILD.bazel b/rs/bitcoin/ckbtc/minter/BUILD.bazel index 56310cc84d6..38bd64a0836 100644 --- a/rs/bitcoin/ckbtc/minter/BUILD.bazel +++ b/rs/bitcoin/ckbtc/minter/BUILD.bazel @@ -129,6 +129,25 @@ rust_test( ], ) +rust_test( + name = "ckbtc_minter_replay_events_tests", + srcs = ["tests/replay_events.rs"], + data = [ + "test_resources/mainnet_events.gz", + "test_resources/testnet_events.gz", + ], + deps = [ + # Keep sorted. + ":ckbtc_minter_lib", + "@crate_index//:bitcoin", + "@crate_index//:candid", + "@crate_index//:flate2", + "@crate_index//:ic-agent", + "@crate_index//:serde", + "@crate_index//:tokio", + ], +) + # integration tests defined in ckbtc minter tests/ rust_ic_test( name = "ckbtc_minter_tests", diff --git a/rs/bitcoin/ckbtc/minter/Cargo.toml b/rs/bitcoin/ckbtc/minter/Cargo.toml index 31984ad3f88..6ff4caf5d8d 100644 --- a/rs/bitcoin/ckbtc/minter/Cargo.toml +++ b/rs/bitcoin/ckbtc/minter/Cargo.toml @@ -52,6 +52,8 @@ assert_matches = { workspace = true } bitcoin = { workspace = true } candid_parser = { workspace = true } canister-test = { path = "../../../rust_canisters/canister_test" } +flate2 = { workspace = true } +ic-agent = { workspace = true } ic-bitcoin-canister-mock = { path = "../../mock" } ic-config = { path = "../../../config" } ic-icrc1-ledger = { path = "../../../ledger_suite/icrc1/ledger" } @@ -60,6 +62,7 @@ ic-test-utilities-load-wasm = { path = "../../../test_utilities/load_wasm" } ic-types = { path = "../../../types/types" } proptest = { workspace = true } simple_asn1 = { workspace = true } +tokio = { workspace = true } [features] self_check = [] diff --git a/rs/bitcoin/ckbtc/minter/src/blocklist.rs b/rs/bitcoin/ckbtc/minter/src/blocklist.rs deleted file mode 100644 index 3f27965b5b6..00000000000 --- a/rs/bitcoin/ckbtc/minter/src/blocklist.rs +++ /dev/null @@ -1,344 +0,0 @@ -/// The list of addresses to which we do not allow retrievals. -/// NOTE: Keep it sorted! -pub const BTC_ADDRESS_BLOCKLIST: &[&str] = &[ - "123WBUDmSJv4GctdVEz6Qq6z8nXSKrJ4KX", - "1295rkVyNfFpqZpXvKGhDqwhP1jZcNNDMV", - "129zKFLoVad9JtxSmDKeJoLCsjhGR7b3vr", - "12HQDsicffSBaYdJ6BhnE22sfjTESmmzKx", - "12NpCkhddSNiDkD9rRYUCHsTT9ReMNiJjG", - "12QtD5BFwRsdNsAZY76UVE1xyCGNTojH9h", - "12VrYZgS1nmf9KHHped24xBb1aLLRpV2cT", - "12YyR9EpvHxBjjKjTWqfKqeyoWnvcraxpW", - "12mNKr2YP4M3CEQvCvVqZsvxuCG47LHMu1", - "12udabs2TkX7NXCSj6KpqXfakjE52ZPLhz", - "134r8iHv69xdT6p5qVKTsHrcUEuBVZAYak", - "13LQJQ1oJ9K7PsqsGfjNhoVv6UeU6hgzQz", - "13RH4JaFhaCxDGPyYE9emjp2aDxdX18uBA", - "13ViCDZyJxxv5cZzpDDsE7aDQ3Y552zpAH", - "13YBQr2Cp1YY3xqq2qngaPb7ca1o4ugeq6", - "13f59kUM5FU8MfTG7DCEugYarDhSD7XCoC", - "13hfsQm6oCaDZehfYBSMFiJVAi1jsL6sQd", - "13mnk8SvDGqsQTHbiGiHBXqtaQCUKfcsnP", - "148LKmyZT3FGE4x1GjsFN6RsAwcjzk5iuE", - "149w62rY42aZBox8fGcmqNsXUzSStKeq8C", - "14gM1HuLVDELNHaFU22qpabjtiWek4HhV1", - "14kqryJUxM3a7aEi117KX9hoLUw592WsMR", - "158treVZBGMBThoaympxccPdZPtqUfYrT9", - "15PggTG7YhJKiE6B16vkKzA1YDTZipXEX4", - "15Pt4NwZaUmMUwS2bQbyyncc7mzgWShtv8", - "15UdZbmGPa2LatD3abtGpphgkHLFWftV4R", - "15YK647qtoZQDzNrvY6HJL6QwXduLHfT28", - "15uqdxqNXQwVf5H7yZPz4TmEGeSccCwdor", - "15yqWQ4sqr7jzCwDtZ3U1KaCa8WMEy7Mm2", - "16EKTes8ahD8xvwisqjc2xSNLiG3fDHatW", - "16PhXY3hNNMTo8kpuJx2emh713KbWpkqci", - "16SPDQFFzgsoNSPiFFTfS8Dw8LLXqia4oc", - "16ZSAEfYpPCj3D94fsNt2okYj9Ue8mxy6T", - "16p2UWTZwXRyK5bTHNVjdDyy1D3EQGsZf2", - "16tByCYzxuWiN8kF9FrK9jJy6eQYLVkQ1i", - "175BUqf8JCU1uoG1iTRKTacDa4uvJDUCw2", - "17UVSMegvrzfobKC82dHXpZLtLcqzW9stF", - "17V7THwHMiDJmDwZK4unhE5HgKFJKx7VCe", - "17a5bpKvEp1j1Trs4qTbcNZrby53JbaS9C", - "17ezuJoT3XBbdcwFZbkTnrXbup11F4uhiy", - "17mhyeBX617ABZ1ffThhUTJkHUcMvCkfd5", - "182NGZbPJXwg2WDrhrPpR7tpiGQkNPF844", - "18Ke1QWE9nQfXuhJijHggZuPJ5ZYxapoBK", - "18Qj1THHuETfYhuRDZycXJbWwDMGw73Poa", - "18cFGAdYcvNHkuhXLBE7izQKCyUW8TzCJE", - "18uKfaUjgG52rVeXEi3wxnveww7zZuECtE", - "194xmrZA53UBsZau2PnJLdmVVW9m5feeS", - "19D8PHBjZH29uS1uPZ4m3sVyqqfF8UFG9o", - "19FQzHibWDhSP8pKmJS3uagFYoisXtehzw", - "19GrL5jnUkGmHXVcraB1Etv5rXCANeLWpq", - "19pPbUDvoSBZafkUCYkD2Z9AkuqqV6sWm7", - "1A3iYY4c3dkgNYGewzYzr7EsqfBuWXibGo", - "1ANpca7g93BwptUJg1zV116v49zn9gjDi3", - "1AXUTu9y3H8w4wYx4BjyFWgRhZKDhmcMrn", - "1AeSq93WDNdLoEJ92sex7T8xQZoYYm8BtS", - "1AjZPMsnmpdK2Rv9KQNfMurTXinscVro9V", - "1AoxtfiBQ22DvbhqAN9Ctb8sULMRhrdwTr", - "1B11Ezqg3AXjFhMdRq5UpPDpNyriYNVtkn", - "1B3u21itzjgKtm7QsNQNCBpSkwzzeDHqrW", - "1B64QRxfaa35MVkf7sDjuGUYAP5izQt7Qi", - "1BCWMwpR4M1nYUuuYe2bmzrNuwGoF9ZAbA", - "1BiUFjzH6wsT73U3tfy4aXHCQsYQHzjk5h", - "1BvJRBRp9ZZ6zLyuZaZsV7g3xP6JokdZQW", - "1C7RpJNE19HgefzWVCSaUqRTHAwGAFkbYV", - "1CF46Rfbp97absrs7zb7dFfZS6qBXUm9EP", - "1CG1aSCxUnbmv9G34ofxTQoHtuVnMLJtQV", - "1CNbhgxGRZvsWnEHotfXge7k2E1UPzBDC7", - "1CddRqw7oSPrT4tt5oXKyx2LiHJDPszy7y", - "1D1ej7zQzywWBDNXKNYpmH7Hso2U9koDG4", - "1D3GuaS9eqKw8dWj9JFQtNufdRtysjSLxZ", - "1DDA93oZPn7wte2eR1ABwcFoxUFxkKMwCf", - "1DGsY4ww3BJnWXTsnmTgWa6UWdoRXgA1pX", - "1DH2xDH7TngrDU6LXciprKCBKNcPA1xX8A", - "1DJoEMvp95yJYWyxAZy8DDBzuvjnrTVrsN", - "1DKGRGJXGNLAtTeFb9SNPNHtrkZ87q7qKi", - "1DT3tenf14cxz9WFNxmYrXFbB6TFiVWA9U", - "1DYFJ6CuBvrxyoQSuBzVsNcetY9tvdsrag", - "1DbShx4r8i2XesthoDBf5EkYWz5dsKEusV", - "1DbvK8P6imBuLcwh2Vruis4xsUb8YAwJQF", - "1Df883c96LVauVsx9FEgnsourD8DELwCUQ", - "1Dpddb1TMjvmNQeYDqgyd1ww6cmwPJRdSk", - "1DtGgdCi9VPKz2Bpq8GQhUQEPnQ5HwaT9n", - "1E9uUnLbyfToazo95vmM3ysYnzgkrL7GeC", - "1ECeZBxCVJ8Wm2JSN3Cyc6rge2gnvD3W5K", - "1EYitrwBYNWuTBcjZFbEUdqHppe2raLpaF", - "1Edue8XZCWNoDBNZgnQkCCivDyr9GEo4x6", - "1EfMVkxQQuZfBdocpJu6RUsCJvenQWbQyE", - "1EpMiZkQVekM5ij12nMiEwttFPcDK9XhX6", - "1EtMuBPQnPCa3cecerdSH1SzydxnhbTmw", - "1EuUMPBCZtSd5pVVFEqmRqUSfU1qy6ASuL", - "1EvhBad5wCZYhBoAsGaciV6AvmZ1osLpeJ", - "1F2Gdug9ib9NQMhKMGGJczzMk5SuENoqrp", - "1F317n2eJEMaEMGiwCqtd5XCU3wF7jzPEW", - "1F7UL41qYm6TvnExZzPHBCyeENvX3XDEMS", - "1FE2cuvkq8n5VGwj5hi8YYQxskwJpovPyV", - "1FFS6pX1TCKTNy668Mbk2Lyoem1qB48kYX", - "1FRyL9gmFGbzfYDAB4iY9836DJe3KSnjP9", - "1FjubFHV4mpYjBmvjsEhZssyiiA4TNmnm2", - "1Fz29BQp82pE3vXXcsZoMNQ3KSHfMzfMe3", - "1G64TFMFVJTjhJXra6x74BBhsfSyiWaFtT", - "1G6DuwDKNHiUWqks2Lgu44cesu7ffFbLK7", - "1G9A8WRjGXdnYY4TNEVRrcaHsMtana4ncF", - "1G9CKRHA3mx22DoT1QyNYrh85VSQ19Y1em", - "1GYuu9d5HPikafbys3k5Q3DRJq6debGsoB", - "1GcKLUUXodTQcLcPD7VLMgvCc4hs5Q775", - "1Ge8JodC2HiBiEuT7D3MoH6Fak6XrcT9Kf", - "1GkLN7DbA9mAtHNzQWNPANcdWbefaz4Gzm", - "1GnFTy5F9qi5MfaRZfgdg2jkyT5xtAHvd8", - "1GqChmWqGtsaLrGbHfgdrV5Nkvahtjjuxr", - "1H8sDTTgJPBKw83EBZDLhXvetCbxZUMMZM", - "1H939dom7i4WDLCKyGbXUp3fs9CSTNRzgL", - "1HH8eiuaTMucTNyvGCUmAvmCZCtdMi8SqK", - "1Hhe61Bwxs8Hd2WxzWY9FQyZicBiZGeSNW", - "1Hn9ErTCPRP6j5UDBeuXPGuq5RtRjFJxJQ", - "1Hpj6qm9i7nMF3VkKfBFtjhEDpEjxHWvgv", - "1HuYfoEwsfHgZiRhbhJrCd5ST3iksa8KEx", - "1J6cgUVEZRKyJhpXJgHWX7YmzkdnHRaLhF", - "1J9oGoAiHeRfeMZeUnJ9W7RpV55CdKtgYE", - "1J9wJH2bamZVxscXAvoDH4jvtGKb7sYFDm", - "1JREJdZupiFhE7ZzQPtASuMCvvpXC7wRsC", - "1Js6goCey2NaqPQptiLANLQGuk4d6mowjP", - "1Juv2Ks3jJFUes8jEGxwgt6T6csBRQmmRw", - "1K2fmE9hfhbRNSZoBvCBWZAvsS5idTUxBG", - "1KSAbh5trMCTZwhiNsuUQvfTtSSTT8zqRk", - "1KUUJPkyDhamZXgpsyXqNGc3x1QPXtdhgz", - "1KctQENEX5QkQMpnMC3Zh9yRAzkMBLpPcr", - "1KgudqxMfYaGzqAA7MS4DcsqejtMteqhix", - "1KkaKujnqwJf7Cbm7JKAZGF3X9d4685m8n", - "1Kuf2Rd8mDyAViwBozGTNYnvWL8uYFrkVo", - "1Kys8fqDen8NGFUJ6AFcXfFW5qquuTH4eh", - "1L4ncif9hh9TnUveqWq77HfWWt6CJWtrnb", - "1LAh7PQwpd1uGiLHae5C5Xz9QXse3y2phq", - "1LBQd4ZxtQYYsDWrCzK4uMxHBJVxmyzs3M", - "1LKE3XA9bf5JFqtGtCHzWj5QGxKGwMfXZw", - "1LQV6yUBcfTjAWvFu3XPhdTgjqihss7i1z", - "1LiNmTUPSJEd92ZgVJjAV3RT9BzUjvUCkx", - "1Licqjca74n8pmNaoARXLLqcTUTHFpxbXH", - "1LpYKb3SXZPve9hsH2QEJZFX279wJVGowi", - "1LrxsRd7zNuxPJcL5rttnoeJFy1y4AffYY", - "1LuDiMd95Df4i2bcvrfw47t2GKpLLXAQMZ", - "1MQBDeRWsiJBf7K1VGjJ7PWEL6GJXMfmLg", - "1MbtT2ZsTtLp7EKZUV9r74cTyqvsMtTP2M", - "1MiQRekg4BatJ12qbiSGnNakLLd8xbLMCG", - "1MnbhWe5wr7Ut45ReyQsm96PwnM9jD7KaH", - "1MtsQsw6n2jvJCWhpCw7jifTfD9Q3rBBVg", - "1N6XqSf3ULpNjko9LrJmHudRoLitjwkETN", - "1NE2NiGhhbkFPSEyNWwj7hKGhGDedBtSrQ", - "1NayLEVF3bEEbDtdF2Cwso1VdEtvVNh2qX", - "1NbGwQwt4uEhg2srAKppLf8QaF6fbp3PZG", - "1NpHuti9NSM9fVTXLkvSDU4AnhqGQ5N53d", - "1NvJm3jfZxENNyqws5BKQvhkLxg9chLJdo", - "1P3ZfGFLezzYGg9k5SVzQmnjyh7nrUmF2y", - "1PJp8diNa89cVHpiT1VPu7EQ8LxYM5HX6v", - "1PWRKxkR5AU7Tc9zPqjdhtu1eGW1QZzs4y", - "1PXxwPVtYxZiCRp9LKq7aKMDFrhAQztvUE", - "1PYtgFS2t6i57WdDvbRa7kPcsagGMBxzfg", - "1PfwHNxUnkpfkK9MKjMqzR3Xq3KCtq9u17", - "1PhqQpaGCrqSxQ6QDXcv14QCd1U98Zp34E", - "1Pu1nAW7kCoSMThMs8QcpM8JxuByQDZgH", - "1Q4tJjH2aBr3AJrzxqa4Z3jPpf5SDgF4jK", - "1Q6saNmqKkyFB9mFR68Ck8F7Dp7dTopF2W", - "1Q9UAQbcDezmyouFrzt94t4dSMxgsUfW1X", - "1QHxyuLGRMHfbNPJikV4Dwhfx45HWfUMWB", - "1QJUiNsNfji6mR1FjAwf6Eg9NxxHPoxpWL", - "1Sf6e4xQv8muMZqYPTdRFf3e5o5eWcg9F", - "1r6S9vpUZPS5rb6gSdwV2bvSFcN3uSq4q", - "31nadacWrgPeAQxKRMabhn3fPhnhi3hjKa", - "31p6woV4e55HUfC2aGynFhzQnGoJFW26cD", - "31t4nEpcwyQJT1VuXdAoQZTT5givRDPsNP", - "32DaxSzUhLBHY2WGSWQYiBSHnRsfQZrrRp", - "32PsiT8itBrEF84ebdaF82yBUEcz5Wc6uY", - "32VgTk8kGvBsqkHhkvtNooGdtqZm46jTVo", - "32fbAZMTaQxNd2fAue1PgsiPgWfcsHBQQt", - "32jgFkZsTEjMFaBvxJnYvJEeTNKTmq5b32", - "32pCmCWEjwhkLwh5BgLNAeBQFp5Gi1hv81", - "32wdqwX3zCEX3DhAVEcKwXCEGdzgBnx1R9", - "331TS6DyASY7iU5CRA8UryBnkPS78fP2B1", - "33KKjn4exdBJQkTtdWxqpdVsWxrw3LareG", - "33Kja69SQVc8kozpoP7Qw6HFtGxHkiWzTz", - "33fWcMdmsB2Ey4CEbVWbjGFkuevBSyP9nG", - "33xWfziVZesgo83U5izdNCBVTnrtBpSwK7", - "343w3Xh64q5UpgpvAPqmsUzxrknde8PQHb", - "347QFbejDBdMZFTxpmn6evvvqyXiqZTCd7", - "34ETiHfQWEYFCCaXmEeQWVmhFH5vz2JMvd", - "34WWXwFKAsXL9zYxbeNPaPV6vDamkjQLUo", - "34dxZvijpBM1YkPybczbQ7DuGuKAnULdfS", - "34kEYgpijvCmjvahRXXQEnBH76UGJVx2wg", - "34kWCKF2wCbe6uinit2uL4ND6d8yxsuxKM", - "34pFGsSYbWEritXncW9unZtQQE9dKSvKku", - "35KAdTa2vqnJzitF2xiUzZn1Gmcas2Y465", - "35LScRJ8hzDvvWh9t9UA8bHGnGNVz3YEfa", - "35QpLWYkvD3ALhjbge5bK2kd7HfHYcDMu3", - "35SwVFxosV3AsvnrBfzdXarqavRbvDyyxv", - "35aTjkBh4yeTypJsi9nuTdoMKHTsawKVgX", - "35eanEz5iYg2eYaxCtMrR4SCoypFqrBWUH", - "35hh9dg3wSvUJz9vFk1FsezLE5Fx3Hudk2", - "35qwVtMEohWDdBWRiCSR7azoP5cbY8SG1Q", - "35vypiSvQsxRiT3YZzGRGVaduUSx67ysZb", - "361AkMKNNWYwZRsCE8pPNmoh5aQf4V7g4p", - "361NP7YcBPQ4KkLT3Y2QZeDEV4M3yi65Ar", - "36XqYWGvUQwBrYLRVuegN4pJJJSPWL1WEu", - "36YGN5dGzqrxMomTHdkT6cYVMnWBw8S7hD", - "36yS87PLuW7sErLg1TY26WzaVarTim7AcC", - "372Wk9NLrMkJzKgqJdatWJy4bYRfxFjgat", - "37dDBCexFPraKW4jGSqkE3NyG52YeZQbJx", - "37g6WgqedzZx6nx51tYgssNG8Hnknyj5nL", - "386wa1UM6nA798AWNh64jdrejZyedeXgUN", - "389Sft4nJFkPGhbagk9FN4jXncA9piYTuU", - "38LjCapRrJEW7w2zwbyS15P9D9UGPjWS44", - "38ncxqt932N9CcfNfYuHGZgCyR85hDkWBW", - "39AALn7eTjdPzLb99hHhD6F7J8QWB3R2Rd", - "39KQvziHwUe2vddbpfC5WkQEV72qbQhxuh", - "39NG2LcGRHXxSr1irpEVnJMw4ydL231sEn", - "39Te8MbphSgs7npDJPj2hbNzhke61NTcnB", - "39eboeqYNFe2VoLC3mUGx4dh6GNhLB3D2q", - "39fhoB2DohisGBbHvvfmkdPdShT75CNHdX", - "3A1HH3PseYMkh2nSrBb4kkVt3815kUNVVC", - "3AFcE2mbSSndcpYFgHoExSmjUc26ef2gQh", - "3ANWhUnHujdwbw2jEuGSRH6bvFsD9BqEy9", - "3AQSmMk5n3c6TKEg9B2WyzYAPm33gJJAA4", - "3AYU365Tcjef7j9pdKF9Xe8rWpEpsH196t", - "3AjiWiUdKB5mcGUSS9mBeoHCeYJw3Zo8r6", - "3AjyprBY5yhijiCjUC5NUJutGbwhd3AQdE", - "3B3vmabBbeDRnVrjvvq3hm85zVB4v5bWFC", - "3B4G1M8eF3cThbeMwhEWkKzczw9QoNTGak", - "3BCN3WgMRJwULTz1vsEQ7NZrBjwaUBf5Ca", - "3BQACtiMXYB9JpUMpkEWt9m8BzswpGHq4X", - "3BWP6ZQAhc4j5wR1b95zJAthJEFvhdees7", - "3BazbaTP8ELJUEfPBV9z5HXEdgBziV9p7W", - "3BsyZ7qRFSi3NsaoV1Ff724qAgrEpjVUHm", - "3CCmt5LjQ5yKkaFY1DWC2SbERVEtWRnSRD", - "3Czhm6xqn8odwz6jgTcjRrUjog28v6aVS8", - "3DCCgmyKozcZkFBzYb1A2x8abZCpAUTPPk", - "3DLGfN7hgsWXXSp9euXcnmWXLpFQuswW2t", - "3DNsaQnaUz7wkQny1ZDSmtz6QfbEShxoDD", - "3E6rY4dSCDW6y2bzJNwrjvTtdmMQjB6yeh", - "3E7YbpXuhh3CWFks1jmvWoV8y5DvsfzE6n", - "3EL5vcYeu1cnivLtR7tnAX3bBirr9ATNAL", - "3EPqGUw2q89pwPZ1UF8FJspE2AyojSTjdu", - "3ES6pqCueDPCnC4hCqhhYuey6gyiRJZw6E", - "3EUjqe9UpmyXCFd6jeu69hoTzndMRfxw9M", - "3EeR8FbcPbkcGj77D6ttneJxmsr3Nu7KGV", - "3F2sZ4jbhvDKQdGbHYPC6ZxFXEau2m5Lqj", - "3F6bbvS1krsc1qR8FsbTDfYQyvkMm3QvmR", - "3FBgeJdhiBe22UoSpp51Vd8dPHVa2A4wZX", - "3GAUBtrTtWp1D9yeXgr3wMg8B599QHa5m5", - "3GMfGEDYMTq9G8dEHet1zLtUFJwYwSNa3Y", - "3GSXNXzyCDoQ1Rhsc7F1jjjFe7DGcHHdcM", - "3GXdtA6kbb4M5aqzZm5qqxcFDFRMW8LqdJ", - "3GYbbYkvqvjF5oYhaKCgQYCvcVE1JENk6J", - "3Gbs4rjcVUtQd8p3CiFUCxPLZwRqurezRZ", - "3GuQjr7kkrR5EjpanMgyAuxuLgrjEUwe21", - "3H3rh85qPaGLy2w6618yZNaH7i8asHv46B", - "3H4qaWi5DS6FMwyZrG9xRRud3Qc5dUVn2U", - "3HJN4jRa4mdfkey9JR9jUhr86yPwL86A3C", - "3HQDRyzwm82MFmLWtmyikDM9JQEtVT6vAp", - "3HQRveQzPifZorZLDXHernc5zjoZax8U9f", - "3HRExd8GKFskZC5inmVcpiyy9UWG7FVa6o", - "3HSZc4BLnQBznjSq7JvXgqNCZUUs3M9fZz", - "3HWjh69cVQvcPeLWVCyVmXEq72nyDSj5zP", - "3He6EyDaCUgmdr4GXqhxbeTQukaGLCByU2", - "3HqA7i3ttECLvgqvq69HNxxUP5BL7Z5YgA", - "3HupEUfKmMhvhXqf8TMoPAyqDcRC1kpe65", - "3J19qffPT6mxQUcV6k5yVURGZtdhpdGr4y", - "3JHMz3mTna1gVCZSPp8NgRFiY7phkv5mA8", - "3JLyyLbwciWAC6re87D7mRknXakR4YbnUd", - "3JUwAS7seL3fh5hxWh9fu3HCiEzjuQLTfg", - "3JXKQ81JzBqVbB8VHdV9Jtd7auWokkdPgY", - "3JhPsVV3KnL9dBYGSZALS9EbrLr97R865a", - "3Jpf9B5P8cvEKSSGp9cES3Upbms8VRnXUb", - "3JuSgFrwnrNfuhvR4GpWAPmeJVot4xrEae", - "3K26aMKmnrv97Pj6YiFcqiXk2LxeHfhnG3", - "3K35dyL85fR9ht7UgzPfd1gLRRXQtNTqE3", - "3K4rjdh8A5yi6LWvft2rbmyZvqEbPSSSX4", - "3K7PMJyMNVnxqsfpmK9r9nJDtzDw9wNwNV", - "3KGQ3hX6eFYtBjTBFSdvdkzHmwZyYWLRQh", - "3KHfXU24Bt3YD5Ef4J7uNp2buCuhrxfGen", - "3Kp8Qc5z7yevDeoQxhS5RSSKnEi5x7AQ43", - "3KvBX3jo69Qn8jHy44M33RYoeYcf8DdRBD", - "3LDbNuDkKmLae5r3a5icPA5CQg2Y8F7ogW", - "3LLUnf3ezw6mCbQ2zCZmGu5rZULzkhxQi7", - "3LbDu1rUXHNyiz4i8eb3KwkSSBMf7C583D", - "3LhnVMcBq4gsR7aDaRr9XmUo17CuYBV4FN", - "3Lpoy53K625zVeE47ZasiG5jGkAxJ27kh1", - "3M7CGBPUJwXXSroWuZ6H5jiprdKCyf7V5M", - "3MD3riFB6U8PykypF6qkvSj8R2SGdUDPn3", - "3MN8nYo1tt5hLxMwMbxDkXWd7Xu522hb9P", - "3MP7yBGSW2gkXVRE8S84T2j4KVgPh3rEzv", - "3MTRvM5QrYZHKo8gh5qKcrPK3RLjxcDCZE", - "3MTrJTFhYK9v1C6pjHtuweZSopfZa4b1wb", - "3MkUNScqf21EcfWq6T4x2MFgBeSTqhB5t6", - "3MvQ4gThF4mmuo49p4dBNchcmFHBRZnYfx", - "3MzLtBQ4Lz9J6w4Qu55TktgxFKZwxYWrP6", - "3N3YSDvp4cbhEgNGabQxTN39kEzJmwG8Ah", - "3N6WeZ6i34taX8Ditser6LKWBcXmt2XXL4", - "3N9YcPBDky9UsMx1RTk33tL4jDkZfSnsPk", - "3NDzzVxiLBUs1WPvVGRfCYDTAD2Ua2PvW4", - "3NPognMSbzyA2JYW2fpkVKWyBMi2XTq2Zt", - "3NQ1aa9ceirMJ1JvRq3eXefvXj1L639fzX", - "3NRJ8aXdUiZdHaiFX9ePX3DhGHzcEi14Fq", - "3P6PzdfETr4275Gn3veLkCyDxA1jV8fHKm", - "3PDmRwotTkRAFRLGTUrucCERp2JdM1q4ar", - "3PUmTuVAW3LkKg53FZ7F97VDBitW4ugwnM", - "3PiCnZrBvGfWAKQ9hr4cCpfaDjy64yNSpE", - "3PyzSbFj3hbQQjTzDzyLSgvFVDjB7yw4Cj", - "3Q5dGfLKkWqWSwYtbMUyc8xGjN5LrRviK4", - "3QAdoc1rDCt8dii1GVPJXvvK6CEJLzCRZw", - "3QEjBiPzw6WZUL4MYMmMU6DY1Y25aVbpQu", - "3QJyT8nThEQakbfqgX86YjCK1Sp9hfNCUW", - "3QVyoH4u3qT88uChAeJVhfB3r6maZt431y", - "3QWUdP5taP4GrRuueVDud1eWetb7hc3wDH", - "3QnWE5GVfQu3wVav91RuFkqip4Ti4NWqAY", - "3QrukkUiBrn23rFUKUgasNd1wYWNk7WdSV", - "3Qw9Fn19gCnga9LfHfpM99aGzuqxBNjR2i", - "bc1q202ajnhxgg9d9jjczmg0g4usp6haqldyy2eakl", - "bc1q237mvl0heyw0r38wd3xz8h5mar96rrwpams8pp", - "bc1q2lpgjntr348pfvxhfy33ehmdzy3gmx8w4052z6", - "bc1q3y5v2khlyvemcz042wl98dzflywr8ghglqws6s", - "bc1q4rzdtlt0uslyw86cp29sctl6ct29g9a95cuup7pn5md9ddj7xgmqpp5m73", - "bc1q86tl9255vg5wldamfymaaz36uqxzm30gs7fhkljvzdlt9t38s3lqgdwdfq", - "bc1q90zrdysy4flyacw7hsury3ajs9yzwtwp6guqpypx94w0d3p58hysvz6pde", - "bc1q9lvynkfpaw330uhqmunzdz6gmafsvapv7y3zty", - "bc1qdt3gml5z5n50y5hm04u2yjdphefkm0fl2zdj68", - "bc1qe95l438kzjcvnsm3kn8n5augf9gpctdlhsq7f7hpnkyvlr7rc7cqupapf7", - "bc1qfg4gfg0y6t6xjnpmlhuwx5k0wlw6nmfzxn2psc", - "bc1qj6j6p0jdefl6pvdzx3kx8245yy5mz6q4luhzes", - "bc1ql7dlyh8xz6tpqk92vztrhqh88dmjvcwrmsemrm", - "bc1qpaly5nm7pfka9v92d6qvl4fc2l9xzee8a6ys3s", - "bc1qqf8kcc9m57xjqcvsvuf989nnl48ve6d2s24cx3", - "bc1qs9u6j78e3utj08mwvqkkmqm9de5xk3g4yh8qtq", - "bc1qsmqpalp3gtgkltag4x3ygevmhh9y2hzk73t2ug", - "bc1qsmv6lkrw65l30yazdqpdjjtwzpvk9f8gfh0cy7", - "bc1qsxf77cvwcd6jv6j8d8j3uhh4g0xqw4meswmwuc", - "bc1quyc6j8ca84q9gjej5jjd2n8hra0vfu0j60fefs57p6e5rerkq07q0l5u3w", - "bc1qv7k70u2zynvem59u88ctdlaw7hc735d8xep9rq", - "bc1qvlzfn6kmezv44d8kw0p5jsmxe6wchv3zc7gsxs", - "bc1qw4cxpe6sxa5dg6sdwxjph959cw6yztrzl4r54s", - "bc1qw7vfgv3r5vnehafl0y95sclg3uqsj87wxs9ad628yjjcq33cwessr6ndyw", - "bc1qwa6zu6qhl6wqnlxp642vcf89nptsassle25ulf", - "bc1qx3e2axj3wsfn0ndtvlwmkghmmgm4583nqg8ngk", -]; diff --git a/rs/bitcoin/ckbtc/minter/src/dashboard.rs b/rs/bitcoin/ckbtc/minter/src/dashboard.rs index d9fc308ab9f..4b50dc9285b 100644 --- a/rs/bitcoin/ckbtc/minter/src/dashboard.rs +++ b/rs/bitcoin/ckbtc/minter/src/dashboard.rs @@ -320,7 +320,7 @@ pub fn build_metadata(s: &CkBtcMinterState) -> String { DisplayAmount(s.kyt_fee), DisplayAmount(s.retrieve_btc_min_amount), DisplayAmount(s.fee_based_retrieve_btc_min_amount), - DisplayAmount(get_total_btc_managed(s)) + DisplayAmount(s.get_total_btc_managed()) ) } @@ -514,17 +514,6 @@ pub fn build_update_balance_principals(s: &CkBtcMinterState) -> String { }) } -fn get_total_btc_managed(s: &CkBtcMinterState) -> u64 { - let mut total_btc = 0_u64; - for req in s.submitted_transactions.iter() { - if let Some(change_output) = &req.change_output { - total_btc += change_output.value; - } - } - total_btc += s.available_utxos.iter().map(|u| u.value).sum::(); - total_btc -} - pub fn build_retrieve_btc_principals(s: &CkBtcMinterState) -> String { with_utf8_buffer(|buf| { for p in &s.retrieve_btc_principals { diff --git a/rs/bitcoin/ckbtc/minter/src/lib.rs b/rs/bitcoin/ckbtc/minter/src/lib.rs index b1bebe11801..3b7453f6816 100644 --- a/rs/bitcoin/ckbtc/minter/src/lib.rs +++ b/rs/bitcoin/ckbtc/minter/src/lib.rs @@ -18,7 +18,6 @@ use std::collections::{BTreeMap, BTreeSet}; use std::time::Duration; pub mod address; -pub mod blocklist; pub mod dashboard; pub mod guard; pub mod lifecycle; diff --git a/rs/bitcoin/ckbtc/minter/src/lifecycle/upgrade.rs b/rs/bitcoin/ckbtc/minter/src/lifecycle/upgrade.rs index 6c7b93ce0b3..168deb884ea 100644 --- a/rs/bitcoin/ckbtc/minter/src/lifecycle/upgrade.rs +++ b/rs/bitcoin/ckbtc/minter/src/lifecycle/upgrade.rs @@ -1,5 +1,6 @@ use crate::logs::P0; use crate::state::eventlog::{replay, Event}; +use crate::state::invariants::CheckInvariantsImpl; use crate::state::{replace_state, Mode}; use crate::storage::{count_events, events, record_event}; use candid::{CandidType, Deserialize}; @@ -51,7 +52,7 @@ pub fn post_upgrade(upgrade_args: Option) { log!(P0, "[upgrade]: replaying {} events", count_events()); - let state = replay(events()).unwrap_or_else(|e| { + let state = replay::(events()).unwrap_or_else(|e| { ic_cdk::trap(&format!( "[upgrade]: failed to replay the event log: {:?}", e diff --git a/rs/bitcoin/ckbtc/minter/src/main.rs b/rs/bitcoin/ckbtc/minter/src/main.rs index 7999524f08b..76ee3611aae 100644 --- a/rs/bitcoin/ckbtc/minter/src/main.rs +++ b/rs/bitcoin/ckbtc/minter/src/main.rs @@ -59,13 +59,13 @@ fn ok_or_die(result: Result<(), String>) { /// Checks that ckBTC minter state internally consistent. #[cfg(feature = "self_check")] fn check_invariants() -> Result<(), String> { - use ic_ckbtc_minter::state::eventlog::replay; + use ic_ckbtc_minter::state::{eventlog::replay, invariants::CheckInvariantsImpl}; read_state(|s| { s.check_invariants()?; let events: Vec<_> = storage::events().collect(); - let recovered_state = replay(events.clone().into_iter()) + let recovered_state = replay::(events.clone().into_iter()) .unwrap_or_else(|e| panic!("failed to replay log {:?}: {:?}", events, e)); recovered_state.check_invariants()?; diff --git a/rs/bitcoin/ckbtc/minter/src/metrics.rs b/rs/bitcoin/ckbtc/minter/src/metrics.rs index 3ce88917560..05d1913409e 100644 --- a/rs/bitcoin/ckbtc/minter/src/metrics.rs +++ b/rs/bitcoin/ckbtc/minter/src/metrics.rs @@ -156,18 +156,7 @@ pub fn encode_metrics( metrics.encode_gauge( "ckbtc_minter_btc_balance", - state::read_state(|s| { - s.available_utxos.iter().map(|u| u.value).sum::() - + s.submitted_transactions - .iter() - .map(|tx| { - tx.change_output - .as_ref() - .map(|out| out.value) - .unwrap_or_default() - }) - .sum::() - }) as f64, + state::read_state(|s| s.get_total_btc_managed()) as f64, "Total BTC amount locked in available UTXOs.", )?; diff --git a/rs/bitcoin/ckbtc/minter/src/state.rs b/rs/bitcoin/ckbtc/minter/src/state.rs index f9f3942d663..c1f361a8e96 100644 --- a/rs/bitcoin/ckbtc/minter/src/state.rs +++ b/rs/bitcoin/ckbtc/minter/src/state.rs @@ -10,17 +10,19 @@ use std::{ pub mod audit; pub mod eventlog; +pub mod invariants; use crate::lifecycle::init::InitArgs; use crate::lifecycle::upgrade::UpgradeArgs; use crate::logs::P0; +use crate::state::invariants::{CheckInvariants, CheckInvariantsImpl}; use crate::{address::BitcoinAddress, ECDSAPublicKey}; use candid::{CandidType, Deserialize, Principal}; use ic_base_types::CanisterId; pub use ic_btc_interface::Network; use ic_btc_interface::{OutPoint, Txid, Utxo}; use ic_canister_log::log; -use ic_utils_ensure::{ensure, ensure_eq}; +use ic_utils_ensure::ensure_eq; use icrc_ledger_types::icrc1::account::Account; use serde::Serialize; use std::collections::btree_map::Entry; @@ -514,91 +516,11 @@ impl CkBtcMinterState { } pub fn check_invariants(&self) -> Result<(), String> { - for utxo in self.available_utxos.iter() { - ensure!( - self.outpoint_account.contains_key(&utxo.outpoint), - "the output_account map is missing an entry for {:?}", - utxo.outpoint - ); - - ensure!( - self.utxos_state_addresses - .iter() - .any(|(_, utxos)| utxos.contains(utxo)), - "available utxo {:?} does not belong to any account", - utxo - ); - } - - for (addr, utxos) in self.utxos_state_addresses.iter() { - for utxo in utxos.iter() { - ensure_eq!( - self.outpoint_account.get(&utxo.outpoint), - Some(addr), - "missing outpoint account for {:?}", - utxo.outpoint - ); - } - } - - for (l, r) in self - .pending_retrieve_btc_requests - .iter() - .zip(self.pending_retrieve_btc_requests.iter().skip(1)) - { - ensure!( - l.received_at <= r.received_at, - "pending retrieve_btc requests are not sorted by receive time" - ); - } - - for tx in &self.stuck_transactions { - ensure!( - self.replacement_txid.contains_key(&tx.txid), - "stuck transaction {} does not have a replacement id", - &tx.txid, - ); - } - - for (old_txid, new_txid) in &self.replacement_txid { - ensure!( - self.stuck_transactions - .iter() - .any(|tx| &tx.txid == old_txid), - "not found stuck transaction {}", - old_txid, - ); - - ensure!( - self.submitted_transactions - .iter() - .chain(self.stuck_transactions.iter()) - .any(|tx| &tx.txid == new_txid), - "not found replacement transaction {}", - new_txid, - ); - } - - ensure_eq!( - self.replacement_txid.len(), - self.rev_replacement_txid.len(), - "direct and reverse TX replacement links don't match" - ); - for (old_txid, new_txid) in &self.replacement_txid { - ensure_eq!( - self.rev_replacement_txid.get(new_txid), - Some(old_txid), - "no back link for {} -> {} TX replacement", - old_txid, - new_txid, - ); - } - - Ok(()) + CheckInvariantsImpl::check_invariants(self) } // public for only for tests - pub(crate) fn add_utxos(&mut self, account: Account, utxos: Vec) { + pub(crate) fn add_utxos(&mut self, account: Account, utxos: Vec) { if utxos.is_empty() { return; } @@ -614,9 +536,9 @@ impl CkBtcMinterState { account_bucket.insert(utxo); } - #[cfg(debug_assertions)] - self.check_invariants() - .expect("state invariants are violated"); + if cfg!(debug_assertions) { + I::check_invariants(self).expect("state invariants are violated"); + } } pub fn retrieve_btc_status_v2_by_account( @@ -1234,6 +1156,17 @@ impl CkBtcMinterState { Ok(()) } + + pub fn get_total_btc_managed(&self) -> u64 { + let mut total_btc = 0_u64; + for req in self.submitted_transactions.iter() { + if let Some(change_output) = &req.change_output { + total_btc += change_output.value; + } + } + total_btc += self.available_utxos.iter().map(|u| u.value).sum::(); + total_btc + } } fn as_sorted_vec(values: impl Iterator, key: impl Fn(&T) -> K) -> Vec { diff --git a/rs/bitcoin/ckbtc/minter/src/state/audit.rs b/rs/bitcoin/ckbtc/minter/src/state/audit.rs index d6405f0616b..82e8efc6c28 100644 --- a/rs/bitcoin/ckbtc/minter/src/state/audit.rs +++ b/rs/bitcoin/ckbtc/minter/src/state/audit.rs @@ -4,6 +4,7 @@ use super::{ eventlog::Event, CkBtcMinterState, FinalizedBtcRetrieval, FinalizedStatus, RetrieveBtcRequest, SubmittedBtcTransaction, UtxoCheckStatus, }; +use crate::state::invariants::CheckInvariantsImpl; use crate::state::{ReimburseDepositTask, ReimbursedDeposit}; use crate::storage::record_event; use crate::ReimbursementReason; @@ -38,7 +39,7 @@ pub fn add_utxos( utxos: utxos.clone(), }); - state.add_utxos(account, utxos); + state.add_utxos::(account, utxos); } pub fn remove_retrieve_btc_request(state: &mut CkBtcMinterState, request: RetrieveBtcRequest) { diff --git a/rs/bitcoin/ckbtc/minter/src/state/eventlog.rs b/rs/bitcoin/ckbtc/minter/src/state/eventlog.rs index 78ed1741d9b..e7e2b064f18 100644 --- a/rs/bitcoin/ckbtc/minter/src/state/eventlog.rs +++ b/rs/bitcoin/ckbtc/minter/src/state/eventlog.rs @@ -1,5 +1,6 @@ use crate::lifecycle::init::InitArgs; use crate::lifecycle::upgrade::UpgradeArgs; +use crate::state::invariants::CheckInvariants; use crate::state::{ ChangeOutput, CkBtcMinterState, FinalizedBtcRetrieval, FinalizedStatus, Overdraft, RetrieveBtcRequest, SubmittedBtcTransaction, UtxoCheckStatus, @@ -188,7 +189,9 @@ pub enum ReplayLogError { } /// Reconstructs the minter state from an event log. -pub fn replay(mut events: impl Iterator) -> Result { +pub fn replay( + mut events: impl Iterator, +) -> Result { let mut state = match events.next() { Some(Event::Init(args)) => CkBtcMinterState::from(args), Some(evt) => { @@ -208,7 +211,7 @@ pub fn replay(mut events: impl Iterator) -> Result state.upgrade(args), Event::ReceivedUtxos { to_account, utxos, .. - } => state.add_utxos(to_account, utxos), + } => state.add_utxos::(to_account, utxos), Event::AcceptedRetrieveBtcRequest(req) => { if let Some(account) = req.reimbursement_account { state diff --git a/rs/bitcoin/ckbtc/minter/src/state/invariants.rs b/rs/bitcoin/ckbtc/minter/src/state/invariants.rs new file mode 100644 index 00000000000..1c0c02d72be --- /dev/null +++ b/rs/bitcoin/ckbtc/minter/src/state/invariants.rs @@ -0,0 +1,97 @@ +use crate::state::CkBtcMinterState; +use ic_utils_ensure::{ensure, ensure_eq}; + +pub trait CheckInvariants { + fn check_invariants(state: &CkBtcMinterState) -> Result<(), String>; +} + +pub enum CheckInvariantsImpl {} + +impl CheckInvariants for CheckInvariantsImpl { + fn check_invariants(state: &CkBtcMinterState) -> Result<(), String> { + for utxo in state.available_utxos.iter() { + ensure!( + state.outpoint_account.contains_key(&utxo.outpoint), + "the output_account map is missing an entry for {:?}", + utxo.outpoint + ); + + ensure!( + state + .utxos_state_addresses + .iter() + .any(|(_, utxos)| utxos.contains(utxo)), + "available utxo {:?} does not belong to any account", + utxo + ); + } + + for (addr, utxos) in state.utxos_state_addresses.iter() { + for utxo in utxos.iter() { + ensure_eq!( + state.outpoint_account.get(&utxo.outpoint), + Some(addr), + "missing outpoint account for {:?}", + utxo.outpoint + ); + } + } + + for (l, r) in state + .pending_retrieve_btc_requests + .iter() + .zip(state.pending_retrieve_btc_requests.iter().skip(1)) + { + ensure!( + l.received_at <= r.received_at, + "pending retrieve_btc requests are not sorted by receive time" + ); + } + + for tx in &state.stuck_transactions { + ensure!( + state.replacement_txid.contains_key(&tx.txid), + "stuck transaction {} does not have a replacement id", + &tx.txid, + ); + } + + for (old_txid, new_txid) in &state.replacement_txid { + ensure!( + state + .stuck_transactions + .iter() + .any(|tx| &tx.txid == old_txid), + "not found stuck transaction {}", + old_txid, + ); + + ensure!( + state + .submitted_transactions + .iter() + .chain(state.stuck_transactions.iter()) + .any(|tx| &tx.txid == new_txid), + "not found replacement transaction {}", + new_txid, + ); + } + + ensure_eq!( + state.replacement_txid.len(), + state.rev_replacement_txid.len(), + "direct and reverse TX replacement links don't match" + ); + for (old_txid, new_txid) in &state.replacement_txid { + ensure_eq!( + state.rev_replacement_txid.get(new_txid), + Some(old_txid), + "no back link for {} -> {} TX replacement", + old_txid, + new_txid, + ); + } + + Ok(()) + } +} diff --git a/rs/bitcoin/ckbtc/minter/src/tests.rs b/rs/bitcoin/ckbtc/minter/src/tests.rs index b522f26e16c..cfb8284eec5 100644 --- a/rs/bitcoin/ckbtc/minter/src/tests.rs +++ b/rs/bitcoin/ckbtc/minter/src/tests.rs @@ -1,3 +1,4 @@ +use crate::state::invariants::CheckInvariantsImpl; use crate::MINTER_FEE_CONSTANT; use crate::{ address::BitcoinAddress, build_unsigned_transaction, estimate_retrieve_btc_fee, fake_sign, @@ -373,17 +374,6 @@ fn test_no_dust_outputs() { assert_eq!(available_utxos.len(), 1); } -#[test] -fn blocklist_is_sorted() { - use crate::blocklist::BTC_ADDRESS_BLOCKLIST; - for (l, r) in BTC_ADDRESS_BLOCKLIST - .iter() - .zip(BTC_ADDRESS_BLOCKLIST.iter().skip(1)) - { - assert!(l < r, "the block list is not sorted: {} >= {}", l, r); - } -} - fn arb_amount() -> impl Strategy { 1..10_000_000_000u64 } @@ -842,7 +832,7 @@ proptest! { new_kyt_principal: None }); for (utxo, acc_idx) in utxos_acc_idx { - state.add_utxos(accounts[acc_idx], vec![utxo]); + state.add_utxos::(accounts[acc_idx], vec![utxo]); state.check_invariants().expect("invariant check failed"); } } @@ -870,7 +860,7 @@ proptest! { let mut available_amount = 0; for (utxo, acc_idx) in utxos_acc_idx { available_amount += utxo.value; - state.add_utxos(accounts[acc_idx], vec![utxo]); + state.add_utxos::(accounts[acc_idx], vec![utxo]); } for req in requests { let block_index = req.block_index; @@ -912,7 +902,7 @@ proptest! { }); for (utxo, acc_idx) in utxos_acc_idx { - state.add_utxos(accounts[acc_idx], vec![utxo]); + state.add_utxos::(accounts[acc_idx], vec![utxo]); } let fee_per_vbyte = 100_000u64; @@ -1206,8 +1196,8 @@ fn test_build_account_to_utxos_table_pagination() { let mut utxos = (1..=30).map(dummy_utxo_from_value).collect::>(); utxos.sort_unstable(); - state.add_utxos(account1, utxos[..10].to_vec()); - state.add_utxos(account2, utxos[10..].to_vec()); + state.add_utxos::(account1, utxos[..10].to_vec()); + state.add_utxos::(account2, utxos[10..].to_vec()); // Check if all pages combined together would give the full utxos set. let pages = [ diff --git a/rs/bitcoin/ckbtc/minter/src/updates/retrieve_btc.rs b/rs/bitcoin/ckbtc/minter/src/updates/retrieve_btc.rs index 358bf06d087..21826947527 100644 --- a/rs/bitcoin/ckbtc/minter/src/updates/retrieve_btc.rs +++ b/rs/bitcoin/ckbtc/minter/src/updates/retrieve_btc.rs @@ -3,7 +3,6 @@ use crate::logs::P0; use crate::logs::P1; use crate::management::check_withdrawal_destination_address; use crate::memo::{BurnMemo, Status}; -use crate::state::ReimbursementReason; use crate::tasks::{schedule_now, TaskType}; use crate::{ address::{account_to_bitcoin_address, BitcoinAddress, ParseAddressError}, @@ -152,13 +151,6 @@ pub async fn retrieve_btc(args: RetrieveBtcArgs) -> Result Result { log!( @@ -216,6 +207,7 @@ pub async fn retrieve_btc(args: RetrieveBtcArgs) -> Result {} } + let burn_memo = BurnMemo::Convert { address: Some(&args.address), kyt_fee: None, @@ -223,6 +215,7 @@ pub async fn retrieve_btc(args: RetrieveBtcArgs) -> Result { + return Err(RetrieveBtcWithApprovalError::GenericError { + error_message: format!("Failed to call KYT canister with error: {:?}", error), + error_code: ErrorCode::KytCallFailed as u64, + }) + } + Ok(status) => match status { + BtcAddressCheckStatus::Tainted => { + return Err(RetrieveBtcWithApprovalError::GenericError { + error_message: "Destination address is tainted".to_string(), + error_code: ErrorCode::TaintedAddress as u64, + }); + } + BtcAddressCheckStatus::Clean => {} + }, + } + let burn_memo_icrc2 = BurnMemo::Convert { address: Some(&args.address), kyt_fee: None, @@ -314,90 +325,28 @@ pub async fn retrieve_btc_with_approval( ) .await?; - let new_kyt_principal = read_state(|s| { - s.new_kyt_principal - .expect("BUG: upgrade procedure must ensure that the new KYT principal is set") - .get() - .into() - }); - - match new_kyt_check_address(new_kyt_principal, args.address.clone()).await { - Ok(status) => { - match status { - BtcAddressCheckStatus::Tainted => { - mutate_state(|s| { - state::audit::schedule_deposit_reimbursement( - s, - Account { - owner: caller, - subaccount: args.from_subaccount, - }, - args.amount, - ReimbursementReason::TaintedDestination { - kyt_provider: new_kyt_principal, - kyt_fee: 0, - }, - block_index, - ); - }); - schedule_now(TaskType::ProcessLogic); - return Err(RetrieveBtcWithApprovalError::GenericError { - error_message: "Destination address is tainted".to_string(), - error_code: ErrorCode::TaintedAddress as u64, - }); - } - BtcAddressCheckStatus::Clean => {} - } - - let request = RetrieveBtcRequest { - amount: args.amount, - address: parsed_address, - block_index, - received_at: ic_cdk::api::time(), - kyt_provider: None, - reimbursement_account: Some(Account { - owner: caller, - subaccount: args.from_subaccount, - }), - }; - - mutate_state(|s| state::audit::accept_retrieve_btc_request(s, request)); - - assert_eq!( - crate::state::RetrieveBtcStatus::Pending, - read_state(|s| s.retrieve_btc_status(block_index)) - ); + let request = RetrieveBtcRequest { + amount: args.amount, + address: parsed_address, + block_index, + received_at: ic_cdk::api::time(), + kyt_provider: None, + reimbursement_account: Some(Account { + owner: caller, + subaccount: args.from_subaccount, + }), + }; - schedule_now(TaskType::ProcessLogic); + mutate_state(|s| state::audit::accept_retrieve_btc_request(s, request)); - Ok(RetrieveBtcOk { block_index }) - } - Err(error) => { - mutate_state(|s| { - state::audit::schedule_deposit_reimbursement( - s, - Account { - owner: caller, - subaccount: args.from_subaccount, - }, - args.amount, - ReimbursementReason::CallFailed, - block_index, - ); - }); + assert_eq!( + crate::state::RetrieveBtcStatus::Pending, + read_state(|s| s.retrieve_btc_status(block_index)) + ); - schedule_now(TaskType::ProcessLogic); + schedule_now(TaskType::ProcessLogic); - Err(RetrieveBtcWithApprovalError::GenericError { - error_message: format!( - "Failed to call KYT canister with error: {:?}, will reimburse {} ckBTC", - error, - crate::tx::DisplayAmount(args.amount), - ), - error_code: ErrorCode::KytCallFailed as u64, - }) - } - } + Ok(RetrieveBtcOk { block_index }) } async fn balance_of(user: Principal) -> Result { diff --git a/rs/bitcoin/ckbtc/minter/test_resources/mainnet_events.gz b/rs/bitcoin/ckbtc/minter/test_resources/mainnet_events.gz new file mode 100644 index 00000000000..074834bdb27 Binary files /dev/null and b/rs/bitcoin/ckbtc/minter/test_resources/mainnet_events.gz differ diff --git a/rs/bitcoin/ckbtc/minter/test_resources/testnet_events.gz b/rs/bitcoin/ckbtc/minter/test_resources/testnet_events.gz new file mode 100644 index 00000000000..5cfcb0cfca6 Binary files /dev/null and b/rs/bitcoin/ckbtc/minter/test_resources/testnet_events.gz differ diff --git a/rs/bitcoin/ckbtc/minter/tests/replay_events.rs b/rs/bitcoin/ckbtc/minter/tests/replay_events.rs new file mode 100644 index 00000000000..d6b65ddb6ef --- /dev/null +++ b/rs/bitcoin/ckbtc/minter/tests/replay_events.rs @@ -0,0 +1,239 @@ +use candid::{CandidType, Deserialize, Principal}; +use ic_agent::Agent; +use ic_ckbtc_minter::state::eventlog::{replay, Event}; +use ic_ckbtc_minter::state::invariants::{CheckInvariants, CheckInvariantsImpl}; +use ic_ckbtc_minter::state::{CkBtcMinterState, Network}; +use std::path::PathBuf; + +#[tokio::test] +async fn should_replay_events_for_mainnet() { + GetEventsFile::Mainnet + .retrieve_and_store_events_if_env() + .await; + + let state = + replay::(GetEventsFile::Mainnet.deserialize().events.into_iter()) + .expect("Failed to replay events"); + state + .check_invariants() + .expect("Failed to check invariants"); + + assert_eq!(state.btc_network, Network::Mainnet); + assert_eq!(state.get_total_btc_managed(), 21_723_786_340); +} + +#[tokio::test] +async fn should_replay_events_for_testnet() { + GetEventsFile::Testnet + .retrieve_and_store_events_if_env() + .await; + + let state = + replay::(GetEventsFile::Testnet.deserialize().events.into_iter()) + .expect("Failed to replay events"); + state + .check_invariants() + .expect("Failed to check invariants"); + + assert_eq!(state.btc_network, Network::Testnet); + assert_eq!(state.get_total_btc_managed(), 16_578_205_978); +} + +// This test is ignored because it takes too long to run, +// roughly 50 minutes. It's useful to run it locally when +// updating mainnet_events.gz or testnet_events.gz. +// bazel test //rs/bitcoin/ckbtc/minter:ckbtc_minter_replay_events_tests --test_arg="should_replay_events_and_check_invariants" --test_arg=--ignored +#[test] +#[ignore] +fn should_replay_events_and_check_invariants() { + for file in [GetEventsFile::Mainnet, GetEventsFile::Testnet] { + let events = file.deserialize(); + println!("Replaying {} {:?} events", events.total_event_count, file); + let _state = replay::(events.events.into_iter()) + .expect("Failed to replay events"); + } +} + +// It's not clear why those events are here in the first place +// but this test ensures that the number of such events doesn't grow. +#[tokio::test] +async fn should_not_grow_number_of_useless_events() { + for file in [GetEventsFile::Mainnet, GetEventsFile::Testnet] { + let events = file.deserialize(); + let received_utxo_to_minter_with_empty_utxos = Event::ReceivedUtxos { + mint_txid: None, + to_account: file.minter_canister_id().into(), + utxos: vec![], + }; + + let useless_events_indexes = + assert_useless_events_eq(&events.events, &received_utxo_to_minter_with_empty_utxos); + + match file { + GetEventsFile::Mainnet => { + assert_eq!(events.total_event_count, 432_050); + assert_eq!(useless_events_indexes.len(), 409_141); + assert_eq!(useless_events_indexes.last(), Some(&411_301_usize)); + } + GetEventsFile::Testnet => { + assert_eq!(events.total_event_count, 46_815); + assert_eq!(useless_events_indexes.len(), 4_044); + assert_eq!(useless_events_indexes.last(), Some(&4_614_usize)); + } + } + } + + fn assert_useless_events_eq(events: &[Event], expected_useless_event: &Event) -> Vec { + let mut indexes = Vec::new(); + for (index, event) in events.iter().enumerate() { + match &event { + Event::ReceivedUtxos { utxos, .. } if utxos.is_empty() => { + assert_eq!(event, expected_useless_event); + indexes.push(index); + } + _ => {} + } + } + indexes + } +} + +#[derive(Debug)] +enum GetEventsFile { + Mainnet, + Testnet, +} + +impl GetEventsFile { + /// To refresh the stored events on disk, call the tests as follows + /// ``` + /// bazel test --spawn_strategy=standalone //rs/bitcoin/ckbtc/minter:ckbtc_minter_replay_events_tests --test_env=RETRIEVE_MINTER_EVENTS=true --test_arg "should_replay_events_for_mainnet" --test_timeout 900 + /// ``` + /// The parameter `spawn_strategy=standalone` is needed, because the events will be fetched from the running canister and the default sandbox doesn't allow it. + /// The parameter `test_env=RETRIEVE_MINTER_EVENTS=true` is needed to enable the fetching of the events. + async fn retrieve_and_store_events_if_env(&self) { + if std::env::var("RETRIEVE_MINTER_EVENTS").map(|s| s.parse().ok().unwrap_or_default()) + == Ok(true) + { + self.retrieve_and_store_events().await; + } + } + + async fn retrieve_and_store_events(&self) { + use candid::Encode; + use flate2::bufread::GzEncoder; + use flate2::Compression; + use ic_agent::{identity::AnonymousIdentity, Agent}; + use std::fs::File; + use std::io::{BufReader, BufWriter, Read, Write}; + + let agent = Agent::builder() + .with_url("https://icp0.io") + .with_identity(AnonymousIdentity) + .build() + .expect("Failed to create agent"); + + const MAX_EVENTS_PER_QUERY: u64 = 2000; + let mut events = Vec::new(); + loop { + let fetched_events = get_events( + &agent, + &self.minter_canister_id(), + events.len() as u64, + MAX_EVENTS_PER_QUERY, + ) + .await; + if fetched_events.is_empty() { + break; + } + events.extend(fetched_events); + } + let total_event_count = events.len() as u64; + + let encoded_all_events = Encode!(&GetEventsResult { + events, + total_event_count + }) + .unwrap(); + let mut gz = GzEncoder::new( + BufReader::new(encoded_all_events.as_slice()), + Compression::best(), + ); + let mut compressed_buffer = Vec::new(); + gz.read_to_end(&mut compressed_buffer) + .expect("BUG: failed to compress events"); + let mut compressed_file = BufWriter::new(File::create(self.path_to_events_file()).unwrap()); + compressed_file + .write_all(&compressed_buffer) + .expect("BUG: failed to write events"); + } + + fn minter_canister_id(&self) -> Principal { + match self { + GetEventsFile::Mainnet => Principal::from_text("mqygn-kiaaa-aaaar-qaadq-cai").unwrap(), + GetEventsFile::Testnet => Principal::from_text("ml52i-qqaaa-aaaar-qaaba-cai").unwrap(), + } + } + + fn path_to_events_file(&self) -> PathBuf { + let mut path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); + path.push(format!("test_resources/{}", self.file_name())); + path + } + + fn file_name(&self) -> &str { + match self { + GetEventsFile::Mainnet => "mainnet_events.gz", + GetEventsFile::Testnet => "testnet_events.gz", + } + } + + fn deserialize(&self) -> GetEventsResult { + use candid::Decode; + use flate2::read::GzDecoder; + use std::fs::File; + use std::io::Read; + + let file = File::open(self.path_to_events_file()).unwrap(); + let mut gz = GzDecoder::new(file); + let mut decompressed_buffer = Vec::new(); + gz.read_to_end(&mut decompressed_buffer) + .expect("BUG: failed to decompress events"); + Decode!(&decompressed_buffer, GetEventsResult).expect("Failed to decode events") + } +} + +async fn get_events(agent: &Agent, minter_id: &Principal, start: u64, length: u64) -> Vec { + use candid::{Decode, Encode}; + use ic_ckbtc_minter::state::eventlog::GetEventsArg; + + let arg = GetEventsArg { start, length }; + + let raw_result = agent + .update(minter_id, "get_events") + .with_arg(Encode!(&arg).unwrap()) + .call_and_wait() + .await + .expect("Failed to call get_events"); + Decode!(&raw_result, Vec).unwrap() +} + +#[derive(Clone, Debug, CandidType, Deserialize)] +pub struct GetEventsResult { + pub events: Vec, + pub total_event_count: u64, +} + +/// This struct is used to skip the check invariants when replaying the events +/// because it otherwise takes too long. +/// +/// This is because invariants are checked upon `ReceivedUtxos` events and +/// each check is linear over the state size, meaning overall complexity is quadratic +/// with the number of `ReceivedUtxos` events. +pub enum SkipCheckInvariantsImpl {} + +impl CheckInvariants for SkipCheckInvariantsImpl { + fn check_invariants(_state: &CkBtcMinterState) -> Result<(), String> { + Ok(()) + } +} diff --git a/rs/bitcoin/ckbtc/minter/tests/tests.rs b/rs/bitcoin/ckbtc/minter/tests/tests.rs index 53c0cd3ff73..98734400602 100644 --- a/rs/bitcoin/ckbtc/minter/tests/tests.rs +++ b/rs/bitcoin/ckbtc/minter/tests/tests.rs @@ -14,14 +14,10 @@ use ic_ckbtc_kyt::{InitArg as KytInitArg, KytMode, LifecycleArg, SetApiKeyArg}; use ic_ckbtc_minter::lifecycle::init::{InitArgs as CkbtcMinterInitArgs, MinterArg}; use ic_ckbtc_minter::lifecycle::upgrade::UpgradeArgs; use ic_ckbtc_minter::queries::{EstimateFeeArg, RetrieveBtcStatusRequest, WithdrawalFee}; -use ic_ckbtc_minter::state::{ - BtcRetrievalStatusV2, Mode, ReimburseDepositTask, ReimbursedDeposit, - ReimbursementReason::{CallFailed, TaintedDestination}, - RetrieveBtcStatus, RetrieveBtcStatusV2, -}; +use ic_ckbtc_minter::state::{BtcRetrievalStatusV2, Mode, RetrieveBtcStatus, RetrieveBtcStatusV2}; use ic_ckbtc_minter::updates::get_btc_address::GetBtcAddressArgs; use ic_ckbtc_minter::updates::retrieve_btc::{ - RetrieveBtcArgs, RetrieveBtcError, RetrieveBtcOk, RetrieveBtcWithApprovalArgs, + ErrorCode, RetrieveBtcArgs, RetrieveBtcError, RetrieveBtcOk, RetrieveBtcWithApprovalArgs, RetrieveBtcWithApprovalError, }; use ic_ckbtc_minter::updates::update_balance::{ @@ -2138,12 +2134,10 @@ fn test_retrieve_btc_with_approval_fail() { let start_canister_result = ckbtc.env.start_canister(ckbtc.ledger_id); assert_matches!(start_canister_result, Ok(_)); - assert_eq!( - ckbtc.balance_of(user_account), - Nat::from(deposit_value - KYT_FEE - TRANSFER_FEE) - ); + let deposited_value = deposit_value - KYT_FEE - TRANSFER_FEE; + assert_eq!(ckbtc.balance_of(user_account), Nat::from(deposited_value)); - // Check that we reimburse ckBTC if the KYT check of the address fails + // Check that the correct error_code is returned if the KYT check of the address fails ckbtc .env @@ -2164,25 +2158,13 @@ fn test_retrieve_btc_with_approval_fail() { ); assert_matches!( retrieve_btc_result, - Err(RetrieveBtcWithApprovalError::GenericError { .. }) + Err(RetrieveBtcWithApprovalError::GenericError { error_code, .. }) + if error_code == ErrorCode::TaintedAddress as u64 ); ckbtc.env.tick(); - assert_eq!( - ckbtc.balance_of(user_account), - Nat::from(deposit_value - KYT_FEE - TRANSFER_FEE) - ); - - ckbtc - .env - .execute_ingress(ckbtc.minter_id, "distribute_kyt_fee", Encode!().unwrap()) - .expect("failed to transfer funds"); - - assert_eq!( - ckbtc.balance_of(Principal::from(ckbtc.kyt_provider)), - Nat::from(KYT_FEE) - ); + assert_eq!(ckbtc.balance_of(user_account), Nat::from(deposited_value)); - // Check that we reimburse ckBTC if the call to the KYT canister fails + // Check that the correct error_code is returned if the call to the KYT canister fails let stop_canister_result = ckbtc.env.stop_canister(ckbtc.new_kyt_id); assert_matches!(stop_canister_result, Ok(_)); @@ -2194,56 +2176,15 @@ fn test_retrieve_btc_with_approval_fail() { ); assert_matches!( retrieve_btc_result, - Err(RetrieveBtcWithApprovalError::GenericError { .. }) - ); - - let reimbursed_tx_block_index_2 = BtcRetrievalStatusV2 { - block_index: 2, - status_v2: Some(RetrieveBtcStatusV2::Reimbursed(ReimbursedDeposit { - account: user_account, - amount: withdrawal_amount, - reason: TaintedDestination { - kyt_provider: ckbtc.new_kyt_id.into(), - kyt_fee: 0, - }, - mint_block_index: 3, - })), - }; - - assert_eq!( - ckbtc.retrieve_btc_status_v2_by_account(Some(user_account)), - vec![ - reimbursed_tx_block_index_2.clone(), - BtcRetrievalStatusV2 { - block_index: 5, - status_v2: Some(RetrieveBtcStatusV2::WillReimburse(ReimburseDepositTask { - account: user_account, - amount: withdrawal_amount, - reason: CallFailed - })) - } - ] - ); - - ckbtc.env.tick(); - assert_eq!( - ckbtc.balance_of(user_account), - Nat::from(deposit_value - KYT_FEE - TRANSFER_FEE) + Err(RetrieveBtcWithApprovalError::GenericError { error_code, .. }) + if error_code == ErrorCode::KytCallFailed as u64 ); + // Balance should be unchanged + assert_eq!(ckbtc.balance_of(user_account), Nat::from(deposited_value)); + // No known reimbursement or pending status because the withdrawal is now rejected before burn. assert_eq!( ckbtc.retrieve_btc_status_v2_by_account(Some(user_account)), - vec![ - reimbursed_tx_block_index_2, - BtcRetrievalStatusV2 { - block_index: 5, - status_v2: Some(RetrieveBtcStatusV2::Reimbursed(ReimbursedDeposit { - account: user_account, - amount: withdrawal_amount, - reason: CallFailed, - mint_block_index: 6 - })) - } - ] + vec![] ); } diff --git a/rs/bitcoin/kyt/src/fetch.rs b/rs/bitcoin/kyt/src/fetch.rs index dcfaac4d08b..4a7b208f8c1 100644 --- a/rs/bitcoin/kyt/src/fetch.rs +++ b/rs/bitcoin/kyt/src/fetch.rs @@ -58,7 +58,6 @@ pub trait FetchEnv { max_response_bytes: u32, ) -> Result; fn cycles_accept(&self, cycles: u128) -> u128; - fn cycles_available(&self) -> u128; /// Try to fetch a transaction given its txid: /// - If it is already available, return `Fetched`. @@ -202,6 +201,8 @@ pub trait FetchEnv { let mut futures = vec![]; let mut jobs = vec![]; + let mut high_load = false; + let mut not_enough_cycles = false; for (index, input) in fetched.tx.inputs.iter().enumerate() { if fetched.input_addresses[index].is_none() { use TryFetchResult::*; @@ -225,17 +226,22 @@ pub trait FetchEnv { .into(); } } - Pending => continue, - HighLoad | NotEnoughCycles => break, + Pending => {} + HighLoad => { + high_load = true; + } + NotEnoughCycles => { + not_enough_cycles = true; + } } } } if futures.is_empty() { - // Return NotEnoughCycles if we have deducted all available cycles - if self.cycles_available() == 0 { + if not_enough_cycles { return CheckTransactionStatus::NotEnoughCycles.into(); - } else { + } + if high_load { return CheckTransactionRetriable::HighLoad.into(); } } diff --git a/rs/bitcoin/kyt/src/fetch/tests.rs b/rs/bitcoin/kyt/src/fetch/tests.rs index 849f014611f..c0eca23ad85 100644 --- a/rs/bitcoin/kyt/src/fetch/tests.rs +++ b/rs/bitcoin/kyt/src/fetch/tests.rs @@ -65,9 +65,6 @@ impl FetchEnv for MockEnv { *available -= cycles; cycles } - fn cycles_available(&self) -> u128 { - *self.available_cycles.borrow() - } } impl MockEnv { @@ -99,6 +96,9 @@ impl MockEnv { fn cycles_accepted(&self) -> u128 { *self.accepted_cycles.borrow() } + fn cycles_available(&self) -> u128 { + *self.available_cycles.borrow() + } } fn mock_txid(v: u8) -> Txid { diff --git a/rs/bitcoin/kyt/src/main.rs b/rs/bitcoin/kyt/src/main.rs index bae663fcf1f..5517f36583e 100644 --- a/rs/bitcoin/kyt/src/main.rs +++ b/rs/bitcoin/kyt/src/main.rs @@ -237,9 +237,6 @@ impl FetchEnv for KytCanisterEnv { fn cycles_accept(&self, cycles: u128) -> u128 { ic_cdk::api::call::msg_cycles_accept128(cycles) } - fn cycles_available(&self) -> u128 { - ic_cdk::api::call::msg_cycles_available128() - } } /// Check the input addresses of a transaction given its txid. diff --git a/rs/boundary_node/certificate_issuance/certificate_orchestrator/src/main.rs b/rs/boundary_node/certificate_issuance/certificate_orchestrator/src/main.rs index aeb81222fe4..6e0d2c7359f 100644 --- a/rs/boundary_node/certificate_issuance/certificate_orchestrator/src/main.rs +++ b/rs/boundary_node/certificate_issuance/certificate_orchestrator/src/main.rs @@ -526,32 +526,42 @@ fn init_fn( }); ID_SEED.with(|s| { - let mut s = s.borrow_mut(); - s.insert((), id_seed); + s.borrow_mut().insert( + (), // + id_seed, // + ) }); - let registration_expiration_ttl = registration_expiration_ttl.unwrap_or(3 * DAY); - + // REGISTRATION_EXPIRATION_TTL REGISTRATION_EXPIRATION_TTL.with(|s| { - let mut s = s.borrow_mut(); - s.insert((), registration_expiration_ttl); + s.borrow_mut().insert( + (), // + registration_expiration_ttl.unwrap_or(3 * DAY), // + ) }); - let in_progress_ttl = in_progress_ttl.unwrap_or(10 * MINUTE); - + // IN_PROGRESS_TTL IN_PROGRESS_TTL.with(|s| { - let mut s = s.borrow_mut(); - s.insert((), in_progress_ttl); + s.borrow_mut().insert( + (), // + in_progress_ttl.unwrap_or(10 * MINUTE), // + ) }); - // authorize the canister ID so that timer functions are authorized - ALLOWED_PRINCIPALS.with(|m| m.borrow_mut().insert(id().to_text().into(), ())); - - let management_task_interval = management_task_interval.unwrap_or(MINUTE); - + // MANAGEMENT_TASK_INTERVAL MANAGEMENT_TASK_INTERVAL.with(|s| { - let mut s = s.borrow_mut(); - s.insert((), management_task_interval); + s.borrow_mut().insert( + (), // + management_task_interval.unwrap_or(MINUTE), // + ) + }); + + // authorize the canister ID so that timer functions are authorized + ALLOWED_PRINCIPALS.with(|m| { + m.borrow_mut().insert( + id().to_text().into(), // principal + (), // + ) }); init_timers_fn(); @@ -612,12 +622,35 @@ fn post_upgrade_fn() { }); // authorize the canister ID so that timer functions are authorized - // this can be removed after we upgraded the canisters that didn't do it in init_fn() - ALLOWED_PRINCIPALS.with(|m| m.borrow_mut().insert(id().to_text().into(), ())); + ALLOWED_PRINCIPALS.with(|m| { + m.borrow_mut().insert( + id().to_text().into(), // principal + (), // + ) + }); + + // REGISTRATION_EXPIRATION_TTL + REGISTRATION_EXPIRATION_TTL.with(|s| { + s.borrow_mut().insert( + (), // + s.borrow().get(&()).unwrap_or(3 * DAY), // + ) + }); + + // IN_PROGRESS_TTL + IN_PROGRESS_TTL.with(|s| { + s.borrow_mut().insert( + (), // + s.borrow().get(&()).unwrap_or(10 * MINUTE), // + ) + }); + // MANAGEMENT_TASK_INTERVAL MANAGEMENT_TASK_INTERVAL.with(|s| { - let mut s = s.borrow_mut(); - s.insert((), MINUTE); + s.borrow_mut().insert( + (), // + s.borrow().get(&()).unwrap_or(MINUTE), // + ) }); init_timers_fn(); diff --git a/rs/canonical_state/certification_version/src/lib.rs b/rs/canonical_state/certification_version/src/lib.rs index 95de0f7304e..21828b2208f 100644 --- a/rs/canonical_state/certification_version/src/lib.rs +++ b/rs/canonical_state/certification_version/src/lib.rs @@ -2,43 +2,6 @@ use strum_macros::{EnumCount, EnumIter}; #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, EnumCount, EnumIter)] pub enum CertificationVersion { - /// Initial version. - V0 = 0, - /// Added canister module hash and controller. - V1 = 1, - /// Added support for multiple canister controllers. - V2 = 2, - /// Added subnet to canister ID ranges routing tables. - V3 = 3, - /// Added optional `Request::cycles_payment` and `Response::cycles_refund` - /// fields that are not yet populated. - V4 = 4, - /// Added support for canister metadata custom sections. - V5 = 5, - /// Encoding of canister metadata custom sections. - V6 = 6, - /// Support for decoding of `StreamHeader::reject_signals`. - /// Support for `done` ingress history status. - V7 = 7, - /// Encoding of `StreamHeader::reject_signals`. - /// Producing `done` ingress history statuses. - V8 = 8, - /// Producing non-empty `StreamHeader::reject_signals`. - V9 = 9, - /// Dropped `SystemMetadata::id_counter`. - V10 = 10, - /// Producing `error_code` field in `request_status` subtree. - V11 = 11, - /// Added `/subnet//node` subtree, with node public keys. - V12 = 12, - /// Dropped `/canister//controller`. - V13 = 13, - /// Define optional `Request::metadata` field. - V14 = 14, - /// Added subnet metrics in `subnet` subtree. - V15 = 15, - /// Added `/api_boundary_nodes` subtree with domain, ipv4_address and ipv6_address for each API boundary node. - V16 = 16, /// Added `flags` to `StreamHeader`. Defined `StreamHeaderFlagBits::ResponsesOnly` flag. V17 = 17, /// Added `deadline` fields to `Request` and `Response`. @@ -69,9 +32,8 @@ impl std::convert::TryFrom for CertificationVersion { type Error = UnsupportedCertificationVersion; fn try_from(n: u32) -> Result { - use strum::IntoEnumIterator; - CertificationVersion::iter() - .nth(n as usize) + all_supported_versions() + .find(|v| *v as u32 == n) .ok_or(UnsupportedCertificationVersion(n)) } } @@ -92,7 +54,8 @@ pub const MIN_SUPPORTED_CERTIFICATION_VERSION: CertificationVersion = Certificat /// this. pub const MAX_SUPPORTED_CERTIFICATION_VERSION: CertificationVersion = CertificationVersion::V19; -/// Returns a list of all certification versions up to [MAX_SUPPORTED_CERTIFICATION_VERSION]. +/// Returns a list of all certification versions from `MIN_SUPPORTED_CERTIFICATION_VERSION` +/// up to `MAX_SUPPORTED_CERTIFICATION_VERSION`. pub fn all_supported_versions() -> impl std::iter::Iterator { use strum::IntoEnumIterator; CertificationVersion::iter().filter(|v| { @@ -105,3 +68,21 @@ fn version_constants_consistent() { assert!(MIN_SUPPORTED_CERTIFICATION_VERSION <= CURRENT_CERTIFICATION_VERSION); assert!(CURRENT_CERTIFICATION_VERSION <= MAX_SUPPORTED_CERTIFICATION_VERSION); } + +#[test] +fn convert_from_u32_succeeds_for_all_supported_certification_versions() { + use strum::IntoEnumIterator; + assert!(all_supported_versions().all(|v| (v as u32).try_into() == Ok(v))); + // Old unsupported version should fail. + let v = CertificationVersion::iter().next().unwrap() as u32 - 1; + assert_eq!( + CertificationVersion::try_from(v), + Err(UnsupportedCertificationVersion(v)) + ); + // Non-existent version should fail. + let v = CertificationVersion::iter().last().unwrap() as u32 + 1; + assert_eq!( + CertificationVersion::try_from(v), + Err(UnsupportedCertificationVersion(v)) + ); +} diff --git a/rs/canonical_state/src/encoding/old_types.rs b/rs/canonical_state/src/encoding/old_types.rs index 24ea7b6afed..5e3292238ac 100644 --- a/rs/canonical_state/src/encoding/old_types.rs +++ b/rs/canonical_state/src/encoding/old_types.rs @@ -1,7 +1,10 @@ -//! Copies of historica canonical types and their conversion logic. +//! Copies of historical canonical types and their conversion logic. //! //! Whenever a canonical type is modified, a copy of the "old" type should be //! made here. +//! +//! Types of certification versions < MIN_SUPPORTED_CERTIFICATION_VERSION can +//! be removed. use std::{ convert::{TryFrom, TryInto}, @@ -12,13 +15,12 @@ use crate::CertificationVersion; use super::types; use crate::encoding::types::{ - Bytes, Cycles, Funds, Response, StreamFlagBits as StreamFlagBitsV17, - STREAM_DEFAULT_FLAGS as STREAM_DEFAULT_FLAGS_V17, + Bytes, Cycles, Funds, StreamFlagBits as StreamFlagBitsV17, STREAM_SUPPORTED_FLAGS as STREAM_SUPPORTED_FLAGS_V17, }; use ic_protobuf::proxy::ProxyDecodeError; use ic_types::messages::NO_DEADLINE; -use ic_types::xnet::{RejectReason, RejectSignal, StreamHeader, StreamIndex}; +use ic_types::xnet::{RejectReason, RejectSignal, StreamIndex}; use serde::{Deserialize, Serialize}; use std::collections::VecDeque; @@ -111,9 +113,7 @@ impl From<(&ic_types::messages::Request, CertificationVersion)> for RequestV17 { method_name: request.method_name.clone(), method_payload: request.method_payload.clone(), cycles_payment: None, - metadata: request.metadata.as_ref().and_then(|metadata| { - (certification_version >= CertificationVersion::V14).then_some(metadata.into()) - }), + metadata: request.metadata.as_ref().map(From::from), } } } @@ -205,281 +205,6 @@ impl TryFrom for ic_types::messages::Response { } } -/// Copy of `types::RequestOrResponse` at canonical version 13 (before the -/// addition of `metadata` to `types::Request`). -#[derive(Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct RequestOrResponseV13 { - #[serde(skip_serializing_if = "Option::is_none")] - pub request: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub response: Option, -} - -/// Copy of `types::Request` at canonical version 13 (before the addition of `metadata`). -#[derive(Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct RequestV13 { - #[serde(with = "serde_bytes")] - pub receiver: Bytes, - #[serde(with = "serde_bytes")] - pub sender: Bytes, - pub sender_reply_callback: u64, - pub payment: Funds, - pub method_name: String, - #[serde(with = "serde_bytes")] - pub method_payload: Bytes, - #[serde(skip_serializing_if = "Option::is_none")] - pub cycles_payment: Option, -} - -impl From<(&ic_types::messages::RequestOrResponse, CertificationVersion)> for RequestOrResponseV13 { - fn from( - (message, certification_version): ( - &ic_types::messages::RequestOrResponse, - CertificationVersion, - ), - ) -> Self { - use ic_types::messages::RequestOrResponse::*; - match message { - Request(request) => Self { - request: Some((request.as_ref(), certification_version).into()), - response: None, - }, - Response(response) => Self { - request: None, - response: Some((response.as_ref(), certification_version).into()), - }, - } - } -} - -impl TryFrom for ic_types::messages::RequestOrResponse { - type Error = ProxyDecodeError; - - fn try_from(message: RequestOrResponseV13) -> Result { - match message { - RequestOrResponseV13 { - request: Some(request), - response: None, - } => Ok(Self::Request(Arc::new(request.try_into()?))), - RequestOrResponseV13 { - request: None, - response: Some(response), - } => Ok(Self::Response(Arc::new(response.try_into()?))), - other => Err(ProxyDecodeError::Other(format!( - "RequestOrResponse: expected exactly one of `request` or `response` to be `Some(_)`, got `{:?}`", - other - ))) - } - } -} - -impl From<(&ic_types::messages::Request, CertificationVersion)> for RequestV13 { - fn from( - (request, certification_version): (&ic_types::messages::Request, CertificationVersion), - ) -> Self { - let funds = Funds { - cycles: (&request.payment, certification_version).into(), - icp: 0, - }; - Self { - receiver: request.receiver.get().to_vec(), - sender: request.sender.get().to_vec(), - sender_reply_callback: request.sender_reply_callback.get(), - payment: funds, - method_name: request.method_name.clone(), - method_payload: request.method_payload.clone(), - cycles_payment: None, - } - } -} - -impl TryFrom for ic_types::messages::Request { - type Error = ProxyDecodeError; - - fn try_from(request: RequestV13) -> Result { - let payment = match request.cycles_payment { - Some(cycles) => cycles, - None => request.payment.cycles, - } - .try_into()?; - - Ok(Self { - receiver: ic_types::CanisterId::unchecked_from_principal( - request.receiver.as_slice().try_into()?, - ), - sender: ic_types::CanisterId::unchecked_from_principal( - request.sender.as_slice().try_into()?, - ), - sender_reply_callback: request.sender_reply_callback.into(), - payment, - method_name: request.method_name, - method_payload: request.method_payload, - metadata: None, - deadline: NO_DEADLINE, - }) - } -} - -/// Copy of `types::Request` at canonical version 3 (before the addition of `cycles_payment`). -#[derive(Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct RequestV3 { - #[serde(with = "serde_bytes")] - pub receiver: types::Bytes, - #[serde(with = "serde_bytes")] - pub sender: types::Bytes, - pub sender_reply_callback: u64, - pub payment: types::Funds, - pub method_name: String, - #[serde(with = "serde_bytes")] - pub method_payload: types::Bytes, -} - -impl From<(&ic_types::messages::Request, CertificationVersion)> for RequestV3 { - fn from( - (request, certification_version): (&ic_types::messages::Request, CertificationVersion), - ) -> Self { - let funds = types::Funds { - cycles: (&request.payment, certification_version).into(), - icp: 0, - }; - Self { - receiver: request.receiver.get().to_vec(), - sender: request.sender.get().to_vec(), - sender_reply_callback: request.sender_reply_callback.get(), - payment: funds, - method_name: request.method_name.clone(), - method_payload: request.method_payload.clone(), - } - } -} - -impl TryFrom for ic_types::messages::Request { - type Error = ProxyDecodeError; - - fn try_from(request: RequestV3) -> Result { - Ok(Self { - receiver: ic_types::CanisterId::unchecked_from_principal( - request.receiver.as_slice().try_into()?, - ), - sender: ic_types::CanisterId::unchecked_from_principal( - request.sender.as_slice().try_into()?, - ), - sender_reply_callback: request.sender_reply_callback.into(), - payment: request.payment.cycles.try_into()?, - method_name: request.method_name, - method_payload: request.method_payload, - metadata: None, - deadline: NO_DEADLINE, - }) - } -} - -/// Copy of `types::Response` at canonical version 3 (before the addition of `cycles_refund`). -#[derive(Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct ResponseV3 { - #[serde(with = "serde_bytes")] - pub originator: types::Bytes, - #[serde(with = "serde_bytes")] - pub respondent: types::Bytes, - pub originator_reply_callback: u64, - pub refund: types::Funds, - pub response_payload: types::Payload, -} - -impl From<(&ic_types::messages::Response, CertificationVersion)> for ResponseV3 { - fn from( - (response, certification_version): (&ic_types::messages::Response, CertificationVersion), - ) -> Self { - let funds = types::Funds { - cycles: (&response.refund, certification_version).into(), - icp: 0, - }; - Self { - originator: response.originator.get().to_vec(), - respondent: response.respondent.get().to_vec(), - originator_reply_callback: response.originator_reply_callback.get(), - refund: funds, - response_payload: (&response.response_payload, certification_version).into(), - } - } -} - -impl TryFrom for ic_types::messages::Response { - type Error = ProxyDecodeError; - - fn try_from(response: ResponseV3) -> Result { - Ok(Self { - originator: ic_types::CanisterId::unchecked_from_principal( - response.originator.as_slice().try_into()?, - ), - respondent: ic_types::CanisterId::unchecked_from_principal( - response.respondent.as_slice().try_into()?, - ), - originator_reply_callback: response.originator_reply_callback.into(), - refund: response.refund.cycles.try_into()?, - response_payload: response.response_payload.try_into()?, - deadline: NO_DEADLINE, - }) - } -} - -/// Copy of `types::RequestOrResponse` at canonical version 3 (before the -/// addition of `cycles_refund` to `types::Request` and `types::Response`). -#[derive(Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct RequestOrResponseV3 { - #[serde(skip_serializing_if = "Option::is_none")] - pub request: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub response: Option, -} - -impl From<(&ic_types::messages::RequestOrResponse, CertificationVersion)> for RequestOrResponseV3 { - fn from( - (message, certification_version): ( - &ic_types::messages::RequestOrResponse, - CertificationVersion, - ), - ) -> Self { - use ic_types::messages::RequestOrResponse::*; - match message { - Request(req) => RequestOrResponseV3 { - request: Some(RequestV3::from((req.as_ref(), certification_version))), - response: None, - }, - Response(resp) => RequestOrResponseV3 { - request: None, - response: Some(ResponseV3::from((resp.as_ref(), certification_version))), - }, - } - } -} - -impl TryFrom for ic_types::messages::RequestOrResponse { - type Error = ProxyDecodeError; - - fn try_from(message: RequestOrResponseV3) -> Result { - match message { - RequestOrResponseV3 { - request: Some(request), - response: None, - } => Ok(Self::Request(Arc::new(request.try_into()?))), - RequestOrResponseV3 { - request: None, - response: Some(response), - } => Ok(Self::Response(Arc::new(response.try_into()?))), - other => Err(ProxyDecodeError::Other(format!( - "RequestOrResponseV3: expected exactly one of `request` or `response` to be `Some(_)`, got `{:?}`", - other - ))) - } - } -} - pub fn is_zero(v: &T) -> bool where T: Into + Copy, @@ -509,21 +234,8 @@ pub struct StreamHeaderV18 { impl From<(&ic_types::xnet::StreamHeader, CertificationVersion)> for StreamHeaderV18 { fn from( - (header, certification_version): (&ic_types::xnet::StreamHeader, CertificationVersion), + (header, _certification_version): (&ic_types::xnet::StreamHeader, CertificationVersion), ) -> Self { - // Replicas with certification version < 9 do not produce reject signals. This - // includes replicas with certification version 8, but they may "inherit" reject - // signals from a replica with certification version 9 after a downgrade. - assert!( - header.reject_signals().is_empty() || certification_version >= CertificationVersion::V8, - "Replicas with certification version < 9 should not be producing reject signals" - ); - // Replicas with certification version < 17 should not have flags set. - assert!( - *header.flags() == STREAM_DEFAULT_FLAGS_V17 - || certification_version >= CertificationVersion::V17 - ); - let mut next_index = header.signals_end(); let mut reject_signal_deltas = vec![0; header.reject_signals().len()]; for (i, stream_index) in header @@ -603,160 +315,3 @@ impl TryFrom for ic_types::xnet::StreamHeader { )) } } - -/// Copy of `types::StreamHeader` at canonical version 16 (before the addition of `flags`). -#[derive(Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct StreamHeaderV16 { - pub begin: u64, - pub end: u64, - pub signals_end: u64, - /// Delta encoded reject signals: the last signal is encoded as the delta - /// between `signals_end` and the stream index of the rejected message; all - /// other signals are encoded as the delta between the next stream index and - /// the current one. - /// - /// Note that `signals_end` is NOT part of the reject signals. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub reject_signal_deltas: Vec, -} - -impl From<(&StreamHeader, CertificationVersion)> for StreamHeaderV16 { - fn from( - (header, certification_version): (&ic_types::xnet::StreamHeader, CertificationVersion), - ) -> Self { - // Replicas with certification version < 9 do not produce reject signals. This - // includes replicas with certification version 8, but they may "inherit" reject - // signals from a replica with certification version 9 after a downgrade. - assert!( - header.reject_signals().is_empty() || certification_version >= CertificationVersion::V8, - "Replicas with certification version < 9 should not be producing reject signals" - ); - // Replicas with certification version < 17 must not have flags set. - // - // This `assert` was added for testing purposes and was never present - // in any version on main net. - assert_eq!(*header.flags(), STREAM_DEFAULT_FLAGS_V17); - - let mut next_index = header.signals_end(); - let mut reject_signal_deltas = vec![0; header.reject_signals().len()]; - for (i, stream_index) in header - .reject_signals() - .iter() - .enumerate() - .map(|(i, signal)| { - // Reject signals at certification version < 19 may not produce signals other than - // `CanisterMigrating`. - assert_eq!(signal.reason, RejectReason::CanisterMigrating); - (i, signal.index) - }) - .rev() - { - assert!(next_index > stream_index); - reject_signal_deltas[i] = next_index.get() - stream_index.get(); - next_index = stream_index; - } - - Self { - begin: header.begin().get(), - end: header.end().get(), - signals_end: header.signals_end().get(), - reject_signal_deltas, - } - } -} - -impl TryFrom for StreamHeader { - type Error = ProxyDecodeError; - fn try_from(header: StreamHeaderV16) -> Result { - let mut reject_signals = VecDeque::with_capacity(header.reject_signal_deltas.len()); - let mut stream_index = StreamIndex::new(header.signals_end); - for delta in header.reject_signal_deltas.iter().rev() { - if stream_index < StreamIndex::new(*delta) { - // Reject signal deltas are invalid. - return Err(ProxyDecodeError::Other(format!( - "StreamHeader: reject signals are invalid, got `signals_end` {:?}, `reject_signal_deltas` {:?}", - header.signals_end, - header.reject_signal_deltas, - ))); - } - stream_index -= StreamIndex::new(*delta); - reject_signals.push_front(RejectSignal::new( - RejectReason::CanisterMigrating, - stream_index, - )); - } - - Ok(Self::new( - header.begin.into(), - header.end.into(), - header.signals_end.into(), - reject_signals, - ic_types::xnet::StreamFlags::default(), - )) - } -} - -/// Copy of `types::StreamHeader` at canonical version 6 (before the addition of -/// `reject_signals`). -#[derive(Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct StreamHeaderV6 { - pub begin: u64, - pub end: u64, - pub signals_end: u64, -} - -impl From<(&StreamHeader, CertificationVersion)> for StreamHeaderV6 { - fn from((header, _certification_version): (&StreamHeader, CertificationVersion)) -> Self { - Self { - begin: header.begin().get(), - end: header.end().get(), - signals_end: header.signals_end().get(), - } - } -} - -impl From for StreamHeader { - fn from(header: StreamHeaderV6) -> Self { - Self::new( - header.begin.into(), - header.end.into(), - header.signals_end.into(), - Default::default(), // reject_signals - Default::default(), // flags - ) - } -} - -/// Canonical representation of state metadata leaf, before dropping `id_counter`. -#[derive(Debug, Serialize)] -pub struct SystemMetadataV9 { - /// The counter used to allocate canister ids. - pub id_counter: u64, - /// Hash bytes of the previous (partial) canonical state. - pub prev_state_hash: Option>, -} - -impl - From<( - &ic_replicated_state::metadata_state::SystemMetadata, - CertificationVersion, - )> for SystemMetadataV9 -{ - fn from( - (metadata, _certification_version): ( - &ic_replicated_state::metadata_state::SystemMetadata, - CertificationVersion, - ), - ) -> Self { - Self { - // `SystemMetadata::generated_id_counter` was removed, set this to its default value. - id_counter: 0, - prev_state_hash: metadata - .prev_state_hash - .as_ref() - .map(|h| h.get_ref().0.clone()), - } - } -} diff --git a/rs/canonical_state/src/encoding/tests/compatibility.rs b/rs/canonical_state/src/encoding/tests/compatibility.rs index 897db89f7b5..b811372a69b 100644 --- a/rs/canonical_state/src/encoding/tests/compatibility.rs +++ b/rs/canonical_state/src/encoding/tests/compatibility.rs @@ -80,7 +80,7 @@ fn canonical_encoding_stream_header() { } } -/// Canonical CBOR encoding (with certification version 8 to version 18) of: +/// Canonical CBOR encoding (with certification version up to 18) of: /// /// ```no_run /// StreamHeader { @@ -114,9 +114,9 @@ fn canonical_encoding_stream_header() { /// ``` /// Used http://cbor.me/ for printing the human friendly output. #[test] -fn canonical_encoding_stream_header_v8_to_v18() { - for certification_version in all_supported_versions() - .filter(|v| v >= &CertificationVersion::V8 && v <= &CertificationVersion::V18) +fn canonical_encoding_stream_header_up_to_v18() { + for certification_version in + all_supported_versions().filter(|v| v <= &CertificationVersion::V18) { let header = StreamHeader::new( 23.into(), @@ -293,7 +293,7 @@ fn canonical_encoding_stream_header_v19_plus() { } } -/// Canonical CBOR encoding (with certification versions 15 and up) of: +/// Canonical CBOR encoding of: /// /// ```no_run /// SubnetMetrics { @@ -337,10 +337,8 @@ fn canonical_encoding_stream_header_v19_plus() { /// ``` /// Used http://cbor.me/ for printing the human friendly output. #[test] -fn canonical_encoding_subnet_metrics_v15_plus() { - for certification_version in - all_supported_versions().filter(|v| v >= &CertificationVersion::V15) - { +fn canonical_encoding_subnet_metrics() { + for certification_version in all_supported_versions() { let mut metrics = SubnetMetrics::default(); metrics.consumed_cycles_by_deleted_canisters = NominalCycles::from(0); metrics.consumed_cycles_http_outcalls = NominalCycles::from(50_000_000_000); @@ -385,73 +383,6 @@ fn canonical_encoding_subnet_metrics_v15_plus() { /// payment: Cycles::new(3), /// method_name: "test".to_string(), /// method_payload: vec![6], -/// metadata: None, -/// deadline: NO_DEADLINE, -/// } -/// ) -/// ``` -/// -/// Expected: -/// -/// ```text -/// A1 # map(1) -/// 00 # field_index(RequestOrResponse::request) -/// A6 # map(6) -/// 00 # field_index(Request::receiver) -/// 4A # bytes(10) -/// 00000000000000010101 # "\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01" -/// 01 # field_index(Request::sender) -/// 4A # bytes(10) -/// 00000000000000020101 # "\x00\x00\x00\x00\x00\x00\x00\x02\x01\x01" -/// 02 # field_index(Request::sender_reply_callback) -/// 03 # unsigned(3) -/// 03 # field_index(Request::payment) -/// A1 # map(1) -/// 00 # field_index(Funds::cycles) -/// A1 # map(1) -/// 00 # field_index(Cycles::raw) -/// 04 # unsigned(4) -/// 04 # field_index(Request::method_name) -/// 64 # text(4) -/// 74657374 # "test" -/// 05 # field_index(Request::method_payload) -/// 41 # bytes(1) -/// 06 # "\x06" -/// ``` -/// Used http://cbor.me/ for printing the human friendly output. -#[test] -fn canonical_encoding_request() { - for certification_version in all_supported_versions() { - let request: RequestOrResponse = Request { - receiver: canister_test_id(1), - sender: canister_test_id(2), - sender_reply_callback: CallbackId::from(3), - payment: Cycles::new(4), - method_name: "test".to_string(), - method_payload: vec![6], - metadata: None, - deadline: NO_DEADLINE, - } - .into(); - - assert_eq!( - "A1 00 A6 00 4A 00 00 00 00 00 00 00 01 01 01 01 4A 00 00 00 00 00 00 00 02 01 01 02 03 03 A1 00 A1 00 04 04 64 74 65 73 74 05 41 06", - as_hex(&encode_message(&request, certification_version)) - ); - } -} - -/// Canonical CBOR encoding (with certification versions 14 and up) of: -/// -/// ```no_run -/// RequestOrResponse::Request( -/// Request { -/// receiver: canister_test_id(1), -/// sender: canister_test_id(2), -/// sender_reply_callback: CallbackId::from(3), -/// payment: Cycles::new(3), -/// method_name: "test".to_string(), -/// method_payload: vec![6], /// metadata: Some(RequestMetadata { /// call_tree_depth: 13, /// call_tree_start_time: Time::as_nanos_since_unix_epoch(101), @@ -496,10 +427,8 @@ fn canonical_encoding_request() { /// ``` /// Used http://cbor.me/ for printing the human friendly output. #[test] -fn canonical_encoding_request_v14_plus() { - for certification_version in - all_supported_versions().filter(|v| v >= &CertificationVersion::V14) - { +fn canonical_encoding_request() { + for certification_version in all_supported_versions() { let request: RequestOrResponse = Request { receiver: canister_test_id(1), sender: canister_test_id(2), @@ -963,19 +892,10 @@ fn canonical_encoding_system_metadata() { let mut metadata = SystemMetadata::new(subnet_test_id(13), SubnetType::Application); metadata.prev_state_hash = Some(CryptoHashOfPartialState::new(CryptoHash(vec![15]))); - if certification_version <= CertificationVersion::V9 { - // `generated_id_counter` encoded up to and including V9. - assert_eq!( - "A2 00 00 01 81 0F", - as_hex(&encode_metadata(&metadata, certification_version)) - ); - } else { - // `generated_id_counter` not encoded starting with V10. - assert_eq!( - "A1 01 81 0F", - as_hex(&encode_metadata(&metadata, certification_version)) - ); - } + assert_eq!( + "A1 01 81 0F", + as_hex(&encode_metadata(&metadata, certification_version)) + ); } } @@ -1526,23 +1446,19 @@ fn encode_with_mutation( // Own fixtures, to ensure that compatibility tests are self-contained. // -fn stream_header(certification_version: CertificationVersion) -> StreamHeader { +fn stream_header(_certification_version: CertificationVersion) -> StreamHeader { StreamHeader::new( 23.into(), 25.into(), 256.into(), - if certification_version >= CertificationVersion::V8 { - vec![ - RejectSignal::new(RejectReason::CanisterMigrating, 249.into()), - RejectSignal::new(RejectReason::CanisterMigrating, 250.into()), - RejectSignal::new(RejectReason::CanisterMigrating, 252.into()), - ] - .into() - } else { - VecDeque::default() - }, + vec![ + RejectSignal::new(RejectReason::CanisterMigrating, 249.into()), + RejectSignal::new(RejectReason::CanisterMigrating, 250.into()), + RejectSignal::new(RejectReason::CanisterMigrating, 252.into()), + ] + .into(), StreamFlags { - deprecated_responses_only: certification_version >= CertificationVersion::V17, + deprecated_responses_only: true, }, ) } @@ -1559,7 +1475,7 @@ fn request(certification_version: CertificationVersion) -> Request { payment: cycles(), method_name: "test".to_string(), method_payload: vec![6], - metadata: request_metadata(certification_version), + metadata: request_metadata(), deadline: deadline(certification_version), } } @@ -1595,8 +1511,8 @@ fn reject_context() -> RejectContext { RejectContext::new(RejectCode::SysFatal, "Oops") } -fn request_metadata(certification_version: CertificationVersion) -> Option { - (certification_version >= CertificationVersion::V14).then_some(RequestMetadata::new( +fn request_metadata() -> Option { + Some(RequestMetadata::new( 13, Time::from_nanos_since_unix_epoch(101), )) diff --git a/rs/canonical_state/src/encoding/tests/test_fixtures.rs b/rs/canonical_state/src/encoding/tests/test_fixtures.rs index c979d0030df..697386dec41 100644 --- a/rs/canonical_state/src/encoding/tests/test_fixtures.rs +++ b/rs/canonical_state/src/encoding/tests/test_fixtures.rs @@ -10,12 +10,10 @@ use ic_types::{ xnet::{RejectReason, RejectSignal, StreamFlags, StreamHeader}, Cycles, Time, }; -use std::collections::VecDeque; pub fn stream_header(certification_version: CertificationVersion) -> StreamHeader { use CertificationVersion::*; let reject_signals = match certification_version { - version if version < V8 => VecDeque::new(), version if version < V19 => vec![ RejectSignal::new(RejectReason::CanisterMigrating, 10.into()), RejectSignal::new(RejectReason::CanisterMigrating, 200.into()), @@ -34,16 +32,17 @@ pub fn stream_header(certification_version: CertificationVersion) -> StreamHeade .into(), }; let flags = StreamFlags { - deprecated_responses_only: certification_version >= CertificationVersion::V17, + deprecated_responses_only: true, }; StreamHeader::new(23.into(), 25.into(), 256.into(), reject_signals, flags) } pub fn request(certification_version: CertificationVersion) -> RequestOrResponse { - let metadata = (certification_version >= CertificationVersion::V14).then_some( - RequestMetadata::new(1, Time::from_nanos_since_unix_epoch(100_000)), - ); + let metadata = Some(RequestMetadata::new( + 1, + Time::from_nanos_since_unix_epoch(100_000), + )); let deadline = if certification_version >= CertificationVersion::V18 { CoarseTime::from_secs_since_unix_epoch(8) } else { diff --git a/rs/canonical_state/src/encoding/types.rs b/rs/canonical_state/src/encoding/types.rs index f825054969d..2884fb14835 100644 --- a/rs/canonical_state/src/encoding/types.rs +++ b/rs/canonical_state/src/encoding/types.rs @@ -195,7 +195,7 @@ pub struct RejectContext { pub struct SystemMetadata { /// The counter used to allocate canister ids. #[serde(skip_serializing_if = "Option::is_none")] - pub id_counter: Option, + pub deprecated_id_counter: Option, /// Hash bytes of the previous (partial) canonical state. pub prev_state_hash: Option>, } @@ -233,19 +233,6 @@ impl From<(&ic_types::xnet::StreamHeader, CertificationVersion)> for StreamHeade fn from( (header, certification_version): (&ic_types::xnet::StreamHeader, CertificationVersion), ) -> Self { - // Replicas with certification version < 9 do not produce reject signals. This - // includes replicas with certification version 8, but they may "inherit" reject - // signals from a replica with certification version 9 after a downgrade. - assert!( - header.reject_signals().is_empty() || certification_version >= CertificationVersion::V8, - "Replicas with certification version < 9 should not be producing reject signals" - ); - // Replicas with certification version < 17 should not have flags set. - assert!( - *header.flags() == STREAM_DEFAULT_FLAGS - || certification_version >= CertificationVersion::V17 - ); - let mut flags = 0; let ic_types::xnet::StreamFlags { deprecated_responses_only, @@ -535,9 +522,7 @@ impl From<(&ic_types::messages::Request, CertificationVersion)> for Request { method_name: request.method_name.clone(), method_payload: request.method_payload.clone(), cycles_payment: None, - metadata: request.metadata.as_ref().and_then(|metadata| { - (certification_version >= CertificationVersion::V14).then_some(metadata.into()) - }), + metadata: request.metadata.as_ref().map(From::from), deadline: request.deadline.as_secs_since_unix_epoch(), } } @@ -743,17 +728,13 @@ impl )> for SystemMetadata { fn from( - (metadata, certification_version): ( + (metadata, _certification_version): ( &ic_replicated_state::metadata_state::SystemMetadata, CertificationVersion, ), ) -> Self { Self { - id_counter: if certification_version <= CertificationVersion::V9 { - Some(0) - } else { - None - }, + deprecated_id_counter: None, prev_state_hash: metadata .prev_state_hash .as_ref() diff --git a/rs/canonical_state/src/lazy_tree_conversion.rs b/rs/canonical_state/src/lazy_tree_conversion.rs index cce96e785ca..1eb80b74765 100644 --- a/rs/canonical_state/src/lazy_tree_conversion.rs +++ b/rs/canonical_state/src/lazy_tree_conversion.rs @@ -294,16 +294,12 @@ pub fn replicated_state_as_lazy_tree(state: &ReplicatedState) -> LazyTree<'_> { fork( FiniteMap::default() - .with_if( - certification_version >= CertificationVersion::V16, - "api_boundary_nodes", - move || { - api_boundary_nodes_as_tree( - &state.metadata.api_boundary_nodes, - certification_version, - ) - }, - ) + .with("api_boundary_nodes", move || { + api_boundary_nodes_as_tree( + &state.metadata.api_boundary_nodes, + certification_version, + ) + }) .with("metadata", move || { system_metadata_as_tree(&state.metadata, certification_version) }) @@ -315,10 +311,7 @@ pub fn replicated_state_as_lazy_tree(state: &ReplicatedState) -> LazyTree<'_> { }) .with_tree( "request_status", - fork(IngressHistoryFork( - &state.metadata.ingress_history, - certification_version, - )), + fork(IngressHistoryFork(&state.metadata.ingress_history)), ) .with("subnet", move || { let inverted_routing_table = Arc::new(invert_routing_table( @@ -380,13 +373,13 @@ fn system_metadata_as_tree( blob(move || encode_metadata(m, certification_version)) } -struct IngressHistoryFork<'a>(&'a IngressHistoryState, CertificationVersion); +struct IngressHistoryFork<'a>(&'a IngressHistoryState); impl<'a> LazyFork<'a> for IngressHistoryFork<'a> { fn edge(&self, label: &Label) -> Option> { let byte_array: [u8; EXPECTED_MESSAGE_ID_LENGTH] = label.as_bytes().try_into().ok()?; let id = MessageId::from(byte_array); - self.0.get(&id).map(|status| status_to_tree(status, self.1)) + self.0.get(&id).map(|status| status_to_tree(status)) } fn labels(&self) -> Box + '_> { @@ -397,7 +390,7 @@ impl<'a> LazyFork<'a> for IngressHistoryFork<'a> { Box::new( self.0 .statuses() - .map(|(id, status)| (Label::from(id.as_bytes()), status_to_tree(status, self.1))), + .map(|(id, status)| (Label::from(id.as_bytes()), status_to_tree(status))), ) } @@ -464,11 +457,11 @@ impl<'a> LazyFork<'a> for ReplyStatus<'a> { } } -const REJECT_STATUS_LABELS: [(&[u8], CertificationVersion); 4] = [ - (ERROR_CODE_LABEL, CertificationVersion::V11), - (REJECT_CODE_LABEL, CertificationVersion::V0), - (REJECT_MESSAGE_LABEL, CertificationVersion::V0), - (STATUS_LABEL, CertificationVersion::V0), +const REJECT_STATUS_LABELS: [&[u8]; 4] = [ + ERROR_CODE_LABEL, + REJECT_CODE_LABEL, + REJECT_MESSAGE_LABEL, + STATUS_LABEL, ]; #[derive(Clone)] @@ -476,7 +469,6 @@ struct RejectStatus<'a> { reject_code: u64, error_code: Option, message: &'a str, - version: CertificationVersion, } impl<'a> LazyFork<'a> for RejectStatus<'a> { @@ -493,20 +485,12 @@ impl<'a> LazyFork<'a> for RejectStatus<'a> { } fn labels(&self) -> Box + 'a> { - let version = self.version; - Box::new( - REJECT_STATUS_LABELS - .iter() - .filter_map(move |(label, v)| (*v <= version).then_some(Label::from(label))), - ) + Box::new(REJECT_STATUS_LABELS.iter().map(From::from)) } fn children(&self) -> Box)> + 'a> { let status = self.clone(); - Box::new(REJECT_STATUS_LABELS.iter().filter_map(move |(label, v)| { - if *v > status.version { - return None; - } + Box::new(REJECT_STATUS_LABELS.iter().filter_map(move |label| { let label = Label::from(label); let fork = status.edge(&label)?; Some((label, fork)) @@ -514,29 +498,23 @@ impl<'a> LazyFork<'a> for RejectStatus<'a> { } fn len(&self) -> usize { - REJECT_STATUS_LABELS - .iter() - .filter(|(_, v)| *v <= self.version) - .count() + REJECT_STATUS_LABELS.len() } } -fn status_to_tree(status: &IngressStatus, version: CertificationVersion) -> LazyTree<'_> { +fn status_to_tree(status: &IngressStatus) -> LazyTree<'_> { match status { IngressStatus::Known { state, .. } => match state { IngressState::Completed(WasmResult::Reply(b)) => fork(ReplyStatus(b)), IngressState::Completed(WasmResult::Reject(s)) => fork(RejectStatus { reject_code: RejectCode::CanisterReject as u64, - error_code: (version >= CertificationVersion::V11) - .then_some(ErrorCode::CanisterRejectedMessage), + error_code: Some(ErrorCode::CanisterRejectedMessage), message: s, - version, }), IngressState::Failed(error) => fork(RejectStatus { reject_code: error.reject_code() as u64, - error_code: (version >= CertificationVersion::V11).then_some(error.code()), + error_code: Some(error.code()), message: error.description(), - version, }), IngressState::Processing | IngressState::Received | IngressState::Done => { fork(OnlyStatus(status.as_str())) @@ -552,46 +530,14 @@ const CONTROLLERS_LABEL: &[u8] = b"controllers"; const METADATA_LABEL: &[u8] = b"metadata"; const MODULE_HASH_LABEL: &[u8] = b"module_hash"; -const CANISTER_LABELS: [(&[u8], CertificationVersion, CertificationVersion); 5] = [ - ( - CERTIFIED_DATA_LABEL, - CertificationVersion::V0, - MAX_SUPPORTED_CERTIFICATION_VERSION, - ), - ( - CONTROLLER_LABEL, - CertificationVersion::V1, - CertificationVersion::V12, - ), - ( - CONTROLLERS_LABEL, - CertificationVersion::V2, - MAX_SUPPORTED_CERTIFICATION_VERSION, - ), - ( - METADATA_LABEL, - CertificationVersion::V6, - MAX_SUPPORTED_CERTIFICATION_VERSION, - ), - ( - MODULE_HASH_LABEL, - CertificationVersion::V1, - MAX_SUPPORTED_CERTIFICATION_VERSION, - ), +const CANISTER_LABELS: [&[u8]; 4] = [ + CERTIFIED_DATA_LABEL, + CONTROLLERS_LABEL, + METADATA_LABEL, + MODULE_HASH_LABEL, ]; -const CANISTER_NO_MODULE_LABELS: [(&[u8], CertificationVersion, CertificationVersion); 2] = [ - ( - CONTROLLER_LABEL, - CertificationVersion::V1, - CertificationVersion::V12, - ), - ( - CONTROLLERS_LABEL, - CertificationVersion::V2, - MAX_SUPPORTED_CERTIFICATION_VERSION, - ), -]; +const CANISTER_NO_MODULE_LABELS: [&[u8]; 1] = [CONTROLLERS_LABEL]; #[derive(Clone)] struct CanisterFork<'a> { @@ -629,60 +575,30 @@ impl<'a> CanisterFork<'a> { impl<'a> LazyFork<'a> for CanisterFork<'a> { fn edge(&self, label: &Label) -> Option> { - CANISTER_LABELS.iter().find(|(l, minv, maxv)| { - l == &label.as_bytes() && *minv <= self.version && self.version <= *maxv - })?; - + CANISTER_LABELS.iter().find(|l| *l == &label.as_bytes())?; self.edge_no_checks(label.as_bytes()) } fn labels(&self) -> Box + 'a> { - let version = self.version; - if self.canister.execution_state.is_some() { - Box::new( - CANISTER_LABELS - .iter() - .filter_map(move |(label, minv, maxv)| { - (*minv <= version && version <= *maxv).then_some(Label::from(label)) - }), - ) - } else { - Box::new( - CANISTER_NO_MODULE_LABELS - .iter() - .filter_map(move |(label, minv, maxv)| { - (*minv <= version && version <= *maxv).then_some(Label::from(label)) - }), - ) + match self.canister.execution_state { + Some(_) => Box::new(CANISTER_LABELS.iter().map(From::from)), + None => Box::new(CANISTER_NO_MODULE_LABELS.iter().map(From::from)), } } fn children(&self) -> Box)> + 'a> { let canister = self.clone(); Box::new( - CANISTER_LABELS - .iter() - .filter_map(move |(label, minv, maxv)| { - if !(*minv <= canister.version && canister.version <= *maxv) { - return None; - } - Some((Label::from(label), canister.edge_no_checks(label)?)) - }), + CANISTER_LABELS.iter().filter_map(move |label| { + Some((Label::from(label), canister.edge_no_checks(label)?)) + }), ) } fn len(&self) -> usize { - let version = self.version; - if self.canister.execution_state.is_some() { - CANISTER_LABELS - .iter() - .filter(move |(_, minv, maxv)| *minv <= version && version <= *maxv) - .count() - } else { - CANISTER_NO_MODULE_LABELS - .iter() - .filter(move |(_, minv, maxv)| *minv <= version && version <= *maxv) - .count() + match self.canister.execution_state { + Some(_) => CANISTER_LABELS.len(), + None => CANISTER_NO_MODULE_LABELS.len(), } } } @@ -740,8 +656,7 @@ fn subnets_as_tree<'a>( fork( FiniteMap::default() .with_tree("public_key", Blob(&subnet_topology.public_key[..], None)) - .with_tree_if( - certification_version > CertificationVersion::V2, + .with_tree( "canister_ranges", blob({ let inverted_routing_table = Arc::clone(&inverted_routing_table); @@ -752,15 +667,11 @@ fn subnets_as_tree<'a>( } }), ) - .with_if( - certification_version > CertificationVersion::V11 - && subnet_id == own_subnet_id, - "node", - move || nodes_as_tree(own_subnet_node_public_keys, certification_version), - ) + .with_if(subnet_id == own_subnet_id, "node", move || { + nodes_as_tree(own_subnet_node_public_keys, certification_version) + }) .with_tree_if( - certification_version >= CertificationVersion::V15 - && subnet_id == own_subnet_id, + subnet_id == own_subnet_id, "metrics", blob(move || encode_subnet_metrics(metrics, certification_version)), ), diff --git a/rs/canonical_state/src/traversal.rs b/rs/canonical_state/src/traversal.rs index 81f2c027b8f..a4c8947859d 100644 --- a/rs/canonical_state/src/traversal.rs +++ b/rs/canonical_state/src/traversal.rs @@ -47,7 +47,7 @@ mod tests { test_visitors::{NoopVisitor, TraceEntry as E, TracingVisitor}, }; use ic_base_types::{NumBytes, NumSeconds}; - use ic_certification_version::{all_supported_versions, CertificationVersion::*}; + use ic_certification_version::all_supported_versions; use ic_registry_routing_table::{CanisterIdRange, RoutingTable}; use ic_registry_subnet_features::SubnetFeatures; use ic_registry_subnet_type::SubnetType; @@ -102,7 +102,7 @@ mod tests { let expected_traversal = vec![ Some(vec![E::StartSubtree]), // global - (certification_version >= V16).then_some(vec![ + Some(vec![ edge("api_boundary_nodes"), E::StartSubtree, E::EndSubtree, // api_boundary_nodes @@ -113,7 +113,7 @@ mod tests { E::EndSubtree, // canisters edge("metadata"), E::VisitBlob(encode_metadata(SystemMetadata { - id_counter: (certification_version <= V9).then_some(0), + deprecated_id_counter: None, prev_state_hash: None, })), edge("request_status"), @@ -167,7 +167,7 @@ mod tests { let expected_traversal = vec![ Some(vec![E::StartSubtree]), // global - (certification_version >= V16).then_some(vec![ + Some(vec![ edge("api_boundary_nodes"), E::StartSubtree, E::EndSubtree, // api_boundary_nodes @@ -178,11 +178,7 @@ mod tests { E::EnterEdge(canister_id.get().into_vec()), E::StartSubtree, ]), - (V1..V13).contains(&certification_version).then_some(vec![ - edge("controller"), - E::VisitBlob(controller.get().to_vec()), - ]), - (certification_version >= V2).then_some(vec![ + Some(vec![ edge("controllers"), E::VisitBlob(controllers_cbor.clone()), ]), @@ -191,7 +187,7 @@ mod tests { E::EndSubtree, // canisters edge("metadata"), E::VisitBlob(encode_metadata(SystemMetadata { - id_counter: (certification_version <= V9).then_some(0), + deprecated_id_counter: None, prev_state_hash: None, })), edge("request_status"), @@ -267,7 +263,7 @@ mod tests { let expected_traversal = vec![ Some(vec![E::StartSubtree]), // global - (certification_version >= V16).then_some(vec![ + Some(vec![ edge("api_boundary_nodes"), E::StartSubtree, E::EndSubtree, // api_boundary_nodes @@ -280,15 +276,11 @@ mod tests { edge("certified_data"), E::VisitBlob(vec![]), ]), - (V1..V13).contains(&certification_version).then_some(vec![ - edge("controller"), - E::VisitBlob(controller.get().to_vec()), - ]), - (certification_version >= V2).then_some(vec![ + Some(vec![ edge("controllers"), E::VisitBlob(controllers_cbor.clone()), ]), - (certification_version >= V6).then_some(vec![ + Some(vec![ edge("metadata"), E::StartSubtree, edge("dummy1"), @@ -299,7 +291,7 @@ mod tests { E::VisitBlob(vec![8, 9]), E::EndSubtree, ]), - (certification_version >= V1).then_some(vec![ + Some(vec![ edge("module_hash"), E::VisitBlob(wasm_binary_hash.to_vec()), ]), @@ -308,7 +300,7 @@ mod tests { E::EndSubtree, // canisters edge("metadata"), E::VisitBlob(encode_metadata(SystemMetadata { - id_counter: (certification_version <= V9).then_some(0), + deprecated_id_counter: None, prev_state_hash: None, })), edge("request_status"), @@ -368,7 +360,7 @@ mod tests { let expected_traversal = vec![ Some(vec![E::StartSubtree]), // global - (certification_version >= V16).then_some(vec![ + Some(vec![ edge("api_boundary_nodes"), E::StartSubtree, E::EndSubtree, // api_boundary_nodes @@ -379,7 +371,7 @@ mod tests { E::EndSubtree, // canisters edge("metadata"), E::VisitBlob(encode_metadata(SystemMetadata { - id_counter: (certification_version <= V9).then_some(0), + deprecated_id_counter: None, prev_state_hash: None, })), edge("request_status"), @@ -530,8 +522,7 @@ mod tests { edge(message_test_id(4)), E::StartSubtree, ]), - (certification_version >= V11) - .then_some(vec![edge("error_code"), E::VisitBlob(b"IC0101".to_vec())]), + Some(vec![edge("error_code"), E::VisitBlob(b"IC0101".to_vec())]), Some(vec![ edge("reject_code"), leb_num(1), @@ -552,8 +543,7 @@ mod tests { edge(message_test_id(6)), E::StartSubtree, ]), - (certification_version >= V11) - .then_some(vec![edge("error_code"), E::VisitBlob(b"IC0406".to_vec())]), + Some(vec![edge("error_code"), E::VisitBlob(b"IC0406".to_vec())]), Some(vec![ edge("reject_code"), leb_num(4), @@ -591,14 +581,14 @@ mod tests { let mut state = ReplicatedState::new(subnet_test_id(1), SubnetType::Application); state.metadata.batch_time += Duration::new(1, 123456789); - // Test all certification versions. + // Test all supported certification versions. for certification_version in all_supported_versions() { state.metadata.certification_version = certification_version; let visitor = TracingVisitor::new(NoopVisitor); let expected_traversal = vec![ Some(vec![E::StartSubtree]), // global - (certification_version >= V16).then_some(vec![ + Some(vec![ edge("api_boundary_nodes"), E::StartSubtree, E::EndSubtree, // api_boundary_nodes @@ -609,7 +599,7 @@ mod tests { E::EndSubtree, // canisters edge("metadata"), E::VisitBlob(encode_metadata(SystemMetadata { - id_counter: (certification_version <= V9).then_some(0), + deprecated_id_counter: None, prev_state_hash: None, })), edge("request_status"), @@ -683,7 +673,7 @@ mod tests { let expected_traversal = vec![ Some(vec![E::StartSubtree]), // global - (certification_version >= V16).then_some(vec![ + Some(vec![ edge("api_boundary_nodes"), E::StartSubtree, E::EndSubtree, // api_boundary_nodes @@ -694,7 +684,7 @@ mod tests { E::EndSubtree, // canisters edge("metadata"), E::VisitBlob(encode_metadata(SystemMetadata { - id_counter: (certification_version <= V9).then_some(0), + deprecated_id_counter: None, prev_state_hash: None, })), edge("request_status"), @@ -708,7 +698,7 @@ mod tests { E::EnterEdge(subnet_test_id(0).get().into_vec()), E::StartSubtree, ]), - (certification_version >= V3).then_some(vec![ + Some(vec![ edge("canister_ranges"), //D9 D9F7 # tag(55799) // 82 # array(2) @@ -731,7 +721,7 @@ mod tests { E::EnterEdge(subnet_test_id(1).get().into_vec()), E::StartSubtree, ]), - (certification_version >= V3).then_some(vec![ + Some(vec![ edge("canister_ranges"), // D9 D9F7 # tag(55799) // 81 # array(1) @@ -742,7 +732,7 @@ mod tests { // 00000000000000140101 # "\x00\x00\x00\x00\x00\x00\x00\x14\x01\x01" E::VisitBlob(hex::decode("d9d9f781824a000000000000000b01014a00000000000000140101").unwrap()), ]), - (certification_version >= V15).then_some(vec![ + Some(vec![ edge("metrics"), // A4 # map(4) // 00 # unsigned(0) @@ -759,7 +749,7 @@ mod tests { // 00 # unsigned(0) E::VisitBlob(hex::decode("a40000010002a2000001000300").unwrap()), ]), - (certification_version >= V12).then_some(vec![ + Some(vec![ edge("node"), E::StartSubtree, E::EnterEdge(node_test_id(2).get().into_vec()), @@ -810,14 +800,14 @@ mod tests { }, }; - // Test all certification versions. + // Test all supported certification versions. for certification_version in all_supported_versions() { state.metadata.certification_version = certification_version; let visitor = TracingVisitor::new(NoopVisitor); let expected_traversal = vec![ Some(vec![E::StartSubtree]), // global - (certification_version >= V16).then_some(vec![ + Some(vec![ edge("api_boundary_nodes"), E::StartSubtree, E::EnterEdge(node_test_id(11).get().into_vec()), @@ -852,7 +842,7 @@ mod tests { E::EndSubtree, // canisters edge("metadata"), E::VisitBlob(encode_metadata(SystemMetadata { - id_counter: (certification_version <= V9).then_some(0), + deprecated_id_counter: None, prev_state_hash: None, })), edge("request_status"), diff --git a/rs/canonical_state/tests/compatibility.rs b/rs/canonical_state/tests/compatibility.rs index 96405b47d58..e45dd64fd10 100644 --- a/rs/canonical_state/tests/compatibility.rs +++ b/rs/canonical_state/tests/compatibility.rs @@ -1,17 +1,14 @@ use ic_base_types::PrincipalId; use ic_canonical_state::{ encoding::{ - old_types::{ - RequestOrResponseV13, RequestOrResponseV17, RequestOrResponseV3, StreamHeaderV16, - StreamHeaderV18, StreamHeaderV6, SystemMetadataV9, - }, + old_types::{RequestOrResponseV17, StreamHeaderV18}, types::{ RequestOrResponse as RequestOrResponseV18, StreamHeader as StreamHeaderV19, SubnetMetrics as SubnetMetricsV15, SystemMetadata as SystemMetadataV10, }, CborProxyDecoder, CborProxyEncoder, }, - CertificationVersion, MAX_SUPPORTED_CERTIFICATION_VERSION, + CertificationVersion, MAX_SUPPORTED_CERTIFICATION_VERSION, MIN_SUPPORTED_CERTIFICATION_VERSION, }; use ic_protobuf::proxy::ProxyDecodeError; use ic_replicated_state::{metadata_state::SubnetMetrics, SystemMetadata}; @@ -74,38 +71,15 @@ pub(crate) fn arb_valid_versioned_stream_header( max_signal_count: usize, ) -> impl Strategy)> { prop_oneof![ - // Stream headers up to and including certification version 8 had no reject - // signals. - ( - arb_stream_header( - /* min_signal_count */ 0, - /* max_signal_count */ 0, - /* with_reject_reasons */ vec![], - /* with_responses_only_flag */ vec![false], - ), - Just(CertificationVersion::V0..=CertificationVersion::V8) - ), - // Stream headers may have reject signals for responses (`CanisterMigrating` only) - // starting with certification version 8. - ( - arb_stream_header( - /* min_signal_count */ 0, - max_signal_count, - /* with_reject_reasons */ vec![RejectReason::CanisterMigrating], - /* with_responses_only_flag */ vec![false], - ), - Just(CertificationVersion::V8..=CertificationVersion::V16) - ), - // Stream headers may have the `StreamHeaderFlagBits::DeprecatedResponsesOnly` flag set - // starting with certification version 17. + // Stream headers up to version 18 may have reject signals for responses + // (`CanisterMigrating` only) and the `DeprecatedResponsesOnly` flag set. ( arb_stream_header( /* min_signal_count */ 0, max_signal_count, /* with_reject_reasons */ vec![RejectReason::CanisterMigrating], - /* with_responses_only_flag */ vec![true, false], ), - Just(CertificationVersion::V17..=CertificationVersion::V18) + Just(MIN_SUPPORTED_CERTIFICATION_VERSION..=CertificationVersion::V18) ), // Stream headers may have flavours of reject signals other than `CanisterMigrating` // starting from certification version 19. @@ -123,7 +97,6 @@ pub(crate) fn arb_valid_versioned_stream_header( RejectReason::OutOfMemory, RejectReason::Unknown ], - /* with_responses_only_flag */ vec![true, false], ), Just(CertificationVersion::V19..=MAX_SUPPORTED_CERTIFICATION_VERSION) ) @@ -135,28 +108,6 @@ pub(crate) fn arb_invalid_versioned_stream_header( max_signal_count: usize, ) -> impl Strategy)> { prop_oneof![ - // Encoding a stream header with non-empty reject signals before certification - // version 8 should panic. - ( - arb_stream_header( - /* min_signal_count */ 1, - max_signal_count, - /* with_reject_reasons */ vec![RejectReason::CanisterMigrating], - /* with_responses_only_flag */ vec![false], - ), - Just(CertificationVersion::V7..=CertificationVersion::V7) - ), - // Encoding a stream header with flags set before certification version 17 - // should panic. - ( - arb_stream_header( - /* min_signal_count */ 0, - max_signal_count, - /* with_reject_reasons */ vec![RejectReason::CanisterMigrating], - /* with_responses_only_flags */ vec![true], - ), - Just(CertificationVersion::V16..=CertificationVersion::V16) - ), // Encoding a stream header with reject signal flavors other than `CanisterMigrating` // before certification version 19 should panic. ( @@ -172,7 +123,6 @@ pub(crate) fn arb_invalid_versioned_stream_header( RejectReason::OutOfMemory, RejectReason::Unknown, ], - /* with_responses_only_flags */ vec![true, false], ), Just(CertificationVersion::V18..=CertificationVersion::V18) ), @@ -185,25 +135,13 @@ lazy_static! { static ref STREAM_HEADER_ENCODINGS: Vec> = vec![ #[allow(clippy::redundant_closure)] VersionedEncoding::new( - CertificationVersion::V0..=CertificationVersion::V6, - "StreamHeaderV6", - |v| StreamHeaderV6::proxy_encode(v), - |v| StreamHeaderV6::proxy_decode(v), - ), - VersionedEncoding::new( - CertificationVersion::V0..=CertificationVersion::V16, - "StreamHeaderV16", - |v| StreamHeaderV16::proxy_encode(v), - |v| StreamHeaderV16::proxy_decode(v), - ), - VersionedEncoding::new( - CertificationVersion::V0..=CertificationVersion::V18, + MIN_SUPPORTED_CERTIFICATION_VERSION..=CertificationVersion::V18, "StreamHeaderV18", |v| StreamHeaderV18::proxy_encode(v), |v| StreamHeaderV18::proxy_decode(v), ), VersionedEncoding::new( - CertificationVersion::V0..=MAX_SUPPORTED_CERTIFICATION_VERSION, + MIN_SUPPORTED_CERTIFICATION_VERSION..=MAX_SUPPORTED_CERTIFICATION_VERSION, "StreamHeader", |v| StreamHeaderV19::proxy_encode(v), |v| StreamHeaderV19::proxy_decode(v), @@ -286,18 +224,13 @@ pub(crate) fn arb_valid_versioned_message( ) -> impl Strategy)> { prop_oneof![ ( - // No `Request::metadata` populated for versions 13 and below. - arbitrary::request_or_response_with_config(false, false), - Just(CertificationVersion::V0..=MAX_SUPPORTED_CERTIFICATION_VERSION) - ), - ( - // Optionally populate `Request::metadata` from version 14 on. - arbitrary::request_or_response_with_config(true, false), - Just(CertificationVersion::V14..=MAX_SUPPORTED_CERTIFICATION_VERSION) + // No `deadline` before version 18. + arbitrary::request_or_response_with_config(false), + Just(MIN_SUPPORTED_CERTIFICATION_VERSION..=MAX_SUPPORTED_CERTIFICATION_VERSION) ), ( // Optionally populate `deadline` from version 18 on. - arbitrary::request_or_response_with_config(true, true), + arbitrary::request_or_response_with_config(true), Just(CertificationVersion::V18..=MAX_SUPPORTED_CERTIFICATION_VERSION) ), ] @@ -309,28 +242,13 @@ lazy_static! { static ref MESSAGE_ENCODINGS: Vec> = vec![ #[allow(clippy::redundant_closure)] VersionedEncoding::new( - CertificationVersion::V0..=CertificationVersion::V3, - "RequestOrResponseV3", - |v| RequestOrResponseV3::proxy_encode(v), - |v| RequestOrResponseV3::proxy_decode(v), - ), - #[allow(clippy::redundant_closure)] - VersionedEncoding::new( - CertificationVersion::V0..=CertificationVersion::V13, - "RequestOrResponseV13", - |v| RequestOrResponseV13::proxy_encode(v), - |v| RequestOrResponseV13::proxy_decode(v), - ), - #[allow(clippy::redundant_closure)] - VersionedEncoding::new( - CertificationVersion::V0..=CertificationVersion::V17, + MIN_SUPPORTED_CERTIFICATION_VERSION..=CertificationVersion::V17, "RequestOrResponseV17", |v| RequestOrResponseV17::proxy_encode(v), |v| RequestOrResponseV17::proxy_decode(v), ), - #[allow(clippy::redundant_closure)] VersionedEncoding::new( - CertificationVersion::V0..=MAX_SUPPORTED_CERTIFICATION_VERSION, + MIN_SUPPORTED_CERTIFICATION_VERSION..=MAX_SUPPORTED_CERTIFICATION_VERSION, "RequestOrResponse", |v| RequestOrResponseV18::proxy_encode(v), |v| RequestOrResponseV18::proxy_decode(v), @@ -392,14 +310,7 @@ lazy_static! { static ref SYSTEM_METADATA_ENCODINGS: Vec> = vec![ #[allow(clippy::redundant_closure)] VersionedEncoding::new( - CertificationVersion::V0..=CertificationVersion::V9, - "SystemMetadataV9", - |v| SystemMetadataV9::proxy_encode(v), - |_v| unimplemented!(), - ), - #[allow(clippy::redundant_closure)] - VersionedEncoding::new( - CertificationVersion::V0..=MAX_SUPPORTED_CERTIFICATION_VERSION, + MIN_SUPPORTED_CERTIFICATION_VERSION..=MAX_SUPPORTED_CERTIFICATION_VERSION, "SystemMetadataV10", |v| SystemMetadataV10::proxy_encode(v), |_v| unimplemented!(), @@ -430,16 +341,11 @@ prop_compose! { pub(crate) fn arb_valid_system_metadata( ) -> impl Strategy)> { prop_oneof![ - // `SystemMetadata` up to and including `V9` had a required `id_counter` field. - ( - arb_system_metadata(), - Just(CertificationVersion::V0..=CertificationVersion::V9) - ), // `SystemMetadata` `V10` and later have an optional `id_counter` field for // backwards compatibility, but it is no longer populated. ( arb_system_metadata(), - Just(CertificationVersion::V10..=MAX_SUPPORTED_CERTIFICATION_VERSION) + Just(MIN_SUPPORTED_CERTIFICATION_VERSION..=MAX_SUPPORTED_CERTIFICATION_VERSION) ), ] } @@ -479,7 +385,7 @@ lazy_static! { static ref SUBNET_METRICS_ENCODINGS: Vec> = vec![ #[allow(clippy::redundant_closure)] VersionedEncoding::new( - CertificationVersion::V15..=MAX_SUPPORTED_CERTIFICATION_VERSION, + MIN_SUPPORTED_CERTIFICATION_VERSION..=MAX_SUPPORTED_CERTIFICATION_VERSION, "SubnetMetricsV15", |v| SubnetMetricsV15::proxy_encode(v), |_v| unimplemented!(), @@ -492,7 +398,7 @@ pub(crate) fn arb_valid_subnet_metrics( ) -> impl Strategy)> { prop_oneof![( arb_subnet_metrics(), - Just(CertificationVersion::V15..=MAX_SUPPORTED_CERTIFICATION_VERSION) + Just(MIN_SUPPORTED_CERTIFICATION_VERSION..=MAX_SUPPORTED_CERTIFICATION_VERSION) )] } diff --git a/rs/execution_environment/BUILD.bazel b/rs/execution_environment/BUILD.bazel index 4acf7228e54..99da0382c6d 100644 --- a/rs/execution_environment/BUILD.bazel +++ b/rs/execution_environment/BUILD.bazel @@ -234,6 +234,25 @@ rust_ic_bench( ] + BENCH_DEPENDENCIES, ) +rust_ic_bench( + name = "load_simulator_canisters_bench", + srcs = ["benches/load_simulator_canisters.rs"], + data = DATA + [ + "//rs/rust_canisters/canister_creator:canister_creator_canister", + "//rs/rust_canisters/load_simulator:load_simulator_canister", + ], + env = dict(ENV.items() + [ + ("CANISTER_CREATOR_CANISTER_WASM_PATH", "$(rootpath //rs/rust_canisters/canister_creator:canister_creator_canister)"), + ("LOAD_SIMULATOR_CANISTER_WASM_PATH", "$(rootpath //rs/rust_canisters/load_simulator:load_simulator_canister)"), + ]), + deps = [ + # Keep sorted. + "//rs/execution_environment/benches/lib:execution_environment_bench", + "//rs/state_machine_tests", + "//rs/types/types_test_utils", + ] + BENCH_DEPENDENCIES, +) + rust_ic_bench( name = "100k_canisters_bench", srcs = ["benches/100k_canisters.rs"], diff --git a/rs/execution_environment/Cargo.toml b/rs/execution_environment/Cargo.toml index 49f4b218cce..e8c759d65af 100644 --- a/rs/execution_environment/Cargo.toml +++ b/rs/execution_environment/Cargo.toml @@ -1,10 +1,10 @@ [package] -name = "ic-execution-environment" -version.workspace = true authors.workspace = true -edition.workspace = true description.workspace = true documentation.workspace = true +edition.workspace = true +name = "ic-execution-environment" +version.workspace = true [dependencies] candid = { workspace = true } @@ -13,7 +13,6 @@ ic-base-types = { path = "../types/base_types" } ic-btc-interface = { workspace = true } ic-canister-sandbox-backend-lib = { path = "../canister_sandbox" } ic-config = { path = "../config" } -ic-limits = { path = "../limits" } ic-crypto-prng = { path = "../crypto/prng" } ic-crypto-sha2 = { path = "../crypto/sha2" } ic-crypto-tree-hash = { path = "../crypto/tree_hash" } @@ -23,6 +22,7 @@ ic-embedders = { path = "../embedders" } ic-error-types = { path = "../types/error_types" } ic-interfaces = { path = "../interfaces" } ic-interfaces-state-manager = { path = "../interfaces/state_manager" } +ic-limits = { path = "../limits" } ic-logger = { path = "../monitoring/logger" } ic-management-canister-types = { path = "../types/management_canister_types" } ic-metrics = { path = "../monitoring/metrics" } @@ -121,6 +121,10 @@ path = "benches/wasm_instructions/main.rs" harness = false name = "scheduler" +[[bench]] +harness = false +name = "load_simulator_canisters" + [[bench]] harness = false name = "100k_canisters" diff --git a/rs/execution_environment/benches/load_simulator_canisters.rs b/rs/execution_environment/benches/load_simulator_canisters.rs new file mode 100644 index 00000000000..f829f81c2c7 --- /dev/null +++ b/rs/execution_environment/benches/load_simulator_canisters.rs @@ -0,0 +1,179 @@ +//! The benchmark installs the specified number of `load_simulator` canisters +//! using the `canister_creator` and executes the specified amount of rounds. +//! It measures the total time taken to complete the rounds and the throughput, +//! i.e., the number of rounds executed per second (essentially, the FR). +//! +//! By default, each `load_simulator`` canister runs a periodic timer with +//! a one-second interval and accesses stable memory every fifth call. +//! +//! This benchmark is useful for debugging and benchmarking scheduler +//! and sandbox eviction changes. For more realistic testnet load tests, +//! refer to the `dfinity/subnet-load-tester` project. +//! +//! Quick start: +//! bazel run //rs/execution_environment:load_simulator_canisters_bench -- --quick 200 +//! +//! Example output: +//! ==> Creating 2 creator canisters... +//! ==> Creating 200 load simulator canisters... +//! ==> Awaiting creation to finish... +//! ==> Installing 200 load simulators... +//! ==> Awaiting installation to finish... +//! Load simulator/200 canisters/10 rounds +//! time: [1.9450 s 1.9450 s 1.9450 s] +//! ^ total time taken to complete 10 rounds +//! thrpt: [5.1414 elem/s 5.1414 elem/s 5.1414 elem/s] +//! ^ number of rounds executed per second (the FR) + +use std::time::Duration; + +use criterion::{criterion_group, criterion_main, Criterion}; +use ic_state_machine_tests::StateMachine; +use ic_types::{Cycles, PrincipalId}; + +const CANISTERS_PER_CREATOR: usize = 100; + +fn bytes_to_str(bytes: &[u8]) -> String { + bytes + .iter() + .map(|byte| byte.to_string()) + .collect::>() + .join(",") +} + +fn setup_env(total_canisters: usize) -> StateMachine { + assert!(total_canisters >= CANISTERS_PER_CREATOR); + assert!(total_canisters % CANISTERS_PER_CREATOR == 0); + + let env = StateMachine::new(); + let wasm = canister_test::Project::cargo_bin_maybe_from_env("canister_creator_canister", &[]); + + let mut creator_ids = vec![]; + let num_creators = total_canisters / CANISTERS_PER_CREATOR; + println!("==> Creating {num_creators} creator canisters..."); + for _ in 1..=num_creators { + let canister_id = env + .install_canister_with_cycles(wasm.clone().bytes(), vec![], None, Cycles::new(1 << 64)) + .unwrap(); + creator_ids.push(canister_id); + } + + println!("==> Creating {total_canisters} load simulator canisters..."); + let mut ingress_ids = vec![]; + for canister_id in creator_ids.iter() { + let ingress_id = env.send_ingress( + PrincipalId::new_anonymous(), + *canister_id, + "create_canisters", + format!("{}", CANISTERS_PER_CREATOR).as_bytes().to_vec(), + ); + ingress_ids.push(ingress_id); + } + + println!("==> Awaiting creation to finish..."); + for ingress_id in ingress_ids.into_iter() { + env.await_ingress(ingress_id, 1_000).unwrap(); + } + + println!("==> Installing {total_canisters} load simulators..."); + let wasm = canister_test::Project::cargo_bin_maybe_from_env("load_simulator_canister", &[]); + let mut ingress_ids = vec![]; + for canister_id in creator_ids.iter() { + let ingress_id = env.send_ingress( + PrincipalId::new_anonymous(), + *canister_id, + "install_code", + format!( + r#"[[{}],[{}]]"#, + bytes_to_str(&wasm.clone().bytes()), + bytes_to_str(&[]) + ) + .as_bytes() + .to_vec(), + ); + ingress_ids.push(ingress_id); + } + + println!("==> Awaiting installation to finish..."); + for ingress_id in ingress_ids.into_iter() { + env.await_ingress(ingress_id, 1_000).unwrap(); + } + env.set_checkpoints_enabled(false); + + env +} + +fn run_load_simulator_canisters(total_canisters: usize, rounds: u64, c: &mut Criterion) { + let mut group = c.benchmark_group("Load simulator"); + group + .throughput(criterion::Throughput::Elements(rounds)) + .bench_function( + format!("{total_canisters} canisters/{rounds} rounds"), + |bench| { + bench.iter_batched( + || setup_env(total_canisters), + |env| { + for _ in 1..=rounds { + env.advance_time(Duration::from_secs(1)); + env.tick(); + } + }, + criterion::BatchSize::PerIteration, + ); + }, + ); +} + +fn load_simulator_canisters_bench_200(c: &mut Criterion) { + run_load_simulator_canisters(200, 10, c); +} +fn load_simulator_canisters_bench_3000(c: &mut Criterion) { + run_load_simulator_canisters(3_000, 100, c); +} +fn load_simulator_canisters_bench_4000(c: &mut Criterion) { + run_load_simulator_canisters(4_000, 100, c); +} +fn load_simulator_canisters_bench_4500(c: &mut Criterion) { + run_load_simulator_canisters(4_500, 100, c); +} +fn load_simulator_canisters_bench_5000(c: &mut Criterion) { + run_load_simulator_canisters(5_000, 100, c); +} +fn load_simulator_canisters_bench_5500(c: &mut Criterion) { + run_load_simulator_canisters(5_500, 100, c); +} +fn load_simulator_canisters_bench_6000(c: &mut Criterion) { + run_load_simulator_canisters(6_000, 100, c); +} +fn load_simulator_canisters_bench_6500(c: &mut Criterion) { + run_load_simulator_canisters(6_500, 100, c); +} +fn load_simulator_canisters_bench_7000(c: &mut Criterion) { + run_load_simulator_canisters(7_000, 100, c); +} +fn load_simulator_canisters_bench_7500(c: &mut Criterion) { + run_load_simulator_canisters(7_500, 100, c); +} +fn load_simulator_canisters_bench_8000(c: &mut Criterion) { + run_load_simulator_canisters(8_000, 100, c); +} +fn load_simulator_canisters_bench_8500(c: &mut Criterion) { + run_load_simulator_canisters(8_500, 100, c); +} + +criterion_group!( + benchmarks, + load_simulator_canisters_bench_200, + load_simulator_canisters_bench_3000, + load_simulator_canisters_bench_4000, + load_simulator_canisters_bench_4500, + load_simulator_canisters_bench_5000, + load_simulator_canisters_bench_5500, + load_simulator_canisters_bench_6000, + load_simulator_canisters_bench_6500, + load_simulator_canisters_bench_7000, + load_simulator_canisters_bench_7500, + load_simulator_canisters_bench_8000, + load_simulator_canisters_bench_8500, +); +criterion_main!(benchmarks); diff --git a/rs/execution_environment/src/execution_environment.rs b/rs/execution_environment/src/execution_environment.rs index 2652384bead..f8ca94166a4 100644 --- a/rs/execution_environment/src/execution_environment.rs +++ b/rs/execution_environment/src/execution_environment.rs @@ -15,7 +15,6 @@ use crate::{ hypervisor::Hypervisor, ic00_permissions::Ic00MethodPermissions, metrics::{CallTreeMetrics, CallTreeMetricsImpl, IngressFilterMetrics}, - RoundSchedule, }; use candid::Encode; use ic_base_types::PrincipalId; @@ -3214,7 +3213,6 @@ impl ExecutionEnvironment { .task_queue .replace_paused_with_aborted_task(aborted_task); } - RoundSchedule::apply_priority_credit(canister); let canister_id = canister.canister_id(); canister.system_state.apply_ingress_induction_cycles_debit( canister_id, diff --git a/rs/execution_environment/src/scheduler/tests.rs b/rs/execution_environment/src/scheduler/tests.rs index bed03d40a8e..9ca7377610c 100644 --- a/rs/execution_environment/src/scheduler/tests.rs +++ b/rs/execution_environment/src/scheduler/tests.rs @@ -4,6 +4,7 @@ use super::{ }; #[cfg(test)] use crate::scheduler::test_utilities::{on_response, other_side}; +use assert_matches::assert_matches; use candid::Encode; use ic00::{ CanisterHttpRequestArgs, HttpMethod, SignWithECDSAArgs, TransformContext, TransformFunc, @@ -34,13 +35,14 @@ use ic_test_utilities_state::{get_running_canister, get_stopped_canister, get_st use ic_test_utilities_types::messages::RequestBuilder; use ic_types::{ batch::ConsensusResponse, + ingress::IngressStatus, messages::{ CallbackId, CanisterMessageOrTask, CanisterTask, Payload, RejectContext, StopCanisterCallId, StopCanisterContext, MAX_RESPONSE_COUNT_BYTES, }, methods::SystemMethod, time::{expiry_time_from_now, UNIX_EPOCH}, - ComputeAllocation, Cycles, Height, NumBytes, + ComputeAllocation, Cycles, Height, LongExecutionMode, NumBytes, }; use ic_types_test_utils::ids::{canister_test_id, message_test_id, subnet_test_id, user_test_id}; use proptest::prelude::*; @@ -1786,6 +1788,103 @@ fn max_canisters_per_round() { assert_eq!(executed_canisters, 200 + 2 * 5); } +#[test] +fn scheduler_long_execution_progress_across_checkpoints() { + let scheduler_cores = 2; + let slice_instructions = 2; + let message_instructions = 40; + + let num_canisters = scheduler_cores; + + let mut test = SchedulerTestBuilder::new() + .with_scheduler_config(SchedulerConfig { + scheduler_cores, + max_instructions_per_round: slice_instructions.into(), + max_instructions_per_message: message_instructions.into(), + max_instructions_per_message_without_dts: slice_instructions.into(), + max_instructions_per_slice: slice_instructions.into(), + instruction_overhead_per_execution: 0.into(), + instruction_overhead_per_canister: 0.into(), + ..SchedulerConfig::application_subnet() + }) + .build(); + + let penalized_long_id = test.create_canister(); + let other_long_id = test.create_canister(); + let mut canister_ids = vec![]; + for _ in 0..num_canisters { + let canister_id = test.create_canister(); + canister_ids.push(canister_id); + } + + // Penalize canister for a long execution. + let message_id = test.send_ingress(penalized_long_id, ingress(message_instructions)); + assert_eq!(test.ingress_status(&message_id), IngressStatus::Unknown); + for _ in 0..message_instructions / slice_instructions { + test.execute_round(ExecutionRoundType::OrdinaryRound); + } + assert_matches!( + test.ingress_status(&message_id), + IngressStatus::Known { + // Canister did not reply. + state: IngressState::Failed(_), + .. + } + ); + // Assert penalized canister accumulated priority is lower. + let penalized = test.state().canister_state(&penalized_long_id).unwrap(); + let other = test.state().canister_state(&other_long_id).unwrap(); + assert!( + penalized.scheduler_state.accumulated_priority < other.scheduler_state.accumulated_priority + ); + + // Start another long execution on the penalized canister. + test.send_ingress(penalized_long_id, ingress(message_instructions)); + test.execute_round(ExecutionRoundType::OrdinaryRound); + test.execute_round(ExecutionRoundType::OrdinaryRound); + // Assert the LEM is prioritized. + let penalized = test.state().canister_state(&penalized_long_id).unwrap(); + assert_eq!( + penalized.scheduler_state.long_execution_mode, + LongExecutionMode::Prioritized + ); + + // Start a long execution on another non-penalized canister. + test.send_ingress(other_long_id, ingress(message_instructions)); + test.execute_round(ExecutionRoundType::OrdinaryRound); + test.execute_round(ExecutionRoundType::OrdinaryRound); + // Assert the LEM is opportunistic. + let other = test.state().canister_state(&other_long_id).unwrap(); + assert_eq!( + other.scheduler_state.long_execution_mode, + LongExecutionMode::Opportunistic + ); + + // Abort both canisters on checkpoint. + test.execute_round(ExecutionRoundType::CheckpointRound); + + // Assert penalized canister accumulated priority is still lower. + let penalized = test.state().canister_state(&penalized_long_id).unwrap(); + let other = test.state().canister_state(&other_long_id).unwrap(); + assert!( + penalized.scheduler_state.accumulated_priority < other.scheduler_state.accumulated_priority + ); + let penalized_executed_before = penalized.system_state.canister_metrics.executed; + + // Send a bunch of messages. + for canister_id in &canister_ids { + test.send_ingress(*canister_id, ingress(slice_instructions)); + } + + // Assert that after the checkpoint the penalized canister continues its long execution. + test.execute_round(ExecutionRoundType::OrdinaryRound); + let penalized = test.state().canister_state(&penalized_long_id).unwrap(); + assert_eq!( + penalized_executed_before + 1, + penalized.system_state.canister_metrics.executed + ); +} + #[test] fn can_fully_execute_canisters_deterministically_until_out_of_cycles() { // In this test we have 5 canisters with 10 input messages each. The maximum diff --git a/rs/ledger_suite/common/ledger_canister_core/src/runtime.rs b/rs/ledger_suite/common/ledger_canister_core/src/runtime.rs index 7b2503b6c5b..4c12cbaf632 100644 --- a/rs/ledger_suite/common/ledger_canister_core/src/runtime.rs +++ b/rs/ledger_suite/common/ledger_canister_core/src/runtime.rs @@ -22,14 +22,14 @@ pub trait Runtime { Out: for<'a> ArgumentDecoder<'a>; } -/// Returns the total amount of memory (heap, stable memory, etc) that has been allocated. +/// Returns the amount of heap memory in bytes that has been allocated. #[cfg(target_arch = "wasm32")] -pub fn total_memory_size_bytes() -> usize { +pub fn heap_memory_size_bytes() -> usize { const WASM_PAGE_SIZE_BYTES: usize = 65536; core::arch::wasm32::memory_size(0) * WASM_PAGE_SIZE_BYTES } #[cfg(not(any(target_arch = "wasm32")))] -pub fn total_memory_size_bytes() -> usize { +pub fn heap_memory_size_bytes() -> usize { 0 } diff --git a/rs/ledger_suite/icp/archive/src/main.rs b/rs/ledger_suite/icp/archive/src/main.rs index 02601d7e012..a668a8cef2b 100644 --- a/rs/ledger_suite/icp/archive/src/main.rs +++ b/rs/ledger_suite/icp/archive/src/main.rs @@ -4,7 +4,7 @@ use dfn_core::api::{caller, print, stable_memory_size_in_pages}; use dfn_core::{over_init, stable, BytesS}; use dfn_protobuf::protobuf; use ic_ledger_canister_core::range_utils; -use ic_ledger_canister_core::runtime::total_memory_size_bytes; +use ic_ledger_canister_core::runtime::heap_memory_size_bytes; use ic_ledger_core::block::{BlockIndex, BlockType, EncodedBlock}; use ic_metrics_encoder::MetricsEncoder; use icp_ledger::{ @@ -251,14 +251,14 @@ fn encode_metrics(w: &mut MetricsEncoder>) -> std::io::Result<()> { "Size of the stable memory allocated by this canister measured in 64K Wasm pages.", )?; w.encode_gauge( - "archive_node_stable_memory_bytes", + "stable_memory_bytes", (stable_memory_size_in_pages() * 64 * 1024) as f64, "Size of the stable memory allocated by this canister measured in bytes.", )?; w.encode_gauge( - "archive_node_total_memory_bytes", - total_memory_size_bytes() as f64, - "Total amount of memory (heap, stable memory, etc) that has been allocated by this canister.", + "heap_memory_bytes", + heap_memory_size_bytes() as f64, + "Size of the heap memory allocated by this canister measured in bytes.", )?; w.encode_gauge( "archive_node_last_upgrade_time_seconds", diff --git a/rs/ledger_suite/icp/index/src/main.rs b/rs/ledger_suite/icp/index/src/main.rs index 5d4bc2983d9..3447e8fa374 100644 --- a/rs/ledger_suite/icp/index/src/main.rs +++ b/rs/ledger_suite/icp/index/src/main.rs @@ -11,7 +11,7 @@ use ic_icp_index::{ Priority, SettledTransaction, SettledTransactionWithId, Status, }; use ic_icrc1_index_ng::GetAccountTransactionsArgs; -use ic_ledger_canister_core::runtime::total_memory_size_bytes; +use ic_ledger_canister_core::runtime::heap_memory_size_bytes; use ic_ledger_core::block::{BlockType, EncodedBlock}; use ic_stable_structures::memory_manager::{MemoryId, VirtualMemory}; use ic_stable_structures::StableBTreeMap; @@ -536,14 +536,14 @@ pub fn encode_metrics(w: &mut ic_metrics_encoder::MetricsEncoder>) -> st "Size of the stable memory allocated by this canister measured in 64K Wasm pages.", )?; w.encode_gauge( - "index_stable_memory_bytes", + "stable_memory_bytes", (ic_cdk::api::stable::stable_size() * 64 * 1024) as f64, - "Size of the stable memory allocated by this canister.", + "Size of the stable memory allocated by this canister measured in bytes.", )?; w.encode_gauge( - "index_total_memory_bytes", - total_memory_size_bytes() as f64, - "Total amount of memory (heap, stable memory, etc) that has been allocated by this canister.", + "heap_memory_bytes", + heap_memory_size_bytes() as f64, + "Size of the heap memory allocated by this canister measured in bytes.", )?; let cycle_balance = ic_cdk::api::canister_balance128() as f64; diff --git a/rs/ledger_suite/icp/index/tests/tests.rs b/rs/ledger_suite/icp/index/tests/tests.rs index 13f5543b7e2..7c0427d02a0 100644 --- a/rs/ledger_suite/icp/index/tests/tests.rs +++ b/rs/ledger_suite/icp/index/tests/tests.rs @@ -1700,8 +1700,8 @@ mod metrics { use ic_icp_index::InitArg; #[test] - fn should_export_total_memory_usage_bytes_metrics() { - ic_ledger_suite_state_machine_tests::metrics::assert_existence_of_index_total_memory_bytes_metric( + fn should_export_heap_memory_usage_bytes_metrics() { + ic_ledger_suite_state_machine_tests::metrics::assert_existence_of_index_heap_memory_bytes_metric( index_wasm(), encode_init_args, ); diff --git a/rs/ledger_suite/icp/ledger/src/main.rs b/rs/ledger_suite/icp/ledger/src/main.rs index 3710370f15c..25b288826d6 100644 --- a/rs/ledger_suite/icp/ledger/src/main.rs +++ b/rs/ledger_suite/icp/ledger/src/main.rs @@ -11,7 +11,7 @@ use dfn_protobuf::protobuf; use ic_base_types::CanisterId; use ic_canister_log::{LogEntry, Sink}; use ic_icrc1::endpoints::{convert_transfer_error, StandardRecord}; -use ic_ledger_canister_core::runtime::total_memory_size_bytes; +use ic_ledger_canister_core::runtime::heap_memory_size_bytes; use ic_ledger_canister_core::{ archive::{Archive, ArchiveOptions}, ledger::{ @@ -1365,14 +1365,14 @@ fn encode_metrics(w: &mut ic_metrics_encoder::MetricsEncoder>) -> std::i "Size of the stable memory allocated by this canister measured in 64K Wasm pages.", )?; w.encode_gauge( - "ledger_stable_memory_bytes", + "stable_memory_bytes", (dfn_core::api::stable_memory_size_in_pages() * 64 * 1024) as f64, - "Size of the stable memory allocated by this canister.", + "Size of the stable memory allocated by this canister measured in bytes.", )?; w.encode_gauge( - "ledger_total_memory_bytes", - total_memory_size_bytes() as f64, - "Total amount of memory (heap, stable memory, etc) that has been allocated by this canister.", + "heap_memory_bytes", + heap_memory_size_bytes() as f64, + "Size of the stable memory allocated by this canister measured in bytes.", )?; w.encode_gauge( "ledger_transactions_by_hash_cache_entries", diff --git a/rs/ledger_suite/icp/ledger/tests/tests.rs b/rs/ledger_suite/icp/ledger/tests/tests.rs index f0e902d3d2f..f0ea89053fd 100644 --- a/rs/ledger_suite/icp/ledger/tests/tests.rs +++ b/rs/ledger_suite/icp/ledger/tests/tests.rs @@ -1724,8 +1724,8 @@ mod metrics { } #[test] - fn should_export_total_memory_usage_metrics() { - ic_ledger_suite_state_machine_tests::metrics::assert_existence_of_ledger_total_memory_bytes_metric( + fn should_export_ledger_heap_memory_usage_metrics() { + ic_ledger_suite_state_machine_tests::metrics::assert_existence_of_heap_memory_bytes_metric( ledger_wasm(), encode_init_args, ); diff --git a/rs/ledger_suite/icrc1/archive/src/main.rs b/rs/ledger_suite/icrc1/archive/src/main.rs index df1007c1e69..a8d8eb3c5e1 100644 --- a/rs/ledger_suite/icrc1/archive/src/main.rs +++ b/rs/ledger_suite/icrc1/archive/src/main.rs @@ -2,7 +2,7 @@ use candid::{candid_method, Principal}; use ic_canisters_http_types::{HttpRequest, HttpResponse, HttpResponseBuilder}; use ic_cdk_macros::{init, post_upgrade, query, update}; use ic_icrc1::{blocks::encoded_block_to_generic_block, Block}; -use ic_ledger_canister_core::runtime::total_memory_size_bytes; +use ic_ledger_canister_core::runtime::heap_memory_size_bytes; use ic_ledger_core::block::{BlockIndex, BlockType, EncodedBlock}; use ic_stable_structures::memory_manager::{MemoryId, VirtualMemory}; use ic_stable_structures::{ @@ -364,14 +364,14 @@ fn encode_metrics(w: &mut ic_metrics_encoder::MetricsEncoder>) -> std::i "Size of the stable memory allocated by this canister measured in 64K Wasm pages.", )?; w.encode_gauge( - "archive_stable_memory_bytes", + "stable_memory_bytes", ic_cdk::api::stable::stable_size() as f64 * 65536f64, - "Size of the stable memory allocated by this canister.", + "Size of the stable memory allocated by this canister measured in bytes.", )?; w.encode_gauge( - "archive_total_memory_bytes", - total_memory_size_bytes() as f64, - "Total amount of memory (heap, stable memory, etc) that has been allocated by this canister.", + "heap_memory_bytes", + heap_memory_size_bytes() as f64, + "Size of the heap memory allocated by this canister measured in bytes.", )?; let cycle_balance = ic_cdk::api::canister_balance128() as f64; diff --git a/rs/ledger_suite/icrc1/index-ng/src/main.rs b/rs/ledger_suite/icrc1/index-ng/src/main.rs index 2d6b9af25e4..644cd22bdaf 100644 --- a/rs/ledger_suite/icrc1/index-ng/src/main.rs +++ b/rs/ledger_suite/icrc1/index-ng/src/main.rs @@ -14,7 +14,7 @@ use ic_icrc1_index_ng::{ GetAccountTransactionsResult, GetBlocksMethod, IndexArg, InitArg, ListSubaccountsArgs, Log, LogEntry, Status, TransactionWithId, UpgradeArg, DEFAULT_MAX_BLOCKS_PER_RESPONSE, }; -use ic_ledger_canister_core::runtime::total_memory_size_bytes; +use ic_ledger_canister_core::runtime::heap_memory_size_bytes; use ic_ledger_core::block::{BlockIndex as BlockIndex64, BlockType, EncodedBlock}; use ic_ledger_core::tokens::{CheckedAdd, CheckedSub, Zero}; use ic_stable_structures::memory_manager::{MemoryId, VirtualMemory}; @@ -1121,14 +1121,14 @@ pub fn encode_metrics(w: &mut ic_metrics_encoder::MetricsEncoder>) -> st "Size of the stable memory allocated by this canister measured in 64K Wasm pages.", )?; w.encode_gauge( - "index_stable_memory_bytes", + "stable_memory_bytes", (ic_cdk::api::stable::stable_size() * 64 * 1024) as f64, - "Size of the stable memory allocated by this canister.", + "Size of the stable memory allocated by this canister measured in bytes.", )?; w.encode_gauge( - "index_total_memory_bytes", - total_memory_size_bytes() as f64, - "Total amount of memory (heap, stable memory, etc) that has been allocated by this canister.", + "heap_memory_bytes", + heap_memory_size_bytes() as f64, + "Size of the heap memory allocated by this canister measured in bytes.", )?; let cycle_balance = ic_cdk::api::canister_balance128() as f64; diff --git a/rs/ledger_suite/icrc1/index-ng/tests/tests.rs b/rs/ledger_suite/icrc1/index-ng/tests/tests.rs index 44ab91f6316..80369d5d978 100644 --- a/rs/ledger_suite/icrc1/index-ng/tests/tests.rs +++ b/rs/ledger_suite/icrc1/index-ng/tests/tests.rs @@ -1352,8 +1352,8 @@ mod metrics { use ic_icrc1_index_ng::InitArg; #[test] - fn should_export_total_memory_usage_bytes_metrics() { - ic_ledger_suite_state_machine_tests::metrics::assert_existence_of_index_total_memory_bytes_metric( + fn should_export_heap_memory_usage_bytes_metrics() { + ic_ledger_suite_state_machine_tests::metrics::assert_existence_of_index_heap_memory_bytes_metric( index_wasm(), encode_init_args, ); diff --git a/rs/ledger_suite/icrc1/index/src/lib.rs b/rs/ledger_suite/icrc1/index/src/lib.rs index 37bd5de3832..6ba18e97592 100644 --- a/rs/ledger_suite/icrc1/index/src/lib.rs +++ b/rs/ledger_suite/icrc1/index/src/lib.rs @@ -2,7 +2,7 @@ use candid::{CandidType, Nat}; use ic_base_types::{CanisterId, PrincipalId}; use ic_canister_profiler::{measure_span, SpanStats}; use ic_cdk::api::stable::{StableReader, StableWriter}; -use ic_ledger_canister_core::runtime::total_memory_size_bytes; +use ic_ledger_canister_core::runtime::heap_memory_size_bytes; use icrc_ledger_types::icrc1::transfer::BlockIndex; use icrc_ledger_types::icrc3::archive::QueryTxArchiveFn; use icrc_ledger_types::icrc3::transactions::{ @@ -451,14 +451,14 @@ pub fn encode_metrics(w: &mut ic_metrics_encoder::MetricsEncoder>) -> st "Size of the stable memory allocated by this canister measured in 64K Wasm pages.", )?; w.encode_gauge( - "index_stable_memory_bytes", + "stable_memory_bytes", (ic_cdk::api::stable::stable_size() * 64 * 1024) as f64, - "Size of the stable memory allocated by this canister.", + "Size of the stable memory allocated by this canister measured in bytes.", )?; w.encode_gauge( - "index_total_memory_bytes", - total_memory_size_bytes() as f64, - "Total amount of memory (heap, stable memory, etc) that has been allocated by this canister.", + "heap_memory_bytes", + heap_memory_size_bytes() as f64, + "Size of the heap memory allocated by this canister measured in bytes.", )?; let cycle_balance = ic_cdk::api::canister_balance128() as f64; diff --git a/rs/ledger_suite/icrc1/index/tests/tests.rs b/rs/ledger_suite/icrc1/index/tests/tests.rs index 81fab8cdd6c..ff213e67fdb 100644 --- a/rs/ledger_suite/icrc1/index/tests/tests.rs +++ b/rs/ledger_suite/icrc1/index/tests/tests.rs @@ -673,8 +673,8 @@ mod metrics { use ic_icrc1_index::InitArgs; #[test] - fn should_export_total_memory_usage_bytes_metrics() { - ic_ledger_suite_state_machine_tests::metrics::assert_existence_of_index_total_memory_bytes_metric( + fn should_export_heap_memory_usage_bytes_metrics() { + ic_ledger_suite_state_machine_tests::metrics::assert_existence_of_index_heap_memory_bytes_metric( index_wasm(), encode_init_args, ); diff --git a/rs/ledger_suite/icrc1/ledger/src/main.rs b/rs/ledger_suite/icrc1/ledger/src/main.rs index 7c1465560aa..43f23d31c44 100644 --- a/rs/ledger_suite/icrc1/ledger/src/main.rs +++ b/rs/ledger_suite/icrc1/ledger/src/main.rs @@ -20,7 +20,7 @@ use ic_ledger_canister_core::ledger::{ apply_transaction, archive_blocks, LedgerAccess, LedgerContext, LedgerData, TransferError as CoreTransferError, }; -use ic_ledger_canister_core::runtime::total_memory_size_bytes; +use ic_ledger_canister_core::runtime::heap_memory_size_bytes; use ic_ledger_core::block::BlockIndex; use ic_ledger_core::timestamp::TimeStamp; use ic_ledger_core::tokens::Zero; @@ -237,14 +237,14 @@ fn encode_metrics(w: &mut ic_metrics_encoder::MetricsEncoder>) -> std::i "Size of the stable memory allocated by this canister measured in 64K Wasm pages.", )?; w.encode_gauge( - "ledger_stable_memory_bytes", + "stable_memory_bytes", (ic_cdk::api::stable::stable_size() * 64 * 1024) as f64, - "Size of the stable memory allocated by this canister.", + "Size of the stable memory allocated by this canister measured in bytes.", )?; w.encode_gauge( - "ledger_total_memory_bytes", - total_memory_size_bytes() as f64, - "Total amount of memory (heap, stable memory, etc) that has been allocated by this canister.", + "heap_memory_bytes", + heap_memory_size_bytes() as f64, + "Size of the heap memory allocated by this canister measured in bytes.", )?; let cycle_balance = ic_cdk::api::canister_balance128() as f64; diff --git a/rs/ledger_suite/icrc1/ledger/tests/tests.rs b/rs/ledger_suite/icrc1/ledger/tests/tests.rs index 910f18fac73..13902f58eb7 100644 --- a/rs/ledger_suite/icrc1/ledger/tests/tests.rs +++ b/rs/ledger_suite/icrc1/ledger/tests/tests.rs @@ -479,8 +479,8 @@ mod metrics { } #[test] - fn should_export_total_memory_usage_metrics() { - ic_ledger_suite_state_machine_tests::metrics::assert_existence_of_ledger_total_memory_bytes_metric( + fn should_export_heap_memory_usage_metrics() { + ic_ledger_suite_state_machine_tests::metrics::assert_existence_of_heap_memory_bytes_metric( ledger_wasm(), encode_init_args, ); diff --git a/rs/ledger_suite/tests/sm-tests/src/metrics.rs b/rs/ledger_suite/tests/sm-tests/src/metrics.rs index 5f6fd4257ee..1788c2cc3fa 100644 --- a/rs/ledger_suite/tests/sm-tests/src/metrics.rs +++ b/rs/ledger_suite/tests/sm-tests/src/metrics.rs @@ -10,13 +10,13 @@ pub enum LedgerSuiteType { ICRC, } -pub fn assert_existence_of_index_total_memory_bytes_metric( +pub fn assert_existence_of_index_heap_memory_bytes_metric( index_wasm: Vec, encode_init_args: fn(Principal) -> T, ) where T: CandidType, { - const METRIC: &str = "index_total_memory_bytes"; + const METRIC: &str = "heap_memory_bytes"; let env = StateMachine::new(); let ledger_id = CanisterId::from_u64(100); @@ -42,13 +42,13 @@ pub fn assert_existence_of_ledger_num_archives_metric( assert_existence_of_metric(&env, canister_id, METRIC); } -pub fn assert_existence_of_ledger_total_memory_bytes_metric( +pub fn assert_existence_of_heap_memory_bytes_metric( ledger_wasm: Vec, encode_init_args: fn(InitArgs) -> T, ) where T: CandidType, { - const METRIC: &str = "ledger_total_memory_bytes"; + const METRIC: &str = "heap_memory_bytes"; let (env, canister_id) = setup(ledger_wasm, encode_init_args, vec![]); diff --git a/rs/messaging/src/routing/stream_handler.rs b/rs/messaging/src/routing/stream_handler.rs index 321f4708995..62dda3c0ed7 100644 --- a/rs/messaging/src/routing/stream_handler.rs +++ b/rs/messaging/src/routing/stream_handler.rs @@ -10,7 +10,7 @@ use ic_interfaces::messaging::{ LABEL_VALUE_CANISTER_OUT_OF_CYCLES, LABEL_VALUE_CANISTER_STOPPED, LABEL_VALUE_CANISTER_STOPPING, LABEL_VALUE_INVALID_MANAGEMENT_PAYLOAD, }; -use ic_logger::{debug, error, fatal, info, trace, ReplicaLogger}; +use ic_logger::{debug, error, info, trace, ReplicaLogger}; use ic_metrics::{ buckets::{add_bucket, decimal_buckets}, MetricsRegistry, @@ -873,22 +873,13 @@ impl StreamHandlerImpl { return Some((RejectReason::CanisterMigrating, msg)); } RequestOrResponse::Response(response) => { - if state.metadata.certification_version >= CertificationVersion::V9 { - debug!( - self.log, - "Inducting response failed: Canister {} is migrating\n{:?}", - response.originator, - response - ); - return Some((RejectReason::CanisterMigrating, msg)); - } else { - fatal!( - self.log, - "Canister {} is being migrated, but cannot produce reject signal for response {:?}", - response.originator, - response - ); - } + debug!( + self.log, + "Inducting response failed: Canister {} is migrating\n{:?}", + response.originator, + response + ); + return Some((RejectReason::CanisterMigrating, msg)); } } } diff --git a/rs/nervous_system/common/src/lib.rs b/rs/nervous_system/common/src/lib.rs index 7a418649ead..04dc8386068 100644 --- a/rs/nervous_system/common/src/lib.rs +++ b/rs/nervous_system/common/src/lib.rs @@ -778,6 +778,16 @@ fn checked_div_mod(dividend: usize, divisor: usize) -> Option<(usize, usize)> { Some((quotient, remainder)) } +/// Converts a sha256 hash into a hex string representation +pub fn hash_to_hex_string(hash: &[u8]) -> String { + use std::fmt::Write; + let mut result_hash = String::new(); + for b in hash { + let _ = write!(result_hash, "{:02x}", b); + } + result_hash +} + #[cfg(test)] mod serve_logs_tests; diff --git a/rs/nervous_system/common/src/tests.rs b/rs/nervous_system/common/src/tests.rs index 430d0f35efd..26c455919d9 100644 --- a/rs/nervous_system/common/src/tests.rs +++ b/rs/nervous_system/common/src/tests.rs @@ -1,6 +1,6 @@ use super::*; -use crate::ledger::compute_neuron_staking_subaccount_bytes; +use crate::{hash_to_hex_string, ledger::compute_neuron_staking_subaccount_bytes}; use ic_base_types::PrincipalId; use ic_crypto_sha2::Sha256; @@ -46,3 +46,21 @@ fn test_compute_neuron_staking_subaccount_bytes() { hash ); } + +#[test] +fn test_hash_to_hex_string_empty() { + let empty: [u8; 0] = []; + assert_eq!(hash_to_hex_string(&empty), ""); +} + +#[test] +fn test_hash_to_hex_string_single_byte() { + let single = [0xAB]; + assert_eq!(hash_to_hex_string(&single), "ab"); +} + +#[test] +fn test_hash_to_hex_string_multiple_bytes() { + let bytes = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]; + assert_eq!(hash_to_hex_string(&bytes), "123456789abcdef0"); +} diff --git a/rs/nervous_system/integration_tests/tests/advance_target_version.rs b/rs/nervous_system/integration_tests/tests/advance_target_version.rs index 0735af5305d..236e162092d 100644 --- a/rs/nervous_system/integration_tests/tests/advance_target_version.rs +++ b/rs/nervous_system/integration_tests/tests/advance_target_version.rs @@ -250,4 +250,74 @@ async fn test_get_upgrade_journal() { &expected_upgrade_journal_entries, ) .await; + + // Check that the target version is set to the new version. + { + let sns_pb::GetUpgradeJournalResponse { target_version, .. } = + sns::governance::get_upgrade_journal(&pocket_ic, sns.governance.canister_id).await; + + assert_eq!(target_version, Some(new_sns_version_2.clone())); + } + + await_with_timeout( + &pocket_ic, + UPGRADE_STEPS_INTERVAL_REFRESH_BACKOFF_SECONDS, + |pocket_ic| async { + sns::governance::get_upgrade_journal(pocket_ic, sns.governance.canister_id) + .await + .deployed_version + }, + &Some(new_sns_version_2.clone()), + ) + .await + .unwrap(); + + // Check that the deployed version is now set to the new version. + { + let sns_pb::GetUpgradeJournalResponse { + deployed_version, .. + } = sns::governance::get_upgrade_journal(&pocket_ic, sns.governance.canister_id).await; + + assert_eq!(deployed_version, Some(new_sns_version_2.clone())); + } + + // Check that the upgrade journal contains the correct entries. + { + expected_upgrade_journal_entries.push( + sns_pb::upgrade_journal_entry::Event::UpgradeStarted( + sns_pb::upgrade_journal_entry::UpgradeStarted::from_behind_target( + initial_sns_version.clone(), + new_sns_version_1.clone(), + ), + ), + ); + + expected_upgrade_journal_entries.push( + sns_pb::upgrade_journal_entry::Event::UpgradeOutcome( + sns_pb::upgrade_journal_entry::UpgradeOutcome::success(None), + ), + ); + + expected_upgrade_journal_entries.push( + sns_pb::upgrade_journal_entry::Event::UpgradeStarted( + sns_pb::upgrade_journal_entry::UpgradeStarted::from_behind_target( + new_sns_version_1.clone(), + new_sns_version_2.clone(), + ), + ), + ); + + expected_upgrade_journal_entries.push( + sns_pb::upgrade_journal_entry::Event::UpgradeOutcome( + sns_pb::upgrade_journal_entry::UpgradeOutcome::success(None), + ), + ); + + assert_upgrade_journal( + &pocket_ic, + sns.governance, + &expected_upgrade_journal_entries, + ) + .await; + } } diff --git a/rs/nns/governance/src/governance.rs b/rs/nns/governance/src/governance.rs index 203fba4027b..0be25b89192 100644 --- a/rs/nns/governance/src/governance.rs +++ b/rs/nns/governance/src/governance.rs @@ -190,7 +190,7 @@ pub const MAX_NEURON_RECENT_BALLOTS: usize = 100; pub const REWARD_DISTRIBUTION_PERIOD_SECONDS: u64 = ONE_DAY_SECONDS; /// The maximum number of neurons supported. -pub const MAX_NUMBER_OF_NEURONS: usize = 350_000; +pub const MAX_NUMBER_OF_NEURONS: usize = 380_000; // Spawning is exempted from rate limiting, so we don't need large of a limit here. pub const MAX_SUSTAINED_NEURONS_PER_HOUR: u64 = 15; diff --git a/rs/nns/sns-wasm/src/pb/mod.rs b/rs/nns/sns-wasm/src/pb/mod.rs index 03dca8a2e7d..08d8816178f 100644 --- a/rs/nns/sns-wasm/src/pb/mod.rs +++ b/rs/nns/sns-wasm/src/pb/mod.rs @@ -13,26 +13,13 @@ use crate::{ use ic_base_types::CanisterId; use ic_cdk::api::stable::StableMemory; use ic_crypto_sha2::Sha256; -use std::{ - collections::HashMap, - convert::TryFrom, - fmt::{Display, Write}, - str::FromStr, -}; +use ic_nervous_system_common::hash_to_hex_string; +use std::{collections::HashMap, convert::TryFrom, str::FromStr}; #[allow(clippy::all)] #[path = "../gen/ic_sns_wasm.pb.v1.rs"] pub mod v1; -/// Converts a sha256 hash into a hex string representation -pub fn hash_to_hex_string(hash: &[u8; 32]) -> String { - let mut result_hash = String::new(); - for b in hash { - let _ = write!(result_hash, "{:02X}", b); - } - result_hash -} - impl AddWasmResponse { pub fn error(message: String) -> Self { Self { @@ -123,7 +110,7 @@ impl From for GetNextSnsVersionResponse { } } -impl Display for SnsVersion { +impl std::fmt::Display for SnsVersion { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut versions_str = HashMap::<&str, String>::new(); diff --git a/rs/nns/sns-wasm/src/sns_wasm.rs b/rs/nns/sns-wasm/src/sns_wasm.rs index e0371ae2ace..c9949152f84 100644 --- a/rs/nns/sns-wasm/src/sns_wasm.rs +++ b/rs/nns/sns-wasm/src/sns_wasm.rs @@ -1,22 +1,18 @@ use crate::{ canister_api::CanisterApi, - pb::{ - hash_to_hex_string, - v1::{ - add_wasm_response, AddWasmRequest, AddWasmResponse, DappCanistersTransferResult, - DeployNewSnsRequest, DeployNewSnsResponse, DeployedSns, - GetDeployedSnsByProposalIdRequest, GetDeployedSnsByProposalIdResponse, - GetNextSnsVersionRequest, GetNextSnsVersionResponse, GetProposalIdThatAddedWasmRequest, - GetProposalIdThatAddedWasmResponse, GetSnsSubnetIdsResponse, - GetWasmMetadataRequest as GetWasmMetadataRequestPb, - GetWasmMetadataResponse as GetWasmMetadataResponsePb, GetWasmRequest, GetWasmResponse, - InsertUpgradePathEntriesRequest, InsertUpgradePathEntriesResponse, - ListDeployedSnsesRequest, ListDeployedSnsesResponse, ListUpgradeStep, - ListUpgradeStepsRequest, ListUpgradeStepsResponse, - MetadataSection as MetadataSectionPb, SnsCanisterIds, SnsCanisterType, SnsUpgrade, - SnsVersion, SnsWasm, SnsWasmError, SnsWasmStableIndex, StableCanisterState, - UpdateSnsSubnetListRequest, UpdateSnsSubnetListResponse, - }, + pb::v1::{ + add_wasm_response, AddWasmRequest, AddWasmResponse, DappCanistersTransferResult, + DeployNewSnsRequest, DeployNewSnsResponse, DeployedSns, GetDeployedSnsByProposalIdRequest, + GetDeployedSnsByProposalIdResponse, GetNextSnsVersionRequest, GetNextSnsVersionResponse, + GetProposalIdThatAddedWasmRequest, GetProposalIdThatAddedWasmResponse, + GetSnsSubnetIdsResponse, GetWasmMetadataRequest as GetWasmMetadataRequestPb, + GetWasmMetadataResponse as GetWasmMetadataResponsePb, GetWasmRequest, GetWasmResponse, + InsertUpgradePathEntriesRequest, InsertUpgradePathEntriesResponse, + ListDeployedSnsesRequest, ListDeployedSnsesResponse, ListUpgradeStep, + ListUpgradeStepsRequest, ListUpgradeStepsResponse, MetadataSection as MetadataSectionPb, + SnsCanisterIds, SnsCanisterType, SnsUpgrade, SnsVersion, SnsWasm, SnsWasmError, + SnsWasmStableIndex, StableCanisterState, UpdateSnsSubnetListRequest, + UpdateSnsSubnetListResponse, }, stable_memory::SnsWasmStableMemory, wasm_metadata::MetadataSection, @@ -25,7 +21,7 @@ use candid::Encode; use ic_base_types::{CanisterId, PrincipalId}; use ic_cdk::api::stable::StableMemory; use ic_nervous_system_clients::canister_id_record::CanisterIdRecord; -use ic_nervous_system_common::{ONE_TRILLION, SNS_CREATION_FEE}; +use ic_nervous_system_common::{hash_to_hex_string, ONE_TRILLION, SNS_CREATION_FEE}; use ic_nervous_system_proto::pb::v1::Canister; use ic_nns_constants::{ DEFAULT_SNS_GOVERNANCE_CANISTER_WASM_MEMORY_LIMIT, diff --git a/rs/orchestrator/src/firewall.rs b/rs/orchestrator/src/firewall.rs index 7d7e974ef0d..87f609f4e4a 100644 --- a/rs/orchestrator/src/firewall.rs +++ b/rs/orchestrator/src/firewall.rs @@ -828,7 +828,6 @@ mod tests { // Make the string parsable by filling the template placeholders with dummy values let cfg = String::from_utf8(CFG_TEMPLATE_BYTES.to_vec()) .unwrap() - .replace("{{ node_index }}", "0") .replace("{{ ipv6_address }}", "::") .replace("{{ backup_retention_time_secs }}", "0") .replace("{{ backup_purging_interval_secs }}", "0") diff --git a/rs/pocket_ic_server/BUILD.bazel b/rs/pocket_ic_server/BUILD.bazel index 87d866b9ee7..3c7d1460dd4 100644 --- a/rs/pocket_ic_server/BUILD.bazel +++ b/rs/pocket_ic_server/BUILD.bazel @@ -153,7 +153,7 @@ rust_library( ]), crate_name = "pocket_ic_server", proc_macro_deps = MACRO_DEPENDENCIES, - version = "6.0.0", + version = "7.0.0", deps = LIB_DEPENDENCIES + [":build_script"], ) diff --git a/rs/pocket_ic_server/CHANGELOG.md b/rs/pocket_ic_server/CHANGELOG.md index a56c970ad93..e4885d76078 100644 --- a/rs/pocket_ic_server/CHANGELOG.md +++ b/rs/pocket_ic_server/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased + + +## 7.0.0 - 2024-11-13 + ### Added - Support for IC Bitcoin API via the management canister if the bitcoin canister is installed as the bitcoin testnet canister (canister ID `g4xu7-jiaaa-aaaan-aaaaq-cai`) on the bitcoin subnet and configured with `Network::Regtest` diff --git a/rs/pocket_ic_server/Cargo.toml b/rs/pocket_ic_server/Cargo.toml index b8263660d1e..9ae4e93dfde 100644 --- a/rs/pocket_ic_server/Cargo.toml +++ b/rs/pocket_ic_server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pocket-ic-server" -version = "6.0.0" +version = "7.0.0" edition = "2021" [dependencies] diff --git a/rs/pocket_ic_server/src/main.rs b/rs/pocket_ic_server/src/main.rs index e33c78ba91c..9911af56018 100644 --- a/rs/pocket_ic_server/src/main.rs +++ b/rs/pocket_ic_server/src/main.rs @@ -54,7 +54,7 @@ const LOG_DIR_PATH_ENV_NAME: &str = "POCKET_IC_LOG_DIR"; const LOG_DIR_LEVELS_ENV_NAME: &str = "POCKET_IC_LOG_DIR_LEVELS"; #[derive(Parser)] -#[clap(version = "6.0.0")] +#[clap(version = "7.0.0")] struct Args { /// The IP address to which the PocketIC server should bind (defaults to 127.0.0.1) #[clap(long, short)] diff --git a/rs/replicated_state/src/metadata_state.rs b/rs/replicated_state/src/metadata_state.rs index 89114422088..7337e6ef1e6 100644 --- a/rs/replicated_state/src/metadata_state.rs +++ b/rs/replicated_state/src/metadata_state.rs @@ -1968,11 +1968,6 @@ impl IngressHistoryState { /// called from within `insert` to ensure that `next_terminal_time` /// is consistently updated and we don't miss any completed statuses. fn forget_terminal_statuses(&mut self, target_size: NumBytes) { - // Before certification version 8 no done statuses are produced - if CURRENT_CERTIFICATION_VERSION < CertificationVersion::V8 { - return; - } - // In debug builds we store the length of the statuses map here so that // we can later debug_assert that no status disappeared. #[cfg(debug_assertions)] @@ -2431,7 +2426,7 @@ pub(crate) mod testing { split_from: None, prev_state_hash: Default::default(), state_sync_version: CURRENT_STATE_SYNC_VERSION, - certification_version: CertificationVersion::V0, + certification_version: CURRENT_CERTIFICATION_VERSION, heap_delta_estimate: Default::default(), subnet_metrics: Default::default(), expected_compiled_wasms: Default::default(), diff --git a/rs/replicated_state/src/metadata_state/tests.rs b/rs/replicated_state/src/metadata_state/tests.rs index 98b4157a1d6..afb8c76ef06 100644 --- a/rs/replicated_state/src/metadata_state/tests.rs +++ b/rs/replicated_state/src/metadata_state/tests.rs @@ -1016,52 +1016,26 @@ fn ingress_history_insert_beyond_limit_will_succeed() { let (inserted_message_id, inserted_status) = insert_status(&mut ingress_history, i, 1); assert_eq!(ingress_history.statuses().count(), i as usize); - if CURRENT_CERTIFICATION_VERSION >= CertificationVersion::V8 { - assert_eq!( - ingress_history.get(&inserted_message_id).unwrap(), - &inserted_status - ); - assert_eq!( - ingress_history - .statuses() - .filter(|(_, status)| matches!( - status, - IngressStatus::Known { - state: IngressState::Completed(_), - .. - } | IngressStatus::Known { - state: IngressState::Failed(_), - .. - } - )) - .count(), - 1 - ); - } else { - assert_eq!( - ingress_history - .statuses() - .filter(|(_, status)| matches!( - status, - IngressStatus::Known { - state: IngressState::Completed(_), - .. - } | IngressStatus::Known { - state: IngressState::Failed(_), - .. - } - )) - .count(), - i as usize - ); - assert!(!ingress_history.statuses().any(|(_, status)| matches!( - status, - IngressStatus::Known { - state: IngressState::Done, - .. - } - ))); - } + assert_eq!( + ingress_history.get(&inserted_message_id).unwrap(), + &inserted_status + ); + assert_eq!( + ingress_history + .statuses() + .filter(|(_, status)| matches!( + status, + IngressStatus::Known { + state: IngressState::Completed(_), + .. + } | IngressStatus::Known { + state: IngressState::Failed(_), + .. + } + )) + .count(), + 1 + ); } // Inserting without available space will directly transition inserted status @@ -1070,53 +1044,27 @@ fn ingress_history_insert_beyond_limit_will_succeed() { let (inserted_message_id, _) = insert_status(&mut ingress_history, i, 0); assert_eq!(ingress_history.statuses().count(), i as usize); - if CURRENT_CERTIFICATION_VERSION >= CertificationVersion::V8 { - assert_eq!( - ingress_history.get(&inserted_message_id).unwrap(), - &test_status_done(i), - ); + assert_eq!( + ingress_history.get(&inserted_message_id).unwrap(), + &test_status_done(i), + ); - assert_eq!( - ingress_history - .statuses() - .filter(|(_, status)| matches!( - status, - IngressStatus::Known { - state: IngressState::Completed(_), - .. - } | IngressStatus::Known { - state: IngressState::Failed(_), - .. - } - )) - .count(), - 0 - ); - } else { - assert_eq!( - ingress_history - .statuses() - .filter(|(_, status)| matches!( - status, - IngressStatus::Known { - state: IngressState::Completed(_), - .. - } | IngressStatus::Known { - state: IngressState::Failed(_), - .. - } - )) - .count(), - i as usize - ); - assert!(!ingress_history.statuses().any(|(_, status)| matches!( - status, - IngressStatus::Known { - state: IngressState::Done, - .. - } - ))); - } + assert_eq!( + ingress_history + .statuses() + .filter(|(_, status)| matches!( + status, + IngressStatus::Known { + state: IngressState::Completed(_), + .. + } | IngressStatus::Known { + state: IngressState::Failed(_), + .. + } + )) + .count(), + 0 + ); } } @@ -1167,12 +1115,9 @@ fn ingress_history_forget_completed_does_not_touch_other_statuses() { // Forgetting terminal statuses when the ingress history only contains non-terminal // statuses should be a no-op. ingress_history_limit.forget_terminal_statuses(NumBytes::from(0)); - // ... except that if current certification version >= 8, the next_terminal_time - // is updated to the first key in the pruning_times map - if CURRENT_CERTIFICATION_VERSION >= CertificationVersion::V8 { - ingress_history_before.next_terminal_time = - *ingress_history_limit.pruning_times().next().unwrap().0; - } + // ... except the next_terminal_time is updated to the first key in the `pruning_times` map. + ingress_history_before.next_terminal_time = + *ingress_history_limit.pruning_times().next().unwrap().0; assert_eq!(ingress_history_before, ingress_history_limit); } @@ -1224,13 +1169,8 @@ fn ingress_history_respects_limits() { }) .count(); - if CURRENT_CERTIFICATION_VERSION >= CertificationVersion::V8 { - assert_eq!(terminal_count, i.min(max_num_terminal) as usize); - assert_eq!(done_count, i.saturating_sub(max_num_terminal) as usize); - } else { - assert_eq!(terminal_count, i as usize); - assert_eq!(done_count, 0); - } + assert_eq!(terminal_count, i.min(max_num_terminal) as usize); + assert_eq!(done_count, i.saturating_sub(max_num_terminal) as usize); assert_eq!( terminal_count + done_count, @@ -1247,10 +1187,6 @@ fn ingress_history_respects_limits() { #[test] fn ingress_history_insert_before_next_complete_time_resets_it() { - if CURRENT_CERTIFICATION_VERSION < CertificationVersion::V8 { - return; - } - let mut ingress_history = IngressHistoryState::new(); // Fill the ingress history with 10 terminal entries... @@ -1312,10 +1248,6 @@ fn ingress_history_insert_before_next_complete_time_resets_it() { #[test] fn ingress_history_forget_behaves_the_same_with_reset_next_complete_time() { - if CURRENT_CERTIFICATION_VERSION < CertificationVersion::V8 { - return; - } - let mut ingress_history = IngressHistoryState::new(); // Fill the ingress history with 10 terminal entries... diff --git a/rs/rust_canisters/load_simulator/BUILD.bazel b/rs/rust_canisters/load_simulator/BUILD.bazel new file mode 100644 index 00000000000..4658e5dc68b --- /dev/null +++ b/rs/rust_canisters/load_simulator/BUILD.bazel @@ -0,0 +1,53 @@ +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_test") +load("//bazel:canisters.bzl", "rust_canister") + +package(default_visibility = ["//visibility:public"]) + +DEPENDENCIES = [ + # Keep sorted. + "@crate_index//:candid", + "@crate_index//:ic-cdk", + "@crate_index//:ic-cdk-timers", +] + +MACRO_DEPENDENCIES = [ + # Keep sorted. + "@crate_index//:ic-cdk-macros", +] + +DEV_DEPENDENCIES = [] + +MACRO_DEV_DEPENDENCIES = [] + +ALIASES = {} + +rust_canister( + name = "load_simulator_canister", + srcs = ["src/main.rs"], + aliases = ALIASES, + keep_name_section = True, + proc_macro_deps = MACRO_DEPENDENCIES, + service_file = ":load_simulator.did", + deps = DEPENDENCIES, +) + +rust_test( + name = "load_simulator_test", + srcs = ["src/main.rs"], + aliases = ALIASES, + data = ["load_simulator.did"], + env = { + "DID_PATH": "rs/rust_canisters/load_simulator/load_simulator.did", + }, + proc_macro_deps = MACRO_DEPENDENCIES + MACRO_DEV_DEPENDENCIES, + deps = DEPENDENCIES + DEV_DEPENDENCIES, +) + +rust_binary( + name = "load_simulator_binary", + srcs = ["src/main.rs"], + aliases = ALIASES, + proc_macro_deps = MACRO_DEPENDENCIES, + visibility = [], + deps = DEPENDENCIES, +) diff --git a/rs/rust_canisters/load_simulator/Cargo.toml b/rs/rust_canisters/load_simulator/Cargo.toml new file mode 100644 index 00000000000..970b2cab70b --- /dev/null +++ b/rs/rust_canisters/load_simulator/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "load-simulator" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "load-simulator" +path = "src/main.rs" + +[dependencies] +candid = { workspace = true } +ic-cdk = { workspace = true } +ic-cdk-macros = { workspace = true } +ic-cdk-timers = { workspace = true } diff --git a/rs/rust_canisters/load_simulator/README.md b/rs/rust_canisters/load_simulator/README.md new file mode 100644 index 00000000000..b1fb93eb1bc --- /dev/null +++ b/rs/rust_canisters/load_simulator/README.md @@ -0,0 +1,25 @@ +Load Simulator Canister +======================= + +This `load_simulator` canister is used in `load_simulator_canisters` benchmark. +By default, each `load_simulator` canister runs a periodic timer with +a one-second interval and accesses stable memory every fifth call. + +This benchmark and canister are useful for debugging and benchmarking scheduler +and sandbox eviction changes. + +For more realistic testnet load tests, refer to the `dfinity/subnet-load-tester` project. +For more details see the `load_simulator_canisters` benchmark. + +Build +----- + +```bash +# Build the Wasm binary +bazel build //rs/rust_canisters/load_simulator:load_simulator_canister + +# Find the optimized canister binary from the root `ic` directory: +ls -l bazel-bin/rs/rust_canisters/load_simulator/load_simulator_canister.wasm.gz +# From other directories: +ls -l $(bazel info bazel-bin)/rs/rust_canisters/load_simulator/load_simulator_canister.wasm.gz +``` diff --git a/rs/rust_canisters/load_simulator/load_simulator.did b/rs/rust_canisters/load_simulator/load_simulator.did new file mode 100644 index 00000000000..02c84eae9e5 --- /dev/null +++ b/rs/rust_canisters/load_simulator/load_simulator.did @@ -0,0 +1 @@ +service : () -> {} \ No newline at end of file diff --git a/rs/rust_canisters/load_simulator/src/main.rs b/rs/rust_canisters/load_simulator/src/main.rs new file mode 100644 index 00000000000..80215c7978d --- /dev/null +++ b/rs/rust_canisters/load_simulator/src/main.rs @@ -0,0 +1,71 @@ +use ic_cdk::api::stable; +use std::cell::RefCell; +use std::time::Duration; + +thread_local! { + static COUNTER: RefCell = const { RefCell::new(0) }; +} + +async fn timer_handler() { + COUNTER.with(|counter| { + let mut counter = counter.borrow_mut(); + *counter += 1; + + // Write to stable memory every 50 timer calls. + if *counter % 50 == 0 { + if stable::stable_size() < 5 { + let _ = stable::stable_grow(1); + } + if stable::stable_size() > 0 { + stable::stable_write(0, &[1, 2, 3]); + } + } + }); +} + +#[ic_cdk::init] +fn set_up_timer() { + // Set up a canister timer to call a function every N seconds. + ic_cdk_timers::set_timer_interval(Duration::from_secs(1), || ic_cdk::spawn(timer_handler())); +} + +// When run on native this prints the candid service definition of this +// canister, from the methods annotated with `candid_method` above. +// +// Note that `cargo test` calls `main`, and `export_service` (which defines +// `__export_service` in the current scope) needs to be called exactly once. So +// in addition to `not(target_family = "wasm")` we have a `not(test)` guard here +// to avoid calling `export_service`, which we need to call in the test below. +#[cfg(not(any(target_family = "wasm", test)))] +fn main() { + // The line below generates did types and service definition from the + // methods annotated with `candid_method` above. The definition is then + // obtained with `__export_service()`. + candid::export_service!(); + std::print!("{}", __export_service()); +} + +#[cfg(any(target_family = "wasm", test))] +fn main() {} + +#[test] +fn check_candid_file() { + let did_path = match std::env::var("DID_PATH") { + Ok(v) => v, + Err(_e) => "load_simulator.did".to_string(), + }; + let candid = String::from_utf8(std::fs::read(did_path).unwrap()).unwrap(); + + // See comments in main above + candid::export_service!(); + let expected = __export_service(); + + if candid != expected { + panic!( + "Generated candid definition does not match load_simulator.did. Run \ + `bazel run //rs/rust_canisters/load_simulator:load_simulator_binary > \ + rs/rust_canisters/load_simulator/load_simulator.did` to update \ + the candid file." + ) + } +} diff --git a/rs/sns/governance/canister/canister.rs b/rs/sns/governance/canister/canister.rs index 8358ef4bd9f..bd26026bdd9 100644 --- a/rs/sns/governance/canister/canister.rs +++ b/rs/sns/governance/canister/canister.rs @@ -473,7 +473,7 @@ fn get_running_sns_version(_: GetRunningSnsVersionRequest) -> GetRunningSnsVersi target_version: upgrade_in_progress.target_version.clone(), mark_failed_at_seconds: upgrade_in_progress.mark_failed_at_seconds, checking_upgrade_lock: upgrade_in_progress.checking_upgrade_lock, - proposal_id: upgrade_in_progress.proposal_id, + proposal_id: upgrade_in_progress.proposal_id.unwrap_or(0), }); GetRunningSnsVersionResponse { deployed_version: governance().proto.deployed_version.clone(), diff --git a/rs/sns/governance/canister/governance.did b/rs/sns/governance/canister/governance.did index 52df740757f..c994ff9caa6 100644 --- a/rs/sns/governance/canister/governance.did +++ b/rs/sns/governance/canister/governance.did @@ -257,7 +257,12 @@ type GetProposalResponse = record { type GetRunningSnsVersionResponse = record { deployed_version : opt Version; - pending_version : opt UpgradeInProgress; + pending_version : opt record { + mark_failed_at_seconds : nat64; + checking_upgrade_lock : nat64; + proposal_id : nat64; + target_version : opt Version; + }; }; type GetSnsInitializationParametersResponse = record { @@ -674,14 +679,14 @@ type TransferSnsTreasuryFunds = record { type UpgradeInProgress = record { mark_failed_at_seconds : nat64; checking_upgrade_lock : nat64; - proposal_id : nat64; + proposal_id : opt nat64; target_version : opt Version; }; type PendingVersion = record { mark_failed_at_seconds : nat64; checking_upgrade_lock : nat64; - proposal_id : nat64; + proposal_id : opt nat64; target_version : opt Version; }; @@ -756,6 +761,7 @@ type TargetVersionSet = record { type TargetVersionReset = record { new_target_version : opt Version; old_target_version : opt Version; + human_readable : opt text; }; type UpgradeStarted = record { @@ -787,6 +793,7 @@ type GetUpgradeJournalResponse = record { upgrade_steps : opt Versions; response_timestamp_seconds : opt nat64; target_version : opt Version; + deployed_version : opt Version; upgrade_journal : opt UpgradeJournal; }; diff --git a/rs/sns/governance/canister/governance_test.did b/rs/sns/governance/canister/governance_test.did index 4d52c0cc148..e3f826d77f5 100644 --- a/rs/sns/governance/canister/governance_test.did +++ b/rs/sns/governance/canister/governance_test.did @@ -266,7 +266,12 @@ type GetProposalResponse = record { type GetRunningSnsVersionResponse = record { deployed_version : opt Version; - pending_version : opt UpgradeInProgress; + pending_version : opt record { + mark_failed_at_seconds : nat64; + checking_upgrade_lock : nat64; + proposal_id : nat64; + target_version : opt Version; + }; }; type GetSnsInitializationParametersResponse = record { @@ -688,14 +693,14 @@ type TransferSnsTreasuryFunds = record { type UpgradeInProgress = record { mark_failed_at_seconds : nat64; checking_upgrade_lock : nat64; - proposal_id : nat64; + proposal_id : opt nat64; target_version : opt Version; }; type PendingVersion = record { mark_failed_at_seconds : nat64; checking_upgrade_lock : nat64; - proposal_id : nat64; + proposal_id : opt nat64; target_version : opt Version; }; @@ -770,6 +775,7 @@ type TargetVersionSet = record { type TargetVersionReset = record { new_target_version : opt Version; old_target_version : opt Version; + human_readable : opt text; }; type UpgradeStarted = record { @@ -801,6 +807,7 @@ type GetUpgradeJournalResponse = record { upgrade_steps : opt Versions; response_timestamp_seconds : opt nat64; target_version : opt Version; + deployed_version : opt Version; upgrade_journal : opt UpgradeJournal; }; diff --git a/rs/sns/governance/proto/ic_sns_governance/pb/v1/governance.proto b/rs/sns/governance/proto/ic_sns_governance/pb/v1/governance.proto index a1f0c55ac45..5c6583d67ac 100644 --- a/rs/sns/governance/proto/ic_sns_governance/pb/v1/governance.proto +++ b/rs/sns/governance/proto/ic_sns_governance/pb/v1/governance.proto @@ -1424,7 +1424,7 @@ message Governance { // allowing us to fail in case we otherwise have gotten stuck. uint64 checking_upgrade_lock = 3; // The proposal that initiated this upgrade - uint64 proposal_id = 4; + optional uint64 proposal_id = 4; } // Version SNS is in process of upgrading to. @@ -2185,6 +2185,7 @@ message UpgradeJournalEntry { message TargetVersionReset { optional Governance.Version old_target_version = 1; optional Governance.Version new_target_version = 2; + optional string human_readable = 3; } message UpgradeStarted { @@ -2230,6 +2231,7 @@ message GetUpgradeJournalResponse { // Currently, this field is always None, but in the "effortless SNS upgrade" // feature, it reflect the version of the SNS that the community has decided to upgrade to. Governance.Version target_version = 3; + Governance.Version deployed_version = 5; UpgradeJournal upgrade_journal = 4; } diff --git a/rs/sns/governance/src/gen/ic_sns_governance.pb.v1.rs b/rs/sns/governance/src/gen/ic_sns_governance.pb.v1.rs index dae7457959f..32da477fc85 100644 --- a/rs/sns/governance/src/gen/ic_sns_governance.pb.v1.rs +++ b/rs/sns/governance/src/gen/ic_sns_governance.pb.v1.rs @@ -1900,8 +1900,8 @@ pub mod governance { #[prost(uint64, tag = "3")] pub checking_upgrade_lock: u64, /// The proposal that initiated this upgrade - #[prost(uint64, tag = "4")] - pub proposal_id: u64, + #[prost(uint64, optional, tag = "4")] + pub proposal_id: ::core::option::Option, } #[derive( candid::CandidType, @@ -3443,6 +3443,8 @@ pub mod upgrade_journal_entry { pub old_target_version: ::core::option::Option, #[prost(message, optional, tag = "2")] pub new_target_version: ::core::option::Option, + #[prost(string, optional, tag = "3")] + pub human_readable: ::core::option::Option<::prost::alloc::string::String>, } #[derive( candid::CandidType, @@ -3600,6 +3602,8 @@ pub struct GetUpgradeJournalResponse { /// feature, it reflect the version of the SNS that the community has decided to upgrade to. #[prost(message, optional, tag = "3")] pub target_version: ::core::option::Option, + #[prost(message, optional, tag = "5")] + pub deployed_version: ::core::option::Option, #[prost(message, optional, tag = "4")] pub upgrade_journal: ::core::option::Option, } diff --git a/rs/sns/governance/src/governance.rs b/rs/sns/governance/src/governance.rs index 3d342a0513a..bac4f6a9034 100644 --- a/rs/sns/governance/src/governance.rs +++ b/rs/sns/governance/src/governance.rs @@ -65,8 +65,8 @@ use crate::{ MAX_NUMBER_OF_PROPOSALS_WITH_BALLOTS, }, sns_upgrade::{ - get_all_sns_canisters, get_running_version, get_upgrade_params, get_wasm, SnsCanisterType, - UpgradeSnsParams, + canister_type_and_wasm_hash_for_upgrade, get_all_sns_canisters, get_canisters_to_upgrade, + get_running_version, get_upgrade_params, get_wasm, SnsCanisterType, UpgradeSnsParams, }, types::{ function_id_to_proposal_criticality, is_registered_function_id, Environment, @@ -389,6 +389,13 @@ impl GovernanceProto { index } + pub fn root_canister_id(&self) -> Result { + let root_canister_id = self.root_canister_id.ok_or_else(|| { + GovernanceError::new_with_message(ErrorType::Unavailable, "No root_canister_id.") + })?; + Ok(CanisterId::unchecked_from_principal(root_canister_id)) + } + pub fn root_canister_id_or_panic(&self) -> CanisterId { CanisterId::unchecked_from_principal(self.root_canister_id.expect("No root_canister_id.")) } @@ -2063,8 +2070,9 @@ impl Governance { } Action::UpgradeSnsToNextVersion(_) => { log!(INFO, "Executing UpgradeSnsToNextVersion action",); - let upgrade_sns_result = - self.perform_upgrade_to_next_sns_version(proposal_id).await; + let upgrade_sns_result = self + .perform_upgrade_to_next_sns_version_legacy(proposal_id) + .await; // If the upgrade returned `Ok(true)` that means the upgrade completed successfully // and the proposal can be marked as "executed". If the upgrade returned `Ok(false)` @@ -2458,7 +2466,7 @@ impl Governance { proposal_id: u64, upgrade: UpgradeSnsControlledCanister, ) -> Result<(), GovernanceError> { - self.check_no_other_upgrades_in_progress(proposal_id)?; + self.check_no_upgrades_in_progress(Some(proposal_id))?; let sns_canisters = get_all_sns_canisters(&*self.env, self.proto.root_canister_id_or_panic()) @@ -2466,7 +2474,7 @@ impl Governance { .map_err(|e| { GovernanceError::new_with_message( ErrorType::External, - format!("Could not get list of SNS canisters from root: {}", e), + format!("Could not get list of SNS canisters from SNS Root: {}", e), ) })?; @@ -2545,12 +2553,13 @@ impl Governance { }) } - pub fn check_no_other_upgrades_in_progress( + /// Used for checking that no upgrades are in progress. Also checks that there are no upgrade proposals in progress except, optionally, one that you pass in as `proposal_id` + pub fn check_no_upgrades_in_progress( &self, - proposal_id: u64, + proposal_id: Option, ) -> Result<(), GovernanceError> { let upgrade_proposals_in_progress = self.upgrade_proposals_in_progress(); - if upgrade_proposals_in_progress != BTreeSet::from([proposal_id]) { + if upgrade_proposals_in_progress != proposal_id.into_iter().collect() { return Err(GovernanceError::new_with_message( ErrorType::ResourceExhausted, format!( @@ -2568,7 +2577,13 @@ impl Governance { if self.proto.pending_version.is_some() { return Err(GovernanceError::new_with_message( ErrorType::ResourceExhausted, - "Upgrade lock currently acquired, not upgrading".to_string(), + format!( + "Upgrade lock acquired (expires at {:?}), not upgrading", + self.proto + .pending_version + .as_ref() + .map(|p| p.mark_failed_at_seconds) + ), )); } @@ -2577,11 +2592,11 @@ impl Governance { /// Return `Ok(true)` if the upgrade was completed successfully, return `Ok(false)` if an /// upgrade was successfully kicked-off, but its completion is pending. - async fn perform_upgrade_to_next_sns_version( + async fn perform_upgrade_to_next_sns_version_legacy( &mut self, proposal_id: u64, ) -> Result { - self.check_no_other_upgrades_in_progress(proposal_id)?; + self.check_no_upgrades_in_progress(Some(proposal_id))?; let current_version = self.proto.deployed_version_or_panic(); let root_canister_id = self.proto.root_canister_id_or_panic(); @@ -2645,7 +2660,7 @@ impl Governance { target_version: Some(next_version), mark_failed_at_seconds: self.env.now() + 5 * 60, checking_upgrade_lock: 0, - proposal_id, + proposal_id: Some(proposal_id), }); log!( @@ -2657,6 +2672,63 @@ impl Governance { Ok(false) } + async fn upgrade_sns_framework_canister( + &mut self, + new_wasm_hash: Vec, + canister_type_to_upgrade: SnsCanisterType, + ) -> Result<(), GovernanceError> { + let root_canister_id = self.proto.root_canister_id()?; + + let target_wasm = get_wasm(&*self.env, new_wasm_hash.to_vec(), canister_type_to_upgrade) + .await + .map_err(|e| { + GovernanceError::new_with_message( + ErrorType::External, + format!("Could not get wasm for upgrade: {}", e), + ) + })? + .wasm; + + let target_is_root = canister_type_to_upgrade == SnsCanisterType::Root; + + if target_is_root { + upgrade_canister_directly( + &*self.env, + root_canister_id, + target_wasm, + Encode!().unwrap(), + ) + .await?; + } else { + let canister_ids_to_upgrade = + get_canisters_to_upgrade(&*self.env, root_canister_id, canister_type_to_upgrade) + .await + .map_err(|e| { + GovernanceError::new_with_message( + ErrorType::External, + format!("Could not get list of SNS canisters from SNS Root: {}", e), + ) + })?; + for target_canister_id in canister_ids_to_upgrade { + self.upgrade_non_root_canister( + target_canister_id, + target_wasm.clone(), + Encode!().unwrap(), + CanisterInstallMode::Upgrade, + ) + .await?; + } + } + + log!( + INFO, + "Successfully kicked off upgrade for SNS canister {:?}", + canister_type_to_upgrade, + ); + + Ok(()) + } + async fn perform_transfer_sns_treasury_funds( &mut self, proposal_id: u64, // This is just to control concurrency. @@ -2777,7 +2849,7 @@ impl Governance { proposal_id: u64, manage_ledger_parameters: ManageLedgerParameters, ) -> Result<(), GovernanceError> { - self.check_no_other_upgrades_in_progress(proposal_id)?; + self.check_no_upgrades_in_progress(Some(proposal_id))?; let current_version = self.proto.deployed_version_or_panic(); let ledger_canister_id = self.proto.ledger_canister_id_or_panic(); @@ -4695,6 +4767,8 @@ impl Governance { self.refresh_cached_upgrade_steps().await; } + self.initiate_upgrade_if_sns_behind_target_version().await; + self.release_upgrade_periodic_task_lock(); } @@ -4748,6 +4822,132 @@ impl Governance { } } } + /// Checks if an automatic upgrade is needed and initiates it. + /// An automatic upgrade is needed if `target_version` is set to a future version on the upgrade path + async fn initiate_upgrade_if_sns_behind_target_version(&mut self) { + // Check that no upgrades are in progress + if self.check_no_upgrades_in_progress(None).is_err() { + // An upgrade is already in progress + return; + } + + let Some(deployed_version) = self.proto.deployed_version.clone() else { + // TODO(NNS1-3445): there should be some way to recover from this state. + log!(ERROR, "No deployed version! This is an internal bug"); + return; + }; + + let Some(target_version) = self.proto.target_version.clone() else { + return; + }; + + let Some(CachedUpgradeSteps { + upgrade_steps: Some(Versions { + versions: upgrade_steps, + }), + .. + }) = &self.proto.cached_upgrade_steps + else { + log!(ERROR, "Cached upgrade steps set to None"); + return; + }; + + // Find the current position of the deployed version + let Some(deployed_position) = upgrade_steps.iter().position(|v| v == &deployed_version) + else { + let human_readable = format!( + "Deployed version {} is not on the upgrade path {:?}", + deployed_version, upgrade_steps + ); + log!(ERROR, "{}", human_readable); + self.invalidate_cached_upgrade_steps(human_readable); + return; + }; + + // Find the target position of the target version + let Some(target_position) = upgrade_steps.iter().position(|v| v == &target_version) else { + let message = format!( + "Target version {} is not on the upgrade path {:?}", + target_version, upgrade_steps + ); + log!(ERROR, "{}", message); + self.invalidate_target_version(message); + return; + }; + + // If the target version is the same as the deployed version, there is nothing to do. + if target_position == deployed_position { + return; + } + + // If the target version is behind the deployed version, we should reset the target version, since otherwise + // people might be under the impression that we could go backwards. + if target_position < deployed_position { + let message = format!( + "Target version {} is behind the deployed version {}", + target_version, deployed_version + ); + self.invalidate_target_version(message); + return; + } + + // since `target_position > deployed_position`, `deployed_position + 1 < upgrade_steps.len()` + let next_version = upgrade_steps[deployed_position + 1].clone(); + + let (canister_type, wasm_hash) = + match canister_type_and_wasm_hash_for_upgrade(&deployed_version, &next_version) { + Ok((canister_type, wasm_hash)) => (canister_type, wasm_hash), + + Err(err) => { + let message = format!("Upgrade attempt failed: {}", err); + log!(ERROR, "{}", message); + self.invalidate_target_version(message); + return; + } + }; + + self.push_to_upgrade_journal(upgrade_journal_entry::UpgradeStarted::from_behind_target( + deployed_version.clone(), + next_version.clone(), + )); + + self.proto.pending_version = Some(PendingVersion { + target_version: Some(next_version.clone()), + mark_failed_at_seconds: self.env.now() + 5 * 60, + checking_upgrade_lock: 0, + proposal_id: None, + }); + + println!("Initiating upgrade to version: {:?}", next_version); + let upgrade_attempt = self + .upgrade_sns_framework_canister(wasm_hash, canister_type) + .await; + if let Err(err) = upgrade_attempt { + let message = format!("Upgrade attempt failed: {}", err); + log!(ERROR, "{}", message); + self.proto.pending_version = None; + self.invalidate_target_version(message); + } + } + + /// Invalidates the cached upgrade steps. + fn invalidate_cached_upgrade_steps(&mut self, human_readable: String) { + self.push_to_upgrade_journal(upgrade_journal_entry::UpgradeStepsReset::new( + human_readable, + vec![], + )); + self.proto.cached_upgrade_steps = None; + } + + /// Invalidates the cached upgrade steps. + fn invalidate_target_version(&mut self, reason: String) { + self.push_to_upgrade_journal(upgrade_journal_entry::TargetVersionReset::new( + self.proto.target_version.clone(), + None, + reason, + )); + self.proto.target_version = None; + } fn release_upgrade_periodic_task_lock(&mut self) { self.upgrade_periodic_task_lock = None; @@ -4837,6 +5037,7 @@ impl Governance { upgrade_steps: cached_upgrade_steps.upgrade_steps, response_timestamp_seconds: cached_upgrade_steps.response_timestamp_seconds, target_version: self.proto.target_version.clone(), + deployed_version: self.proto.deployed_version.clone(), // TODO(NNS1-3416): Bound the size of the response. upgrade_journal: self.proto.upgrade_journal.clone(), }, @@ -4844,6 +5045,7 @@ impl Governance { upgrade_steps: None, response_timestamp_seconds: None, target_version: None, + deployed_version: self.proto.deployed_version.clone(), // TODO(NNS1-3416): Bound the size of the response. upgrade_journal: self.proto.upgrade_journal.clone(), }, @@ -5459,7 +5661,9 @@ impl Governance { target_version ); self.push_to_upgrade_journal(upgrade_journal_entry::UpgradeOutcome::success(None)); - self.set_proposal_execution_status(proposal_id, Ok(())); + if let Some(proposal_id) = proposal_id { + self.set_proposal_execution_status(proposal_id, Ok(())); + } self.proto.deployed_version = Some(target_version); self.proto.pending_version = None; } @@ -5489,12 +5693,14 @@ impl Governance { // an error for an UpgradeSnsToNextVersion actions failure. This unblocks further upgrade proposals. fn fail_sns_upgrade_to_next_version_proposal( &mut self, - proposal_id: u64, + proposal_id: Option, error: GovernanceError, ) { log!(ERROR, "{}", error.error_message); let result = Err(error); - self.set_proposal_execution_status(proposal_id, result); + if let Some(proposal_id) = proposal_id { + self.set_proposal_execution_status(proposal_id, result); + } self.proto.pending_version = None; } @@ -5535,7 +5741,7 @@ impl Governance { self.fail_sns_upgrade_to_next_version_proposal( pending_version.proposal_id, GovernanceError::new_with_message(ErrorType::External, error), - ); + ) } FailStuckUpgradeInProgressResponse {} @@ -7275,7 +7481,7 @@ mod tests { target_version: Some(next_version.into()), mark_failed_at_seconds: now + 5 * 60, checking_upgrade_lock: 0, - proposal_id, + proposal_id: Some(proposal_id), } ); // We do not check the upgrade completion in this test because of limitations @@ -7590,7 +7796,7 @@ mod tests { target_version: Some(next_version.clone().into()), mark_failed_at_seconds, checking_upgrade_lock: 0, - proposal_id: 0, + proposal_id: Some(0), }); // Make sure Governance state is correctly set @@ -7600,7 +7806,7 @@ mod tests { target_version: Some(next_version.clone().into()), mark_failed_at_seconds, checking_upgrade_lock: 0, - proposal_id: 0, + proposal_id: Some(0), } ); assert_eq!( @@ -7688,7 +7894,7 @@ mod tests { target_version: Some(next_version.clone().into()), mark_failed_at_seconds: now - 1, checking_upgrade_lock: 0, - proposal_id: 0, + proposal_id: Some(0), }), ..basic_governance_proto() } @@ -7706,7 +7912,7 @@ mod tests { target_version: Some(next_version.into()), mark_failed_at_seconds: now - 1, checking_upgrade_lock: 0, - proposal_id: 0, + proposal_id: Some(0), } ); assert_eq!( @@ -7783,7 +7989,7 @@ mod tests { target_version: Some(next_version.clone().into()), mark_failed_at_seconds: now + 5 * 60, checking_upgrade_lock: 0, - proposal_id, + proposal_id: Some(proposal_id), }), // we make a proposal that is already decided so that it won't execute again because // proposals to upgrade SNS's cannot execute if there's no deployed_version set on Governance state @@ -7829,7 +8035,7 @@ mod tests { target_version: Some(next_version.clone().into()), mark_failed_at_seconds: now + 5 * 60, checking_upgrade_lock: 0, - proposal_id, + proposal_id: Some(proposal_id), } ); assert_eq!( @@ -7924,7 +8130,7 @@ mod tests { target_version: Some(next_version.clone().into()), mark_failed_at_seconds: now + 1, checking_upgrade_lock: 0, - proposal_id, + proposal_id: Some(proposal_id), }), // we make a proposal that is already decided so that it won't execute again because // proposals to upgrade SNS's cannot execute if there's no deployed_version set on Governance state @@ -7970,7 +8176,7 @@ mod tests { target_version: Some(next_version.clone().into()), mark_failed_at_seconds: now + 1, checking_upgrade_lock: 0, - proposal_id, + proposal_id: Some(proposal_id), } ); assert_eq!( @@ -7987,7 +8193,7 @@ mod tests { target_version: Some(next_version.into()), mark_failed_at_seconds: now + 1, checking_upgrade_lock: 0, - proposal_id, + proposal_id: Some(proposal_id), } ); @@ -8060,7 +8266,7 @@ mod tests { target_version: Some(next_version.clone().into()), mark_failed_at_seconds: now - 1, checking_upgrade_lock: 0, - proposal_id, + proposal_id: Some(proposal_id), }), // we make a proposal that is already decided so that it won't execute again because // proposals to upgrade SNS's cannot execute if there's no deployed_version set on Governance state @@ -8106,7 +8312,7 @@ mod tests { target_version: Some(next_version.clone().into()), mark_failed_at_seconds: now - 1, checking_upgrade_lock: 0, - proposal_id, + proposal_id: Some(proposal_id), } ); assert_eq!( @@ -8208,7 +8414,7 @@ mod tests { target_version: None, mark_failed_at_seconds: now - 1, checking_upgrade_lock: 0, - proposal_id, + proposal_id: Some(proposal_id), }), // we make a proposal that is already decided so that it won't execute again because // proposals to upgrade SNS's cannot execute if there's no deployed_version set on Governance state @@ -8343,7 +8549,7 @@ mod tests { target_version: Some(next_version.clone().into()), mark_failed_at_seconds: now + 5 * 60, checking_upgrade_lock: 0, - proposal_id, + proposal_id: Some(proposal_id), }), // we make a proposal that is already decided so that it won't execute again because // proposals to upgrade SNS's cannot execute if there's no deployed_version set on Governance state @@ -8389,7 +8595,7 @@ mod tests { target_version: Some(next_version.into()), mark_failed_at_seconds: now + 5 * 60, checking_upgrade_lock: 0, - proposal_id, + proposal_id: Some(proposal_id), } ); @@ -8550,7 +8756,7 @@ mod tests { target_version: Some(next_version.clone().into()), mark_failed_at_seconds: now + 5 * 60, checking_upgrade_lock: 0, - proposal_id, + proposal_id: Some(proposal_id), }), ..basic_governance_proto() } @@ -8568,7 +8774,7 @@ mod tests { target_version: Some(next_version.clone().into()), mark_failed_at_seconds: now + 5 * 60, checking_upgrade_lock: 0, - proposal_id, + proposal_id: Some(proposal_id), } ); assert_eq!( @@ -8628,7 +8834,7 @@ mod tests { target_version: Some(next_version.clone().into()), mark_failed_at_seconds: now + 5 * 60, checking_upgrade_lock: 0, - proposal_id, + proposal_id: Some(proposal_id), }), ..basic_governance_proto() } @@ -8646,7 +8852,7 @@ mod tests { target_version: Some(next_version.clone().into()), mark_failed_at_seconds: now + 5 * 60, checking_upgrade_lock: 0, - proposal_id, + proposal_id: Some(proposal_id), } ); assert_eq!( @@ -8842,7 +9048,7 @@ mod tests { ); // Step 2: Run code under test. - let result = governance.check_no_other_upgrades_in_progress(upgrade_proposal_id); + let result = governance.check_no_upgrades_in_progress(Some(upgrade_proposal_id)); // Step 3: Inspect result. assert!(result.is_ok(), "{:#?}", result); @@ -8902,7 +9108,7 @@ mod tests { ); // Step 2: Run code under test. - let result = governance.check_no_other_upgrades_in_progress(executing_upgrade_proposal_id); + let result = governance.check_no_upgrades_in_progress(Some(executing_upgrade_proposal_id)); // Step 3: Inspect result. assert!(result.is_ok(), "{:#?}", result); @@ -8963,7 +9169,7 @@ mod tests { ); // Step 2: Run code under test. - let result = governance.check_no_other_upgrades_in_progress(upgrade_proposal_id); + let result = governance.check_no_upgrades_in_progress(Some(upgrade_proposal_id)); // Step 3: Inspect result. assert!(result.is_ok(), "{:#?}", result); @@ -9008,12 +9214,12 @@ mod tests { ); // Step 2 & 3: Run code under test, and inspect results. - let result = governance.check_no_other_upgrades_in_progress(proposal_id); + let result = governance.check_no_upgrades_in_progress(Some(proposal_id)); assert!(result.is_ok(), "{:#?}", result); // Other upgrades should be blocked by proposal 1 though. let some_other_proposal_id = 99_u64; - match governance.check_no_other_upgrades_in_progress(some_other_proposal_id) { + match governance.check_no_upgrades_in_progress(Some(some_other_proposal_id)) { Ok(_) => panic!("Some other upgrade proposal was not blocked."), Err(err) => assert_eq!( err.error_type, @@ -9067,7 +9273,7 @@ mod tests { ); // Step 2 & 3: Run code under test, and inspect results. - match governance.check_no_other_upgrades_in_progress(proposal_id) { + match governance.check_no_upgrades_in_progress(Some(proposal_id)) { Ok(_) => panic!("Some other upgrade proposal was not blocked."), Err(err) => assert_eq!( err.error_type, @@ -9078,7 +9284,7 @@ mod tests { } let some_other_proposal_id = 99_u64; - match governance.check_no_other_upgrades_in_progress(some_other_proposal_id) { + match governance.check_no_upgrades_in_progress(Some(some_other_proposal_id)) { Ok(_) => panic!("Some other upgrade proposal was not blocked."), Err(err) => assert_eq!( err.error_type, diff --git a/rs/sns/governance/src/governance/tests/fail_stuck_upgrade_in_progress_tests.rs b/rs/sns/governance/src/governance/tests/fail_stuck_upgrade_in_progress_tests.rs index 3ac095b0561..e4fc9344cbc 100644 --- a/rs/sns/governance/src/governance/tests/fail_stuck_upgrade_in_progress_tests.rs +++ b/rs/sns/governance/src/governance/tests/fail_stuck_upgrade_in_progress_tests.rs @@ -79,7 +79,7 @@ lazy_static! { target_version: Some(SNS_VERSION_2.clone()), mark_failed_at_seconds: UPGRADE_DEADLINE_TIMESTAMP_SECONDS, checking_upgrade_lock: 10, - proposal_id: UPGRADE_PROPOSAL_ID, + proposal_id: Some(UPGRADE_PROPOSAL_ID), }), // we make a proposal that is already decided so that it won't execute again because // proposals to upgrade SNS's cannot execute if there's no deployed_version set on Governance state @@ -173,7 +173,7 @@ fn test_does_nothing_if_upgrade_attempt_not_expired() { target_version: Some(SNS_VERSION_2.clone()), mark_failed_at_seconds: UPGRADE_DEADLINE_TIMESTAMP_SECONDS, checking_upgrade_lock: 10, - proposal_id: UPGRADE_PROPOSAL_ID, + proposal_id: Some(UPGRADE_PROPOSAL_ID), }; assert_eq!( governance.proto.pending_version.clone().unwrap(), @@ -252,7 +252,7 @@ fn test_fails_proposal_and_removes_upgrade_if_upgrade_attempt_is_expired() { target_version: Some(SNS_VERSION_2.clone()), mark_failed_at_seconds: UPGRADE_DEADLINE_TIMESTAMP_SECONDS, checking_upgrade_lock: 10, - proposal_id: UPGRADE_PROPOSAL_ID, + proposal_id: Some(UPGRADE_PROPOSAL_ID), } ); assert_eq!( diff --git a/rs/sns/governance/src/sns_upgrade.rs b/rs/sns/governance/src/sns_upgrade.rs index 82ea9695d7a..c778d3638a9 100644 --- a/rs/sns/governance/src/sns_upgrade.rs +++ b/rs/sns/governance/src/sns_upgrade.rs @@ -137,7 +137,7 @@ pub(crate) async fn get_proposal_id_that_added_wasm( Ok(proposal_id) } -async fn get_canisters_to_upgrade( +pub(crate) async fn get_canisters_to_upgrade( env: &dyn Environment, root_canister_id: CanisterId, canister_type: SnsCanisterType, @@ -174,7 +174,7 @@ async fn get_canisters_to_upgrade( .collect() } -fn canister_type_and_wasm_hash_for_upgrade( +pub(crate) fn canister_type_and_wasm_hash_for_upgrade( current_version: &Version, next_version: &Version, ) -> Result<(SnsCanisterType, Vec), String> { diff --git a/rs/sns/governance/src/types.rs b/rs/sns/governance/src/types.rs index 6c6f9a937e2..d8d1ca2f45d 100644 --- a/rs/sns/governance/src/types.rs +++ b/rs/sns/governance/src/types.rs @@ -16,7 +16,7 @@ use crate::{ governance::{ self, neuron_in_flight_command::{self, SyncCommand}, - SnsMetadata, + SnsMetadata, Version, }, governance_error::ErrorType, manage_neuron, @@ -46,8 +46,8 @@ use ic_icrc1_ledger::UpgradeArgs as LedgerUpgradeArgs; use ic_ledger_core::tokens::TOKEN_SUBDIVIDABLE_BY; use ic_management_canister_types::CanisterInstallModeError; use ic_nervous_system_common::{ - ledger_validation::MAX_LOGO_LENGTH, NervousSystemError, DEFAULT_TRANSFER_FEE, ONE_DAY_SECONDS, - ONE_MONTH_SECONDS, ONE_YEAR_SECONDS, + hash_to_hex_string, ledger_validation::MAX_LOGO_LENGTH, NervousSystemError, + DEFAULT_TRANSFER_FEE, ONE_DAY_SECONDS, ONE_MONTH_SECONDS, ONE_YEAR_SECONDS, }; use ic_nervous_system_common_validation::validate_proposal_url; use ic_nervous_system_proto::pb::v1::{Duration as PbDuration, Percentage}; @@ -2531,6 +2531,21 @@ impl From for Vec { } } +impl std::fmt::Display for Version { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "SnsVersion {{ root:{}, governance:{}, swap:{}, index:{}, ledger:{}, archive:{} }}", + hash_to_hex_string(&self.root_wasm_hash), + hash_to_hex_string(&self.governance_wasm_hash), + hash_to_hex_string(&self.swap_wasm_hash), + hash_to_hex_string(&self.index_wasm_hash), + hash_to_hex_string(&self.ledger_wasm_hash), + hash_to_hex_string(&self.archive_wasm_hash) + ) + } +} + pub mod test_helpers { use super::*; use rand::Rng; diff --git a/rs/sns/governance/src/upgrade_journal.rs b/rs/sns/governance/src/upgrade_journal.rs index 5ca22adf572..c7ef987f8af 100644 --- a/rs/sns/governance/src/upgrade_journal.rs +++ b/rs/sns/governance/src/upgrade_journal.rs @@ -14,6 +14,16 @@ impl upgrade_journal_entry::UpgradeStepsRefreshed { } } +impl upgrade_journal_entry::UpgradeStepsReset { + /// Creates a new UpgradeStepsReset event with the given versions and message + pub fn new(human_readable: String, versions: Vec) -> Self { + Self { + human_readable: Some(human_readable), + upgrade_steps: Some(Versions { versions }), + } + } +} + impl upgrade_journal_entry::TargetVersionSet { /// Creates a new TargetVersionSet event with old and new versions pub fn new(old_version: Option, new_version: Option) -> Self { @@ -26,10 +36,15 @@ impl upgrade_journal_entry::TargetVersionSet { impl upgrade_journal_entry::TargetVersionReset { /// Creates a new TargetVersionReset event with old and new versions - pub fn new(old_version: Option, new_version: Option) -> Self { + pub fn new( + old_version: Option, + new_version: Option, + human_readable: String, + ) -> Self { Self { old_target_version: old_version, new_target_version: new_version, + human_readable: Some(human_readable), } } } @@ -120,6 +135,11 @@ impl From for upgrade_journal_entr upgrade_journal_entry::Event::UpgradeStepsRefreshed(event) } } +impl From for upgrade_journal_entry::Event { + fn from(event: upgrade_journal_entry::UpgradeStepsReset) -> Self { + upgrade_journal_entry::Event::UpgradeStepsReset(event) + } +} impl From for upgrade_journal_entry::Event { fn from(event: upgrade_journal_entry::UpgradeStarted) -> Self { upgrade_journal_entry::Event::UpgradeStarted(event) diff --git a/rs/state_manager/src/tree_hash.rs b/rs/state_manager/src/tree_hash.rs index 02afb34013b..fc44276e08b 100644 --- a/rs/state_manager/src/tree_hash.rs +++ b/rs/state_manager/src/tree_hash.rs @@ -219,26 +219,19 @@ mod tests { for i in 1..6 { stream.push( RequestBuilder::new() - .metadata( - (certification_version >= CertificationVersion::V14 && i % 5 != 0) - .then_some(RequestMetadata::new( - i % 3, - Time::from_nanos_since_unix_epoch(i % 2), - )), - ) + .metadata((i % 5 != 0).then_some(RequestMetadata::new( + i % 3, + Time::from_nanos_since_unix_epoch(i % 2), + ))) .deadline(maybe_deadline(i)) .build() .into(), ); } - if certification_version >= CertificationVersion::V8 { - stream.push_reject_signal(RejectReason::CanisterMigrating); - } - if certification_version >= CertificationVersion::V17 { - stream.set_reverse_stream_flags(StreamFlags { - deprecated_responses_only: true, - }); - } + stream.push_reject_signal(RejectReason::CanisterMigrating); + stream.set_reverse_stream_flags(StreamFlags { + deprecated_responses_only: true, + }); if certification_version >= CertificationVersion::V19 { stream.push_reject_signal(RejectReason::CanisterNotFound); stream.push_reject_signal(RejectReason::QueueFull); @@ -259,21 +252,19 @@ mod tests { ); } - if certification_version >= CertificationVersion::V11 { - state.set_ingress_status( - message_test_id(7), - IngressStatus::Known { - state: IngressState::Failed(UserError::new( - ErrorCode::CanisterNotFound, - "canister not found", - )), - receiver: canister_id.into(), - user_id: user_test_id(1), - time: Time::from_nanos_since_unix_epoch(12345), - }, - NumBytes::from(u64::MAX), - ); - } + state.set_ingress_status( + message_test_id(7), + IngressStatus::Known { + state: IngressState::Failed(UserError::new( + ErrorCode::CanisterNotFound, + "canister not found", + )), + receiver: canister_id.into(), + user_id: user_test_id(1), + time: Time::from_nanos_since_unix_epoch(12345), + }, + NumBytes::from(u64::MAX), + ); state.metadata.node_public_keys = btreemap! { node_test_id(1) => vec![1; 44], diff --git a/rs/state_manager/tests/state_manager.rs b/rs/state_manager/tests/state_manager.rs index ac7c301d89b..60bc1b4bae9 100644 --- a/rs/state_manager/tests/state_manager.rs +++ b/rs/state_manager/tests/state_manager.rs @@ -1,9 +1,5 @@ use assert_matches::assert_matches; use ic_base_types::SnapshotId; -use ic_certification_version::{ - CertificationVersion::{V11, V15}, - CURRENT_CERTIFICATION_VERSION, -}; use ic_config::{ flag_status::FlagStatus, state_manager::{lsmt_config_default, Config, LsmtConfig}, @@ -4427,31 +4423,23 @@ fn certified_read_can_certify_node_public_keys_since_v12() { .expect("failed to read certified state"); assert_eq!(cert, delivered_certification); - if CURRENT_CERTIFICATION_VERSION > V11 { - assert_eq!( - tree_payload(mixed_tree), - SubTree(flatmap! { - label("subnet") => SubTree( - flatmap! { - label(subnet_test_id(42).get_ref()) => SubTree( - flatmap!{ - label("node") => SubTree( - flatmap! { - label(node_test_id(39).get_ref()) => SubTree( - flatmap!(label("public_key") => Leaf(vec![39u8; 44])) - ), - }) - }) + assert_eq!( + tree_payload(mixed_tree), + SubTree(flatmap! { + label("subnet") => SubTree( + flatmap! { + label(subnet_test_id(42).get_ref()) => SubTree( + flatmap!{ + label("node") => SubTree( + flatmap! { + label(node_test_id(39).get_ref()) => SubTree( + flatmap!(label("public_key") => Leaf(vec![39u8; 44])) + ), + }) }) - }) - ); - } else { - assert!( - mixed_tree.lookup(&path[..]).is_absent(), - "mixed_tree: {:#?}", - mixed_tree - ); - } + }) + }) + ); }) } @@ -4493,28 +4481,20 @@ fn certified_read_can_certify_api_boundary_nodes_since_v16() { .expect("failed to read certified state"); assert_eq!(cert, delivered_certification); - if CURRENT_CERTIFICATION_VERSION > V15 { - assert_eq!( - tree_payload(mixed_tree), - SubTree(flatmap! { - label("api_boundary_nodes") => SubTree( - flatmap! { - label(node_test_id(11).get_ref()) => SubTree( - flatmap!{ - label("domain") => Leaf("api-bn11-example.com".to_string().into_bytes()), - label("ipv4_address") => Leaf("127.0.0.1".to_string().into_bytes()), - label("ipv6_address") => Leaf("2001:0db8:85a3:0000:0000:8a2e:0370:7334".to_string().into_bytes()), - }) + assert_eq!( + tree_payload(mixed_tree), + SubTree(flatmap! { + label("api_boundary_nodes") => SubTree( + flatmap! { + label(node_test_id(11).get_ref()) => SubTree( + flatmap!{ + label("domain") => Leaf("api-bn11-example.com".to_string().into_bytes()), + label("ipv4_address") => Leaf("127.0.0.1".to_string().into_bytes()), + label("ipv6_address") => Leaf("2001:0db8:85a3:0000:0000:8a2e:0370:7334".to_string().into_bytes()), }) - }) - ); - } else { - assert!( - mixed_tree.lookup(&path[..]).is_absent(), - "mixed_tree: {:#?}", - mixed_tree - ); - } + }) + }) + ); }) } @@ -4547,21 +4527,12 @@ fn certified_read_succeeds_for_empty_forks() { LookupStatus::Found(&ic_crypto_tree_hash::MixedHashTree::Empty) ); - if CURRENT_CERTIFICATION_VERSION > V15 { - // If there are no api boundary nodes present, the lookup status should be `MixedHashTree::Empty`. - // This behavior is in consistent with looking up `/streams` and `/canister`. - assert_matches!( - lookup_api_boundary_nodes, - LookupStatus::Found(&ic_crypto_tree_hash::MixedHashTree::Empty) - ); - } else { - // The `/api_boundary_nodes` subtree is not added to the state tree yet. The lookup status should be absent. - assert!( - lookup_api_boundary_nodes.is_absent(), - "api_boundary_nodes: {:#?}", - lookup_api_boundary_nodes - ) - } + // If there are no api boundary nodes present, the lookup status should be `MixedHashTree::Empty`. + // This behavior is in consistent with looking up `/streams` and `/canister`. + assert_matches!( + lookup_api_boundary_nodes, + LookupStatus::Found(&ic_crypto_tree_hash::MixedHashTree::Empty) + ); }) } diff --git a/rs/test_utilities/state/src/lib.rs b/rs/test_utilities/state/src/lib.rs index c21b6b1bf58..dbcdf866669 100644 --- a/rs/test_utilities/state/src/lib.rs +++ b/rs/test_utilities/state/src/lib.rs @@ -886,7 +886,7 @@ prop_compose! { )( msg_start in 0..10000u64, msgs in prop::collection::vec( - arbitrary::request_or_response_with_config(true, true), + arbitrary::request_or_response_with_config(true), min_size..=max_size ), (signals_end, reject_signals) in arb_reject_signals( @@ -950,12 +950,11 @@ prop_compose! { min_signal_count: usize, max_signal_count: usize, with_reject_reasons: Vec, - with_responses_only_flag: Vec, )( msg_start in 0..10000u64, msg_len in 0..10000u64, (signals_end, reject_signals) in arb_reject_signals(min_signal_count, max_signal_count, with_reject_reasons), - responses_only in proptest::sample::select(with_responses_only_flag), + responses_only in any::(), ) -> StreamHeader { let begin = StreamIndex::from(msg_start); let end = StreamIndex::from(msg_start + msg_len); diff --git a/rs/tests/ckbtc/ckbtc_minter_kyt.rs b/rs/tests/ckbtc/ckbtc_minter_kyt.rs index 903ee91707d..9c34f1eb4f4 100644 --- a/rs/tests/ckbtc/ckbtc_minter_kyt.rs +++ b/rs/tests/ckbtc/ckbtc_minter_kyt.rs @@ -34,6 +34,7 @@ use ic_tests_ckbtc::{ }; use icrc_ledger_agent::{CallMode, Icrc1Agent}; use icrc_ledger_types::icrc1::{account::Account, transfer::TransferArg}; +use icrc_ledger_types::icrc3::blocks::GetBlocksRequest; use slog::debug; /// Test update_balance method of the minter canister. @@ -332,6 +333,15 @@ pub fn test_kyt(env: TestEnv) { { assert_eq!(error_code, 1); assert_eq!(error_message, "Destination address is tainted"); + // assert that the ckBTC ledger didn't add more transactions + let txs = ledger_agent + .get_blocks(GetBlocksRequest { + start: transfer_result, + length: 10_u8.into(), + }) + .await + .expect("Error while calling ledger get_blocks"); + assert_eq!(txs.blocks.len(), 1); } else { panic!("Expected to see a tainted destination address.") } diff --git a/rs/tests/dashboards/IC/bitcoin-adapter.json b/rs/tests/dashboards/IC/bitcoin-adapter.json new file mode 100644 index 00000000000..cac7254b880 --- /dev/null +++ b/rs/tests/dashboards/IC/bitcoin-adapter.json @@ -0,0 +1,1502 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "enable": true, + "expr": "process_start_time_seconds{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"} * 1000", + "iconColor": "red", + "name": "Replica started", + "step": "10", + "tagKeys": "instance", + "titleFormat": "Replica started", + "useValueForTime": "on" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "production" + ], + "targetBlank": true, + "type": "dashboards" + } + ], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 28, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "# Deployments\n\n## Mainnet\n\nw4rem-dv5e3-widiz-wbpea-kbttk-mnzfm-tzrc7-svcj3-kbxyb-zamch-hqe\n\n## Testnet\n\nw4rem-dv5e3-widiz-wbpea-kbttk-mnzfm-tzrc7-svcj3-kbxyb-zamch-hqe", + "mode": "markdown" + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Info", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 7 + }, + "id": 36, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "adapter_btc${btc_network}_process_resident_memory_bytes{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Adapter memory usage", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 7 + }, + "id": 42, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "adapter_btc${btc_network}_requests_total{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}", + "legendFormat": "{{type}}/{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Replica requests", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 15 + }, + "id": 26, + "panels": [], + "title": "General adapter metrics", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 44, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "changes(adapter_btc${btc_network}_idle_total{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}[$period])", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Idle state transitions", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 24 + }, + "id": 41, + "panels": [], + "title": "Adapter bitcoin network metrics", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 25 + }, + "id": 24, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "adapter_btc${btc_network}_tip_height{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Adapter tip height", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 25 + }, + "id": 31, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "adapter_btc${btc_network}_block_cache_size_bytes{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Block cache size", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 33 + }, + "id": 35, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "adapter_btc${btc_network}_header_cache_size{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Number of headers stored", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 33 + }, + "id": 30, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "adapter_btc${btc_network}_bitcoin_network_messages_received_total{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}", + "legendFormat": "{{type}}/{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Received bitcoin network messages by type", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 41 + }, + "id": 33, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "adapter_btc${btc_network}_blockchain_tips{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Chain tips", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 41 + }, + "id": 32, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "adapter_btc${btc_network}_bitcoin_network_messages_sent_total{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}", + "legendFormat": "{{type}}/{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Sent bitcoin network messages by type", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 49 + }, + "id": 43, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "adapter_btc${btc_network}_tx_store_size{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Number of transaction stored", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 57 + }, + "id": 39, + "panels": [], + "title": "Adapter connection metrics", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 58 + }, + "id": 29, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "adapter_btc${btc_network}_available_connections{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Adapter active connections", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 58 + }, + "id": 37, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "adapter_btc${btc_network}_known_peer_addresses{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Number of peer addresses stored", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 66 + }, + "id": 34, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "rate(adapter_btc${btc_network}_connection_total{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}[$period])", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Connection setup rate", + "type": "timeseries" + } + ], + "refresh": false, + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "IC Metrics (cluster local)", + "value": "IC Metrics (cluster local)" + }, + "hide": 0, + "includeAll": false, + "multi": false, + "name": "datasource", + "options": [], + "query": "prometheus", + "queryValue": "", + "refresh": 1, + "regex": "/IC.*/", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "mainnet", + "value": "mainnet" + }, + "hide": 0, + "includeAll": false, + "multi": false, + "name": "btc_network", + "options": [ + { + "selected": true, + "text": "mainnet", + "value": "mainnet" + }, + { + "selected": false, + "text": "testnet", + "value": "testnet" + } + ], + "query": "mainnet,testnet", + "queryValue": "", + "skipUrlSync": false, + "type": "custom" + }, + { + "current": { + "selected": false, + "text": "mercury", + "value": "mercury" + }, + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "definition": "label_values(ic)", + "hide": 0, + "includeAll": false, + "label": "ic", + "multi": false, + "name": "ic", + "options": [], + "query": { + "query": "label_values(ic)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "w4rem-dv5e3-widiz-wbpea-kbttk-mnzfm-tzrc7-svcj3-kbxyb-zamch-hqe", + "value": "w4rem-dv5e3-widiz-wbpea-kbttk-mnzfm-tzrc7-svcj3-kbxyb-zamch-hqe" + }, + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "definition": "label_values(up{ic=\"$ic\"},ic_subnet)", + "hide": 0, + "includeAll": false, + "label": "subnet", + "multi": false, + "name": "ic_subnet", + "options": [], + "query": { + "query": "label_values(up{ic=\"$ic\"},ic_subnet)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "definition": "label_values(up{ic=\"$ic\",job=\"replica\",ic_subnet=~\"$ic_subnet\"}, instance)", + "hide": 0, + "includeAll": true, + "label": "instance", + "multi": true, + "name": "instance", + "options": [], + "query": { + "query": "label_values(up{ic=\"$ic\",job=\"replica\",ic_subnet=~\"$ic_subnet\"}, instance)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "auto": true, + "auto_count": 200, + "auto_min": "20s", + "current": { + "selected": false, + "text": "auto", + "value": "$__auto_interval_period" + }, + "hide": 0, + "label": "Aggregation period", + "name": "period", + "options": [ + { + "selected": true, + "text": "auto", + "value": "$__auto_interval_period" + }, + { + "selected": false, + "text": "20s", + "value": "20s" + }, + { + "selected": false, + "text": "30s", + "value": "30s" + }, + { + "selected": false, + "text": "1m", + "value": "1m" + }, + { + "selected": false, + "text": "2m", + "value": "2m" + }, + { + "selected": false, + "text": "5m", + "value": "5m" + }, + { + "selected": false, + "text": "10m", + "value": "10m" + }, + { + "selected": false, + "text": "30m", + "value": "30m" + }, + { + "selected": false, + "text": "1h", + "value": "1h" + } + ], + "query": "20s,30s,1m,2m,5m,10m,30m,1h", + "queryValue": "", + "refresh": 2, + "skipUrlSync": false, + "type": "interval" + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "utc", + "title": "Bitcoin Adapter", + "uid": "bitcoin-adapter", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/rs/tests/dashboards/IC/https-outcalls.json b/rs/tests/dashboards/IC/https-outcalls.json new file mode 100644 index 00000000000..8e6d0f2c80a --- /dev/null +++ b/rs/tests/dashboards/IC/https-outcalls.json @@ -0,0 +1,2222 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "enable": true, + "expr": "process_start_time_seconds{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"} * 1000", + "iconColor": "red", + "name": "Replica started", + "step": "10", + "tagKeys": "instance", + "titleFormat": "Replica started", + "useValueForTime": "on" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "production" + ], + "targetBlank": true, + "type": "dashboards" + } + ], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "gridPos": { + "h": 5, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 28, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "# Notes\n\nThe exchange rate canister, which uses this feature is on the system subnet:\n\nuzr34-akd3s-xrdag-3ql62-ocgoh-ld2ao-tamcv-54e7j-krwgb-2gm4z-oqe\n", + "mode": "markdown" + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Info", + "type": "text" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 5 + }, + "id": 54, + "panels": [], + "title": "Global IC feature metrics ", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 6 + }, + "id": 50, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "sum by ($ic) (max by (ic_subnet) (replicated_state_consumed_cycles_from_replica_start{ic=\"$ic\", use_case=\"HTTPOutcalls\"}/1000000000000))", + "legendFormat": "Cycles [T]", + "range": true, + "refId": "A" + } + ], + "title": "Total cycles used (trillions)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "This metric is not persisted across upgrades. So a decrease usually means that a subnet upgraded", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "Success" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 6 + }, + "id": 52, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "sum by ($ic) (max by (ic_subnet) (canister_http_success_delivered{job=\"replica\",ic=\"$ic\"}))", + "legendFormat": "Success", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "sum by ($ic) (max by (ic_subnet) (canister_http_divergences_delivered{job=\"replica\",ic=\"$ic\"}))", + "hide": false, + "legendFormat": "Diverged", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "sum by ($ic) (max by (ic_subnet) (canister_http_timeouts_delivered{job=\"replica\",ic=\"$ic\"}))", + "hide": false, + "legendFormat": "Timeout", + "range": true, + "refId": "C" + } + ], + "title": "Total http requests", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 14 + }, + "id": 63, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "label_replace(\n avg by (ic_subnet) (rate(canister_http_success_delivered{job=\"replica\",ic=\"$ic\"}[$period])),\n \"ic_subnet\", \"$1\", \"ic_subnet\", \"([0-9a-z]+)-.*\"\n)", + "legendFormat": "{{ic_subnet}} Success", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "label_replace(\n avg by (ic_subnet) (rate(canister_http_timeouts_delivered{job=\"replica\",ic=\"$ic\"}[$period])),\n \"ic_subnet\", \"$1\", \"ic_subnet\", \"([0-9a-z]+)-.*\"\n)", + "hide": false, + "legendFormat": "{{ic_subnet}} Timeout", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "label_replace(\n avg by (ic_subnet) (rate(canister_http_divergences_delivered{job=\"replica\",ic=\"$ic\"}[$period])),\n \"ic_subnet\", \"$1\", \"ic_subnet\", \"([0-9a-z]+)-.*\"\n)", + "hide": false, + "legendFormat": "{{ic_subnet}} Diverged", + "range": true, + "refId": "C" + } + ], + "title": "Http requests rate by subnet", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "This metric is not persisted across upgrades. So a decrease usually means that a subnet upgraded", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "Success" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 14 + }, + "id": 62, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "sum by ($ic) (max by (ic_subnet) (canister_http_success_delivered{job=\"replica\", ic=\"$ic\", ic_subnet!=\"uzr34-akd3s-xrdag-3ql62-ocgoh-ld2ao-tamcv-54e7j-krwgb-2gm4z-oqe\"}))", + "legendFormat": "Success", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "sum by ($ic) (max by (ic_subnet) (canister_http_divergences_delivered{job=\"replica\",ic=\"$ic\", ic_subnet!=\"uzr34-akd3s-xrdag-3ql62-ocgoh-ld2ao-tamcv-54e7j-krwgb-2gm4z-oqe\"}))", + "hide": false, + "legendFormat": "Diverged", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "sum by ($ic) (max by (ic_subnet) (canister_http_timeouts_delivered{job=\"replica\",ic=\"$ic\", ic_subnet!=\"uzr34-akd3s-xrdag-3ql62-ocgoh-ld2ao-tamcv-54e7j-krwgb-2gm4z-oqe\"}))", + "hide": false, + "legendFormat": "Timeout", + "range": true, + "refId": "C" + } + ], + "title": "Total http requests without exchange rate canister", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 22 + }, + "id": 49, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "label_replace(\n avg by (ic_subnet) (replicated_state_consumed_cycles_from_replica_start{use_case=\"HTTPOutcalls\"}/1000000000000),\n \"ic_subnet\", \"$1\", \"ic_subnet\", \"([0-9a-z]+)-.*\"\n)\n", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Cycle usage in T by subnet", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 22 + }, + "id": 64, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "label_replace(\n avg by (ic_subnet) (rate(replicated_state_consumed_cycles_from_replica_start{ic = \"$ic\", job=\"replica\",use_case=\"HTTPOutcalls\"}[$period])/1000000000000),\n \"ic_subnet\", \"$1\", \"ic_subnet\", \"([0-9a-z]+)-.*\"\n)\n", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Cycle burn rate in T/s by subnet", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 30 + }, + "id": 39, + "panels": [], + "title": "Feature metrics", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 31 + }, + "id": 51, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "label_replace(\n max by (ic_subnet) (canister_http_success_delivered{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\"}),\n \"ic_subnet\", \"$1\", \"ic_subnet\", \"([0-9a-z]+)-.*\"\n)\n", + "legendFormat": "{{ic_subnet}} Success", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "label_replace(\n max by (ic_subnet) (canister_http_timeouts_delivered{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\"}),\n \"ic_subnet\", \"$1\", \"ic_subnet\", \"([0-9a-z]+)-.*\"\n)\n", + "hide": false, + "legendFormat": "{{ic_subnet}} Timeout", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "label_replace(\n max by (ic_subnet) (canister_http_divergences_delivered{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\"}),\n \"ic_subnet\", \"$1\", \"ic_subnet\", \"([0-9a-z]+)-.*\"\n)", + "hide": false, + "legendFormat": "{{ic_subnet}} Diverged", + "range": true, + "refId": "C" + } + ], + "title": "Total http requests", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 31 + }, + "id": 55, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "label_replace(\n avg by (ic_subnet) (rate(canister_http_success_delivered{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\"}[$period])),\n \"ic_subnet\", \"$1\", \"ic_subnet\", \"([0-9a-z]+)-.*\"\n)", + "legendFormat": "{{ic_subnet}} Success", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "label_replace(\n avg by (ic_subnet) (rate(canister_http_timeouts_delivered{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\"}[$period])),\n \"ic_subnet\", \"$1\", \"ic_subnet\", \"([0-9a-z]+)-.*\"\n)", + "hide": false, + "legendFormat": "{{ic_subnet}} Timeout", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "label_replace(\n avg by (ic_subnet) (rate(canister_http_divergences_delivered{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\"}[$period])),\n \"ic_subnet\", \"$1\", \"ic_subnet\", \"([0-9a-z]+)-.*\"\n)", + "hide": false, + "legendFormat": "{{ic_subnet}} Diverged", + "range": true, + "refId": "C" + } + ], + "title": "Http requests rate", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 39 + }, + "id": 26, + "panels": [], + "title": "Adapter metrics", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 40 + }, + "id": 36, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "adapter_canisterhttp_process_resident_memory_bytes{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Adapter memory usage", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 40 + }, + "id": 42, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "adapter_canisterhttp_requests_total{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Requests made by each adapter", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 48 + }, + "id": 45, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "rate(adapter_canisterhttp_requests_total{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}[$period])", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Adapter RPS", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "binBps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 48 + }, + "id": 46, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "rate(adapter_canisterhttp_network_traffic_bytes_total{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\",link=\"down\"}[$period])", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Incoming adapter network traffic", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "binBps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 56 + }, + "id": 44, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "rate(adapter_canisterhttp_network_traffic_bytes_total{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\",link=\"up\"}[$period])", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Outgoing adapter network traffic", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 56 + }, + "id": 48, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "adapter_canisterhttp_request_errors_total{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}", + "legendFormat": "{{cause}}/{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Total adapter errors", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 64 + }, + "id": 47, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "rate(adapter_canisterhttp_request_errors_total{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}[$period])", + "legendFormat": "{{cause}}/{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Adapter error rate", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 72 + }, + "id": 57, + "panels": [], + "title": "Adapter client metrics", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 73 + }, + "id": 59, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "canister_http_in_client_requests{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Adapter inflight requests", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 73 + }, + "id": 60, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "\nsum by (status_code) (rate(canister_http_external_http_request_duration_seconds_count{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}[$period]))\n", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Adapter request status codes", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 81 + }, + "id": 58, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(canister_http_external_http_request_duration_seconds_bucket{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}[$period])) by (le)) ", + "legendFormat": "p90", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.5, sum(rate(canister_http_external_http_request_duration_seconds_bucket{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}[$period])) by (le)) ", + "hide": false, + "legendFormat": "p50", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(canister_http_external_http_request_duration_seconds_bucket{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}[$period])) by (le)) ", + "hide": false, + "legendFormat": "p99", + "range": true, + "refId": "C" + } + ], + "title": "Adapter request duration", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 81 + }, + "id": 61, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(canister_http_transform_duration_seconds_bucket{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}[$period])) by (le)) \n", + "legendFormat": "p90", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "\nhistogram_quantile(0.5, sum(rate(canister_http_transform_duration_seconds_bucket{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}[$period])) by (le)) \n", + "hide": false, + "legendFormat": "p50", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(canister_http_transform_duration_seconds_bucket{job=\"replica\",ic=\"$ic\",ic_subnet=~\"$ic_subnet\",instance=~\"$instance\"}[$period])) by (le)) \n", + "hide": false, + "legendFormat": "p99", + "range": true, + "refId": "C" + } + ], + "title": "Request transform duration", + "type": "timeseries" + } + ], + "refresh": false, + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "IC Metrics (cluster local)", + "value": "IC Metrics (cluster local)" + }, + "hide": 0, + "includeAll": false, + "multi": false, + "name": "datasource", + "options": [], + "query": "prometheus", + "queryValue": "", + "refresh": 1, + "regex": "/IC.*/", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "mercury", + "value": "mercury" + }, + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "definition": "label_values(ic)", + "hide": 0, + "includeAll": false, + "label": "ic", + "multi": false, + "name": "ic", + "options": [], + "query": { + "query": "label_values(ic)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": true, + "text": "uzr34-akd3s-xrdag-3ql62-ocgoh-ld2ao-tamcv-54e7j-krwgb-2gm4z-oqe", + "value": "uzr34-akd3s-xrdag-3ql62-ocgoh-ld2ao-tamcv-54e7j-krwgb-2gm4z-oqe" + }, + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "definition": "label_values(up{ic=\"$ic\"},ic_subnet)", + "hide": 0, + "includeAll": false, + "label": "subnet", + "multi": false, + "name": "ic_subnet", + "options": [], + "query": { + "query": "label_values(up{ic=\"$ic\"},ic_subnet)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": { + "type": "prometheus", + "uid": "000000001" + }, + "definition": "label_values(up{ic=\"$ic\",job=\"replica\",ic_subnet=~\"$ic_subnet\"}, instance)", + "hide": 0, + "includeAll": true, + "label": "instance", + "multi": true, + "name": "instance", + "options": [], + "query": { + "query": "label_values(up{ic=\"$ic\",job=\"replica\",ic_subnet=~\"$ic_subnet\"}, instance)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "auto": true, + "auto_count": 200, + "auto_min": "20s", + "current": { + "selected": false, + "text": "auto", + "value": "$__auto_interval_period" + }, + "hide": 0, + "label": "Aggregation period", + "name": "period", + "options": [ + { + "selected": true, + "text": "auto", + "value": "$__auto_interval_period" + }, + { + "selected": false, + "text": "20s", + "value": "20s" + }, + { + "selected": false, + "text": "30s", + "value": "30s" + }, + { + "selected": false, + "text": "1m", + "value": "1m" + }, + { + "selected": false, + "text": "2m", + "value": "2m" + }, + { + "selected": false, + "text": "5m", + "value": "5m" + }, + { + "selected": false, + "text": "10m", + "value": "10m" + }, + { + "selected": false, + "text": "30m", + "value": "30m" + }, + { + "selected": false, + "text": "1h", + "value": "1h" + } + ], + "query": "20s,30s,1m,2m,5m,10m,30m,1h", + "queryValue": "", + "refresh": 2, + "skipUrlSync": false, + "type": "interval" + } + ] + }, + "time": { + "from": "now-2d", + "to": "now" + }, + "timepicker": {}, + "timezone": "utc", + "title": "Https outcalls", + "uid": "https-outcalls", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/rs/tests/driver/src/util.rs b/rs/tests/driver/src/util.rs index 49d1021bcf7..ac381be4ddd 100644 --- a/rs/tests/driver/src/util.rs +++ b/rs/tests/driver/src/util.rs @@ -1424,7 +1424,6 @@ pub fn get_config() -> ConfigOptional { // Make the string parsable by filling the template placeholders with dummy values let cfg = String::from_utf8_lossy(CFG_TEMPLATE_BYTES) .to_string() - .replace("{{ node_index }}", "0") .replace("{{ ipv6_address }}", "::") .replace("{{ backup_retention_time_secs }}", "0") .replace("{{ backup_purging_interval_secs }}", "0") diff --git a/rs/types/types_test_utils/src/arbitrary.rs b/rs/types/types_test_utils/src/arbitrary.rs index f71216d6790..6ca7392afd0 100644 --- a/rs/types/types_test_utils/src/arbitrary.rs +++ b/rs/types/types_test_utils/src/arbitrary.rs @@ -123,9 +123,8 @@ prop_compose! { } prop_compose! { - /// Generates an arbitrary [`Request`], with or without populated `metadata` and - /// `deadline` fields. - pub fn request_with_config(populate_metadata: bool, populate_deadline: bool)( + /// Generates an arbitrary [`Request`], with or without a populated `deadline` field. + pub fn request_with_config(populate_deadline: bool)( receiver in canister_id(), sender in canister_id(), cycles_payment in any::(), @@ -142,7 +141,7 @@ prop_compose! { payment: Cycles::from(cycles_payment), method_name, method_payload, - metadata: if populate_metadata { metadata } else { None }, + metadata, deadline: if populate_deadline { deadline } else { NO_DEADLINE }, } } @@ -158,7 +157,7 @@ prop_compose! { // Always populate all fields, regardless of e.g. current certification version. // `ic_canonical_state` should not be using this generator; and all other crates / // proptests should be able to deal with all fields being populated. - request in request_with_config(true, true), + request in request_with_config(true), ) -> Request { request } @@ -213,15 +212,13 @@ prop_compose! { } } -/// Produces an arbitrary [`RequestOrResponse`], with the respective fields +/// Produces an arbitrary [`RequestOrResponse`], with the `deadline` field /// populated or not. pub fn request_or_response_with_config( - populate_request_metadata: bool, populate_deadline: bool, ) -> impl Strategy { prop_oneof![ - request_with_config(populate_request_metadata, populate_deadline) - .prop_flat_map(|req| Just(req.into())), + request_with_config(populate_deadline).prop_flat_map(|req| Just(req.into())), response_with_config(populate_deadline).prop_flat_map(|rep| Just(rep.into())), ] }