From c8edca72254a7146e53cd5cd924362e87fcf7faa Mon Sep 17 00:00:00 2001 From: NathanosDev Date: Thu, 16 Nov 2023 11:17:58 +0100 Subject: [PATCH] feat: add rb_tree to ic_certification crate --- .github/actions/setup-dfx/action.yml | 2 +- .github/workflows/pull-request.yml | 2 +- Cargo.lock | 72 +- Cargo.toml | 2 +- examples/certified-counter/Cargo.lock | 1095 ----------------- examples/certified-counter/Cargo.toml | 4 - examples/certified-counter/dfx.json | 4 +- .../certified-counter/src/backend/Cargo.toml | 5 +- .../certified-counter/src/backend/src/lib.rs | 4 +- .../src/frontend/assets/.ic-assets.json | 2 +- .../src/frontend/src/.ic-assets.json | 2 +- .../certificate-verification-js/README.md | 2 +- packages/ic-cbor/src/cbor_parse_hash_tree.rs | 4 +- packages/ic-certification-testing/README.md | 5 +- .../ic-certification/src/hash_tree/mod.rs | 45 +- packages/ic-certification/src/lib.rs | 10 +- packages/ic-certification/src/rb_tree/mod.rs | 1023 +++++++++++++++ .../ic-certification/src/rb_tree/tests.rs | 389 ++++++ .../Cargo.toml | 1 - .../src/asset_tree.rs | 2 +- .../src/certificate.rs | 10 +- .../src/expr_tree.rs | 2 +- .../src/hash.rs | 2 +- .../src/nested_tree.rs | 6 +- .../src/v2_certificate_fixture.rs | 2 +- .../ic-response-verification-wasm/Cargo.lock | 12 - packages/ic-response-verification/Cargo.toml | 1 - .../src/hash/request_hash.rs | 4 +- .../src/hash/response_hash.rs | 12 +- .../src/test_utils.rs | 4 +- .../src/validation/v1_validation.rs | 4 +- .../src/validation/v2_validation.rs | 10 +- .../verify_request_response_pair.rs | 8 +- 33 files changed, 1562 insertions(+), 1190 deletions(-) delete mode 100644 examples/certified-counter/Cargo.lock delete mode 100644 examples/certified-counter/Cargo.toml create mode 100644 packages/ic-certification/src/rb_tree/mod.rs create mode 100644 packages/ic-certification/src/rb_tree/tests.rs diff --git a/.github/actions/setup-dfx/action.yml b/.github/actions/setup-dfx/action.yml index 30d6fb48..96fb5143 100644 --- a/.github/actions/setup-dfx/action.yml +++ b/.github/actions/setup-dfx/action.yml @@ -13,7 +13,7 @@ runs: - name: Install dfx uses: dfinity/setup-dfx@main with: - dfx-version: '0.14.2' + dfx-version: '0.15.1' - name: Configure DFX shell: bash diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 4a8647ba..64a59ae8 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -28,7 +28,7 @@ jobs: run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - name: Build Cargo crates - run: cargo build --release + run: cargo build --release --workspace --exclude certified_counter_backend - name: DFX prepare Certified Counter working-directory: examples/certified-counter diff --git a/Cargo.lock b/Cargo.lock index 1f569837..04ecb826 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -367,6 +367,20 @@ dependencies = [ "libc", ] +[[package]] +name = "certified_counter_backend" +version = "0.1.0" +dependencies = [ + "anyhow", + "candid 0.8.4", + "ic-cdk", + "ic-cdk-macros", + "ic-certification 1.2.0", + "serde", + "serde_cbor", + "sha2 0.10.8", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -1222,6 +1236,34 @@ dependencies = [ "thiserror", ] +[[package]] +name = "ic-cdk" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c98b304a2657bad15bcb547625a018e13cf596676d834cfd93023395a6e2e03a" +dependencies = [ + "candid 0.8.4", + "cfg-if", + "ic-cdk-macros", + "ic0", + "serde", + "serde_bytes", +] + +[[package]] +name = "ic-cdk-macros" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebf50458685a0fc6b0e414cdba487610aeb199ac94db52d9fd76270565debee7" +dependencies = [ + "candid 0.8.4", + "proc-macro2", + "quote", + "serde", + "serde_tokenstream", + "syn 1.0.109", +] + [[package]] name = "ic-certificate-verification" version = "1.2.0" @@ -1283,17 +1325,6 @@ dependencies = [ "wasm-bindgen-console-logger", ] -[[package]] -name = "ic-certified-map" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197524aecec47db0b6c0c9f8821aad47272c2bd762c7a0ffe9715eaca0364061" -dependencies = [ - "serde", - "serde_bytes", - "sha2 0.10.8", -] - [[package]] name = "ic-constants" version = "0.8.0" @@ -1500,7 +1531,6 @@ dependencies = [ "ic-certificate-verification", "ic-certification 1.2.0", "ic-certification-testing", - "ic-certified-map", "ic-crypto-tree-hash", "ic-representation-independent-hash", "ic-response-verification-test-utils", @@ -1528,7 +1558,6 @@ dependencies = [ "hex", "ic-certification 1.2.0", "ic-certification-testing", - "ic-certified-map", "ic-types", "leb128", "serde", @@ -1674,6 +1703,12 @@ dependencies = [ "sha2 0.9.9", ] +[[package]] +name = "ic0" +version = "0.18.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576c539151d4769fb4d1a0c25c4108dd18facd04c5695b02cf2d226ab4e43aa5" + [[package]] name = "ic_bls12_381" version = "0.8.0" @@ -2733,6 +2768,17 @@ dependencies = [ "syn 2.0.37", ] +[[package]] +name = "serde_tokenstream" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "797ba1d80299b264f3aac68ab5d12e5825a561749db4df7cd7c8083900c5d4e9" +dependencies = [ + "proc-macro2", + "serde", + "syn 1.0.109", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" diff --git a/Cargo.toml b/Cargo.toml index dbebd5a3..114b6fe6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "examples/rust", + "examples/certified-counter/src/backend", "packages/ic-cbor", "packages/ic-certification", "packages/ic-certificate-verification", @@ -14,7 +15,6 @@ members = [ # https://github.com/rust-lang/cargo/issues/9406 # these projects need wasm32-unknown-unknown as their target exclude = [ - "examples/certified-counter/src/backend", "packages/ic-response-verification-wasm", "packages/ic-certification-testing-wasm", ] diff --git a/examples/certified-counter/Cargo.lock b/examples/certified-counter/Cargo.lock deleted file mode 100644 index 0524ddc8..00000000 --- a/examples/certified-counter/Cargo.lock +++ /dev/null @@ -1,1095 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aho-corasick" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "ascii-canvas" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" -dependencies = [ - "term", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backend" -version = "0.1.0" -dependencies = [ - "anyhow", - "candid", - "hex", - "ic-cdk", - "ic-cdk-macros", - "ic-certified-map", - "serde", - "serde_cbor", - "sha2", -] - -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" - -[[package]] -name = "binread" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16598dfc8e6578e9b597d9910ba2e73618385dc9f4b1d43dd92c349d6be6418f" -dependencies = [ - "binread_derive", - "lazy_static", - "rustversion", -] - -[[package]] -name = "binread_derive" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d9672209df1714ee804b1f4d4f68c8eb2a90b1f7a07acf472f88ce198ef1fed" -dependencies = [ - "either", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "candid" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244005a1917bb7614cd775ca8a5d59efeb5ac74397bb14ba29a19347ebd78591" -dependencies = [ - "anyhow", - "binread", - "byteorder", - "candid_derive", - "codespan-reporting", - "crc32fast", - "data-encoding", - "hex", - "lalrpop", - "lalrpop-util", - "leb128", - "logos", - "num-bigint", - "num-traits", - "num_enum", - "paste", - "pretty", - "serde", - "serde_bytes", - "sha2", - "thiserror", -] - -[[package]] -name = "candid_derive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f1f4db7c7d04b87b70b3a35c5dc5c2c9dd73cef8bdf6760e2f18a0d45350dd" -dependencies = [ - "lazy_static", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - -[[package]] -name = "cpufeatures" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "data-encoding" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" - -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "ena" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" -dependencies = [ - "log", -] - -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "half" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "ic-cdk" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c98b304a2657bad15bcb547625a018e13cf596676d834cfd93023395a6e2e03a" -dependencies = [ - "candid", - "cfg-if", - "ic-cdk-macros", - "ic0", - "serde", - "serde_bytes", -] - -[[package]] -name = "ic-cdk-macros" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebf50458685a0fc6b0e414cdba487610aeb199ac94db52d9fd76270565debee7" -dependencies = [ - "candid", - "proc-macro2", - "quote", - "serde", - "serde_tokenstream", - "syn 1.0.109", -] - -[[package]] -name = "ic-certified-map" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6adc65afeffc619a7cd19553c66c79820908c12f42191af90cfb39e2e93c4431" -dependencies = [ - "serde", - "serde_bytes", - "sha2", -] - -[[package]] -name = "ic0" -version = "0.18.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187fa0cecf46628330b7a390a1a65fb0637ea00d3a1121aa847ecbebc0f3ff79" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys", -] - -[[package]] -name = "is-terminal" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" -dependencies = [ - "hermit-abi", - "io-lifetimes", - "rustix", - "windows-sys", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "lalrpop" -version = "0.19.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1cbf952127589f2851ab2046af368fd20645491bb4b376f04b7f94d7a9837b" -dependencies = [ - "ascii-canvas", - "bit-set", - "diff", - "ena", - "is-terminal", - "itertools", - "lalrpop-util", - "petgraph", - "regex", - "regex-syntax 0.6.29", - "string_cache", - "term", - "tiny-keccak", - "unicode-xid", -] - -[[package]] -name = "lalrpop-util" -version = "0.19.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3c48237b9604c5a4702de6b824e02006c3214327564636aef27c1028a8fa0ed" -dependencies = [ - "regex", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "leb128" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" - -[[package]] -name = "libc" -version = "0.2.146" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" - -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "lock_api" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" - -[[package]] -name = "logos" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf8b031682c67a8e3d5446840f9573eb7fe26efe7ec8d195c9ac4c0647c502f1" -dependencies = [ - "logos-derive", -] - -[[package]] -name = "logos-derive" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d849148dbaf9661a6151d1ca82b13bb4c4c128146a88d05253b38d4e2f496c" -dependencies = [ - "beef", - "fnv", - "proc-macro2", - "quote", - "regex-syntax 0.6.29", - "syn 1.0.109", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", - "serde", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.3.5", - "smallvec", - "windows-targets", -] - -[[package]] -name = "paste" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" - -[[package]] -name = "petgraph" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" -dependencies = [ - "fixedbitset", - "indexmap", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "pretty" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9940b913ee56ddd94aec2d3cd179dd47068236f42a1a6415ccf9d880ce2a61" -dependencies = [ - "arrayvec", - "typed-arena", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit", -] - -[[package]] -name = "proc-macro2" -version = "1.0.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom", - "redox_syscall 0.2.16", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.7.2", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" - -[[package]] -name = "rustix" -version = "0.37.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4eb579851244c2c03e7c24f501c3432bed80b8f720af1d6e5b0e0f01555a035" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "rustversion" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "serde" -version = "1.0.164" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_bytes" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.164" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.18", -] - -[[package]] -name = "serde_tokenstream" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "797ba1d80299b264f3aac68ab5d12e5825a561749db4df7cd7c8083900c5d4e9" -dependencies = [ - "proc-macro2", - "serde", - "syn 1.0.109", -] - -[[package]] -name = "sha2" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "siphasher" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared", - "precomputed-hash", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.18", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "toml_datetime" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" - -[[package]] -name = "toml_edit" -version = "0.19.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow", -] - -[[package]] -name = "typed-arena" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "unicode-ident" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "winnow" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" -dependencies = [ - "memchr", -] diff --git a/examples/certified-counter/Cargo.toml b/examples/certified-counter/Cargo.toml deleted file mode 100644 index 27339fc5..00000000 --- a/examples/certified-counter/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "src/backend", -] diff --git a/examples/certified-counter/dfx.json b/examples/certified-counter/dfx.json index 34bd17b0..8a5d1a11 100644 --- a/examples/certified-counter/dfx.json +++ b/examples/certified-counter/dfx.json @@ -2,7 +2,7 @@ "canisters": { "backend": { "candid": "src/backend/backend.did", - "package": "backend", + "package": "certified_counter_backend", "type": "rust" }, "frontend": { @@ -15,6 +15,6 @@ } }, "output_env_file": ".env", - "dfx": "0.14.2", + "dfx": "0.15.1", "version": 1 } diff --git a/examples/certified-counter/src/backend/Cargo.toml b/examples/certified-counter/src/backend/Cargo.toml index fd8ff986..74e00b50 100644 --- a/examples/certified-counter/src/backend/Cargo.toml +++ b/examples/certified-counter/src/backend/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "backend" +name = "certified_counter_backend" version = "0.1.0" edition = "2021" @@ -9,10 +9,9 @@ crate-type = ["cdylib"] [dependencies] anyhow = "1.0.71" candid = "0.8.2" -hex = "0.4.3" ic-cdk = "0.6.0" ic-cdk-macros = "0.6.0" -ic-certified-map = "0.3.4" +ic-certification = { path = "../../../../packages/ic-certification" } serde = "1.0.164" serde_cbor = "0.11.2" sha2 = "0.10" diff --git a/examples/certified-counter/src/backend/src/lib.rs b/examples/certified-counter/src/backend/src/lib.rs index 125570a3..20d6d908 100644 --- a/examples/certified-counter/src/backend/src/lib.rs +++ b/examples/certified-counter/src/backend/src/lib.rs @@ -1,6 +1,6 @@ use ic_cdk::export::candid::CandidType; use ic_cdk::*; -use ic_certified_map::*; +use ic_certification::{AsHashTree, RbTree}; use serde::Serialize; use std::cell::*; @@ -27,7 +27,7 @@ fn inc_count() { TREE.with(|tree| { let mut tree = tree.borrow_mut(); - tree.insert("count", hex::decode(hash(&count.to_be_bytes())).unwrap()); + tree.insert("count", hash(&count.to_be_bytes()).to_vec()); ic_cdk::api::set_certified_data(&tree.root_hash()); }) diff --git a/examples/certified-counter/src/frontend/assets/.ic-assets.json b/examples/certified-counter/src/frontend/assets/.ic-assets.json index 3ab949ab..2022eb02 100644 --- a/examples/certified-counter/src/frontend/assets/.ic-assets.json +++ b/examples/certified-counter/src/frontend/assets/.ic-assets.json @@ -2,7 +2,7 @@ { "match": "**/*", "headers": { - "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-eval';connect-src 'self' https://ic0.app https://*.ic0.app;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';upgrade-insecure-requests;", + "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-eval';connect-src 'self' http://localhost:8080 https://ic0.app https://*.ic0.app;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';upgrade-insecure-requests;", "Permissions-Policy": "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), speaker-selection=(), conversion-measurement=(), focus-without-user-activation=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), sync-script=(), trust-token-redemption=(), window-placement=(), vertical-scroll=()", "X-Frame-Options": "DENY", "Referrer-Policy": "same-origin", diff --git a/examples/certified-counter/src/frontend/src/.ic-assets.json b/examples/certified-counter/src/frontend/src/.ic-assets.json index 3ab949ab..2022eb02 100644 --- a/examples/certified-counter/src/frontend/src/.ic-assets.json +++ b/examples/certified-counter/src/frontend/src/.ic-assets.json @@ -2,7 +2,7 @@ { "match": "**/*", "headers": { - "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-eval';connect-src 'self' https://ic0.app https://*.ic0.app;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';upgrade-insecure-requests;", + "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-eval';connect-src 'self' http://localhost:8080 https://ic0.app https://*.ic0.app;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';upgrade-insecure-requests;", "Permissions-Policy": "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), speaker-selection=(), conversion-measurement=(), focus-without-user-activation=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), sync-script=(), trust-token-redemption=(), window-placement=(), vertical-scroll=()", "X-Frame-Options": "DENY", "Referrer-Policy": "same-origin", diff --git a/packages/certificate-verification-js/README.md b/packages/certificate-verification-js/README.md index 59e82e0e..24d37e25 100644 --- a/packages/certificate-verification-js/README.md +++ b/packages/certificate-verification-js/README.md @@ -26,7 +26,7 @@ service : { }; ``` -Check [ic-certified-map](https://github.com/dfinity/cdk-rs/tree/main/library/ic-certified-map) for details on how to create `certificate` and `witness` inside your canister. +Check [ic-certification](https://docs.rs/ic_certification/latest/ic_certification/) for details on how to create `certificate` and `witness` inside your canister. `calculateDataHash` is a userland provided function that can calculate the hash of the data returned from the canister. This must be calculated in the same way on the canister and the frontend. diff --git a/packages/ic-cbor/src/cbor_parse_hash_tree.rs b/packages/ic-cbor/src/cbor_parse_hash_tree.rs index 7a430a00..29db61fd 100644 --- a/packages/ic-cbor/src/cbor_parse_hash_tree.rs +++ b/packages/ic-cbor/src/cbor_parse_hash_tree.rs @@ -1,6 +1,6 @@ use crate::{parse_cbor, CborError, CborHashTree, CborResult, CborValue}; use ic_certification::{ - hash_tree::{empty, fork, label, leaf, pruned, Label, Sha256Digest}, + hash_tree::{empty, fork, label, leaf, pruned, Hash, Label}, HashTree, }; @@ -36,7 +36,7 @@ pub fn parsed_cbor_to_tree(parsed_cbor: &CborValue) -> CborResult { CborHashTree::Pruned => { if let Some(CborValue::ByteString(data)) = cbor_tags.pop() { - let digest: Sha256Digest = TryFrom::<&[u8]>::try_from(data.as_ref()) + let digest: Hash = TryFrom::<&[u8]>::try_from(data.as_ref()) .map_err(CborError::IncorrectPrunedDataLength)?; Ok(pruned(digest)) diff --git a/packages/ic-certification-testing/README.md b/packages/ic-certification-testing/README.md index 08f43e01..f9409b0e 100644 --- a/packages/ic-certification-testing/README.md +++ b/packages/ic-certification-testing/README.md @@ -6,7 +6,7 @@ This package provides a set of utilities to create these certificates for the pu ## Usage -First, a hash tree must be created containing the data that needs to be certified. This can be done using the [ic-certified-map](https://docs.rs/ic-certified-map/latest/ic_certified_map/) library. The root hash of this tree is then used to create the certificate. +First, a hash tree must be created containing the data that needs to be certified. This can be done using the [ic-certification](https://docs.rs/ic_certification/latest/ic_certification/) library. The root hash of this tree is then used to create the certificate. The [ic-certification](https://docs.rs/ic-certification/latest/ic_certification/), [ic-cbor](https://docs.rs/ic-cbor/latest/ic_cbor/) and [ic-certificate-verification](https://docs.rs/ic-certificate-verification/latest/ic_certificate_verification/) libraries can then be used to decode the certificate and verify it. @@ -14,8 +14,7 @@ The [ic-certification](https://docs.rs/ic-certification/latest/ic_certification/ use ic_certification_testing::{CertificateBuilder, CertificateData}; use ic_cbor::CertificateToCbor; use ic_certificate_verification::VerifyCertificate; -use ic_certification::Certificate; -use ic_certified_map::{AsHashTree, RbTree}; +use ic_certification::{Certificate, AsHashTree, RbTree}; use ic_types::CanisterId; use sha2::{Digest, Sha256}; use std::time::{SystemTime, UNIX_EPOCH}; diff --git a/packages/ic-certification/src/hash_tree/mod.rs b/packages/ic-certification/src/hash_tree/mod.rs index abf8d2e7..b6b9cac6 100644 --- a/packages/ic-certification/src/hash_tree/mod.rs +++ b/packages/ic-certification/src/hash_tree/mod.rs @@ -8,7 +8,7 @@ use std::{ }; /// Sha256 Digest: 32 bytes -pub type Sha256Digest = [u8; 32]; +pub type Hash = [u8; 32]; #[derive(Clone, Hash, Ord, PartialOrd, Eq, PartialEq)] /// For labeled [HashTreeNode] @@ -171,7 +171,7 @@ pub enum SubtreeLookupResult> { /// A HashTree representing a full tree. #[derive(Clone, PartialEq, Eq)] pub struct HashTree> { - root: HashTreeNode, + pub(crate) root: HashTreeNode, } impl> fmt::Debug for HashTree { @@ -186,7 +186,7 @@ impl> fmt::Debug for HashTree { impl> HashTree { /// Recomputes root hash of the full tree that this hash tree was constructed from. #[inline] - pub fn digest(&self) -> Sha256Digest { + pub fn digest(&self) -> Hash { self.root.digest() } @@ -271,7 +271,7 @@ pub fn leaf, L: Into>(leaf: L) -> HashTree, C: Into>(content: C) -> HashTree { +pub fn pruned, C: Into>(content: C) -> HashTree { HashTree { root: HashTreeNode::Pruned(content.into()), } @@ -283,7 +283,7 @@ pub fn pruned, C: Into>(content: C) -> HashTr pub fn pruned_from_hex, C: AsRef>( content: C, ) -> Result, FromHexError> { - let mut decode: Sha256Digest = [0; 32]; + let mut decode: Hash = [0; 32]; hex::decode_to_slice(content.as_ref(), &mut decode)?; Ok(pruned(decode)) @@ -315,7 +315,7 @@ pub enum HashTreeNode> { Fork(Box<(Self, Self)>), Labeled(Label, Box), Leaf(Storage), - Pruned(Sha256Digest), + Pruned(Hash), } impl> fmt::Debug for HashTreeNode { @@ -401,7 +401,7 @@ impl> HashTreeNode { /// Calculate the digest of this node only. #[inline] - pub fn digest(&self) -> Sha256Digest { + pub fn digest(&self) -> Hash { let mut hasher = sha2::Sha256::new(); self.domain_sep(&mut hasher); @@ -781,5 +781,36 @@ mod serde_impl { } } +/// Identifiably hashes a fork in the branch. Used for hashing [`HashTree::Fork`]. +pub fn fork_hash(l: &Hash, r: &Hash) -> Hash { + let mut h = domain_sep("ic-hashtree-fork"); + h.update(&l[..]); + h.update(&r[..]); + h.finalize().into() +} + +/// Identifiably hashes a leaf node's data. Used for hashing [`HashTree::Leaf`]. +pub fn leaf_hash(data: &[u8]) -> Hash { + let mut h = domain_sep("ic-hashtree-leaf"); + h.update(data); + h.finalize().into() +} + +/// Identifiably hashes a label for this branch. Used for hashing [`HashTree::Labeled`]. +pub fn labeled_hash(label: &[u8], content_hash: &Hash) -> Hash { + let mut h = domain_sep("ic-hashtree-labeled"); + h.update(label); + h.update(&content_hash[..]); + h.finalize().into() +} + +fn domain_sep(s: &str) -> sha2::Sha256 { + let buf: [u8; 1] = [s.len() as u8]; + let mut h = sha2::Sha256::new(); + h.update(&buf[..]); + h.update(s.as_bytes()); + h +} + #[cfg(test)] mod tests; diff --git a/packages/ic-certification/src/lib.rs b/packages/ic-certification/src/lib.rs index 78140108..5cef3878 100644 --- a/packages/ic-certification/src/lib.rs +++ b/packages/ic-certification/src/lib.rs @@ -2,17 +2,21 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -use hash_tree::Sha256Digest; use hex::FromHexError; pub mod certificate; pub mod hash_tree; +pub use crate::hash_tree::*; +pub mod rb_tree; +pub use crate::rb_tree::*; #[doc(inline)] pub use hash_tree::LookupResult; /// A HashTree representing a full tree. pub type HashTree = hash_tree::HashTree>; +/// A HashTreeNode representing a node in a tree. +pub type HashTreeNode = hash_tree::HashTreeNode>; /// For labeled [`HashTreeNode`](hash_tree::HashTreeNode) pub type Label = hash_tree::Label>; /// A result of looking up for a subtree. @@ -37,7 +41,7 @@ pub fn fork(left: HashTree, right: HashTree) -> HashTree { /// Create a labeled hash tree. #[inline] -pub fn label, N: Into>(label: L, node: N) -> HashTree { +pub fn labeled, N: Into>(label: L, node: N) -> HashTree { hash_tree::label(label, node) } @@ -49,7 +53,7 @@ pub fn leaf>>(leaf: L) -> HashTree { /// Create a pruned tree node. #[inline] -pub fn pruned>(content: C) -> HashTree { +pub fn pruned>(content: C) -> HashTree { hash_tree::pruned(content) } diff --git a/packages/ic-certification/src/rb_tree/mod.rs b/packages/ic-certification/src/rb_tree/mod.rs new file mode 100644 index 00000000..480d7034 --- /dev/null +++ b/packages/ic-certification/src/rb_tree/mod.rs @@ -0,0 +1,1023 @@ +use crate::{ + empty, + hash_tree::{fork, fork_hash, labeled_hash, leaf_hash, Hash}, + labeled, leaf, pruned, HashTree, HashTreeNode, +}; +use std::borrow::Cow; +use std::cmp::Ordering::{self, Equal, Greater, Less}; +use std::fmt; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Color { + Red, + Black, +} + +impl Color { + fn flip_assign(&mut self) { + *self = self.flip() + } + + fn flip(self) -> Self { + match self { + Self::Red => Self::Black, + Self::Black => Self::Red, + } + } +} + +/// Types that can be converted into a [`HashTree`]. +pub trait AsHashTree { + /// Returns the root hash of the tree without constructing it. + /// Must be equivalent to `as_hash_tree().reconstruct()`. + fn root_hash(&self) -> Hash; + + /// Constructs a hash tree corresponding to the data. + fn as_hash_tree(&self) -> HashTree; +} + +impl AsHashTree for Vec { + fn root_hash(&self) -> Hash { + leaf_hash(&self[..]) + } + + fn as_hash_tree(&self) -> HashTree { + leaf(Cow::from(&self[..])) + } +} + +impl AsHashTree for Hash { + fn root_hash(&self) -> Hash { + leaf_hash(&self[..]) + } + + fn as_hash_tree(&self) -> HashTree { + leaf(Cow::from(&self[..])) + } +} + +impl, V: AsHashTree + 'static> AsHashTree for RbTree { + fn root_hash(&self) -> Hash { + match self.root.as_ref() { + None => empty().digest(), + Some(n) => n.subtree_hash, + } + } + + fn as_hash_tree(&self) -> HashTree { + Node::full_witness_tree(&self.root, Node::data_tree) + } +} + +#[derive(PartialEq, Debug, Clone, Copy)] +enum KeyBound<'a> { + Exact(&'a [u8]), + Neighbor(&'a [u8]), +} + +impl<'a> AsRef<[u8]> for KeyBound<'a> { + fn as_ref(&self) -> &'a [u8] { + match self { + KeyBound::Exact(key) => key, + KeyBound::Neighbor(key) => key, + } + } +} + +type NodeRef = Option>>; + +// 1. All leaves are black. +// 2. Children of a red node are black. +// 3. Every path from a node goes through the same number of black +// nodes. +#[derive(Clone, Debug)] +struct Node { + key: K, + value: V, + left: NodeRef, + right: NodeRef, + color: Color, + + /// Hash of the full hash tree built from this node and its + /// children. It needs to be recomputed after every rotation. + subtree_hash: Hash, +} + +impl, V: AsHashTree + 'static> Node { + fn new(key: K, value: V) -> Box> { + let value_hash = value.root_hash(); + let data_hash = labeled_hash(key.as_ref(), &value_hash); + Box::new(Self { + key, + value, + left: None, + right: None, + color: Color::Red, + subtree_hash: data_hash, + }) + } + + fn data_hash(&self) -> Hash { + labeled_hash(self.key.as_ref(), &self.value.root_hash()) + } + + fn left_hash_tree(&self) -> HashTree { + match self.left.as_ref() { + None => empty(), + Some(l) => pruned(l.subtree_hash), + } + } + + fn right_hash_tree(&self) -> HashTree { + match self.right.as_ref() { + None => empty(), + Some(r) => pruned(r.subtree_hash), + } + } + + fn visit<'a, F>(n: &'a NodeRef, f: &mut F) + where + F: 'a + FnMut(&'a [u8], &'a V), + { + if let Some(n) = n { + Self::visit(&n.left, f); + (*f)(n.key.as_ref(), &n.value); + Self::visit(&n.right, f) + } + } + + fn data_tree(&self) -> HashTree { + labeled(self.key.as_ref(), self.value.as_hash_tree()) + } + + fn subtree_with<'a>(&'a self, f: impl FnOnce(&'a V) -> HashTree) -> HashTree { + labeled(self.key.as_ref(), f(&self.value)) + } + + fn witness_tree(&self) -> HashTree { + labeled(self.key.as_ref(), pruned(self.value.root_hash())) + } + + fn full_witness_tree<'a>(n: &'a NodeRef, f: fn(&'a Node) -> HashTree) -> HashTree { + match n { + None => empty(), + Some(n) => three_way_fork( + Self::full_witness_tree(&n.left, f), + f(n), + Self::full_witness_tree(&n.right, f), + ), + } + } + + fn update_subtree_hash(&mut self) { + self.subtree_hash = self.compute_subtree_hash(); + } + + fn compute_subtree_hash(&self) -> Hash { + let h = self.data_hash(); + + match (self.left.as_ref(), self.right.as_ref()) { + (None, None) => h, + (Some(l), None) => fork_hash(&l.subtree_hash, &h), + (None, Some(r)) => fork_hash(&h, &r.subtree_hash), + (Some(l), Some(r)) => fork_hash(&l.subtree_hash, &fork_hash(&h, &r.subtree_hash)), + } + } +} + +#[derive(PartialEq, Debug)] +enum Visit { + Pre, + In, + Post, +} + +/// Iterator over a RbTree. +#[derive(Debug)] +pub struct Iter<'a, K, V> { + /// Invariants: + /// 1. visit == Pre: none of the nodes in parents were visited yet. + /// 2. visit == In: the last node in parents and all its left children are visited. + /// 3. visit == Post: all the nodes reachable from the last node in parents are visited. + visit: Visit, + parents: Vec<&'a Node>, +} + +impl<'a, K, V> Iter<'a, K, V> { + /// This function is an adaptation of the traverse_step procedure described in + /// section 7.2 "Bidirectional Bifurcate Coordinates" of + /// "Elements of Programming" by A. Stepanov and P. McJones, p. 118. + /// http://elementsofprogramming.com/eop.pdf + /// + /// The main difference is that our nodes don't have parent links for two reasons: + /// 1. They don't play well with safe Rust ownership model. + /// 2. Iterating a tree shouldn't be an operation common enough to complicate the code. + fn step(&mut self) -> bool { + match self.parents.last() { + Some(tip) => { + match self.visit { + Visit::Pre => { + if let Some(l) = &tip.left { + self.parents.push(l); + } else { + self.visit = Visit::In; + } + } + Visit::In => { + if let Some(r) = &tip.right { + self.parents.push(r); + self.visit = Visit::Pre; + } else { + self.visit = Visit::Post; + } + } + Visit::Post => { + let tip = self.parents.pop().unwrap(); + if let Some(parent) = self.parents.last() { + if parent + .left + .as_ref() + .map(|l| l.as_ref() as *const Node) + == Some(tip as *const Node) + { + self.visit = Visit::In; + } + } + } + } + true + } + None => false, + } + } +} + +impl<'a, K, V> std::iter::Iterator for Iter<'a, K, V> { + type Item = (&'a K, &'a V); + + fn next(&mut self) -> Option { + while self.step() { + if self.visit == Visit::In { + return self.parents.last().map(|n| (&n.key, &n.value)); + } + } + None + } +} + +/// Implements mutable left-leaning red-black trees as defined in +/// +#[derive(Default, Clone)] +pub struct RbTree { + root: NodeRef, +} + +impl PartialEq for RbTree +where + K: 'static + AsRef<[u8]> + PartialEq, + V: 'static + AsHashTree + PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + self.iter().eq(other.iter()) + } +} + +impl Eq for RbTree +where + K: 'static + AsRef<[u8]> + Eq, + V: 'static + AsHashTree + Eq, +{ +} + +impl PartialOrd for RbTree +where + K: 'static + AsRef<[u8]> + PartialOrd, + V: 'static + AsHashTree + PartialOrd, +{ + fn partial_cmp(&self, other: &Self) -> Option { + self.iter().partial_cmp(other.iter()) + } +} + +impl Ord for RbTree +where + K: 'static + AsRef<[u8]> + Ord, + V: 'static + AsHashTree + Ord, +{ + fn cmp(&self, other: &Self) -> Ordering { + self.iter().cmp(other.iter()) + } +} + +impl std::iter::FromIterator<(K, V)> for RbTree +where + K: 'static + AsRef<[u8]>, + V: 'static + AsHashTree, +{ + fn from_iter(iter: T) -> Self + where + T: IntoIterator, + { + let mut t = RbTree::::new(); + for (k, v) in iter.into_iter() { + t.insert(k, v); + } + t + } +} + +impl std::fmt::Debug for RbTree +where + K: 'static + AsRef<[u8]> + std::fmt::Debug, + V: 'static + AsHashTree + std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[")?; + let mut first = true; + for (k, v) in self.iter() { + if !first { + write!(f, ", ")?; + } + first = false; + write!(f, "({:?}, {:?})", k, v)?; + } + write!(f, "]") + } +} + +impl RbTree { + /// Constructs a new empty tree. + pub const fn new() -> Self { + Self { root: None } + } + + /// Returns true if the map is empty. + pub const fn is_empty(&self) -> bool { + self.root.is_none() + } +} + +impl, V: AsHashTree + 'static> RbTree { + /// Looks up the key in the map and returns the associated value, if there is one. + pub fn get(&self, key: &[u8]) -> Option<&V> { + let mut root = self.root.as_ref(); + while let Some(n) = root { + match key.cmp(n.key.as_ref()) { + Equal => return Some(&n.value), + Less => root = n.left.as_ref(), + Greater => root = n.right.as_ref(), + } + } + None + } + + /// Updates the value corresponding to the specified key. + pub fn modify(&mut self, key: &[u8], f: impl FnOnce(&mut V)) { + fn go, V: AsHashTree + 'static>( + h: &mut NodeRef, + k: &[u8], + f: impl FnOnce(&mut V), + ) { + if let Some(h) = h { + match k.as_ref().cmp(h.key.as_ref()) { + Equal => { + f(&mut h.value); + h.update_subtree_hash(); + } + Less => { + go(&mut h.left, k, f); + h.update_subtree_hash(); + } + Greater => { + go(&mut h.right, k, f); + h.update_subtree_hash(); + } + } + } + } + go(&mut self.root, key, f) + } + + fn range_witness<'a>( + &'a self, + left: Option>, + right: Option>, + f: fn(&'a Node) -> HashTree, + ) -> HashTree { + match (left, right) { + (None, None) => Node::full_witness_tree(&self.root, f), + (Some(l), None) => self.witness_range_above(l, f), + (None, Some(r)) => self.witness_range_below(r, f), + (Some(l), Some(r)) => self.witness_range_between(l, r, f), + } + } + + /// Constructs a hash tree that acts as a proof that there is a + /// entry with the specified key in this map. The proof also + /// contains the value in question. + /// + /// If the key is not in the map, returns a proof of absence. + pub fn witness<'a>(&'a self, key: &[u8]) -> HashTree { + self.nested_witness(key, |v| v.as_hash_tree()) + } + + /// Like `witness`, but gives the caller more control over the + /// construction of the value witness. This method is useful for + /// constructing witnesses for nested certified maps. + pub fn nested_witness<'a>(&'a self, key: &[u8], f: impl FnOnce(&'a V) -> HashTree) -> HashTree { + if let Some(t) = self.lookup_and_build_witness(key, f) { + return t; + } + self.range_witness( + self.lower_bound(key), + self.upper_bound(key), + Node::witness_tree, + ) + } + + /// Returns a witness enumerating all the keys in this map. The + /// resulting tree doesn't include values, they are replaced with + /// "Pruned" nodes. + pub fn keys(&self) -> HashTree { + Node::full_witness_tree(&self.root, Node::witness_tree) + } + + /// Returns a witness for the keys in the specified range. The + /// resulting tree doesn't include values, they are replaced with + /// "Pruned" nodes. + pub fn key_range(&self, first: &[u8], last: &[u8]) -> HashTree { + self.range_witness( + self.lower_bound(first), + self.upper_bound(last), + Node::witness_tree, + ) + } + + /// Returns a witness for the key-value pairs in the specified range. + /// The resulting tree contains both keys and values. + pub fn value_range(&self, first: &[u8], last: &[u8]) -> HashTree { + self.range_witness( + self.lower_bound(first), + self.upper_bound(last), + Node::data_tree, + ) + } + + /// Returns a witness that enumerates all the keys starting with + /// the specified prefix. + pub fn keys_with_prefix(&self, prefix: &[u8]) -> HashTree { + self.range_witness( + self.lower_bound(prefix), + self.right_prefix_neighbor(prefix), + Node::witness_tree, + ) + } + + /// Creates an iterator over the map's keys and values. + pub fn iter(&self) -> Iter<'_, K, V> { + match &self.root { + None => Iter { + visit: Visit::Pre, + parents: vec![], + }, + Some(n) => Iter { + visit: Visit::Pre, + parents: vec![n], + }, + } + } + + /// Enumerates all the key-value pairs in the tree. + pub fn for_each<'a, F>(&'a self, mut f: F) + where + F: 'a + FnMut(&'a [u8], &'a V), + { + Node::visit(&self.root, &mut f) + } + + fn witness_range_above<'a>( + &'a self, + lo: KeyBound<'a>, + f: fn(&'a Node) -> HashTree, + ) -> HashTree { + fn go<'a, K: 'static + AsRef<[u8]>, V: AsHashTree + 'static>( + n: &'a NodeRef, + lo: KeyBound<'a>, + f: fn(&'a Node) -> HashTree, + ) -> HashTree { + match n { + None => empty(), + Some(n) => match n.key.as_ref().cmp(lo.as_ref()) { + Equal => three_way_fork( + n.left_hash_tree(), + match lo { + KeyBound::Exact(_) => f(n), + KeyBound::Neighbor(_) => n.witness_tree(), + }, + Node::full_witness_tree(&n.right, f), + ), + Less => three_way_fork( + n.left_hash_tree(), + pruned(n.data_hash()), + go(&n.right, lo, f), + ), + Greater => three_way_fork( + go(&n.left, lo, f), + f(n), + Node::full_witness_tree(&n.right, f), + ), + }, + } + } + go(&self.root, lo, f) + } + + fn witness_range_below<'a>( + &'a self, + hi: KeyBound<'a>, + f: fn(&'a Node) -> HashTree, + ) -> HashTree { + fn go<'a, K: 'static + AsRef<[u8]>, V: AsHashTree + 'static>( + n: &'a NodeRef, + hi: KeyBound<'a>, + f: fn(&'a Node) -> HashTree, + ) -> HashTree { + match n { + None => empty(), + Some(n) => match n.key.as_ref().cmp(hi.as_ref()) { + Equal => three_way_fork( + Node::full_witness_tree(&n.left, f), + match hi { + KeyBound::Exact(_) => f(n), + KeyBound::Neighbor(_) => n.witness_tree(), + }, + n.right_hash_tree(), + ), + Greater => three_way_fork( + go(&n.left, hi, f), + pruned(n.data_hash()), + n.right_hash_tree(), + ), + Less => three_way_fork( + Node::full_witness_tree(&n.left, f), + f(n), + go(&n.right, hi, f), + ), + }, + } + } + go(&self.root, hi, f) + } + + fn witness_range_between<'a>( + &'a self, + lo: KeyBound<'a>, + hi: KeyBound<'a>, + f: fn(&'a Node) -> HashTree, + ) -> HashTree { + debug_assert!( + lo.as_ref() <= hi.as_ref(), + "lo = {:?} > hi = {:?}", + lo.as_ref(), + hi.as_ref() + ); + fn go<'a, K: 'static + AsRef<[u8]>, V: AsHashTree + 'static>( + n: &'a NodeRef, + lo: KeyBound<'a>, + hi: KeyBound<'a>, + f: fn(&'a Node) -> HashTree, + ) -> HashTree { + match n { + None => empty(), + Some(n) => { + let k = n.key.as_ref(); + match (lo.as_ref().cmp(k), k.cmp(hi.as_ref())) { + (Less, Less) => { + three_way_fork(go(&n.left, lo, hi, f), f(n), go(&n.right, lo, hi, f)) + } + (Equal, Equal) => three_way_fork( + n.left_hash_tree(), + match (lo, hi) { + (KeyBound::Exact(_), _) => f(n), + (_, KeyBound::Exact(_)) => f(n), + _ => n.witness_tree(), + }, + n.right_hash_tree(), + ), + (_, Equal) => three_way_fork( + go(&n.left, lo, hi, f), + match hi { + KeyBound::Exact(_) => f(n), + KeyBound::Neighbor(_) => n.witness_tree(), + }, + n.right_hash_tree(), + ), + (Equal, _) => three_way_fork( + n.left_hash_tree(), + match lo { + KeyBound::Exact(_) => f(n), + KeyBound::Neighbor(_) => n.witness_tree(), + }, + go(&n.right, lo, hi, f), + ), + (Less, Greater) => three_way_fork( + go(&n.left, lo, hi, f), + pruned(n.data_hash()), + n.right_hash_tree(), + ), + (Greater, Less) => three_way_fork( + n.left_hash_tree(), + pruned(n.data_hash()), + go(&n.right, lo, hi, f), + ), + _ => pruned(n.subtree_hash), + } + } + } + } + go(&self.root, lo, hi, f) + } + + fn lower_bound(&self, key: &[u8]) -> Option> { + fn go<'a, K: 'static + AsRef<[u8]>, V>( + n: &'a NodeRef, + key: &[u8], + ) -> Option> { + n.as_ref().and_then(|n| { + let node_key = n.key.as_ref(); + match node_key.cmp(key) { + Less => go(&n.right, key).or(Some(KeyBound::Neighbor(node_key))), + Equal => Some(KeyBound::Exact(node_key)), + Greater => go(&n.left, key), + } + }) + } + go(&self.root, key) + } + + fn upper_bound(&self, key: &[u8]) -> Option> { + fn go<'a, K: 'static + AsRef<[u8]>, V>( + n: &'a NodeRef, + key: &[u8], + ) -> Option> { + n.as_ref().and_then(|n| { + let node_key = n.key.as_ref(); + match node_key.cmp(key) { + Less => go(&n.right, key), + Equal => Some(KeyBound::Exact(node_key)), + Greater => go(&n.left, key).or(Some(KeyBound::Neighbor(node_key))), + } + }) + } + go(&self.root, key) + } + + fn right_prefix_neighbor(&self, prefix: &[u8]) -> Option> { + fn is_prefix_of(p: &[u8], x: &[u8]) -> bool { + if p.len() > x.len() { + return false; + } + &x[0..p.len()] == p + } + fn go<'a, K: 'static + AsRef<[u8]>, V>( + n: &'a NodeRef, + prefix: &[u8], + ) -> Option> { + n.as_ref().and_then(|n| { + let node_key = n.key.as_ref(); + match node_key.cmp(prefix) { + Greater if is_prefix_of(prefix, node_key) => go(&n.right, prefix), + Greater => go(&n.left, prefix).or(Some(KeyBound::Neighbor(node_key))), + Less | Equal => go(&n.right, prefix), + } + }) + } + go(&self.root, prefix) + } + + fn lookup_and_build_witness<'a>( + &'a self, + key: &[u8], + f: impl FnOnce(&'a V) -> HashTree, + ) -> Option { + fn go<'a, K: 'static + AsRef<[u8]>, V: AsHashTree + 'static>( + n: &'a NodeRef, + key: &[u8], + f: impl FnOnce(&'a V) -> HashTree, + ) -> Option { + n.as_ref().and_then(|n| match key.cmp(n.key.as_ref()) { + Equal => Some(three_way_fork( + n.left_hash_tree(), + n.subtree_with(f), + n.right_hash_tree(), + )), + Less => { + let subtree = go(&n.left, key, f)?; + Some(three_way_fork( + subtree, + pruned(n.data_hash()), + n.right_hash_tree(), + )) + } + Greater => { + let subtree = go(&n.right, key, f)?; + Some(three_way_fork( + n.left_hash_tree(), + pruned(n.data_hash()), + subtree, + )) + } + }) + } + go(&self.root, key, f) + } + + /// Inserts a key-value entry into the map. + pub fn insert(&mut self, key: K, value: V) { + fn go, V: AsHashTree + 'static>( + h: NodeRef, + k: K, + v: V, + ) -> Box> { + match h { + None => Node::new(k, v), + Some(mut h) => { + match k.as_ref().cmp(h.key.as_ref()) { + Equal => { + h.value = v; + } + Less => { + h.left = Some(go(h.left, k, v)); + } + Greater => { + h.right = Some(go(h.right, k, v)); + } + } + h.update_subtree_hash(); + balance(h) + } + } + } + let mut root = go(self.root.take(), key, value); + root.color = Color::Black; + self.root = Some(root); + + #[cfg(test)] + debug_assert!( + is_balanced(&self.root), + "the tree is not balanced:\n{:?}", + DebugView(&self.root) + ); + } + + /// Removes the specified key from the map. + pub fn delete(&mut self, key: &[u8]) { + fn move_red_left, V: AsHashTree + 'static>( + mut h: Box>, + ) -> Box> { + flip_colors(&mut h); + if is_red(&h.right.as_ref().unwrap().left) { + h.right = Some(rotate_right(h.right.take().unwrap())); + h = rotate_left(h); + flip_colors(&mut h); + } + h + } + + fn move_red_right, V: AsHashTree + 'static>( + mut h: Box>, + ) -> Box> { + flip_colors(&mut h); + if is_red(&h.left.as_ref().unwrap().left) { + h = rotate_right(h); + flip_colors(&mut h); + } + h + } + + #[inline] + fn min, V: AsHashTree + 'static>( + mut h: &mut Box>, + ) -> &mut Box> { + while h.left.is_some() { + h = h.left.as_mut().unwrap(); + } + h + } + + fn delete_min, V: AsHashTree + 'static>( + mut h: Box>, + ) -> NodeRef { + if h.left.is_none() { + debug_assert!(h.right.is_none()); + drop(h); + return None; + } + if !is_red(&h.left) && !is_red(&h.left.as_ref().unwrap().left) { + h = move_red_left(h); + } + h.left = delete_min(h.left.unwrap()); + h.update_subtree_hash(); + Some(balance(h)) + } + + fn go, V: AsHashTree + 'static>( + mut h: Box>, + key: &[u8], + ) -> NodeRef { + if key < h.key.as_ref() { + debug_assert!(h.left.is_some(), "the key must be present in the tree"); + if !is_red(&h.left) && !is_red(&h.left.as_ref().unwrap().left) { + h = move_red_left(h); + } + h.left = go(h.left.take().unwrap(), key); + } else { + if is_red(&h.left) { + h = rotate_right(h); + } + if key == h.key.as_ref() && h.right.is_none() { + debug_assert!(h.left.is_none()); + drop(h); + return None; + } + + if !is_red(&h.right) && !is_red(&h.right.as_ref().unwrap().left) { + h = move_red_right(h); + } + + if key == h.key.as_ref() { + let m = min(h.right.as_mut().unwrap()); + std::mem::swap(&mut h.key, &mut m.key); + std::mem::swap(&mut h.value, &mut m.value); + h.right = delete_min(h.right.take().unwrap()); + } else { + h.right = go(h.right.take().unwrap(), key); + } + } + h.update_subtree_hash(); + Some(balance(h)) + } + + if self.get(key).is_none() { + return; + } + + if !is_red(&self.root.as_ref().unwrap().left) && !is_red(&self.root.as_ref().unwrap().right) + { + self.root.as_mut().unwrap().color = Color::Red; + } + self.root = go(self.root.take().unwrap(), key); + if let Some(n) = self.root.as_mut() { + n.color = Color::Black; + } + + #[cfg(test)] + debug_assert!( + is_balanced(&self.root), + "unbalanced map: {:?}", + DebugView(&self.root) + ); + + debug_assert!(self.get(key).is_none()); + } +} + +fn three_way_fork<'a>(l: HashTree, m: HashTree, r: HashTree) -> HashTree { + match (l.root, m.root, r.root) { + (HashTreeNode::Empty(), m, HashTreeNode::Empty()) => HashTree { root: m }, + (l, m, HashTreeNode::Empty()) => fork(HashTree { root: l }, HashTree { root: m }), + (HashTreeNode::Empty(), m, r) => fork(HashTree { root: m }, HashTree { root: r }), + (HashTreeNode::Pruned(lhash), HashTreeNode::Pruned(mhash), HashTreeNode::Pruned(rhash)) => { + pruned(fork_hash(&lhash, &fork_hash(&mhash, &rhash))) + } + (l, HashTreeNode::Pruned(mhash), HashTreeNode::Pruned(rhash)) => { + fork(HashTree { root: l }, pruned(fork_hash(&mhash, &rhash))) + } + (l, m, r) => fork( + HashTree { root: l }, + fork(HashTree { root: m }, HashTree { root: r }), + ), + } +} + +// helper functions +fn is_red(x: &NodeRef) -> bool { + x.as_ref().map(|h| h.color == Color::Red).unwrap_or(false) +} + +fn balance + 'static, V: AsHashTree + 'static>( + mut h: Box>, +) -> Box> { + if is_red(&h.right) && !is_red(&h.left) { + h = rotate_left(h); + } + if is_red(&h.left) && is_red(&h.left.as_ref().unwrap().left) { + h = rotate_right(h); + } + if is_red(&h.left) && is_red(&h.right) { + flip_colors(&mut h) + } + h +} + +/// Make a left-leaning link lean to the right. +fn rotate_right, V: AsHashTree + 'static>( + mut h: Box>, +) -> Box> { + debug_assert!(is_red(&h.left)); + + let mut x = h.left.take().unwrap(); + h.left = x.right.take(); + h.update_subtree_hash(); + + x.right = Some(h); + x.color = x.right.as_ref().unwrap().color; + x.right.as_mut().unwrap().color = Color::Red; + x.update_subtree_hash(); + + x +} + +fn rotate_left, V: AsHashTree + 'static>( + mut h: Box>, +) -> Box> { + debug_assert!(is_red(&h.right)); + + let mut x = h.right.take().unwrap(); + h.right = x.left.take(); + h.update_subtree_hash(); + + x.left = Some(h); + x.color = x.left.as_ref().unwrap().color; + x.left.as_mut().unwrap().color = Color::Red; + x.update_subtree_hash(); + + x +} + +fn flip_colors(h: &mut Box>) { + h.color.flip_assign(); + h.left.as_mut().unwrap().color.flip_assign(); + h.right.as_mut().unwrap().color.flip_assign(); +} + +#[cfg(test)] +fn is_balanced(root: &NodeRef) -> bool { + fn go(node: &NodeRef, mut num_black: usize) -> bool { + match node { + None => num_black == 0, + Some(ref n) => { + if !is_red(node) { + debug_assert!(num_black > 0); + num_black -= 1; + } else { + assert!(!is_red(&n.left)); + assert!(!is_red(&n.right)); + } + go(&n.left, num_black) && go(&n.right, num_black) + } + } + } + + let mut num_black = 0; + let mut x = root; + while let Some(n) = x { + if !is_red(x) { + num_black += 1; + } + x = &n.left; + } + go(root, num_black) +} + +struct DebugView<'a, K, V>(&'a NodeRef); + +impl<'a, K: AsRef<[u8]>, V> fmt::Debug for DebugView<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn go, V>( + f: &mut fmt::Formatter<'_>, + node: &NodeRef, + offset: usize, + ) -> fmt::Result { + match node { + None => writeln!(f, "{:width$}[B] ", "", width = offset), + Some(ref h) => { + writeln!( + f, + "{:width$}[{}] {:?}", + "", + if is_red(node) { "R" } else { "B" }, + h.key.as_ref(), + width = offset + )?; + go(f, &h.left, offset + 2)?; + go(f, &h.right, offset + 2) + } + } + } + go(f, self.0, 0) + } +} + +#[cfg(test)] +mod tests; diff --git a/packages/ic-certification/src/rb_tree/tests.rs b/packages/ic-certification/src/rb_tree/tests.rs new file mode 100644 index 00000000..06cc54ef --- /dev/null +++ b/packages/ic-certification/src/rb_tree/tests.rs @@ -0,0 +1,389 @@ +use super::*; +use std::convert::AsRef; + +type TreeOfBytes = RbTree, Vec>; + +fn insert(t: &mut TreeOfBytes, k: impl AsRef<[u8]>, v: impl AsRef<[u8]>) { + t.insert(k.as_ref().to_vec(), v.as_ref().to_vec()) +} + +fn get_labels<'a>(ht: &'a HashTreeNode) -> Vec<&'a [u8]> { + fn go<'a>(t: &'a HashTreeNode, keys: &mut Vec<&'a [u8]>) { + match t { + HashTreeNode::Labeled(key, _) => { + keys.push(key.as_bytes()); + } + HashTreeNode::Fork(lr) => { + go(&lr.0, keys); + go(&lr.1, keys); + } + _ => (), + } + } + let mut keys = vec![]; + go(ht, &mut keys); + keys +} + +fn get_leaf_values<'a>(ht: &'a HashTreeNode) -> Vec<&'a [u8]> { + fn go<'a>(t: &'a HashTreeNode, values: &mut Vec<&'a [u8]>) { + match t { + HashTreeNode::Leaf(value) => { + values.push(&value); + } + HashTreeNode::Fork(lr) => { + go(&lr.0, values); + go(&lr.1, values); + } + HashTreeNode::Labeled(_, t) => { + go(t, values); + } + _ => (), + } + } + let mut values = vec![]; + go(ht, &mut values); + values +} + +#[test] +fn test_witness() { + let mut t = TreeOfBytes::new(); + for i in 0u64..10 { + let key = (1 + 2 * i).to_be_bytes(); + let val = (1 + 2 * i).to_le_bytes(); + insert(&mut t, key, val); + assert_eq!(t.get(&key[..]).map(|v| &v[..]), Some(&val[..])); + } + + for i in 0u64..10 { + let key = (1 + 2 * i).to_be_bytes(); + let ht = t.witness(&key[..]); + assert_eq!( + ht.digest(), + t.root_hash(), + "key: {}, witness {:?}", + hex::encode(key), + ht + ); + + let ht = t.keys_with_prefix(&key[..]); + assert_eq!( + ht.digest(), + t.root_hash(), + "key: {}, lower bound: {:?}, upper_bound: {:?}, witness {:?}", + hex::encode(key), + t.lower_bound(&key[..]).map(hex::encode), + t.right_prefix_neighbor(&key[..]).map(hex::encode), + ht + ); + } + + for i in 0u64..10 { + for j in i..10 { + let start = (2 * i).to_be_bytes(); + let end = (2 * j).to_be_bytes(); + let ht = t.key_range(&start[..], &end[..]); + assert_eq!( + ht.digest(), + t.root_hash(), + "key range: [{}, {}], witness {:?}", + hex::encode(&start[..]), + hex::encode(&end[..]), + ht + ); + + let ht = t.value_range(&start[..], &end[..]); + assert_eq!( + ht.digest(), + t.root_hash(), + "key range: [{}, {}], witness {:?}", + hex::encode(&start[..]), + hex::encode(&end[..]), + ht + ); + } + } + + for i in 0u64..11 { + let key = (2 * i).to_be_bytes(); + let ht = t.witness(&key[..]); + assert_eq!( + ht.digest(), + t.root_hash(), + "key: {}, witness {:?}", + hex::encode(&key[..]), + ht + ); + } + + for i in 0u64..10 { + let key = (1 + 2 * i).to_be_bytes(); + let val = (1 + 2 * i).to_le_bytes(); + + assert_eq!(t.get(&key[..]).map(|v| &v[..]), Some(&val[..])); + + t.delete(&key[..]); + for j in 0u64..10 { + let witness_key = (1 + 2 * j).to_be_bytes(); + let ht = t.witness(&witness_key[..]); + assert_eq!( + ht.digest(), + t.root_hash(), + "key: {}, witness {:?}", + hex::encode(&key[..]), + ht + ); + } + assert_eq!(t.get(&key[..]), None); + } +} + +#[test] +fn test_key_bounds() { + let mut t = TreeOfBytes::new(); + t.insert(vec![1], vec![10]); + t.insert(vec![3], vec![30]); + + assert_eq!(t.lower_bound(&[0u8][..]), None); + assert_eq!(t.lower_bound(&[1u8][..]), Some(KeyBound::Exact(&[1u8]))); + assert_eq!(t.lower_bound(&[2u8][..]), Some(KeyBound::Neighbor(&[1u8]))); + assert_eq!(t.lower_bound(&[3u8][..]), Some(KeyBound::Exact(&[3u8]))); + assert_eq!(t.lower_bound(&[4u8][..]), Some(KeyBound::Neighbor(&[3u8]))); + + assert_eq!(t.upper_bound(&[0u8][..]), Some(KeyBound::Neighbor(&[1u8]))); + assert_eq!(t.upper_bound(&[1u8][..]), Some(KeyBound::Exact(&[1u8]))); + assert_eq!(t.upper_bound(&[2u8][..]), Some(KeyBound::Neighbor(&[3u8]))); + assert_eq!(t.upper_bound(&[3u8][..]), Some(KeyBound::Exact(&[3u8]))); + assert_eq!(t.upper_bound(&[4u8][..]), None); +} + +#[test] +fn test_prefix_neighbor() { + let mut t = TreeOfBytes::new(); + insert(&mut t, b"a/b", vec![0]); + insert(&mut t, b"a/b/c", vec![1]); + insert(&mut t, b"a/b/d", vec![2]); + insert(&mut t, b"a/c/d", vec![3]); + + assert_eq!( + t.right_prefix_neighbor(b"a/b/c"), + Some(KeyBound::Neighbor(&b"a/b/d"[..])) + ); + assert_eq!( + t.right_prefix_neighbor(b"a/b"), + Some(KeyBound::Neighbor(&b"a/c/d"[..])) + ); + assert_eq!(t.right_prefix_neighbor(b"a/c/d"), None); + assert_eq!(t.right_prefix_neighbor(b"a"), None); +} + +#[test] +fn simple_delete_test() { + let mut t = TreeOfBytes::new(); + insert(&mut t, b"x", b"a"); + insert(&mut t, b"y", b"b"); + insert(&mut t, b"z", b"c"); + + t.delete(b"x"); + assert_eq!(t.get(b"x"), None); + assert_eq!(t.get(b"y").map(|v| &v[..]), Some(&b"b"[..])); + assert_eq!(t.get(b"z").map(|v| &v[..]), Some(&b"c"[..])); + + t.delete(b"y"); + assert_eq!(t.get(b"y").map(|v| &v[..]), None); + assert_eq!(t.get(b"z").map(|v| &v[..]), Some(&b"c"[..])); + + t.delete(b"z"); + assert_eq!(t.get(b"z").map(|v| &v[..]), None); +} + +#[test] +fn simple_delete_test_2() { + let mut t = TreeOfBytes::new(); + insert(&mut t, b"x", b"y"); + insert(&mut t, b"z", b"w"); + + t.delete(b"z"); + assert_eq!(t.get(b"z"), None); + assert_eq!(t.get(b"x").map(|v| &v[..]), Some(&b"y"[..])); +} + +#[test] +fn map_model_test() { + use std::collections::HashMap; + + let mut hm: HashMap, Vec> = HashMap::new(); + let mut rb = TreeOfBytes::new(); + + for i in 0..100u64 { + hm.insert(i.to_be_bytes().to_vec(), i.to_be_bytes().to_vec()); + insert(&mut rb, i.to_be_bytes(), i.to_be_bytes()); + + for k in hm.keys() { + assert_eq!(hm.get(k), rb.get(k)); + } + } + let keys: Vec<_> = hm.keys().cloned().collect(); + + for k in keys { + hm.remove(&k); + + assert!(rb.get(&k).is_some()); + rb.delete(&k); + assert!(rb.get(&k).is_none()); + + for k in hm.keys() { + assert_eq!(hm.get(k), rb.get(k)); + } + } +} + +#[test] +fn test_nested_witness() { + let mut rb: RbTree, TreeOfBytes> = RbTree::new(); + let mut nested = RbTree::new(); + nested.insert(b"bottom".to_vec(), b"data".to_vec()); + rb.insert(b"top".to_vec(), nested); + + let ht = rb.nested_witness(&b"top"[..], |v| v.witness(&b"bottom"[..])); + + assert_eq!(ht.digest(), rb.root_hash()); + match ht.root { + HashTreeNode::Labeled(lt, tt) => { + assert_eq!(lt.as_bytes(), b"top"); + match &(*tt) { + HashTreeNode::Labeled(lb, _) => { + assert_eq!(lb.as_bytes(), b"bottom"); + } + other => panic!("unexpected nested tree: {:?}", other), + } + } + other => panic!("expected a labeled tree, got {:?}", other), + } + + rb.modify(b"top", |m| m.delete(b"bottom")); + let ht = rb.nested_witness(&b"top"[..], |v| v.witness(&b"bottom"[..])); + assert_eq!(ht.digest(), rb.root_hash()); +} + +#[test] +fn test_witness_key_range() { + let mut t = TreeOfBytes::new(); + insert(&mut t, b"b", b"x"); + insert(&mut t, b"d", b"y"); + insert(&mut t, b"f", b"z"); + + assert_eq!(get_labels(&t.key_range(b"a", b"a").root), vec![b"b"]); + assert_eq!(get_labels(&t.key_range(b"a", b"b").root), vec![b"b"]); + assert_eq!(get_labels(&t.key_range(b"a", b"c").root), vec![b"b", b"d"]); + assert_eq!( + get_labels(&t.key_range(b"a", b"f").root), + vec![b"b", b"d", b"f"] + ); + assert_eq!( + get_labels(&t.key_range(b"a", b"z").root), + vec![b"b", b"d", b"f"] + ); + + assert_eq!(get_labels(&t.key_range(b"b", b"b").root), vec![b"b"]); + assert_eq!(get_labels(&t.key_range(b"b", b"c").root), vec![b"b", b"d"]); + assert_eq!( + get_labels(&t.key_range(b"b", b"f").root), + vec![b"b", b"d", b"f"] + ); + assert_eq!( + get_labels(&t.key_range(b"b", b"z").root), + vec![b"b", b"d", b"f"] + ); + + assert_eq!(get_labels(&t.key_range(b"d", b"e").root), vec![b"d", b"f"]); + assert_eq!(get_labels(&t.key_range(b"d", b"f").root), vec![b"d", b"f"]); + assert_eq!(get_labels(&t.key_range(b"d", b"z").root), vec![b"d", b"f"]); + assert_eq!(get_labels(&t.key_range(b"y", b"z").root), vec![b"f"]); + + assert!(get_leaf_values(&t.key_range(b"a", b"z").root).is_empty()); +} + +#[test] +fn test_witness_value_range() { + let mut t = TreeOfBytes::new(); + insert(&mut t, b"b", b"x"); + insert(&mut t, b"d", b"y"); + insert(&mut t, b"f", b"z"); + + assert_eq!(get_labels(&t.value_range(b"a", b"a").root), vec![b"b"]); + assert!(get_leaf_values(&t.value_range(b"a", b"a").root).is_empty()); + + assert_eq!(get_labels(&t.value_range(b"a", b"b").root), vec![b"b"]); + assert_eq!(get_leaf_values(&t.value_range(b"a", b"b").root), vec![b"x"]); + + assert_eq!(get_labels(&t.value_range(b"f", b"z").root), vec![b"f"]); + assert_eq!(get_leaf_values(&t.value_range(b"f", b"z").root), vec![b"z"]); + + assert_eq!(get_labels(&t.value_range(b"g", b"z").root), vec![b"f"]); + assert!(get_leaf_values(&t.value_range(b"g", b"z").root).is_empty()); + + assert_eq!( + get_labels(&t.value_range(b"a", b"z").root), + vec![b"b", b"d", b"f"] + ); + assert_eq!( + get_leaf_values(&t.value_range(b"a", b"z").root), + vec![b"x", b"y", b"z"] + ); +} + +#[test] +fn test_iter() { + let mut t = TreeOfBytes::new(); + let mut v = vec![]; + for k in 0..100u64 { + insert(&mut t, k.to_be_bytes(), (k + 10).to_be_bytes()); + v.push((k.to_be_bytes().to_vec(), (k + 10).to_be_bytes().to_vec())); + assert!( + t.iter().eq(v.iter().map(|(k, v)| (k, v))), + "iterators aren't equal {:?} vs {:?}", + &t.iter().collect::>(), + v + ); + } +} + +#[test] +fn test_equality() { + let mut t1 = TreeOfBytes::new(); + for k in (0..100u64).rev() { + insert(&mut t1, k.to_be_bytes(), (k + 10).to_be_bytes()); + } + let t2 = (0..100u64) + .map(|k| (k.to_be_bytes().to_vec(), (k + 10).to_be_bytes().to_vec())) + .collect(); + + assert_eq!(t1, t2); + assert_eq!(t1.cmp(&t2), Equal); + + insert(&mut t1, 200u64.to_be_bytes(), 210u64.to_be_bytes()); + assert_ne!(t1, t2); + assert_ne!(t1.cmp(&t2), Equal); +} + +#[test] +fn test_ordering() { + let t1: TreeOfBytes = (0..10u64) + .map(|k| (k.to_be_bytes().to_vec(), k.to_be_bytes().to_vec())) + .collect(); + let t2: TreeOfBytes = (0..10u64) + .map(|k| (k.to_be_bytes().to_vec(), (k + 1).to_be_bytes().to_vec())) + .collect(); + let t3: TreeOfBytes = (0..5u64) + .map(|k| (k.to_be_bytes().to_vec(), k.to_be_bytes().to_vec())) + .collect(); + let t4: TreeOfBytes = (1..5u64) + .map(|k| (k.to_be_bytes().to_vec(), k.to_be_bytes().to_vec())) + .collect(); + + assert_eq!(t1.cmp(&t2), Less); + assert_eq!(t1.cmp(&t3), Greater); + assert_eq!(t1.cmp(&t4), Less); +} diff --git a/packages/ic-response-verification-test-utils/Cargo.toml b/packages/ic-response-verification-test-utils/Cargo.toml index adcddeec..b75b3bd7 100644 --- a/packages/ic-response-verification-test-utils/Cargo.toml +++ b/packages/ic-response-verification-test-utils/Cargo.toml @@ -8,7 +8,6 @@ base64 = "0.21" hex = "0.4" ic-certification-testing = { path = "../ic-certification-testing", version = "1.2.0" } ic-types = { git = "https://github.com/dfinity/ic", rev = "6e3bb8100e7724a8ec53dac26faa3426378a6953" } -ic-certified-map = "0.4" sha2 = "0.10" serde_cbor = "0.11" leb128 = "0.2" diff --git a/packages/ic-response-verification-test-utils/src/asset_tree.rs b/packages/ic-response-verification-test-utils/src/asset_tree.rs index 101cfeb0..b8a41df9 100644 --- a/packages/ic-response-verification-test-utils/src/asset_tree.rs +++ b/packages/ic-response-verification-test-utils/src/asset_tree.rs @@ -1,6 +1,6 @@ use crate::hash::hash; use crate::{cbor_encode, hash_from_hex}; -use ic_certified_map::{labeled, labeled_hash, AsHashTree, Hash, HashTree, RbTree}; +use ic_certification::{labeled, labeled_hash, AsHashTree, Hash, HashTree, RbTree}; const LABEL_ASSETS: &[u8] = b"http_assets"; diff --git a/packages/ic-response-verification-test-utils/src/certificate.rs b/packages/ic-response-verification-test-utils/src/certificate.rs index b65ef69d..624e2395 100644 --- a/packages/ic-response-verification-test-utils/src/certificate.rs +++ b/packages/ic-response-verification-test-utils/src/certificate.rs @@ -1,5 +1,5 @@ use crate::{base64_encode, hex_decode}; -use ic_certification::{fork, label, leaf, pruned_from_hex, Certificate, Delegation, HashTree}; +use ic_certification::{fork, labeled, leaf, pruned_from_hex, Certificate, Delegation, HashTree}; use ic_types::CanisterId; use std::str::FromStr; @@ -63,12 +63,12 @@ pub fn create_certificate(options: Option) -> Certific let tree = fork( fork( fork( - label("canister", - label(canister_id, + labeled("canister", + labeled(canister_id, fork( fork( fork( - label( + labeled( "certified_data", leaf(certified_data) ), @@ -86,7 +86,7 @@ pub fn create_certificate(options: Option) -> Certific ), fork( create_pruned("0B2F12CF83A8A339691C0D39BE38432CEE1A64DA8E3898BB69AFDB6970823C5E"), - label( + labeled( "time", leaf(time), ), diff --git a/packages/ic-response-verification-test-utils/src/expr_tree.rs b/packages/ic-response-verification-test-utils/src/expr_tree.rs index 6ab54c07..c9b2e133 100644 --- a/packages/ic-response-verification-test-utils/src/expr_tree.rs +++ b/packages/ic-response-verification-test-utils/src/expr_tree.rs @@ -1,5 +1,5 @@ use crate::{cbor_encode, NestedTree}; -use ic_certified_map::{labeled, labeled_hash, AsHashTree, Hash, HashTree}; +use ic_certification::{labeled, labeled_hash, AsHashTree, Hash, HashTree}; const LABEL_EXPR: &[u8] = b"http_expr"; diff --git a/packages/ic-response-verification-test-utils/src/hash.rs b/packages/ic-response-verification-test-utils/src/hash.rs index 82b79880..c76c7d4b 100644 --- a/packages/ic-response-verification-test-utils/src/hash.rs +++ b/packages/ic-response-verification-test-utils/src/hash.rs @@ -1,4 +1,4 @@ -use ic_certified_map::Hash; +use ic_certification::Hash; use sha2::{Digest, Sha256}; pub fn hash(data: T) -> Hash diff --git a/packages/ic-response-verification-test-utils/src/nested_tree.rs b/packages/ic-response-verification-test-utils/src/nested_tree.rs index 0553a824..a0ea94c6 100644 --- a/packages/ic-response-verification-test-utils/src/nested_tree.rs +++ b/packages/ic-response-verification-test-utils/src/nested_tree.rs @@ -1,4 +1,4 @@ -use ic_certified_map::{AsHashTree, HashTree, RbTree}; +use ic_certification::{AsHashTree, Hash, HashTree, RbTree}; pub trait NestedTreeKeyRequirements: Clone + AsRef<[u8]> + 'static {} pub trait NestedTreeValueRequirements: AsHashTree + 'static {} @@ -18,14 +18,14 @@ impl Default for N } impl AsHashTree for NestedTree { - fn root_hash(&self) -> ic_certified_map::Hash { + fn root_hash(&self) -> Hash { match self { NestedTree::Leaf(a) => a.root_hash(), NestedTree::Nested(tree) => tree.root_hash(), } } - fn as_hash_tree(&self) -> HashTree<'_> { + fn as_hash_tree(&self) -> HashTree { match self { NestedTree::Leaf(a) => a.as_hash_tree(), NestedTree::Nested(tree) => tree.as_hash_tree(), diff --git a/packages/ic-response-verification-test-utils/src/v2_certificate_fixture.rs b/packages/ic-response-verification-test-utils/src/v2_certificate_fixture.rs index 0978a56b..6cbb7708 100644 --- a/packages/ic-response-verification-test-utils/src/v2_certificate_fixture.rs +++ b/packages/ic-response-verification-test-utils/src/v2_certificate_fixture.rs @@ -1,8 +1,8 @@ use crate::{ cbor_encode, create_expr_tree_path, create_versioned_certificate_header, hash, ExprTree, }; +use ic_certification::Hash; use ic_certification_testing::{CertificateBuilder, CertificateData}; -use ic_certified_map::Hash; use ic_types::CanisterId; pub struct V2TreeFixture { diff --git a/packages/ic-response-verification-wasm/Cargo.lock b/packages/ic-response-verification-wasm/Cargo.lock index 873c752c..ec7be93a 100644 --- a/packages/ic-response-verification-wasm/Cargo.lock +++ b/packages/ic-response-verification-wasm/Cargo.lock @@ -881,17 +881,6 @@ dependencies = [ "wasm-bindgen-console-logger", ] -[[package]] -name = "ic-certified-map" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197524aecec47db0b6c0c9f8821aad47272c2bd762c7a0ffe9715eaca0364061" -dependencies = [ - "serde", - "serde_bytes", - "sha2 0.10.6", -] - [[package]] name = "ic-constants" version = "0.8.0" @@ -1116,7 +1105,6 @@ dependencies = [ "hex", "ic-certification", "ic-certification-testing", - "ic-certified-map", "ic-types", "leb128", "serde", diff --git a/packages/ic-response-verification/Cargo.toml b/packages/ic-response-verification/Cargo.toml index d71653fb..246dd111 100644 --- a/packages/ic-response-verification/Cargo.toml +++ b/packages/ic-response-verification/Cargo.toml @@ -48,6 +48,5 @@ serde = "1.0" ic-response-verification-test-utils = { path = "../ic-response-verification-test-utils" } ic-crypto-tree-hash = { git = "https://github.com/dfinity/ic", rev = "6e3bb8100e7724a8ec53dac26faa3426378a6953" } ic-types = { git = "https://github.com/dfinity/ic", rev = "6e3bb8100e7724a8ec53dac26faa3426378a6953" } -ic-certified-map = "0.4" rstest = "0.18" ic-certification-testing = { path = "../ic-certification-testing" } diff --git a/packages/ic-response-verification/src/hash/request_hash.rs b/packages/ic-response-verification/src/hash/request_hash.rs index 91a03bfe..bdbc8e48 100644 --- a/packages/ic-response-verification/src/hash/request_hash.rs +++ b/packages/ic-response-verification/src/hash/request_hash.rs @@ -1,6 +1,6 @@ use crate::error::ResponseVerificationResult; use crate::types::{Request, RequestCertification}; -use ic_certification::hash_tree::Sha256Digest; +use ic_certification::hash_tree::Hash; use ic_representation_independent_hash::{hash, representation_independent_hash, Value}; /// Calculates the @@ -10,7 +10,7 @@ use ic_representation_independent_hash::{hash, representation_independent_hash, pub fn request_hash( request: &Request, request_certification: &RequestCertification, -) -> ResponseVerificationResult { +) -> ResponseVerificationResult { let mut filtered_headers = get_filtered_headers(&request.headers, request_certification); filtered_headers.push(( diff --git a/packages/ic-response-verification/src/hash/response_hash.rs b/packages/ic-response-verification/src/hash/response_hash.rs index c6eec133..e8324d20 100644 --- a/packages/ic-response-verification/src/hash/response_hash.rs +++ b/packages/ic-response-verification/src/hash/response_hash.rs @@ -1,5 +1,5 @@ use crate::types::{Response, ResponseCertification}; -use ic_certification::hash_tree::Sha256Digest; +use ic_certification::hash_tree::Hash; use ic_representation_independent_hash::{hash, representation_independent_hash, Value}; const CERTIFICATE_HEADER_NAME: &str = "IC-Certificate"; @@ -83,10 +83,7 @@ pub fn filter_response_headers( /// Calculates the /// [Representation Independent Hash](https://internetcomputer.org/docs/current/references/ic-interface-spec/#hash-of-map) /// of [ResponseHeaders] that have been filtered with [filter_response_headers]. -pub fn response_headers_hash( - status_code: &u64, - response_headers: &ResponseHeaders, -) -> Sha256Digest { +pub fn response_headers_hash(status_code: &u64, response_headers: &ResponseHeaders) -> Hash { let mut headers_to_verify: Vec<(String, Value)> = response_headers .headers .iter() @@ -116,10 +113,7 @@ pub fn response_headers_hash( /// [Representation Independent Hash](https://internetcomputer.org/docs/current/references/ic-interface-spec/#hash-of-map) /// of a [crate::types::Response] according to [crate::types::ResponseCertification] returned from /// [crate::cel::cel_to_certification]. -pub fn response_hash( - response: &Response, - response_certification: &ResponseCertification, -) -> Sha256Digest { +pub fn response_hash(response: &Response, response_certification: &ResponseCertification) -> Hash { let filtered_headers = filter_response_headers(response, response_certification); let concatenated_hashes = [ response_headers_hash(&response.status_code.into(), &filtered_headers), diff --git a/packages/ic-response-verification/src/test_utils.rs b/packages/ic-response-verification/src/test_utils.rs index 8bbf5244..49714d7e 100644 --- a/packages/ic-response-verification/src/test_utils.rs +++ b/packages/ic-response-verification/src/test_utils.rs @@ -1,6 +1,6 @@ #[cfg(test)] pub mod test_utils { - use ic_certification::hash_tree::{fork, label, leaf, pruned_from_hex, Sha256Digest}; + use ic_certification::hash_tree::{fork, label, leaf, pruned_from_hex, Hash}; use ic_certification::HashTree; use ic_response_verification_test_utils::{base64_encode, hex_decode}; @@ -48,7 +48,7 @@ pub mod test_utils { pruned_from_hex(data).unwrap() } - pub fn sha256_from_hex(data: &str) -> Sha256Digest { + pub fn sha256_from_hex(data: &str) -> Hash { TryFrom::try_from(hex_decode(data)).unwrap() } diff --git a/packages/ic-response-verification/src/validation/v1_validation.rs b/packages/ic-response-verification/src/validation/v1_validation.rs index c11f5597..00b9cfb1 100644 --- a/packages/ic-response-verification/src/validation/v1_validation.rs +++ b/packages/ic-response-verification/src/validation/v1_validation.rs @@ -1,6 +1,6 @@ -use ic_certification::{hash_tree::Sha256Digest, HashTree, LookupResult}; +use ic_certification::{hash_tree::Hash, HashTree, LookupResult}; -pub fn validate_body(tree: &HashTree, request_path: &str, body_sha: &Sha256Digest) -> bool { +pub fn validate_body(tree: &HashTree, request_path: &str, body_sha: &Hash) -> bool { let asset_path = ["http_assets".as_bytes(), request_path.as_bytes()]; let index_fallback_path = ["http_assets".as_bytes(), "/index.html".as_bytes()]; diff --git a/packages/ic-response-verification/src/validation/v2_validation.rs b/packages/ic-response-verification/src/validation/v2_validation.rs index 47906525..7d248df8 100644 --- a/packages/ic-response-verification/src/validation/v2_validation.rs +++ b/packages/ic-response-verification/src/validation/v2_validation.rs @@ -1,6 +1,6 @@ use crate::types::Certification; use ic_certification::hash_tree::HashTreeNode; -use ic_certification::{hash_tree::Sha256Digest, HashTree, Label, SubtreeLookupResult}; +use ic_certification::{hash_tree::Hash, HashTree, Label, SubtreeLookupResult}; fn path_from_parts(parts: &[T]) -> Vec