diff --git a/Cargo.lock b/Cargo.lock index 5402b55..84ab556 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1323,23 +1323,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dilithium-crypto" -version = "0.1.0" -source = "git+https://github.com/Quantus-Network/chain?tag=v0.1.6#98ceb8de72a8eea0a5d1db2da4793c3331ef07c1" -dependencies = [ - "log", - "parity-scale-codec", - "poseidon-resonance 0.9.0 (git+https://github.com/Quantus-Network/poseidon-resonance)", - "rusty-crystals-dilithium", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-runtime-interface 29.0.1", - "thiserror 1.0.69", -] - [[package]] name = "dirs" version = "6.0.0" @@ -1405,6 +1388,27 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "dyn-clonable" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a36efbb9bfd58e1723780aa04b61aba95ace6a05d9ffabfdb0b43672552f0805" +dependencies = [ + "dyn-clonable-impl", + "dyn-clone", +] + +[[package]] +name = "dyn-clonable-impl" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8671d54058979a37a26f3511fbf8d198ba1aa35ffb202c42587d918d77213a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "dyn-clone" version = "1.0.20" @@ -1821,17 +1825,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.16" @@ -2165,7 +2158,7 @@ dependencies = [ "hyper", "libc", "pin-project-lite", - "socket2", + "socket2 0.6.0", "tokio", "tower-service", "tracing", @@ -2751,11 +2744,11 @@ dependencies = [ [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] [[package]] @@ -2764,6 +2757,15 @@ version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +[[package]] +name = "memory-db" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" +dependencies = [ + "hash-db", +] + [[package]] name = "memory-db" version = "0.34.0" @@ -2855,12 +2857,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" dependencies = [ - "overload", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -3004,10 +3005,120 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] -name = "overload" -version = "0.1.1" +name = "p3-dft" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b2764a3982d22d62aa933c8de6f9d71d8a474c9110b69e675dea1887bdeffc" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "tracing", +] + +[[package]] +name = "p3-field" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc13a73509fe09c67b339951ca8d4cc6e61c9bf08c130dbc90dda52452918cc2" +dependencies = [ + "itertools 0.14.0", + "num-bigint", + "p3-maybe-rayon", + "p3-util", + "paste", + "rand 0.9.2", + "serde", + "tracing", +] + +[[package]] +name = "p3-goldilocks" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552849f6309ffde34af0d31aa9a2d0a549cb0ec138d9792bfbf4a17800742362" +dependencies = [ + "num-bigint", + "p3-dft", + "p3-field", + "p3-mds", + "p3-poseidon2", + "p3-symmetric", + "p3-util", + "paste", + "rand 0.9.2", + "serde", +] + +[[package]] +name = "p3-matrix" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e1e9f69c2fe15768b3ceb2915edb88c47398aa22c485d8163deab2a47fe194" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "p3-maybe-rayon", + "p3-util", + "rand 0.9.2", + "serde", + "tracing", + "transpose", +] + +[[package]] +name = "p3-maybe-rayon" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33f765046b763d046728b3246b690f81dfa7ccd7523b7a1582c74f616fbce6a0" + +[[package]] +name = "p3-mds" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c90541c6056712daf2ee69ec328db8b5605ae8dbafe60226c8eb75eaac0e1f9" +dependencies = [ + "p3-dft", + "p3-field", + "p3-symmetric", + "p3-util", + "rand 0.9.2", +] + +[[package]] +name = "p3-poseidon2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88e9f053f120a78ad27e9c1991a0ea547777328ca24025c42364d6ee2667d59a" +dependencies = [ + "p3-field", + "p3-mds", + "p3-symmetric", + "p3-util", + "rand 0.9.2", +] + +[[package]] +name = "p3-symmetric" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d5db8f05a26d706dfd8aaf7aa4272ca4f3e7a075db897ec7108f24fad78759" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "serde", +] + +[[package]] +name = "p3-util" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +checksum = "6dfee67245d9ce78a15176728da2280032f0a84b5819a39a953e7ec03cfd9bd7" +dependencies = [ + "serde", +] [[package]] name = "parity-bip39" @@ -3175,51 +3286,17 @@ dependencies = [ "spki", ] -[[package]] -name = "plonky2" -version = "1.0.2" -source = "git+https://github.com/Quantus-Network/plonky2#9c06fe53083dd1672951b6d5bc3aa03bde4d1a66" -dependencies = [ - "ahash", - "anyhow", - "hashbrown 0.14.5", - "itertools 0.11.0", - "keccak-hash 0.8.0", - "log", - "num", - "plonky2_field", - "plonky2_maybe_rayon", - "plonky2_util", - "rand 0.8.5", - "serde", - "static_assertions", - "unroll", -] - -[[package]] -name = "plonky2_field" -version = "1.0.0" -source = "git+https://github.com/Quantus-Network/plonky2#9c06fe53083dd1672951b6d5bc3aa03bde4d1a66" -dependencies = [ - "anyhow", - "itertools 0.11.0", - "num", - "plonky2_util", - "rustc_version", - "serde", - "static_assertions", - "unroll", -] - [[package]] name = "plonky2_maybe_rayon" version = "1.0.0" -source = "git+https://github.com/Quantus-Network/plonky2#9c06fe53083dd1672951b6d5bc3aa03bde4d1a66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1e554181dc95243b8d9948ae7bae5759c7fb2502fed28f671f95ef38079406" [[package]] name = "plonky2_util" version = "1.0.0" -source = "git+https://github.com/Quantus-Network/plonky2#9c06fe53083dd1672951b6d5bc3aa03bde4d1a66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c32c137808ca984ab2458b612b7eb0462d853ee041a3136e83d54b96074c7610" [[package]] name = "polkavm-common" @@ -3332,38 +3409,6 @@ dependencies = [ "universal-hash", ] -[[package]] -name = "poseidon-resonance" -version = "0.9.0" -source = "git+https://github.com/Quantus-Network/poseidon-resonance?branch=main#c37290f40cd821970c00844663cf22c7408457b7" -dependencies = [ - "log", - "parity-scale-codec", - "plonky2", - "scale-info", - "serde", - "sp-core", - "sp-runtime", - "sp-storage", - "sp-trie", -] - -[[package]] -name = "poseidon-resonance" -version = "0.9.0" -source = "git+https://github.com/Quantus-Network/poseidon-resonance#c37290f40cd821970c00844663cf22c7408457b7" -dependencies = [ - "log", - "parity-scale-codec", - "plonky2", - "scale-info", - "serde", - "sp-core", - "sp-runtime", - "sp-storage", - "sp-trie", -] - [[package]] name = "potential_utf" version = "0.1.3" @@ -3476,6 +3521,145 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "qp-dilithium-crypto" +version = "0.1.3" +source = "git+https://github.com/Quantus-Network/chain#9622d50aa583def6f3ae77415ad08c94d0b8ccf8" +dependencies = [ + "log", + "parity-scale-codec", + "qp-poseidon", + "qp-rusty-crystals-dilithium", + "qp-rusty-crystals-hdwallet", + "scale-info", + "sp-core 37.0.0", + "sp-io", + "sp-runtime", + "thiserror 1.0.69", +] + +[[package]] +name = "qp-plonky2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "068162bd3730e381744eca219f98f0db22874afc8fa24631611e975c93d6cf68" +dependencies = [ + "ahash", + "anyhow", + "hashbrown 0.14.5", + "itertools 0.11.0", + "keccak-hash 0.8.0", + "log", + "num", + "plonky2_maybe_rayon", + "plonky2_util", + "qp-plonky2-field", + "rand 0.8.5", + "serde", + "static_assertions", + "unroll", +] + +[[package]] +name = "qp-plonky2-field" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b2f7e0854f9e14aa9b3f6d544ccf71087e751d6adbe6a7e50d2c75fb561d97" +dependencies = [ + "anyhow", + "itertools 0.11.0", + "num", + "plonky2_util", + "rustc_version", + "serde", + "static_assertions", + "unroll", +] + +[[package]] +name = "qp-poseidon" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33181134496120c212a0a2098215cf45a68d50fe656de6fb30b31e73babe9383" +dependencies = [ + "log", + "p3-field", + "p3-goldilocks", + "parity-scale-codec", + "qp-poseidon-core", + "scale-info", + "serde", + "sp-core 37.0.0", + "sp-runtime", + "sp-storage", + "sp-trie 40.0.0", +] + +[[package]] +name = "qp-poseidon-core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec326fc2631a929de09a38af2613a3db5230882c12a2f68205693ec632751e8b" +dependencies = [ + "p3-field", + "p3-goldilocks", + "p3-poseidon2", + "p3-symmetric", + "rand 0.9.2", + "rand_chacha 0.9.0", +] + +[[package]] +name = "qp-rusty-crystals-dilithium" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e13b64304720dcfa52557209fddc1ac67b08fcaff31c132099fd37b551dfb1" +dependencies = [ + "sha2 0.10.9", +] + +[[package]] +name = "qp-rusty-crystals-hdwallet" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e86ee06c92d0629f6e6c45972f046069a68742577a796b17f5a507457be493d" +dependencies = [ + "bip39", + "hex", + "hex-literal", + "nam-tiny-hderive", + "qp-poseidon-core", + "qp-rusty-crystals-dilithium", + "rand_chacha 0.9.0", + "rand_core 0.9.3", + "serde", + "serde_json", + "thiserror 2.0.16", +] + +[[package]] +name = "qp-wormhole-circuit" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea70a3a1bf545450cb6320782389cfbfe58fcf0ecf724aa666383807bf5548b" +dependencies = [ + "anyhow", + "hex", + "qp-plonky2", + "qp-zk-circuits-common", +] + +[[package]] +name = "qp-zk-circuits-common" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a2a62915be0513d045f4d92829c0a26d6f3e8897353e8f8356a7eabef025329" +dependencies = [ + "anyhow", + "qp-plonky2", + "serde", +] + [[package]] name = "quantus-cli" version = "0.1.0" @@ -3485,27 +3669,34 @@ dependencies = [ "chrono", "clap", "colored", - "dilithium-crypto", "dirs", "hex", "jsonrpsee", "parity-scale-codec", - "poseidon-resonance 0.9.0 (git+https://github.com/Quantus-Network/poseidon-resonance?branch=main)", + "qp-dilithium-crypto", + "qp-poseidon", + "qp-poseidon-core", + "qp-rusty-crystals-dilithium", + "qp-rusty-crystals-hdwallet", + "qp-wormhole-circuit", + "qp-zk-circuits-common", "rand 0.9.2", "rpassword", - "rusty-crystals-dilithium", - "rusty-crystals-hdwallet", "serde", "serde_json", "sha2 0.10.9", - "sp-core", + "sp-core 37.0.0", "sp-runtime", + "sp-state-machine 0.44.0", + "sp-storage", + "sp-trie 38.0.0", "subxt", "subxt-metadata", "tempfile", "thiserror 2.0.16", "tokio", "toml 0.9.5", + "trie-db 0.29.1", ] [[package]] @@ -3529,19 +3720,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - [[package]] name = "rand" version = "0.8.5" @@ -3563,16 +3741,6 @@ dependencies = [ "rand_core 0.9.3", ] -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - [[package]] name = "rand_chacha" version = "0.3.1" @@ -3593,15 +3761,6 @@ dependencies = [ "rand_core 0.9.3", ] -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - [[package]] name = "rand_core" version = "0.6.4" @@ -3620,15 +3779,6 @@ dependencies = [ "getrandom 0.3.3", ] -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - [[package]] name = "redox_syscall" version = "0.5.17" @@ -3677,17 +3827,8 @@ checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.10", - "regex-syntax 0.8.6", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] @@ -3698,15 +3839,9 @@ checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.6", + "regex-syntax", ] -[[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.8.6" @@ -3878,37 +4013,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "rusty-crystals-dilithium" -version = "1.0.0" -source = "git+https://github.com/Quantus-Network/rusty-crystals.git#e78049a3d391446d8366b12db733ecaf5acd78eb" -dependencies = [ - "rand 0.7.3", - "sha2 0.10.9", -] - -[[package]] -name = "rusty-crystals-hdwallet" -version = "0.1.1" -source = "git+https://github.com/Quantus-Network/rusty-crystals.git#e78049a3d391446d8366b12db733ecaf5acd78eb" -dependencies = [ - "bip39", - "hex", - "hex-literal", - "hmac 0.12.1", - "nam-tiny-hderive", - "poseidon-resonance 0.9.0 (git+https://github.com/Quantus-Network/poseidon-resonance)", - "rand 0.8.5", - "rand_chacha 0.9.0", - "rand_core 0.6.4", - "rusty-crystals-dilithium", - "serde", - "serde_json", - "sha2 0.10.9", - "sp-core", - "thiserror 2.0.16", -] - [[package]] name = "ruzstd" version = "0.8.1" @@ -4466,6 +4570,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.0" @@ -4500,7 +4614,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", + "sp-core 37.0.0", "sp-io", ] @@ -4519,6 +4633,53 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "sp-core" +version = "35.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4532774405a712a366a98080cbb4daa28c38ddff0ec595902ad6ee6a78a809f8" +dependencies = [ + "array-bytes", + "bitflags 1.3.2", + "blake2", + "bounded-collections", + "bs58", + "dyn-clonable", + "ed25519-zebra", + "futures", + "hash-db", + "hash256-std-hasher", + "impl-serde", + "itertools 0.11.0", + "k256", + "libsecp256k1", + "log", + "merlin", + "parity-bip39", + "parity-scale-codec", + "parking_lot", + "paste", + "primitive-types 0.13.1", + "rand 0.8.5", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-crypto-hashing", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface 29.0.1", + "sp-std", + "sp-storage", + "ss58-registry", + "substrate-bip39", + "thiserror 1.0.69", + "tracing", + "w3f-bls", + "zeroize", +] + [[package]] name = "sp-core" version = "37.0.0" @@ -4619,14 +4780,14 @@ dependencies = [ "polkavm-derive 0.24.0", "rustversion", "secp256k1", - "sp-core", + "sp-core 37.0.0", "sp-crypto-hashing", "sp-externalities", "sp-keystore", "sp-runtime-interface 30.0.0", - "sp-state-machine", + "sp-state-machine 0.46.0", "sp-tracing", - "sp-trie", + "sp-trie 40.0.0", "tracing", "tracing-core", ] @@ -4639,7 +4800,7 @@ checksum = "269d0ee360f6d072f9203485afea35583ac151521a525cc48b2a107fc576c2d9" dependencies = [ "parity-scale-codec", "parking_lot", - "sp-core", + "sp-core 37.0.0", "sp-externalities", ] @@ -4674,10 +4835,10 @@ dependencies = [ "simple-mermaid", "sp-application-crypto", "sp-arithmetic", - "sp-core", + "sp-core 37.0.0", "sp-io", "sp-std", - "sp-trie", + "sp-trie 40.0.0", "sp-weights", "tracing", "tuplex", @@ -4751,6 +4912,26 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "sp-state-machine" +version = "0.44.0" +source = "git+https://github.com/Quantus-Network/zk-state-machine#06d1e9545428b57aa56e8997c7bf24eaeb48a103" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "parking_lot", + "rand 0.8.5", + "smallvec", + "sp-core 35.0.0", + "sp-externalities", + "sp-panic-handler", + "sp-trie 38.0.0", + "thiserror 1.0.69", + "tracing", + "trie-db 0.29.1", +] + [[package]] name = "sp-state-machine" version = "0.46.0" @@ -4763,13 +4944,13 @@ dependencies = [ "parking_lot", "rand 0.8.5", "smallvec", - "sp-core", + "sp-core 37.0.0", "sp-externalities", "sp-panic-handler", - "sp-trie", + "sp-trie 40.0.0", "thiserror 1.0.69", "tracing", - "trie-db", + "trie-db 0.30.0", ] [[package]] @@ -4803,6 +4984,29 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "sp-trie" +version = "38.0.0" +source = "git+https://github.com/Quantus-Network/zk-trie#bb1c142115ef1c7cae9378dd438b179d113d29cb" +dependencies = [ + "ahash", + "hash-db", + "log", + "memory-db 0.32.0", + "nohash-hasher", + "parity-scale-codec", + "parking_lot", + "rand 0.8.5", + "scale-info", + "schnellru", + "sp-core 35.0.0", + "sp-externalities", + "thiserror 1.0.69", + "tracing", + "trie-db 0.29.1", + "trie-root", +] + [[package]] name = "sp-trie" version = "40.0.0" @@ -4813,19 +5017,19 @@ dependencies = [ "foldhash", "hash-db", "hashbrown 0.15.5", - "memory-db", + "memory-db 0.34.0", "nohash-hasher", "parity-scale-codec", "parking_lot", "rand 0.8.5", "scale-info", "schnellru", - "sp-core", + "sp-core 37.0.0", "sp-externalities", "substrate-prometheus-endpoint", "thiserror 1.0.69", "tracing", - "trie-db", + "trie-db 0.30.0", "trie-root", ] @@ -4911,6 +5115,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + [[package]] name = "strsim" version = "0.11.1" @@ -5296,9 +5506,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.47.1" +version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" dependencies = [ "backtrace", "bytes", @@ -5309,9 +5519,9 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "slab", - "socket2", + "socket2 0.5.10", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -5513,14 +5723,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "sharded-slab", "smallvec", "thread_local", @@ -5530,6 +5740,28 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + +[[package]] +name = "trie-db" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c992b4f40c234a074d48a757efeabb1a6be88af84c0c23f7ca158950cb0ae7f" +dependencies = [ + "hash-db", + "log", + "rustc-hex", + "smallvec", +] + [[package]] name = "trie-db" version = "0.30.0" @@ -5787,12 +6019,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index bccc64f..f1370b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,46 +17,57 @@ path = "src/main.rs" [dependencies] # CLI and async runtime -clap = { version = "4.5", features = ["derive"] } -tokio = { version = "1.46", features = ["full"] } +clap = { version = "=4.5", features = ["derive"] } +tokio = { version = "=1.46", features = ["full"] } # Serialization and configuration -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -toml = "0.9" +serde = { version = "=1.0", features = ["derive"] } +serde_json = "=1.0" +toml = "=0.9" # Error handling -thiserror = "2.0" +thiserror = "=2.0" # Terminal UI -colored = "3.0" +colored = "=3.0" # Additional utilities -sha2 = "0.10" -hex = "0.4" -chrono = { version = "0.4", features = ["serde"] } -dirs = "6.0" -rpassword = "7.4" +sha2 = "=0.10" +hex = "=0.4" +chrono = { version = "=0.4", features = ["serde"] } +dirs = "=6.0" +rpassword = "=7.4" # Quantum-Safe Encryption -argon2 = "0.5" # Password-based key derivation (quantum-safe) -rand = "0.9" -aes-gcm = "0.10" # AES-256-GCM (quantum-safe with 256-bit keys) +argon2 = "=0.5" # Password-based key derivation (quantum-safe) +rand = "=0.9" +aes-gcm = "=0.10" # AES-256-GCM (quantum-safe with 256-bit keys) -# Quantus crypto dependencies -rusty-crystals-dilithium = { git = "https://github.com/Quantus-Network/rusty-crystals.git", package = "rusty-crystals-dilithium" } -rusty-crystals-hdwallet = { git = "https://github.com/Quantus-Network/rusty-crystals.git", package = "rusty-crystals-hdwallet" } -poseidon-resonance = { git = "https://github.com/Quantus-Network/poseidon-resonance", branch = "main", features = [ + +qp-dilithium-crypto = { git = "https://github.com/Quantus-Network/chain", package = "qp-dilithium-crypto" } +qp-poseidon = { version = "0.9.5", default-features = false, features = [ "serde", ] } -dilithium-crypto = { git = "https://github.com/Quantus-Network/chain", package = "dilithium-crypto", tag = "v0.1.6" } +qp-poseidon-core = { version = "0.9.5", default-features = false } +qp-rusty-crystals-dilithium = { version = "1.0.3", default-features = false } +qp-rusty-crystals-hdwallet = { version = "0.1.4" } + +# Wormhole and zk-circuits +qp-wormhole-circuit = { version = "0.1.2", default-features = false } +qp-zk-circuits-common = { version = "0.1.2", default-features = false, features = [ + "no_random", +] } +trie-db = { version = "=0.29.1", default-features = false } +sp-trie = { git = "https://github.com/Quantus-Network/zk-trie" } # Blockchain and RPC client codec = { package = "parity-scale-codec", version = "3.7", features = [ "derive", ] } sp-core = { version = "37.0.0" } -sp-runtime = { version = "42.0.0" } +sp-runtime = { version = "42.0.0", default-features = false } +sp-storage = { version = "22.0.0", default-features = false } +sp-state-machine = { git = "https://github.com/Quantus-Network/zk-state-machine" } subxt = "0.43.0" jsonrpsee = { version = "0.24", features = ["client"] } diff --git a/src/chain/client.rs b/src/chain/client.rs index bd3c798..e9c13d4 100644 --- a/src/chain/client.rs +++ b/src/chain/client.rs @@ -4,11 +4,17 @@ //! across all CLI modules. use crate::{error::QuantusError, log_verbose}; -use dilithium_crypto::types::DilithiumSignatureScheme; -use jsonrpsee::ws_client::{WsClient, WsClientBuilder}; -use poseidon_resonance::PoseidonHasher; -use sp_core::{crypto::AccountId32, ByteArray}; +use jsonrpsee::{ + core::{client::ClientT, traits::ToRpcParams}, + rpc_params, + ws_client::{WsClient, WsClientBuilder}, +}; +use qp_dilithium_crypto::types::DilithiumSignatureScheme; +use qp_poseidon::PoseidonHasher; +use serde::{Deserialize, Serialize}; +use sp_core::{crypto::AccountId32, ByteArray, Bytes, H256}; use sp_runtime::{traits::IdentifyAccount, MultiAddress}; +use sp_storage::StorageKey; use std::{sync::Arc, time::Duration}; use subxt::{ backend::rpc::RpcClient, @@ -44,6 +50,14 @@ impl Config for ChainConfig { type ExtrinsicParams = DefaultExtrinsicParams; } +// Exact structure from +// https://github.com/paritytech/substrate/blob/master/client/rpc-api/src/state/helpers.rs +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ReadProof { + pub at: Hash, + pub proof: Vec, +} + /// Wrapper around OnlineClient that also stores the node URL and RPC client #[derive(Clone)] pub struct QuantusClient { @@ -152,6 +166,46 @@ impl QuantusClient { Ok(latest_hash) } + pub async fn get_storage_proof_by_keys( + &self, + storage_keys: Vec, + at_block: Option, + ) -> crate::error::Result> { + // Optional: debug the exact JSON we're sending + // use jsonrpsee::core::client::ClientT; + let params: jsonrpsee::core::params::ArrayParams = rpc_params![storage_keys, at_block]; + let params_clone = params.clone().to_rpc_params()?; + println!("Sending RPC request with params: {params_clone:?}"); + + let proof: ReadProof = + self.rpc_client.request("state_getReadProof", params).await.map_err(|e| { + crate::error::QuantusError::NetworkError(format!( + "Failed to fetch storage proof: {e:?}" + )) + })?; + Ok(proof) + } + + pub async fn get_block_header( + &self, + block_hash: H256, + ) -> crate::error::Result> { + log_verbose!("🔍 Fetching block header for block: {:?}", block_hash); + + let header: SubstrateHeader = self + .rpc_client + .request("chain_getHeader", rpc_params![block_hash]) + .await + .map_err(|e| { + crate::error::QuantusError::NetworkError(format!( + "Failed to fetch block header: {e:?}" + )) + })?; + + log_verbose!("📦 Block header: {:?}", header); + Ok(header) + } + /// Get account nonce from the best block (latest) using direct RPC call /// This bypasses SubXT's default behavior of using finalized blocks pub async fn get_account_nonce_from_best_block( @@ -262,12 +316,12 @@ impl QuantusClient { } // Implement subxt::tx::Signer for ResonancePair -impl subxt::tx::Signer for dilithium_crypto::types::DilithiumPair { +impl subxt::tx::Signer for qp_dilithium_crypto::types::DilithiumPair { fn account_id(&self) -> ::AccountId { let resonance_public = - dilithium_crypto::types::DilithiumPublic::from_slice(self.public.as_slice()) + qp_dilithium_crypto::types::DilithiumPublic::from_slice(self.public.as_slice()) .expect("Invalid public key"); - ::into_account( + ::into_account( resonance_public, ) } @@ -277,7 +331,10 @@ impl subxt::tx::Signer for dilithium_crypto::types::DilithiumPair { // sp_core::Pair::sign returns ResonanceSignatureWithPublic, which we need to wrap in // ResonanceSignatureScheme let signature_with_public = - ::sign(self, signer_payload); - dilithium_crypto::types::DilithiumSignatureScheme::Dilithium(signature_with_public) + ::sign( + self, + signer_payload, + ); + qp_dilithium_crypto::types::DilithiumSignatureScheme::Dilithium(signature_with_public) } } diff --git a/src/cli/block.rs b/src/cli/block.rs index 0da228a..955c260 100644 --- a/src/cli/block.rs +++ b/src/cli/block.rs @@ -5,7 +5,7 @@ use crate::{ }; use clap::Subcommand; use colored::Colorize; -use poseidon_resonance::PoseidonHasher; +use qp_poseidon::PoseidonHasher; use sp_core::crypto::Ss58Codec; use std::str::FromStr; use subxt::events::EventDetails; diff --git a/src/cli/mod.rs b/src/cli/mod.rs index e21597b..04e2e8c 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -17,6 +17,7 @@ pub mod storage; pub mod system; pub mod tech_collective; pub mod wallet; +pub mod wormhole; /// Main CLI commands #[derive(Subcommand, Debug)] @@ -76,6 +77,10 @@ pub enum Commands { #[command(subcommand)] TechCollective(tech_collective::TechCollectiveCommands), + /// Wormhole commands + #[command(subcommand)] + Wormhole(wormhole::WormholeCommands), + /// Runtime management commands (requires root/sudo permissions) #[command(subcommand)] Runtime(runtime::RuntimeCommands), @@ -235,6 +240,8 @@ pub async fn execute_command( storage::handle_storage_command(storage_cmd, node_url).await, Commands::TechCollective(tech_collective_cmd) => tech_collective::handle_tech_collective_command(tech_collective_cmd, node_url).await, + Commands::Wormhole(wormhole_cmd) => + wormhole::handle_wormhole_command(wormhole_cmd, node_url).await, Commands::Runtime(runtime_cmd) => runtime::handle_runtime_command(runtime_cmd, node_url).await, Commands::Call { diff --git a/src/cli/progress_spinner.rs b/src/cli/progress_spinner.rs index d1c05e3..25baf58 100644 --- a/src/cli/progress_spinner.rs +++ b/src/cli/progress_spinner.rs @@ -41,9 +41,16 @@ pub async fn wait_for_tx_confirmation( ) -> Result { // Use ProgressSpinner to show waiting progress let mut spinner = ProgressSpinner::new(); + // let mut blocks_sub = client.blocks().subscribe_finalized().await?; + + // while let Some(bloock) = blocks_sub.next().await { + // log_verbose!("New finalized block received"); + // spinner.tick(); + // break; + // } // For now, we use a simple delay approach similar to substrate-api-client - for _ in 0..10 { + for _ in 0..30 { spinner.tick(); tokio::time::sleep(std::time::Duration::from_secs(1)).await; } @@ -52,6 +59,6 @@ pub async fn wait_for_tx_confirmation( println!(); use crate::log_verbose; - log_verbose!("✅ Transaction likely finalized (after 6s delay)"); + log_verbose!("✅ Transaction likely finalized (after 30s delay)"); Ok(true) } diff --git a/src/cli/storage.rs b/src/cli/storage.rs index 3791191..299269a 100644 --- a/src/cli/storage.rs +++ b/src/cli/storage.rs @@ -205,7 +205,16 @@ pub async fn get_storage_raw( // Get the latest block hash to read from the latest state (not finalized) let latest_block_hash = quantus_client.get_latest_block().await?; - let storage_at = quantus_client.client().storage().at(latest_block_hash); + get_storage_at_block_raw(quantus_client, key, latest_block_hash).await +} + +/// Get raw storage value by key +pub async fn get_storage_at_block_raw( + quantus_client: &crate::chain::client::QuantusClient, + key: Vec, + block_hash: subxt::utils::H256, +) -> crate::error::Result>> { + let storage_at = quantus_client.client().storage().at(block_hash); let result = storage_at.fetch_raw(key).await?; diff --git a/src/cli/wormhole.rs b/src/cli/wormhole.rs new file mode 100644 index 0000000..627cb3a --- /dev/null +++ b/src/cli/wormhole.rs @@ -0,0 +1,548 @@ +use crate::{ + chain::{client::QuantusClient, quantus_subxt}, + cli::{ + progress_spinner::wait_for_tx_confirmation, + send::{ + format_balance_with_symbol, get_balance, get_chain_properties, + parse_amount_with_decimals, validate_and_format_amount, + }, + storage::{get_storage_at_block_raw, get_storage_raw}, + }, + error::Result, + log_info, log_print, log_success, log_verbose, +}; +use clap::Subcommand; +use codec::{Decode, Encode}; +use colored::Colorize; +use hex; +use qp_dilithium_crypto::traits::WormholeAddress; +use qp_poseidon::PoseidonHasher; +use qp_rusty_crystals_hdwallet::wormhole::WormholePair; +use rand::Rng; +use serde::Serialize; +use sp_core::{ + crypto::{AccountId32, Ss58Codec}, + twox_128, Bytes, Hasher, +}; + +use sp_runtime::traits::IdentifyAccount; +use sp_state_machine::read_proof_check; +use sp_storage::StorageKey; +use sp_trie::StorageProof; +use trie_db::{ + node::{Node, NodeHandle}, + NodeCodec, TrieLayout, +}; + +#[derive(Debug, Serialize)] +struct TransferProofBundle { + transfer_count: u64, + state_root: String, // hex (no 0x) + storage_proof: Vec, // hex-encoded nodes (no 0x) + indices: Vec, // byte offsets in hex-string space, for node hashes +} + +/// Wormhole commands +#[derive(Subcommand, Debug)] +pub enum WormholeCommands { + /// Generate a new wormhole address and secret + GenerateAddress, + + /// Spend funds from a wormhole address + Spend { + /// The hex-encoded secret key for the wormhole address + #[arg(long)] + secret: String, + + /// Recipient's on-chain address + #[arg(short, long)] + to: String, + + /// Amount to send (e.g., "10", "10.5", "0.0001") + #[arg(short, long)] + amount: String, + + /// Wallet name to sign the bridge transaction + #[arg(short, long)] + from: String, + + /// Password for the wallet + #[arg(short, long)] + password: Option, + + /// Read password from file + #[arg(long)] + password_file: Option, + }, + /// Generate transfer proof data (for testing purposes) + GenerateProof { + /// The hex-encoded secret key for the wormhole address + #[arg(long)] + secret: String, + + /// Amount to send (e.g., "10", "10.5", "0.0001") + #[arg(short, long)] + amount: String, + + /// Wallet name to sign the bridge transaction + #[arg(short, long)] + from_wallet: String, + + /// Password for the wallet + #[arg(short, long)] + password: Option, + + /// Read password from file + #[arg(long)] + password_file: Option, + + /// Optional tip amount to prioritize the transaction (e.g., "1", "0.5") + #[arg(long)] + tip: Option, + }, +} + +/// Handle wormhole commands +pub async fn handle_wormhole_command(command: WormholeCommands, node_url: &str) -> Result<()> { + match command { + WormholeCommands::GenerateAddress => { + log_print!("Generating new wormhole address..."); + + let mut seed = [0u8; 32]; + rand::rng().fill(&mut seed); // TODO replace this with a source of randomness with a higher source of entropy + + let wormhole_pair = WormholePair::generate_new(seed).map_err(|e| { + crate::error::QuantusError::Generic(format!("Wormhole generation error: {e:?}")) + })?; + + // The on-chain address for funding MUST be the unspendable account derived + // from the secret key. The ZK proof verifies transfers to this address. + let wormhole_address = WormholeAddress(sp_core::H256(wormhole_pair.address)); + let unspendable_account: AccountId32 = wormhole_address.into_account(); + + log_print!("{}", "XXXXXXXXXXXXXXX Quantus Wormhole Details XXXXXXXXXXXXXXXXX".yellow()); + log_print!( + "{}: {}", + "Wormhole Address account ID".green(), + unspendable_account.to_ss58check().bright_cyan() + ); + log_print!( + "{}: 0x{}", + "Wormhole Address".green(), + hex::encode(wormhole_pair.address).bright_cyan() + ); + log_print!( + "{}: 0x{}", + "Secret Key ".green(), + hex::encode(wormhole_pair.secret).bright_cyan() + ); + log_print!( + "{}", + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".yellow() + ); + + log_success!("Wormhole address generated successfully!"); + }, + WormholeCommands::Spend { + secret: _, + to: _, + amount: _, + from: _, + password: _, + password_file: _, + } => { + log_print!("🚀 Initiating wormhole spend..."); + }, + WormholeCommands::GenerateProof { + secret, + amount, + from_wallet, + password, + password_file, + tip, + } => { + // Create quantus chain client + let quantus_client = QuantusClient::new(node_url).await?; + // Parse and validate the amount + let (amount, _) = validate_and_format_amount(&quantus_client, &amount).await?; + // Get password securely for decryption + log_verbose!("📦 Using wallet: {}", from_wallet.bright_blue().bold()); + let keypair = + crate::wallet::load_keypair_from_wallet(&from_wallet, password, password_file)?; + let from = keypair.to_account_id_32(); + // log the funding account as a bytes array + log_print!( + "Funding account address (bytes): {:?}", + >::as_ref(&from) + ); + + // Get account information + let from_account_id = keypair.to_account_id_ss58check(); + let balance = get_balance(&quantus_client, &from_account_id).await?; + + // Get formatted balance with proper decimals + let formatted_balance = format_balance_with_symbol(&quantus_client, balance).await?; + log_verbose!("💰 Current balance: {}", formatted_balance.bright_yellow()); + + if balance < amount { + return Err(crate::error::QuantusError::InsufficientBalance { + available: balance, + required: amount, + }); + } + log_print!("Generating new transfer proof..."); + let secret_bytes: [u8; 32] = hex::decode(secret.trim_start_matches("0x")) + .map_err(|e| { + crate::error::QuantusError::Generic(format!("Hex decode error: {e:?}")) + })? + .try_into() + .map_err(|_| { + crate::error::QuantusError::Generic("Secret must be 32 bytes".to_string()) + })?; + let wormhole_pair = WormholePair::generate_pair_from_secret(&secret_bytes); + let wormhole_address = WormholeAddress(sp_core::H256(wormhole_pair.address)); + let unspendable_account = wormhole_address.into_account(); + log_print!( + "Derived unspendable account address (bytes): {:?}", + >::as_ref(&unspendable_account) + ); + let unspendable_account_id_bytes: [u8; 32] = *unspendable_account.as_ref(); + let unspendable_account_subxt = + subxt::ext::subxt_core::utils::AccountId32::from(unspendable_account_id_bytes); + // Submit transaction + // Create the transfer call using static API from quantus_subxt + let transfer_call = quantus_subxt::api::tx().balances().transfer_allow_death( + subxt::ext::subxt_core::utils::MultiAddress::Id(unspendable_account_subxt.clone()), + amount, + ); + + // Parse tip amount if provided + let tip_amount = if let Some(tip_str) = &tip { + // Get chain properties for proper decimal parsing + let (_, decimals) = get_chain_properties(&quantus_client).await?; + parse_amount_with_decimals(tip_str, decimals).ok() + } else { + None + }; + + let tip_to_use = tip_amount.unwrap_or(10_000_000_000); // Use default 10 DEV + + // Construct the storage key to fetch the transfer count + let pallet = "Balances".to_string(); + let name = "TransferCount".to_string(); + let mut storage_key = twox_128(pallet.as_bytes()).to_vec(); + storage_key.extend(&twox_128(name.as_bytes())); + + // Construct the storage key to fetch the transfer count + let latest_block_hash = quantus_client.get_latest_block().await?; + let transfer_count_previous = + get_transfer_count(&quantus_client, &storage_key, &latest_block_hash).await?; + + // Submit the transaction + let hash = crate::cli::common::submit_transaction( + &quantus_client, + &keypair, + transfer_call, + Some(tip_to_use), + ) + .await?; + log_verbose!("Transaction submitted with hash: 0x{}", hex::encode(hash)); + wait_for_tx_confirmation(quantus_client.client(), hash).await?; + let tx_block_hash = quantus_client.get_latest_block().await?; + log_info!("✅ Transaction confirmed and finalized on chain at block {}", tx_block_hash); + let transfer_count = + get_transfer_count(&quantus_client, &storage_key, &tx_block_hash).await?; + log_verbose!("Transfer count range: {} - {}", transfer_count_previous, transfer_count); + + let mut correct_storage_key = Vec::::new(); + let mut correct_leaf_hash = [0u8; 32]; + let mut matched_count: Option = None; + + let pallet_prefix = twox_128("Balances".as_bytes()); + let storage_prefix = twox_128("TransferProof".as_bytes()); + let storage_key_prefix = [&pallet_prefix[..], &storage_prefix[..]].concat(); + + for count in transfer_count_previous..transfer_count { + let computed_leaf_hash = + compute_transfer_proof_leaf(count, &from, &unspendable_account, amount); + let storage_key = [&storage_key_prefix[..], computed_leaf_hash.as_ref()].concat(); + let result = get_storage_raw(&quantus_client, storage_key.to_vec()).await?; + if result.is_some() { + correct_storage_key = storage_key; + correct_leaf_hash = computed_leaf_hash; + matched_count = Some(count); + log_success!( + "🍀 Found matching storage key: {:?} for transfer proof with leaf hash: {:?} for transfer count {:?}", + hex::encode(&correct_storage_key), + hex::encode(correct_leaf_hash), + count + ); + break; + }; + } + if correct_storage_key.is_empty() || correct_leaf_hash == [0u8; 32] { + return Err(crate::error::QuantusError::Generic( + "🍀 No matching storage key / leaf hash found for transfer proof".to_string(), + )); + } + let proof = quantus_client + .get_storage_proof_by_keys( + vec![StorageKey(correct_storage_key.clone())], + Some(tx_block_hash), + ) + .await?; + let proof_as_u8: Vec> = + proof.proof.iter().map(|bytes: &Bytes| bytes.0.clone()).collect(); + let (leaf_checked, last_idx) = + check_leaf(&correct_leaf_hash, proof_as_u8[proof_as_u8.len() - 1].clone()); + let check_string = if leaf_checked { "✅" } else { "⚔️" }; + log_verbose!("🍀 Leaf check: {check_string}"); + tree_structure_check(&proof_as_u8)?; + let header = quantus_client.get_block_header(tx_block_hash).await?; + // encode the header + let encoded_header = header.encode(); + // log the encoded header as hex + log_verbose!("Encoded header: 0x{}", hex::encode(&encoded_header)); + let state_root = header.state_root; + + // Build the vectors used in-circuit and capture indices + let (storage_proof, indices) = + prepare_proof_for_circuit(proof_as_u8.clone(), hex::encode(state_root), last_idx); + + // Verify the proof against the runtime (safety) + let expected_value = ().encode(); + let items = vec![correct_storage_key.clone()]; + let storage_proof_typed = StorageProof::new(proof_as_u8.clone()); + let result = read_proof_check::>>( + state_root, + storage_proof_typed, + &items, + ); + + match result { + Ok(map) => match map.get(&correct_storage_key) { + Some(Some(value)) if value == &expected_value => { + log_print!( + "Proof verified for key {:?}", + hex::encode(&correct_storage_key) + ); + let data = TransferProofBundle { + transfer_count: matched_count.unwrap(), + state_root: hex::encode(state_root), + storage_proof, + indices, + }; + // pretty print the TransferProofBundle + log_print!("🍀 TransferProofBundle: {:?}", data); + }, + other => + return Err(crate::error::QuantusError::Generic(format!( + "Unexpected proof map result: {other:?}" + ))), + }, + Err(e) => + return Err(crate::error::QuantusError::Generic(format!( + "Failed to check proof: {e:?}" + ))), + } + }, + } + Ok(()) +} + +async fn get_transfer_count( + quantus_client: &QuantusClient, + storage_key: &[u8], + block_hash: &subxt::utils::H256, +) -> Result { + let result = + get_storage_at_block_raw(quantus_client, storage_key.to_vec(), *block_hash).await?; + let value_bytes = result.ok_or_else(|| { + crate::error::QuantusError::Generic("TransferCount not found in storage.".to_string()) + })?; + let transfer_count = u64::decode(&mut &value_bytes[..]).unwrap(); + Ok(transfer_count) +} + +pub fn compute_transfer_proof_leaf( + tx_count: u64, + from: &AccountId32, + to: &AccountId32, + amount: u128, +) -> [u8; 32] { + // Step 1: Encode the key components into a single byte vector + let mut key_bytes = Vec::new(); + key_bytes.extend_from_slice(&tx_count.encode()); + key_bytes.extend_from_slice(&from.encode()); + key_bytes.extend_from_slice(&to.encode()); + key_bytes.extend_from_slice(&amount.encode()); + // Step 2: Hash the concatenated bytes using PoseidonHasher + PoseidonHasher::hash_storage::(&key_bytes) +} + +// Function to check that the 24 byte suffix of the leaf hash is the last [-32, -8] bytes of the +// leaf node +pub fn check_leaf(leaf_hash: &[u8; 32], leaf_node: Vec) -> (bool, usize) { + let hash_suffix = &leaf_hash[8..32]; + let mut last_idx = 0usize; + let mut found = false; + + for i in 0..=leaf_node.len().saturating_sub(hash_suffix.len()) { + if &leaf_node[i..i + hash_suffix.len()] == hash_suffix { + last_idx = i; + found = true; + break; + } + } + + log_verbose!( + "Checking leaf hash suffix: {:?} in leaf_node at index: {:?}", + hex::encode(hash_suffix), + last_idx + ); + log_verbose!("leaf_node: {:?}", hex::encode(leaf_node.clone())); + + (found, (last_idx * 2).saturating_sub(16)) +} + +pub fn tree_structure_check(proof: &[Vec]) -> Result<()> { + for (i, node_data) in proof.iter().enumerate() { + let node_hash = ::hash(node_data); + match as TrieLayout>::Codec::decode(node_data) { + Ok(node) => match &node { + Node::Empty => log_verbose!("Proof node {}: Empty", i), + Node::Leaf(partial, value) => { + let nibbles: Vec = partial.right_iter().collect(); + log_verbose!( + "Proof node {}: Leaf, partial: {:?}, value: {:?} hash: {:?} bytes: {:?}", + i, + hex::encode(&nibbles), + value, + node_hash, + hex::encode(node_data) + ); + }, + Node::Extension(partial, _) => { + let nibbles: Vec = partial.right_iter().collect(); + log_verbose!( + "Proof node {}: Extension, partial: {:?} hash: {:?} bytes: {:?}", + i, + hex::encode(&nibbles), + node_hash, + hex::encode(node_data) + ); + }, + Node::Branch(children, value) => { + log_verbose!( + "Proof node {}: Branch, value: {:?} hash: {:?} bytes: {:?}", + i, + value, + node_hash, + hex::encode(node_data) + ); + for (j, child) in children.iter().enumerate() { + if let Some(child) = child { + log_verbose!(" Child {}: {:?}", j, child); + } + } + }, + Node::NibbledBranch(partial, children, value) => { + let nibbles: Vec = partial.right_iter().collect(); + let children = children + .iter() + .filter_map(|x| { + x.as_ref().map(|val| match val { + NodeHandle::Hash(h) => hex::encode(h), + NodeHandle::Inline(i) => hex::encode(i), + }) + }) + .collect::>(); + log_verbose!( + "Proof node {}: NibbledBranch, partial: {:?}, value: {:?}, children: {:?} hash: {:?} bytes: {:?}", + i, + hex::encode(&nibbles), + value, + children, + node_hash, + hex::encode(node_data) + ); + }, + }, + Err(e) => log_verbose!("Failed to decode proof node {}: {:?}", i, e), + } + } + Ok(()) +} + +fn prepare_proof_for_circuit( + proof: Vec>, + state_root: String, + last_idx: usize, +) -> (Vec, Vec) { + let mut hashes = Vec::::new(); + let mut bytes = Vec::::new(); + let mut parts = Vec::<(String, String)>::new(); + let mut storage_proof = Vec::::new(); + for node_data in proof.iter() { + let hash = hex::encode(::hash(node_data)); + let node_bytes = hex::encode(node_data); + if hash == state_root { + storage_proof.push(node_bytes); + } else { + // don't put the hash in if it is the root + hashes.push(hash); + bytes.push(node_bytes.clone()); + } + } + + log_verbose!("Finished constructing bytes and hashes vectors {:?} {:?}", bytes, hashes); + + let mut ordered_hashes = Vec::::new(); + let mut indices = Vec::::new(); + + while !hashes.is_empty() { + for i in (0..hashes.len()).rev() { + let hash = hashes[i].clone(); + if let Some(last) = storage_proof.last() { + if let Some(index) = last.find(&hash) { + let (left, right) = last.split_at(index); + indices.push(index); + parts.push((left.to_string(), right.to_string())); + storage_proof.push(bytes[i].clone()); + ordered_hashes.push(hash.clone()); + hashes.remove(i); + bytes.remove(i); + } + } + } + } + indices.push(last_idx); + + // iterate through the storage proof, printing the size of each. + for (i, node) in storage_proof.iter().enumerate() { + println!("Storage proof node {}: {} bytes", i, (node.len() / 16)); + } + + log_verbose!( + "Storage proof generated: {:?} {:?} {:?} {:?}", + &storage_proof, + parts, + ordered_hashes, + indices + ); + + for (i, _) in storage_proof.iter().enumerate() { + if i == parts.len() { + break; + } + let part = parts[i].clone(); + let hash = ordered_hashes[i].clone(); + if part.1[..64] != hash { + panic!("storage proof index incorrect {:?} != {:?}", part.1, hash); + } else { + log_verbose!("storage proof index correct: {:?}", part.0.len()); + } + } + + (storage_proof, indices) +} diff --git a/src/lib.rs b/src/lib.rs index 0b000a6..95389b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ pub use error::{QuantusError as Error, Result}; pub use chain::client::{ChainConfig, QuantusClient}; // Re-export dilithium crypto -pub use dilithium_crypto; +pub use qp_dilithium_crypto; // Re-export commonly used types from sp_core and sp_runtime pub use sp_core::crypto::AccountId32; diff --git a/src/wallet/keystore.rs b/src/wallet/keystore.rs index c585078..591a6c1 100644 --- a/src/wallet/keystore.rs +++ b/src/wallet/keystore.rs @@ -5,7 +5,7 @@ /// - Loading and decrypting wallet data with post-quantum cryptography /// - Managing wallet files on disk with quantum-resistant security use crate::error::{Result, WalletError}; -use rusty_crystals_dilithium::ml_dsa_87::{Keypair, PublicKey, SecretKey}; +use qp_rusty_crystals_dilithium::ml_dsa_87::{Keypair, PublicKey, SecretKey}; use serde::{Deserialize, Serialize}; use sp_core::{ crypto::{AccountId32, Ss58Codec}, @@ -22,7 +22,7 @@ use rand::{rng, RngCore}; use std::path::Path; -use dilithium_crypto::types::{DilithiumPair, DilithiumPublic}; +use qp_dilithium_crypto::types::{DilithiumPair, DilithiumPublic}; use sp_runtime::traits::IdentifyAccount; /// Quantum-safe key pair using Dilithium post-quantum signatures @@ -84,7 +84,7 @@ impl QuantumKeyPair { } /// Convert to subxt Signer for use - pub fn to_subxt_signer(&self) -> Result { + pub fn to_subxt_signer(&self) -> Result { // Convert to DilithiumPair first - now it implements subxt::tx::Signer let resonance_pair = self.to_resonance_pair()?; diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 011ae84..6d61541 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -10,7 +10,8 @@ pub mod password; use crate::error::{Result, WalletError}; pub use keystore::{Keystore, QuantumKeyPair, WalletData}; -use rusty_crystals_hdwallet::{generate_mnemonic, HDLattice}; +use qp_rusty_crystals_hdwallet::{generate_mnemonic, HDLattice}; +use rand::Rng; use serde::{Deserialize, Serialize}; use sp_core::crypto::Ss58Codec; use sp_runtime::traits::IdentifyAccount; @@ -49,9 +50,11 @@ impl WalletManager { if keystore.load_wallet(name)?.is_some() { return Err(WalletError::AlreadyExists.into()); } + let mut seed = [0u8; 32]; + rand::rng().fill(&mut seed); // TODO replace this with a source of randomness with a higher source of entropy // Generate a new Dilithium keypair' - let mnemonic = generate_mnemonic(24).map_err(|_| WalletError::KeyGeneration)?; + let mnemonic = generate_mnemonic(24, seed).map_err(|_| WalletError::KeyGeneration)?; let lattice = HDLattice::from_mnemonic(&mnemonic, None).expect("Failed to generate lattice"); let dilithium_keypair = lattice.generate_keys(); @@ -92,9 +95,9 @@ impl WalletManager { // Generate the appropriate test keypair let resonance_pair = match name { - "crystal_alice" => dilithium_crypto::crystal_alice(), - "crystal_bob" => dilithium_crypto::dilithium_bob(), - "crystal_charlie" => dilithium_crypto::crystal_charlie(), + "crystal_alice" => qp_dilithium_crypto::crystal_alice(), + "crystal_bob" => qp_dilithium_crypto::dilithium_bob(), + "crystal_charlie" => qp_dilithium_crypto::crystal_charlie(), _ => return Err(WalletError::KeyGeneration.into()), };