From 575ca531a70423ccfb33d9f2487ad7e76dfdc206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Bj=C3=B6rkqvist?= Date: Fri, 27 Dec 2024 09:43:44 +0200 Subject: [PATCH] chore(ICRC_Index): FI-1468: Remove old ICRC index canister (#3286) The old ICRC index canister, which stores the blocks on the heap (as opposed to the `index-ng` canister which stores the blocks in stable memory), is deprecated. To avoid confusion, remove it from the repository. --------- Co-authored-by: IDX GitHub Automation --- Cargo.lock | 35 - Cargo.toml | 1 - publish/canisters/BUILD.bazel | 1 - rs/bitcoin/ckbtc/mainnet/README.md | 9 +- rs/bitcoin/ckbtc/mainnet/dfx.json | 6 +- rs/bitcoin/ckbtc/staging/dfx.json | 118 +-- rs/ledger_suite/icrc1/index-ng/BUILD.bazel | 4 - rs/ledger_suite/icrc1/index-ng/Cargo.toml | 1 - rs/ledger_suite/icrc1/index-ng/tests/tests.rs | 142 +--- rs/ledger_suite/icrc1/index/BUILD.bazel | 114 --- rs/ledger_suite/icrc1/index/Cargo.toml | 46 -- rs/ledger_suite/icrc1/index/build.rs | 7 - rs/ledger_suite/icrc1/index/index.did | 96 --- rs/ledger_suite/icrc1/index/src/lib.rs | 733 ------------------ rs/ledger_suite/icrc1/index/src/main.rs | 102 --- rs/ledger_suite/icrc1/index/tests/tests.rs | 688 ---------------- rs/nns/integration_tests/BUILD.bazel | 2 - rs/nns/sns-wasm/BUILD.bazel | 2 - rs/sns/integration_tests/BUILD.bazel | 2 - 19 files changed, 71 insertions(+), 2038 deletions(-) delete mode 100644 rs/ledger_suite/icrc1/index/BUILD.bazel delete mode 100644 rs/ledger_suite/icrc1/index/Cargo.toml delete mode 100644 rs/ledger_suite/icrc1/index/build.rs delete mode 100644 rs/ledger_suite/icrc1/index/index.did delete mode 100644 rs/ledger_suite/icrc1/index/src/lib.rs delete mode 100644 rs/ledger_suite/icrc1/index/src/main.rs delete mode 100644 rs/ledger_suite/icrc1/index/tests/tests.rs diff --git a/Cargo.lock b/Cargo.lock index f7414e4e240..055bfd21060 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8948,40 +8948,6 @@ dependencies = [ "serde", ] -[[package]] -name = "ic-icrc1-index" -version = "0.9.0" -dependencies = [ - "assert_matches", - "async-trait", - "candid", - "candid_parser", - "ciborium", - "ic-base-types", - "ic-canister-profiler", - "ic-canisters-http-types", - "ic-cdk 0.16.0", - "ic-cdk-macros 0.9.0", - "ic-cdk-timers", - "ic-icrc1", - "ic-icrc1-ledger", - "ic-icrc1-tokens-u64", - "ic-ledger-canister-core", - "ic-ledger-core", - "ic-ledger-hash-of", - "ic-ledger-suite-state-machine-tests", - "ic-metrics-encoder", - "ic-rosetta-test-utils", - "ic-state-machine-tests", - "ic-test-utilities-load-wasm", - "icrc-ledger-types", - "num-traits", - "proptest", - "scopeguard", - "serde", - "serde_json", -] - [[package]] name = "ic-icrc1-index-ng" version = "0.9.0" @@ -8999,7 +8965,6 @@ dependencies = [ "ic-cdk-timers", "ic-crypto-sha2", "ic-icrc1", - "ic-icrc1-index", "ic-icrc1-ledger", "ic-icrc1-test-utils", "ic-icrc1-tokens-u256", diff --git a/Cargo.toml b/Cargo.toml index 3d246ee8c3b..a4352eccaa4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -288,7 +288,6 @@ members = [ "rs/rosetta-api/icp/ledger_canister_blocks_synchronizer", "rs/rosetta-api/icp/ledger_canister_blocks_synchronizer/test_utils", "rs/ledger_suite/icrc1", - "rs/ledger_suite/icrc1/index", "rs/ledger_suite/icrc1/index-ng", "rs/ledger_suite/icrc1/ledger", "rs/ledger_suite/tests/sm-tests", diff --git a/publish/canisters/BUILD.bazel b/publish/canisters/BUILD.bazel index b4f480f7003..1955eff350f 100644 --- a/publish/canisters/BUILD.bazel +++ b/publish/canisters/BUILD.bazel @@ -18,7 +18,6 @@ CANISTERS = { "ic-ckbtc-minter_debug.wasm.gz": "//rs/bitcoin/ckbtc/minter:ckbtc_minter_debug", "ic-ckbtc-kyt.wasm.gz": "//rs/bitcoin/ckbtc/kyt:kyt_canister", "ic-cketh-minter.wasm.gz": "//rs/ethereum/cketh/minter:cketh_minter", - "ic-icrc1-index.wasm.gz": "//rs/ledger_suite/icrc1/index:index_canister", "ic-icrc1-index-ng.wasm.gz": "//rs/ledger_suite/icrc1/index-ng:index_ng_canister", "ic-icrc1-index-ng-u256.wasm.gz": "//rs/ledger_suite/icrc1/index-ng:index_ng_canister_u256", "ic-icrc1-ledger.wasm.gz": "//rs/ledger_suite/icrc1/ledger:ledger_canister", diff --git a/rs/bitcoin/ckbtc/mainnet/README.md b/rs/bitcoin/ckbtc/mainnet/README.md index 8c746d4c706..b412d6e094d 100644 --- a/rs/bitcoin/ckbtc/mainnet/README.md +++ b/rs/bitcoin/ckbtc/mainnet/README.md @@ -3,7 +3,6 @@ Root canister id: `r7inp-6aaaa-aaaaa-aaabq-cai`. Subnet: `pzp6e-ekpqk-3c5x7-2h6so-njoeq-mt45d-h3h6c-q3mxf-vpeq5-fk5o7-yae` - ## Installing the minter ([`mqygn-kiaaa-aaaar-qaadq-cai`](https://dashboard.internetcomputer.org/canister/mqygn-kiaaa-aaaar-qaadq-cai)) Notes on init args: @@ -81,7 +80,7 @@ Notes on init args: * The transfer fee is 10 ckBTC Satoshis. * There are no initial balances: the minter is responsible for minting all ckBTC. * Archive max memory size is 3 GiB, or 3_221_225_472 bytes. We can afford to use that much memory because archives store transactions in stable memory. -* The `max_memo_length` was last udpated to 80 in [NNS proposal 123422](https://dashboard.internetcomputer.org/proposal/123422). +* The `max_memo_length` was last updated to 80 in [NNS proposal 123422](https://dashboard.internetcomputer.org/proposal/123422). The metadata contains the official ckBTC logo. @@ -138,7 +137,7 @@ bazel build //rs/registry/admin:ic-admin Encoding the init args: ```shell -didc encode -d ../../../ledger_suite/icrc1/index/index.did -t '(InitArgs)' '(record { ledger_id = principal "mxzaz-hqaaa-aaaar-qaada-cai" })' | xxd -r -p > index_arg.bin +didc encode -d ../../../ledger_suite/icrc1/index-ng/index-ng.did -t '(opt IndexArg)' '(opt variant { Init = record { ledger_id = principal "mxzaz-hqaaa-aaaar-qaada-cai" } })' | xxd -r -p > index_arg.bin ``` Submitting the install proposal: @@ -156,7 +155,7 @@ bazel build //rs/registry/admin:ic-admin --proposer $NEURON_ID \ --canister-id n5wcd-faaaa-aaaar-qaaea-cai \ --mode install \ - --wasm-module-path ./ic-icrc1-index.wasm.gz \ + --wasm-module-path ./ic-icrc1-index-ng.wasm.gz \ --wasm-module-sha256 $WASM_SHA256 \ --arg index_arg.bin \ --summary-file ./index_proposal.md @@ -177,7 +176,7 @@ bazel build //rs/registry/admin:ic-admin --proposer $NEURON_ID \ --canister-id n5wcd-faaaa-aaaar-qaaea-cai \ --mode upgrade \ - --wasm-module-path ./ic-icrc1-index.wasm.gz \ + --wasm-module-path ./ic-icrc1-index-ng.wasm.gz \ --wasm-module-sha256 $WASM_SHA256 \ --summary-file ./index_upgrade.md ``` diff --git a/rs/bitcoin/ckbtc/mainnet/dfx.json b/rs/bitcoin/ckbtc/mainnet/dfx.json index 48d51478d74..ca0f34feb16 100644 --- a/rs/bitcoin/ckbtc/mainnet/dfx.json +++ b/rs/bitcoin/ckbtc/mainnet/dfx.json @@ -32,10 +32,10 @@ "index": { "type": "custom", "build": [ - "bazel build //rs/ledger_suite/icrc1/index:index_canister" + "bazel build //rs/ledger_suite/icrc1/index-ng:index_ng_canister" ], - "wasm": "../../../../bazel-bin/rs/ledger_suite/icrc1/index/index_canister.wasm", - "candid": "../../../ledger_suite/icrc1/index/index.did", + "wasm": "../../../../bazel-bin/rs/ledger_suite/icrc1/index-ng/index_ng_canister.wasm", + "candid": "../../../ledger_suite/icrc1/index-ng/index-ng.did", "shrink": false }, "btc_checker": { diff --git a/rs/bitcoin/ckbtc/staging/dfx.json b/rs/bitcoin/ckbtc/staging/dfx.json index 2089106eb7f..633a51aa2ef 100644 --- a/rs/bitcoin/ckbtc/staging/dfx.json +++ b/rs/bitcoin/ckbtc/staging/dfx.json @@ -1,63 +1,63 @@ { - "version": 1, - "dfx": "0.12.1", - "canisters": { - "minter": { - "type": "custom", - "build": [ - "bazel build //rs/bitcoin/ckbtc/minter:ckbtc_minter" - ], - "wasm": "../../../../bazel-bin/rs/bitcoin/ckbtc/minter/ckbtc_minter.wasm", - "candid": "../minter/ckbtc_minter.did", - "shrink": false - }, - "ledger": { - "type": "custom", - "build": [ - "bazel build //rs/ledger_suite/icrc1/ledger:ledger_canister" - ], - "wasm": "../../../../bazel-bin/rs/ledger_suite/icrc1/ledger/ledger_canister.wasm", - "candid": "../../../ledger_suite/icrc1/ledger/ledger.did", - "shrink": false - }, - "archive": { - "type": "custom", - "build": [ - "bazel build //rs/ledger_suite/icrc1/archive:archive_canister" - ], - "wasm": "../../../../bazel-bin/rs/ledger_suite/icrc1/archive/archive_canister.wasm", - "candid": "../../../ledger_suite/icrc1/archive/archive.did", - "shrink": false - }, - "index": { - "type": "custom", - "build": [ - "bazel build //rs/ledger_suite/icrc1/index:index_canister" - ], - "wasm": "../../../../bazel-bin/rs/ledger_suite/icrc1/index/index_canister.wasm", - "candid": "../../../ledger_suite/icrc1/index/index.did", - "shrink": false - }, - "kyt": { - "type": "custom", - "build": [ - "bazel build //rs/bitcoin/ckbtc/kyt:kyt_canister" - ], - "wasm": "../../../../bazel-bin/rs/bitcoin/ckbtc/kyt/kyt_canister.wasm", - "candid": "../kyt/kyt.did", - "shrink": false - } + "version": 1, + "dfx": "0.12.1", + "canisters": { + "minter": { + "type": "custom", + "build": [ + "bazel build //rs/bitcoin/ckbtc/minter:ckbtc_minter" + ], + "wasm": "../../../../bazel-bin/rs/bitcoin/ckbtc/minter/ckbtc_minter.wasm", + "candid": "../minter/ckbtc_minter.did", + "shrink": false }, - "defaults": { - "build": { - "packtool": "", - "args": "" - } + "ledger": { + "type": "custom", + "build": [ + "bazel build //rs/ledger_suite/icrc1/ledger:ledger_canister" + ], + "wasm": "../../../../bazel-bin/rs/ledger_suite/icrc1/ledger/ledger_canister.wasm", + "candid": "../../../ledger_suite/icrc1/ledger/ledger.did", + "shrink": false }, - "networks": { - "local": { - "bind": "127.0.0.1:8000", - "type": "ephemeral" - } + "archive": { + "type": "custom", + "build": [ + "bazel build //rs/ledger_suite/icrc1/archive:archive_canister" + ], + "wasm": "../../../../bazel-bin/rs/ledger_suite/icrc1/archive/archive_canister.wasm", + "candid": "../../../ledger_suite/icrc1/archive/archive.did", + "shrink": false + }, + "index": { + "type": "custom", + "build": [ + "bazel build //rs/ledger_suite/icrc1/index-ng:index_ng_canister" + ], + "wasm": "../../../../bazel-bin/rs/ledger_suite/icrc1/index-ng/index_ng_canister.wasm", + "candid": "../../../ledger_suite/icrc1/index-ng/index-ng.did", + "shrink": false + }, + "kyt": { + "type": "custom", + "build": [ + "bazel build //rs/bitcoin/ckbtc/kyt:kyt_canister" + ], + "wasm": "../../../../bazel-bin/rs/bitcoin/ckbtc/kyt/kyt_canister.wasm", + "candid": "../kyt/kyt.did", + "shrink": false + } + }, + "defaults": { + "build": { + "packtool": "", + "args": "" + } + }, + "networks": { + "local": { + "bind": "127.0.0.1:8000", + "type": "ephemeral" } -} \ No newline at end of file + } +} diff --git a/rs/ledger_suite/icrc1/index-ng/BUILD.bazel b/rs/ledger_suite/icrc1/index-ng/BUILD.bazel index b4423b3292c..832eb1a3c60 100644 --- a/rs/ledger_suite/icrc1/index-ng/BUILD.bazel +++ b/rs/ledger_suite/icrc1/index-ng/BUILD.bazel @@ -98,8 +98,6 @@ rust_test( ], crate_features = conf["crate_features"], data = [ - "//rs/ledger_suite/icrc1/index:index_canister.wasm", - ] + [ conf["index_wasm"], conf["ledger_wasm"], ], @@ -107,7 +105,6 @@ rust_test( "RUST_TEST_THREADS": "4", "CARGO_MANIFEST_DIR": "rs/ledger_suite/icrc1/index-ng", "IC_ICRC1_INDEX_NG_WASM_PATH": "$(rootpath " + conf["index_wasm"] + ")", - "IC_ICRC1_INDEX_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/index:index_canister.wasm)", "IC_ICRC1_LEDGER_WASM_PATH": "$(rootpath " + conf["ledger_wasm"] + ")", }, extra_srcs = ["tests/common/mod.rs"], @@ -119,7 +116,6 @@ rust_test( "//rs/ledger_suite/common/ledger_canister_core", "//rs/ledger_suite/common/ledger_core", "//rs/ledger_suite/icrc1", - "//rs/ledger_suite/icrc1/index", "//rs/ledger_suite/icrc1/ledger", "//rs/ledger_suite/icrc1/test_utils", "//rs/ledger_suite/icrc1/tokens_u64", diff --git a/rs/ledger_suite/icrc1/index-ng/Cargo.toml b/rs/ledger_suite/icrc1/index-ng/Cargo.toml index ae0eeefb44c..e023a262261 100644 --- a/rs/ledger_suite/icrc1/index-ng/Cargo.toml +++ b/rs/ledger_suite/icrc1/index-ng/Cargo.toml @@ -38,7 +38,6 @@ serde_json = { workspace = true } candid_parser = { workspace = true } ic-agent = { workspace = true } ic-base-types = { path = "../../../types/base_types" } -ic-icrc1-index = { path = "../index" } ic-icrc1-ledger = { path = "../ledger" } ic-ledger-suite-state-machine-tests = { path = "../../tests/sm-tests" } ic-icrc1-test-utils = { path = "../test_utils" } diff --git a/rs/ledger_suite/icrc1/index-ng/tests/tests.rs b/rs/ledger_suite/icrc1/index-ng/tests/tests.rs index 80369d5d978..9edffa62dde 100644 --- a/rs/ledger_suite/icrc1/index-ng/tests/tests.rs +++ b/rs/ledger_suite/icrc1/index-ng/tests/tests.rs @@ -35,7 +35,7 @@ mod common; fn index_wasm() -> Vec { ic_test_utilities_load_wasm::load_wasm( std::env::var("CARGO_MANIFEST_DIR").unwrap(), - "ic-icrc1-index", + "ic-icrc1-index-ng", &[], ) } @@ -69,12 +69,6 @@ fn index_init_arg_without_interval(ledger_id: CanisterId) -> IndexInitArg { } } -fn install_index(env: &StateMachine, ledger_id: CanisterId) -> CanisterId { - let args = ic_icrc1_index::InitArgs { ledger_id }; - env.install_canister(index_wasm(), Encode!(&args).unwrap(), None) - .unwrap() -} - fn icrc1_balance_of(env: &StateMachine, canister_id: CanisterId, account: Account) -> u64 { let res = env .execute_ingress(canister_id, "icrc1_balance_of", Encode!(&account).unwrap()) @@ -244,28 +238,6 @@ fn approve( .unwrap() } -// Same as get_account_transactions but with the old index interface. -fn old_get_account_transactions( - env: &StateMachine, - index_id: CanisterId, - account: Account, - start: Option, - max_results: u64, -) -> ic_icrc1_index::GetTransactionsResult { - let req = ic_icrc1_index::GetAccountTransactionsArgs { - account, - start: start.map(|n| n.into()), - max_results: max_results.into(), - }; - let req = Encode!(&req).expect("Failed to encode GetAccountTransactionsArgs"); - let res = env - .execute_ingress(index_id, "get_account_transactions", req) - .expect("Failed to get_account_transactions") - .bytes(); - Decode!(&res, ic_icrc1_index::GetTransactionsResult) - .expect("Failed to decode GetTransactionsResult") -} - fn get_account_transactions( env: &StateMachine, index_id: CanisterId, @@ -1131,110 +1103,6 @@ fn test_fee_collector() { ); } -#[test] -fn test_get_account_transactions_vs_old_index() { - let mut runner = TestRunner::new(TestRunnerConfig::with_cases(1)); - let now = SystemTime::now(); - let minter = Arc::new(minter_identity()); - let minter_principal = minter.sender().unwrap(); - runner - .run( - &(valid_transactions_strategy(minter, FEE, 10, now),), - |(transactions,)| { - let env = &StateMachine::new(); - // To match the time of the valid transaction strategy we have to align the StateMachine time with the generated strategy - env.set_time(now); - let ledger_id = install_ledger( - env, - vec![], - default_archive_options(), - None, - minter_principal, - ); - let index_ng_id = install_index_ng(env, index_init_arg_without_interval(ledger_id)); - let index_id = install_index(env, ledger_id); - - for arg_with_caller in &transactions { - apply_arg_with_caller(env, ledger_id, arg_with_caller.clone()); - } - wait_until_sync_is_completed(env, index_ng_id, ledger_id); - - for account in transactions - .iter() - .flat_map(|tx| tx.accounts()) - .collect::>() - { - assert_eq!( - old_get_account_transactions(env, index_id, account, None, u64::MAX), - old_get_account_transactions(env, index_ng_id, account, None, u64::MAX), - ); - } - - Ok(()) - }, - ) - .unwrap(); -} - -#[test] -fn test_upgrade_index_to_index_ng() { - let mut runner = TestRunner::new(TestRunnerConfig { - cases: 1, - max_shrink_iters: 0, - ..Default::default() - }); - let now = SystemTime::now(); - let minter = Arc::new(minter_identity()); - let minter_principal = minter.sender().unwrap(); - runner - .run( - &(valid_transactions_strategy(minter, FEE, 10, now),), - |(transactions,)| { - let env = &StateMachine::new(); - // To match the time of the valid transaction strategy we have to align the StateMachine time with the generated strategy - env.set_time(now); - let ledger_id = install_ledger( - env, - vec![], - default_archive_options(), - None, - minter_principal, - ); - let index_ng_id = install_index_ng(env, index_init_arg_without_interval(ledger_id)); - let index_id = install_index(env, ledger_id); - - for arg_with_caller in &transactions { - apply_arg_with_caller(env, ledger_id, arg_with_caller.clone()); - } - - env.tick(); - wait_until_sync_is_completed(env, index_ng_id, ledger_id); - - // Upgrade the index canister to the index-ng. - let arg = Encode!(&None::).unwrap(); - env.upgrade_canister(index_id, index_ng_wasm(), arg) - .unwrap(); - - wait_until_sync_is_completed(env, index_id, ledger_id); - - // Check that the old get_account_transactions still works and return - // the right data. - for account in transactions - .iter() - .flat_map(|tx| tx.accounts()) - .collect::>() - { - assert_eq!( - old_get_account_transactions(env, index_id, account, None, u64::MAX), - old_get_account_transactions(env, index_ng_id, account, None, u64::MAX), - ); - } - Ok(()) - }, - ) - .unwrap(); -} - #[test] fn test_index_ledger_coherence() { let mut runner = TestRunner::new(TestRunnerConfig::with_cases(1)); @@ -1349,7 +1217,7 @@ fn test_index_http_request_decoding_quota() { mod metrics { use crate::index_wasm; use candid::Principal; - use ic_icrc1_index_ng::InitArg; + use ic_icrc1_index_ng::{IndexArg, InitArg}; #[test] fn should_export_heap_memory_usage_bytes_metrics() { @@ -1359,10 +1227,10 @@ mod metrics { ); } - fn encode_init_args(ledger_id: Principal) -> InitArg { - InitArg { + fn encode_init_args(ledger_id: Principal) -> Option { + Some(IndexArg::Init(InitArg { ledger_id, retrieve_blocks_from_ledger_interval_seconds: None, - } + })) } } diff --git a/rs/ledger_suite/icrc1/index/BUILD.bazel b/rs/ledger_suite/icrc1/index/BUILD.bazel deleted file mode 100644 index b49d03b4a07..00000000000 --- a/rs/ledger_suite/icrc1/index/BUILD.bazel +++ /dev/null @@ -1,114 +0,0 @@ -load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") -load("//bazel:canisters.bzl", "rust_canister") -load("//bazel:defs.bzl", "rust_ic_test") - -package(default_visibility = ["//visibility:public"]) - -DEPENDENCIES = [ - # Keep sorted. - "//packages/icrc-ledger-types:icrc_ledger_types", - "//rs/ledger_suite/common/ledger_canister_core", - "//rs/ledger_suite/icrc1", - "//rs/ledger_suite/icrc1/ledger", - "//rs/ledger_suite/icrc1/tokens_u64", - "//rs/types/base_types", - "@crate_index//:candid", - "@crate_index//:ciborium", - "@crate_index//:ic-cdk", - "@crate_index//:ic-cdk-timers", - "@crate_index//:ic-metrics-encoder", - "@crate_index//:num-traits", - "@crate_index//:scopeguard", - "@crate_index//:serde", -] - -MACRO_DEPENDENCIES = [ - # Keep sorted. - "@crate_index//:async-trait", - "@crate_index//:ic-cdk-macros", -] - -rust_library( - name = "index", - srcs = glob( - ["src/**"], - exclude = ["src/main.rs"], - ), - crate_name = "ic_icrc1_index", - proc_macro_deps = MACRO_DEPENDENCIES, - deps = DEPENDENCIES + [ - "//rs/rust_canisters/canister_profiler", - ], -) - -rust_canister( - name = "index_canister", - srcs = ["src/main.rs"], - compile_data = ["index.did"], - crate_name = "ic_icrc1_index_canister", - proc_macro_deps = MACRO_DEPENDENCIES, - rustc_env = { - "INDEX_DID_PATH": "$(execpath :index.did)", - }, - service_file = ":index.did", - deps = [ - # Keep sorted. - ":index", - "//rs/rust_canisters/http_types", - ] + DEPENDENCIES, -) - -rust_test( - name = "index_crate_test", - crate = ":index", - deps = DEPENDENCIES + [ - "@crate_index//:assert_matches", - "@crate_index//:proptest", - "@crate_index//:serde_json", - ], -) - -rust_test( - name = "index_canister_test", - crate = ":_wasm_index_canister", - data = [":index.did"], - env = { - "CARGO_MANIFEST_DIR": "rs/ledger_suite/icrc1/index", - }, - deps = ["@crate_index//:candid_parser"], -) - -rust_ic_test( - name = "index_test", - srcs = ["tests/tests.rs"], - data = [ - ":index_canister.wasm", - "//rs/ledger_suite/icrc1/ledger:ledger_canister.wasm", - ], - env = { - "CARGO_MANIFEST_DIR": "rs/ledger_suite/icrc1/index", - "IC_ICRC1_INDEX_WASM_PATH": "$(rootpath :index_canister.wasm)", - "IC_ICRC1_LEDGER_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/ledger:ledger_canister.wasm)", - }, - deps = [ - # Keep sorted. - ":index", - "//packages/ic-ledger-hash-of:ic_ledger_hash_of", - "//packages/icrc-ledger-types:icrc_ledger_types", - "//rs/ledger_suite/common/ledger_canister_core", - "//rs/ledger_suite/common/ledger_core", - "//rs/ledger_suite/icrc1", - "//rs/ledger_suite/icrc1/ledger", - "//rs/ledger_suite/icrc1/tokens_u64", - "//rs/ledger_suite/tests/sm-tests:ic-ledger-suite-state-machine-tests", - "//rs/rosetta-api/icp/test_utils", - "//rs/rust_canisters/http_types", - "//rs/state_machine_tests", - "//rs/test_utilities/load_wasm", - "//rs/types/base_types", - "@crate_index//:assert_matches", - "@crate_index//:candid", - "@crate_index//:num-traits", - "@crate_index//:proptest", - ], -) diff --git a/rs/ledger_suite/icrc1/index/Cargo.toml b/rs/ledger_suite/icrc1/index/Cargo.toml deleted file mode 100644 index 4826f616c8b..00000000000 --- a/rs/ledger_suite/icrc1/index/Cargo.toml +++ /dev/null @@ -1,46 +0,0 @@ -[package] -name = "ic-icrc1-index" -version.workspace = true -authors.workspace = true -edition.workspace = true -description.workspace = true -documentation.workspace = true - -[dependencies] -async-trait = { workspace = true } -candid = { workspace = true } -ciborium = { workspace = true } -ic-base-types = { path = "../../../types/base_types" } -ic-canister-profiler = { path = "../../../rust_canisters/canister_profiler" } -ic-canisters-http-types = { path = "../../../rust_canisters/http_types" } -ic-cdk = { workspace = true } -ic-cdk-macros = { workspace = true } -ic-cdk-timers = { workspace = true } -ic-icrc1 = { path = ".." } -ic-icrc1-ledger = { path = "../ledger" } -ic-icrc1-tokens-u64 = { path = "../tokens_u64" } -ic-ledger-canister-core = { path = "../../common/ledger_canister_core" } -ic-ledger-hash-of = { path = "../../../../packages/ic-ledger-hash-of" } -ic-metrics-encoder = "1.1" -icrc-ledger-types = { path = "../../../../packages/icrc-ledger-types" } -num-traits = { workspace = true } -scopeguard = "1.1.0" -serde = { workspace = true } - -[dev-dependencies] -assert_matches = { workspace = true } -candid_parser = { workspace = true } -ic-base-types = { path = "../../../types/base_types" } -ic-icrc1-ledger = { path = "../ledger" } -ic-ledger-suite-state-machine-tests = { path = "../../tests/sm-tests" } -ic-ledger-core = { path = "../../common/ledger_core" } -ic-rosetta-test-utils = { path = "../../../rosetta-api/icp/test_utils" } -ic-state-machine-tests = { path = "../../../state_machine_tests" } -ic-test-utilities-load-wasm = { path = "../../../test_utilities/load_wasm" } -num-traits = { workspace = true } -proptest = { workspace = true } -serde_json = { workspace = true } - -[[bin]] -name = "ic-icrc1-index" -path = "src/main.rs" diff --git a/rs/ledger_suite/icrc1/index/build.rs b/rs/ledger_suite/icrc1/index/build.rs deleted file mode 100644 index 935b0b0e0fa..00000000000 --- a/rs/ledger_suite/icrc1/index/build.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() { - let did_path = std::path::PathBuf::from("index.did") - .canonicalize() - .unwrap(); - - println!("cargo:rustc-env=INDEX_DID_PATH={}", did_path.display()); -} diff --git a/rs/ledger_suite/icrc1/index/index.did b/rs/ledger_suite/icrc1/index/index.did deleted file mode 100644 index a6b35d0c89f..00000000000 --- a/rs/ledger_suite/icrc1/index/index.did +++ /dev/null @@ -1,96 +0,0 @@ -type TxId = nat; - -type Account = record { owner : principal; subaccount : opt blob }; - -type SubAccount = blob; - -type Transaction = record { - burn : opt Burn; - kind : text; - mint : opt Mint; - approve : opt Approve; - timestamp : nat64; - transfer : opt Transfer; -}; - -type Approve = record { - fee : opt nat; - from : Account; - memo : opt vec nat8; - created_at_time : opt nat64; - amount : nat; - expected_allowance : opt nat; - expires_at : opt nat64; - spender : Account; -}; - -type Burn = record { - from : Account; - memo : opt vec nat8; - created_at_time : opt nat64; - amount : nat; - spender : opt Account; -}; - -type Mint = record { - to : Account; - memo : opt vec nat8; - created_at_time : opt nat64; - amount : nat; -}; - -type Transfer = record { - to : Account; - fee : opt nat; - from : Account; - memo : opt vec nat8; - created_at_time : opt nat64; - amount : nat; - spender : opt Account; -}; - -type GetAccountTransactionsArgs = record { - account : Account; - // The txid of the last transaction seen by the client. - // If None then the results will start from the most recent - // txid. - start : opt TxId; - // Maximum number of transactions to fetch. - max_results : nat; -}; - -type TransactionWithId = record { - id : TxId; - transaction : Transaction; -}; - -type GetTransactions = record { - transactions : vec TransactionWithId; - // The txid of the oldest transaction the account has - oldest_tx_id : opt TxId; -}; - -type GetTransactionsErr = record { - message : text; -}; - -type GetTransactionsResult = variant { - Ok : GetTransactions; - Err : GetTransactionsErr; -}; - -type ListSubaccountsArgs = record { - owner: principal; - start: opt SubAccount; -}; - -// The initialization parameters of the Index canister. -type InitArgs = record { - ledger_id : principal; -}; - -service : (InitArgs) -> { - get_account_transactions : (GetAccountTransactionsArgs) -> (GetTransactionsResult); - ledger_id : () -> (principal) query; - list_subaccounts : (ListSubaccountsArgs) -> (vec SubAccount) query; -} diff --git a/rs/ledger_suite/icrc1/index/src/lib.rs b/rs/ledger_suite/icrc1/index/src/lib.rs deleted file mode 100644 index 6ba18e97592..00000000000 --- a/rs/ledger_suite/icrc1/index/src/lib.rs +++ /dev/null @@ -1,733 +0,0 @@ -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::heap_memory_size_bytes; -use icrc_ledger_types::icrc1::transfer::BlockIndex; -use icrc_ledger_types::icrc3::archive::QueryTxArchiveFn; -use icrc_ledger_types::icrc3::transactions::{ - Approve, GetTransactionsResponse, Transaction, TransactionRange, Transfer, -}; -use icrc_ledger_types::{ - icrc1::account::Account, icrc1::account::Subaccount, icrc3::archive::ArchivedRange, - icrc3::transactions::GetTransactionsRequest, -}; -use num_traits::cast::ToPrimitive; -use scopeguard::{guard, ScopeGuard}; -use serde::{Deserialize, Serialize}; -use std::cell::RefCell; -use std::collections::{btree_map, BTreeMap}; -use std::ops::Bound::{Included, Unbounded}; -use std::time::Duration; - -// Maximum number of subaccounts that can be returned -// by [list_subaccounts] -const MAX_SUBACCOUNTS_PER_RESPONSE: usize = 1000; - -// Maximum number of transactions that can be returned -// by [get_account_transactions] -const MAX_TRANSACTIONS_PER_RESPONSE: usize = 1000; - -// One second in nanosecond -const SEC_NANOS: u64 = 1_000_000_000; -const DEFAULT_MAX_WAIT_TIME_NANOS: u64 = 2_u64 * SEC_NANOS; -const DEFAULT_RETRY_WAIT_TIME_NANOS: u64 = 2_u64 * SEC_NANOS; - -const LOG_PREFIX: &str = "[ic-icrc1-index] "; - -#[derive(Debug, Deserialize, Serialize)] -struct Index { - // The id of the Ledger canister to index - pub ledger_id: CanisterId, - - // The next txid to query from the Ledger - pub next_txid: u64, - - // Whether there is a build_index running right now - #[serde(default)] - pub is_build_index_running: bool, - - // Last wait time in nanoseconds. - #[serde(default)] - pub last_wait_time: u64, - - // The index of transactions per account - pub account_index: BTreeMap>>, - - // The number of unique (principal, subaccount) pairs in the index. - pub accounts_num: u64, -} - -impl Index { - fn from(init_args: InitArgs) -> Self { - Self { - ledger_id: init_args.ledger_id, - next_txid: 0, - is_build_index_running: false, - account_index: BTreeMap::new(), - accounts_num: 0, - last_wait_time: 0, - } - } -} - -thread_local! { - static INDEX: RefCell> = const { RefCell::new(None) }; - static PROFILING_DATA: RefCell = RefCell::new(SpanStats::default()); -} - -fn with_index(f: impl FnOnce(&Index) -> R) -> R { - INDEX.with(|idx| f(idx.borrow().as_ref().expect("Index state is not set!"))) -} - -fn with_index_mut(f: impl FnOnce(&mut Index) -> R) -> R { - INDEX.with(|idx| f(idx.borrow_mut().as_mut().expect("Index state is not set!"))) -} - -pub fn ledger_id() -> CanisterId { - with_index(|idx| idx.ledger_id) -} - -#[derive(Clone, Debug, CandidType, candid::Deserialize)] -pub struct InitArgs { - // The Ledger canister id of the Ledger to index. - pub ledger_id: CanisterId, -} - -pub fn init(init_args: InitArgs) { - INDEX.with(|idx| *idx.borrow_mut() = Some(Index::from(init_args))); -} - -#[derive(Eq, PartialEq, Debug, CandidType, candid::Deserialize)] -pub struct GetAccountTransactionsArgs { - pub account: Account, - // The txid of the last transaction seen by the client. - // If None then the results will start from the most recent - // txid. - pub start: Option, - // Maximum number of transactions to fetch. - pub max_results: Nat, -} - -#[derive(Eq, PartialEq, Debug, CandidType, candid::Deserialize)] -pub struct TransactionWithId { - pub id: BlockIndex, - pub transaction: Transaction, -} - -#[derive(Eq, PartialEq, Debug, CandidType, candid::Deserialize)] -pub struct GetTransactions { - pub transactions: Vec, - // The txid of the oldest transaction the account has - pub oldest_tx_id: Option, -} - -#[derive(Eq, PartialEq, Debug, CandidType, candid::Deserialize)] -pub struct GetTransactionsErr { - pub message: String, -} - -pub type GetTransactionsResult = Result; - -#[derive(Eq, PartialEq, Debug, CandidType, Deserialize)] -pub struct ListSubaccountsArgs { - pub owner: PrincipalId, - // The last subaccount seen by the client for the given principal. - // This subaccount is included in the result. - // If None then the results will start from the first - // in natural order. - pub start: Option, -} - -pub fn list_subaccounts(list_subaccounts_args: ListSubaccountsArgs) -> Vec { - with_index( - |idx| match idx.account_index.get(&list_subaccounts_args.owner) { - None => vec![], - Some(subaccounts) => subaccounts - .range(( - list_subaccounts_args - .start - .map(Included) - .unwrap_or(Unbounded), - Unbounded, - )) - .take(MAX_SUBACCOUNTS_PER_RESPONSE) - .map(|(k, _)| *k) - .collect(), - }, - ) -} - -async fn get_transactions_from_ledger( - start: u64, - length: usize, -) -> Result { - let ledger_id = ledger_id(); - let req = GetTransactionsRequest { - start: Nat::from(start), - length: Nat::from(length), - }; - let (res,): (GetTransactionsResponse,) = - ic_cdk::call(ledger_id.get().0, "get_transactions", (req,)) - .await - .map_err(|(code, str)| format!("code: {:#?} message: {}", code, str))?; - Ok(res) -} - -async fn get_transactions_from_archive( - archived: &ArchivedRange, -) -> Result { - let req = GetTransactionsRequest { - start: archived.start.clone(), - length: archived.length.clone(), - }; - let (res,): (TransactionRange,) = ic_cdk::call( - archived.callback.canister_id, - &archived.callback.method, - (req,), - ) - .await - .map_err(|(code, str)| format!("code: {:#?} message: {}", code, str))?; - Ok(res) -} - -pub async fn build_index() -> Result<(), String> { - if with_index(|idx| idx.is_build_index_running) { - return Err("build_index already running".to_string()); - } - with_index_mut(|idx| { - idx.is_build_index_running = true; - }); - let failure_guard = guard((), |_| { - with_index_mut(|idx| { - idx.is_build_index_running = false; - }); - ic_cdk_timers::set_timer(Duration::from_nanos(DEFAULT_RETRY_WAIT_TIME_NANOS), || { - ic_cdk::spawn(async { - let _ = build_index().await; - }) - }); - }); - let next_txid = with_index(|idx| idx.next_txid); - let res = get_transactions_from_ledger(next_txid, MAX_TRANSACTIONS_PER_RESPONSE).await?; - let mut tx_indexed_cout: usize = 0; - for archived in res.archived_transactions { - // The archive node limits the number of transactions returned by a - // single get_transaction call. - let last_txid = archived.start.clone() + archived.length.clone(); - let mut next_archived_txid = archived.start.clone(); - while next_archived_txid < last_txid { - let archived = ArchivedRange:: { - start: next_archived_txid, - length: archived.length.clone(), - callback: archived.callback.clone(), - }; - let res = get_transactions_from_archive(&archived).await?; - let mut idx = archived - .start - .0 - .to_u64() - .ok_or("The Ledger returned an index that is not a valid u64")?; - for transaction in res.transactions { - index_transaction(idx, transaction)?; - idx += 1; - tx_indexed_cout += 1; - } - next_archived_txid = Nat::from(idx); - } - } - let mut idx = res - .first_index - .0 - .to_u64() - .ok_or("The Ledger returned an index that is not a valid u64")?; - for transaction in res.transactions { - index_transaction(idx, transaction)?; - idx += 1; - tx_indexed_cout += 1; - } - let wait_time: u64 = compute_wait_time(tx_indexed_cout); - ic_cdk::eprintln!( - "{}Indexed: {} waiting : {}", - LOG_PREFIX, - tx_indexed_cout, - wait_time / SEC_NANOS - ); - ScopeGuard::into_inner(failure_guard); - with_index_mut(|idx| { - idx.is_build_index_running = false; - idx.last_wait_time = wait_time; - }); - ic_cdk_timers::set_timer(Duration::from_nanos(wait_time), || { - ic_cdk::spawn(async { - let _ = build_index().await; - }) - }); - Ok(()) -} - -/// Compute the waiting time before next indexing -pub fn compute_wait_time(indexed_tx_count: usize) -> u64 { - if indexed_tx_count >= MAX_TRANSACTIONS_PER_RESPONSE { - // If we indexed more than MAX_SPEED_THRESHOLD, - // we index again on the next build_index call. - return 0; - } - ((1_f64 - indexed_tx_count as f64 / MAX_TRANSACTIONS_PER_RESPONSE as f64) - * DEFAULT_MAX_WAIT_TIME_NANOS as f64) as u64 -} - -fn index_transaction(txid: u64, transaction: Transaction) -> Result<(), String> { - match transaction.kind.as_str() { - "mint" => { - let mint = transaction - .mint - .ok_or("Got a transaction with kind 'mint' but the mint field was None")? - .to; - add_tx(txid, mint); - Ok(()) - } - "burn" => { - let burn = transaction - .burn - .ok_or("Got a transaction with kind 'burn' but the burn field was None")? - .from; - add_tx(txid, burn); - Ok(()) - } - "transfer" => { - let Transfer { from, to, .. } = transaction - .transfer - .ok_or("Got a transaction with kind 'transfer' but the transfer field was None")?; - add_tx(txid, from); - add_tx(txid, to); - Ok(()) - } - "approve" => { - let Approve { from, spender, .. } = transaction - .approve - .ok_or("Got a transaction with kind 'approve' but the approve field was None")?; - add_tx(txid, from); - add_tx(txid, spender); - Ok(()) - } - kind => Err(format!("Found transaction of unknown kind {}", kind)), - } -} - -fn add_tx(txid: u64, account: Account) { - measure_span(&PROFILING_DATA, "add_tx", move || { - with_index_mut(|idx| { - let account_index = match idx.account_index.entry(account.owner.into()) { - btree_map::Entry::Vacant(v) => v.insert(BTreeMap::new()), - btree_map::Entry::Occupied(o) => o.into_mut(), - }; - match account_index.entry(*account.effective_subaccount()) { - btree_map::Entry::Vacant(v) => { - idx.accounts_num += 1; - let _ = v.insert(vec![txid]); - } - btree_map::Entry::Occupied(o) => o.into_mut().push(txid), - }; - idx.next_txid = txid + 1; - }); - }) -} - -/// Returns args.max_results transactions ids of the account args.account -/// since args.start. -/// The transactions will be sorted from the most recent to the least recent. -/// -/// If arg.start is not set then it represents the max txid. A query with -/// start=None will always return the most recent transaction as first transaction -/// -/// Examples [1, 3, 5, 6, 9]: -/// start=None max_results=3 => [9, 6, 5] // last 3 -/// start=9 max_results=3 => [9, 6, 5] // last 3 before 10 -/// start=5 max_results=2 => [5, 3] // last 2 after 5 -/// start=3 max_results=3 => [3, 1] // last 3 before 3 but there are only 2 txs -/// start=0 max_results=2 => [] // start is before oldest txid -fn get_account_transactions_ids(args: GetAccountTransactionsArgs) -> Vec { - // The SNS Ledger txid (or block index) is a u64 - if args.start.is_some() && args.start.as_ref().unwrap() > &Nat::from(u64::MAX) { - return vec![]; - } - let max_results = (&args.max_results) - .min(&Nat::from(MAX_TRANSACTIONS_PER_RESPONSE)) - .0 - .to_usize() - .unwrap(); - with_index(|idx| { - let account_index = match idx.account_index.get(&args.account.owner.into()) { - Some(account_index) => account_index, - None => return vec![], - }; - let txids = match account_index.get(args.account.effective_subaccount()) { - Some(txids) => txids, - None => return vec![], - }; - let start_pos = match &args.start { - None => txids.len() - 1, - // binary_search doc: - // If the value is found then Result::Ok is returned, containing the index - // of the matching element. ... If the value is not found then Result::Err - // is returned, containing the index where a matching element could be - // inserted while maintaining sorted order. - Some(start) => match txids.binary_search(&start.0.to_u64().unwrap()) { - Ok(i) => i, - Err(i) if i > 0 => i - 1, - _ => return vec![], - }, - }; - let end_pos = (start_pos as i64 - max_results as i64 + 1).max(0) as usize; - (end_pos..=start_pos) - .rev() - .map(|pos| *txids.get(pos).unwrap()) - .collect() - }) -} - -pub async fn get_account_transactions(args: GetAccountTransactionsArgs) -> GetTransactionsResult { - let oldest_tx_id = get_oldest_txid(&args.account.clone()); - let txids = get_account_transactions_ids(args); - let mut txs = vec![]; - for txid in &txids { - match get_transactions_from_ledger(*txid, 1).await { - Ok(mut res) => { - if let Some(tx) = res.transactions.pop() { - txs.push(TransactionWithId { - id: Nat::from(*txid), - transaction: tx, - }) - } else if let Some(archive) = res.archived_transactions.first() { - match get_transactions_from_archive(archive).await { - Ok(res) if !res.transactions.is_empty() => txs.push(TransactionWithId { - id: Nat::from(*txid), - transaction: res.transactions.first().unwrap().clone(), - }), - Ok(_) => { - let message = format!("Error fetching transaction {} from archive {}: archive didn't return the transaction!", txid, archive.callback.canister_id); - ic_cdk::eprintln!("{}{}", LOG_PREFIX, message); - return Err(GetTransactionsErr { message }); - } - Err(e) => { - let message = format!( - "Error fetching transaction {}from archive {}: {}", - txid, archive.callback.canister_id, e - ); - ic_cdk::eprintln!("{}{}", LOG_PREFIX, message); - return Err(GetTransactionsErr { message }); - } - } - } - } - Err(e) => { - let message = format!("Error fetching transaction {}: {}", txid, e); - ic_cdk::eprintln!("{}{}", LOG_PREFIX, message); - return Err(GetTransactionsErr { message }); - } - } - } - Ok(GetTransactions { - transactions: txs, - oldest_tx_id, - }) -} - -fn get_oldest_txid(account: &Account) -> Option { - with_index(|idx| { - idx.account_index - .get(&account.owner.into())? - .get(account.effective_subaccount())? - .first() - .map(|txid| Nat::from(*txid)) - }) -} - -pub fn encode_metrics(w: &mut ic_metrics_encoder::MetricsEncoder>) -> std::io::Result<()> { - w.encode_gauge( - "index_stable_memory_pages", - ic_cdk::api::stable::stable_size() as f64, - "Size of the stable memory allocated by this canister measured in 64K Wasm pages.", - )?; - w.encode_gauge( - "stable_memory_bytes", - (ic_cdk::api::stable::stable_size() * 64 * 1024) as f64, - "Size of the stable memory allocated by this canister measured in bytes.", - )?; - w.encode_gauge( - "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; - w.encode_gauge( - "index_cycle_balance", - cycle_balance, - "Cycle balance on this canister.", - )?; - w.gauge_vec("cycle_balance", "Cycle balance on this canister.")? - .value(&[("canister", "icrc1-index")], cycle_balance)?; - - w.encode_gauge( - "index_number_of_transactions", - with_index(|idx| idx.next_txid) as f64, - "Total number of transaction stored in the main memory.", - )?; - w.encode_gauge( - "index_number_of_accounts", - with_index(|idx| idx.accounts_num) as f64, - "Total number of accounts indexed.", - )?; - w.encode_gauge( - "index_last_wait_time", - with_index(|idx| idx.last_wait_time) as f64, - "Last amount of time waited between two transactions fetch.", - )?; - PROFILING_DATA.with(|cell| -> std::io::Result<()> { - cell.borrow().record_metrics(w.histogram_vec( - "index_profile_instructions", - "Statistics for how many instructions index operations require.", - )?)?; - Ok(()) - })?; - Ok(()) -} - -pub fn pre_upgrade() { - ic_cdk::println!("Running pre-upgrade on index canister..."); - with_index(|idx| ciborium::ser::into_writer(idx, StableWriter::default())) - .expect("failed to encode index state"); -} - -pub fn post_upgrade() { - ic_cdk::println!("Running post-upgrade on index canister..."); - INDEX.with(|idx| { - *idx.borrow_mut() = Some( - ciborium::de::from_reader(StableReader::default()) - .expect("failed to decode index state"), - ) - }); -} - -#[cfg(test)] -mod tests { - use std::collections::{btree_map, BTreeMap}; - - use candid::Nat; - use ic_base_types::{CanisterId, PrincipalId}; - use icrc_ledger_types::icrc1::account::Account; - - use proptest::{option, proptest}; - - use crate::{ - add_tx, get_account_transactions_ids, with_index, GetAccountTransactionsArgs, Index, INDEX, - }; - - fn account(n: u64) -> Account { - Account { - owner: PrincipalId::new_user_test_id(n).0, - subaccount: None, - } - } - - fn init_state(txids: Vec<(Account, Vec)>) { - INDEX.with(|idx| { - let mut account_index = BTreeMap::new(); - for (account, new_txids) in txids { - let account_index = match account_index.entry(PrincipalId(account.owner)) { - btree_map::Entry::Vacant(v) => v.insert(BTreeMap::new()), - btree_map::Entry::Occupied(o) => o.into_mut(), - }; - let txids = match account_index.entry(*account.effective_subaccount()) { - btree_map::Entry::Vacant(v) => v.insert(vec![]), - btree_map::Entry::Occupied(o) => o.into_mut(), - }; - txids.extend(new_txids); - } - *idx.borrow_mut() = Some(Index { - ledger_id: CanisterId::from_u64(42), - next_txid: 0, - is_build_index_running: false, - account_index, - accounts_num: 0, - last_wait_time: 0, - }); - }); - } - - fn check_get_account_transactions_ids( - start: Option, - max_results: u64, - expected: Vec, - ) { - let actual = get_account_transactions_ids(GetAccountTransactionsArgs { - account: account(1), - start: start.map(Nat::from), - max_results: Nat::from(max_results), - }); - assert_eq!( - actual, expected, - "start: {:?} max_results: {}", - start, max_results - ); - } - - proptest! { - #[test] - fn get_account_transactions_ids_no_account_fuzzy(start in option::of(u64::MIN..u64::MAX), max_results in u64::MIN..u64::MAX) { - init_state(vec![]); - check_get_account_transactions_ids(start, max_results, vec![]); - } - } - - #[test] - fn get_account_transactions_start_none() { - init_state(vec![(account(1), vec![1, 3, 5, 6, 9])]); - - // first element - check_get_account_transactions_ids(None, 1, vec![9]); - - // first 2 elements - check_get_account_transactions_ids(None, 2, vec![9, 6]); - - // all elements - check_get_account_transactions_ids(None, 5, vec![9, 6, 5, 3, 1]); - } - - proptest! { - #[test] - fn get_account_transactions_from_end_fuzzy(max_results in 6u64..) { - init_state(vec![(account(1), vec![1, 3, 5, 6, 9])]); - - // max_results > num transactions - check_get_account_transactions_ids(None, max_results, vec![9, 6, 5, 3, 1]); - } - } - - #[test] - fn get_account_transactions_start_some() { - init_state(vec![(account(1), vec![1, 3, 5, 6, 9])]); - - // start matches an existing txid, return that tx - check_get_account_transactions_ids(Some(3), 1, vec![3]); - - // start matches an existing txid, return that tx with the previous one - check_get_account_transactions_ids(Some(5), 2, vec![5, 3]); - - // start matches the last txid, return the last tx and the previous one - check_get_account_transactions_ids(Some(9), 2, vec![9, 6]); - - // start matches the first txid, return that tx - check_get_account_transactions_ids(Some(1), 2, vec![1]); - - // start doesn't match an existing txid and there is 1 tx that has txid < start, return that - check_get_account_transactions_ids(Some(2), 1, vec![1]); - - // start doesn't match an existing txid and there are no txs with txid < start - check_get_account_transactions_ids(Some(0), 1, vec![]); - - // start is bigger than any other txid, return the last tx - check_get_account_transactions_ids(Some(10), 1, vec![9]); - } - - proptest! { - #[test] - fn get_account_transactions_start_some_fuzzy(max_results in 5u64..) { - init_state(vec![(account(1), vec![1, 3, 5, 6, 9])]); - - // all results from each existing txid - check_get_account_transactions_ids(Some(0), max_results, vec![]); - check_get_account_transactions_ids(Some(1), max_results, vec![1]); - check_get_account_transactions_ids(Some(3), max_results, vec![3, 1]); - check_get_account_transactions_ids(Some(5), max_results, vec![5, 3, 1]); - check_get_account_transactions_ids(Some(6), max_results, vec![6, 5, 3, 1]); - check_get_account_transactions_ids(Some(9), max_results, vec![9, 6, 5, 3, 1]); - - // all results from non-existing txid - check_get_account_transactions_ids(Some(2), max_results, vec![1]); - check_get_account_transactions_ids(Some(4), max_results, vec![3, 1]); - check_get_account_transactions_ids(Some(7), max_results, vec![6, 5, 3, 1]); - check_get_account_transactions_ids(Some(8), max_results, vec![6, 5, 3, 1]); - check_get_account_transactions_ids(Some(10), max_results, vec![9, 6, 5, 3, 1]); - } - - #[test] - fn get_account_transactions_start_some_fuzzy_out_of_range(max_results in 0u64..) { - init_state(vec![(account(1), vec![1, 3, 5, 6, 9])]); - - // start = 0 so the results must always be empty - check_get_account_transactions_ids(Some(0), max_results, vec![]); - } - } - - proptest! { - #[test] - fn get_account_transactions_check_panics(start in option::of(0u64..), max_results in u64::MIN..u64::MAX) { - init_state(vec![(account(1), vec![1, 3, 5, 6, 9])]); - - get_account_transactions_ids(GetAccountTransactionsArgs { - account: account(1), - start: start.map(Nat::from), - max_results: Nat::from(max_results), - }); - } - } - - proptest! { - #[test] - fn test_compute_wait_time(indexed_tx_count in 0..10_000_usize) { - let wait_time = crate::compute_wait_time(indexed_tx_count); - let next_wait_time = crate::compute_wait_time(indexed_tx_count + 1); - assert!(wait_time <= 100 * crate::SEC_NANOS); - assert!(next_wait_time <= wait_time); - } - } - - #[test] - fn account_num() { - init_state(vec![]); - - let mut next_txid = 0u64..; - let mut add_tx_for = |principal: u64, subaccount_number: u64| -> u64 { - let mut subaccount = [0u8; 32]; - subaccount[0..8].copy_from_slice(&subaccount_number.to_le_bytes()); - let account = Account { - owner: PrincipalId::new_user_test_id(principal).0, - subaccount: Some(subaccount), - }; - add_tx(next_txid.next().unwrap(), account); - with_index(|idx| idx.accounts_num) - }; - - // no accounts at the beginning - assert_eq!(0, with_index(|idx| idx.accounts_num)); - - // new tx for new principal => add one account - assert_eq!(1, add_tx_for(0, 0)); - - // new tx for existing principal => same number of accounts - assert_eq!(1, add_tx_for(0, 0)); - - // new tx for new principal => add one account - assert_eq!(2, add_tx_for(1, 0)); - - // new tx for existing principals => same number of accounts - assert_eq!(2, add_tx_for(0, 0)); - assert_eq!(2, add_tx_for(1, 0)); - - // new tx for exiting principals but new subaccount - assert_eq!(3, add_tx_for(0, 1)); - assert_eq!(3, add_tx_for(0, 1)); - assert_eq!(4, add_tx_for(0, 2)); - assert_eq!(4, add_tx_for(0, 2)); - assert_eq!(5, add_tx_for(1, 1)); - assert_eq!(5, add_tx_for(1, 1)); - - // new tx for new principal and different subaccounts - assert_eq!(6, add_tx_for(2, 1)); - assert_eq!(7, add_tx_for(2, 3)); - assert_eq!(8, add_tx_for(2, 10)); - } -} diff --git a/rs/ledger_suite/icrc1/index/src/main.rs b/rs/ledger_suite/icrc1/index/src/main.rs deleted file mode 100644 index b96b02b32b6..00000000000 --- a/rs/ledger_suite/icrc1/index/src/main.rs +++ /dev/null @@ -1,102 +0,0 @@ -use candid::candid_method; -use ic_base_types::CanisterId; -use ic_canisters_http_types::{HttpRequest, HttpResponse, HttpResponseBuilder}; -use ic_cdk_macros::{init, post_upgrade, pre_upgrade, query, update}; -use ic_icrc1_index::{ - encode_metrics, GetAccountTransactionsArgs, GetTransactionsResult, InitArgs, - ListSubaccountsArgs, -}; -use icrc_ledger_types::icrc1::account::Subaccount; -use std::time::Duration; - -fn main() {} - -#[init] -#[candid_method(init)] -fn init(args: InitArgs) { - ic_icrc1_index::init(args); - ic_cdk_timers::set_timer(Duration::from_secs(1), || { - ic_cdk::spawn(async { - let _ = ic_icrc1_index::build_index().await; - }) - }); -} - -#[update] -#[candid_method(update)] -async fn get_account_transactions(args: GetAccountTransactionsArgs) -> GetTransactionsResult { - ic_icrc1_index::get_account_transactions(args).await -} - -#[query] -#[candid_method(query)] -fn list_subaccounts(args: ListSubaccountsArgs) -> Vec { - ic_icrc1_index::list_subaccounts(args) -} - -#[query] -#[candid_method(query)] -fn ledger_id() -> CanisterId { - ic_icrc1_index::ledger_id() -} - -#[query(hidden = true, decoding_quota = 10000)] -fn http_request(req: HttpRequest) -> HttpResponse { - if req.path() == "/metrics" { - let mut writer = - ic_metrics_encoder::MetricsEncoder::new(vec![], ic_cdk::api::time() as i64 / 1_000_000); - - match encode_metrics(&mut writer) { - Ok(()) => HttpResponseBuilder::ok() - .header("Content-Type", "text/plain; version=0.0.4") - .with_body_and_content_length(writer.into_inner()) - .build(), - Err(err) => { - HttpResponseBuilder::server_error(format!("Failed to encode metrics: {}", err)) - .build() - } - } - } else { - HttpResponseBuilder::not_found().build() - } -} - -#[pre_upgrade] -fn pre_upgrade() { - ic_icrc1_index::pre_upgrade() -} - -#[post_upgrade] -fn post_upgrade() { - ic_icrc1_index::post_upgrade(); - ic_cdk_timers::set_timer(Duration::from_secs(1), || { - ic_cdk::spawn(async { - let _ = ic_icrc1_index::build_index().await; - }) - }); -} - -#[query(hidden = true)] -fn __get_candid_interface_tmp_hack() -> &'static str { - include_str!(env!("INDEX_DID_PATH")) -} - -#[test] -fn check_candid_interface() { - use candid_parser::utils::{service_equal, CandidSource}; - use std::path::PathBuf; - - candid::export_service!(); - - let new_interface = __export_service(); - - // check the public interface against the actual one - let old_interface = - PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("index.did"); - - service_equal( - CandidSource::Text(&new_interface), - CandidSource::File(old_interface.as_path()), - ) - .expect("the index interface is not compatible with index.did"); -} diff --git a/rs/ledger_suite/icrc1/index/tests/tests.rs b/rs/ledger_suite/icrc1/index/tests/tests.rs deleted file mode 100644 index ff213e67fdb..00000000000 --- a/rs/ledger_suite/icrc1/index/tests/tests.rs +++ /dev/null @@ -1,688 +0,0 @@ -use assert_matches::assert_matches; -use candid::{Decode, Encode, Nat}; -use ic_base_types::CanisterId; -use ic_base_types::PrincipalId; -use ic_icrc1::{Block, Operation, Transaction}; -use ic_icrc1_index::{ - GetAccountTransactionsArgs, GetTransactions, GetTransactionsResult, InitArgs as IndexInitArgs, - ListSubaccountsArgs, TransactionWithId, -}; -use ic_icrc1_ledger::{FeatureFlags, InitArgsBuilder as LedgerInitArgsBuilder, LedgerArgument}; -use ic_icrc1_tokens_u64::U64; -use ic_ledger_canister_core::archive::ArchiveOptions; -use ic_ledger_core::{ - block::{BlockIndex, BlockType, EncodedBlock}, - timestamp::TimeStamp, - tokens::Zero, -}; -use ic_ledger_hash_of::HashOf; -use ic_rosetta_test_utils::test_http_request_decoding_quota; -use ic_state_machine_tests::StateMachine; -use icrc_ledger_types::icrc1::transfer::{Memo, TransferArg, TransferError}; -use icrc_ledger_types::icrc2::approve::{ApproveArgs, ApproveError}; -use icrc_ledger_types::{ - icrc1::account::Account, icrc1::account::Subaccount, icrc3::archive::ArchiveInfo, -}; -use num_traits::cast::ToPrimitive; -use std::path::PathBuf; -use std::time::Duration; - -const FEE: u64 = 10_000; -const ARCHIVE_TRIGGER_THRESHOLD: u64 = 10; -const NUM_BLOCKS_TO_ARCHIVE: usize = 5; -const MINT_BLOCKS_PER_ARCHIVE: u64 = 5; - -const MINTER: Account = Account { - owner: PrincipalId::new(0, [0u8; 29]).0, - subaccount: None, -}; - -// Metadata-related constants -const TOKEN_NAME: &str = "Test Token"; -const TOKEN_SYMBOL: &str = "XTST"; -const TEXT_META_KEY: &str = "test:image"; -const TEXT_META_VALUE: &str = "grumpy_cat.png"; -const BLOB_META_KEY: &str = "test:blob"; -const BLOB_META_VALUE: &[u8] = b"\xca\xfe\xba\xbe"; -const NAT_META_KEY: &str = "test:nat"; -const NAT_META_VALUE: u128 = u128::MAX; -const INT_META_KEY: &str = "test:int"; -const INT_META_VALUE: i128 = i128::MIN; - -type Tokens = U64; - -fn index_wasm() -> Vec { - ic_test_utilities_load_wasm::load_wasm( - std::env::var("CARGO_MANIFEST_DIR").unwrap(), - "ic-icrc1-index", - &[], - ) -} - -fn ledger_wasm() -> Vec { - ic_test_utilities_load_wasm::load_wasm( - PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()) - .parent() - .unwrap() - .join("ledger"), - "ic-icrc1-ledger", - &[], - ) -} - -fn mint_block() -> EncodedBlock { - Block::::from_transaction( - Some(HashOf::new([0; 32])), - Transaction { - operation: Operation::Mint { - to: account(0), - amount: Tokens::new(1), - }, - created_at_time: Some(1), - memo: Some(Memo::from([1; 32].to_vec())), - }, - TimeStamp::new(3, 4), - Tokens::zero(), - None, - ) - .encode() -} - -fn default_archive_options() -> ArchiveOptions { - ArchiveOptions { - trigger_threshold: ARCHIVE_TRIGGER_THRESHOLD as usize, - num_blocks_to_archive: NUM_BLOCKS_TO_ARCHIVE, - node_max_memory_size_bytes: None, - max_message_size_bytes: None, - controller_id: PrincipalId::new_user_test_id(100), - more_controller_ids: None, - cycles_for_archive_creation: None, - max_transactions_per_response: None, - } -} - -fn install_ledger( - env: &StateMachine, - initial_balances: Vec<(Account, u64)>, - archive_options: ArchiveOptions, -) -> CanisterId { - let mut builder = LedgerInitArgsBuilder::with_symbol_and_name(TOKEN_SYMBOL, TOKEN_NAME) - .with_minting_account(MINTER) - .with_transfer_fee(FEE) - .with_metadata_entry(NAT_META_KEY, NAT_META_VALUE) - .with_metadata_entry(INT_META_KEY, INT_META_VALUE) - .with_metadata_entry(TEXT_META_KEY, TEXT_META_VALUE) - .with_metadata_entry(BLOB_META_KEY, BLOB_META_VALUE) - .with_archive_options(archive_options) - .with_feature_flags(FeatureFlags { icrc2: true }); - for (account, amount) in initial_balances { - builder = builder.with_initial_balance(account, amount); - } - - env.install_canister( - ledger_wasm(), - Encode!(&LedgerArgument::Init(builder.build())).unwrap(), - None, - ) - .unwrap() -} - -fn install_index(env: &StateMachine, ledger_id: CanisterId) -> CanisterId { - let args = IndexInitArgs { ledger_id }; - env.install_canister(index_wasm(), Encode!(&args).unwrap(), None) - .unwrap() -} - -fn send_transfer( - env: &StateMachine, - ledger: CanisterId, - from: PrincipalId, - arg: &TransferArg, -) -> Result { - Decode!( - &env.execute_ingress_as( - from, - ledger, - "icrc1_transfer", - Encode!(arg) - .unwrap() - ) - .expect("failed to transfer funds") - .bytes(), - Result - ) - .expect("failed to decode transfer response") - .map(|n| n.0.to_u64().unwrap()) -} - -fn send_approval( - env: &StateMachine, - ledger: CanisterId, - from: PrincipalId, - arg: &ApproveArgs, -) -> Result { - Decode!( - &env.execute_ingress_as( - from, - ledger, - "icrc2_approve", - Encode!(arg) - .unwrap() - ) - .expect("failed to create an approval") - .bytes(), - Result - ) - .expect("failed to decode approve response") - .map(|n| n.0.to_u64().unwrap()) -} - -fn check_mint(id: u64, to: Account, amount: u64, transaction: &TransactionWithId) { - assert_eq!("mint".to_string(), transaction.transaction.kind); - let mint = transaction.transaction.mint.as_ref().unwrap(); - assert_eq!( - (&transaction.id, &mint.to, &mint.amount, &mint.memo), - (&Nat::from(id), &to, &Nat::from(amount), &None) - ) -} - -fn check_burn(id: u64, from: Account, amount: u64, transaction: &TransactionWithId) { - assert_eq!("burn".to_string(), transaction.transaction.kind); - let burn = transaction.transaction.burn.as_ref().unwrap(); - assert_eq!( - (&transaction.id, &burn.from, &burn.amount, &burn.memo), - (&Nat::from(id), &from, &Nat::from(amount), &None) - ) -} - -fn check_transfer( - id: u64, - from: Account, - to: Account, - amount: u64, - transaction: &TransactionWithId, -) { - assert_eq!("transfer".to_string(), transaction.transaction.kind); - let transfer = transaction.transaction.transfer.as_ref().unwrap(); - assert_eq!( - ( - &transaction.id, - &transfer.from, - &transfer.to, - &transfer.amount, - &transfer.memo - ), - (&Nat::from(id), &from, &to, &Nat::from(amount), &None) - ) -} - -fn check_approval( - id: u64, - from: Account, - spender: Account, - amount: u64, - transaction: &TransactionWithId, -) { - assert_eq!("approve".to_string(), transaction.transaction.kind); - let approve = transaction.transaction.approve.as_ref().unwrap(); - assert_eq!( - ( - &transaction.id, - &approve.from, - &approve.spender, - &approve.amount, - &approve.memo - ), - (&Nat::from(id), &from, &spender, &Nat::from(amount), &None) - ) -} - -fn transfer( - env: &StateMachine, - ledger: CanisterId, - from: Account, - to: Account, - amount: u64, -) -> BlockIndex { - send_transfer( - env, - ledger, - PrincipalId(from.owner), - &TransferArg { - from_subaccount: from.subaccount, - to, - fee: None, - created_at_time: None, - amount: Nat::from(amount), - memo: None, - }, - ) - .unwrap() -} - -fn burn(env: &StateMachine, ledger: CanisterId, from: Account, amount: u64) -> BlockIndex { - transfer(env, ledger, from, MINTER, amount) -} - -fn mint(env: &StateMachine, ledger: CanisterId, to: Account, amount: u64) -> BlockIndex { - transfer(env, ledger, MINTER, to, amount) -} - -fn approve( - env: &StateMachine, - ledger: CanisterId, - from: Account, - spender: Account, - amount: u64, -) -> BlockIndex { - send_approval( - env, - ledger, - PrincipalId(from.owner), - &ApproveArgs { - from_subaccount: from.subaccount, - spender, - amount: Nat::from(amount), - expected_allowance: None, - expires_at: None, - fee: None, - memo: None, - created_at_time: None, - }, - ) - .unwrap() -} - -fn archives(env: &StateMachine, ledger: CanisterId) -> Vec { - Decode!( - &env.query(ledger, "archives", Encode!().unwrap()) - .expect("failed to transfer funds") - .bytes(), - Vec - ) - .unwrap() -} - -fn get_account_transactions( - env: &StateMachine, - index: CanisterId, - account: Account, - start: Option, - max_results: u64, -) -> GetTransactions { - Decode!( - &env.execute_ingress_as( - PrincipalId(account.owner), - index, - "get_account_transactions", - Encode!(&GetAccountTransactionsArgs { - account, - start: start.map(Nat::from), - max_results: Nat::from(max_results), - }) - .unwrap() - ) - .expect("failed to get_account_transactions") - .bytes(), - GetTransactionsResult - ) - .expect("failed to decode get_account_transactions response") - .expect("failed to get the range of transactions!") -} - -fn list_subaccounts( - env: &StateMachine, - index: CanisterId, - account: Account, - start: Option, -) -> Vec { - Decode!( - &env.execute_ingress_as( - PrincipalId(account.owner), - index, - "list_subaccounts", - Encode!(&ListSubaccountsArgs { - owner: PrincipalId(account.owner), - start, - }) - .unwrap() - ) - .expect("failed to list_subaccounts") - .bytes(), - Vec - ) - .expect("failed to decode list_subaccounts response") -} - -fn index_ledger_id(env: &StateMachine, index: CanisterId) -> CanisterId { - Decode!( - &env.query(index, "ledger_id", Encode!().unwrap()) - .expect("Unable to query ledger_id from index") - .bytes(), - CanisterId - ) - .expect("failed to decode ledger_id response") -} - -fn account(n: u64) -> Account { - Account { - owner: PrincipalId::new_user_test_id(n).0, - subaccount: None, - } -} - -fn account_with_subaccount(n: u64, s: u128) -> Account { - let mut sub: [u8; 32] = [0; 32]; - sub[..16].copy_from_slice(&s.to_be_bytes()); - Account { - owner: PrincipalId::new_user_test_id(n).0, - subaccount: Some(sub), - } -} - -#[test] -fn test() { - //Adding tokens to 2_000 subaccounts of one principal - let offset: u64 = 2_000; // The offset is the number of transactions we add to the ledger at initialization - - let initial_balances: Vec<_> = (0..2_000u128) - .map(|i| (account_with_subaccount(10, i), 1)) - .collect(); - - let env = StateMachine::new(); - - let ledger_id = install_ledger(&env, initial_balances, default_archive_options()); - - let index_id = install_index(&env, ledger_id); - - assert_eq!(ledger_id, index_ledger_id(&env, index_id)); - - for _ in 0..100 { - env.advance_time(Duration::from_secs(60)); - env.tick(); - } - - // add some transactions - mint(&env, ledger_id, account(1), 100000); // block=0 - mint(&env, ledger_id, account(2), 200000); // block=1 - transfer(&env, ledger_id, account(1), account(2), 1); // block=2 - transfer(&env, ledger_id, account(2), account(1), 10); // block=3 - transfer(&env, ledger_id, account(2), account(1), 20); // block=4 - burn(&env, ledger_id, account(1), 10000); // block=5 - - env.advance_time(Duration::from_secs(60)); - env.tick(); // trigger index heartbeat - - let txs = get_account_transactions(&env, index_id, account(1), None, u64::MAX); - assert_eq!(Some(Nat::from(offset)), txs.oldest_tx_id); - let txs = txs.transactions; - assert_eq!(5, txs.len()); - check_burn(5 + offset, account(1), 10000, txs.first().unwrap()); - check_transfer(4 + offset, account(2), account(1), 20, txs.get(1).unwrap()); - check_transfer(3 + offset, account(2), account(1), 10, txs.get(2).unwrap()); - check_transfer(2 + offset, account(1), account(2), 1, txs.get(3).unwrap()); - check_mint(offset, account(1), 100000, txs.get(4).unwrap()); - - let txs = get_account_transactions(&env, index_id, account(2), None, u64::MAX); - assert_eq!(Some(Nat::from(1 + offset)), txs.oldest_tx_id); - let txs = txs.transactions; - assert_eq!(4, txs.len()); - check_transfer(4 + offset, account(2), account(1), 20, txs.first().unwrap()); - check_transfer(3 + offset, account(2), account(1), 10, txs.get(1).unwrap()); - check_transfer(2 + offset, account(1), account(2), 1, txs.get(2).unwrap()); - check_mint(1 + offset, account(2), 200000, txs.get(3).unwrap()); - - // // add more transactions - transfer(&env, ledger_id, account(1), account(3), 6); // block=6 - transfer(&env, ledger_id, account(1), account(2), 7); // block=7 - - // add an approval - approve(&env, ledger_id, account(1), account(4), 10); // block=8 - - env.advance_time(Duration::from_secs(60)); - env.tick(); // trigger index heartbeat - - // fetch the more recent transfers and approvals - let txs = get_account_transactions(&env, index_id, account(1), Some(offset + 9), 3); - assert_eq!(Some(Nat::from(offset)), txs.oldest_tx_id); - let txs = txs.transactions; - check_approval(8 + offset, account(1), account(4), 10, txs.first().unwrap()); - check_transfer(7 + offset, account(1), account(2), 7, txs.get(1).unwrap()); - check_transfer(6 + offset, account(1), account(3), 6, txs.get(2).unwrap()); - - // fetch transactions for the receiver of the approval - let txs = get_account_transactions(&env, index_id, account(4), Some(offset + 9), u64::MAX); - assert_eq!(Some(Nat::from(offset + 8)), txs.oldest_tx_id); - let txs = txs.transactions; - assert_eq!(1, txs.len()); - check_approval(8 + offset, account(1), account(4), 10, txs.first().unwrap()); - - // // fetch two older transaction by setting a start to the oldest tx id seen - let txs = get_account_transactions(&env, index_id, account(1), Some(offset + 5), 2); - assert_eq!(Some(Nat::from(offset)), txs.oldest_tx_id); - let txs = txs.transactions; - check_burn(5 + offset, account(1), 10000, txs.first().unwrap()); - check_transfer(4 + offset, account(2), account(1), 20, txs.get(1).unwrap()); - - // verify if we can query the first 1_000 subaccounts of a principal - let subs: Vec = list_subaccounts(&env, index_id, account(10), None); - assert_eq!(1000, subs.len()); - - //verify if we can query the 500 last subaccounts by adding a start parameter to the list_subaccounts call - let start_sub: u128 = 1500; - let mut sub: [u8; 32] = [0; 32]; - sub[..16].copy_from_slice(&start_sub.to_be_bytes()); - let subs: Vec = list_subaccounts(&env, index_id, account(10), Some(sub)); - assert_eq!(500, subs.len()); -} - -#[test] -fn test_wait_time() { - let env = StateMachine::new(); - let ledger_id = install_ledger(&env, vec![], default_archive_options()); - let index_id = install_index(&env, ledger_id); - - env.tick(); - - // add some transactions - mint(&env, ledger_id, account(1), 100000); // block=0 - mint(&env, ledger_id, account(2), 200000); // block=1 - transfer(&env, ledger_id, account(1), account(2), 1); // block=2 - transfer(&env, ledger_id, account(2), account(1), 10); // block=3 - burn(&env, ledger_id, account(1), 10000); // block=4 - - env.advance_time(Duration::from_secs(10)); - env.tick(); - - let txs = get_account_transactions(&env, index_id, account(1), None, u64::MAX); - assert!(!txs.transactions.is_empty()); - - // Next heartbeat should happen 60 seconds later, as we only indexed - // 5 transactions. - mint(&env, ledger_id, account(3), 100000); // block=5 - env.tick(); // trigger index heartbeat that shouldn't index new transactions. - - let txs = get_account_transactions(&env, index_id, account(3), None, u64::MAX); - assert!(txs.transactions.is_empty()); - env.advance_time(Duration::from_secs(1)); - env.tick(); - - let txs = get_account_transactions(&env, index_id, account(3), None, u64::MAX); - assert!(txs.transactions.is_empty()); - - env.advance_time(Duration::from_secs(1)); - env.tick(); - - let txs = get_account_transactions(&env, index_id, account(3), None, u64::MAX); - assert!(!txs.transactions.is_empty()); -} - -#[test] -fn test_upgrade() { - let env = StateMachine::new(); - let ledger_id = install_ledger(&env, vec![], default_archive_options()); - let index_id = install_index(&env, ledger_id); - - // add some transactions - mint(&env, ledger_id, account(1), 100000); // block=0 - transfer(&env, ledger_id, account(1), account(2), 1); // block=1 - - env.advance_time(Duration::from_secs(60)); - env.tick(); - // upgrade the Index - env.upgrade_canister(index_id, index_wasm(), vec![]) - .expect("Failed to upgrade the Index canister"); - - let txs = get_account_transactions(&env, index_id, account(1), None, u64::MAX); - let txs = txs.transactions; - check_mint(0, account(1), 100000, txs.get(1).unwrap()); - check_transfer(1, account(1), account(2), 1, txs.first().unwrap()); -} - -#[test] -fn test_ledger_stopped() { - let env = StateMachine::new(); - let ledger_id = install_ledger(&env, vec![], default_archive_options()); - let index_id = install_index(&env, ledger_id); - - mint(&env, ledger_id, account(1), 100000); // block=0 - transfer(&env, ledger_id, account(1), account(2), 1); // block=1 - - env.advance_time(Duration::from_secs(60)); - env.tick(); - - let txs = get_account_transactions(&env, index_id, account(1), None, u64::MAX); - let txs = txs.transactions; - check_mint(0, account(1), 100000, txs.get(1).unwrap()); - check_transfer(1, account(1), account(2), 1, txs.first().unwrap()); - - let stop_result = env.stop_canister(ledger_id); - assert_matches!(stop_result, Ok(_)); - - env.advance_time(Duration::from_secs(60)); - env.tick(); - - let start_result = env.start_canister(ledger_id); - assert_matches!(start_result, Ok(_)); - - mint(&env, ledger_id, account(1), 100000); // block=2 - transfer(&env, ledger_id, account(1), account(2), 1); // block=3 - env.advance_time(Duration::from_secs(60)); - env.tick(); - - let txs = get_account_transactions(&env, index_id, account(1), None, u64::MAX); - let txs = txs.transactions; - check_mint(2, account(1), 100000, txs.get(1).unwrap()); - check_transfer(3, account(1), account(2), 1, txs.first().unwrap()); -} - -#[test] -fn test_index_archived_txs() { - let env = StateMachine::new(); - - // we want to create two archives. Each archive holds MINT_BLOCKS_PER_ARCHIVE mint blocks - // and the trigger threshold is ARCHIVE_TRIGGER_THRESHOLD - let num_txs = ARCHIVE_TRIGGER_THRESHOLD + MINT_BLOCKS_PER_ARCHIVE; - - // install the ledger and add enough transactions to create an archive - let ledger_id = install_ledger( - &env, - vec![], - ArchiveOptions { - node_max_memory_size_bytes: Some( - MINT_BLOCKS_PER_ARCHIVE * mint_block().size_bytes() as u64, - ), - ..default_archive_options() - }, - ); - for idx in 0..num_txs { - mint(&env, ledger_id, account(1), idx); - } - assert_eq!(2, archives(&env, ledger_id).len()); - - // install the index and let it index all the transaction - let index_id = install_index(&env, ledger_id); - for _ in 0..10 { - env.advance_time(Duration::from_secs(60)); - env.tick(); - } - let txs = get_account_transactions(&env, index_id, account(1), None, u64::MAX); - let txs = txs.transactions; - assert_eq!(num_txs, txs.len() as u64); - for idx in 0..num_txs { - assert_eq!( - Nat::from(idx), - txs.get((num_txs - idx - 1) as usize) - .unwrap_or_else(|| panic!("Transaction {} not found in index!", num_txs - idx - 1)) - .id - ); - } -} - -#[test] -fn test_index_archived_txs_paging() { - // The archive node does paging when there are too many transactions. - // This test verifies that the Index canister respects the paging. - const MAX_TXS_PER_GET_TRANSACTIONS_RESPONSE: u64 = 1; - const NUM_ARCHIVED_TXS: usize = 2; - - let env = StateMachine::new(); - - // install the ledger and add enough transactions to create an archive - let ledger_id = install_ledger( - &env, - vec![], - ArchiveOptions { - num_blocks_to_archive: NUM_ARCHIVED_TXS, - max_transactions_per_response: Some(MAX_TXS_PER_GET_TRANSACTIONS_RESPONSE), - ..default_archive_options() - }, - ); - for idx in 0..ARCHIVE_TRIGGER_THRESHOLD { - mint(&env, ledger_id, account(1), idx); - } - - // install the index and let it index all the transaction - let index_id = install_index(&env, ledger_id); - for _ in 0..10 { - env.advance_time(Duration::from_secs(60)); - env.tick(); - } - - // check that the index has exactly indexed num_txs transactions - let txs = get_account_transactions(&env, index_id, account(1), None, u64::MAX); - let txs = txs.transactions; - let actual_txids: Vec = txs.iter().map(|tx| tx.id.0.to_u64().unwrap()).collect(); - let expected_txids: Vec = (0..ARCHIVE_TRIGGER_THRESHOLD).rev().collect(); - assert_eq!(expected_txids, actual_txids); -} - -#[test] -fn test_index_http_request_decoding_quota() { - let env = StateMachine::new(); - let ledger_id = install_ledger(&env, vec![], default_archive_options()); - let index_id = install_index(&env, ledger_id); - - test_http_request_decoding_quota(&env, index_id); -} - -mod metrics { - use crate::index_wasm; - use candid::Principal; - use ic_base_types::{CanisterId, PrincipalId}; - use ic_icrc1_index::InitArgs; - - #[test] - 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, - ); - } - - fn encode_init_args(ledger_id: Principal) -> InitArgs { - InitArgs { - ledger_id: CanisterId::unchecked_from_principal(PrincipalId(ledger_id)), - } - } -} diff --git a/rs/nns/integration_tests/BUILD.bazel b/rs/nns/integration_tests/BUILD.bazel index dc794a2e471..ae0be0d5bca 100644 --- a/rs/nns/integration_tests/BUILD.bazel +++ b/rs/nns/integration_tests/BUILD.bazel @@ -145,7 +145,6 @@ DEV_DATA = [ "//rs/ledger_suite/icp/ledger:ledger-canister-wasm", "//rs/ledger_suite/icp/ledger:ledger-canister-wasm-notify-method", "//rs/ledger_suite/icrc1/archive:archive_canister", - "//rs/ledger_suite/icrc1/index:index_canister", "//rs/ledger_suite/icrc1/index-ng:index_ng_canister", "//rs/ledger_suite/icrc1/ledger:ledger_canister", "//rs/nns/cmc:cycles-minting-canister", @@ -178,7 +177,6 @@ DEV_ENV = { "LEDGER_ARCHIVE_NODE_CANISTER_WASM_PATH": "$(rootpath //rs/ledger_suite/icp/archive:ledger-archive-node-canister-wasm)", "LIFELINE_CANISTER_WASM_PATH": "$(rootpath //rs/nns/handlers/lifeline/impl:lifeline_canister)", "IC_ICRC1_ARCHIVE_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/archive:archive_canister)", - "IC_ICRC1_INDEX_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/index:index_canister)", "IC_ICRC1_INDEX_NG_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/index-ng:index_ng_canister)", "IC_ICRC1_LEDGER_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/ledger:ledger_canister)", "GENESIS_TOKEN_CANISTER_WASM_PATH": "$(rootpath //rs/nns/gtc:genesis-token-canister)", diff --git a/rs/nns/sns-wasm/BUILD.bazel b/rs/nns/sns-wasm/BUILD.bazel index 4bfa93d5cfa..d72e30ae122 100644 --- a/rs/nns/sns-wasm/BUILD.bazel +++ b/rs/nns/sns-wasm/BUILD.bazel @@ -135,7 +135,6 @@ rust_ic_test_suite_with_extra_srcs( ":sns-wasm-canister", "//rs/ledger_suite/icp/ledger:ledger-canister-wasm-notify-method", "//rs/ledger_suite/icrc1/archive:archive_canister", - "//rs/ledger_suite/icrc1/index:index_canister", "//rs/ledger_suite/icrc1/index-ng:index_ng_canister", "//rs/ledger_suite/icrc1/ledger:ledger_canister", "//rs/nns/cmc:cycles-minting-canister", @@ -163,7 +162,6 @@ rust_ic_test_suite_with_extra_srcs( "GENESIS_TOKEN_CANISTER_WASM_PATH": "$(rootpath //rs/nns/gtc:genesis-token-canister)", "SNS_ROOT_CANISTER_WASM_PATH": "$(rootpath //rs/sns/root:sns-root-canister)", "SNS_GOVERNANCE_CANISTER_WASM_PATH": "$(rootpath //rs/sns/governance:sns-governance-canister)", - "IC_ICRC1_INDEX_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/index:index_canister)", "IC_ICRC1_INDEX_NG_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/index-ng:index_ng_canister)", "IC_ICRC1_LEDGER_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/ledger:ledger_canister)", "SNS_SWAP_CANISTER_WASM_PATH": "$(rootpath //rs/sns/swap:sns-swap-canister)", diff --git a/rs/sns/integration_tests/BUILD.bazel b/rs/sns/integration_tests/BUILD.bazel index 2eda895b531..cd37a5ec7b1 100644 --- a/rs/sns/integration_tests/BUILD.bazel +++ b/rs/sns/integration_tests/BUILD.bazel @@ -143,7 +143,6 @@ DATA_DEPS = [ "//rs/ledger_suite/icp/ledger:ledger-canister-wasm", "//rs/ledger_suite/icp/ledger:ledger-canister-wasm-notify-method", "//rs/ledger_suite/icrc1/archive:archive_canister", - "//rs/ledger_suite/icrc1/index:index_canister", "//rs/ledger_suite/icrc1/index-ng:index_ng_canister", "//rs/ledger_suite/icrc1/ledger:ledger_canister", "//rs/nervous_system/common/test_canister", @@ -166,7 +165,6 @@ ENV = { "GENESIS_TOKEN_CANISTER_WASM_PATH": "$(rootpath //rs/nns/gtc:genesis-token-canister)", "GOVERNANCE_CANISTER_TEST_WASM_PATH": "$(rootpath //rs/nns/governance:governance-canister-test)", "IC_ICRC1_ARCHIVE_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/archive:archive_canister)", - "IC_ICRC1_INDEX_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/index:index_canister)", "IC_ICRC1_INDEX_NG_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/index-ng:index_ng_canister)", "IC_ICRC1_LEDGER_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/ledger:ledger_canister)", "LEDGER_CANISTER_WASM_PATH": "$(rootpath //rs/ledger_suite/icp/ledger:ledger-canister-wasm)",