feat: reverse mutations generation, mutations serialization (#355)
* feat: revert mutations generation, mutations serialization * tests: check both `apply_mutations` and `apply_mutations_with_reversion` * feat: add `num_leaves` method for `Smt` * refactor: improve ad-hoc benchmarks * chore: update crate version to v0.13.1
This commit is contained in:
parent
1444bbc0f2
commit
589839fef1
12 changed files with 533 additions and 150 deletions
|
@ -1,9 +1,14 @@
|
||||||
|
## 0.13.1 (2024-12-26)
|
||||||
|
|
||||||
|
- Generate reverse mutations set on applying of mutations set, implemented serialization of `MutationsSet` (#355).
|
||||||
|
|
||||||
## 0.13.0 (2024-11-24)
|
## 0.13.0 (2024-11-24)
|
||||||
|
|
||||||
- Fixed a bug in the implementation of `draw_integers` for `RpoRandomCoin` (#343).
|
- Fixed a bug in the implementation of `draw_integers` for `RpoRandomCoin` (#343).
|
||||||
- [BREAKING] Refactor error messages and use `thiserror` to derive errors (#344).
|
- [BREAKING] Refactor error messages and use `thiserror` to derive errors (#344).
|
||||||
- [BREAKING] Updated Winterfell dependency to v0.11 (#346).
|
- [BREAKING] Updated Winterfell dependency to v0.11 (#346).
|
||||||
|
|
||||||
|
|
||||||
## 0.12.0 (2024-10-30)
|
## 0.12.0 (2024-10-30)
|
||||||
|
|
||||||
- [BREAKING] Updated Winterfell dependency to v0.10 (#338).
|
- [BREAKING] Updated Winterfell dependency to v0.10 (#338).
|
||||||
|
|
124
Cargo.lock
generated
124
Cargo.lock
generated
|
@ -92,18 +92,18 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit-set"
|
name = "bit-set"
|
||||||
version = "0.5.3"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit-vec",
|
"bit-vec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit-vec"
|
name = "bit-vec"
|
||||||
version = "0.6.3"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
|
@ -113,9 +113,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blake3"
|
name = "blake3"
|
||||||
version = "1.5.4"
|
version = "1.5.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7"
|
checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayref",
|
"arrayref",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
|
@ -153,9 +153,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.1"
|
version = "1.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
|
checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -197,9 +197,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.21"
|
version = "4.5.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f"
|
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
@ -207,9 +207,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.21"
|
version = "4.5.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec"
|
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
|
@ -231,9 +231,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.7.3"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7"
|
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
|
@ -294,9 +294,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.8.5"
|
version = "0.8.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-epoch",
|
"crossbeam-epoch",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
|
@ -313,9 +313,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.20"
|
version = "0.8.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crunchy"
|
name = "crunchy"
|
||||||
|
@ -351,19 +351,19 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.9"
|
version = "0.3.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.2.0"
|
version = "2.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
|
@ -456,9 +456,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.13"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2"
|
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jobserver"
|
name = "jobserver"
|
||||||
|
@ -471,10 +471,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.72"
|
version = "0.3.76"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
|
checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -495,9 +496,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.164"
|
version = "0.2.169"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
|
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libm"
|
name = "libm"
|
||||||
|
@ -525,7 +526,7 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miden-crypto"
|
name = "miden-crypto"
|
||||||
version = "0.13.0"
|
version = "0.13.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert_matches",
|
"assert_matches",
|
||||||
"blake3",
|
"blake3",
|
||||||
|
@ -685,9 +686,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proptest"
|
name = "proptest"
|
||||||
version = "1.5.0"
|
version = "1.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d"
|
checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit-set",
|
"bit-set",
|
||||||
"bit-vec",
|
"bit-vec",
|
||||||
|
@ -711,9 +712,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.37"
|
version = "1.0.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
@ -808,15 +809,15 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.41"
|
version = "0.38.42"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
|
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -854,18 +855,18 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.215"
|
version = "1.0.216"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.215"
|
version = "1.0.216"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -874,9 +875,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.133"
|
version = "1.0.134"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -908,9 +909,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.89"
|
version = "2.0.92"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
|
checksum = "70ae51629bf965c5c098cc9e87908a3df5301051a9e087d6f9bef5c9771ed126"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -932,18 +933,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.3"
|
version = "2.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa"
|
checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "2.0.3"
|
version = "2.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568"
|
checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1017,9 +1018,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.95"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
|
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
@ -1028,13 +1029,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-backend"
|
name = "wasm-bindgen-backend"
|
||||||
version = "0.2.95"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
|
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
|
@ -1043,9 +1043,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.95"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
|
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
|
@ -1053,9 +1053,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.95"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1066,15 +1066,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.95"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
|
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.72"
|
version = "0.3.76"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112"
|
checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
[package]
|
[package]
|
||||||
name = "miden-crypto"
|
name = "miden-crypto"
|
||||||
version = "0.13.0"
|
version = "0.13.1"
|
||||||
description = "Miden Cryptographic primitives"
|
description = "Miden Cryptographic primitives"
|
||||||
authors = ["miden contributors"]
|
authors = ["miden contributors"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/0xPolygonMiden/crypto"
|
repository = "https://github.com/0xPolygonMiden/crypto"
|
||||||
documentation = "https://docs.rs/miden-crypto/0.13.0"
|
documentation = "https://docs.rs/miden-crypto/0.13.1"
|
||||||
categories = ["cryptography", "no-std"]
|
categories = ["cryptography", "no-std"]
|
||||||
keywords = ["miden", "crypto", "hash", "merkle"]
|
keywords = ["miden", "crypto", "hash", "merkle"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
@ -65,7 +65,7 @@ assert_matches = { version = "1.5", default-features = false }
|
||||||
criterion = { version = "0.5", features = ["html_reports"] }
|
criterion = { version = "0.5", features = ["html_reports"] }
|
||||||
getrandom = { version = "0.2", features = ["js"] }
|
getrandom = { version = "0.2", features = ["js"] }
|
||||||
hex = { version = "0.4", default-features = false, features = ["alloc"] }
|
hex = { version = "0.4", default-features = false, features = ["alloc"] }
|
||||||
proptest = "1.5"
|
proptest = "1.6"
|
||||||
rand_chacha = { version = "0.3", default-features = false }
|
rand_chacha = { version = "0.3", default-features = false }
|
||||||
rand-utils = { version = "0.11", package = "winter-rand-utils" }
|
rand-utils = { version = "0.11", package = "winter-rand-utils" }
|
||||||
seq-macro = { version = "0.3" }
|
seq-macro = { version = "0.3" }
|
||||||
|
|
8
Makefile
8
Makefile
|
@ -81,6 +81,10 @@ build-sve: ## Build with sve support
|
||||||
|
|
||||||
# --- benchmarking --------------------------------------------------------------------------------
|
# --- benchmarking --------------------------------------------------------------------------------
|
||||||
|
|
||||||
.PHONY: bench-tx
|
.PHONY: bench
|
||||||
bench-tx: ## Run crypto benchmarks
|
bench: ## Run crypto benchmarks
|
||||||
cargo bench
|
cargo bench
|
||||||
|
|
||||||
|
.PHONY: bench-smt-concurrent
|
||||||
|
bench-smt-concurrent: ## Run SMT benchmarks with concurrent feature
|
||||||
|
cargo run --release --features executable -- --size 1000000
|
||||||
|
|
162
src/main.rs
162
src/main.rs
|
@ -4,8 +4,9 @@ use clap::Parser;
|
||||||
use miden_crypto::{
|
use miden_crypto::{
|
||||||
hash::rpo::{Rpo256, RpoDigest},
|
hash::rpo::{Rpo256, RpoDigest},
|
||||||
merkle::{MerkleError, Smt},
|
merkle::{MerkleError, Smt},
|
||||||
Felt, Word, ONE,
|
Felt, Word, EMPTY_WORD, ONE,
|
||||||
};
|
};
|
||||||
|
use rand::{prelude::IteratorRandom, thread_rng, Rng};
|
||||||
use rand_utils::rand_value;
|
use rand_utils::rand_value;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
|
@ -13,7 +14,7 @@ use rand_utils::rand_value;
|
||||||
pub struct BenchmarkCmd {
|
pub struct BenchmarkCmd {
|
||||||
/// Size of the tree
|
/// Size of the tree
|
||||||
#[clap(short = 's', long = "size")]
|
#[clap(short = 's', long = "size")]
|
||||||
size: u64,
|
size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -29,101 +30,153 @@ pub fn benchmark_smt() {
|
||||||
let mut entries = Vec::new();
|
let mut entries = Vec::new();
|
||||||
for i in 0..tree_size {
|
for i in 0..tree_size {
|
||||||
let key = rand_value::<RpoDigest>();
|
let key = rand_value::<RpoDigest>();
|
||||||
let value = [ONE, ONE, ONE, Felt::new(i)];
|
let value = [ONE, ONE, ONE, Felt::new(i as u64)];
|
||||||
entries.push((key, value));
|
entries.push((key, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut tree = construction(entries, tree_size).unwrap();
|
let mut tree = construction(entries.clone(), tree_size).unwrap();
|
||||||
insertion(&mut tree, tree_size).unwrap();
|
insertion(&mut tree).unwrap();
|
||||||
batched_insertion(&mut tree, tree_size).unwrap();
|
batched_insertion(&mut tree).unwrap();
|
||||||
proof_generation(&mut tree, tree_size).unwrap();
|
batched_update(&mut tree, entries).unwrap();
|
||||||
|
proof_generation(&mut tree).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the construction benchmark for [`Smt`], returning the constructed tree.
|
/// Runs the construction benchmark for [`Smt`], returning the constructed tree.
|
||||||
pub fn construction(entries: Vec<(RpoDigest, Word)>, size: u64) -> Result<Smt, MerkleError> {
|
pub fn construction(entries: Vec<(RpoDigest, Word)>, size: usize) -> Result<Smt, MerkleError> {
|
||||||
println!("Running a construction benchmark:");
|
println!("Running a construction benchmark:");
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let tree = Smt::with_entries(entries)?;
|
let tree = Smt::with_entries(entries)?;
|
||||||
let elapsed = now.elapsed();
|
let elapsed = now.elapsed().as_secs_f32();
|
||||||
println!(
|
|
||||||
"Constructed a SMT with {} key-value pairs in {:.3} seconds",
|
|
||||||
size,
|
|
||||||
elapsed.as_secs_f32(),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
println!("Constructed a SMT with {size} key-value pairs in {elapsed:.1} seconds");
|
||||||
println!("Number of leaf nodes: {}\n", tree.leaves().count());
|
println!("Number of leaf nodes: {}\n", tree.leaves().count());
|
||||||
|
|
||||||
Ok(tree)
|
Ok(tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the insertion benchmark for the [`Smt`].
|
/// Runs the insertion benchmark for the [`Smt`].
|
||||||
pub fn insertion(tree: &mut Smt, size: u64) -> Result<(), MerkleError> {
|
pub fn insertion(tree: &mut Smt) -> Result<(), MerkleError> {
|
||||||
|
const NUM_INSERTIONS: usize = 1_000;
|
||||||
|
|
||||||
println!("Running an insertion benchmark:");
|
println!("Running an insertion benchmark:");
|
||||||
|
|
||||||
|
let size = tree.num_leaves();
|
||||||
|
|
||||||
let mut insertion_times = Vec::new();
|
let mut insertion_times = Vec::new();
|
||||||
|
|
||||||
for i in 0..20 {
|
for i in 0..NUM_INSERTIONS {
|
||||||
let test_key = Rpo256::hash(&rand_value::<u64>().to_be_bytes());
|
let test_key = Rpo256::hash(&rand_value::<u64>().to_be_bytes());
|
||||||
let test_value = [ONE, ONE, ONE, Felt::new(size + i)];
|
let test_value = [ONE, ONE, ONE, Felt::new((size + i) as u64)];
|
||||||
|
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
tree.insert(test_key, test_value);
|
tree.insert(test_key, test_value);
|
||||||
let elapsed = now.elapsed();
|
let elapsed = now.elapsed();
|
||||||
insertion_times.push(elapsed.as_secs_f32());
|
insertion_times.push(elapsed.as_micros());
|
||||||
}
|
}
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"An average insertion time measured by 20 inserts into a SMT with {} key-value pairs is {:.3} milliseconds\n",
|
"An average insertion time measured by {NUM_INSERTIONS} inserts into an SMT with {size} leaves is {:.0} μs\n",
|
||||||
size,
|
// calculate the average
|
||||||
// calculate the average by dividing by 20 and convert to milliseconds by multiplying by
|
insertion_times.iter().sum::<u128>() as f64 / (NUM_INSERTIONS as f64),
|
||||||
// 1000. As a result, we can only multiply by 50
|
|
||||||
insertion_times.iter().sum::<f32>() * 50f32,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn batched_insertion(tree: &mut Smt, size: u64) -> Result<(), MerkleError> {
|
pub fn batched_insertion(tree: &mut Smt) -> Result<(), MerkleError> {
|
||||||
|
const NUM_INSERTIONS: usize = 1_000;
|
||||||
|
|
||||||
println!("Running a batched insertion benchmark:");
|
println!("Running a batched insertion benchmark:");
|
||||||
|
|
||||||
let new_pairs: Vec<(RpoDigest, Word)> = (0..1000)
|
let size = tree.num_leaves();
|
||||||
|
|
||||||
|
let new_pairs: Vec<(RpoDigest, Word)> = (0..NUM_INSERTIONS)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let key = Rpo256::hash(&rand_value::<u64>().to_be_bytes());
|
let key = Rpo256::hash(&rand_value::<u64>().to_be_bytes());
|
||||||
let value = [ONE, ONE, ONE, Felt::new(size + i)];
|
let value = [ONE, ONE, ONE, Felt::new((size + i) as u64)];
|
||||||
(key, value)
|
(key, value)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let mutations = tree.compute_mutations(new_pairs);
|
let mutations = tree.compute_mutations(new_pairs);
|
||||||
let compute_elapsed = now.elapsed();
|
let compute_elapsed = now.elapsed().as_secs_f64() * 1000_f64; // time in ms
|
||||||
|
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
tree.apply_mutations(mutations).unwrap();
|
tree.apply_mutations(mutations)?;
|
||||||
let apply_elapsed = now.elapsed();
|
let apply_elapsed = now.elapsed().as_secs_f64() * 1000_f64; // time in ms
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"An average batch computation time measured by a 1k-batch into an SMT with {} key-value pairs over {:.3} milliseconds is {:.3} milliseconds",
|
"An average insert-batch computation time measured by a {NUM_INSERTIONS}-batch into an SMT with {size} leaves over {:.1} ms is {:.0} μs",
|
||||||
size,
|
compute_elapsed,
|
||||||
compute_elapsed.as_secs_f32() * 1000f32,
|
compute_elapsed * 1000_f64 / NUM_INSERTIONS as f64, // time in μs
|
||||||
// Dividing by the number of iterations, 1000, and then multiplying by 1000 to get
|
|
||||||
// milliseconds, cancels out.
|
|
||||||
compute_elapsed.as_secs_f32(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"An average batch application time measured by a 1k-batch into an SMT with {} key-value pairs over {:.3} milliseconds is {:.3} milliseconds",
|
"An average insert-batch application time measured by a {NUM_INSERTIONS}-batch into an SMT with {size} leaves over {:.1} ms is {:.0} μs",
|
||||||
size,
|
apply_elapsed,
|
||||||
apply_elapsed.as_secs_f32() * 1000f32,
|
apply_elapsed * 1000_f64 / NUM_INSERTIONS as f64, // time in μs
|
||||||
// Dividing by the number of iterations, 1000, and then multiplying by 1000 to get
|
|
||||||
// milliseconds, cancels out.
|
|
||||||
apply_elapsed.as_secs_f32(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"An average batch insertion time measured by a 1k-batch into an SMT with {} key-value pairs totals to {:.3} milliseconds",
|
"An average batch insertion time measured by a 1k-batch into an SMT with {size} leaves totals to {:.1} ms",
|
||||||
size,
|
(compute_elapsed + apply_elapsed),
|
||||||
(compute_elapsed + apply_elapsed).as_secs_f32() * 1000f32,
|
);
|
||||||
|
|
||||||
|
println!();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn batched_update(tree: &mut Smt, entries: Vec<(RpoDigest, Word)>) -> Result<(), MerkleError> {
|
||||||
|
const NUM_UPDATES: usize = 1_000;
|
||||||
|
const REMOVAL_PROBABILITY: f64 = 0.2;
|
||||||
|
|
||||||
|
println!("Running a batched update benchmark:");
|
||||||
|
|
||||||
|
let size = tree.num_leaves();
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
|
let new_pairs =
|
||||||
|
entries
|
||||||
|
.into_iter()
|
||||||
|
.choose_multiple(&mut rng, NUM_UPDATES)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(key, _)| {
|
||||||
|
let value = if rng.gen_bool(REMOVAL_PROBABILITY) {
|
||||||
|
EMPTY_WORD
|
||||||
|
} else {
|
||||||
|
[ONE, ONE, ONE, Felt::new(rng.gen())]
|
||||||
|
};
|
||||||
|
|
||||||
|
(key, value)
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(new_pairs.len(), NUM_UPDATES);
|
||||||
|
|
||||||
|
let now = Instant::now();
|
||||||
|
let mutations = tree.compute_mutations(new_pairs);
|
||||||
|
let compute_elapsed = now.elapsed().as_secs_f64() * 1000_f64; // time in ms
|
||||||
|
|
||||||
|
let now = Instant::now();
|
||||||
|
tree.apply_mutations(mutations)?;
|
||||||
|
let apply_elapsed = now.elapsed().as_secs_f64() * 1000_f64; // time in ms
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"An average update-batch computation time measured by a {NUM_UPDATES}-batch into an SMT with {size} leaves over {:.1} ms is {:.0} μs",
|
||||||
|
compute_elapsed,
|
||||||
|
compute_elapsed * 1000_f64 / NUM_UPDATES as f64, // time in μs
|
||||||
|
);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"An average update-batch application time measured by a {NUM_UPDATES}-batch into an SMT with {size} leaves over {:.1} ms is {:.0} μs",
|
||||||
|
apply_elapsed,
|
||||||
|
apply_elapsed * 1000_f64 / NUM_UPDATES as f64, // time in μs
|
||||||
|
);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"An average batch update time measured by a 1k-batch into an SMT with {size} leaves totals to {:.1} ms",
|
||||||
|
(compute_elapsed + apply_elapsed),
|
||||||
);
|
);
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
@ -132,28 +185,29 @@ pub fn batched_insertion(tree: &mut Smt, size: u64) -> Result<(), MerkleError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the proof generation benchmark for the [`Smt`].
|
/// Runs the proof generation benchmark for the [`Smt`].
|
||||||
pub fn proof_generation(tree: &mut Smt, size: u64) -> Result<(), MerkleError> {
|
pub fn proof_generation(tree: &mut Smt) -> Result<(), MerkleError> {
|
||||||
|
const NUM_PROOFS: usize = 100;
|
||||||
|
|
||||||
println!("Running a proof generation benchmark:");
|
println!("Running a proof generation benchmark:");
|
||||||
|
|
||||||
let mut insertion_times = Vec::new();
|
let mut insertion_times = Vec::new();
|
||||||
|
|
||||||
for i in 0..20 {
|
let size = tree.num_leaves();
|
||||||
|
|
||||||
|
for i in 0..NUM_PROOFS {
|
||||||
let test_key = Rpo256::hash(&rand_value::<u64>().to_be_bytes());
|
let test_key = Rpo256::hash(&rand_value::<u64>().to_be_bytes());
|
||||||
let test_value = [ONE, ONE, ONE, Felt::new(size + i)];
|
let test_value = [ONE, ONE, ONE, Felt::new((size + i) as u64)];
|
||||||
tree.insert(test_key, test_value);
|
tree.insert(test_key, test_value);
|
||||||
|
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let _proof = tree.open(&test_key);
|
let _proof = tree.open(&test_key);
|
||||||
let elapsed = now.elapsed();
|
insertion_times.push(now.elapsed().as_micros());
|
||||||
insertion_times.push(elapsed.as_secs_f32());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"An average proving time measured by 20 value proofs in a SMT with {} key-value pairs in {:.3} microseconds",
|
"An average proving time measured by {NUM_PROOFS} value proofs in an SMT with {size} leaves in {:.0} μs",
|
||||||
size,
|
// calculate the average
|
||||||
// calculate the average by dividing by 20 and convert to microseconds by multiplying by
|
insertion_times.iter().sum::<u128>() as f64 / (NUM_PROOFS as f64),
|
||||||
// 1000000. As a result, we can only multiply by 50000
|
|
||||||
insertion_times.iter().sum::<f32>() * 50000f32,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -128,7 +128,7 @@ impl NodeIndex {
|
||||||
self.value
|
self.value
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the current instance points to a right sibling node.
|
/// Returns `true` if the current instance points to a right sibling node.
|
||||||
pub const fn is_value_odd(&self) -> bool {
|
pub const fn is_value_odd(&self) -> bool {
|
||||||
(self.value & 1) == 1
|
(self.value & 1) == 1
|
||||||
}
|
}
|
||||||
|
|
|
@ -303,7 +303,7 @@ impl PartialMmr {
|
||||||
|
|
||||||
if leaf_pos + 1 == self.forest
|
if leaf_pos + 1 == self.forest
|
||||||
&& path.depth() == 0
|
&& path.depth() == 0
|
||||||
&& self.peaks.last().map_or(false, |v| *v == leaf)
|
&& self.peaks.last().is_some_and(|v| *v == leaf)
|
||||||
{
|
{
|
||||||
self.track_latest = true;
|
self.track_latest = true;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
|
@ -70,7 +70,7 @@ impl SmtLeaf {
|
||||||
Self::Single((key, value))
|
Self::Single((key, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a new single leaf with the specified entry. The leaf index is derived from the
|
/// Returns a new multiple leaf with the specified entries. The leaf index is derived from the
|
||||||
/// entries' keys.
|
/// entries' keys.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
|
|
@ -114,6 +114,11 @@ impl Smt {
|
||||||
<Self as SparseMerkleTree<SMT_DEPTH>>::root(self)
|
<Self as SparseMerkleTree<SMT_DEPTH>>::root(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the number of non-empty leaves in this tree.
|
||||||
|
pub fn num_leaves(&self) -> usize {
|
||||||
|
self.leaves.len()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the leaf to which `key` maps
|
/// Returns the leaf to which `key` maps
|
||||||
pub fn get_leaf(&self, key: &RpoDigest) -> SmtLeaf {
|
pub fn get_leaf(&self, key: &RpoDigest) -> SmtLeaf {
|
||||||
<Self as SparseMerkleTree<SMT_DEPTH>>::get_leaf(self, key)
|
<Self as SparseMerkleTree<SMT_DEPTH>>::get_leaf(self, key)
|
||||||
|
@ -200,7 +205,7 @@ impl Smt {
|
||||||
<Self as SparseMerkleTree<SMT_DEPTH>>::compute_mutations(self, kv_pairs)
|
<Self as SparseMerkleTree<SMT_DEPTH>>::compute_mutations(self, kv_pairs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply the prospective mutations computed with [`Smt::compute_mutations()`] to this tree.
|
/// Applies the prospective mutations computed with [`Smt::compute_mutations()`] to this tree.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// If `mutations` was computed on a tree with a different root than this one, returns
|
/// If `mutations` was computed on a tree with a different root than this one, returns
|
||||||
|
@ -214,6 +219,23 @@ impl Smt {
|
||||||
<Self as SparseMerkleTree<SMT_DEPTH>>::apply_mutations(self, mutations)
|
<Self as SparseMerkleTree<SMT_DEPTH>>::apply_mutations(self, mutations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Applies the prospective mutations computed with [`Smt::compute_mutations()`] to this tree
|
||||||
|
/// and returns the reverse mutation set.
|
||||||
|
///
|
||||||
|
/// Applying the reverse mutation sets to the updated tree will revert the changes.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// If `mutations` was computed on a tree with a different root than this one, returns
|
||||||
|
/// [`MerkleError::ConflictingRoots`] with a two-item [`Vec`]. The first item is the root hash
|
||||||
|
/// the `mutations` were computed against, and the second item is the actual current root of
|
||||||
|
/// this tree.
|
||||||
|
pub fn apply_mutations_with_reversion(
|
||||||
|
&mut self,
|
||||||
|
mutations: MutationSet<SMT_DEPTH, RpoDigest, Word>,
|
||||||
|
) -> Result<MutationSet<SMT_DEPTH, RpoDigest, Word>, MerkleError> {
|
||||||
|
<Self as SparseMerkleTree<SMT_DEPTH>>::apply_mutations_with_reversion(self, mutations)
|
||||||
|
}
|
||||||
|
|
||||||
// HELPERS
|
// HELPERS
|
||||||
// --------------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -275,12 +297,12 @@ impl SparseMerkleTree<SMT_DEPTH> for Smt {
|
||||||
.unwrap_or_else(|| EmptySubtreeRoots::get_inner_node(SMT_DEPTH, index.depth()))
|
.unwrap_or_else(|| EmptySubtreeRoots::get_inner_node(SMT_DEPTH, index.depth()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_inner_node(&mut self, index: NodeIndex, inner_node: InnerNode) {
|
fn insert_inner_node(&mut self, index: NodeIndex, inner_node: InnerNode) -> Option<InnerNode> {
|
||||||
self.inner_nodes.insert(index, inner_node);
|
self.inner_nodes.insert(index, inner_node)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_inner_node(&mut self, index: NodeIndex) {
|
fn remove_inner_node(&mut self, index: NodeIndex) -> Option<InnerNode> {
|
||||||
let _ = self.inner_nodes.remove(&index);
|
self.inner_nodes.remove(&index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_value(&mut self, key: Self::Key, value: Self::Value) -> Option<Self::Value> {
|
fn insert_value(&mut self, key: Self::Key, value: Self::Value) -> Option<Self::Value> {
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
use alloc::vec::Vec;
|
use alloc::{collections::BTreeMap, vec::Vec};
|
||||||
|
|
||||||
use super::{Felt, LeafIndex, NodeIndex, Rpo256, RpoDigest, Smt, SmtLeaf, EMPTY_WORD, SMT_DEPTH};
|
use super::{Felt, LeafIndex, NodeIndex, Rpo256, RpoDigest, Smt, SmtLeaf, EMPTY_WORD, SMT_DEPTH};
|
||||||
use crate::{
|
use crate::{
|
||||||
merkle::{smt::SparseMerkleTree, EmptySubtreeRoots, MerkleStore},
|
merkle::{
|
||||||
|
smt::{NodeMutation, SparseMerkleTree},
|
||||||
|
EmptySubtreeRoots, MerkleStore, MutationSet,
|
||||||
|
},
|
||||||
utils::{Deserializable, Serializable},
|
utils::{Deserializable, Serializable},
|
||||||
Word, ONE, WORD_SIZE,
|
Word, ONE, WORD_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
// SMT
|
// SMT
|
||||||
// --------------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -412,21 +414,49 @@ fn test_prospective_insertion() {
|
||||||
|
|
||||||
let mutations = smt.compute_mutations(vec![(key_1, value_1)]);
|
let mutations = smt.compute_mutations(vec![(key_1, value_1)]);
|
||||||
assert_eq!(mutations.root(), root_1, "prospective root 1 did not match actual root 1");
|
assert_eq!(mutations.root(), root_1, "prospective root 1 did not match actual root 1");
|
||||||
smt.apply_mutations(mutations).unwrap();
|
let revert = apply_mutations(&mut smt, mutations);
|
||||||
assert_eq!(smt.root(), root_1, "mutations before and after apply did not match");
|
assert_eq!(smt.root(), root_1, "mutations before and after apply did not match");
|
||||||
|
assert_eq!(revert.old_root, smt.root(), "reverse mutations old root did not match");
|
||||||
|
assert_eq!(revert.root(), root_empty, "reverse mutations new root did not match");
|
||||||
|
assert_eq!(
|
||||||
|
revert.new_pairs,
|
||||||
|
BTreeMap::from_iter([(key_1, EMPTY_WORD)]),
|
||||||
|
"reverse mutations pairs did not match"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
revert.node_mutations,
|
||||||
|
smt.inner_nodes.keys().map(|key| (*key, NodeMutation::Removal)).collect(),
|
||||||
|
"reverse mutations inner nodes did not match"
|
||||||
|
);
|
||||||
|
|
||||||
let mutations = smt.compute_mutations(vec![(key_2, value_2)]);
|
let mutations = smt.compute_mutations(vec![(key_2, value_2)]);
|
||||||
assert_eq!(mutations.root(), root_2, "prospective root 2 did not match actual root 2");
|
assert_eq!(mutations.root(), root_2, "prospective root 2 did not match actual root 2");
|
||||||
let mutations =
|
let mutations =
|
||||||
smt.compute_mutations(vec![(key_3, EMPTY_WORD), (key_2, value_2), (key_3, value_3)]);
|
smt.compute_mutations(vec![(key_3, EMPTY_WORD), (key_2, value_2), (key_3, value_3)]);
|
||||||
assert_eq!(mutations.root(), root_3, "mutations before and after apply did not match");
|
assert_eq!(mutations.root(), root_3, "mutations before and after apply did not match");
|
||||||
smt.apply_mutations(mutations).unwrap();
|
let old_root = smt.root();
|
||||||
|
let revert = apply_mutations(&mut smt, mutations);
|
||||||
|
assert_eq!(revert.old_root, smt.root(), "reverse mutations old root did not match");
|
||||||
|
assert_eq!(revert.root(), old_root, "reverse mutations new root did not match");
|
||||||
|
assert_eq!(
|
||||||
|
revert.new_pairs,
|
||||||
|
BTreeMap::from_iter([(key_2, EMPTY_WORD), (key_3, EMPTY_WORD)]),
|
||||||
|
"reverse mutations pairs did not match"
|
||||||
|
);
|
||||||
|
|
||||||
// Edge case: multiple values at the same key, where a later pair restores the original value.
|
// Edge case: multiple values at the same key, where a later pair restores the original value.
|
||||||
let mutations = smt.compute_mutations(vec![(key_3, EMPTY_WORD), (key_3, value_3)]);
|
let mutations = smt.compute_mutations(vec![(key_3, EMPTY_WORD), (key_3, value_3)]);
|
||||||
assert_eq!(mutations.root(), root_3);
|
assert_eq!(mutations.root(), root_3);
|
||||||
smt.apply_mutations(mutations).unwrap();
|
let old_root = smt.root();
|
||||||
|
let revert = apply_mutations(&mut smt, mutations);
|
||||||
assert_eq!(smt.root(), root_3);
|
assert_eq!(smt.root(), root_3);
|
||||||
|
assert_eq!(revert.old_root, smt.root(), "reverse mutations old root did not match");
|
||||||
|
assert_eq!(revert.root(), old_root, "reverse mutations new root did not match");
|
||||||
|
assert_eq!(
|
||||||
|
revert.new_pairs,
|
||||||
|
BTreeMap::from_iter([(key_3, value_3)]),
|
||||||
|
"reverse mutations pairs did not match"
|
||||||
|
);
|
||||||
|
|
||||||
// Test batch updates, and that the order doesn't matter.
|
// Test batch updates, and that the order doesn't matter.
|
||||||
let pairs =
|
let pairs =
|
||||||
|
@ -437,8 +467,16 @@ fn test_prospective_insertion() {
|
||||||
root_empty,
|
root_empty,
|
||||||
"prospective root for batch removal did not match actual root",
|
"prospective root for batch removal did not match actual root",
|
||||||
);
|
);
|
||||||
smt.apply_mutations(mutations).unwrap();
|
let old_root = smt.root();
|
||||||
|
let revert = apply_mutations(&mut smt, mutations);
|
||||||
assert_eq!(smt.root(), root_empty, "mutations before and after apply did not match");
|
assert_eq!(smt.root(), root_empty, "mutations before and after apply did not match");
|
||||||
|
assert_eq!(revert.old_root, smt.root(), "reverse mutations old root did not match");
|
||||||
|
assert_eq!(revert.root(), old_root, "reverse mutations new root did not match");
|
||||||
|
assert_eq!(
|
||||||
|
revert.new_pairs,
|
||||||
|
BTreeMap::from_iter([(key_1, value_1), (key_2, value_2), (key_3, value_3)]),
|
||||||
|
"reverse mutations pairs did not match"
|
||||||
|
);
|
||||||
|
|
||||||
let pairs = vec![(key_3, value_3), (key_1, value_1), (key_2, value_2)];
|
let pairs = vec![(key_3, value_3), (key_1, value_1), (key_2, value_2)];
|
||||||
let mutations = smt.compute_mutations(pairs);
|
let mutations = smt.compute_mutations(pairs);
|
||||||
|
@ -447,6 +485,72 @@ fn test_prospective_insertion() {
|
||||||
assert_eq!(smt.root(), root_3);
|
assert_eq!(smt.root(), root_3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mutations_revert() {
|
||||||
|
let mut smt = Smt::default();
|
||||||
|
|
||||||
|
let key_1: RpoDigest = RpoDigest::from([ONE, ONE, ONE, Felt::new(1)]);
|
||||||
|
let key_2: RpoDigest =
|
||||||
|
RpoDigest::from([2_u32.into(), 2_u32.into(), 2_u32.into(), Felt::new(2)]);
|
||||||
|
let key_3: RpoDigest =
|
||||||
|
RpoDigest::from([0_u32.into(), 0_u32.into(), 0_u32.into(), Felt::new(3)]);
|
||||||
|
|
||||||
|
let value_1 = [ONE; WORD_SIZE];
|
||||||
|
let value_2 = [2_u32.into(); WORD_SIZE];
|
||||||
|
let value_3 = [3_u32.into(); WORD_SIZE];
|
||||||
|
|
||||||
|
smt.insert(key_1, value_1);
|
||||||
|
smt.insert(key_2, value_2);
|
||||||
|
|
||||||
|
let mutations =
|
||||||
|
smt.compute_mutations(vec![(key_1, EMPTY_WORD), (key_2, value_1), (key_3, value_3)]);
|
||||||
|
|
||||||
|
let original = smt.clone();
|
||||||
|
|
||||||
|
let revert = smt.apply_mutations_with_reversion(mutations).unwrap();
|
||||||
|
assert_eq!(revert.old_root, smt.root(), "reverse mutations old root did not match");
|
||||||
|
assert_eq!(revert.root(), original.root(), "reverse mutations new root did not match");
|
||||||
|
|
||||||
|
smt.apply_mutations(revert).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(smt, original, "SMT with applied revert mutations did not match original SMT");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mutation_set_serialization() {
|
||||||
|
let mut smt = Smt::default();
|
||||||
|
|
||||||
|
let key_1: RpoDigest = RpoDigest::from([ONE, ONE, ONE, Felt::new(1)]);
|
||||||
|
let key_2: RpoDigest =
|
||||||
|
RpoDigest::from([2_u32.into(), 2_u32.into(), 2_u32.into(), Felt::new(2)]);
|
||||||
|
let key_3: RpoDigest =
|
||||||
|
RpoDigest::from([0_u32.into(), 0_u32.into(), 0_u32.into(), Felt::new(3)]);
|
||||||
|
|
||||||
|
let value_1 = [ONE; WORD_SIZE];
|
||||||
|
let value_2 = [2_u32.into(); WORD_SIZE];
|
||||||
|
let value_3 = [3_u32.into(); WORD_SIZE];
|
||||||
|
|
||||||
|
smt.insert(key_1, value_1);
|
||||||
|
smt.insert(key_2, value_2);
|
||||||
|
|
||||||
|
let mutations =
|
||||||
|
smt.compute_mutations(vec![(key_1, EMPTY_WORD), (key_2, value_1), (key_3, value_3)]);
|
||||||
|
|
||||||
|
let serialized = mutations.to_bytes();
|
||||||
|
let deserialized =
|
||||||
|
MutationSet::<SMT_DEPTH, RpoDigest, Word>::read_from_bytes(&serialized).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(deserialized, mutations, "deserialized mutations did not match original");
|
||||||
|
|
||||||
|
let revert = smt.apply_mutations_with_reversion(mutations).unwrap();
|
||||||
|
|
||||||
|
let serialized = revert.to_bytes();
|
||||||
|
let deserialized =
|
||||||
|
MutationSet::<SMT_DEPTH, RpoDigest, Word>::read_from_bytes(&serialized).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(deserialized, revert, "deserialized mutations did not match original");
|
||||||
|
}
|
||||||
|
|
||||||
/// Tests that 2 key-value pairs stored in the same leaf have the same path
|
/// Tests that 2 key-value pairs stored in the same leaf have the same path
|
||||||
#[test]
|
#[test]
|
||||||
fn test_smt_path_to_keys_in_same_leaf_are_equal() {
|
fn test_smt_path_to_keys_in_same_leaf_are_equal() {
|
||||||
|
@ -602,3 +706,19 @@ fn build_multiple_leaf_node(kv_pairs: &[(RpoDigest, Word)]) -> RpoDigest {
|
||||||
|
|
||||||
Rpo256::hash_elements(&elements)
|
Rpo256::hash_elements(&elements)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Applies mutations with and without reversion to the given SMT, comparing resulting SMTs,
|
||||||
|
/// returning mutation set for reversion.
|
||||||
|
fn apply_mutations(
|
||||||
|
smt: &mut Smt,
|
||||||
|
mutation_set: MutationSet<SMT_DEPTH, RpoDigest, Word>,
|
||||||
|
) -> MutationSet<SMT_DEPTH, RpoDigest, Word> {
|
||||||
|
let mut smt2 = smt.clone();
|
||||||
|
|
||||||
|
let reversion = smt.apply_mutations_with_reversion(mutation_set.clone()).unwrap();
|
||||||
|
smt2.apply_mutations(mutation_set).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(&smt2, smt);
|
||||||
|
|
||||||
|
reversion
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use alloc::{collections::BTreeMap, vec::Vec};
|
use alloc::{collections::BTreeMap, vec::Vec};
|
||||||
|
|
||||||
|
use winter_utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
|
||||||
|
|
||||||
use super::{EmptySubtreeRoots, InnerNodeInfo, MerkleError, MerklePath, NodeIndex};
|
use super::{EmptySubtreeRoots, InnerNodeInfo, MerkleError, MerklePath, NodeIndex};
|
||||||
use crate::{
|
use crate::{
|
||||||
hash::rpo::{Rpo256, RpoDigest},
|
hash::rpo::{Rpo256, RpoDigest},
|
||||||
|
@ -135,7 +137,7 @@ pub(crate) trait SparseMerkleTree<const DEPTH: u8> {
|
||||||
if node_hash == *EmptySubtreeRoots::entry(DEPTH, node_depth) {
|
if node_hash == *EmptySubtreeRoots::entry(DEPTH, node_depth) {
|
||||||
// If a subtree is empty, then can remove the inner node, since it's equal to the
|
// If a subtree is empty, then can remove the inner node, since it's equal to the
|
||||||
// default value
|
// default value
|
||||||
self.remove_inner_node(index)
|
self.remove_inner_node(index);
|
||||||
} else {
|
} else {
|
||||||
self.insert_inner_node(index, InnerNode { left, right });
|
self.insert_inner_node(index, InnerNode { left, right });
|
||||||
}
|
}
|
||||||
|
@ -241,7 +243,7 @@ pub(crate) trait SparseMerkleTree<const DEPTH: u8> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply the prospective mutations computed with [`SparseMerkleTree::compute_mutations()`] to
|
/// Applies the prospective mutations computed with [`SparseMerkleTree::compute_mutations()`] to
|
||||||
/// this tree.
|
/// this tree.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
@ -275,8 +277,12 @@ pub(crate) trait SparseMerkleTree<const DEPTH: u8> {
|
||||||
|
|
||||||
for (index, mutation) in node_mutations {
|
for (index, mutation) in node_mutations {
|
||||||
match mutation {
|
match mutation {
|
||||||
Removal => self.remove_inner_node(index),
|
Removal => {
|
||||||
Addition(node) => self.insert_inner_node(index, node),
|
self.remove_inner_node(index);
|
||||||
|
},
|
||||||
|
Addition(node) => {
|
||||||
|
self.insert_inner_node(index, node);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,6 +295,76 @@ pub(crate) trait SparseMerkleTree<const DEPTH: u8> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Applies the prospective mutations computed with [`SparseMerkleTree::compute_mutations()`] to
|
||||||
|
/// this tree and returns the reverse mutation set. Applying the reverse mutation sets to the
|
||||||
|
/// updated tree will revert the changes.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// If `mutations` was computed on a tree with a different root than this one, returns
|
||||||
|
/// [`MerkleError::ConflictingRoots`] with a two-item [`Vec`]. The first item is the root hash
|
||||||
|
/// the `mutations` were computed against, and the second item is the actual current root of
|
||||||
|
/// this tree.
|
||||||
|
fn apply_mutations_with_reversion(
|
||||||
|
&mut self,
|
||||||
|
mutations: MutationSet<DEPTH, Self::Key, Self::Value>,
|
||||||
|
) -> Result<MutationSet<DEPTH, Self::Key, Self::Value>, MerkleError>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
use NodeMutation::*;
|
||||||
|
let MutationSet {
|
||||||
|
old_root,
|
||||||
|
node_mutations,
|
||||||
|
new_pairs,
|
||||||
|
new_root,
|
||||||
|
} = mutations;
|
||||||
|
|
||||||
|
// Guard against accidentally trying to apply mutations that were computed against a
|
||||||
|
// different tree, including a stale version of this tree.
|
||||||
|
if old_root != self.root() {
|
||||||
|
return Err(MerkleError::ConflictingRoots {
|
||||||
|
expected_root: self.root(),
|
||||||
|
actual_root: old_root,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut reverse_mutations = BTreeMap::new();
|
||||||
|
for (index, mutation) in node_mutations {
|
||||||
|
match mutation {
|
||||||
|
Removal => {
|
||||||
|
if let Some(node) = self.remove_inner_node(index) {
|
||||||
|
reverse_mutations.insert(index, Addition(node));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Addition(node) => {
|
||||||
|
if let Some(old_node) = self.insert_inner_node(index, node) {
|
||||||
|
reverse_mutations.insert(index, Addition(old_node));
|
||||||
|
} else {
|
||||||
|
reverse_mutations.insert(index, Removal);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut reverse_pairs = BTreeMap::new();
|
||||||
|
for (key, value) in new_pairs {
|
||||||
|
if let Some(old_value) = self.insert_value(key.clone(), value) {
|
||||||
|
reverse_pairs.insert(key, old_value);
|
||||||
|
} else {
|
||||||
|
reverse_pairs.insert(key, Self::EMPTY_VALUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_root(new_root);
|
||||||
|
|
||||||
|
Ok(MutationSet {
|
||||||
|
old_root: new_root,
|
||||||
|
node_mutations: reverse_mutations,
|
||||||
|
new_pairs: reverse_pairs,
|
||||||
|
new_root: old_root,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// REQUIRED METHODS
|
// REQUIRED METHODS
|
||||||
// ---------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -302,10 +378,10 @@ pub(crate) trait SparseMerkleTree<const DEPTH: u8> {
|
||||||
fn get_inner_node(&self, index: NodeIndex) -> InnerNode;
|
fn get_inner_node(&self, index: NodeIndex) -> InnerNode;
|
||||||
|
|
||||||
/// Inserts an inner node at the given index
|
/// Inserts an inner node at the given index
|
||||||
fn insert_inner_node(&mut self, index: NodeIndex, inner_node: InnerNode);
|
fn insert_inner_node(&mut self, index: NodeIndex, inner_node: InnerNode) -> Option<InnerNode>;
|
||||||
|
|
||||||
/// Removes an inner node at the given index
|
/// Removes an inner node at the given index
|
||||||
fn remove_inner_node(&mut self, index: NodeIndex);
|
fn remove_inner_node(&mut self, index: NodeIndex) -> Option<InnerNode>;
|
||||||
|
|
||||||
/// Inserts a leaf node, and returns the value at the key if already exists
|
/// Inserts a leaf node, and returns the value at the key if already exists
|
||||||
fn insert_value(&mut self, key: Self::Key, value: Self::Value) -> Option<Self::Value>;
|
fn insert_value(&mut self, key: Self::Key, value: Self::Value) -> Option<Self::Value>;
|
||||||
|
@ -352,7 +428,7 @@ pub(crate) trait SparseMerkleTree<const DEPTH: u8> {
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub(crate) struct InnerNode {
|
pub struct InnerNode {
|
||||||
pub left: RpoDigest,
|
pub left: RpoDigest,
|
||||||
pub right: RpoDigest,
|
pub right: RpoDigest,
|
||||||
}
|
}
|
||||||
|
@ -423,7 +499,7 @@ impl<const DEPTH: u8> TryFrom<NodeIndex> for LeafIndex<DEPTH> {
|
||||||
/// [`MutationSet`] stores this type in relation to a [`NodeIndex`] to keep track of what changes
|
/// [`MutationSet`] stores this type in relation to a [`NodeIndex`] to keep track of what changes
|
||||||
/// need to occur at which node indices.
|
/// need to occur at which node indices.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub(crate) enum NodeMutation {
|
pub enum NodeMutation {
|
||||||
/// Corresponds to [`SparseMerkleTree::remove_inner_node()`].
|
/// Corresponds to [`SparseMerkleTree::remove_inner_node()`].
|
||||||
Removal,
|
Removal,
|
||||||
/// Corresponds to [`SparseMerkleTree::insert_inner_node()`].
|
/// Corresponds to [`SparseMerkleTree::insert_inner_node()`].
|
||||||
|
@ -456,9 +532,94 @@ pub struct MutationSet<const DEPTH: u8, K, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const DEPTH: u8, K, V> MutationSet<DEPTH, K, V> {
|
impl<const DEPTH: u8, K, V> MutationSet<DEPTH, K, V> {
|
||||||
/// Queries the root that was calculated during `SparseMerkleTree::compute_mutations()`. See
|
/// Returns the SMT root that was calculated during `SparseMerkleTree::compute_mutations()`. See
|
||||||
/// that method for more information.
|
/// that method for more information.
|
||||||
pub fn root(&self) -> RpoDigest {
|
pub fn root(&self) -> RpoDigest {
|
||||||
self.new_root
|
self.new_root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the SMT root before the mutations were applied.
|
||||||
|
pub fn old_root(&self) -> RpoDigest {
|
||||||
|
self.old_root
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the set of inner nodes that need to be removed or added.
|
||||||
|
pub fn node_mutations(&self) -> &BTreeMap<NodeIndex, NodeMutation> {
|
||||||
|
&self.node_mutations
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the set of top-level key-value pairs that need to be added, updated or deleted
|
||||||
|
/// (i.e. set to `EMPTY_WORD`).
|
||||||
|
pub fn new_pairs(&self) -> &BTreeMap<K, V> {
|
||||||
|
&self.new_pairs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SERIALIZATION
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
|
impl Serializable for InnerNode {
|
||||||
|
fn write_into<W: ByteWriter>(&self, target: &mut W) {
|
||||||
|
self.left.write_into(target);
|
||||||
|
self.right.write_into(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deserializable for InnerNode {
|
||||||
|
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
|
||||||
|
let left = source.read()?;
|
||||||
|
let right = source.read()?;
|
||||||
|
|
||||||
|
Ok(Self { left, right })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for NodeMutation {
|
||||||
|
fn write_into<W: ByteWriter>(&self, target: &mut W) {
|
||||||
|
match self {
|
||||||
|
NodeMutation::Removal => target.write_bool(false),
|
||||||
|
NodeMutation::Addition(inner_node) => {
|
||||||
|
target.write_bool(true);
|
||||||
|
inner_node.write_into(target);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deserializable for NodeMutation {
|
||||||
|
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
|
||||||
|
if source.read_bool()? {
|
||||||
|
let inner_node = source.read()?;
|
||||||
|
return Ok(NodeMutation::Addition(inner_node));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(NodeMutation::Removal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const DEPTH: u8, K: Serializable, V: Serializable> Serializable for MutationSet<DEPTH, K, V> {
|
||||||
|
fn write_into<W: ByteWriter>(&self, target: &mut W) {
|
||||||
|
target.write(self.old_root);
|
||||||
|
target.write(self.new_root);
|
||||||
|
self.node_mutations.write_into(target);
|
||||||
|
self.new_pairs.write_into(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const DEPTH: u8, K: Deserializable + Ord, V: Deserializable> Deserializable
|
||||||
|
for MutationSet<DEPTH, K, V>
|
||||||
|
{
|
||||||
|
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
|
||||||
|
let old_root = source.read()?;
|
||||||
|
let new_root = source.read()?;
|
||||||
|
let node_mutations = source.read()?;
|
||||||
|
let new_pairs = source.read()?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
old_root,
|
||||||
|
node_mutations,
|
||||||
|
new_pairs,
|
||||||
|
new_root,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,7 +221,7 @@ impl<const DEPTH: u8> SimpleSmt<DEPTH> {
|
||||||
<Self as SparseMerkleTree<DEPTH>>::compute_mutations(self, kv_pairs)
|
<Self as SparseMerkleTree<DEPTH>>::compute_mutations(self, kv_pairs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply the prospective mutations computed with [`SimpleSmt::compute_mutations()`] to this
|
/// Applies the prospective mutations computed with [`SimpleSmt::compute_mutations()`] to this
|
||||||
/// tree.
|
/// tree.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
@ -236,6 +236,23 @@ impl<const DEPTH: u8> SimpleSmt<DEPTH> {
|
||||||
<Self as SparseMerkleTree<DEPTH>>::apply_mutations(self, mutations)
|
<Self as SparseMerkleTree<DEPTH>>::apply_mutations(self, mutations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Applies the prospective mutations computed with [`SimpleSmt::compute_mutations()`] to
|
||||||
|
/// this tree and returns the reverse mutation set.
|
||||||
|
///
|
||||||
|
/// Applying the reverse mutation sets to the updated tree will revert the changes.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// If `mutations` was computed on a tree with a different root than this one, returns
|
||||||
|
/// [`MerkleError::ConflictingRoots`] with a two-item [`alloc::vec::Vec`]. The first item is the
|
||||||
|
/// root hash the `mutations` were computed against, and the second item is the actual
|
||||||
|
/// current root of this tree.
|
||||||
|
pub fn apply_mutations_with_reversion(
|
||||||
|
&mut self,
|
||||||
|
mutations: MutationSet<DEPTH, LeafIndex<DEPTH>, Word>,
|
||||||
|
) -> Result<MutationSet<DEPTH, LeafIndex<DEPTH>, Word>, MerkleError> {
|
||||||
|
<Self as SparseMerkleTree<DEPTH>>::apply_mutations_with_reversion(self, mutations)
|
||||||
|
}
|
||||||
|
|
||||||
/// Inserts a subtree at the specified index. The depth at which the subtree is inserted is
|
/// Inserts a subtree at the specified index. The depth at which the subtree is inserted is
|
||||||
/// computed as `DEPTH - SUBTREE_DEPTH`.
|
/// computed as `DEPTH - SUBTREE_DEPTH`.
|
||||||
///
|
///
|
||||||
|
@ -321,12 +338,12 @@ impl<const DEPTH: u8> SparseMerkleTree<DEPTH> for SimpleSmt<DEPTH> {
|
||||||
.unwrap_or_else(|| EmptySubtreeRoots::get_inner_node(DEPTH, index.depth()))
|
.unwrap_or_else(|| EmptySubtreeRoots::get_inner_node(DEPTH, index.depth()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_inner_node(&mut self, index: NodeIndex, inner_node: InnerNode) {
|
fn insert_inner_node(&mut self, index: NodeIndex, inner_node: InnerNode) -> Option<InnerNode> {
|
||||||
self.inner_nodes.insert(index, inner_node);
|
self.inner_nodes.insert(index, inner_node)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_inner_node(&mut self, index: NodeIndex) {
|
fn remove_inner_node(&mut self, index: NodeIndex) -> Option<InnerNode> {
|
||||||
let _ = self.inner_nodes.remove(&index);
|
self.inner_nodes.remove(&index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_value(&mut self, key: LeafIndex<DEPTH>, value: Word) -> Option<Word> {
|
fn insert_value(&mut self, key: LeafIndex<DEPTH>, value: Word) -> Option<Word> {
|
||||||
|
|
Loading…
Add table
Reference in a new issue