From a06af6da0a11ba890144be8bb0845378355a00af Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 6 Aug 2025 13:39:09 -0300 Subject: [PATCH 01/58] nssa basic project structure --- Cargo.lock | 335 ++++- Cargo.toml | 1 + nssa/Cargo.toml | 8 + nssa/program_methods/Cargo.toml | 11 + nssa/program_methods/build.rs | 4 + nssa/program_methods/guest/Cargo.lock | 1321 +++++++++++++++++ nssa/program_methods/guest/Cargo.toml | 10 + nssa/program_methods/guest/src/bin/example.rs | 6 + nssa/program_methods/src/lib.rs | 1 + nssa/rust-toolchain.toml | 5 + nssa/src/lib.rs | 14 + 11 files changed, 1675 insertions(+), 41 deletions(-) create mode 100644 nssa/Cargo.toml create mode 100644 nssa/program_methods/Cargo.toml create mode 100644 nssa/program_methods/build.rs create mode 100644 nssa/program_methods/guest/Cargo.lock create mode 100644 nssa/program_methods/guest/Cargo.toml create mode 100644 nssa/program_methods/guest/src/bin/example.rs create mode 100644 nssa/program_methods/src/lib.rs create mode 100644 nssa/rust-toolchain.toml create mode 100644 nssa/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 5d64132..62c9a42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -779,6 +779,19 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bonsai-sdk" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bce8d6acc5286a16e94c29e9c885d1869358885e08a6feeb6bc54e36fe20055" +dependencies = [ + "duplicate", + "maybe-async", + "reqwest 0.12.22", + "serde", + "thiserror 1.0.69", +] + [[package]] name = "bonsai-sdk" version = "1.4.0" @@ -1017,7 +1030,7 @@ dependencies = [ "k256", "log", "reqwest 0.11.27", - "risc0-zkvm", + "risc0-zkvm 2.3.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "rs_merkle", "secp256k1-zkp", "serde", @@ -2976,7 +2989,7 @@ dependencies = [ "log", "rand 0.8.5", "reqwest 0.11.27", - "risc0-zkvm", + "risc0-zkvm 2.3.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "sc_core", "secp256k1-zkp", "serde", @@ -3052,6 +3065,14 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nssa" +version = "0.1.0" +dependencies = [ + "program-methods", + "risc0-zkvm 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-bigint" version = "0.3.3" @@ -3418,6 +3439,13 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "program-methods" +version = "0.1.0" +dependencies = [ + "risc0-build 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "prost" version = "0.13.5" @@ -3783,6 +3811,25 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3df6368f71f205ff9c33c076d170dd56ebf68e8161c733c0caa07a7a5509ed53" +[[package]] +name = "risc0-binfmt" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62eb7025356a233c1bc267c458a2ce56fcfc89b136d813c8a77be14ef1eaf2b1" +dependencies = [ + "anyhow", + "borsh", + "derive_more 2.0.1", + "elf", + "lazy_static", + "postcard", + "risc0-zkp 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-zkvm-platform 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "semver", + "serde", + "tracing", +] + [[package]] name = "risc0-binfmt" version = "2.0.2" @@ -3794,13 +3841,37 @@ dependencies = [ "elf", "lazy_static", "postcard", - "risc0-zkp", - "risc0-zkvm-platform", + "risc0-zkp 2.0.2 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-zkvm-platform 2.0.3 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "semver", "serde", "tracing", ] +[[package]] +name = "risc0-build" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ffc0f135e6c1e9851e7e19438d03ff41a9d49199ee4f6c17b8bb30b4f83910" +dependencies = [ + "anyhow", + "cargo_metadata", + "derive_builder", + "dirs", + "docker-generate", + "hex", + "risc0-binfmt 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-zkos-v1compat 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-zkp 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-zkvm-platform 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rzup 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "semver", + "serde", + "serde_json", + "stability", + "tempfile", +] + [[package]] name = "risc0-build" version = "2.3.1" @@ -3812,11 +3883,11 @@ dependencies = [ "dirs", "docker-generate", "hex", - "risc0-binfmt", - "risc0-zkos-v1compat", - "risc0-zkp", - "risc0-zkvm-platform", - "rzup", + "risc0-binfmt 2.0.2 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-zkos-v1compat 2.0.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-zkp 2.0.2 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-zkvm-platform 2.0.3 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "rzup 0.4.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "semver", "serde", "serde_json", @@ -3838,6 +3909,22 @@ dependencies = [ "tempfile", ] +[[package]] +name = "risc0-circuit-keccak" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0094af5a57b020388a03bdd3834959c7d62723f1687be81414ade25104d93263" +dependencies = [ + "anyhow", + "bytemuck", + "paste", + "risc0-binfmt 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-circuit-recursion 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-core 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-zkp 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing", +] + [[package]] name = "risc0-circuit-keccak" version = "3.0.0" @@ -3849,12 +3936,12 @@ dependencies = [ "keccak", "paste", "rayon", - "risc0-binfmt", + "risc0-binfmt 2.0.2 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "risc0-circuit-keccak-sys", - "risc0-circuit-recursion", - "risc0-core", + "risc0-circuit-recursion 3.0.0 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-core 2.0.0 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "risc0-sys", - "risc0-zkp", + "risc0-zkp 2.0.2 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "tracing", "xz2", ] @@ -3869,11 +3956,26 @@ dependencies = [ "derive_more 2.0.1", "glob", "risc0-build-kernel", - "risc0-core", + "risc0-core 2.0.0 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "risc0-sys", "sppark", ] +[[package]] +name = "risc0-circuit-recursion" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ebded45c902c2b6939924a1cddd1d06b5d1d4ad1531e8798ebfee78f9c038d" +dependencies = [ + "anyhow", + "bytemuck", + "hex", + "metal", + "risc0-core 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-zkp 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing", +] + [[package]] name = "risc0-circuit-recursion" version = "3.0.0" @@ -3890,9 +3992,9 @@ dependencies = [ "rand 0.8.5", "rayon", "risc0-circuit-recursion-sys", - "risc0-core", + "risc0-core 2.0.0 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "risc0-sys", - "risc0-zkp", + "risc0-zkp 2.0.2 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "serde", "sha2", "tracing", @@ -3906,11 +4008,29 @@ source = "git+https://github.com/risc0/risc0.git?branch=release-2.3#03853380e36e dependencies = [ "glob", "risc0-build-kernel", - "risc0-core", + "risc0-core 2.0.0 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "risc0-sys", "sppark", ] +[[package]] +name = "risc0-circuit-rv32im" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15030849f8356f01f23c74b37dbfa4283100b594eb634109993e9e005ef45f64" +dependencies = [ + "anyhow", + "bit-vec", + "bytemuck", + "derive_more 2.0.1", + "paste", + "risc0-binfmt 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-core 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-zkp 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "tracing", +] + [[package]] name = "risc0-circuit-rv32im" version = "3.0.0" @@ -3932,11 +4052,11 @@ dependencies = [ "rand 0.8.5", "rayon", "ringbuffer", - "risc0-binfmt", + "risc0-binfmt 2.0.2 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "risc0-circuit-rv32im-sys", - "risc0-core", + "risc0-core 2.0.0 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "risc0-sys", - "risc0-zkp", + "risc0-zkp 2.0.2 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "serde", "smallvec", "tracing", @@ -3952,11 +4072,22 @@ dependencies = [ "derive_more 2.0.1", "glob", "risc0-build-kernel", - "risc0-core", + "risc0-core 2.0.0 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "risc0-sys", "sppark", ] +[[package]] +name = "risc0-core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317bbf70a8750b64d4fd7a2bdc9d7d5f30d8bb305cae486962c797ef35c8d08e" +dependencies = [ + "bytemuck", + "bytemuck_derive", + "rand_core 0.6.4", +] + [[package]] name = "risc0-core" version = "2.0.0" @@ -3969,6 +4100,27 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "risc0-groth16" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cf5d0b673d5fc67a89147c2e9c53134707dcc8137a43d1ef06b4ff68e99b74f" +dependencies = [ + "anyhow", + "ark-bn254", + "ark-ec", + "ark-groth16", + "ark-serialize", + "bytemuck", + "hex", + "num-bigint 0.4.6", + "num-traits", + "risc0-binfmt 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-zkp 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "stability", +] + [[package]] name = "risc0-groth16" version = "2.0.2" @@ -3983,9 +4135,9 @@ dependencies = [ "hex", "num-bigint 0.4.6", "num-traits", - "risc0-binfmt", - "risc0-core", - "risc0-zkp", + "risc0-binfmt 2.0.2 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-core 2.0.0 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-zkp 2.0.2 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "serde", "serde_json", "stability", @@ -4004,6 +4156,16 @@ dependencies = [ "sppark", ] +[[package]] +name = "risc0-zkos-v1compat" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76c479b69d1987cb54ac72dcc017197296fdcd6daf78fafc10cbbd3a167a7de" +dependencies = [ + "include_bytes_aligned", + "no_std_strings", +] + [[package]] name = "risc0-zkos-v1compat" version = "2.0.1" @@ -4013,6 +4175,31 @@ dependencies = [ "no_std_strings", ] +[[package]] +name = "risc0-zkp" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a287e9cd6d7b3b38eeb49c62090c46a1935922309fbd997a9143ed8c43c8f3cb" +dependencies = [ + "anyhow", + "blake2", + "borsh", + "bytemuck", + "cfg-if", + "digest", + "hex", + "hex-literal", + "metal", + "paste", + "rand_core 0.6.4", + "risc0-core 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-zkvm-platform 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "sha2", + "stability", + "tracing", +] + [[package]] name = "risc0-zkp" version = "2.0.2" @@ -4035,15 +4222,52 @@ dependencies = [ "rand 0.8.5", "rand_core 0.6.4", "rayon", - "risc0-core", + "risc0-core 2.0.0 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "risc0-sys", - "risc0-zkvm-platform", + "risc0-zkvm-platform 2.0.3 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "serde", "sha2", "stability", "tracing", ] +[[package]] +name = "risc0-zkvm" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9684b333c1c5d83f29ce2a92314ccfafd9d8cdfa6c4e19c07b97015d2f1eb9d0" +dependencies = [ + "anyhow", + "bincode", + "bonsai-sdk 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "borsh", + "bytemuck", + "bytes", + "derive_more 2.0.1", + "getrandom 0.2.16", + "hex", + "lazy-regex", + "prost", + "risc0-binfmt 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-build 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-circuit-keccak 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-circuit-recursion 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-circuit-rv32im 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-core 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-groth16 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-zkos-v1compat 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-zkp 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "risc0-zkvm-platform 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rrs-lib", + "rzup 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "semver", + "serde", + "sha2", + "stability", + "tempfile", + "tracing", +] + [[package]] name = "risc0-zkvm" version = "2.3.1" @@ -4052,7 +4276,7 @@ dependencies = [ "addr2line 0.22.0", "anyhow", "bincode", - "bonsai-sdk", + "bonsai-sdk 1.4.0 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "borsh", "bytemuck", "bytes", @@ -4068,19 +4292,19 @@ dependencies = [ "prost", "rand 0.8.5", "rayon", - "risc0-binfmt", - "risc0-build", - "risc0-circuit-keccak", - "risc0-circuit-recursion", - "risc0-circuit-rv32im", - "risc0-core", - "risc0-groth16", - "risc0-zkos-v1compat", - "risc0-zkp", - "risc0-zkvm-platform", + "risc0-binfmt 2.0.2 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-build 2.3.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-circuit-keccak 3.0.0 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-circuit-recursion 3.0.0 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-circuit-rv32im 3.0.0 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-core 2.0.0 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-groth16 2.0.2 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-zkos-v1compat 2.0.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-zkp 2.0.2 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-zkvm-platform 2.0.3 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "rrs-lib", "rustc-demangle", - "rzup", + "rzup 0.4.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "semver", "serde", "sha2", @@ -4090,6 +4314,20 @@ dependencies = [ "typetag", ] +[[package]] +name = "risc0-zkvm-platform" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cae9cb2c2f6cab2dfa395ea6e2576713929040c7fb0c5f4150d13e1119d18686" +dependencies = [ + "bytemuck", + "cfg-if", + "getrandom 0.2.16", + "getrandom 0.3.3", + "libm", + "stability", +] + [[package]] name = "risc0-zkvm-platform" version = "2.0.3" @@ -4252,6 +4490,21 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "rzup" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "400558bf12d4292a7804093b60a437ba8b0219ea7d53716b2c010a0d31e5f4a8" +dependencies = [ + "semver", + "serde", + "strum", + "tempfile", + "thiserror 2.0.12", + "toml 0.8.23", + "yaml-rust2", +] + [[package]] name = "rzup" version = "0.4.1" @@ -4283,7 +4536,7 @@ dependencies = [ "light-poseidon", "log", "rand 0.8.5", - "risc0-zkvm", + "risc0-zkvm 2.3.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "secp256k1-zkp", "serde", "serde_json", @@ -4832,7 +5085,7 @@ dependencies = [ name = "test-methods" version = "0.1.0" dependencies = [ - "risc0-build", + "risc0-build 2.3.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", ] [[package]] @@ -5929,7 +6182,7 @@ dependencies = [ "env_logger", "log", "rand 0.8.5", - "risc0-zkvm", + "risc0-zkvm 2.3.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "serde", "serde_json", "test-methods", diff --git a/Cargo.toml b/Cargo.toml index 4ce7c9a..a5cdd3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ members = [ "common", "sc_core", "integration_tests", + "nssa", ] [workspace.dependencies] diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml new file mode 100644 index 0000000..d1c9021 --- /dev/null +++ b/nssa/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "nssa" +version = "0.1.0" +edition = "2024" + +[dependencies] +risc0-zkvm = "2.2" +program-methods = { path = "program_methods" } diff --git a/nssa/program_methods/Cargo.toml b/nssa/program_methods/Cargo.toml new file mode 100644 index 0000000..d6ebc8f --- /dev/null +++ b/nssa/program_methods/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "program-methods" +version = "0.1.0" +edition = "2021" + +[build-dependencies] +risc0-build = { version = "2.2" } + +[package.metadata.risc0] +methods = ["guest"] + diff --git a/nssa/program_methods/build.rs b/nssa/program_methods/build.rs new file mode 100644 index 0000000..aa600df --- /dev/null +++ b/nssa/program_methods/build.rs @@ -0,0 +1,4 @@ +fn main() { + risc0_build::embed_methods(); +} + diff --git a/nssa/program_methods/guest/Cargo.lock b/nssa/program_methods/guest/Cargo.lock new file mode 100644 index 0000000..5a80568 --- /dev/null +++ b/nssa/program_methods/guest/Cargo.lock @@ -0,0 +1,1321 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-r1cs-std", + "ark-std", +] + +[[package]] +name = "ark-crypto-primitives" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0c292754729c8a190e50414fd1a37093c786c709899f29c9f7daccecfa855e" +dependencies = [ + "ahash", + "ark-crypto-primitives-macros", + "ark-ec", + "ark-ff", + "ark-relations", + "ark-serialize", + "ark-snark", + "ark-std", + "blake2", + "derivative", + "digest", + "fnv", + "merlin", + "sha2", +] + +[[package]] +name = "ark-crypto-primitives-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e89fe77d1f0f4fe5b96dfc940923d88d17b6a773808124f21e764dfb063c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "educe", + "fnv", + "hashbrown", + "itertools", + "num-bigint", + "num-integer", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "arrayvec", + "digest", + "educe", + "itertools", + "num-bigint", + "num-traits", + "paste", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn 2.0.104", +] + +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "ark-groth16" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88f1d0f3a534bb54188b8dcc104307db6c56cdae574ddc3212aec0625740fc7e" +dependencies = [ + "ark-crypto-primitives", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-relations", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-poly" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" +dependencies = [ + "ahash", + "ark-ff", + "ark-serialize", + "ark-std", + "educe", + "fnv", + "hashbrown", +] + +[[package]] +name = "ark-r1cs-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941551ef1df4c7a401de7068758db6503598e6f01850bdb2cfdb614a1f9dbea1" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-relations", + "ark-std", + "educe", + "num-bigint", + "num-integer", + "num-traits", + "tracing", +] + +[[package]] +name = "ark-relations" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec46ddc93e7af44bcab5230937635b06fb5744464dd6a7e7b083e80ebd274384" +dependencies = [ + "ark-ff", + "ark-std", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "arrayvec", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "ark-snark" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d368e2848c2d4c129ce7679a7d0d2d612b6a274d3ea6a13bad4445d61b381b88" +dependencies = [ + "ark-ff", + "ark-relations", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "borsh" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "bytemuck" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elf" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" + +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +dependencies = [ + "allocator-api2", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "include_bytes_aligned" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee796ad498c8d9a1d68e477df8f754ed784ef875de1414ebdaf169f70a6a784" + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core", + "zeroize", +] + +[[package]] +name = "metal" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" +dependencies = [ + "bitflags 2.9.1", + "block", + "core-graphics-types", + "foreign-types", + "log", + "objc", + "paste", +] + +[[package]] +name = "no_std_strings" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5b0c77c1b780822bc749a33e39aeb2c07584ab93332303babeabb645298a76e" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "programs" +version = "0.1.0" +dependencies = [ + "risc0-zkvm", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "risc0-binfmt" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62eb7025356a233c1bc267c458a2ce56fcfc89b136d813c8a77be14ef1eaf2b1" +dependencies = [ + "anyhow", + "borsh", + "derive_more", + "elf", + "lazy_static", + "postcard", + "risc0-zkp", + "risc0-zkvm-platform", + "semver", + "serde", + "tracing", +] + +[[package]] +name = "risc0-circuit-keccak" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0094af5a57b020388a03bdd3834959c7d62723f1687be81414ade25104d93263" +dependencies = [ + "anyhow", + "bytemuck", + "paste", + "risc0-binfmt", + "risc0-circuit-recursion", + "risc0-core", + "risc0-zkp", + "tracing", +] + +[[package]] +name = "risc0-circuit-recursion" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ebded45c902c2b6939924a1cddd1d06b5d1d4ad1531e8798ebfee78f9c038d" +dependencies = [ + "anyhow", + "bytemuck", + "hex", + "metal", + "risc0-core", + "risc0-zkp", + "tracing", +] + +[[package]] +name = "risc0-circuit-rv32im" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15030849f8356f01f23c74b37dbfa4283100b594eb634109993e9e005ef45f64" +dependencies = [ + "anyhow", + "bit-vec", + "bytemuck", + "derive_more", + "paste", + "risc0-binfmt", + "risc0-core", + "risc0-zkp", + "serde", + "tracing", +] + +[[package]] +name = "risc0-core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317bbf70a8750b64d4fd7a2bdc9d7d5f30d8bb305cae486962c797ef35c8d08e" +dependencies = [ + "bytemuck", + "bytemuck_derive", + "rand_core", +] + +[[package]] +name = "risc0-groth16" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cf5d0b673d5fc67a89147c2e9c53134707dcc8137a43d1ef06b4ff68e99b74f" +dependencies = [ + "anyhow", + "ark-bn254", + "ark-ec", + "ark-groth16", + "ark-serialize", + "bytemuck", + "hex", + "num-bigint", + "num-traits", + "risc0-binfmt", + "risc0-zkp", + "serde", + "stability", +] + +[[package]] +name = "risc0-zkos-v1compat" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76c479b69d1987cb54ac72dcc017197296fdcd6daf78fafc10cbbd3a167a7de" +dependencies = [ + "include_bytes_aligned", + "no_std_strings", +] + +[[package]] +name = "risc0-zkp" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a287e9cd6d7b3b38eeb49c62090c46a1935922309fbd997a9143ed8c43c8f3cb" +dependencies = [ + "anyhow", + "blake2", + "borsh", + "bytemuck", + "cfg-if", + "digest", + "hex", + "hex-literal", + "metal", + "paste", + "rand_core", + "risc0-core", + "risc0-zkvm-platform", + "serde", + "sha2", + "stability", + "tracing", +] + +[[package]] +name = "risc0-zkvm" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9684b333c1c5d83f29ce2a92314ccfafd9d8cdfa6c4e19c07b97015d2f1eb9d0" +dependencies = [ + "anyhow", + "borsh", + "bytemuck", + "derive_more", + "getrandom 0.2.16", + "hex", + "risc0-binfmt", + "risc0-circuit-keccak", + "risc0-circuit-recursion", + "risc0-circuit-rv32im", + "risc0-core", + "risc0-groth16", + "risc0-zkos-v1compat", + "risc0-zkp", + "risc0-zkvm-platform", + "rrs-lib", + "semver", + "serde", + "sha2", + "stability", + "tracing", +] + +[[package]] +name = "risc0-zkvm-platform" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cae9cb2c2f6cab2dfa395ea6e2576713929040c7fb0c5f4150d13e1119d18686" +dependencies = [ + "bytemuck", + "cfg-if", + "getrandom 0.2.16", + "getrandom 0.3.3", + "libm", + "stability", +] + +[[package]] +name = "rrs-lib" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4382d3af3a4ebdae7f64ba6edd9114fff92c89808004c4943b393377a25d001" +dependencies = [ + "downcast-rs", + "paste", +] + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stability" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" +dependencies = [ + "quote", + "syn 2.0.104", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "tracing-core", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "winnow" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] diff --git a/nssa/program_methods/guest/Cargo.toml b/nssa/program_methods/guest/Cargo.toml new file mode 100644 index 0000000..b14d765 --- /dev/null +++ b/nssa/program_methods/guest/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "programs" +version = "0.1.0" +edition = "2021" + +[workspace] + +[dependencies] +risc0-zkvm = { version = "2.2.0", default-features = false, features = ['std'] } + diff --git a/nssa/program_methods/guest/src/bin/example.rs b/nssa/program_methods/guest/src/bin/example.rs new file mode 100644 index 0000000..be07fee --- /dev/null +++ b/nssa/program_methods/guest/src/bin/example.rs @@ -0,0 +1,6 @@ +use risc0_zkvm::guest::env; +fn main() { + let a: u32 = env::read(); + env::commit(&a); +} + diff --git a/nssa/program_methods/src/lib.rs b/nssa/program_methods/src/lib.rs new file mode 100644 index 0000000..1bdb308 --- /dev/null +++ b/nssa/program_methods/src/lib.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/methods.rs")); diff --git a/nssa/rust-toolchain.toml b/nssa/rust-toolchain.toml new file mode 100644 index 0000000..8663d46 --- /dev/null +++ b/nssa/rust-toolchain.toml @@ -0,0 +1,5 @@ +[toolchain] +channel = "stable" +components = ["rustfmt", "rust-src"] +profile = "minimal" + diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs new file mode 100644 index 0000000..b93cf3f --- /dev/null +++ b/nssa/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From aba8f3549f9d4f1e350ade1fc48aed0e6b0cbcf6 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 6 Aug 2025 20:05:04 -0300 Subject: [PATCH 02/58] public transactions wip --- Cargo.lock | 10 + nssa/Cargo.toml | 1 + nssa/core/Cargo.toml | 8 + nssa/core/src/account/commitment.rs | 3 + nssa/core/src/account/mod.rs | 37 + nssa/core/src/account/nullifier.rs | 3 + nssa/core/src/lib.rs | 2 + nssa/core/src/program.rs | 63 + nssa/program_methods/guest/Cargo.lock | 1898 ++++++++++++++++- nssa/program_methods/guest/Cargo.toml | 1 + .../guest/src/bin/authenticated_transfer.rs | 31 + nssa/program_methods/guest/src/bin/example.rs | 6 - nssa/src/address.rs | 17 + nssa/src/lib.rs | 55 +- nssa/src/public_transaction.rs | 85 + nssa/src/signature.rs | 26 + nssa/src/state.rs | 169 ++ 17 files changed, 2386 insertions(+), 29 deletions(-) create mode 100644 nssa/core/Cargo.toml create mode 100644 nssa/core/src/account/commitment.rs create mode 100644 nssa/core/src/account/mod.rs create mode 100644 nssa/core/src/account/nullifier.rs create mode 100644 nssa/core/src/lib.rs create mode 100644 nssa/core/src/program.rs create mode 100644 nssa/program_methods/guest/src/bin/authenticated_transfer.rs delete mode 100644 nssa/program_methods/guest/src/bin/example.rs create mode 100644 nssa/src/address.rs create mode 100644 nssa/src/public_transaction.rs create mode 100644 nssa/src/signature.rs create mode 100644 nssa/src/state.rs diff --git a/Cargo.lock b/Cargo.lock index 62c9a42..fc2f455 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2987,6 +2987,7 @@ dependencies = [ "hex", "k256", "log", + "nssa", "rand 0.8.5", "reqwest 0.11.27", "risc0-zkvm 2.3.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", @@ -3069,10 +3070,19 @@ dependencies = [ name = "nssa" version = "0.1.0" dependencies = [ + "nssa-core", "program-methods", "risc0-zkvm 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "nssa-core" +version = "0.1.0" +dependencies = [ + "risc0-zkvm 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", +] + [[package]] name = "num-bigint" version = "0.3.3" diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index d1c9021..243d3f1 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -5,4 +5,5 @@ edition = "2024" [dependencies] risc0-zkvm = "2.2" +nssa-core = {path="core"} program-methods = { path = "program_methods" } diff --git a/nssa/core/Cargo.toml b/nssa/core/Cargo.toml new file mode 100644 index 0000000..3a61860 --- /dev/null +++ b/nssa/core/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "nssa-core" +version = "0.1.0" +edition = "2024" + +[dependencies] +risc0-zkvm = "2.2" +serde = { version = "1.0", default-features = false } diff --git a/nssa/core/src/account/commitment.rs b/nssa/core/src/account/commitment.rs new file mode 100644 index 0000000..99ddf84 --- /dev/null +++ b/nssa/core/src/account/commitment.rs @@ -0,0 +1,3 @@ +pub(crate) struct Commitment { + value: [u8; 32], +} diff --git a/nssa/core/src/account/mod.rs b/nssa/core/src/account/mod.rs new file mode 100644 index 0000000..1814cdd --- /dev/null +++ b/nssa/core/src/account/mod.rs @@ -0,0 +1,37 @@ +mod commitment; +mod nullifier; + +pub(crate) use commitment::Commitment; +pub(crate) use nullifier::Nullifier; +use serde::{Deserialize, Serialize}; + +use crate::program::ProgramId; + +pub type Nonce = u128; +type Data = Vec; + +/// Account to be used both in public and private contexts +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct Account { + pub program_owner: ProgramId, + pub balance: u128, + pub data: Data, + pub nonce: Nonce, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct AccountWithMetadata { + pub account: Account, + pub is_authorized: bool, +} + +impl Default for Account { + fn default() -> Self { + Self { + program_owner: [0; 8], + balance: 0, + data: vec![], + nonce: 0, + } + } +} diff --git a/nssa/core/src/account/nullifier.rs b/nssa/core/src/account/nullifier.rs new file mode 100644 index 0000000..cc3e906 --- /dev/null +++ b/nssa/core/src/account/nullifier.rs @@ -0,0 +1,3 @@ +pub(crate) struct Nullifier { + value: [u8; 32] +} diff --git a/nssa/core/src/lib.rs b/nssa/core/src/lib.rs new file mode 100644 index 0000000..d20620e --- /dev/null +++ b/nssa/core/src/lib.rs @@ -0,0 +1,2 @@ +pub mod account; +pub mod program; diff --git a/nssa/core/src/program.rs b/nssa/core/src/program.rs new file mode 100644 index 0000000..7f66c2a --- /dev/null +++ b/nssa/core/src/program.rs @@ -0,0 +1,63 @@ +use serde::{Deserialize, Serialize}; + +use crate::account::{Account, AccountWithMetadata}; + +pub type ProgramId = [u32; 8]; + +/// A trait to be implemented by inner programs. +pub trait Program { + const PROGRAM_ID: ProgramId; + const PROGRAM_ELF: &[u8]; + type InstructionData: Serialize + for<'de> Deserialize<'de>; +} + +/// Validates well-behaved program execution +/// +/// # Parameters +/// - `pre_states`: The list of input accounts, each annotated with authorization metadata. +/// - `post_states`: The list of resulting accounts after executing the program logic. +/// - `executing_program_id`: The identifier of the program that was executed. +pub fn validate_constraints( + pre_states: &[AccountWithMetadata], + post_states: &[Account], + executing_program_id: ProgramId, +) -> Result<(), ()> { + // 1. Lengths must match + if pre_states.len() != post_states.len() { + return Err(()); + } + + for (pre, post) in pre_states.iter().zip(post_states) { + // 2. Nonce must remain unchanged + if pre.account.nonce != post.nonce { + return Err(()); + } + + // 3. Ownership change only allowed from default accounts + if pre.account.program_owner != post.program_owner && pre.account != Account::default() { + return Err(()); + } + + // 4. Decreasing balance only allowed if owned by executing program + if post.balance < pre.account.balance && pre.account.program_owner != executing_program_id { + return Err(()); + } + + // 5. Data changes only allowed if owned by executing program + if pre.account.data != post.data + && (executing_program_id != pre.account.program_owner + || executing_program_id != post.program_owner) + { + return Err(()); + } + } + + // 6. Total balance is preserved + let total_balance_pre_states: u128 = pre_states.iter().map(|pre| pre.account.balance).sum(); + let total_balance_post_states: u128 = post_states.iter().map(|post| post.balance).sum(); + if total_balance_pre_states != total_balance_post_states { + return Err(()); + } + + Ok(()) +} diff --git a/nssa/program_methods/guest/Cargo.lock b/nssa/program_methods/guest/Cargo.lock index 5a80568..1d3063f 100644 --- a/nssa/program_methods/guest/Cargo.lock +++ b/nssa/program_methods/guest/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "ahash" version = "0.8.12" @@ -14,6 +29,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -84,7 +108,7 @@ dependencies = [ "ark-std", "educe", "fnv", - "hashbrown", + "hashbrown 0.15.4", "itertools", "num-bigint", "num-integer", @@ -162,7 +186,7 @@ dependencies = [ "ark-std", "educe", "fnv", - "hashbrown", + "hashbrown 0.15.4", ] [[package]] @@ -237,9 +261,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" dependencies = [ "num-traits", - "rand", + "rand 0.8.5", ] +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + [[package]] name = "arrayvec" version = "0.7.6" @@ -252,6 +282,36 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bit-vec" version = "0.8.0" @@ -294,6 +354,19 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bonsai-sdk" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bce8d6acc5286a16e94c29e9c885d1869358885e08a6feeb6bc54e36fe20055" +dependencies = [ + "duplicate", + "maybe-async", + "reqwest", + "serde", + "thiserror 1.0.69", +] + [[package]] name = "borsh" version = "1.5.7" @@ -317,6 +390,12 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + [[package]] name = "bytemuck" version = "1.23.1" @@ -343,6 +422,56 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +dependencies = [ + "serde", +] + +[[package]] +name = "camino" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.12", +] + +[[package]] +name = "cc" +version = "1.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.1" @@ -361,7 +490,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" dependencies = [ - "thiserror", + "thiserror 2.0.12", ] [[package]] @@ -416,6 +545,41 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.104", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.104", +] + [[package]] name = "derivative" version = "2.2.0" @@ -427,6 +591,37 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.104", +] + [[package]] name = "derive_more" version = "2.0.1" @@ -460,12 +655,60 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "docker-generate" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf673e0848ef09fa4aeeba78e681cf651c0c7d35f76ee38cec8e55bc32fa111" + [[package]] name = "downcast-rs" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "duplicate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de78e66ac9061e030587b2a2e75cc88f22304913c907b11307bca737141230cb" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", +] + [[package]] name = "educe" version = "0.6.0" @@ -502,6 +745,15 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "enum-ordinalize" version = "4.3.0" @@ -528,6 +780,22 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "fnv" version = "1.0.7" @@ -561,6 +829,77 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -578,8 +917,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -589,9 +930,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", ] [[package]] @@ -603,6 +961,27 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hex" version = "0.4.3" @@ -615,6 +994,219 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.0", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "include_bytes_aligned" version = "0.1.4" @@ -628,7 +1220,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.4", +] + +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", ] [[package]] @@ -640,6 +1259,22 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "keccak" version = "0.1.5" @@ -649,6 +1284,29 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "lazy-regex" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126" +dependencies = [ + "lazy-regex-proc_macros", + "once_cell", + "regex", +] + +[[package]] +name = "lazy-regex-proc_macros" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 2.0.104", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -670,12 +1328,40 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +[[package]] +name = "libredox" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" +dependencies = [ + "bitflags 2.9.1", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + [[package]] name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "malloc_buf" version = "0.0.6" @@ -685,6 +1371,17 @@ dependencies = [ "libc", ] +[[package]] +name = "maybe-async" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "memchr" version = "2.7.5" @@ -699,7 +1396,7 @@ checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" dependencies = [ "byteorder", "keccak", - "rand_core", + "rand_core 0.6.4", "zeroize", ] @@ -718,12 +1415,40 @@ dependencies = [ "paste", ] +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + [[package]] name = "no_std_strings" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5b0c77c1b780822bc749a33e39aeb2c07584ab93332303babeabb645298a76e" +[[package]] +name = "nssa-core" +version = "0.1.0" +dependencies = [ + "risc0-zkvm", + "serde", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -761,24 +1486,51 @@ dependencies = [ "malloc_buf", ] +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "postcard" version = "1.1.3" @@ -791,6 +1543,15 @@ dependencies = [ "serde", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -809,6 +1570,30 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -822,9 +1607,88 @@ dependencies = [ name = "programs" version = "0.1.0" dependencies = [ + "nssa-core", "risc0-zkvm", ] +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "quinn" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2 0.5.10", + "thiserror 2.0.12", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +dependencies = [ + "bytes", + "getrandom 0.3.3", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.12", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.5.10", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "quote" version = "1.0.40" @@ -846,8 +1710,18 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -857,7 +1731,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -866,6 +1750,111 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.12.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "risc0-binfmt" version = "2.0.2" @@ -885,6 +1874,30 @@ dependencies = [ "tracing", ] +[[package]] +name = "risc0-build" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ffc0f135e6c1e9851e7e19438d03ff41a9d49199ee4f6c17b8bb30b4f83910" +dependencies = [ + "anyhow", + "cargo_metadata", + "derive_builder", + "dirs", + "docker-generate", + "hex", + "risc0-binfmt", + "risc0-zkos-v1compat", + "risc0-zkp", + "risc0-zkvm-platform", + "rzup", + "semver", + "serde", + "serde_json", + "stability", + "tempfile", +] + [[package]] name = "risc0-circuit-keccak" version = "3.0.0" @@ -942,7 +1955,7 @@ checksum = "317bbf70a8750b64d4fd7a2bdc9d7d5f30d8bb305cae486962c797ef35c8d08e" dependencies = [ "bytemuck", "bytemuck_derive", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -992,7 +2005,7 @@ dependencies = [ "hex-literal", "metal", "paste", - "rand_core", + "rand_core 0.6.4", "risc0-core", "risc0-zkvm-platform", "serde", @@ -1008,12 +2021,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9684b333c1c5d83f29ce2a92314ccfafd9d8cdfa6c4e19c07b97015d2f1eb9d0" dependencies = [ "anyhow", + "bincode", + "bonsai-sdk", "borsh", "bytemuck", + "bytes", "derive_more", "getrandom 0.2.16", "hex", + "lazy-regex", + "prost", "risc0-binfmt", + "risc0-build", "risc0-circuit-keccak", "risc0-circuit-recursion", "risc0-circuit-rv32im", @@ -1023,10 +2042,12 @@ dependencies = [ "risc0-zkp", "risc0-zkvm-platform", "rrs-lib", + "rzup", "semver", "serde", "sha2", "stability", + "tempfile", "tracing", ] @@ -1054,6 +2075,93 @@ dependencies = [ "paste", ] +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.60.2", +] + +[[package]] +name = "rustls" +version = "0.23.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "rzup" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "400558bf12d4292a7804093b60a437ba8b0219ea7d53716b2c010a0d31e5f4a8" +dependencies = [ + "semver", + "serde", + "strum", + "tempfile", + "thiserror 2.0.12", + "toml", + "yaml-rust2", +] + [[package]] name = "semver" version = "1.0.26" @@ -1083,6 +2191,39 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "serde_json" +version = "1.0.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha2" version = "0.10.9" @@ -1094,6 +2235,44 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "spin" version = "0.9.8" @@ -1110,6 +2289,40 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.104", +] + [[package]] name = "subtle" version = "2.6.1" @@ -1138,13 +2351,66 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] @@ -1158,11 +2424,91 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "pin-project-lite", + "slab", + "socket2 0.6.0", + "windows-sys 0.59.0", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -1171,10 +2517,64 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", + "toml_write", "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags 2.9.1", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.41" @@ -1217,6 +2617,12 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.18.0" @@ -1235,6 +2641,29 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "valuable" version = "0.1.1" @@ -1247,6 +2676,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -1262,6 +2700,347 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.104", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.7.12" @@ -1280,6 +3059,47 @@ dependencies = [ "bitflags 2.9.1", ] +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yaml-rust2" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a1a1c0bc9823338a3bdf8c61f994f23ac004c6fa32c08cd152984499b445e8d" +dependencies = [ + "arraydeque", + "encoding_rs", + "hashlink", +] + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.26" @@ -1300,6 +3120,27 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" @@ -1319,3 +3160,36 @@ dependencies = [ "quote", "syn 2.0.104", ] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] diff --git a/nssa/program_methods/guest/Cargo.toml b/nssa/program_methods/guest/Cargo.toml index b14d765..37e7a79 100644 --- a/nssa/program_methods/guest/Cargo.toml +++ b/nssa/program_methods/guest/Cargo.toml @@ -7,4 +7,5 @@ edition = "2021" [dependencies] risc0-zkvm = { version = "2.2.0", default-features = false, features = ['std'] } +nssa-core = {path = "../../core"} diff --git a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs new file mode 100644 index 0000000..b24e004 --- /dev/null +++ b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs @@ -0,0 +1,31 @@ +use nssa_core::account::{Account, AccountWithMetadata}; +use risc0_zkvm::guest::env; + +/// A transfer of balance program. +/// To be used both in public and private contexts. +fn main() { + // Read input accounts. + // It is expected to receive only two accounts: [sender_account, receiver_account] + let input_accounts: Vec = env::read(); + let balance_to_move: u128 = env::read(); + + // Unpack sender and receiver + assert_eq!(input_accounts.len(), 2); + let [sender, receiver] = input_accounts + .try_into() + .unwrap_or_else(|_| panic!("Bad input")); + + // Check sender has authorized this operation + assert!(sender.is_authorized); + + // Check sender has enough balance + assert!(sender.account.balance >= balance_to_move); + + // Create accounts post states, with updated balances + let mut sender_post = sender.account.clone(); + let mut receiver_post = receiver.account.clone(); + sender_post.balance -= balance_to_move; + receiver_post.balance += balance_to_move; + + env::commit(&vec![sender_post, receiver_post]); +} diff --git a/nssa/program_methods/guest/src/bin/example.rs b/nssa/program_methods/guest/src/bin/example.rs deleted file mode 100644 index be07fee..0000000 --- a/nssa/program_methods/guest/src/bin/example.rs +++ /dev/null @@ -1,6 +0,0 @@ -use risc0_zkvm::guest::env; -fn main() { - let a: u32 = env::read(); - env::commit(&a); -} - diff --git a/nssa/src/address.rs b/nssa/src/address.rs new file mode 100644 index 0000000..57533a6 --- /dev/null +++ b/nssa/src/address.rs @@ -0,0 +1,17 @@ +use crate::signature::PublicKey; + +#[derive(Clone, Hash, PartialEq, Eq)] +pub(crate) struct Address { + pub(crate) value: [u8; 32], +} + +impl Address { + pub(crate) fn new(value: [u8; 32]) -> Self { + Self { value } + } + + pub(crate) fn from_public_key(public_key: &PublicKey) -> Self { + // TODO: implement + Address::new([public_key.0; 32]) + } +} diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs index b93cf3f..4c2479f 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -1,14 +1,47 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right +use nssa_core::{ + account::{Account, AccountWithMetadata}, + program::{Program, ProgramId}, +}; +use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID}; +use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor}; + +mod address; +mod public_transaction; +mod signature; +pub mod state; + +struct AuthenticatedTransferProgram; +impl Program for AuthenticatedTransferProgram { + const PROGRAM_ID: ProgramId = AUTHENTICATED_TRANSFER_ID; + const PROGRAM_ELF: &[u8] = AUTHENTICATED_TRANSFER_ELF; + type InstructionData = u128; } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } +/// Writes inputs to `env_builder` in the order expected by the programs +fn write_inputs( + pre_states: &[AccountWithMetadata], + instruction_data: P::InstructionData, + env_builder: &mut ExecutorEnvBuilder, +) -> Result<(), ()> { + let pre_states = pre_states.to_vec(); + env_builder.write(&pre_states).map_err(|_| ())?; + env_builder.write(&instruction_data).map_err(|_| ())?; + Ok(()) +} + +fn execute_public( + pre_states: &[AccountWithMetadata], + instruction_data: P::InstructionData, +) -> Result, ()> { + // Write inputs to the program + let mut env_builder = ExecutorEnv::builder(); + write_inputs::

(pre_states, instruction_data, &mut env_builder)?; + let env = env_builder.build().unwrap(); + + // Execute the program (without proving) + let executor = default_executor(); + let session_info = executor.execute(env, P::PROGRAM_ELF).map_err(|_| ())?; + + // Get (inputs and) outputs + session_info.journal.decode().map_err(|_| ()) } diff --git a/nssa/src/public_transaction.rs b/nssa/src/public_transaction.rs new file mode 100644 index 0000000..1812bff --- /dev/null +++ b/nssa/src/public_transaction.rs @@ -0,0 +1,85 @@ +use nssa_core::{ + account::{Account, Nonce}, + program::ProgramId, +}; + +use crate::{ + address::Address, + signature::{PrivateKey, PublicKey, Signature}, +}; + +pub(crate) struct Message { + pub(crate) program_id: ProgramId, + pub(crate) addresses: Vec

, + pub(crate) nonces: Vec, + // TODO: change to Vec for general programs + pub(crate) instruction_data: u128, +} + +impl Message { + pub(crate) fn new( + program_id: ProgramId, + addresses: Vec
, + nonces: Vec, + instruction_data: u128, + ) -> Self { + Self { + program_id, + addresses, + nonces, + instruction_data, + } + } + + fn to_bytes(&self) -> Vec { + //TODO: implement + vec![0, 0] + } +} + +pub(crate) struct WitnessSet { + pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>, +} + +impl WitnessSet { + pub(crate) fn for_message(message: &Message, private_keys: &[PrivateKey]) -> Self { + let message_bytes = message.to_bytes(); + let signatures_and_public_keys = private_keys + .iter() + .map(|key| (Signature::new(key, &message_bytes), PublicKey::new(key))) + .collect(); + Self { + signatures_and_public_keys, + } + } +} + +pub(crate) struct PublicTransaction { + message: Message, + witness_set: WitnessSet, +} + +impl PublicTransaction { + pub(crate) fn message(&self) -> &Message { + &self.message + } + + pub(crate) fn witness_set(&self) -> &WitnessSet { + &self.witness_set + } + + pub(crate) fn signer_addresses(&self) -> Vec
{ + self.witness_set + .signatures_and_public_keys + .iter() + .map(|(_, public_key)| Address::from_public_key(public_key)) + .collect() + } + + pub(crate) fn new(message: Message, witness_set: WitnessSet) -> Self { + Self { + message, + witness_set, + } + } +} diff --git a/nssa/src/signature.rs b/nssa/src/signature.rs new file mode 100644 index 0000000..ea90b56 --- /dev/null +++ b/nssa/src/signature.rs @@ -0,0 +1,26 @@ +use crate::{address::Address, public_transaction::Message}; + +pub(crate) struct Signature; + +// TODO: Dummy impl. Replace by actual private key. +pub(crate) struct PrivateKey(pub(crate) u8); +// TODO: Dummy impl. Replace by actual public key. +pub(crate) struct PublicKey(pub(crate) u8); + +impl PublicKey { + pub(crate) fn new(key: &PrivateKey) -> Self { + // TODO: implement + Self(key.0) + } +} + +impl Signature { + pub(crate) fn new(key: &PrivateKey, message: &[u8]) -> Self { + Self + } + + pub(crate) fn is_valid_for(&self, message: &Message, public_key: &PublicKey) -> bool { + // TODO: implement + true + } +} diff --git a/nssa/src/state.rs b/nssa/src/state.rs new file mode 100644 index 0000000..5310fda --- /dev/null +++ b/nssa/src/state.rs @@ -0,0 +1,169 @@ +use crate::{ + AuthenticatedTransferProgram, address::Address, execute_public, + public_transaction::PublicTransaction, +}; +use nssa_core::{ + account::{Account, AccountWithMetadata}, + program::{Program, validate_constraints}, +}; +use std::collections::{HashMap, HashSet}; + +struct V01State { + public_state: HashMap, +} + +impl V01State { + fn transition_from_public_transaction(&mut self, tx: PublicTransaction) -> Result<(), ()> { + let state_diff = self + .execute_and_verify_public_transaction(&tx) + .map_err(|_| ())?; + + for (address, post) in state_diff.into_iter() { + let current_account = self.get_account_by_address_mut(address); + *current_account = post; + } + + for address in tx.signer_addresses() { + let current_account = self.get_account_by_address_mut(address); + current_account.nonce += 1; + } + Ok(()) + } + + fn get_account_by_address_mut(&mut self, address: Address) -> &mut Account { + self.public_state + .entry(address) + .or_insert_with(Account::default) + } + + fn get_account_by_address(&self, address: &Address) -> Account { + self.public_state + .get(address) + .cloned() + .unwrap_or(Account::default()) + } + + fn execute_and_verify_public_transaction( + &mut self, + tx: &PublicTransaction, + ) -> Result, ()> { + let message = tx.message(); + let witness_set = tx.witness_set(); + + // All addresses must be different + if message.addresses.iter().collect::>().len() != message.addresses.len() { + return Err(()); + } + + if message.nonces.len() != witness_set.signatures_and_public_keys.len() { + return Err(()); + } + + let mut authorized_addresses = Vec::new(); + for ((signature, public_key), nonce) in witness_set + .signatures_and_public_keys + .iter() + .zip(message.nonces.iter()) + { + // Check the signature is valid + if !signature.is_valid_for(message, public_key) { + return Err(()); + } + + // Check the nonce corresponds to the current nonce on the public state. + let address = Address::from_public_key(public_key); + let current_nonce = self.get_account_by_address(&address).nonce; + if current_nonce != *nonce { + return Err(()); + } + + authorized_addresses.push(address); + } + + // Build pre_states for execution + let pre_states: Vec<_> = message + .addresses + .iter() + .map(|address| AccountWithMetadata { + account: self.get_account_by_address(address), + is_authorized: authorized_addresses.contains(address), + }) + .collect(); + + // Check the `program_id` corresponds to a built-in program + // Only allowed program so far is the authenticated transfer program + if message.program_id != AuthenticatedTransferProgram::PROGRAM_ID { + return Err(()); + } + + // // Execute program + let post_states = + execute_public::(&pre_states, message.instruction_data) + .map_err(|_| ())?; + + // Verify execution corresponds to a well-behaved program. + // See the # Programs section for the definition of the `validate_constraints` method. + validate_constraints(&pre_states, &post_states, message.program_id).map_err(|_| ())?; + + if (post_states.len() != message.addresses.len()) { + return Err(()); + } + + Ok(message + .addresses + .iter() + .cloned() + .zip(post_states.into_iter()) + .collect()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + public_transaction::{self, WitnessSet}, + signature::PrivateKey, + }; + + fn genesis_state_for_tests() -> (V01State, Address) { + let account_1 = { + let mut this = Account::default(); + this.program_owner = AuthenticatedTransferProgram::PROGRAM_ID; + this.balance = 100; + this + }; + let address_1 = Address::new([1; 32]); + let public_state = [(address_1.clone(), account_1)].into_iter().collect(); + (V01State { public_state }, address_1) + } + + fn transfer_transaction_for_tests( + from: Address, + from_key: PrivateKey, + to: Address, + balance: u128, + ) -> PublicTransaction { + let addresses = vec![from, to]; + let nonces = vec![0]; + let program_id = AuthenticatedTransferProgram::PROGRAM_ID; + let message = public_transaction::Message::new(program_id, addresses, nonces, balance); + let witness_set = WitnessSet::for_message(&message, &[from_key]); + PublicTransaction::new(message, witness_set) + } + + #[test] + fn test_1() { + let (mut genesis_state, address) = genesis_state_for_tests(); + let from = address; + let from_key = PrivateKey(1); + let to = Address::new([2; 32]); + let balance_to_move = 5; + let tx = transfer_transaction_for_tests(from, from_key, to.clone(), 5); + let _ = genesis_state.transition_from_public_transaction(tx); + assert_eq!( + genesis_state.get_account_by_address(&to).balance, + balance_to_move + ); + } +} From ae9963aec302b723386948883d63281aa4e05481 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Thu, 7 Aug 2025 09:33:54 -0300 Subject: [PATCH 03/58] add test --- Cargo.lock | 1 - nssa/src/state.rs | 74 +++++++++++++++++++++++++++++++++++------------ 2 files changed, 55 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc2f455..07f1410 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2987,7 +2987,6 @@ dependencies = [ "hex", "k256", "log", - "nssa", "rand 0.8.5", "reqwest 0.11.27", "risc0-zkvm 2.3.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 5310fda..a636859 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -121,49 +121,85 @@ impl V01State { #[cfg(test)] mod tests { use super::*; - use crate::{ - public_transaction::{self, WitnessSet}, - signature::PrivateKey, - }; + use crate::{public_transaction, signature::PrivateKey}; - fn genesis_state_for_tests() -> (V01State, Address) { - let account_1 = { - let mut this = Account::default(); - this.program_owner = AuthenticatedTransferProgram::PROGRAM_ID; - this.balance = 100; - this - }; - let address_1 = Address::new([1; 32]); - let public_state = [(address_1.clone(), account_1)].into_iter().collect(); - (V01State { public_state }, address_1) + fn genesis_state_for_tests(balances: &[u128], addresses: &[Address]) -> V01State { + assert_eq!(balances.len(), addresses.len()); + let accounts: Vec = balances + .iter() + .map(|balance| { + let mut account = Account::default(); + account.balance = *balance; + account.program_owner = AuthenticatedTransferProgram::PROGRAM_ID; + account + }) + .collect(); + + let public_state = addresses + .to_owned() + .into_iter() + .zip(accounts.into_iter()) + .collect(); + V01State { public_state } } fn transfer_transaction_for_tests( from: Address, from_key: PrivateKey, + nonce: u128, to: Address, balance: u128, ) -> PublicTransaction { let addresses = vec![from, to]; - let nonces = vec![0]; + let nonces = vec![nonce]; let program_id = AuthenticatedTransferProgram::PROGRAM_ID; let message = public_transaction::Message::new(program_id, addresses, nonces, balance); - let witness_set = WitnessSet::for_message(&message, &[from_key]); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[from_key]); PublicTransaction::new(message, witness_set) } #[test] fn test_1() { - let (mut genesis_state, address) = genesis_state_for_tests(); - let from = address; + let addresses = [Address::new([1; 32])]; + let balances = [100]; + let mut genesis_state = genesis_state_for_tests(&balances, &addresses); + let from = addresses[0].clone(); let from_key = PrivateKey(1); let to = Address::new([2; 32]); let balance_to_move = 5; - let tx = transfer_transaction_for_tests(from, from_key, to.clone(), 5); + let tx = + transfer_transaction_for_tests(from.clone(), from_key, 0, to.clone(), balance_to_move); let _ = genesis_state.transition_from_public_transaction(tx); assert_eq!( genesis_state.get_account_by_address(&to).balance, balance_to_move ); + assert_eq!( + genesis_state.get_account_by_address(&from).balance, + balances[0] - balance_to_move + ); + assert_eq!(genesis_state.get_account_by_address(&from).nonce, 1); + assert_eq!(genesis_state.get_account_by_address(&to).nonce, 0); + } + + #[test] + fn test_2() { + let addresses = [Address::new([1; 32]), Address::new([99; 32])]; + let balances = [100, 200]; + let mut genesis_state = genesis_state_for_tests(&balances, &addresses); + let from = addresses[1].clone(); + let from_key = PrivateKey(99); + let to = addresses[0].clone(); + let balance_to_move = 8; + let to_previous_balance = genesis_state.get_account_by_address(&to).balance; + let tx = transfer_transaction_for_tests(from.clone(), from_key, 0, to.clone(), balance_to_move); + let _ = genesis_state.transition_from_public_transaction(tx); + assert_eq!(genesis_state.get_account_by_address(&to).balance, 108); + assert_eq!( + genesis_state.get_account_by_address(&from).balance, + balances[1] - balance_to_move + ); + assert_eq!(genesis_state.get_account_by_address(&from).nonce, 1); + assert_eq!(genesis_state.get_account_by_address(&to).nonce, 0); } } From ec909a1625f2b0819cf12115ad853e6c64847bb2 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Thu, 7 Aug 2025 15:19:06 -0300 Subject: [PATCH 04/58] adapt sequencer to use nssa --- Cargo.lock | 294 ++---------- Cargo.toml | 6 +- common/Cargo.toml | 3 + common/src/block.rs | 5 +- common/src/rpc_primitives/requests.rs | 6 +- nssa/Cargo.toml | 3 + nssa/core/src/account/mod.rs | 2 +- nssa/src/address.rs | 8 +- nssa/src/lib.rs | 14 +- nssa/src/public_transaction.rs | 28 +- nssa/src/signature.rs | 13 +- nssa/src/state.rs | 69 ++- sequencer_core/Cargo.toml | 3 + sequencer_core/src/config.rs | 2 +- sequencer_core/src/lib.rs | 452 +++++------------- sequencer_core/src/mempool_transaction.rs | 8 +- .../src/sequencer_store/block_store.rs | 38 +- sequencer_core/src/sequencer_store/mod.rs | 16 +- sequencer_rpc/Cargo.toml | 3 + sequencer_rpc/src/process.rs | 114 ++--- 20 files changed, 320 insertions(+), 767 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 07f1410..e78bd99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -147,7 +147,6 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" dependencies = [ - "actix-macros", "futures-core", "tokio", ] @@ -627,17 +626,6 @@ dependencies = [ "critical-section", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "auto_ops" version = "0.3.0" @@ -972,45 +960,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "clap" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" -dependencies = [ - "atty", - "bitflags 1.3.2", - "clap_derive", - "clap_lex", - "indexmap 1.9.3", - "once_cell", - "strsim 0.10.0", - "termcolor", - "textwrap", -] - -[[package]] -name = "clap_derive" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" -dependencies = [ - "heck 0.4.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - [[package]] name = "cobs" version = "0.3.0" @@ -1029,6 +978,7 @@ dependencies = [ "hex", "k256", "log", + "nssa", "reqwest 0.11.27", "risc0-zkvm 2.3.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "rs_merkle", @@ -1262,7 +1212,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.11.1", + "strsim", "syn 2.0.104", ] @@ -1952,13 +1902,19 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.10.0", + "indexmap", "slab", "tokio", "tokio-util", "tracing", ] +[[package]] +name = "half" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" + [[package]] name = "hash32" version = "0.2.1" @@ -2030,15 +1986,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.5.2" @@ -2365,16 +2312,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee796ad498c8d9a1d68e477df8f754ed784ef875de1414ebdaf169f70a6a784" -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - [[package]] name = "indexmap" version = "2.10.0" @@ -2394,32 +2331,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "integration_tests" -version = "0.1.0" -dependencies = [ - "accounts", - "actix", - "actix-web", - "anyhow", - "clap", - "common", - "env_logger", - "hex", - "log", - "node_core", - "node_rpc", - "node_runner", - "sequencer_core", - "sequencer_rpc", - "sequencer_runner", - "serde", - "serde_json", - "tempfile", - "tokio", - "toml 0.7.8", -] - [[package]] name = "inventory" version = "0.3.20" @@ -2462,7 +2373,7 @@ version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ - "hermit-abi 0.5.2", + "hermit-abi", "libc", "windows-sys 0.59.0", ] @@ -2973,88 +2884,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5b0c77c1b780822bc749a33e39aeb2c07584ab93332303babeabb645298a76e" -[[package]] -name = "node_core" -version = "0.1.0" -dependencies = [ - "accounts", - "actix-rt", - "anyhow", - "bincode", - "common", - "elliptic-curve", - "env_logger", - "hex", - "k256", - "log", - "rand 0.8.5", - "reqwest 0.11.27", - "risc0-zkvm 2.3.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", - "sc_core", - "secp256k1-zkp", - "serde", - "serde_json", - "sha2", - "storage", - "tempfile", - "thiserror 1.0.69", - "tokio", - "utxo", - "zkvm", -] - -[[package]] -name = "node_rpc" -version = "0.1.0" -dependencies = [ - "accounts", - "actix", - "actix-cors", - "actix-web", - "anyhow", - "common", - "consensus", - "env_logger", - "futures", - "hex", - "log", - "networking", - "node_core", - "serde", - "serde_json", - "storage", - "tokio", - "utxo", - "vm", - "zkvm", -] - -[[package]] -name = "node_runner" -version = "0.1.0" -dependencies = [ - "accounts", - "actix", - "actix-web", - "anyhow", - "clap", - "common", - "consensus", - "env_logger", - "hex", - "log", - "networking", - "node_core", - "node_rpc", - "serde", - "serde_json", - "storage", - "tokio", - "utxo", - "vm", - "zkvm", -] - [[package]] name = "nom" version = "7.1.3" @@ -3072,6 +2901,9 @@ dependencies = [ "nssa-core", "program-methods", "risc0-zkvm 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "serde_cbor", + "sha2", ] [[package]] @@ -3248,12 +3080,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "os_str_bytes" -version = "6.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" - [[package]] name = "parking_lot" version = "0.12.4" @@ -3412,7 +3238,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "toml_edit 0.22.27", + "toml_edit", ] [[package]] @@ -4510,7 +4336,7 @@ dependencies = [ "strum", "tempfile", "thiserror 2.0.12", - "toml 0.8.23", + "toml", "yaml-rust2", ] @@ -4524,7 +4350,7 @@ dependencies = [ "strum", "tempfile", "thiserror 2.0.12", - "toml 0.8.23", + "toml", "yaml-rust2", ] @@ -4672,6 +4498,7 @@ dependencies = [ "k256", "log", "mempool", + "nssa", "rand 0.8.5", "secp256k1-zkp", "serde", @@ -4698,6 +4525,7 @@ dependencies = [ "log", "mempool", "networking", + "nssa", "sequencer_core", "serde", "serde_json", @@ -4706,28 +4534,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "sequencer_runner" -version = "0.1.0" -dependencies = [ - "actix", - "actix-web", - "anyhow", - "clap", - "common", - "consensus", - "env_logger", - "log", - "mempool", - "networking", - "sequencer_core", - "sequencer_rpc", - "serde", - "serde_json", - "tokio", - "toml 0.7.8", -] - [[package]] name = "serde" version = "1.0.219" @@ -4737,6 +4543,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.219" @@ -4953,12 +4769,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -5097,12 +4907,6 @@ dependencies = [ "risc0-build 2.3.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", ] -[[package]] -name = "textwrap" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" - [[package]] name = "thiserror" version = "1.0.69" @@ -5260,18 +5064,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.15", -] - [[package]] name = "toml" version = "0.8.23" @@ -5281,7 +5073,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.27", + "toml_edit", ] [[package]] @@ -5293,31 +5085,18 @@ dependencies = [ "serde", ] -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.10.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.10.0", + "indexmap", "serde", "serde_spanned", "toml_datetime", "toml_write", - "winnow 0.7.12", + "winnow", ] [[package]] @@ -5974,15 +5753,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - [[package]] name = "winnow" version = "0.7.12" @@ -6175,7 +5945,7 @@ dependencies = [ "crossbeam-utils", "displaydoc", "flate2", - "indexmap 2.10.0", + "indexmap", "memchr", "thiserror 2.0.12", "zopfli", diff --git a/Cargo.toml b/Cargo.toml index a5cdd3a..1385130 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,19 @@ [workspace] resolver = "2" members = [ - "node_runner", - "sequencer_runner", + # "sequencer_runner", "storage", "accounts", "utxo", "vm", "networking", "consensus", - "node_rpc", "sequencer_rpc", "mempool", "zkvm", - "node_core", "sequencer_core", "common", "sc_core", - "integration_tests", "nssa", ] diff --git a/common/Cargo.toml b/common/Cargo.toml index 7e4e0ab..642d820 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -21,3 +21,6 @@ hex.workspace = true [dependencies.secp256k1-zkp] workspace = true features = ["std", "rand-std", "rand", "serde", "global-context"] + +[dependencies.nssa] +path = "../nssa" diff --git a/common/src/block.rs b/common/src/block.rs index 7757667..bc62dca 100644 --- a/common/src/block.rs +++ b/common/src/block.rs @@ -2,6 +2,7 @@ use rs_merkle::Hasher; use serde::{Deserialize, Serialize}; use crate::{merkle_tree_public::hasher::OwnHasher, transaction::Transaction}; +use nssa; pub type BlockHash = [u8; 32]; pub type Data = Vec; @@ -13,7 +14,7 @@ pub struct Block { pub prev_block_id: BlockId, pub prev_block_hash: BlockHash, pub hash: BlockHash, - pub transactions: Vec, + pub transactions: Vec, pub data: Data, } @@ -22,7 +23,7 @@ pub struct HashableBlockData { pub block_id: BlockId, pub prev_block_id: BlockId, pub prev_block_hash: BlockHash, - pub transactions: Vec, + pub transactions: Vec, pub data: Data, } diff --git a/common/src/rpc_primitives/requests.rs b/common/src/rpc_primitives/requests.rs index 29612c5..4a46162 100644 --- a/common/src/rpc_primitives/requests.rs +++ b/common/src/rpc_primitives/requests.rs @@ -18,7 +18,7 @@ pub struct RegisterAccountRequest { #[derive(Serialize, Deserialize, Debug)] pub struct SendTxRequest { - pub transaction: Transaction, + pub transaction: nssa::PublicTransaction, ///UTXO Commitment Root, Pub Tx Root pub tx_roots: [[u8; 32]; 2], } @@ -89,10 +89,10 @@ pub struct GetLastBlockResponse { #[derive(Serialize, Deserialize, Debug)] pub struct GetAccountBalanceResponse { - pub balance: u64, + pub balance: u128, } #[derive(Serialize, Deserialize, Debug)] pub struct GetTransactionByHashResponse { - pub transaction: Option, + pub transaction: Option, } diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index 243d3f1..30f0747 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -7,3 +7,6 @@ edition = "2024" risc0-zkvm = "2.2" nssa-core = {path="core"} program-methods = { path = "program_methods" } +serde = "1.0.219" +serde_cbor = "0.11.2" +sha2 = "0.10.9" diff --git a/nssa/core/src/account/mod.rs b/nssa/core/src/account/mod.rs index 1814cdd..5d4cdd9 100644 --- a/nssa/core/src/account/mod.rs +++ b/nssa/core/src/account/mod.rs @@ -11,7 +11,7 @@ pub type Nonce = u128; type Data = Vec; /// Account to be used both in public and private contexts -#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct Account { pub program_owner: ProgramId, pub balance: u128, diff --git a/nssa/src/address.rs b/nssa/src/address.rs index 57533a6..4267397 100644 --- a/nssa/src/address.rs +++ b/nssa/src/address.rs @@ -1,12 +1,14 @@ +use serde::{Deserialize, Serialize}; + use crate::signature::PublicKey; -#[derive(Clone, Hash, PartialEq, Eq)] -pub(crate) struct Address { +#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] +pub struct Address { pub(crate) value: [u8; 32], } impl Address { - pub(crate) fn new(value: [u8; 32]) -> Self { + pub fn new(value: [u8; 32]) -> Self { Self { value } } diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs index 4c2479f..81f48c3 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -1,16 +1,22 @@ use nssa_core::{ account::{Account, AccountWithMetadata}, - program::{Program, ProgramId}, + program::ProgramId, }; use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID}; use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor}; mod address; -mod public_transaction; +pub mod public_transaction; mod signature; -pub mod state; +mod state; -struct AuthenticatedTransferProgram; +pub use address::Address; +pub use nssa_core::program::Program; +pub use public_transaction::PublicTransaction; +pub use signature::PrivateKey; +pub use state::V01State; + +pub struct AuthenticatedTransferProgram; impl Program for AuthenticatedTransferProgram { const PROGRAM_ID: ProgramId = AUTHENTICATED_TRANSFER_ID; const PROGRAM_ELF: &[u8] = AUTHENTICATED_TRANSFER_ELF; diff --git a/nssa/src/public_transaction.rs b/nssa/src/public_transaction.rs index 1812bff..7c150db 100644 --- a/nssa/src/public_transaction.rs +++ b/nssa/src/public_transaction.rs @@ -2,13 +2,16 @@ use nssa_core::{ account::{Account, Nonce}, program::ProgramId, }; +use serde::{Deserialize, Serialize}; +use sha2::{digest::FixedOutput, Digest}; use crate::{ address::Address, signature::{PrivateKey, PublicKey, Signature}, }; -pub(crate) struct Message { +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct Message { pub(crate) program_id: ProgramId, pub(crate) addresses: Vec
, pub(crate) nonces: Vec, @@ -17,7 +20,7 @@ pub(crate) struct Message { } impl Message { - pub(crate) fn new( + pub fn new( program_id: ProgramId, addresses: Vec
, nonces: Vec, @@ -37,12 +40,13 @@ impl Message { } } -pub(crate) struct WitnessSet { +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct WitnessSet { pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>, } impl WitnessSet { - pub(crate) fn for_message(message: &Message, private_keys: &[PrivateKey]) -> Self { + pub fn for_message(message: &Message, private_keys: &[PrivateKey]) -> Self { let message_bytes = message.to_bytes(); let signatures_and_public_keys = private_keys .iter() @@ -54,17 +58,18 @@ impl WitnessSet { } } -pub(crate) struct PublicTransaction { +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct PublicTransaction { message: Message, witness_set: WitnessSet, } impl PublicTransaction { - pub(crate) fn message(&self) -> &Message { + pub fn message(&self) -> &Message { &self.message } - pub(crate) fn witness_set(&self) -> &WitnessSet { + pub fn witness_set(&self) -> &WitnessSet { &self.witness_set } @@ -76,10 +81,17 @@ impl PublicTransaction { .collect() } - pub(crate) fn new(message: Message, witness_set: WitnessSet) -> Self { + pub fn new(message: Message, witness_set: WitnessSet) -> Self { Self { message, witness_set, } } + + pub fn hash(&self) -> [u8; 32] { + let bytes = serde_cbor::to_vec(&self).unwrap(); + let mut hasher = sha2::Sha256::new(); + hasher.update(&bytes); + hasher.finalize_fixed().try_into().unwrap() + } } diff --git a/nssa/src/signature.rs b/nssa/src/signature.rs index ea90b56..2f018f1 100644 --- a/nssa/src/signature.rs +++ b/nssa/src/signature.rs @@ -1,10 +1,21 @@ +use serde::{Deserialize, Serialize}; + use crate::{address::Address, public_transaction::Message}; +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub(crate) struct Signature; // TODO: Dummy impl. Replace by actual private key. -pub(crate) struct PrivateKey(pub(crate) u8); +pub struct PrivateKey(pub(crate) u8); + +impl PrivateKey { + pub fn new(dummy_value: u8) -> Self { + Self(dummy_value) + } +} + // TODO: Dummy impl. Replace by actual public key. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub(crate) struct PublicKey(pub(crate) u8); impl PublicKey { diff --git a/nssa/src/state.rs b/nssa/src/state.rs index a636859..2a09c10 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -8,12 +8,28 @@ use nssa_core::{ }; use std::collections::{HashMap, HashSet}; -struct V01State { +pub struct V01State { public_state: HashMap, } impl V01State { - fn transition_from_public_transaction(&mut self, tx: PublicTransaction) -> Result<(), ()> { + pub fn new_with_genesis_accounts(initial_data: &[([u8; 32], u128)]) -> Self { + // TODO:: remove this assert? + let public_state = initial_data + .to_owned() + .into_iter() + .map(|(address_value, balance)| { + let mut account = Account::default(); + account.balance = balance; + account.program_owner = AuthenticatedTransferProgram::PROGRAM_ID; + let address = Address::new(address_value); + (address, account) + }) + .collect(); + Self { public_state } + } + + pub fn transition_from_public_transaction(&mut self, tx: &PublicTransaction) -> Result<(), ()> { let state_diff = self .execute_and_verify_public_transaction(&tx) .map_err(|_| ())?; @@ -36,7 +52,7 @@ impl V01State { .or_insert_with(Account::default) } - fn get_account_by_address(&self, address: &Address) -> Account { + pub fn get_account_by_address(&self, address: &Address) -> Account { self.public_state .get(address) .cloned() @@ -123,26 +139,6 @@ mod tests { use super::*; use crate::{public_transaction, signature::PrivateKey}; - fn genesis_state_for_tests(balances: &[u128], addresses: &[Address]) -> V01State { - assert_eq!(balances.len(), addresses.len()); - let accounts: Vec = balances - .iter() - .map(|balance| { - let mut account = Account::default(); - account.balance = *balance; - account.program_owner = AuthenticatedTransferProgram::PROGRAM_ID; - account - }) - .collect(); - - let public_state = addresses - .to_owned() - .into_iter() - .zip(accounts.into_iter()) - .collect(); - V01State { public_state } - } - fn transfer_transaction_for_tests( from: Address, from_key: PrivateKey, @@ -160,23 +156,22 @@ mod tests { #[test] fn test_1() { - let addresses = [Address::new([1; 32])]; - let balances = [100]; - let mut genesis_state = genesis_state_for_tests(&balances, &addresses); - let from = addresses[0].clone(); + let initial_data = [([1; 32], 100)]; + let mut genesis_state = V01State::new_with_genesis_accounts(&initial_data); + let from = Address::new(initial_data[0].0.clone()); let from_key = PrivateKey(1); let to = Address::new([2; 32]); let balance_to_move = 5; let tx = transfer_transaction_for_tests(from.clone(), from_key, 0, to.clone(), balance_to_move); - let _ = genesis_state.transition_from_public_transaction(tx); + let _ = genesis_state.transition_from_public_transaction(&tx); assert_eq!( genesis_state.get_account_by_address(&to).balance, balance_to_move ); assert_eq!( genesis_state.get_account_by_address(&from).balance, - balances[0] - balance_to_move + initial_data[0].1 - balance_to_move ); assert_eq!(genesis_state.get_account_by_address(&from).nonce, 1); assert_eq!(genesis_state.get_account_by_address(&to).nonce, 0); @@ -184,20 +179,20 @@ mod tests { #[test] fn test_2() { - let addresses = [Address::new([1; 32]), Address::new([99; 32])]; - let balances = [100, 200]; - let mut genesis_state = genesis_state_for_tests(&balances, &addresses); - let from = addresses[1].clone(); + let initial_data = [([1; 32], 100), ([99; 32], 200)]; + let mut genesis_state = V01State::new_with_genesis_accounts(&initial_data); + let from = Address::new(initial_data[1].0.clone()); let from_key = PrivateKey(99); - let to = addresses[0].clone(); + let to = Address::new(initial_data[0].0.clone()); let balance_to_move = 8; let to_previous_balance = genesis_state.get_account_by_address(&to).balance; - let tx = transfer_transaction_for_tests(from.clone(), from_key, 0, to.clone(), balance_to_move); - let _ = genesis_state.transition_from_public_transaction(tx); + let tx = + transfer_transaction_for_tests(from.clone(), from_key, 0, to.clone(), balance_to_move); + let _ = genesis_state.transition_from_public_transaction(&tx); assert_eq!(genesis_state.get_account_by_address(&to).balance, 108); assert_eq!( genesis_state.get_account_by_address(&from).balance, - balances[1] - balance_to_move + initial_data[1].1 - balance_to_move ); assert_eq!(genesis_state.get_account_by_address(&from).nonce, 1); assert_eq!(genesis_state.get_account_by_address(&to).nonce, 0); diff --git a/sequencer_core/Cargo.toml b/sequencer_core/Cargo.toml index f148655..2083f42 100644 --- a/sequencer_core/Cargo.toml +++ b/sequencer_core/Cargo.toml @@ -28,6 +28,9 @@ path = "../accounts" [dependencies.common] path = "../common" +[dependencies.nssa] +path = "../nssa" + [dependencies.secp256k1-zkp] workspace = true features = ["std", "rand-std", "rand", "serde", "global-context"] diff --git a/sequencer_core/src/config.rs b/sequencer_core/src/config.rs index 2dda687..4a99f0b 100644 --- a/sequencer_core/src/config.rs +++ b/sequencer_core/src/config.rs @@ -6,7 +6,7 @@ use std::path::PathBuf; pub struct AccountInitialData { ///Hex encoded `AccountAddress` pub addr: String, - pub balance: u64, + pub balance: u128, } #[derive(Clone, Serialize, Deserialize)] diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 80ac760..1275bb2 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -34,7 +34,7 @@ pub enum TransactionMalformationErrorKind { TxHashAlreadyPresentInTree { tx: TreeHashType }, NullifierAlreadyPresentInTree { tx: TreeHashType }, UTXOCommitmentAlreadyPresentInTree { tx: TreeHashType }, - MempoolFullForRound { tx: TreeHashType }, + MempoolFullForRound, ChainStateFurtherThanTransactionState { tx: TreeHashType }, FailedToInsert { tx: TreeHashType, details: String }, InvalidSignature, @@ -67,146 +67,36 @@ impl SequencerCore { } } - pub fn get_tree_roots(&self) -> [[u8; 32]; 2] { - [ - self.store - .utxo_commitments_store - .get_root() - .unwrap_or([0; 32]), - self.store.pub_tx_store.get_root().unwrap_or([0; 32]), - ] - } + // pub fn get_tree_roots(&self) -> [[u8; 32]; 2] { + // [ + // self.store + // .utxo_commitments_store + // .get_root() + // .unwrap_or([0; 32]), + // self.store.pub_tx_store.get_root().unwrap_or([0; 32]), + // ] + // } pub fn transaction_pre_check( &mut self, - tx: Transaction, - tx_roots: [[u8; 32]; 2], - ) -> Result { - let tx = tx - .into_authenticated() - .map_err(|_| TransactionMalformationErrorKind::InvalidSignature)?; - - let TransactionBody { - tx_kind, - ref execution_input, - ref execution_output, - ref utxo_commitments_created_hashes, - ref nullifier_created_hashes, - .. - } = tx.transaction().body(); - - let tx_hash = *tx.hash(); - - let mempool_size = self.mempool.len(); - - if mempool_size >= self.sequencer_config.max_num_tx_in_block { - return Err(TransactionMalformationErrorKind::MempoolFullForRound { tx: tx_hash }); - } - - let curr_sequencer_roots = self.get_tree_roots(); - - if tx_roots != curr_sequencer_roots { - return Err( - TransactionMalformationErrorKind::ChainStateFurtherThanTransactionState { - tx: tx_hash, - }, - ); - } - - //Sanity check - match tx_kind { - TxKind::Public => { - if !utxo_commitments_created_hashes.is_empty() - || !nullifier_created_hashes.is_empty() - { - //Public transactions can not make private operations. - return Err( - TransactionMalformationErrorKind::PublicTransactionChangedPrivateData { - tx: tx_hash, - }, - ); - } - } - TxKind::Private => { - if !execution_input.is_empty() || !execution_output.is_empty() { - //Not entirely necessary, but useful simplification for a future. - //This way only shielded and deshielded transactions can be used for interaction - //between public and private state. - return Err( - TransactionMalformationErrorKind::PrivateTransactionChangedPublicData { - tx: tx_hash, - }, - ); - } - } - _ => {} - }; - - //Native transfers checks - if let Ok(native_transfer_action) = - serde_json::from_slice::(execution_input) - { - let signer_address = address::from_public_key(&tx.transaction().public_key); - - //Correct sender check - if native_transfer_action.from != signer_address { - return Err(TransactionMalformationErrorKind::IncorrectSender); - } - } - - //Tree checks - let tx_tree_check = self.store.pub_tx_store.get_tx(tx_hash).is_some(); - let nullifier_tree_check = nullifier_created_hashes.iter().any(|nullifier_hash| { - self.store.nullifier_store.contains(&UTXONullifier { - utxo_hash: *nullifier_hash, - }) - }); - let utxo_commitments_check = - utxo_commitments_created_hashes - .iter() - .any(|utxo_commitment_hash| { - self.store - .utxo_commitments_store - .get_tx(*utxo_commitment_hash) - .is_some() - }); - - if tx_tree_check { - return Err( - TransactionMalformationErrorKind::TxHashAlreadyPresentInTree { tx: *tx.hash() }, - ); - } - - if nullifier_tree_check { - return Err( - TransactionMalformationErrorKind::NullifierAlreadyPresentInTree { tx: *tx.hash() }, - ); - } - - if utxo_commitments_check { - return Err( - TransactionMalformationErrorKind::UTXOCommitmentAlreadyPresentInTree { - tx: *tx.hash(), - }, - ); - } - + tx: nssa::PublicTransaction, + // tx_roots: [[u8; 32]; 2], + ) -> Result { + // TODO: Stateless checks here Ok(tx) } pub fn push_tx_into_mempool_pre_check( &mut self, - transaction: Transaction, - tx_roots: [[u8; 32]; 2], + transaction: nssa::PublicTransaction, + // _tx_roots: [[u8; 32]; 2], ) -> Result<(), TransactionMalformationErrorKind> { let mempool_size = self.mempool.len(); if mempool_size >= self.sequencer_config.max_num_tx_in_block { - return Err(TransactionMalformationErrorKind::MempoolFullForRound { - tx: transaction.body().hash(), - }); + return Err(TransactionMalformationErrorKind::MempoolFullForRound); } - let authenticated_tx = self.transaction_pre_check(transaction, tx_roots)?; + let authenticated_tx = self.transaction_pre_check(transaction)?; self.mempool.push_item(authenticated_tx.into()); @@ -215,77 +105,15 @@ impl SequencerCore { fn execute_check_transaction_on_state( &mut self, - mempool_tx: &MempoolTransaction, - ) -> Result<(), TransactionMalformationErrorKind> { - let TransactionBody { - ref utxo_commitments_created_hashes, - ref nullifier_created_hashes, - execution_input, - .. - } = mempool_tx.auth_tx.transaction().body(); + mempool_tx: MempoolTransaction, + ) -> Result { + let tx = mempool_tx.auth_tx; - let tx_hash = *mempool_tx.auth_tx.hash(); + self.store.state.transition_from_public_transaction(&tx)?; - //Balance move - if let Ok(native_transfer_action) = - serde_json::from_slice::(execution_input) - { - // Nonce check - let signer_addres = - address::from_public_key(&mempool_tx.auth_tx.transaction().public_key); - if self.store.acc_store.get_account_nonce(&signer_addres) - != native_transfer_action.nonce - { - return Err(TransactionMalformationErrorKind::NonceMismatch { tx: tx_hash }); - } + // self.store.pub_tx_store.add_tx(mempool_tx.auth_tx); - let from_balance = self - .store - .acc_store - .get_account_balance(&native_transfer_action.from); - let to_balance = self - .store - .acc_store - .get_account_balance(&native_transfer_action.to); - - //Balance check - if from_balance < native_transfer_action.balance_to_move { - return Err(TransactionMalformationErrorKind::BalanceMismatch { tx: tx_hash }); - } - - self.store.acc_store.set_account_balance( - &native_transfer_action.from, - from_balance - native_transfer_action.balance_to_move, - ); - self.store.acc_store.set_account_balance( - &native_transfer_action.to, - to_balance + native_transfer_action.balance_to_move, - ); - - self.store.acc_store.increase_nonce(&signer_addres); - } - - for utxo_comm in utxo_commitments_created_hashes { - self.store - .utxo_commitments_store - .add_tx(&UTXOCommitment { hash: *utxo_comm }); - } - - for nullifier in nullifier_created_hashes.iter() { - self.store.nullifier_store.insert(UTXONullifier { - utxo_hash: *nullifier, - }); - } - - self.store - .pub_tx_store - .add_tx(mempool_tx.auth_tx.transaction()); - - Ok(()) - } - - pub fn register_account(&mut self, account_addr: AccountAddress) { - self.store.acc_store.register_account(account_addr); + Ok(tx) } ///Produces new block from transactions in mempool @@ -298,13 +126,7 @@ impl SequencerCore { let valid_transactions = transactions .into_iter() - .filter_map(|mempool_tx| { - if self.execute_check_transaction_on_state(&mempool_tx).is_ok() { - Some(mempool_tx.auth_tx.into_transaction()) - } else { - None - } - }) + .filter_map(|mempool_tx| self.execute_check_transaction_on_state(mempool_tx).ok()) .collect(); let prev_block_hash = self @@ -340,6 +162,7 @@ mod tests { use common::transaction::{SignaturePrivateKey, Transaction, TransactionBody, TxKind}; use k256::{ecdsa::SigningKey, FieldBytes}; use mempool_transaction::MempoolTransaction; + use nssa::Program; use secp256k1_zkp::Tweak; fn setup_sequencer_config_variable_initial_accounts( @@ -362,13 +185,15 @@ mod tests { fn setup_sequencer_config() -> SequencerConfig { let acc1_addr = vec![ - 13, 150, 223, 204, 65, 64, 25, 56, 12, 157, 222, 12, 211, 220, 229, 170, 201, 15, 181, - 68, 59, 248, 113, 16, 135, 65, 174, 175, 222, 85, 42, 215, + // 13, 150, 223, 204, 65, 64, 25, 56, 12, 157, 222, 12, 211, 220, 229, 170, 201, 15, 181, + // 68, 59, 248, 113, 16, 135, 65, 174, 175, 222, 85, 42, 215, + 1; 32 ]; let acc2_addr = vec![ - 151, 72, 112, 233, 190, 141, 10, 192, 138, 168, 59, 63, 199, 167, 166, 134, 41, 29, - 135, 50, 80, 138, 186, 152, 179, 96, 128, 243, 156, 44, 243, 100, + // 151, 72, 112, 233, 190, 141, 10, 192, 138, 168, 59, 63, 199, 167, 166, 134, 41, 29, + // 135, 50, 80, 138, 186, 152, 179, 96, 128, 243, 156, 44, 243, 100, + 2; 32 ]; let initial_acc1 = AccountInitialData { @@ -386,92 +211,61 @@ mod tests { setup_sequencer_config_variable_initial_accounts(initial_accounts) } - fn create_dummy_transaction( - nullifier_created_hashes: Vec<[u8; 32]>, - utxo_commitments_spent_hashes: Vec<[u8; 32]>, - utxo_commitments_created_hashes: Vec<[u8; 32]>, - ) -> Transaction { - let mut rng = rand::thread_rng(); - - let body = TransactionBody { - tx_kind: TxKind::Private, - execution_input: vec![], - execution_output: vec![], - utxo_commitments_spent_hashes, - utxo_commitments_created_hashes, - nullifier_created_hashes, - execution_proof_private: "dummy_proof".to_string(), - encoded_data: vec![], - ephemeral_pub_key: vec![10, 11, 12], - commitment: vec![], - tweak: Tweak::new(&mut rng), - secret_r: [0; 32], - sc_addr: "sc_addr".to_string(), - state_changes: (serde_json::Value::Null, 0), - }; - Transaction::new(body, SignaturePrivateKey::random(&mut rng)) + fn create_dummy_transaction() -> nssa::PublicTransaction { + let program_id = nssa::AuthenticatedTransferProgram::PROGRAM_ID; + let addresses = vec![]; + let nonces = vec![]; + let instruction_data = 0; + let message = + nssa::public_transaction::Message::new(program_id, addresses, nonces, instruction_data); + let private_key = nssa::PrivateKey::new(1); + let witness_set = + nssa::public_transaction::WitnessSet::for_message(&message, &[private_key]); + nssa::PublicTransaction::new(message, witness_set) } fn create_dummy_transaction_native_token_transfer( from: [u8; 32], - nonce: u64, + nonce: u128, to: [u8; 32], - balance_to_move: u64, - signing_key: SigningKey, - ) -> Transaction { - let mut rng = rand::thread_rng(); - - let native_token_transfer = PublicNativeTokenSend { - from, - nonce, - to, - balance_to_move, - }; - - let body = TransactionBody { - tx_kind: TxKind::Public, - execution_input: serde_json::to_vec(&native_token_transfer).unwrap(), - execution_output: vec![], - utxo_commitments_spent_hashes: vec![], - utxo_commitments_created_hashes: vec![], - nullifier_created_hashes: vec![], - execution_proof_private: "".to_string(), - encoded_data: vec![], - ephemeral_pub_key: vec![10, 11, 12], - commitment: vec![], - tweak: Tweak::new(&mut rng), - secret_r: [0; 32], - sc_addr: "sc_addr".to_string(), - state_changes: (serde_json::Value::Null, 0), - }; - Transaction::new(body, signing_key) + balance_to_move: u128, + signing_key: nssa::PrivateKey, + ) -> nssa::PublicTransaction { + let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)]; + let nonces = vec![nonce]; + let program_id = nssa::AuthenticatedTransferProgram::PROGRAM_ID; + let message = + nssa::public_transaction::Message::new(program_id, addresses, nonces, balance_to_move); + let witness_set = + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); + nssa::PublicTransaction::new(message, witness_set) } - fn create_signing_key_for_account1() -> SigningKey { - let pub_sign_key_acc1 = [ - 133, 143, 177, 187, 252, 66, 237, 236, 234, 252, 244, 138, 5, 151, 3, 99, 217, 231, - 112, 217, 77, 211, 58, 218, 176, 68, 99, 53, 152, 228, 198, 190, - ]; - - let field_bytes = FieldBytes::from_slice(&pub_sign_key_acc1); - SigningKey::from_bytes(field_bytes).unwrap() + fn create_signing_key_for_account1() -> nssa::PrivateKey { + // let pub_sign_key_acc1 = [ + // 133, 143, 177, 187, 252, 66, 237, 236, 234, 252, 244, 138, 5, 151, 3, 99, 217, 231, + // 112, 217, 77, 211, 58, 218, 176, 68, 99, 53, 152, 228, 198, 190, + // ]; + // + // let field_bytes = FieldBytes::from_slice(&pub_sign_key_acc1); + // SigningKey::from_bytes(field_bytes).unwrap() + nssa::PrivateKey::new(1) } - fn create_signing_key_for_account2() -> SigningKey { - let pub_sign_key_acc2 = [ - 54, 90, 62, 225, 71, 225, 228, 148, 143, 53, 210, 23, 137, 158, 171, 156, 48, 7, 139, - 52, 117, 242, 214, 7, 99, 29, 122, 184, 59, 116, 144, 107, - ]; - - let field_bytes = FieldBytes::from_slice(&pub_sign_key_acc2); - SigningKey::from_bytes(field_bytes).unwrap() + fn create_signing_key_for_account2() -> nssa::PrivateKey { + // let pub_sign_key_acc2 = [ + // 54, 90, 62, 225, 71, 225, 228, 148, 143, 53, 210, 23, 137, 158, 171, 156, 48, 7, 139, + // 52, 117, 242, 214, 7, 99, 29, 122, 184, 59, 116, 144, 107, + // ]; + // + // let field_bytes = FieldBytes::from_slice(&pub_sign_key_acc2); + // SigningKey::from_bytes(field_bytes).unwrap() + nssa::PrivateKey::new(2) } fn common_setup(sequencer: &mut SequencerCore) { - let tx = create_dummy_transaction(vec![[9; 32]], vec![[7; 32]], vec![[8; 32]]); - let mempool_tx = MempoolTransaction { - auth_tx: tx.into_authenticated().unwrap(), - }; + let tx = create_dummy_transaction(); + let mempool_tx = MempoolTransaction { auth_tx: tx }; sequencer.mempool.push_item(mempool_tx); sequencer @@ -497,16 +291,16 @@ mod tests { .try_into() .unwrap(); - assert!(sequencer.store.acc_store.contains_account(&acc1_addr)); - assert!(sequencer.store.acc_store.contains_account(&acc2_addr)); + let balance_acc_1 = sequencer.store.state.get_account_by_address(&nssa::Address::new(acc1_addr)).balance; + let balance_acc_2 = sequencer.store.state.get_account_by_address(&nssa::Address::new(acc2_addr)).balance; assert_eq!( 10000, - sequencer.store.acc_store.get_account_balance(&acc1_addr) + balance_acc_1 ); assert_eq!( 20000, - sequencer.store.acc_store.get_account_balance(&acc2_addr) + balance_acc_2 ); } @@ -548,32 +342,16 @@ mod tests { .try_into() .unwrap(); - assert!(sequencer.store.acc_store.contains_account(&acc1_addr)); - assert!(sequencer.store.acc_store.contains_account(&acc2_addr)); - - assert_eq!(sequencer.store.acc_store.len(), intial_accounts_len); - assert_eq!( 10000, - sequencer.store.acc_store.get_account_balance(&acc1_addr) + sequencer.store.state.get_account_by_address(&nssa::Address::new(acc1_addr)).balance ); assert_eq!( 20000, - sequencer.store.acc_store.get_account_balance(&acc2_addr) + sequencer.store.state.get_account_by_address(&nssa::Address::new(acc2_addr)).balance ); } - #[test] - fn test_get_tree_roots() { - let config = setup_sequencer_config(); - let mut sequencer = SequencerCore::start_from_config(config); - - common_setup(&mut sequencer); - - let roots = sequencer.get_tree_roots(); - assert_eq!(roots.len(), 2); // Should return two roots - } - #[test] fn test_transaction_pre_check_pass() { let config = setup_sequencer_config(); @@ -581,9 +359,9 @@ mod tests { common_setup(&mut sequencer); - let tx = create_dummy_transaction(vec![[91; 32]], vec![[71; 32]], vec![[81; 32]]); - let tx_roots = sequencer.get_tree_roots(); - let result = sequencer.transaction_pre_check(tx, tx_roots); + let tx = create_dummy_transaction(); + // let tx_roots = sequencer.get_tree_roots(); + let result = sequencer.transaction_pre_check(tx); assert!(result.is_ok()); } @@ -607,8 +385,7 @@ mod tests { let sign_key1 = create_signing_key_for_account1(); let tx = create_dummy_transaction_native_token_transfer(acc1, 0, acc2, 10, sign_key1); - let tx_roots = sequencer.get_tree_roots(); - let result = sequencer.transaction_pre_check(tx, tx_roots); + let result = sequencer.transaction_pre_check(tx); assert!(result.is_ok()); } @@ -632,13 +409,13 @@ mod tests { let sign_key2 = create_signing_key_for_account2(); let tx = create_dummy_transaction_native_token_transfer(acc1, 0, acc2, 10, sign_key2); - let tx_roots = sequencer.get_tree_roots(); - let result = sequencer.transaction_pre_check(tx, tx_roots); + // let tx_roots = sequencer.get_tree_roots(); + let tx = sequencer.transaction_pre_check(tx).unwrap(); - assert_eq!( - result.err().unwrap(), - TransactionMalformationErrorKind::IncorrectSender - ); + let result = + sequencer.execute_check_transaction_on_state(MempoolTransaction { auth_tx: tx }); + + assert_eq!(result.err().unwrap(), ()); } #[test] @@ -660,16 +437,19 @@ mod tests { let sign_key1 = create_signing_key_for_account1(); let tx = create_dummy_transaction_native_token_transfer(acc1, 0, acc2, 10000000, sign_key1); - let tx_roots = sequencer.get_tree_roots(); - let result = sequencer.transaction_pre_check(tx, tx_roots); + // let tx_roots = sequencer.get_tree_roots(); + let result = sequencer.transaction_pre_check(tx); //Passed pre-check assert!(result.is_ok()); - let result = sequencer.execute_check_transaction_on_state(&result.unwrap().into()); + let result = sequencer.execute_check_transaction_on_state(MempoolTransaction { + auth_tx: result.unwrap(), + }); let is_failed_at_balance_mismatch = matches!( result.err().unwrap(), - TransactionMalformationErrorKind::BalanceMismatch { tx: _ } + // TransactionMalformationErrorKind::BalanceMismatch { tx: _ } + () ); assert!(is_failed_at_balance_mismatch); @@ -696,11 +476,11 @@ mod tests { let tx = create_dummy_transaction_native_token_transfer(acc1, 0, acc2, 100, sign_key1); sequencer - .execute_check_transaction_on_state(&tx.into_authenticated().unwrap().into()) + .execute_check_transaction_on_state(MempoolTransaction { auth_tx: tx }) .unwrap(); - let bal_from = sequencer.store.acc_store.get_account_balance(&acc1); - let bal_to = sequencer.store.acc_store.get_account_balance(&acc2); + let bal_from = sequencer.store.state.get_account_by_address(&nssa::Address::new(acc1)).balance; + let bal_to = sequencer.store.state.get_account_by_address(&nssa::Address::new(acc2)).balance; assert_eq!(bal_from, 9900); assert_eq!(bal_to, 20100); @@ -716,16 +496,16 @@ mod tests { common_setup(&mut sequencer); - let tx = create_dummy_transaction(vec![[92; 32]], vec![[72; 32]], vec![[82; 32]]); - let tx_roots = sequencer.get_tree_roots(); + let tx = create_dummy_transaction(); + // let tx_roots = sequencer.get_tree_roots(); // Fill the mempool let dummy_tx = MempoolTransaction { - auth_tx: tx.clone().into_authenticated().unwrap(), + auth_tx: tx.clone(), }; sequencer.mempool.push_item(dummy_tx); - let result = sequencer.push_tx_into_mempool_pre_check(tx, tx_roots); + let result = sequencer.push_tx_into_mempool_pre_check(tx); assert!(matches!( result, @@ -740,10 +520,10 @@ mod tests { common_setup(&mut sequencer); - let tx = create_dummy_transaction(vec![[93; 32]], vec![[73; 32]], vec![[83; 32]]); - let tx_roots = sequencer.get_tree_roots(); + let tx = create_dummy_transaction(); + // let tx_roots = sequencer.get_tree_roots(); - let result = sequencer.push_tx_into_mempool_pre_check(tx, tx_roots); + let result = sequencer.push_tx_into_mempool_pre_check(tx); assert!(result.is_ok()); assert_eq!(sequencer.mempool.len(), 1); } @@ -754,10 +534,8 @@ mod tests { let mut sequencer = SequencerCore::start_from_config(config); let genesis_height = sequencer.chain_height; - let tx = create_dummy_transaction(vec![[94; 32]], vec![[7; 32]], vec![[8; 32]]); - let tx_mempool = MempoolTransaction { - auth_tx: tx.into_authenticated().unwrap(), - }; + let tx = create_dummy_transaction(); + let tx_mempool = MempoolTransaction { auth_tx: tx }; sequencer.mempool.push_item(tx_mempool); let block_id = sequencer.produce_new_block_with_mempool_transactions(); @@ -786,10 +564,10 @@ mod tests { let tx = create_dummy_transaction_native_token_transfer(acc1, 0, acc2, 100, sign_key1); let tx_mempool_original = MempoolTransaction { - auth_tx: tx.clone().into_authenticated().unwrap(), + auth_tx: tx.clone(), }; let tx_mempool_replay = MempoolTransaction { - auth_tx: tx.clone().into_authenticated().unwrap(), + auth_tx: tx.clone(), }; // Pushing two copies of the same tx to the mempool @@ -832,7 +610,7 @@ mod tests { // The transaction should be included the first time let tx_mempool_original = MempoolTransaction { - auth_tx: tx.clone().into_authenticated().unwrap(), + auth_tx: tx.clone(), }; sequencer.mempool.push_item(tx_mempool_original); let current_height = sequencer @@ -846,9 +624,7 @@ mod tests { assert_eq!(block.transactions, vec![tx.clone()]); // Add same transaction should fail - let tx_mempool_replay = MempoolTransaction { - auth_tx: tx.into_authenticated().unwrap(), - }; + let tx_mempool_replay = MempoolTransaction { auth_tx: tx }; sequencer.mempool.push_item(tx_mempool_replay); let current_height = sequencer .produce_new_block_with_mempool_transactions() diff --git a/sequencer_core/src/mempool_transaction.rs b/sequencer_core/src/mempool_transaction.rs index 551aaf3..1ba1a2b 100644 --- a/sequencer_core/src/mempool_transaction.rs +++ b/sequencer_core/src/mempool_transaction.rs @@ -2,11 +2,11 @@ use common::{merkle_tree_public::TreeHashType, transaction::AuthenticatedTransac use mempool::mempoolitem::MemPoolItem; pub struct MempoolTransaction { - pub auth_tx: AuthenticatedTransaction, + pub auth_tx: nssa::PublicTransaction, } -impl From for MempoolTransaction { - fn from(auth_tx: AuthenticatedTransaction) -> Self { +impl From for MempoolTransaction { + fn from(auth_tx: nssa::PublicTransaction) -> Self { Self { auth_tx } } } @@ -15,6 +15,6 @@ impl MemPoolItem for MempoolTransaction { type Identifier = TreeHashType; fn identifier(&self) -> Self::Identifier { - *self.auth_tx.hash() + self.auth_tx.hash() } } diff --git a/sequencer_core/src/sequencer_store/block_store.rs b/sequencer_core/src/sequencer_store/block_store.rs index dbefdc2..88afed5 100644 --- a/sequencer_core/src/sequencer_store/block_store.rs +++ b/sequencer_core/src/sequencer_store/block_store.rs @@ -51,12 +51,12 @@ impl SequecerBlockStore { } /// Returns the transaction corresponding to the given hash, if it exists in the blockchain. - pub fn get_transaction_by_hash(&self, hash: TreeHashType) -> Option { + pub fn get_transaction_by_hash(&self, hash: TreeHashType) -> Option { let block_id = self.tx_hash_to_block_map.get(&hash); let block = block_id.map(|&id| self.get_block_at_id(id)); if let Some(Ok(block)) = block { for transaction in block.transactions.into_iter() { - if transaction.body().hash() == hash { + if transaction.hash() == hash { return Some(transaction); } } @@ -69,7 +69,7 @@ fn block_to_transactions_map(block: &Block) -> HashMap { block .transactions .iter() - .map(|transaction| (transaction.body().hash(), block.block_id)) + .map(|transaction| (transaction.hash(), block.block_id)) .collect() } @@ -77,26 +77,18 @@ fn block_to_transactions_map(block: &Block) -> HashMap { mod tests { use super::*; use common::transaction::{SignaturePrivateKey, TransactionBody}; + use nssa::Program; use tempfile::tempdir; - fn create_dummy_block_with_transaction(block_id: u64) -> (Block, Transaction) { - let body = TransactionBody { - tx_kind: common::transaction::TxKind::Public, - execution_input: Default::default(), - execution_output: Default::default(), - utxo_commitments_spent_hashes: Default::default(), - utxo_commitments_created_hashes: Default::default(), - nullifier_created_hashes: Default::default(), - execution_proof_private: Default::default(), - encoded_data: Default::default(), - ephemeral_pub_key: Default::default(), - commitment: Default::default(), - tweak: Default::default(), - secret_r: Default::default(), - sc_addr: Default::default(), - state_changes: Default::default(), - }; - let tx = Transaction::new(body, SignaturePrivateKey::from_slice(&[1; 32]).unwrap()); + fn create_dummy_block_with_transaction(block_id: u64) -> (Block, nssa::PublicTransaction) { + let program_id = nssa::AuthenticatedTransferProgram::PROGRAM_ID; + let addresses = vec![]; + let nonces = vec![]; + let instruction_data = 0; + let message = nssa::public_transaction::Message::new(program_id, addresses, nonces, instruction_data); + let private_key = nssa::PrivateKey::new(1); + let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[private_key]); + let tx = nssa::PublicTransaction::new(message, witness_set); ( Block { block_id, @@ -127,12 +119,12 @@ mod tests { SequecerBlockStore::open_db_with_genesis(path, Some(genesis_block)).unwrap(); let (block, tx) = create_dummy_block_with_transaction(1); // Try retrieve a tx that's not in the chain yet. - let retrieved_tx = node_store.get_transaction_by_hash(tx.body().hash()); + let retrieved_tx = node_store.get_transaction_by_hash(tx.hash()); assert_eq!(None, retrieved_tx); // Add the block with the transaction node_store.put_block_at_id(block).unwrap(); // Try again - let retrieved_tx = node_store.get_transaction_by_hash(tx.body().hash()); + let retrieved_tx = node_store.get_transaction_by_hash(tx.hash()); assert_eq!(Some(tx), retrieved_tx); } } diff --git a/sequencer_core/src/sequencer_store/mod.rs b/sequencer_core/src/sequencer_store/mod.rs index a85fd23..f329591 100644 --- a/sequencer_core/src/sequencer_store/mod.rs +++ b/sequencer_core/src/sequencer_store/mod.rs @@ -1,5 +1,6 @@ use std::{collections::HashSet, path::Path}; +use accounts::account_core::address::AccountAddress; use accounts_store::SequencerAccountsStore; use block_store::SequecerBlockStore; use common::{ @@ -8,6 +9,7 @@ use common::{ nullifier::UTXONullifier, }; use rand::{rngs::OsRng, RngCore}; +use nssa; use crate::config::AccountInitialData; @@ -15,11 +17,11 @@ pub mod accounts_store; pub mod block_store; pub struct SequecerChainStore { - pub acc_store: SequencerAccountsStore, + pub state: nssa::V01State, pub block_store: SequecerBlockStore, pub nullifier_store: HashSet, pub utxo_commitments_store: UTXOCommitmentsMerkleTree, - pub pub_tx_store: PublicTransactionMerkleTree, + // pub pub_tx_store: PublicTransactionMerkleTree, } impl SequecerChainStore { @@ -29,7 +31,7 @@ impl SequecerChainStore { is_genesis_random: bool, initial_accounts: &[AccountInitialData], ) -> Self { - let init_accs: Vec<_> = initial_accounts + let init_accs: Vec<(AccountAddress, u128)> = initial_accounts .iter() .map(|acc_data| { ( @@ -42,10 +44,10 @@ impl SequecerChainStore { }) .collect(); - let acc_store = SequencerAccountsStore::new(&init_accs); + let state = nssa::V01State::new_with_genesis_accounts(&init_accs); let nullifier_store = HashSet::new(); let utxo_commitments_store = UTXOCommitmentsMerkleTree::new(vec![]); - let pub_tx_store = PublicTransactionMerkleTree::new(vec![]); + // let pub_tx_store = PublicTransactionMerkleTree::new(vec![]); let mut data = [0; 32]; let mut prev_block_hash = [0; 32]; @@ -74,11 +76,11 @@ impl SequecerChainStore { .unwrap(); Self { - acc_store, + state, block_store, nullifier_store, utxo_commitments_store, - pub_tx_store, + // pub_tx_store, } } } diff --git a/sequencer_rpc/Cargo.toml b/sequencer_rpc/Cargo.toml index fb33c2d..1c1de4a 100644 --- a/sequencer_rpc/Cargo.toml +++ b/sequencer_rpc/Cargo.toml @@ -38,3 +38,6 @@ path = "../storage" [dependencies.common] path = "../common" + +[dependencies.nssa] +path = "../nssa" diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 77c6e8b..7f4386a 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -1,4 +1,5 @@ use actix_web::Error as HttpError; +use nssa; use sequencer_core::config::AccountInitialData; use serde_json::Value; @@ -24,7 +25,6 @@ use common::rpc_primitives::requests::{ use super::{respond, types::err_rpc::RpcErr, JsonHandler}; pub const HELLO: &str = "hello"; -pub const REGISTER_ACCOUNT: &str = "register_account"; pub const SEND_TX: &str = "send_tx"; pub const GET_BLOCK: &str = "get_block"; pub const GET_GENESIS: &str = "get_genesis"; @@ -66,29 +66,13 @@ impl JsonHandler { respond(helperstruct) } - async fn process_register_account_request(&self, request: Request) -> Result { - let acc_req = RegisterAccountRequest::parse(Some(request.params))?; - - { - let mut acc_store = self.sequencer_state.lock().await; - - acc_store.register_account(acc_req.address); - } - - let helperstruct = RegisterAccountResponse { - status: SUCCESS.to_string(), - }; - - respond(helperstruct) - } - async fn process_send_tx(&self, request: Request) -> Result { let send_tx_req = SendTxRequest::parse(Some(request.params))?; { let mut state = self.sequencer_state.lock().await; - state.push_tx_into_mempool_pre_check(send_tx_req.transaction, send_tx_req.tx_roots)?; + state.push_tx_into_mempool_pre_check(send_tx_req.transaction)?; } let helperstruct = SendTxResponse { @@ -164,13 +148,16 @@ impl JsonHandler { let get_account_req = GetAccountBalanceRequest::parse(Some(request.params))?; let address_bytes = hex::decode(get_account_req.address) .map_err(|_| RpcError::invalid_params("invalid hex".to_string()))?; - let address = address_bytes - .try_into() - .map_err(|_| RpcError::invalid_params("invalid length".to_string()))?; + let address = nssa::Address::new( + address_bytes + .try_into() + .map_err(|_| RpcError::invalid_params("invalid length".to_string()))?, + ); let balance = { let state = self.sequencer_state.lock().await; - state.store.acc_store.get_account_balance(&address) + let account = state.store.state.get_account_by_address(&address); + account.balance }; let helperstruct = GetAccountBalanceResponse { balance }; @@ -199,7 +186,6 @@ impl JsonHandler { pub async fn process_request_internal(&self, request: Request) -> Result { match request.method.as_ref() { HELLO => self.process_temp_hello(request).await, - REGISTER_ACCOUNT => self.process_register_account_request(request).await, SEND_TX => self.process_send_tx(request).await, GET_BLOCK => self.process_get_block_data(request).await, GET_GENESIS => self.process_get_genesis(request).await, @@ -221,6 +207,7 @@ mod tests { rpc_primitives::RpcPollingConfig, transaction::{SignaturePrivateKey, Transaction, TransactionBody}, }; + use nssa::Program; use sequencer_core::{ config::{AccountInitialData, SequencerConfig}, SequencerCore, @@ -233,13 +220,15 @@ mod tests { let tempdir = tempdir().unwrap(); let home = tempdir.path().to_path_buf(); let acc1_addr = vec![ - 13, 150, 223, 204, 65, 64, 25, 56, 12, 157, 222, 12, 211, 220, 229, 170, 201, 15, 181, - 68, 59, 248, 113, 16, 135, 65, 174, 175, 222, 85, 42, 215, + // 13, 150, 223, 204, 65, 64, 25, 56, 12, 157, 222, 12, 211, 220, 229, 170, 201, 15, 181, + // 68, 59, 248, 113, 16, 135, 65, 174, 175, 222, 85, 42, 215, + 1; 32 ]; let acc2_addr = vec![ - 151, 72, 112, 233, 190, 141, 10, 192, 138, 168, 59, 63, 199, 167, 166, 134, 41, 29, - 135, 50, 80, 138, 186, 152, 179, 96, 128, 243, 156, 44, 243, 100, + // 151, 72, 112, 233, 190, 141, 10, 192, 138, 168, 59, 63, 199, 167, 166, 134, 41, 29, + // 135, 50, 80, 138, 186, 152, 179, 96, 128, 243, 156, 44, 243, 100, + 2; 32 ]; let initial_acc1 = AccountInitialData { @@ -268,32 +257,25 @@ mod tests { fn json_handler_for_tests() -> (JsonHandler, Vec) { let config = sequencer_config_for_tests(); - let mut sequencer_core = SequencerCore::start_from_config(config); - let initial_accounts = sequencer_core.sequencer_config.initial_accounts.clone(); - let tx_body = TransactionBody { - tx_kind: common::transaction::TxKind::Shielded, - execution_input: Default::default(), - execution_output: Default::default(), - utxo_commitments_spent_hashes: Default::default(), - utxo_commitments_created_hashes: Default::default(), - nullifier_created_hashes: Default::default(), - execution_proof_private: Default::default(), - encoded_data: Default::default(), - ephemeral_pub_key: Default::default(), - commitment: Default::default(), - tweak: Default::default(), - secret_r: Default::default(), - sc_addr: Default::default(), - state_changes: Default::default(), - }; - let tx = Transaction::new(tx_body, SignaturePrivateKey::from_slice(&[1; 32]).unwrap()); + let from = nssa::Address::new([1; 32]); + let signing_key = nssa::PrivateKey::new(1); + let to = nssa::Address::new([2; 32]); + let balance_to_move = 10; + + let addresses = vec![from, to]; + let nonces = vec![0]; + let program_id = nssa::AuthenticatedTransferProgram::PROGRAM_ID; + let message = + nssa::public_transaction::Message::new(program_id, addresses, nonces, balance_to_move); + let witness_set = + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); + let tx = nssa::PublicTransaction::new(message, witness_set); + + sequencer_core.push_tx_into_mempool_pre_check(tx).unwrap(); - sequencer_core - .push_tx_into_mempool_pre_check(tx, [[0; 32]; 2]) - .unwrap(); sequencer_core .produce_new_block_with_mempool_transactions() .unwrap(); @@ -414,7 +396,7 @@ mod tests { "id": 1, "jsonrpc": "2.0", "result": { - "balance": 10000 + "balance": 10000 - 10 } }); @@ -499,7 +481,7 @@ mod tests { let request = serde_json::json!({ "jsonrpc": "2.0", "method": "get_transaction_by_hash", - "params": { "hash": "a5210ef33912a448cfe6eda43878c144df81f7bdf51d19b5ddf97be11806a6ed"}, + "params": { "hash": "e5f0c9b4b7732a2f4946b8e7a5f7c641b004559b1a13b1ccc600f29477725240"}, "id": 1 }); @@ -508,24 +490,20 @@ mod tests { "jsonrpc": "2.0", "result": { "transaction": { - "body": { - "commitment": [], - "encoded_data": [], - "ephemeral_pub_key": [], - "execution_input": [], - "execution_output": [], - "execution_proof_private": "", - "nullifier_created_hashes": [], - "sc_addr": "", - "secret_r": vec![0; 32], - "state_changes": [null, 0], - "tweak": "0".repeat(64), - "tx_kind": "Shielded", - "utxo_commitments_created_hashes": [], - "utxo_commitments_spent_hashes": [], + "message": { + "addresses": [ + { "value": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, + { "value": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] } + ], + "instruction_data": 10, + "nonces": [0], + "program_id": nssa::AuthenticatedTransferProgram::PROGRAM_ID, }, - "public_key": "3056301006072A8648CE3D020106052B8104000A034200041B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F70BEAF8F588B541507FED6A642C5AB42DFDF8120A7F639DE5122D47A69A8E8D1", - "signature": "A4E0D6A25BE829B006124F0DFD766427967AA3BEA96C29219E79BB2CC871891F384748C27E28718A4450AA78709FBF1A57DB33BCD575A2C819D2A439C2D878E6" + "witness_set": { + "signatures_and_public_keys": [ + [null, 1] + ] + } } } }); From ef6af9a967492ca3631561cd680d63e10746a7ad Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Fri, 8 Aug 2025 10:51:23 -0300 Subject: [PATCH 05/58] solve linking problem --- Cargo.lock | 168 +++++++- Cargo.toml | 2 +- common/Cargo.toml | 1 - common/src/lib.rs | 2 +- nssa/Cargo.toml | 2 +- nssa/core/Cargo.toml | 2 +- nssa/program_methods/Cargo.toml | 2 +- nssa/program_methods/guest/Cargo.toml | 2 +- sequencer_core/src/lib.rs | 10 - sequencer_core/src/sequencer_store/mod.rs | 1 - zkvm/src/lib.rs | 30 +- zkvm/test_methods/Cargo.toml | 2 +- zkvm/test_methods/guest/Cargo.lock | 449 ++++++++++++++++++---- zkvm/test_methods/guest/Cargo.toml | 4 +- 14 files changed, 559 insertions(+), 118 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e78bd99..51ba37d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -626,6 +626,17 @@ dependencies = [ "critical-section", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + [[package]] name = "auto_ops" version = "0.3.0" @@ -960,6 +971,45 @@ dependencies = [ "libloading", ] +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_derive", + "clap_lex", + "indexmap 1.9.3", + "once_cell", + "strsim 0.10.0", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "cobs" version = "0.3.0" @@ -980,7 +1030,6 @@ dependencies = [ "log", "nssa", "reqwest 0.11.27", - "risc0-zkvm 2.3.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", "rs_merkle", "secp256k1-zkp", "serde", @@ -1212,7 +1261,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.11.1", "syn 2.0.104", ] @@ -1902,7 +1951,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -1986,6 +2035,15 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hermit-abi" version = "0.5.2" @@ -2312,6 +2370,16 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee796ad498c8d9a1d68e477df8f754ed784ef875de1414ebdaf169f70a6a784" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "indexmap" version = "2.10.0" @@ -2373,7 +2441,7 @@ version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ - "hermit-abi", + "hermit-abi 0.5.2", "libc", "windows-sys 0.59.0", ] @@ -3080,6 +3148,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + [[package]] name = "parking_lot" version = "0.12.4" @@ -3238,7 +3312,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "toml_edit", + "toml_edit 0.22.27", ] [[package]] @@ -4242,7 +4316,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4336,7 +4410,7 @@ dependencies = [ "strum", "tempfile", "thiserror 2.0.12", - "toml", + "toml 0.8.23", "yaml-rust2", ] @@ -4350,7 +4424,7 @@ dependencies = [ "strum", "tempfile", "thiserror 2.0.12", - "toml", + "toml 0.8.23", "yaml-rust2", ] @@ -4534,6 +4608,28 @@ dependencies = [ "tokio", ] +[[package]] +name = "sequencer_runner" +version = "0.1.0" +dependencies = [ + "actix", + "actix-web", + "anyhow", + "clap", + "common", + "consensus", + "env_logger", + "log", + "mempool", + "networking", + "sequencer_core", + "sequencer_rpc", + "serde", + "serde_json", + "tokio", + "toml 0.7.8", +] + [[package]] name = "serde" version = "1.0.219" @@ -4769,6 +4865,12 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strsim" version = "0.11.1" @@ -4904,9 +5006,15 @@ dependencies = [ name = "test-methods" version = "0.1.0" dependencies = [ - "risc0-build 2.3.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)", + "risc0-build 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" + [[package]] name = "thiserror" version = "1.0.69" @@ -5064,6 +5172,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + [[package]] name = "toml" version = "0.8.23" @@ -5073,7 +5193,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.22.27", ] [[package]] @@ -5085,18 +5205,31 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.10.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap", + "indexmap 2.10.0", "serde", "serde_spanned", "toml_datetime", "toml_write", - "winnow", + "winnow 0.7.12", ] [[package]] @@ -5753,6 +5886,15 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "0.7.12" @@ -5945,7 +6087,7 @@ dependencies = [ "crossbeam-utils", "displaydoc", "flate2", - "indexmap", + "indexmap 2.10.0", "memchr", "thiserror 2.0.12", "zopfli", diff --git a/Cargo.toml b/Cargo.toml index 1385130..d29e39a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] resolver = "2" members = [ - # "sequencer_runner", + "sequencer_runner", "storage", "accounts", "utxo", diff --git a/common/Cargo.toml b/common/Cargo.toml index 642d820..8bfc317 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -9,7 +9,6 @@ thiserror.workspace = true serde_json.workspace = true serde.workspace = true reqwest.workspace = true -risc0-zkvm = { git = "https://github.com/risc0/risc0.git", branch = "release-2.3" } k256.workspace = true rs_merkle.workspace = true diff --git a/common/src/lib.rs b/common/src/lib.rs index 1722218..9ef0653 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -61,7 +61,7 @@ pub enum ExecutionFailureKind { #[error("Failed prove execution err: {0:?}")] ProveError(anyhow::Error), #[error("Failed to decode data from VM: {0:?}")] - DecodeError(#[from] risc0_zkvm::serde::Error), + DecodeError(String), #[error("Inputs amounts does not match outputs")] AmountMismatchError, #[error("Sequencer client error: {0:?}")] diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index 30f0747..c50dc59 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -risc0-zkvm = "2.2" +risc0-zkvm = "2.3.1" nssa-core = {path="core"} program-methods = { path = "program_methods" } serde = "1.0.219" diff --git a/nssa/core/Cargo.toml b/nssa/core/Cargo.toml index 3a61860..feb907d 100644 --- a/nssa/core/Cargo.toml +++ b/nssa/core/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" edition = "2024" [dependencies] -risc0-zkvm = "2.2" +risc0-zkvm = "2.3.1" serde = { version = "1.0", default-features = false } diff --git a/nssa/program_methods/Cargo.toml b/nssa/program_methods/Cargo.toml index d6ebc8f..0f31c9b 100644 --- a/nssa/program_methods/Cargo.toml +++ b/nssa/program_methods/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [build-dependencies] -risc0-build = { version = "2.2" } +risc0-build = { version = "2.3.1" } [package.metadata.risc0] methods = ["guest"] diff --git a/nssa/program_methods/guest/Cargo.toml b/nssa/program_methods/guest/Cargo.toml index 37e7a79..12bddbf 100644 --- a/nssa/program_methods/guest/Cargo.toml +++ b/nssa/program_methods/guest/Cargo.toml @@ -6,6 +6,6 @@ edition = "2021" [workspace] [dependencies] -risc0-zkvm = { version = "2.2.0", default-features = false, features = ['std'] } +risc0-zkvm = { version = "2.3.1", default-features = false, features = ['std'] } nssa-core = {path = "../../core"} diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 1275bb2..d4f8df0 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -67,16 +67,6 @@ impl SequencerCore { } } - // pub fn get_tree_roots(&self) -> [[u8; 32]; 2] { - // [ - // self.store - // .utxo_commitments_store - // .get_root() - // .unwrap_or([0; 32]), - // self.store.pub_tx_store.get_root().unwrap_or([0; 32]), - // ] - // } - pub fn transaction_pre_check( &mut self, tx: nssa::PublicTransaction, diff --git a/sequencer_core/src/sequencer_store/mod.rs b/sequencer_core/src/sequencer_store/mod.rs index f329591..ac06ac1 100644 --- a/sequencer_core/src/sequencer_store/mod.rs +++ b/sequencer_core/src/sequencer_store/mod.rs @@ -21,7 +21,6 @@ pub struct SequecerChainStore { pub block_store: SequecerBlockStore, pub nullifier_store: HashSet, pub utxo_commitments_store: UTXOCommitmentsMerkleTree, - // pub pub_tx_store: PublicTransactionMerkleTree, } impl SequecerChainStore { diff --git a/zkvm/src/lib.rs b/zkvm/src/lib.rs index 0fbae70..191456f 100644 --- a/zkvm/src/lib.rs +++ b/zkvm/src/lib.rs @@ -63,7 +63,10 @@ pub fn prove_mint_utxo( .map_err(ExecutionFailureKind::prove_error)? .receipt; - let digest: UTXOPayload = receipt.journal.decode()?; + let digest: UTXOPayload = receipt + .journal + .decode() + .map_err(|e| ExecutionFailureKind::DecodeError(e.to_string()))?; Ok((UTXO::create_utxo_from_payload(digest), receipt)) } @@ -110,7 +113,10 @@ pub fn prove_send_utxo( .map_err(ExecutionFailureKind::prove_error)? .receipt; - let digest: Vec<(UTXOPayload, AccountAddress)> = receipt.journal.decode()?; + let digest: Vec<(UTXOPayload, AccountAddress)> = receipt + .journal + .decode() + .map_err(|e| ExecutionFailureKind::DecodeError(e.to_string()))?; Ok(( digest @@ -158,7 +164,10 @@ pub fn prove_send_utxo_multiple_assets_one_receiver( .map_err(ExecutionFailureKind::prove_error)? .receipt; - let digest: (Vec, Vec) = receipt.journal.decode()?; + let digest: (Vec, Vec) = receipt + .journal + .decode() + .map_err(|e| ExecutionFailureKind::DecodeError(e.to_string()))?; Ok(( digest @@ -220,7 +229,10 @@ pub fn prove_send_utxo_shielded( .map_err(ExecutionFailureKind::prove_error)? .receipt; - let digest: Vec<(UTXOPayload, AccountAddress)> = receipt.journal.decode()?; + let digest: Vec<(UTXOPayload, AccountAddress)> = receipt + .journal + .decode() + .map_err(|e| ExecutionFailureKind::DecodeError(e.to_string()))?; Ok(( digest @@ -273,7 +285,10 @@ pub fn prove_send_utxo_deshielded( .map_err(ExecutionFailureKind::prove_error)? .receipt; - let digest: Vec<(UTXOPayload, AccountAddress)> = receipt.journal.decode()?; + let digest: Vec<(UTXOPayload, AccountAddress)> = receipt + .journal + .decode() + .map_err(|e| ExecutionFailureKind::DecodeError(e.to_string()))?; Ok(( digest @@ -313,7 +328,10 @@ pub fn prove_mint_utxo_multiple_assets( .map_err(ExecutionFailureKind::prove_error)? .receipt; - let digest: Vec = receipt.journal.decode()?; + let digest: Vec = receipt + .journal + .decode() + .map_err(|e| ExecutionFailureKind::DecodeError(e.to_string()))?; Ok(( digest diff --git a/zkvm/test_methods/Cargo.toml b/zkvm/test_methods/Cargo.toml index 48f27b8..c79f640 100644 --- a/zkvm/test_methods/Cargo.toml +++ b/zkvm/test_methods/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [build-dependencies] -risc0-build = { git = "https://github.com/risc0/risc0.git", branch = "release-2.3" } +risc0-build = { version = "2.3.1" } [package.metadata.risc0] methods = ["guest"] diff --git a/zkvm/test_methods/guest/Cargo.lock b/zkvm/test_methods/guest/Cargo.lock index 0ee4887..af2db41 100644 --- a/zkvm/test_methods/guest/Cargo.lock +++ b/zkvm/test_methods/guest/Cargo.lock @@ -14,6 +14,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "anyhow" version = "1.0.93" @@ -22,21 +28,24 @@ checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "ark-bn254" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" dependencies = [ "ark-ec", "ark-ff", + "ark-r1cs-std", "ark-std", ] [[package]] name = "ark-crypto-primitives" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3a13b34da09176a8baba701233fdffbaa7c1b1192ce031a3da4e55ce1f1a56" +checksum = "1e0c292754729c8a190e50414fd1a37093c786c709899f29c9f7daccecfa855e" dependencies = [ + "ahash", + "ark-crypto-primitives-macros", "ark-ec", "ark-ff", "ark-relations", @@ -46,74 +55,91 @@ dependencies = [ "blake2", "derivative", "digest", + "fnv", + "merlin", "sha2", ] [[package]] -name = "ark-ec" -version = "0.4.2" +name = "ark-crypto-primitives-macros" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +checksum = "e7e89fe77d1f0f4fe5b96dfc940923d88d17b6a773808124f21e764dfb063c6a" dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash", "ark-ff", "ark-poly", "ark-serialize", "ark-std", - "derivative", - "hashbrown 0.13.2", + "educe", + "fnv", + "hashbrown", "itertools", + "num-bigint", + "num-integer", "num-traits", "zeroize", ] [[package]] name = "ark-ff" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" dependencies = [ "ark-ff-asm", "ark-ff-macros", "ark-serialize", "ark-std", - "derivative", + "arrayvec", "digest", + "educe", "itertools", "num-bigint", "num-traits", "paste", - "rustc_version", "zeroize", ] [[package]] name = "ark-ff-asm" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.87", ] [[package]] name = "ark-ff-macros" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" dependencies = [ "num-bigint", "num-traits", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.87", ] [[package]] name = "ark-groth16" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20ceafa83848c3e390f1cbf124bc3193b3e639b3f02009e0e290809a501b95fc" +checksum = "88f1d0f3a534bb54188b8dcc104307db6c56cdae574ddc3212aec0625740fc7e" dependencies = [ "ark-crypto-primitives", "ark-ec", @@ -126,22 +152,41 @@ dependencies = [ [[package]] name = "ark-poly" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" dependencies = [ + "ahash", "ark-ff", "ark-serialize", "ark-std", - "derivative", - "hashbrown 0.13.2", + "educe", + "fnv", + "hashbrown", +] + +[[package]] +name = "ark-r1cs-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941551ef1df4c7a401de7068758db6503598e6f01850bdb2cfdb614a1f9dbea1" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-relations", + "ark-std", + "educe", + "num-bigint", + "num-integer", + "num-traits", + "tracing", ] [[package]] name = "ark-relations" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00796b6efc05a3f48225e59cb6a2cda78881e7c390872d5786aaf112f31fb4f0" +checksum = "ec46ddc93e7af44bcab5230937635b06fb5744464dd6a7e7b083e80ebd274384" dependencies = [ "ark-ff", "ark-std", @@ -151,32 +196,33 @@ dependencies = [ [[package]] name = "ark-serialize" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" dependencies = [ "ark-serialize-derive", "ark-std", + "arrayvec", "digest", "num-bigint", ] [[package]] name = "ark-serialize-derive" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.87", ] [[package]] name = "ark-snark" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d3cc6833a335bb8a600241889ead68ee89a3cf8448081fb7694c0fe503da63" +checksum = "d368e2848c2d4c129ce7679a7d0d2d612b6a274d3ea6a13bad4445d61b381b88" dependencies = [ "ark-ff", "ark-relations", @@ -186,20 +232,32 @@ dependencies = [ [[package]] name = "ark-std" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" dependencies = [ "num-traits", "rand", ] +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "1.3.2" @@ -261,18 +319,18 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" +checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", @@ -297,6 +355,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -360,6 +427,27 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "unicode-xid", +] + [[package]] name = "digest" version = "0.10.7" @@ -378,6 +466,18 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "either" version = "1.13.0" @@ -390,12 +490,50 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foreign-types" version = "0.5.0" @@ -441,16 +579,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] -name = "hashbrown" -version = "0.13.2" +name = "getrandom" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ - "ahash", + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -458,6 +599,9 @@ name = "hashbrown" version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +dependencies = [ + "allocator-api2", +] [[package]] name = "hex" @@ -471,6 +615,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "include_bytes_aligned" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee796ad498c8d9a1d68e477df8f754ed784ef875de1414ebdaf169f70a6a784" + [[package]] name = "indexmap" version = "2.6.0" @@ -478,14 +628,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.1", + "hashbrown", ] [[package]] name = "itertools" -version = "0.10.5" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -496,6 +646,24 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + [[package]] name = "libc" version = "0.2.162" @@ -529,6 +697,18 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core", + "zeroize", +] + [[package]] name = "metal" version = "0.29.0" @@ -544,6 +724,12 @@ dependencies = [ "paste", ] +[[package]] +name = "no_std_strings" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5b0c77c1b780822bc749a33e39aeb2c07584ab93332303babeabb645298a76e" + [[package]] name = "num-bigint" version = "0.4.6" @@ -599,6 +785,18 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -635,6 +833,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "rand" version = "0.8.5" @@ -663,22 +867,44 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "risc0-binfmt" -version = "1.2.0-alpha.1" -source = "git+https://github.com/risc0/risc0.git#cc8e628638530815fa8b9ac840543c720ec402e8" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62eb7025356a233c1bc267c458a2ce56fcfc89b136d813c8a77be14ef1eaf2b1" dependencies = [ "anyhow", "borsh", + "derive_more", "elf", + "lazy_static", + "postcard", "risc0-zkp", "risc0-zkvm-platform", + "semver", "serde", "tracing", ] +[[package]] +name = "risc0-circuit-keccak" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0094af5a57b020388a03bdd3834959c7d62723f1687be81414ade25104d93263" +dependencies = [ + "anyhow", + "bytemuck", + "paste", + "risc0-binfmt", + "risc0-circuit-recursion", + "risc0-core", + "risc0-zkp", + "tracing", +] + [[package]] name = "risc0-circuit-recursion" -version = "1.2.0-alpha.1" -source = "git+https://github.com/risc0/risc0.git#cc8e628638530815fa8b9ac840543c720ec402e8" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ebded45c902c2b6939924a1cddd1d06b5d1d4ad1531e8798ebfee78f9c038d" dependencies = [ "anyhow", "bytemuck", @@ -691,32 +917,38 @@ dependencies = [ [[package]] name = "risc0-circuit-rv32im" -version = "1.2.0-alpha.1" -source = "git+https://github.com/risc0/risc0.git#cc8e628638530815fa8b9ac840543c720ec402e8" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15030849f8356f01f23c74b37dbfa4283100b594eb634109993e9e005ef45f64" dependencies = [ "anyhow", - "metal", + "bit-vec", + "bytemuck", + "derive_more", + "paste", "risc0-binfmt", "risc0-core", "risc0-zkp", - "risc0-zkvm-platform", "serde", "tracing", ] [[package]] name = "risc0-core" -version = "1.2.0-alpha.1" -source = "git+https://github.com/risc0/risc0.git#cc8e628638530815fa8b9ac840543c720ec402e8" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317bbf70a8750b64d4fd7a2bdc9d7d5f30d8bb305cae486962c797ef35c8d08e" dependencies = [ "bytemuck", + "bytemuck_derive", "rand_core", ] [[package]] name = "risc0-groth16" -version = "1.2.0-alpha.1" -source = "git+https://github.com/risc0/risc0.git#cc8e628638530815fa8b9ac840543c720ec402e8" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cf5d0b673d5fc67a89147c2e9c53134707dcc8137a43d1ef06b4ff68e99b74f" dependencies = [ "anyhow", "ark-bn254", @@ -733,10 +965,21 @@ dependencies = [ "stability", ] +[[package]] +name = "risc0-zkos-v1compat" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76c479b69d1987cb54ac72dcc017197296fdcd6daf78fafc10cbbd3a167a7de" +dependencies = [ + "include_bytes_aligned", + "no_std_strings", +] + [[package]] name = "risc0-zkp" -version = "1.2.0-alpha.1" -source = "git+https://github.com/risc0/risc0.git#cc8e628638530815fa8b9ac840543c720ec402e8" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a287e9cd6d7b3b38eeb49c62090c46a1935922309fbd997a9143ed8c43c8f3cb" dependencies = [ "anyhow", "blake2", @@ -753,24 +996,29 @@ dependencies = [ "risc0-zkvm-platform", "serde", "sha2", + "stability", "tracing", ] [[package]] name = "risc0-zkvm" -version = "1.2.0-alpha.1" -source = "git+https://github.com/risc0/risc0.git#cc8e628638530815fa8b9ac840543c720ec402e8" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9684b333c1c5d83f29ce2a92314ccfafd9d8cdfa6c4e19c07b97015d2f1eb9d0" dependencies = [ "anyhow", "borsh", "bytemuck", - "getrandom", + "derive_more", + "getrandom 0.2.15", "hex", "risc0-binfmt", + "risc0-circuit-keccak", "risc0-circuit-recursion", "risc0-circuit-rv32im", "risc0-core", "risc0-groth16", + "risc0-zkos-v1compat", "risc0-zkp", "risc0-zkvm-platform", "rrs-lib", @@ -783,11 +1031,14 @@ dependencies = [ [[package]] name = "risc0-zkvm-platform" -version = "1.2.0-alpha.1" -source = "git+https://github.com/risc0/risc0.git#cc8e628638530815fa8b9ac840543c720ec402e8" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cae9cb2c2f6cab2dfa395ea6e2576713929040c7fb0c5f4150d13e1119d18686" dependencies = [ "bytemuck", - "getrandom", + "cfg-if", + "getrandom 0.2.15", + "getrandom 0.3.3", "libm", "stability", ] @@ -802,15 +1053,6 @@ dependencies = [ "paste", ] -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - [[package]] name = "ryu" version = "1.0.18" @@ -822,6 +1064,9 @@ name = "semver" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] [[package]] name = "serde" @@ -866,6 +1111,12 @@ dependencies = [ "digest", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "stability" version = "0.2.1" @@ -913,6 +1164,26 @@ dependencies = [ "serde_json", ] +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "toml_datetime" version = "0.6.8" @@ -984,6 +1255,12 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "valuable" version = "0.1.0" @@ -1002,6 +1279,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "winnow" version = "0.6.20" @@ -1011,6 +1297,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/zkvm/test_methods/guest/Cargo.toml b/zkvm/test_methods/guest/Cargo.toml index ea2c331..c80f35b 100644 --- a/zkvm/test_methods/guest/Cargo.toml +++ b/zkvm/test_methods/guest/Cargo.toml @@ -7,9 +7,7 @@ edition = "2021" [dependencies] serde_json = "1.0.81" -risc0-zkvm = { git = "https://github.com/risc0/risc0.git", default-features = false, features = [ - "std", -] } +risc0-zkvm = { version = "2.3.1", default-features = false, features = ['std'] } [dependencies.serde] features = ["derive"] From 3dfcb475340b0e0876aa0d2f7426e51a3b0b136b Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Fri, 8 Aug 2025 13:32:50 -0300 Subject: [PATCH 06/58] add tests for default account --- nssa/Cargo.toml | 2 +- nssa/core/src/account/mod.rs | 40 ++++++++++++++++++----- sequencer_core/src/sequencer_store/mod.rs | 10 ------ sequencer_rpc/src/process.rs | 2 +- 4 files changed, 33 insertions(+), 21 deletions(-) diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index c50dc59..ece3262 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] risc0-zkvm = "2.3.1" -nssa-core = {path="core"} +nssa-core = { path = "core"} program-methods = { path = "program_methods" } serde = "1.0.219" serde_cbor = "0.11.2" diff --git a/nssa/core/src/account/mod.rs b/nssa/core/src/account/mod.rs index 5d4cdd9..7761309 100644 --- a/nssa/core/src/account/mod.rs +++ b/nssa/core/src/account/mod.rs @@ -11,7 +11,7 @@ pub type Nonce = u128; type Data = Vec; /// Account to be used both in public and private contexts -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct Account { pub program_owner: ProgramId, pub balance: u128, @@ -25,13 +25,35 @@ pub struct AccountWithMetadata { pub is_authorized: bool, } -impl Default for Account { - fn default() -> Self { - Self { - program_owner: [0; 8], - balance: 0, - data: vec![], - nonce: 0, - } +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_zero_balance_account_data_creation() { + let new_acc = Account::default(); + + assert_eq!(new_acc.balance, 0); + } + + #[test] + fn test_zero_nonce_account_data_creation() { + let new_acc = Account::default(); + + assert_eq!(new_acc.nonce, 0); + } + + #[test] + fn test_empty_data_account_data_creation() { + let new_acc = Account::default(); + + assert_eq!(new_acc.data, vec![]); + } + + #[test] + fn test_default_program_owner_account_data_creation() { + let new_acc = Account::default(); + + assert_eq!(new_acc.program_owner, [0; 8]); } } diff --git a/sequencer_core/src/sequencer_store/mod.rs b/sequencer_core/src/sequencer_store/mod.rs index ac06ac1..d77af1c 100644 --- a/sequencer_core/src/sequencer_store/mod.rs +++ b/sequencer_core/src/sequencer_store/mod.rs @@ -1,7 +1,6 @@ use std::{collections::HashSet, path::Path}; use accounts::account_core::address::AccountAddress; -use accounts_store::SequencerAccountsStore; use block_store::SequecerBlockStore; use common::{ block::{Block, HashableBlockData}, @@ -13,14 +12,11 @@ use nssa; use crate::config::AccountInitialData; -pub mod accounts_store; pub mod block_store; pub struct SequecerChainStore { pub state: nssa::V01State, pub block_store: SequecerBlockStore, - pub nullifier_store: HashSet, - pub utxo_commitments_store: UTXOCommitmentsMerkleTree, } impl SequecerChainStore { @@ -44,9 +40,6 @@ impl SequecerChainStore { .collect(); let state = nssa::V01State::new_with_genesis_accounts(&init_accs); - let nullifier_store = HashSet::new(); - let utxo_commitments_store = UTXOCommitmentsMerkleTree::new(vec![]); - // let pub_tx_store = PublicTransactionMerkleTree::new(vec![]); let mut data = [0; 32]; let mut prev_block_hash = [0; 32]; @@ -77,9 +70,6 @@ impl SequecerChainStore { Self { state, block_store, - nullifier_store, - utxo_commitments_store, - // pub_tx_store, } } } diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 7f4386a..d80c6f3 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -481,7 +481,7 @@ mod tests { let request = serde_json::json!({ "jsonrpc": "2.0", "method": "get_transaction_by_hash", - "params": { "hash": "e5f0c9b4b7732a2f4946b8e7a5f7c641b004559b1a13b1ccc600f29477725240"}, + "params": { "hash":"fc96dbd7603f1f97bfd96c31e09c35dc877a436c4aa71437b3e8f5bbf69b77fc"}, "id": 1 }); From 002ad048b0a3bdd03da0cf98a8945b42ed57523b Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Fri, 8 Aug 2025 16:19:50 -0300 Subject: [PATCH 07/58] refactor programs. Add builtin programs. Implement account claim logic --- mempool/src/lib.rs | 2 +- nssa/core/src/account/commitment.rs | 3 - nssa/core/src/account/mod.rs | 9 +- nssa/core/src/account/nullifier.rs | 3 - nssa/core/src/program.rs | 9 +- nssa/src/lib.rs | 38 ++- nssa/src/state.rs | 105 ++++-- sequencer_core/src/lib.rs | 4 +- sequencer_core/src/mempool_transaction.rs | 2 +- .../src/sequencer_store/accounts_store.rs | 314 ------------------ .../src/sequencer_store/block_store.rs | 8 +- sequencer_rpc/src/process.rs | 4 +- 12 files changed, 114 insertions(+), 387 deletions(-) delete mode 100644 nssa/core/src/account/commitment.rs delete mode 100644 nssa/core/src/account/nullifier.rs delete mode 100644 sequencer_core/src/sequencer_store/accounts_store.rs diff --git a/mempool/src/lib.rs b/mempool/src/lib.rs index 5298bfc..60c3c81 100644 --- a/mempool/src/lib.rs +++ b/mempool/src/lib.rs @@ -4,7 +4,7 @@ use mempoolitem::MemPoolItem; pub mod mempoolitem; -pub struct MemPool { +pub struct MemPool { items: VecDeque, } diff --git a/nssa/core/src/account/commitment.rs b/nssa/core/src/account/commitment.rs deleted file mode 100644 index 99ddf84..0000000 --- a/nssa/core/src/account/commitment.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub(crate) struct Commitment { - value: [u8; 32], -} diff --git a/nssa/core/src/account/mod.rs b/nssa/core/src/account/mod.rs index 7761309..9947953 100644 --- a/nssa/core/src/account/mod.rs +++ b/nssa/core/src/account/mod.rs @@ -1,8 +1,3 @@ -mod commitment; -mod nullifier; - -pub(crate) use commitment::Commitment; -pub(crate) use nullifier::Nullifier; use serde::{Deserialize, Serialize}; use crate::program::ProgramId; @@ -27,6 +22,8 @@ pub struct AccountWithMetadata { #[cfg(test)] mod tests { + use crate::program::DEFAULT_PROGRAM_ID; + use super::*; #[test] @@ -54,6 +51,6 @@ mod tests { fn test_default_program_owner_account_data_creation() { let new_acc = Account::default(); - assert_eq!(new_acc.program_owner, [0; 8]); + assert_eq!(new_acc.program_owner, DEFAULT_PROGRAM_ID); } } diff --git a/nssa/core/src/account/nullifier.rs b/nssa/core/src/account/nullifier.rs deleted file mode 100644 index cc3e906..0000000 --- a/nssa/core/src/account/nullifier.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub(crate) struct Nullifier { - value: [u8; 32] -} diff --git a/nssa/core/src/program.rs b/nssa/core/src/program.rs index 7f66c2a..ec9bdc7 100644 --- a/nssa/core/src/program.rs +++ b/nssa/core/src/program.rs @@ -3,12 +3,11 @@ use serde::{Deserialize, Serialize}; use crate::account::{Account, AccountWithMetadata}; pub type ProgramId = [u32; 8]; +pub const DEFAULT_PROGRAM_ID: ProgramId = [0; 8]; -/// A trait to be implemented by inner programs. -pub trait Program { - const PROGRAM_ID: ProgramId; - const PROGRAM_ELF: &[u8]; - type InstructionData: Serialize + for<'de> Deserialize<'de>; +pub struct Program { + pub id: ProgramId, + pub elf: &'static [u8], } /// Validates well-behaved program execution diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs index 81f48c3..d911d78 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -1,6 +1,6 @@ use nssa_core::{ account::{Account, AccountWithMetadata}, - program::ProgramId, + program::{DEFAULT_PROGRAM_ID, ProgramId}, }; use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID}; use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor}; @@ -16,17 +16,15 @@ pub use public_transaction::PublicTransaction; pub use signature::PrivateKey; pub use state::V01State; -pub struct AuthenticatedTransferProgram; -impl Program for AuthenticatedTransferProgram { - const PROGRAM_ID: ProgramId = AUTHENTICATED_TRANSFER_ID; - const PROGRAM_ELF: &[u8] = AUTHENTICATED_TRANSFER_ELF; - type InstructionData = u128; -} +pub const AUTHENTICATED_TRANSFER_PROGRAM: Program = Program { + id: AUTHENTICATED_TRANSFER_ID, + elf: AUTHENTICATED_TRANSFER_ELF, +}; /// Writes inputs to `env_builder` in the order expected by the programs -fn write_inputs( +fn write_inputs( pre_states: &[AccountWithMetadata], - instruction_data: P::InstructionData, + instruction_data: u128, env_builder: &mut ExecutorEnvBuilder, ) -> Result<(), ()> { let pre_states = pre_states.to_vec(); @@ -35,19 +33,29 @@ fn write_inputs( Ok(()) } -fn execute_public( +fn execute_public( pre_states: &[AccountWithMetadata], - instruction_data: P::InstructionData, + instruction_data: u128, + program: &Program, ) -> Result, ()> { // Write inputs to the program let mut env_builder = ExecutorEnv::builder(); - write_inputs::

(pre_states, instruction_data, &mut env_builder)?; + write_inputs(pre_states, instruction_data, &mut env_builder)?; let env = env_builder.build().unwrap(); // Execute the program (without proving) let executor = default_executor(); - let session_info = executor.execute(env, P::PROGRAM_ELF).map_err(|_| ())?; + let session_info = executor.execute(env, program.elf).map_err(|_| ())?; - // Get (inputs and) outputs - session_info.journal.decode().map_err(|_| ()) + // Get outputs + let mut post_states: Vec = session_info.journal.decode().map_err(|_| ())?; + + // Claim any output account with default program owner field + for account in post_states.iter_mut() { + if account.program_owner == DEFAULT_PROGRAM_ID { + account.program_owner = program.id; + } + } + + Ok(post_states) } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 2a09c10..a16f013 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -1,32 +1,41 @@ use crate::{ - AuthenticatedTransferProgram, address::Address, execute_public, + AUTHENTICATED_TRANSFER_PROGRAM, address::Address, execute_public, public_transaction::PublicTransaction, }; use nssa_core::{ account::{Account, AccountWithMetadata}, - program::{Program, validate_constraints}, + program::{Program, ProgramId, validate_constraints}, }; use std::collections::{HashMap, HashSet}; pub struct V01State { public_state: HashMap, + builtin_programs: HashMap, } impl V01State { pub fn new_with_genesis_accounts(initial_data: &[([u8; 32], u128)]) -> Self { - // TODO:: remove this assert? let public_state = initial_data .to_owned() .into_iter() .map(|(address_value, balance)| { let mut account = Account::default(); account.balance = balance; - account.program_owner = AuthenticatedTransferProgram::PROGRAM_ID; + account.program_owner = AUTHENTICATED_TRANSFER_PROGRAM.id; let address = Address::new(address_value); (address, account) }) .collect(); - Self { public_state } + + let builtin_programs = HashMap::from([( + AUTHENTICATED_TRANSFER_PROGRAM.id, + AUTHENTICATED_TRANSFER_PROGRAM, + )]); + + Self { + public_state, + builtin_programs, + } } pub fn transition_from_public_transaction(&mut self, tx: &PublicTransaction) -> Result<(), ()> { @@ -108,20 +117,19 @@ impl V01State { // Check the `program_id` corresponds to a built-in program // Only allowed program so far is the authenticated transfer program - if message.program_id != AuthenticatedTransferProgram::PROGRAM_ID { + let Some(program) = self.builtin_programs.get(&message.program_id) else { return Err(()); - } + }; // // Execute program let post_states = - execute_public::(&pre_states, message.instruction_data) - .map_err(|_| ())?; + execute_public(&pre_states, message.instruction_data, program).map_err(|_| ())?; // Verify execution corresponds to a well-behaved program. // See the # Programs section for the definition of the `validate_constraints` method. validate_constraints(&pre_states, &post_states, message.program_id).map_err(|_| ())?; - if (post_states.len() != message.addresses.len()) { + if post_states.len() != message.addresses.len() { return Err(()); } @@ -148,7 +156,7 @@ mod tests { ) -> PublicTransaction { let addresses = vec![from, to]; let nonces = vec![nonce]; - let program_id = AuthenticatedTransferProgram::PROGRAM_ID; + let program_id = AUTHENTICATED_TRANSFER_PROGRAM.id; let message = public_transaction::Message::new(program_id, addresses, nonces, balance); let witness_set = public_transaction::WitnessSet::for_message(&message, &[from_key]); PublicTransaction::new(message, witness_set) @@ -157,44 +165,77 @@ mod tests { #[test] fn test_1() { let initial_data = [([1; 32], 100)]; - let mut genesis_state = V01State::new_with_genesis_accounts(&initial_data); + let mut state = V01State::new_with_genesis_accounts(&initial_data); let from = Address::new(initial_data[0].0.clone()); let from_key = PrivateKey(1); let to = Address::new([2; 32]); let balance_to_move = 5; let tx = transfer_transaction_for_tests(from.clone(), from_key, 0, to.clone(), balance_to_move); - let _ = genesis_state.transition_from_public_transaction(&tx); - assert_eq!( - genesis_state.get_account_by_address(&to).balance, - balance_to_move - ); - assert_eq!( - genesis_state.get_account_by_address(&from).balance, - initial_data[0].1 - balance_to_move - ); - assert_eq!(genesis_state.get_account_by_address(&from).nonce, 1); - assert_eq!(genesis_state.get_account_by_address(&to).nonce, 0); + state.transition_from_public_transaction(&tx).unwrap(); + + assert_eq!(state.get_account_by_address(&to).balance, 5); + assert_eq!(state.get_account_by_address(&from).balance, 95); + assert_eq!(state.get_account_by_address(&from).nonce, 1); + assert_eq!(state.get_account_by_address(&to).nonce, 0); } #[test] fn test_2() { let initial_data = [([1; 32], 100), ([99; 32], 200)]; - let mut genesis_state = V01State::new_with_genesis_accounts(&initial_data); + let mut state = V01State::new_with_genesis_accounts(&initial_data); let from = Address::new(initial_data[1].0.clone()); let from_key = PrivateKey(99); let to = Address::new(initial_data[0].0.clone()); let balance_to_move = 8; - let to_previous_balance = genesis_state.get_account_by_address(&to).balance; + let to_previous_balance = state.get_account_by_address(&to).balance; let tx = transfer_transaction_for_tests(from.clone(), from_key, 0, to.clone(), balance_to_move); - let _ = genesis_state.transition_from_public_transaction(&tx); - assert_eq!(genesis_state.get_account_by_address(&to).balance, 108); - assert_eq!( - genesis_state.get_account_by_address(&from).balance, - initial_data[1].1 - balance_to_move + state.transition_from_public_transaction(&tx).unwrap(); + + assert_eq!(state.get_account_by_address(&to).balance, 108); + assert_eq!(state.get_account_by_address(&from).balance, 192); + assert_eq!(state.get_account_by_address(&from).nonce, 1); + assert_eq!(state.get_account_by_address(&to).nonce, 0); + } + + #[test] + fn test_3() { + let initial_data = [([1; 32], 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data); + let address_1 = Address::new(initial_data[0].0.clone()); + let key_1 = PrivateKey(1); + let address_2 = Address::new([2; 32]); + + let key_2 = PrivateKey(2); + let address_3 = Address::new([3; 32]); + + let balance_to_move = 5; + let tx = transfer_transaction_for_tests( + address_1.clone(), + key_1, + 0, + address_2.clone(), + balance_to_move, ); - assert_eq!(genesis_state.get_account_by_address(&from).nonce, 1); - assert_eq!(genesis_state.get_account_by_address(&to).nonce, 0); + state.transition_from_public_transaction(&tx).unwrap(); + + let balance_to_move = 3; + let tx = transfer_transaction_for_tests( + address_2.clone(), + key_2, + 0, + address_3.clone(), + balance_to_move, + ); + state.transition_from_public_transaction(&tx).unwrap(); + + assert_eq!(state.get_account_by_address(&address_1).balance, 95); + assert_eq!(state.get_account_by_address(&address_2).balance, 2); + assert_eq!(state.get_account_by_address(&address_3).balance, 3); + + assert_eq!(state.get_account_by_address(&address_1).nonce, 1); + assert_eq!(state.get_account_by_address(&address_2).nonce, 1); + assert_eq!(state.get_account_by_address(&address_3).nonce, 0); } } diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index d4f8df0..5bfa6a3 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -202,7 +202,7 @@ mod tests { } fn create_dummy_transaction() -> nssa::PublicTransaction { - let program_id = nssa::AuthenticatedTransferProgram::PROGRAM_ID; + let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id; let addresses = vec![]; let nonces = vec![]; let instruction_data = 0; @@ -223,7 +223,7 @@ mod tests { ) -> nssa::PublicTransaction { let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)]; let nonces = vec![nonce]; - let program_id = nssa::AuthenticatedTransferProgram::PROGRAM_ID; + let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id; let message = nssa::public_transaction::Message::new(program_id, addresses, nonces, balance_to_move); let witness_set = diff --git a/sequencer_core/src/mempool_transaction.rs b/sequencer_core/src/mempool_transaction.rs index 1ba1a2b..d59be5b 100644 --- a/sequencer_core/src/mempool_transaction.rs +++ b/sequencer_core/src/mempool_transaction.rs @@ -1,4 +1,4 @@ -use common::{merkle_tree_public::TreeHashType, transaction::AuthenticatedTransaction}; +use common::merkle_tree_public::TreeHashType; use mempool::mempoolitem::MemPoolItem; pub struct MempoolTransaction { diff --git a/sequencer_core/src/sequencer_store/accounts_store.rs b/sequencer_core/src/sequencer_store/accounts_store.rs deleted file mode 100644 index 4f02220..0000000 --- a/sequencer_core/src/sequencer_store/accounts_store.rs +++ /dev/null @@ -1,314 +0,0 @@ -use accounts::account_core::address::AccountAddress; -use anyhow::Result; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct AccountPublicData { - pub balance: u64, - pub address: AccountAddress, - nonce: u64, -} - -impl AccountPublicData { - pub fn new(address: AccountAddress) -> Self { - Self { - balance: 0, - nonce: 0, - address, - } - } - - fn new_with_balance(address: AccountAddress, balance: u64) -> Self { - Self { - balance, - address, - nonce: 0, - } - } -} - -#[derive(Debug, Clone)] -pub struct SequencerAccountsStore { - accounts: HashMap, -} - -impl SequencerAccountsStore { - pub fn new(initial_accounts: &[(AccountAddress, u64)]) -> Self { - let mut accounts = HashMap::new(); - - for (account_addr, balance) in initial_accounts { - accounts.insert( - *account_addr, - AccountPublicData::new_with_balance(*account_addr, *balance), - ); - } - - Self { accounts } - } - - ///Register new account in accounts store - /// - ///Starts with zero public balance - pub fn register_account(&mut self, account_addr: AccountAddress) { - self.accounts - .insert(account_addr, AccountPublicData::new(account_addr)); - } - - ///Check, if `account_addr` present in account store - pub fn contains_account(&self, account_addr: &AccountAddress) -> bool { - self.accounts.contains_key(account_addr) - } - - ///Check `account_addr` balance, - /// - ///returns 0, if account address not found - pub fn get_account_balance(&self, account_addr: &AccountAddress) -> u64 { - self.accounts - .get(account_addr) - .map(|acc| acc.balance) - .unwrap_or(0) - } - - pub fn get_account_nonce(&self, account_addr: &AccountAddress) -> u64 { - self.accounts - .get(account_addr) - .map(|acc| acc.nonce) - .unwrap_or(0) - } - - ///Update `account_addr` balance, - /// - /// returns 0, if account address not found, otherwise returns previous balance - /// - /// Also, if account was not previously found, sets it with zero balance - pub fn set_account_balance(&mut self, account_addr: &AccountAddress, new_balance: u64) -> u64 { - let acc_data = self.accounts.get_mut(account_addr); - - if let Some(acc_data) = acc_data { - let old_balance = acc_data.balance; - - acc_data.balance = new_balance; - - old_balance - } else { - self.register_account(*account_addr); - - let acc = self.accounts.get_mut(account_addr).unwrap(); - - acc.balance = new_balance; - - 0 - } - } - - ///Update `account_addr` nonce, - /// - /// Returns previous nonce - pub fn increase_nonce(&mut self, account_addr: &AccountAddress) -> u64 { - if let Some(acc_data) = self.accounts.get_mut(account_addr) { - let old_nonce = acc_data.nonce; - acc_data.nonce += 1; - old_nonce - } else { - self.register_account(*account_addr); - self.increase_nonce(account_addr) - } - } - - ///Remove account from storage - /// - /// Fails, if `balance` is != 0 - /// - /// Returns `Option` which is `None` if `account_addr` vere not present in store - pub fn unregister_account( - &mut self, - account_addr: AccountAddress, - ) -> Result> { - if self.get_account_balance(&account_addr) == 0 { - Ok(self.accounts.remove(&account_addr).map(|data| data.address)) - } else { - anyhow::bail!("Chain consistency violation: It is forbidden to remove account with nonzero balance"); - } - } - - ///Number of accounts present in store - pub fn len(&self) -> usize { - self.accounts.len() - } - - ///Is accounts store empty - pub fn is_empty(&self) -> bool { - self.accounts.is_empty() - } -} - -impl Default for SequencerAccountsStore { - fn default() -> Self { - Self::new(&[]) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_zero_balance_account_data_creation() { - let new_acc = AccountPublicData::new([1; 32]); - - assert_eq!(new_acc.balance, 0); - assert_eq!(new_acc.address, [1; 32]); - } - - #[test] - fn test_zero_nonce_account_data_creation() { - let new_acc = AccountPublicData::new([1; 32]); - - assert_eq!(new_acc.nonce, 0); - } - - #[test] - fn test_non_zero_balance_account_data_creation() { - let new_acc = AccountPublicData::new_with_balance([1; 32], 10); - - assert_eq!(new_acc.balance, 10); - assert_eq!(new_acc.address, [1; 32]); - } - - #[test] - fn test_zero_nonce_account_data_creation_with_balance() { - let new_acc = AccountPublicData::new_with_balance([1; 32], 10); - - assert_eq!(new_acc.nonce, 0); - } - - #[test] - fn default_account_sequencer_store() { - let seq_acc_store = SequencerAccountsStore::default(); - - assert!(seq_acc_store.accounts.is_empty()); - } - - #[test] - fn account_sequencer_store_register_acc() { - let mut seq_acc_store = SequencerAccountsStore::default(); - - seq_acc_store.register_account([1; 32]); - - assert!(seq_acc_store.contains_account(&[1; 32])); - - let acc_balance = seq_acc_store.get_account_balance(&[1; 32]); - - assert_eq!(acc_balance, 0); - } - - #[test] - fn account_sequencer_store_unregister_acc_not_present() { - let mut seq_acc_store = SequencerAccountsStore::default(); - - seq_acc_store.register_account([1; 32]); - - let rem_res = seq_acc_store.unregister_account([2; 32]).unwrap(); - - assert!(rem_res.is_none()); - } - - #[test] - fn account_sequencer_store_unregister_acc_not_zero_balance() { - let mut seq_acc_store = SequencerAccountsStore::new(&[([1; 32], 12), ([2; 32], 100)]); - - let rem_res = seq_acc_store.unregister_account([1; 32]); - - assert!(rem_res.is_err()); - } - - #[test] - fn account_sequencer_store_unregister_acc() { - let mut seq_acc_store = SequencerAccountsStore::default(); - - seq_acc_store.register_account([1; 32]); - - assert!(seq_acc_store.contains_account(&[1; 32])); - - seq_acc_store.unregister_account([1; 32]).unwrap().unwrap(); - - assert!(!seq_acc_store.contains_account(&[1; 32])); - } - - #[test] - fn account_sequencer_store_with_preset_accounts_1() { - let seq_acc_store = SequencerAccountsStore::new(&[([1; 32], 12), ([2; 32], 100)]); - - assert!(seq_acc_store.contains_account(&[1; 32])); - assert!(seq_acc_store.contains_account(&[2; 32])); - - let acc_balance = seq_acc_store.get_account_balance(&[1; 32]); - - assert_eq!(acc_balance, 12); - - let acc_balance = seq_acc_store.get_account_balance(&[2; 32]); - - assert_eq!(acc_balance, 100); - } - - #[test] - fn account_sequencer_store_with_preset_accounts_2() { - let seq_acc_store = - SequencerAccountsStore::new(&[([6; 32], 120), ([7; 32], 15), ([8; 32], 10)]); - - assert!(seq_acc_store.contains_account(&[6; 32])); - assert!(seq_acc_store.contains_account(&[7; 32])); - assert!(seq_acc_store.contains_account(&[8; 32])); - - let acc_balance = seq_acc_store.get_account_balance(&[6; 32]); - - assert_eq!(acc_balance, 120); - - let acc_balance = seq_acc_store.get_account_balance(&[7; 32]); - - assert_eq!(acc_balance, 15); - - let acc_balance = seq_acc_store.get_account_balance(&[8; 32]); - - assert_eq!(acc_balance, 10); - } - - #[test] - fn account_sequencer_store_fetch_unknown_account() { - let seq_acc_store = - SequencerAccountsStore::new(&[([6; 32], 120), ([7; 32], 15), ([8; 32], 10)]); - - let acc_balance = seq_acc_store.get_account_balance(&[9; 32]); - - assert_eq!(acc_balance, 0); - } - - #[test] - fn account_sequencer_store_is_empty_test() { - let seq_acc_store = SequencerAccountsStore::default(); - - assert!(seq_acc_store.is_empty()); - } - - #[test] - fn account_sequencer_store_set_balance_to_unknown_account() { - let mut seq_acc_store = SequencerAccountsStore::default(); - - let ret = seq_acc_store.set_account_balance(&[1; 32], 100); - - assert_eq!(ret, 0); - assert!(seq_acc_store.contains_account(&[1; 32])); - assert_eq!(seq_acc_store.get_account_balance(&[1; 32]), 100); - } - - #[test] - fn test_increase_nonce() { - let mut account_store = SequencerAccountsStore::default(); - let address = [1; 32]; - let first_nonce = account_store.increase_nonce(&address); - assert_eq!(first_nonce, 0); - let second_nonce = account_store.increase_nonce(&address); - assert_eq!(second_nonce, 1); - } -} diff --git a/sequencer_core/src/sequencer_store/block_store.rs b/sequencer_core/src/sequencer_store/block_store.rs index 88afed5..36dbee8 100644 --- a/sequencer_core/src/sequencer_store/block_store.rs +++ b/sequencer_core/src/sequencer_store/block_store.rs @@ -81,13 +81,15 @@ mod tests { use tempfile::tempdir; fn create_dummy_block_with_transaction(block_id: u64) -> (Block, nssa::PublicTransaction) { - let program_id = nssa::AuthenticatedTransferProgram::PROGRAM_ID; + let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id; let addresses = vec![]; let nonces = vec![]; let instruction_data = 0; - let message = nssa::public_transaction::Message::new(program_id, addresses, nonces, instruction_data); + let message = + nssa::public_transaction::Message::new(program_id, addresses, nonces, instruction_data); let private_key = nssa::PrivateKey::new(1); - let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[private_key]); + let witness_set = + nssa::public_transaction::WitnessSet::for_message(&message, &[private_key]); let tx = nssa::PublicTransaction::new(message, witness_set); ( Block { diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index d80c6f3..9815f3c 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -267,7 +267,7 @@ mod tests { let addresses = vec![from, to]; let nonces = vec![0]; - let program_id = nssa::AuthenticatedTransferProgram::PROGRAM_ID; + let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id; let message = nssa::public_transaction::Message::new(program_id, addresses, nonces, balance_to_move); let witness_set = @@ -497,7 +497,7 @@ mod tests { ], "instruction_data": 10, "nonces": [0], - "program_id": nssa::AuthenticatedTransferProgram::PROGRAM_ID, + "program_id": nssa::AUTHENTICATED_TRANSFER_PROGRAM.id, }, "witness_set": { "signatures_and_public_keys": [ From 22c16c1ec99452da0cfba0e6a8a4377bd9aac11b Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Fri, 8 Aug 2025 16:37:20 -0300 Subject: [PATCH 08/58] improve tests function to avoid manual entry of hex string in test --- sequencer_rpc/src/process.rs | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 9815f3c..adba07a 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -255,7 +255,11 @@ mod tests { } } - fn json_handler_for_tests() -> (JsonHandler, Vec) { + fn components_for_tests() -> ( + JsonHandler, + Vec, + nssa::PublicTransaction, + ) { let config = sequencer_config_for_tests(); let mut sequencer_core = SequencerCore::start_from_config(config); let initial_accounts = sequencer_core.sequencer_config.initial_accounts.clone(); @@ -274,7 +278,9 @@ mod tests { nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); let tx = nssa::PublicTransaction::new(message, witness_set); - sequencer_core.push_tx_into_mempool_pre_check(tx).unwrap(); + sequencer_core + .push_tx_into_mempool_pre_check(tx.clone()) + .unwrap(); sequencer_core .produce_new_block_with_mempool_transactions() @@ -288,6 +294,7 @@ mod tests { sequencer_state: sequencer_core, }, initial_accounts, + tx, ) } @@ -314,7 +321,7 @@ mod tests { #[actix_web::test] async fn test_get_account_balance_for_non_existent_account() { - let (json_handler, _) = json_handler_for_tests(); + let (json_handler, _, _) = components_for_tests(); let request = serde_json::json!({ "jsonrpc": "2.0", "method": "get_account_balance", @@ -336,7 +343,7 @@ mod tests { #[actix_web::test] async fn test_get_account_balance_for_invalid_hex() { - let (json_handler, _) = json_handler_for_tests(); + let (json_handler, _, _) = components_for_tests(); let request = serde_json::json!({ "jsonrpc": "2.0", "method": "get_account_balance", @@ -359,7 +366,7 @@ mod tests { #[actix_web::test] async fn test_get_account_balance_for_invalid_length() { - let (json_handler, _) = json_handler_for_tests(); + let (json_handler, _, _) = components_for_tests(); let request = serde_json::json!({ "jsonrpc": "2.0", "method": "get_account_balance", @@ -382,7 +389,7 @@ mod tests { #[actix_web::test] async fn test_get_account_balance_for_existing_account() { - let (json_handler, initial_accounts) = json_handler_for_tests(); + let (json_handler, initial_accounts, _) = components_for_tests(); let acc1_addr = initial_accounts[0].addr.clone(); @@ -407,7 +414,7 @@ mod tests { #[actix_web::test] async fn test_get_transaction_by_hash_for_non_existent_hash() { - let (json_handler, _) = json_handler_for_tests(); + let (json_handler, _, _) = components_for_tests(); let request = serde_json::json!({ "jsonrpc": "2.0", "method": "get_transaction_by_hash", @@ -429,7 +436,7 @@ mod tests { #[actix_web::test] async fn test_get_transaction_by_hash_for_invalid_hex() { - let (json_handler, _) = json_handler_for_tests(); + let (json_handler, _, _) = components_for_tests(); let request = serde_json::json!({ "jsonrpc": "2.0", "method": "get_transaction_by_hash", @@ -453,7 +460,7 @@ mod tests { #[actix_web::test] async fn test_get_transaction_by_hash_for_invalid_length() { - let (json_handler, _) = json_handler_for_tests(); + let (json_handler, _, _) = components_for_tests(); let request = serde_json::json!({ "jsonrpc": "2.0", "method": "get_transaction_by_hash", @@ -477,11 +484,12 @@ mod tests { #[actix_web::test] async fn test_get_transaction_by_hash_for_existing_transaction() { - let (json_handler, _) = json_handler_for_tests(); + let (json_handler, _, tx) = components_for_tests(); + let tx_hash_hex = hex::encode(tx.hash()); let request = serde_json::json!({ "jsonrpc": "2.0", "method": "get_transaction_by_hash", - "params": { "hash":"fc96dbd7603f1f97bfd96c31e09c35dc877a436c4aa71437b3e8f5bbf69b77fc"}, + "params": { "hash": tx_hash_hex}, "id": 1 }); From 13cc82e3ed4baec1ae24159d5dc9db5e1270bfa7 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Fri, 8 Aug 2025 16:53:15 -0300 Subject: [PATCH 09/58] remove unnecessary mempoolitem trait --- mempool/src/lib.rs | 16 +--- mempool/src/mempoolitem.rs | 4 - sequencer_core/src/lib.rs | 102 ++++++++++------------ sequencer_core/src/mempool_transaction.rs | 20 ----- 4 files changed, 50 insertions(+), 92 deletions(-) delete mode 100644 mempool/src/mempoolitem.rs delete mode 100644 sequencer_core/src/mempool_transaction.rs diff --git a/mempool/src/lib.rs b/mempool/src/lib.rs index 60c3c81..ab1c5d3 100644 --- a/mempool/src/lib.rs +++ b/mempool/src/lib.rs @@ -1,14 +1,10 @@ use std::collections::VecDeque; -use mempoolitem::MemPoolItem; - -pub mod mempoolitem; - pub struct MemPool { items: VecDeque, } -impl MemPool { +impl MemPool { pub fn new() -> Self { Self { items: VecDeque::new(), @@ -55,7 +51,7 @@ impl MemPool { } } -impl Default for MemPool { +impl Default for MemPool { fn default() -> Self { Self::new() } @@ -74,14 +70,6 @@ mod tests { id: ItemId, } - impl MemPoolItem for TestItem { - type Identifier = ItemId; - - fn identifier(&self) -> Self::Identifier { - self.id - } - } - fn test_item_with_id(id: u64) -> TestItem { TestItem { id } } diff --git a/mempool/src/mempoolitem.rs b/mempool/src/mempoolitem.rs deleted file mode 100644 index b3e3530..0000000 --- a/mempool/src/mempoolitem.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub trait MemPoolItem { - type Identifier; - fn identifier(&self) -> Self::Identifier; -} diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 5bfa6a3..392d772 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -12,17 +12,15 @@ use common::{ }; use config::SequencerConfig; use mempool::MemPool; -use mempool_transaction::MempoolTransaction; use sequencer_store::SequecerChainStore; use serde::{Deserialize, Serialize}; pub mod config; -pub mod mempool_transaction; pub mod sequencer_store; pub struct SequencerCore { pub store: SequecerChainStore, - pub mempool: MemPool, + pub mempool: MemPool, pub sequencer_config: SequencerConfig, pub chain_height: u64, } @@ -61,7 +59,7 @@ impl SequencerCore { config.is_genesis_random, &config.initial_accounts, ), - mempool: MemPool::::default(), + mempool: MemPool::default(), chain_height: config.genesis_id, sequencer_config: config, } @@ -95,10 +93,8 @@ impl SequencerCore { fn execute_check_transaction_on_state( &mut self, - mempool_tx: MempoolTransaction, + tx: nssa::PublicTransaction, ) -> Result { - let tx = mempool_tx.auth_tx; - self.store.state.transition_from_public_transaction(&tx)?; // self.store.pub_tx_store.add_tx(mempool_tx.auth_tx); @@ -116,7 +112,7 @@ impl SequencerCore { let valid_transactions = transactions .into_iter() - .filter_map(|mempool_tx| self.execute_check_transaction_on_state(mempool_tx).ok()) + .filter_map(|tx| self.execute_check_transaction_on_state(tx).ok()) .collect(); let prev_block_hash = self @@ -151,7 +147,6 @@ mod tests { use common::transaction::{SignaturePrivateKey, Transaction, TransactionBody, TxKind}; use k256::{ecdsa::SigningKey, FieldBytes}; - use mempool_transaction::MempoolTransaction; use nssa::Program; use secp256k1_zkp::Tweak; @@ -255,8 +250,7 @@ mod tests { fn common_setup(sequencer: &mut SequencerCore) { let tx = create_dummy_transaction(); - let mempool_tx = MempoolTransaction { auth_tx: tx }; - sequencer.mempool.push_item(mempool_tx); + sequencer.mempool.push_item(tx); sequencer .produce_new_block_with_mempool_transactions() @@ -281,17 +275,19 @@ mod tests { .try_into() .unwrap(); - let balance_acc_1 = sequencer.store.state.get_account_by_address(&nssa::Address::new(acc1_addr)).balance; - let balance_acc_2 = sequencer.store.state.get_account_by_address(&nssa::Address::new(acc2_addr)).balance; + let balance_acc_1 = sequencer + .store + .state + .get_account_by_address(&nssa::Address::new(acc1_addr)) + .balance; + let balance_acc_2 = sequencer + .store + .state + .get_account_by_address(&nssa::Address::new(acc2_addr)) + .balance; - assert_eq!( - 10000, - balance_acc_1 - ); - assert_eq!( - 20000, - balance_acc_2 - ); + assert_eq!(10000, balance_acc_1); + assert_eq!(20000, balance_acc_2); } #[test] @@ -334,11 +330,19 @@ mod tests { assert_eq!( 10000, - sequencer.store.state.get_account_by_address(&nssa::Address::new(acc1_addr)).balance + sequencer + .store + .state + .get_account_by_address(&nssa::Address::new(acc1_addr)) + .balance ); assert_eq!( 20000, - sequencer.store.state.get_account_by_address(&nssa::Address::new(acc2_addr)).balance + sequencer + .store + .state + .get_account_by_address(&nssa::Address::new(acc2_addr)) + .balance ); } @@ -402,8 +406,7 @@ mod tests { // let tx_roots = sequencer.get_tree_roots(); let tx = sequencer.transaction_pre_check(tx).unwrap(); - let result = - sequencer.execute_check_transaction_on_state(MempoolTransaction { auth_tx: tx }); + let result = sequencer.execute_check_transaction_on_state(tx); assert_eq!(result.err().unwrap(), ()); } @@ -433,9 +436,7 @@ mod tests { //Passed pre-check assert!(result.is_ok()); - let result = sequencer.execute_check_transaction_on_state(MempoolTransaction { - auth_tx: result.unwrap(), - }); + let result = sequencer.execute_check_transaction_on_state(result.unwrap()); let is_failed_at_balance_mismatch = matches!( result.err().unwrap(), // TransactionMalformationErrorKind::BalanceMismatch { tx: _ } @@ -465,12 +466,18 @@ mod tests { let tx = create_dummy_transaction_native_token_transfer(acc1, 0, acc2, 100, sign_key1); - sequencer - .execute_check_transaction_on_state(MempoolTransaction { auth_tx: tx }) - .unwrap(); + sequencer.execute_check_transaction_on_state(tx).unwrap(); - let bal_from = sequencer.store.state.get_account_by_address(&nssa::Address::new(acc1)).balance; - let bal_to = sequencer.store.state.get_account_by_address(&nssa::Address::new(acc2)).balance; + let bal_from = sequencer + .store + .state + .get_account_by_address(&nssa::Address::new(acc1)) + .balance; + let bal_to = sequencer + .store + .state + .get_account_by_address(&nssa::Address::new(acc2)) + .balance; assert_eq!(bal_from, 9900); assert_eq!(bal_to, 20100); @@ -490,10 +497,7 @@ mod tests { // let tx_roots = sequencer.get_tree_roots(); // Fill the mempool - let dummy_tx = MempoolTransaction { - auth_tx: tx.clone(), - }; - sequencer.mempool.push_item(dummy_tx); + sequencer.mempool.push_item(tx.clone()); let result = sequencer.push_tx_into_mempool_pre_check(tx); @@ -525,8 +529,7 @@ mod tests { let genesis_height = sequencer.chain_height; let tx = create_dummy_transaction(); - let tx_mempool = MempoolTransaction { auth_tx: tx }; - sequencer.mempool.push_item(tx_mempool); + sequencer.mempool.push_item(tx); let block_id = sequencer.produce_new_block_with_mempool_transactions(); assert!(block_id.is_ok()); @@ -553,16 +556,11 @@ mod tests { let tx = create_dummy_transaction_native_token_transfer(acc1, 0, acc2, 100, sign_key1); - let tx_mempool_original = MempoolTransaction { - auth_tx: tx.clone(), - }; - let tx_mempool_replay = MempoolTransaction { - auth_tx: tx.clone(), - }; - + let tx_original = tx.clone(); + let tx_replay = tx.clone(); // Pushing two copies of the same tx to the mempool - sequencer.mempool.push_item(tx_mempool_original); - sequencer.mempool.push_item(tx_mempool_replay); + sequencer.mempool.push_item(tx_original); + sequencer.mempool.push_item(tx_replay); // Create block let current_height = sequencer @@ -599,10 +597,7 @@ mod tests { let tx = create_dummy_transaction_native_token_transfer(acc1, 0, acc2, 100, sign_key1); // The transaction should be included the first time - let tx_mempool_original = MempoolTransaction { - auth_tx: tx.clone(), - }; - sequencer.mempool.push_item(tx_mempool_original); + sequencer.mempool.push_item(tx.clone()); let current_height = sequencer .produce_new_block_with_mempool_transactions() .unwrap(); @@ -614,8 +609,7 @@ mod tests { assert_eq!(block.transactions, vec![tx.clone()]); // Add same transaction should fail - let tx_mempool_replay = MempoolTransaction { auth_tx: tx }; - sequencer.mempool.push_item(tx_mempool_replay); + sequencer.mempool.push_item(tx); let current_height = sequencer .produce_new_block_with_mempool_transactions() .unwrap(); diff --git a/sequencer_core/src/mempool_transaction.rs b/sequencer_core/src/mempool_transaction.rs deleted file mode 100644 index d59be5b..0000000 --- a/sequencer_core/src/mempool_transaction.rs +++ /dev/null @@ -1,20 +0,0 @@ -use common::merkle_tree_public::TreeHashType; -use mempool::mempoolitem::MemPoolItem; - -pub struct MempoolTransaction { - pub auth_tx: nssa::PublicTransaction, -} - -impl From for MempoolTransaction { - fn from(auth_tx: nssa::PublicTransaction) -> Self { - Self { auth_tx } - } -} - -impl MemPoolItem for MempoolTransaction { - type Identifier = TreeHashType; - - fn identifier(&self) -> Self::Identifier { - self.auth_tx.hash() - } -} From 3188f98117619484aef6cf38ce30c22d51726985 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sat, 9 Aug 2025 18:24:53 -0300 Subject: [PATCH 10/58] early returns instead of panicking in program methods --- .../guest/src/bin/authenticated_transfer.rs | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs index b24e004..979064c 100644 --- a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs +++ b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs @@ -9,17 +9,21 @@ fn main() { let input_accounts: Vec = env::read(); let balance_to_move: u128 = env::read(); - // Unpack sender and receiver - assert_eq!(input_accounts.len(), 2); - let [sender, receiver] = input_accounts - .try_into() - .unwrap_or_else(|_| panic!("Bad input")); + // Continue only if input_accounts is an array of two elements + let [sender, receiver] = match input_accounts.try_into() { + Ok(array) => array, + Err(_) => return, // silently return on bad input + }; - // Check sender has authorized this operation - assert!(sender.is_authorized); + // Continue only if the sender has authorized this operation + if !sender.is_authorized { + return; + } - // Check sender has enough balance - assert!(sender.account.balance >= balance_to_move); + // Continue only if the sender has enough balance + if sender.account.balance < balance_to_move { + return; + } // Create accounts post states, with updated balances let mut sender_post = sender.account.clone(); From fcb90f6f11892a04514e9bd81c1f3f407d7ada1f Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sat, 9 Aug 2025 18:37:04 -0300 Subject: [PATCH 11/58] clippy --- nssa/src/lib.rs | 2 +- nssa/src/public_transaction.rs | 4 ++-- nssa/src/signature.rs | 2 +- nssa/src/state.rs | 14 +++++++------- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs index 455afa2..7010a88 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -1,6 +1,6 @@ use nssa_core::{ account::{Account, AccountWithMetadata}, - program::{DEFAULT_PROGRAM_ID, ProgramId}, + program::DEFAULT_PROGRAM_ID, }; use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID}; use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor}; diff --git a/nssa/src/public_transaction.rs b/nssa/src/public_transaction.rs index 828e906..756cbdd 100644 --- a/nssa/src/public_transaction.rs +++ b/nssa/src/public_transaction.rs @@ -1,5 +1,5 @@ use nssa_core::{ - account::{Account, Nonce}, + account::Nonce, program::ProgramId, }; use serde::{Deserialize, Serialize}; @@ -92,6 +92,6 @@ impl PublicTransaction { let bytes = serde_cbor::to_vec(&self).unwrap(); let mut hasher = sha2::Sha256::new(); hasher.update(&bytes); - hasher.finalize_fixed().try_into().unwrap() + hasher.finalize_fixed().into() } } diff --git a/nssa/src/signature.rs b/nssa/src/signature.rs index be8b937..4c0ebb7 100644 --- a/nssa/src/signature.rs +++ b/nssa/src/signature.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::{address::Address, public_transaction::Message}; +use crate::public_transaction::Message; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub(crate) struct Signature; diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 986073c..b4d697a 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -40,7 +40,7 @@ impl V01State { pub fn transition_from_public_transaction(&mut self, tx: &PublicTransaction) -> Result<(), ()> { let state_diff = self - .execute_and_verify_public_transaction(&tx) + .execute_and_verify_public_transaction(tx) .map_err(|_| ())?; for (address, post) in state_diff.into_iter() { @@ -58,7 +58,7 @@ impl V01State { fn get_account_by_address_mut(&mut self, address: Address) -> &mut Account { self.public_state .entry(address) - .or_insert_with(Account::default) + .or_default() } pub fn get_account_by_address(&self, address: &Address) -> Account { @@ -137,7 +137,7 @@ impl V01State { .addresses .iter() .cloned() - .zip(post_states.into_iter()) + .zip(post_states) .collect()) } } @@ -166,7 +166,7 @@ mod tests { fn test_1() { let initial_data = [([1; 32], 100)]; let mut state = V01State::new_with_genesis_accounts(&initial_data); - let from = Address::new(initial_data[0].0.clone()); + let from = Address::new(initial_data[0].0); let from_key = PrivateKey(1); let to = Address::new([2; 32]); let balance_to_move = 5; @@ -184,9 +184,9 @@ mod tests { fn test_2() { let initial_data = [([1; 32], 100), ([99; 32], 200)]; let mut state = V01State::new_with_genesis_accounts(&initial_data); - let from = Address::new(initial_data[1].0.clone()); + let from = Address::new(initial_data[1].0); let from_key = PrivateKey(99); - let to = Address::new(initial_data[0].0.clone()); + let to = Address::new(initial_data[0].0); let balance_to_move = 8; let to_previous_balance = state.get_account_by_address(&to).balance; let tx = @@ -203,7 +203,7 @@ mod tests { fn test_3() { let initial_data = [([1; 32], 100)]; let mut state = V01State::new_with_genesis_accounts(&initial_data); - let address_1 = Address::new(initial_data[0].0.clone()); + let address_1 = Address::new(initial_data[0].0); let key_1 = PrivateKey(1); let address_2 = Address::new([2; 32]); From 00773d7457cb185c8b9cc55ae2e76dd421bbcc9a Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sat, 9 Aug 2025 18:40:32 -0300 Subject: [PATCH 12/58] clippy --- accounts/src/account_core/address.rs | 2 -- accounts/src/account_core/mod.rs | 4 ++-- accounts/src/key_management/mod.rs | 8 ++++---- common/src/block.rs | 2 +- common/src/rpc_primitives/requests.rs | 1 - common/src/sequencer_client/json.rs | 1 - common/src/sequencer_client/mod.rs | 1 - common/src/test_utils.rs | 8 +------- nssa/core/src/program.rs | 1 - nssa/src/signature.rs | 4 ++-- nssa/src/state.rs | 9 +++++---- sequencer_core/src/lib.rs | 13 +++---------- sequencer_core/src/sequencer_store/block_store.rs | 4 ++-- sequencer_core/src/sequencer_store/mod.rs | 8 ++------ sequencer_rpc/src/process.rs | 10 +++------- wallet/src/lib.rs | 7 +------ 16 files changed, 26 insertions(+), 57 deletions(-) diff --git a/accounts/src/account_core/address.rs b/accounts/src/account_core/address.rs index 3b61521..0740027 100644 --- a/accounts/src/account_core/address.rs +++ b/accounts/src/account_core/address.rs @@ -1,5 +1,3 @@ -use common::transaction::SignaturePublicKey; -use tiny_keccak::{Hasher, Keccak}; // TODO: Consider wrapping `AccountAddress` in a struct. diff --git a/accounts/src/account_core/mod.rs b/accounts/src/account_core/mod.rs index 6b8384d..58154f0 100644 --- a/accounts/src/account_core/mod.rs +++ b/accounts/src/account_core/mod.rs @@ -117,7 +117,7 @@ impl AccountPublicMask { impl Account { pub fn new() -> Self { let key_holder = AddressKeyHolder::new_os_random(); - let public_key = nssa::PublicKey::new(&key_holder.get_pub_account_signing_key()); + let public_key = nssa::PublicKey::new(key_holder.get_pub_account_signing_key()); let address = nssa::Address::from_public_key(&public_key); let balance = 0; let utxos = HashMap::new(); @@ -132,7 +132,7 @@ impl Account { pub fn new_with_balance(balance: u64) -> Self { let key_holder = AddressKeyHolder::new_os_random(); - let public_key = nssa::PublicKey::new(&key_holder.get_pub_account_signing_key()); + let public_key = nssa::PublicKey::new(key_holder.get_pub_account_signing_key()); let address = nssa::Address::from_public_key(&public_key); let utxos = HashMap::new(); diff --git a/accounts/src/key_management/mod.rs b/accounts/src/key_management/mod.rs index 055b34d..c0d26fb 100644 --- a/accounts/src/key_management/mod.rs +++ b/accounts/src/key_management/mod.rs @@ -1,15 +1,15 @@ use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit}; use constants_types::{CipherText, Nonce}; use elliptic_curve::point::AffineCoordinates; -use k256::{ecdsa::SigningKey, AffinePoint, FieldBytes}; +use k256::AffinePoint; use log::info; -use rand::{rngs::OsRng, Rng, RngCore}; +use rand::{rngs::OsRng, Rng}; use secret_holders::{SeedHolder, TopSecretKeyHolder, UTXOSecretKeyHolder}; use serde::{Deserialize, Serialize}; use crate::account_core::PublicKey; pub type PublicAccountSigningKey = [u8; 32]; -use nssa::{self, PrivateKey}; +use nssa::{self}; pub mod constants_types; pub mod ephemeral_key_holder; @@ -118,7 +118,7 @@ mod tests { use elliptic_curve::point::AffineCoordinates; use k256::{AffinePoint, ProjectivePoint, Scalar}; - use crate::{account_core::address, key_management::ephemeral_key_holder::EphemeralKeyHolder}; + use crate::key_management::ephemeral_key_holder::EphemeralKeyHolder; use super::*; diff --git a/common/src/block.rs b/common/src/block.rs index 52d70ba..65514e2 100644 --- a/common/src/block.rs +++ b/common/src/block.rs @@ -1,7 +1,7 @@ use rs_merkle::Hasher; use serde::{Deserialize, Serialize}; -use crate::{merkle_tree_public::hasher::OwnHasher, transaction::Transaction}; +use crate::merkle_tree_public::hasher::OwnHasher; use nssa; pub type BlockHash = [u8; 32]; diff --git a/common/src/rpc_primitives/requests.rs b/common/src/rpc_primitives/requests.rs index fa78f74..ebfad60 100644 --- a/common/src/rpc_primitives/requests.rs +++ b/common/src/rpc_primitives/requests.rs @@ -1,6 +1,5 @@ use crate::block::Block; use crate::parse_request; -use crate::transaction::Transaction; use super::errors::RpcParseError; use super::parser::parse_params; diff --git a/common/src/sequencer_client/json.rs b/common/src/sequencer_client/json.rs index 11a8888..3afb54f 100644 --- a/common/src/sequencer_client/json.rs +++ b/common/src/sequencer_client/json.rs @@ -1,6 +1,5 @@ use serde::{Deserialize, Serialize}; -use crate::transaction::Transaction; //Requests diff --git a/common/src/sequencer_client/mod.rs b/common/src/sequencer_client/mod.rs index 7fcca2d..82f30a9 100644 --- a/common/src/sequencer_client/mod.rs +++ b/common/src/sequencer_client/mod.rs @@ -8,7 +8,6 @@ use reqwest::Client; use serde_json::Value; use crate::sequencer_client::json::AccountInitialData; -use crate::transaction::Transaction; use crate::{SequencerClientError, SequencerRpcError}; pub mod json; diff --git a/common/src/test_utils.rs b/common/src/test_utils.rs index 0a98dd2..bf734af 100644 --- a/common/src/test_utils.rs +++ b/common/src/test_utils.rs @@ -1,12 +1,6 @@ -use k256::ecdsa::SigningKey; use nssa; -use secp256k1_zkp::Tweak; -use crate::{ - block::{Block, HashableBlockData}, - execution_input::PublicNativeTokenSend, - transaction::{SignaturePrivateKey, Transaction, TransactionBody, TxKind}, -}; +use crate::block::{Block, HashableBlockData}; //Dummy producers diff --git a/nssa/core/src/program.rs b/nssa/core/src/program.rs index ec9bdc7..24db838 100644 --- a/nssa/core/src/program.rs +++ b/nssa/core/src/program.rs @@ -1,4 +1,3 @@ -use serde::{Deserialize, Serialize}; use crate::account::{Account, AccountWithMetadata}; diff --git a/nssa/src/signature.rs b/nssa/src/signature.rs index 4c0ebb7..9cdc9b6 100644 --- a/nssa/src/signature.rs +++ b/nssa/src/signature.rs @@ -28,11 +28,11 @@ impl PublicKey { } impl Signature { - pub(crate) fn new(key: &PrivateKey, message: &[u8]) -> Self { + pub(crate) fn new(_key: &PrivateKey, message: &[u8]) -> Self { Self } - pub(crate) fn is_valid_for(&self, message: &Message, public_key: &PublicKey) -> bool { + pub(crate) fn is_valid_for(&self, _message: &Message, _public_key: &PublicKey) -> bool { // TODO: implement true } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index b4d697a..319a9c2 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -19,9 +19,11 @@ impl V01State { .to_owned() .into_iter() .map(|(address_value, balance)| { - let mut account = Account::default(); - account.balance = balance; - account.program_owner = AUTHENTICATED_TRANSFER_PROGRAM.id; + let account = Account { + balance: balance, + program_owner: AUTHENTICATED_TRANSFER_PROGRAM.id, + ..Account::default() + }; let address = Address::new(address_value); (address, account) }) @@ -188,7 +190,6 @@ mod tests { let from_key = PrivateKey(99); let to = Address::new(initial_data[0].0); let balance_to_move = 8; - let to_previous_balance = state.get_account_by_address(&to).balance; let tx = transfer_transaction_for_tests(from.clone(), from_key, 0, to.clone(), balance_to_move); state.transition_from_public_transaction(&tx).unwrap(); diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 33e00df..0123b66 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -1,14 +1,9 @@ use std::fmt::Display; -use accounts::account_core::address::{self, AccountAddress}; use anyhow::Result; use common::{ block::HashableBlockData, - execution_input::PublicNativeTokenSend, merkle_tree_public::TreeHashType, - nullifier::UTXONullifier, - transaction::{AuthenticatedTransaction, Transaction, TransactionBody, TxKind}, - utxo_commitment::UTXOCommitment, }; use config::SequencerConfig; use mempool::MemPool; @@ -86,7 +81,7 @@ impl SequencerCore { let authenticated_tx = self.transaction_pre_check(transaction)?; - self.mempool.push_item(authenticated_tx.into()); + self.mempool.push_item(authenticated_tx); Ok(()) } @@ -144,7 +139,7 @@ mod tests { use crate::config::AccountInitialData; use super::*; - use nssa::Program; + fn setup_sequencer_config_variable_initial_accounts( initial_accounts: Vec, @@ -310,8 +305,6 @@ mod tests { let initial_accounts = vec![initial_acc1, initial_acc2]; - let intial_accounts_len = initial_accounts.len(); - let config = setup_sequencer_config_variable_initial_accounts(initial_accounts); let sequencer = SequencerCore::start_from_config(config.clone()); @@ -507,7 +500,7 @@ mod tests { assert!(matches!( result, - Err(TransactionMalformationErrorKind::MempoolFullForRound { .. }) + Err(TransactionMalformationErrorKind::MempoolFullForRound) )); } diff --git a/sequencer_core/src/sequencer_store/block_store.rs b/sequencer_core/src/sequencer_store/block_store.rs index ca31c8f..b06fd73 100644 --- a/sequencer_core/src/sequencer_store/block_store.rs +++ b/sequencer_core/src/sequencer_store/block_store.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, path::Path}; use anyhow::Result; -use common::{block::Block, merkle_tree_public::TreeHashType, transaction::Transaction}; +use common::{block::Block, merkle_tree_public::TreeHashType}; use storage::RocksDBIO; pub struct SequecerBlockStore { @@ -76,7 +76,7 @@ fn block_to_transactions_map(block: &Block) -> HashMap { #[cfg(test)] mod tests { use super::*; - use nssa::Program; + use tempfile::tempdir; // // fn create_dummy_block_with_transaction(block_id: u64) -> (Block, nssa::PublicTransaction) { diff --git a/sequencer_core/src/sequencer_store/mod.rs b/sequencer_core/src/sequencer_store/mod.rs index 9fc266c..e0a15fc 100644 --- a/sequencer_core/src/sequencer_store/mod.rs +++ b/sequencer_core/src/sequencer_store/mod.rs @@ -1,12 +1,8 @@ -use std::{collections::HashSet, path::Path}; +use std::path::Path; use accounts::account_core::address::AccountAddress; use block_store::SequecerBlockStore; -use common::{ - block::HashableBlockData, - merkle_tree_public::merkle_tree::{PublicTransactionMerkleTree, UTXOCommitmentsMerkleTree}, - nullifier::UTXONullifier, -}; +use common::block::HashableBlockData; use nssa; use rand::{rngs::OsRng, RngCore}; diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 066700c..fabf31e 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -18,8 +18,7 @@ use common::{ use common::rpc_primitives::requests::{ GetBlockDataRequest, GetBlockDataResponse, GetGenesisIdRequest, GetGenesisIdResponse, - GetLastBlockRequest, GetLastBlockResponse, HelloRequest, HelloResponse, RegisterAccountRequest, - RegisterAccountResponse, SendTxRequest, SendTxResponse, + GetLastBlockRequest, GetLastBlockResponse, HelloRequest, HelloResponse, SendTxRequest, SendTxResponse, }; use super::{respond, types::err_rpc::RpcErr, JsonHandler}; @@ -203,11 +202,8 @@ mod tests { use std::sync::Arc; use crate::{rpc_handler, JsonHandler}; - use common::{ - rpc_primitives::RpcPollingConfig, - transaction::{SignaturePrivateKey, Transaction, TransactionBody}, - }; - use nssa::Program; + use common::rpc_primitives::RpcPollingConfig; + use sequencer_core::{ config::{AccountInitialData, SequencerConfig}, SequencerCore, diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 26a4fe3..9f80d8e 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -1,23 +1,18 @@ use std::sync::Arc; use common::{ - execution_input::PublicNativeTokenSend, sequencer_client::{json::SendTxResponse, SequencerClient}, - transaction::Transaction, ExecutionFailureKind, }; use accounts::account_core::{address::AccountAddress, Account}; use anyhow::Result; use chain_storage::NodeChainStore; -use common::transaction::TransactionBody; use config::NodeConfig; use log::info; -use sc_core::proofs_circuits::pedersen_commitment_vec; use tokio::sync::RwLock; use clap::{Parser, Subcommand}; -use nssa; use crate::helperfunctions::{fetch_config, produce_account_addr_from_hex}; @@ -101,7 +96,7 @@ impl NodeCore { let signing_key = account.key_holder.get_pub_account_signing_key(); let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[&signing_key]); + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); let tx = nssa::PublicTransaction::new(message, witness_set); From 7d23983309935065fb199b8661437232ea270b7b Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sat, 9 Aug 2025 19:05:49 -0300 Subject: [PATCH 13/58] fix test --- nssa/src/lib.rs | 1 + nssa/src/public_transaction.rs | 2 +- nssa/src/signature.rs | 11 ++++++----- sequencer_core/src/lib.rs | 28 ++++++++++++++++------------ 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs index 7010a88..a8a3aac 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -15,6 +15,7 @@ pub use nssa_core::program::Program; pub use public_transaction::PublicTransaction; pub use signature::PrivateKey; pub use signature::PublicKey; +pub use signature::Signature; pub use state::V01State; pub const AUTHENTICATED_TRANSFER_PROGRAM: Program = Program { diff --git a/nssa/src/public_transaction.rs b/nssa/src/public_transaction.rs index 756cbdd..ab91039 100644 --- a/nssa/src/public_transaction.rs +++ b/nssa/src/public_transaction.rs @@ -42,7 +42,7 @@ impl Message { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct WitnessSet { - pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>, + pub signatures_and_public_keys: Vec<(Signature, PublicKey)>, } impl WitnessSet { diff --git a/nssa/src/signature.rs b/nssa/src/signature.rs index 9cdc9b6..e4c4f30 100644 --- a/nssa/src/signature.rs +++ b/nssa/src/signature.rs @@ -3,10 +3,11 @@ use serde::{Deserialize, Serialize}; use crate::public_transaction::Message; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub(crate) struct Signature; +pub struct Signature(pub(crate) u8); // TODO: Dummy impl. Replace by actual private key. // TODO: Remove Debug, Clone, Serialize, Deserialize, PartialEq and Eq for security reasons +// TODO: Implement Zeroize #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct PrivateKey(pub(crate) u8); @@ -28,12 +29,12 @@ impl PublicKey { } impl Signature { - pub(crate) fn new(_key: &PrivateKey, message: &[u8]) -> Self { - Self + pub(crate) fn new(key: &PrivateKey, message: &[u8]) -> Self { + Signature(key.0) } - pub(crate) fn is_valid_for(&self, _message: &Message, _public_key: &PublicKey) -> bool { + pub fn is_valid_for(&self, _message: &Message, public_key: &PublicKey) -> bool { // TODO: implement - true + self.0 == public_key.0 } } diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 0123b66..2e34ff7 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -1,10 +1,7 @@ use std::fmt::Display; use anyhow::Result; -use common::{ - block::HashableBlockData, - merkle_tree_public::TreeHashType, -}; +use common::{block::HashableBlockData, merkle_tree_public::TreeHashType}; use config::SequencerConfig; use mempool::MemPool; use sequencer_store::SequecerChainStore; @@ -63,10 +60,18 @@ impl SequencerCore { pub fn transaction_pre_check( &mut self, tx: nssa::PublicTransaction, - // tx_roots: [[u8; 32]; 2], ) -> Result { - // TODO: Stateless checks here - Ok(tx) + // Stateless checks here + if tx + .witness_set() + .signatures_and_public_keys + .iter() + .all(|(signature, public_key)| signature.is_valid_for(tx.message(), public_key)) + { + Ok(tx) + } else { + Err(TransactionMalformationErrorKind::InvalidSignature) + } } pub fn push_tx_into_mempool_pre_check( @@ -92,8 +97,6 @@ impl SequencerCore { ) -> Result { self.store.state.transition_from_public_transaction(&tx)?; - // self.store.pub_tx_store.add_tx(mempool_tx.auth_tx); - Ok(tx) } @@ -139,7 +142,6 @@ mod tests { use crate::config::AccountInitialData; use super::*; - fn setup_sequencer_config_variable_initial_accounts( initial_accounts: Vec, @@ -396,12 +398,14 @@ mod tests { let tx = common::test_utils::create_dummy_transaction_native_token_transfer( acc1, 0, acc2, 10, sign_key2, ); - // let tx_roots = sequencer.get_tree_roots(); + + // Signature is valid, stateless check pass let tx = sequencer.transaction_pre_check(tx).unwrap(); + // Signature is not from sender. Execution fails let result = sequencer.execute_check_transaction_on_state(tx); - assert_eq!(result.err().unwrap(), ()); + assert!(matches!(result, Err(()))); } #[test] From 04f6474799456401380f713167337c22c0385191 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sat, 9 Aug 2025 19:20:19 -0300 Subject: [PATCH 14/58] clippy --- .../guest/src/bin/authenticated_transfer.rs | 2 +- nssa/src/signature.rs | 2 +- nssa/src/state.rs | 17 +++++------------ sequencer_rpc/src/process.rs | 2 +- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs index 979064c..d3ac729 100644 --- a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs +++ b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs @@ -1,4 +1,4 @@ -use nssa_core::account::{Account, AccountWithMetadata}; +use nssa_core::account::AccountWithMetadata; use risc0_zkvm::guest::env; /// A transfer of balance program. diff --git a/nssa/src/signature.rs b/nssa/src/signature.rs index e4c4f30..9e36a7c 100644 --- a/nssa/src/signature.rs +++ b/nssa/src/signature.rs @@ -29,7 +29,7 @@ impl PublicKey { } impl Signature { - pub(crate) fn new(key: &PrivateKey, message: &[u8]) -> Self { + pub(crate) fn new(key: &PrivateKey, _message: &[u8]) -> Self { Signature(key.0) } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 319a9c2..66328b1 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -16,11 +16,11 @@ pub struct V01State { impl V01State { pub fn new_with_genesis_accounts(initial_data: &[([u8; 32], u128)]) -> Self { let public_state = initial_data - .to_owned() - .into_iter() + .iter() + .copied() .map(|(address_value, balance)| { let account = Account { - balance: balance, + balance, program_owner: AUTHENTICATED_TRANSFER_PROGRAM.id, ..Account::default() }; @@ -58,9 +58,7 @@ impl V01State { } fn get_account_by_address_mut(&mut self, address: Address) -> &mut Account { - self.public_state - .entry(address) - .or_default() + self.public_state.entry(address).or_default() } pub fn get_account_by_address(&self, address: &Address) -> Account { @@ -135,12 +133,7 @@ impl V01State { return Err(()); } - Ok(message - .addresses - .iter() - .cloned() - .zip(post_states) - .collect()) + Ok(message.addresses.iter().cloned().zip(post_states).collect()) } } diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index fabf31e..7647b6d 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -505,7 +505,7 @@ mod tests { }, "witness_set": { "signatures_and_public_keys": [ - [null, 1] + [1, 1] ] } } From 55e241dc97f5ceee25bdfd562f6f1aec9a09c661 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sat, 9 Aug 2025 19:49:07 -0300 Subject: [PATCH 15/58] add error handling --- Cargo.toml | 4 ++-- nssa/Cargo.toml | 1 + nssa/core/src/program.rs | 17 ++++++++--------- nssa/src/error.rs | 16 ++++++++++++++++ nssa/src/lib.rs | 24 ++++++++++++++++++------ nssa/src/state.rs | 38 +++++++++++++++++++++----------------- sequencer_core/src/lib.rs | 7 +++---- 7 files changed, 69 insertions(+), 38 deletions(-) create mode 100644 nssa/src/error.rs diff --git a/Cargo.toml b/Cargo.toml index 4a938b4..ac01a24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ members = [ ] [workspace.dependencies] -anyhow = "1.0" +anyhow = "1.0.98" num_cpus = "1.13.1" openssl = { version = "0.10", features = ["vendored"] } openssl-probe = { version = "0.1.2" } @@ -30,7 +30,7 @@ lazy_static = "1.5.0" env_logger = "0.10" log = "0.4" lru = "0.7.8" -thiserror = "1.0" +thiserror = "2.0.12" rs_merkle = "1.4" sha2 = "0.10.8" hex = "0.4.3" diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index ece3262..2bb5db1 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] +thiserror = "2.0.12" risc0-zkvm = "2.3.1" nssa-core = { path = "core"} program-methods = { path = "program_methods" } diff --git a/nssa/core/src/program.rs b/nssa/core/src/program.rs index 24db838..6ecd4cf 100644 --- a/nssa/core/src/program.rs +++ b/nssa/core/src/program.rs @@ -1,4 +1,3 @@ - use crate::account::{Account, AccountWithMetadata}; pub type ProgramId = [u32; 8]; @@ -19,26 +18,26 @@ pub fn validate_constraints( pre_states: &[AccountWithMetadata], post_states: &[Account], executing_program_id: ProgramId, -) -> Result<(), ()> { +) -> bool { // 1. Lengths must match if pre_states.len() != post_states.len() { - return Err(()); + return false; } for (pre, post) in pre_states.iter().zip(post_states) { // 2. Nonce must remain unchanged if pre.account.nonce != post.nonce { - return Err(()); + return false; } // 3. Ownership change only allowed from default accounts if pre.account.program_owner != post.program_owner && pre.account != Account::default() { - return Err(()); + return false; } // 4. Decreasing balance only allowed if owned by executing program if post.balance < pre.account.balance && pre.account.program_owner != executing_program_id { - return Err(()); + return false; } // 5. Data changes only allowed if owned by executing program @@ -46,7 +45,7 @@ pub fn validate_constraints( && (executing_program_id != pre.account.program_owner || executing_program_id != post.program_owner) { - return Err(()); + return false; } } @@ -54,8 +53,8 @@ pub fn validate_constraints( let total_balance_pre_states: u128 = pre_states.iter().map(|pre| pre.account.balance).sum(); let total_balance_post_states: u128 = post_states.iter().map(|post| post.balance).sum(); if total_balance_pre_states != total_balance_post_states { - return Err(()); + return false; } - Ok(()) + true } diff --git a/nssa/src/error.rs b/nssa/src/error.rs new file mode 100644 index 0000000..dd62570 --- /dev/null +++ b/nssa/src/error.rs @@ -0,0 +1,16 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum NssaError { + #[error("Invalid input: {0}")] + InvalidInput(String), + + #[error("Operation failed")] + OperationFailed, + + #[error("Risc0 error: {0}")] + ProgramExecutionFailed(String), + + #[error("Program violated execution rules")] + InvalidProgramBehavior, +} diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs index a8a3aac..11f2231 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -6,6 +6,7 @@ use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID}; use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor}; mod address; +pub mod error; pub mod public_transaction; mod signature; mod state; @@ -18,6 +19,8 @@ pub use signature::PublicKey; pub use signature::Signature; pub use state::V01State; +use crate::error::NssaError; + pub const AUTHENTICATED_TRANSFER_PROGRAM: Program = Program { id: AUTHENTICATED_TRANSFER_ID, elf: AUTHENTICATED_TRANSFER_ELF, @@ -28,10 +31,14 @@ fn write_inputs( pre_states: &[AccountWithMetadata], instruction_data: u128, env_builder: &mut ExecutorEnvBuilder, -) -> Result<(), ()> { +) -> Result<(), NssaError> { let pre_states = pre_states.to_vec(); - env_builder.write(&pre_states).map_err(|_| ())?; - env_builder.write(&instruction_data).map_err(|_| ())?; + env_builder + .write(&pre_states) + .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; + env_builder + .write(&instruction_data) + .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; Ok(()) } @@ -39,7 +46,7 @@ fn execute_public( pre_states: &[AccountWithMetadata], instruction_data: u128, program: &Program, -) -> Result, ()> { +) -> Result, NssaError> { // Write inputs to the program let mut env_builder = ExecutorEnv::builder(); write_inputs(pre_states, instruction_data, &mut env_builder)?; @@ -47,10 +54,15 @@ fn execute_public( // Execute the program (without proving) let executor = default_executor(); - let session_info = executor.execute(env, program.elf).map_err(|_| ())?; + let session_info = executor + .execute(env, program.elf) + .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; // Get outputs - let mut post_states: Vec = session_info.journal.decode().map_err(|_| ())?; + let mut post_states: Vec = session_info + .journal + .decode() + .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; // Claim any output account with default program owner field for account in post_states.iter_mut() { diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 66328b1..6459a30 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -1,5 +1,5 @@ use crate::{ - AUTHENTICATED_TRANSFER_PROGRAM, address::Address, execute_public, + AUTHENTICATED_TRANSFER_PROGRAM, address::Address, error::NssaError, execute_public, public_transaction::PublicTransaction, }; use nssa_core::{ @@ -40,10 +40,11 @@ impl V01State { } } - pub fn transition_from_public_transaction(&mut self, tx: &PublicTransaction) -> Result<(), ()> { - let state_diff = self - .execute_and_verify_public_transaction(tx) - .map_err(|_| ())?; + pub fn transition_from_public_transaction( + &mut self, + tx: &PublicTransaction, + ) -> Result<(), NssaError> { + let state_diff = self.execute_and_verify_public_transaction(tx)?; for (address, post) in state_diff.into_iter() { let current_account = self.get_account_by_address_mut(address); @@ -71,17 +72,21 @@ impl V01State { fn execute_and_verify_public_transaction( &mut self, tx: &PublicTransaction, - ) -> Result, ()> { + ) -> Result, NssaError> { let message = tx.message(); let witness_set = tx.witness_set(); // All addresses must be different if message.addresses.iter().collect::>().len() != message.addresses.len() { - return Err(()); + return Err(NssaError::InvalidInput( + "Duplicate addresses found in message".into(), + )); } if message.nonces.len() != witness_set.signatures_and_public_keys.len() { - return Err(()); + return Err(NssaError::InvalidInput( + "Mismatch between number of nonces and signatures/public keys".into(), + )); } let mut authorized_addresses = Vec::new(); @@ -92,14 +97,16 @@ impl V01State { { // Check the signature is valid if !signature.is_valid_for(message, public_key) { - return Err(()); + return Err(NssaError::InvalidInput( + "Invalid signature for given message and public key".into(), + )); } // Check the nonce corresponds to the current nonce on the public state. let address = Address::from_public_key(public_key); let current_nonce = self.get_account_by_address(&address).nonce; if current_nonce != *nonce { - return Err(()); + return Err(NssaError::InvalidInput("Nonce mismatch".into())); } authorized_addresses.push(address); @@ -118,19 +125,16 @@ impl V01State { // Check the `program_id` corresponds to a built-in program // Only allowed program so far is the authenticated transfer program let Some(program) = self.builtin_programs.get(&message.program_id) else { - return Err(()); + return Err(NssaError::InvalidInput("Unknown program".into())); }; // // Execute program - let post_states = - execute_public(&pre_states, message.instruction_data, program).map_err(|_| ())?; + let post_states = execute_public(&pre_states, message.instruction_data, program)?; // Verify execution corresponds to a well-behaved program. // See the # Programs section for the definition of the `validate_constraints` method. - validate_constraints(&pre_states, &post_states, message.program_id).map_err(|_| ())?; - - if post_states.len() != message.addresses.len() { - return Err(()); + if !validate_constraints(&pre_states, &post_states, message.program_id) { + return Err(NssaError::InvalidProgramBehavior); } Ok(message.addresses.iter().cloned().zip(post_states).collect()) diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 2e34ff7..aec2703 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -94,7 +94,7 @@ impl SequencerCore { fn execute_check_transaction_on_state( &mut self, tx: nssa::PublicTransaction, - ) -> Result { + ) -> Result { self.store.state.transition_from_public_transaction(&tx)?; Ok(tx) @@ -405,7 +405,7 @@ mod tests { // Signature is not from sender. Execution fails let result = sequencer.execute_check_transaction_on_state(tx); - assert!(matches!(result, Err(()))); + assert!(matches!(result, Err(nssa::error::NssaError::ProgramExecutionFailed(_)))); } #[test] @@ -438,8 +438,7 @@ mod tests { let result = sequencer.execute_check_transaction_on_state(result.unwrap()); let is_failed_at_balance_mismatch = matches!( result.err().unwrap(), - // TransactionMalformationErrorKind::BalanceMismatch { tx: _ } - () + nssa::error::NssaError::ProgramExecutionFailed(_) ); assert!(is_failed_at_balance_mismatch); From 51d8ac47cf2835b9ba759e52ad1facbf55dbf9d9 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sat, 9 Aug 2025 20:16:18 -0300 Subject: [PATCH 16/58] refactor program --- common/src/test_utils.rs | 29 ++------------- nssa/core/src/program.rs | 5 --- nssa/src/lib.rs | 69 ++++++++++++++++++++---------------- nssa/src/state.rs | 6 ++-- sequencer_rpc/src/process.rs | 4 +-- wallet/src/lib.rs | 2 +- 6 files changed, 47 insertions(+), 68 deletions(-) diff --git a/common/src/test_utils.rs b/common/src/test_utils.rs index bf734af..6fe3324 100644 --- a/common/src/test_utils.rs +++ b/common/src/test_utils.rs @@ -31,7 +31,7 @@ pub fn produce_dummy_block( } pub fn produce_dummy_empty_transaction() -> nssa::PublicTransaction { - let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id; + let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id(); let addresses = vec![]; let nonces = vec![]; let instruction_data = 0; @@ -42,31 +42,6 @@ pub fn produce_dummy_empty_transaction() -> nssa::PublicTransaction { nssa::PublicTransaction::new(message, witness_set) } -// pub fn create_dummy_private_transaction_random_signer( -// nullifier_created_hashes: Vec<[u8; 32]>, -// utxo_commitments_spent_hashes: Vec<[u8; 32]>, -// utxo_commitments_created_hashes: Vec<[u8; 32]>, -// ) -> Transaction { -// let mut rng = rand::thread_rng(); -// -// let body = TransactionBody { -// tx_kind: TxKind::Private, -// execution_input: vec![], -// execution_output: vec![], -// utxo_commitments_spent_hashes, -// utxo_commitments_created_hashes, -// nullifier_created_hashes, -// execution_proof_private: "dummy_proof".to_string(), -// encoded_data: vec![], -// ephemeral_pub_key: vec![10, 11, 12], -// commitment: vec![], -// tweak: Tweak::new(&mut rng), -// secret_r: [0; 32], -// sc_addr: "sc_addr".to_string(), -// }; -// Transaction::new(body, SignaturePrivateKey::random(&mut rng)) -// } - pub fn create_dummy_transaction_native_token_transfer( from: [u8; 32], nonce: u128, @@ -76,7 +51,7 @@ pub fn create_dummy_transaction_native_token_transfer( ) -> nssa::PublicTransaction { let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)]; let nonces = vec![nonce]; - let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id; + let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id(); let message = nssa::public_transaction::Message::new(program_id, addresses, nonces, balance_to_move); let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[&signing_key]); diff --git a/nssa/core/src/program.rs b/nssa/core/src/program.rs index 6ecd4cf..ce58a3b 100644 --- a/nssa/core/src/program.rs +++ b/nssa/core/src/program.rs @@ -3,11 +3,6 @@ use crate::account::{Account, AccountWithMetadata}; pub type ProgramId = [u32; 8]; pub const DEFAULT_PROGRAM_ID: ProgramId = [0; 8]; -pub struct Program { - pub id: ProgramId, - pub elf: &'static [u8], -} - /// Validates well-behaved program execution /// /// # Parameters diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs index 11f2231..ee4d2ff 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -1,6 +1,6 @@ use nssa_core::{ account::{Account, AccountWithMetadata}, - program::DEFAULT_PROGRAM_ID, + program::{DEFAULT_PROGRAM_ID, ProgramId}, }; use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID}; use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor}; @@ -12,7 +12,6 @@ mod signature; mod state; pub use address::Address; -pub use nssa_core::program::Program; pub use public_transaction::PublicTransaction; pub use signature::PrivateKey; pub use signature::PublicKey; @@ -26,6 +25,11 @@ pub const AUTHENTICATED_TRANSFER_PROGRAM: Program = Program { elf: AUTHENTICATED_TRANSFER_ELF, }; +pub struct Program { + id: ProgramId, + elf: &'static [u8], +} + /// Writes inputs to `env_builder` in the order expected by the programs fn write_inputs( pre_states: &[AccountWithMetadata], @@ -42,34 +46,39 @@ fn write_inputs( Ok(()) } -fn execute_public( - pre_states: &[AccountWithMetadata], - instruction_data: u128, - program: &Program, -) -> Result, NssaError> { - // Write inputs to the program - let mut env_builder = ExecutorEnv::builder(); - write_inputs(pre_states, instruction_data, &mut env_builder)?; - let env = env_builder.build().unwrap(); - - // Execute the program (without proving) - let executor = default_executor(); - let session_info = executor - .execute(env, program.elf) - .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; - - // Get outputs - let mut post_states: Vec = session_info - .journal - .decode() - .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; - - // Claim any output account with default program owner field - for account in post_states.iter_mut() { - if account.program_owner == DEFAULT_PROGRAM_ID { - account.program_owner = program.id; - } +impl Program { + pub fn id(&self) -> ProgramId { + self.id } + pub(crate) fn execute( + &self, + pre_states: &[AccountWithMetadata], + instruction_data: u128, + ) -> Result, NssaError> { + // Write inputs to the program + let mut env_builder = ExecutorEnv::builder(); + write_inputs(pre_states, instruction_data, &mut env_builder)?; + let env = env_builder.build().unwrap(); - Ok(post_states) + // Execute the program (without proving) + let executor = default_executor(); + let session_info = executor + .execute(env, self.elf) + .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; + + // Get outputs + let mut post_states: Vec = session_info + .journal + .decode() + .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; + + // Claim any output account with default program owner field + for account in post_states.iter_mut() { + if account.program_owner == DEFAULT_PROGRAM_ID { + account.program_owner = self.id; + } + } + + Ok(post_states) + } } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 6459a30..31d3695 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -1,10 +1,10 @@ use crate::{ - AUTHENTICATED_TRANSFER_PROGRAM, address::Address, error::NssaError, execute_public, + AUTHENTICATED_TRANSFER_PROGRAM, Program, address::Address, error::NssaError, public_transaction::PublicTransaction, }; use nssa_core::{ account::{Account, AccountWithMetadata}, - program::{Program, ProgramId, validate_constraints}, + program::{ProgramId, validate_constraints}, }; use std::collections::{HashMap, HashSet}; @@ -129,7 +129,7 @@ impl V01State { }; // // Execute program - let post_states = execute_public(&pre_states, message.instruction_data, program)?; + let post_states = program.execute(&pre_states, message.instruction_data)?; // Verify execution corresponds to a well-behaved program. // See the # Programs section for the definition of the `validate_constraints` method. diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 7647b6d..8aede21 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -267,7 +267,7 @@ mod tests { let addresses = vec![from, to]; let nonces = vec![0]; - let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id; + let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id(); let message = nssa::public_transaction::Message::new(program_id, addresses, nonces, balance_to_move); let witness_set = @@ -501,7 +501,7 @@ mod tests { ], "instruction_data": 10, "nonces": [0], - "program_id": nssa::AUTHENTICATED_TRANSFER_PROGRAM.id, + "program_id": nssa::AUTHENTICATED_TRANSFER_PROGRAM.id(), }, "witness_set": { "signatures_and_public_keys": [ diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 9f80d8e..472159c 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -86,7 +86,7 @@ impl NodeCore { if let Some(account) = account { let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)]; let nonces = vec![nonce]; - let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id; + let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id(); let message = nssa::public_transaction::Message::new( program_id, addresses, From 6030e54cd7dd9885e46197c41d2255cb92e28b8f Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sat, 9 Aug 2025 20:25:58 -0300 Subject: [PATCH 17/58] refactor program --- accounts/src/account_core/address.rs | 1 - common/src/sequencer_client/json.rs | 1 - common/src/test_utils.rs | 6 +- nssa/src/lib.rs | 73 +------------------ nssa/src/program.rs | 71 ++++++++++++++++++ nssa/src/public_transaction.rs | 5 +- nssa/src/state.rs | 10 ++- sequencer_core/src/lib.rs | 17 +++-- .../src/sequencer_store/block_store.rs | 2 +- sequencer_rpc/src/process.rs | 25 +++---- wallet/src/lib.rs | 2 +- 11 files changed, 105 insertions(+), 108 deletions(-) create mode 100644 nssa/src/program.rs diff --git a/accounts/src/account_core/address.rs b/accounts/src/account_core/address.rs index 0740027..83ce792 100644 --- a/accounts/src/account_core/address.rs +++ b/accounts/src/account_core/address.rs @@ -1,4 +1,3 @@ - // TODO: Consider wrapping `AccountAddress` in a struct. pub type AccountAddress = [u8; 32]; diff --git a/common/src/sequencer_client/json.rs b/common/src/sequencer_client/json.rs index 3afb54f..119442b 100644 --- a/common/src/sequencer_client/json.rs +++ b/common/src/sequencer_client/json.rs @@ -1,6 +1,5 @@ use serde::{Deserialize, Serialize}; - //Requests #[derive(Serialize, Deserialize, Debug)] diff --git a/common/src/test_utils.rs b/common/src/test_utils.rs index 6fe3324..455d998 100644 --- a/common/src/test_utils.rs +++ b/common/src/test_utils.rs @@ -31,7 +31,7 @@ pub fn produce_dummy_block( } pub fn produce_dummy_empty_transaction() -> nssa::PublicTransaction { - let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id(); + let program_id = nssa::program::AUTHENTICATED_TRANSFER_PROGRAM.id(); let addresses = vec![]; let nonces = vec![]; let instruction_data = 0; @@ -42,7 +42,7 @@ pub fn produce_dummy_empty_transaction() -> nssa::PublicTransaction { nssa::PublicTransaction::new(message, witness_set) } -pub fn create_dummy_transaction_native_token_transfer( +pub fn create_transaction_native_token_transfer( from: [u8; 32], nonce: u128, to: [u8; 32], @@ -51,7 +51,7 @@ pub fn create_dummy_transaction_native_token_transfer( ) -> nssa::PublicTransaction { let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)]; let nonces = vec![nonce]; - let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id(); + let program_id = nssa::program::AUTHENTICATED_TRANSFER_PROGRAM.id(); let message = nssa::public_transaction::Message::new(program_id, addresses, nonces, balance_to_move); let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[&signing_key]); diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs index ee4d2ff..ab4fe02 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -1,12 +1,6 @@ -use nssa_core::{ - account::{Account, AccountWithMetadata}, - program::{DEFAULT_PROGRAM_ID, ProgramId}, -}; -use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID}; -use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor}; - mod address; pub mod error; +pub mod program; pub mod public_transaction; mod signature; mod state; @@ -17,68 +11,3 @@ pub use signature::PrivateKey; pub use signature::PublicKey; pub use signature::Signature; pub use state::V01State; - -use crate::error::NssaError; - -pub const AUTHENTICATED_TRANSFER_PROGRAM: Program = Program { - id: AUTHENTICATED_TRANSFER_ID, - elf: AUTHENTICATED_TRANSFER_ELF, -}; - -pub struct Program { - id: ProgramId, - elf: &'static [u8], -} - -/// Writes inputs to `env_builder` in the order expected by the programs -fn write_inputs( - pre_states: &[AccountWithMetadata], - instruction_data: u128, - env_builder: &mut ExecutorEnvBuilder, -) -> Result<(), NssaError> { - let pre_states = pre_states.to_vec(); - env_builder - .write(&pre_states) - .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; - env_builder - .write(&instruction_data) - .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; - Ok(()) -} - -impl Program { - pub fn id(&self) -> ProgramId { - self.id - } - pub(crate) fn execute( - &self, - pre_states: &[AccountWithMetadata], - instruction_data: u128, - ) -> Result, NssaError> { - // Write inputs to the program - let mut env_builder = ExecutorEnv::builder(); - write_inputs(pre_states, instruction_data, &mut env_builder)?; - let env = env_builder.build().unwrap(); - - // Execute the program (without proving) - let executor = default_executor(); - let session_info = executor - .execute(env, self.elf) - .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; - - // Get outputs - let mut post_states: Vec = session_info - .journal - .decode() - .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; - - // Claim any output account with default program owner field - for account in post_states.iter_mut() { - if account.program_owner == DEFAULT_PROGRAM_ID { - account.program_owner = self.id; - } - } - - Ok(post_states) - } -} diff --git a/nssa/src/program.rs b/nssa/src/program.rs new file mode 100644 index 0000000..d6a178e --- /dev/null +++ b/nssa/src/program.rs @@ -0,0 +1,71 @@ +use nssa_core::{ + account::{Account, AccountWithMetadata}, + program::{DEFAULT_PROGRAM_ID, ProgramId}, +}; +use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID}; +use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor}; + +use crate::error::NssaError; + +pub struct Program { + id: ProgramId, + elf: &'static [u8], +} + +pub const AUTHENTICATED_TRANSFER_PROGRAM: Program = Program { + id: AUTHENTICATED_TRANSFER_ID, + elf: AUTHENTICATED_TRANSFER_ELF, +}; + +impl Program { + pub fn id(&self) -> ProgramId { + self.id + } + pub(crate) fn execute( + &self, + pre_states: &[AccountWithMetadata], + instruction_data: u128, + ) -> Result, NssaError> { + // Write inputs to the program + let mut env_builder = ExecutorEnv::builder(); + Self::write_inputs(pre_states, instruction_data, &mut env_builder)?; + let env = env_builder.build().unwrap(); + + // Execute the program (without proving) + let executor = default_executor(); + let session_info = executor + .execute(env, self.elf) + .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; + + // Get outputs + let mut post_states: Vec = session_info + .journal + .decode() + .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; + + // Claim any output account with default program owner field + for account in post_states.iter_mut() { + if account.program_owner == DEFAULT_PROGRAM_ID { + account.program_owner = self.id; + } + } + + Ok(post_states) + } + + /// Writes inputs to `env_builder` in the order expected by the programs + fn write_inputs( + pre_states: &[AccountWithMetadata], + instruction_data: u128, + env_builder: &mut ExecutorEnvBuilder, + ) -> Result<(), NssaError> { + let pre_states = pre_states.to_vec(); + env_builder + .write(&pre_states) + .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; + env_builder + .write(&instruction_data) + .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; + Ok(()) + } +} diff --git a/nssa/src/public_transaction.rs b/nssa/src/public_transaction.rs index ab91039..166d20c 100644 --- a/nssa/src/public_transaction.rs +++ b/nssa/src/public_transaction.rs @@ -1,7 +1,4 @@ -use nssa_core::{ - account::Nonce, - program::ProgramId, -}; +use nssa_core::{account::Nonce, program::ProgramId}; use serde::{Deserialize, Serialize}; use sha2::{Digest, digest::FixedOutput}; diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 31d3695..2bd0418 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -1,5 +1,7 @@ use crate::{ - AUTHENTICATED_TRANSFER_PROGRAM, Program, address::Address, error::NssaError, + address::Address, + error::NssaError, + program::{AUTHENTICATED_TRANSFER_PROGRAM, Program}, public_transaction::PublicTransaction, }; use nssa_core::{ @@ -21,7 +23,7 @@ impl V01State { .map(|(address_value, balance)| { let account = Account { balance, - program_owner: AUTHENTICATED_TRANSFER_PROGRAM.id, + program_owner: AUTHENTICATED_TRANSFER_PROGRAM.id(), ..Account::default() }; let address = Address::new(address_value); @@ -30,7 +32,7 @@ impl V01State { .collect(); let builtin_programs = HashMap::from([( - AUTHENTICATED_TRANSFER_PROGRAM.id, + AUTHENTICATED_TRANSFER_PROGRAM.id(), AUTHENTICATED_TRANSFER_PROGRAM, )]); @@ -155,7 +157,7 @@ mod tests { ) -> PublicTransaction { let addresses = vec![from, to]; let nonces = vec![nonce]; - let program_id = AUTHENTICATED_TRANSFER_PROGRAM.id; + let program_id = AUTHENTICATED_TRANSFER_PROGRAM.id(); let message = public_transaction::Message::new(program_id, addresses, nonces, balance); let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]); PublicTransaction::new(message, witness_set) diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index aec2703..04f6b7c 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -369,7 +369,7 @@ mod tests { let sign_key1 = create_signing_key_for_account1(); - let tx = common::test_utils::create_dummy_transaction_native_token_transfer( + let tx = common::test_utils::create_transaction_native_token_transfer( acc1, 0, acc2, 10, sign_key1, ); let result = sequencer.transaction_pre_check(tx); @@ -395,7 +395,7 @@ mod tests { let sign_key2 = create_signing_key_for_account2(); - let tx = common::test_utils::create_dummy_transaction_native_token_transfer( + let tx = common::test_utils::create_transaction_native_token_transfer( acc1, 0, acc2, 10, sign_key2, ); @@ -405,7 +405,10 @@ mod tests { // Signature is not from sender. Execution fails let result = sequencer.execute_check_transaction_on_state(tx); - assert!(matches!(result, Err(nssa::error::NssaError::ProgramExecutionFailed(_)))); + assert!(matches!( + result, + Err(nssa::error::NssaError::ProgramExecutionFailed(_)) + )); } #[test] @@ -426,7 +429,7 @@ mod tests { let sign_key1 = create_signing_key_for_account1(); - let tx = common::test_utils::create_dummy_transaction_native_token_transfer( + let tx = common::test_utils::create_transaction_native_token_transfer( acc1, 0, acc2, 10000000, sign_key1, ); // let tx_roots = sequencer.get_tree_roots(); @@ -462,7 +465,7 @@ mod tests { let sign_key1 = create_signing_key_for_account1(); - let tx = common::test_utils::create_dummy_transaction_native_token_transfer( + let tx = common::test_utils::create_transaction_native_token_transfer( acc1, 0, acc2, 100, sign_key1, ); @@ -553,7 +556,7 @@ mod tests { let sign_key1 = create_signing_key_for_account1(); - let tx = common::test_utils::create_dummy_transaction_native_token_transfer( + let tx = common::test_utils::create_transaction_native_token_transfer( acc1, 0, acc2, 100, sign_key1, ); @@ -595,7 +598,7 @@ mod tests { let sign_key1 = create_signing_key_for_account1(); - let tx = common::test_utils::create_dummy_transaction_native_token_transfer( + let tx = common::test_utils::create_transaction_native_token_transfer( acc1, 0, acc2, 100, sign_key1, ); diff --git a/sequencer_core/src/sequencer_store/block_store.rs b/sequencer_core/src/sequencer_store/block_store.rs index b06fd73..d07b4a4 100644 --- a/sequencer_core/src/sequencer_store/block_store.rs +++ b/sequencer_core/src/sequencer_store/block_store.rs @@ -76,7 +76,7 @@ fn block_to_transactions_map(block: &Block) -> HashMap { #[cfg(test)] mod tests { use super::*; - + use tempfile::tempdir; // // fn create_dummy_block_with_transaction(block_id: u64) -> (Block, nssa::PublicTransaction) { diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 8aede21..70f1986 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -18,7 +18,8 @@ use common::{ use common::rpc_primitives::requests::{ GetBlockDataRequest, GetBlockDataResponse, GetGenesisIdRequest, GetGenesisIdResponse, - GetLastBlockRequest, GetLastBlockResponse, HelloRequest, HelloResponse, SendTxRequest, SendTxResponse, + GetLastBlockRequest, GetLastBlockResponse, HelloRequest, HelloResponse, SendTxRequest, + SendTxResponse, }; use super::{respond, types::err_rpc::RpcErr, JsonHandler}; @@ -203,7 +204,7 @@ mod tests { use crate::{rpc_handler, JsonHandler}; use common::rpc_primitives::RpcPollingConfig; - + use sequencer_core::{ config::{AccountInitialData, SequencerConfig}, SequencerCore, @@ -260,19 +261,15 @@ mod tests { let mut sequencer_core = SequencerCore::start_from_config(config); let initial_accounts = sequencer_core.sequencer_config.initial_accounts.clone(); - let from = nssa::Address::new([1; 32]); let signing_key = nssa::PrivateKey::new(1); - let to = nssa::Address::new([2; 32]); let balance_to_move = 10; - - let addresses = vec![from, to]; - let nonces = vec![0]; - let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id(); - let message = - nssa::public_transaction::Message::new(program_id, addresses, nonces, balance_to_move); - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[&signing_key]); - let tx = nssa::PublicTransaction::new(message, witness_set); + let tx = common::test_utils::create_transaction_native_token_transfer( + [1; 32], + 0, + [2; 32], + balance_to_move, + signing_key, + ); sequencer_core .push_tx_into_mempool_pre_check(tx.clone()) @@ -501,7 +498,7 @@ mod tests { ], "instruction_data": 10, "nonces": [0], - "program_id": nssa::AUTHENTICATED_TRANSFER_PROGRAM.id(), + "program_id": nssa::program::AUTHENTICATED_TRANSFER_PROGRAM.id(), }, "witness_set": { "signatures_and_public_keys": [ diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 472159c..34f8cc2 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -86,7 +86,7 @@ impl NodeCore { if let Some(account) = account { let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)]; let nonces = vec![nonce]; - let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id(); + let program_id = nssa::program::AUTHENTICATED_TRANSFER_PROGRAM.id(); let message = nssa::public_transaction::Message::new( program_id, addresses, From 4f7bde100cd7eb85d3a3f3c8c1c0d354e0194570 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sat, 9 Aug 2025 20:35:44 -0300 Subject: [PATCH 18/58] minor change --- nssa/src/program.rs | 1 + nssa/src/public_transaction.rs | 6 +++- nssa/src/state.rs | 58 ++++++++++++++++++++++------------ sequencer_core/src/lib.rs | 3 +- 4 files changed, 44 insertions(+), 24 deletions(-) diff --git a/nssa/src/program.rs b/nssa/src/program.rs index d6a178e..8b6fac6 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -21,6 +21,7 @@ impl Program { pub fn id(&self) -> ProgramId { self.id } + pub(crate) fn execute( &self, pre_states: &[AccountWithMetadata], diff --git a/nssa/src/public_transaction.rs b/nssa/src/public_transaction.rs index 166d20c..33a054f 100644 --- a/nssa/src/public_transaction.rs +++ b/nssa/src/public_transaction.rs @@ -39,7 +39,7 @@ impl Message { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct WitnessSet { - pub signatures_and_public_keys: Vec<(Signature, PublicKey)>, + pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>, } impl WitnessSet { @@ -53,6 +53,10 @@ impl WitnessSet { signatures_and_public_keys, } } + + pub fn iter_signatures(&self) -> impl Iterator { + self.signatures_and_public_keys.iter() + } } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 2bd0418..b14eb72 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -57,6 +57,7 @@ impl V01State { let current_account = self.get_account_by_address_mut(address); current_account.nonce += 1; } + Ok(()) } @@ -92,11 +93,7 @@ impl V01State { } let mut authorized_addresses = Vec::new(); - for ((signature, public_key), nonce) in witness_set - .signatures_and_public_keys - .iter() - .zip(message.nonces.iter()) - { + for ((signature, public_key), nonce) in witness_set.iter_signatures().zip(&message.nonces) { // Check the signature is valid if !signature.is_valid_for(message, public_key) { return Err(NssaError::InvalidInput( @@ -148,7 +145,7 @@ mod tests { use super::*; use crate::{public_transaction, signature::PrivateKey}; - fn transfer_transaction_for_tests( + fn transfer_transaction( from: Address, from_key: PrivateKey, nonce: u128, @@ -164,54 +161,75 @@ mod tests { } #[test] - fn test_1() { + fn transition_from_authenticated_transfer_program_invocation_default_account_destination() { let initial_data = [([1; 32], 100)]; let mut state = V01State::new_with_genesis_accounts(&initial_data); let from = Address::new(initial_data[0].0); let from_key = PrivateKey(1); let to = Address::new([2; 32]); + assert_eq!(state.get_account_by_address(&to), Account::default()); let balance_to_move = 5; - let tx = - transfer_transaction_for_tests(from.clone(), from_key, 0, to.clone(), balance_to_move); + + let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move); state.transition_from_public_transaction(&tx).unwrap(); - assert_eq!(state.get_account_by_address(&to).balance, 5); assert_eq!(state.get_account_by_address(&from).balance, 95); + assert_eq!(state.get_account_by_address(&to).balance, 5); assert_eq!(state.get_account_by_address(&from).nonce, 1); assert_eq!(state.get_account_by_address(&to).nonce, 0); } #[test] - fn test_2() { + fn transition_from_authenticated_transfer_program_invocation_insuficient_balance() { + let initial_data = [([1; 32], 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data); + let from = Address::new(initial_data[0].0); + let from_key = PrivateKey(1); + let to = Address::new([2; 32]); + let balance_to_move = 101; + assert!(state.get_account_by_address(&from).balance < balance_to_move); + + let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move); + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::ProgramExecutionFailed(_)))); + assert_eq!(state.get_account_by_address(&from).balance, 100); + assert_eq!(state.get_account_by_address(&to).balance, 0); + assert_eq!(state.get_account_by_address(&from).nonce, 0); + assert_eq!(state.get_account_by_address(&to).nonce, 0); + } + + #[test] + fn transition_from_authenticated_transfer_program_invocation_non_default_account_destination() { let initial_data = [([1; 32], 100), ([99; 32], 200)]; let mut state = V01State::new_with_genesis_accounts(&initial_data); let from = Address::new(initial_data[1].0); let from_key = PrivateKey(99); let to = Address::new(initial_data[0].0); + assert_ne!(state.get_account_by_address(&to), Account::default()); let balance_to_move = 8; - let tx = - transfer_transaction_for_tests(from.clone(), from_key, 0, to.clone(), balance_to_move); + + let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move); state.transition_from_public_transaction(&tx).unwrap(); - assert_eq!(state.get_account_by_address(&to).balance, 108); assert_eq!(state.get_account_by_address(&from).balance, 192); + assert_eq!(state.get_account_by_address(&to).balance, 108); assert_eq!(state.get_account_by_address(&from).nonce, 1); assert_eq!(state.get_account_by_address(&to).nonce, 0); } #[test] - fn test_3() { + fn transition_from_chained_authenticated_transfer_program_invocations() { let initial_data = [([1; 32], 100)]; let mut state = V01State::new_with_genesis_accounts(&initial_data); let address_1 = Address::new(initial_data[0].0); let key_1 = PrivateKey(1); let address_2 = Address::new([2; 32]); - let key_2 = PrivateKey(2); let address_3 = Address::new([3; 32]); - let balance_to_move = 5; - let tx = transfer_transaction_for_tests( + + let tx = transfer_transaction( address_1.clone(), key_1, 0, @@ -219,9 +237,8 @@ mod tests { balance_to_move, ); state.transition_from_public_transaction(&tx).unwrap(); - let balance_to_move = 3; - let tx = transfer_transaction_for_tests( + let tx = transfer_transaction( address_2.clone(), key_2, 0, @@ -233,7 +250,6 @@ mod tests { assert_eq!(state.get_account_by_address(&address_1).balance, 95); assert_eq!(state.get_account_by_address(&address_2).balance, 2); assert_eq!(state.get_account_by_address(&address_3).balance, 3); - assert_eq!(state.get_account_by_address(&address_1).nonce, 1); assert_eq!(state.get_account_by_address(&address_2).nonce, 1); assert_eq!(state.get_account_by_address(&address_3).nonce, 0); diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 04f6b7c..04bd26b 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -64,8 +64,7 @@ impl SequencerCore { // Stateless checks here if tx .witness_set() - .signatures_and_public_keys - .iter() + .iter_signatures() .all(|(signature, public_key)| signature.is_valid_for(tx.message(), public_key)) { Ok(tx) From bfdf039ef365d6b71ccfae6a91fce46f08a2308b Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sun, 10 Aug 2025 00:53:53 -0300 Subject: [PATCH 19/58] add test folder --- common/src/test_utils.rs | 4 +- nssa/Cargo.toml | 5 +- .../guest/src/bin/authenticated_transfer.rs | 2 +- nssa/src/lib.rs | 3 + nssa/src/program.rs | 30 +++- nssa/src/state.rs | 131 ++---------------- nssa/src/tests/mod.rs | 1 + nssa/src/tests/state_tests.rs | 131 ++++++++++++++++++ nssa/test_program_methods/Cargo.toml | 11 ++ nssa/test_program_methods/build.rs | 3 + nssa/test_program_methods/guest/Cargo.toml | 11 ++ .../guest/src/bin/nonce_changer.rs | 18 +++ nssa/test_program_methods/src/lib.rs | 1 + sequencer_core/src/lib.rs | 44 ------ .../src/sequencer_store/block_store.rs | 26 +--- sequencer_rpc/src/process.rs | 2 +- wallet/src/lib.rs | 2 +- 17 files changed, 225 insertions(+), 200 deletions(-) create mode 100644 nssa/src/tests/mod.rs create mode 100644 nssa/src/tests/state_tests.rs create mode 100644 nssa/test_program_methods/Cargo.toml create mode 100644 nssa/test_program_methods/build.rs create mode 100644 nssa/test_program_methods/guest/Cargo.toml create mode 100644 nssa/test_program_methods/guest/src/bin/nonce_changer.rs create mode 100644 nssa/test_program_methods/src/lib.rs diff --git a/common/src/test_utils.rs b/common/src/test_utils.rs index 455d998..8cc071f 100644 --- a/common/src/test_utils.rs +++ b/common/src/test_utils.rs @@ -31,7 +31,7 @@ pub fn produce_dummy_block( } pub fn produce_dummy_empty_transaction() -> nssa::PublicTransaction { - let program_id = nssa::program::AUTHENTICATED_TRANSFER_PROGRAM.id(); + let program_id = nssa::program::Program::authenticated_transfer_program().id(); let addresses = vec![]; let nonces = vec![]; let instruction_data = 0; @@ -51,7 +51,7 @@ pub fn create_transaction_native_token_transfer( ) -> nssa::PublicTransaction { let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)]; let nonces = vec![nonce]; - let program_id = nssa::program::AUTHENTICATED_TRANSFER_PROGRAM.id(); + let program_id = nssa::program::Program::authenticated_transfer_program().id(); let message = nssa::public_transaction::Message::new(program_id, addresses, nonces, balance_to_move); let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[&signing_key]); diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index 2bb5db1..aea82b9 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -6,8 +6,11 @@ edition = "2024" [dependencies] thiserror = "2.0.12" risc0-zkvm = "2.3.1" -nssa-core = { path = "core"} +nssa-core = { path = "core" } program-methods = { path = "program_methods" } serde = "1.0.219" serde_cbor = "0.11.2" sha2 = "0.10.9" + +[dev-dependencies] +test-program-methods = { path = "test_program_methods" } diff --git a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs index d3ac729..8ed3fc4 100644 --- a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs +++ b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs @@ -12,7 +12,7 @@ fn main() { // Continue only if input_accounts is an array of two elements let [sender, receiver] = match input_accounts.try_into() { Ok(array) => array, - Err(_) => return, // silently return on bad input + Err(_) => return, }; // Continue only if the sender has authorized this operation diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs index ab4fe02..bde852c 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -11,3 +11,6 @@ pub use signature::PrivateKey; pub use signature::PublicKey; pub use signature::Signature; pub use state::V01State; + +#[cfg(test)] +mod tests; diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 8b6fac6..6c25828 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -8,15 +8,10 @@ use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor}; use crate::error::NssaError; pub struct Program { - id: ProgramId, - elf: &'static [u8], + pub(crate) id: ProgramId, + pub(crate) elf: &'static [u8], } -pub const AUTHENTICATED_TRANSFER_PROGRAM: Program = Program { - id: AUTHENTICATED_TRANSFER_ID, - elf: AUTHENTICATED_TRANSFER_ELF, -}; - impl Program { pub fn id(&self) -> ProgramId { self.id @@ -69,4 +64,25 @@ impl Program { .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; Ok(()) } + + pub fn authenticated_transfer_program() -> Self { + Self { + id: AUTHENTICATED_TRANSFER_ID, + elf: AUTHENTICATED_TRANSFER_ELF, + } + } +} + +// Test utils +#[cfg(test)] +impl Program { + /// A program that changes the nonce of an account + pub fn nonce_changer_program() -> Self { + use test_program_methods::{NONCE_CHANGER_ELF, NONCE_CHANGER_ID}; + + Program { + id: NONCE_CHANGER_ID, + elf: NONCE_CHANGER_ELF, + } + } } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index b14eb72..3b7b77b 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -1,8 +1,5 @@ use crate::{ - address::Address, - error::NssaError, - program::{AUTHENTICATED_TRANSFER_PROGRAM, Program}, - public_transaction::PublicTransaction, + address::Address, error::NssaError, program::Program, public_transaction::PublicTransaction, }; use nssa_core::{ account::{Account, AccountWithMetadata}, @@ -17,13 +14,14 @@ pub struct V01State { impl V01State { pub fn new_with_genesis_accounts(initial_data: &[([u8; 32], u128)]) -> Self { + let authenticated_transfer_program = Program::authenticated_transfer_program(); let public_state = initial_data .iter() .copied() .map(|(address_value, balance)| { let account = Account { balance, - program_owner: AUTHENTICATED_TRANSFER_PROGRAM.id(), + program_owner: authenticated_transfer_program.id(), ..Account::default() }; let address = Address::new(address_value); @@ -32,8 +30,8 @@ impl V01State { .collect(); let builtin_programs = HashMap::from([( - AUTHENTICATED_TRANSFER_PROGRAM.id(), - AUTHENTICATED_TRANSFER_PROGRAM, + authenticated_transfer_program.id(), + authenticated_transfer_program, )]); Self { @@ -140,118 +138,15 @@ impl V01State { } } +// Test utils #[cfg(test)] -mod tests { - use super::*; - use crate::{public_transaction, signature::PrivateKey}; - - fn transfer_transaction( - from: Address, - from_key: PrivateKey, - nonce: u128, - to: Address, - balance: u128, - ) -> PublicTransaction { - let addresses = vec![from, to]; - let nonces = vec![nonce]; - let program_id = AUTHENTICATED_TRANSFER_PROGRAM.id(); - let message = public_transaction::Message::new(program_id, addresses, nonces, balance); - let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]); - PublicTransaction::new(message, witness_set) - } - - #[test] - fn transition_from_authenticated_transfer_program_invocation_default_account_destination() { - let initial_data = [([1; 32], 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data); - let from = Address::new(initial_data[0].0); - let from_key = PrivateKey(1); - let to = Address::new([2; 32]); - assert_eq!(state.get_account_by_address(&to), Account::default()); - let balance_to_move = 5; - - let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move); - state.transition_from_public_transaction(&tx).unwrap(); - - assert_eq!(state.get_account_by_address(&from).balance, 95); - assert_eq!(state.get_account_by_address(&to).balance, 5); - assert_eq!(state.get_account_by_address(&from).nonce, 1); - assert_eq!(state.get_account_by_address(&to).nonce, 0); - } - - #[test] - fn transition_from_authenticated_transfer_program_invocation_insuficient_balance() { - let initial_data = [([1; 32], 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data); - let from = Address::new(initial_data[0].0); - let from_key = PrivateKey(1); - let to = Address::new([2; 32]); - let balance_to_move = 101; - assert!(state.get_account_by_address(&from).balance < balance_to_move); - - let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move); - let result = state.transition_from_public_transaction(&tx); - - assert!(matches!(result, Err(NssaError::ProgramExecutionFailed(_)))); - assert_eq!(state.get_account_by_address(&from).balance, 100); - assert_eq!(state.get_account_by_address(&to).balance, 0); - assert_eq!(state.get_account_by_address(&from).nonce, 0); - assert_eq!(state.get_account_by_address(&to).nonce, 0); - } - - #[test] - fn transition_from_authenticated_transfer_program_invocation_non_default_account_destination() { - let initial_data = [([1; 32], 100), ([99; 32], 200)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data); - let from = Address::new(initial_data[1].0); - let from_key = PrivateKey(99); - let to = Address::new(initial_data[0].0); - assert_ne!(state.get_account_by_address(&to), Account::default()); - let balance_to_move = 8; - - let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move); - state.transition_from_public_transaction(&tx).unwrap(); - - assert_eq!(state.get_account_by_address(&from).balance, 192); - assert_eq!(state.get_account_by_address(&to).balance, 108); - assert_eq!(state.get_account_by_address(&from).nonce, 1); - assert_eq!(state.get_account_by_address(&to).nonce, 0); - } - - #[test] - fn transition_from_chained_authenticated_transfer_program_invocations() { - let initial_data = [([1; 32], 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data); - let address_1 = Address::new(initial_data[0].0); - let key_1 = PrivateKey(1); - let address_2 = Address::new([2; 32]); - let key_2 = PrivateKey(2); - let address_3 = Address::new([3; 32]); - let balance_to_move = 5; - - let tx = transfer_transaction( - address_1.clone(), - key_1, - 0, - address_2.clone(), - balance_to_move, +impl V01State { + /// Include test programs in the builtin programs map + pub fn with_test_programs(mut self) -> Self { + self.builtin_programs.insert( + Program::nonce_changer_program().id(), + Program::nonce_changer_program(), ); - state.transition_from_public_transaction(&tx).unwrap(); - let balance_to_move = 3; - let tx = transfer_transaction( - address_2.clone(), - key_2, - 0, - address_3.clone(), - balance_to_move, - ); - state.transition_from_public_transaction(&tx).unwrap(); - - assert_eq!(state.get_account_by_address(&address_1).balance, 95); - assert_eq!(state.get_account_by_address(&address_2).balance, 2); - assert_eq!(state.get_account_by_address(&address_3).balance, 3); - assert_eq!(state.get_account_by_address(&address_1).nonce, 1); - assert_eq!(state.get_account_by_address(&address_2).nonce, 1); - assert_eq!(state.get_account_by_address(&address_3).nonce, 0); + self } } diff --git a/nssa/src/tests/mod.rs b/nssa/src/tests/mod.rs new file mode 100644 index 0000000..8d49dbd --- /dev/null +++ b/nssa/src/tests/mod.rs @@ -0,0 +1 @@ +mod state_tests; diff --git a/nssa/src/tests/state_tests.rs b/nssa/src/tests/state_tests.rs new file mode 100644 index 0000000..cae157f --- /dev/null +++ b/nssa/src/tests/state_tests.rs @@ -0,0 +1,131 @@ +use crate::{ + Address, PublicTransaction, V01State, error::NssaError, program::Program, public_transaction, + signature::PrivateKey, +}; +use nssa_core::account::Account; + +#[test] +fn test_programs_cant_change_account_nonces() { + let initial_data = [([1; 32], 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let addresses = vec![Address::new([1; 32])]; + let nonces = vec![]; + let program_id = Program::nonce_changer_program().id(); + let message = public_transaction::Message::new(program_id, addresses, nonces, 5); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); +} + +fn transfer_transaction( + from: Address, + from_key: PrivateKey, + nonce: u128, + to: Address, + balance: u128, +) -> PublicTransaction { + let addresses = vec![from, to]; + let nonces = vec![nonce]; + let program_id = Program::authenticated_transfer_program().id(); + let message = public_transaction::Message::new(program_id, addresses, nonces, balance); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]); + PublicTransaction::new(message, witness_set) +} + +#[test] +fn transition_from_authenticated_transfer_program_invocation_default_account_destination() { + let initial_data = [([1; 32], 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data); + let from = Address::new(initial_data[0].0); + let from_key = PrivateKey(1); + let to = Address::new([2; 32]); + assert_eq!(state.get_account_by_address(&to), Account::default()); + let balance_to_move = 5; + + let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move); + state.transition_from_public_transaction(&tx).unwrap(); + + assert_eq!(state.get_account_by_address(&from).balance, 95); + assert_eq!(state.get_account_by_address(&to).balance, 5); + assert_eq!(state.get_account_by_address(&from).nonce, 1); + assert_eq!(state.get_account_by_address(&to).nonce, 0); +} + +#[test] +fn transition_from_authenticated_transfer_program_invocation_insuficient_balance() { + let initial_data = [([1; 32], 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data); + let from = Address::new(initial_data[0].0); + let from_key = PrivateKey(1); + let to = Address::new([2; 32]); + let balance_to_move = 101; + assert!(state.get_account_by_address(&from).balance < balance_to_move); + + let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move); + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::ProgramExecutionFailed(_)))); + assert_eq!(state.get_account_by_address(&from).balance, 100); + assert_eq!(state.get_account_by_address(&to).balance, 0); + assert_eq!(state.get_account_by_address(&from).nonce, 0); + assert_eq!(state.get_account_by_address(&to).nonce, 0); +} + +#[test] +fn transition_from_authenticated_transfer_program_invocation_non_default_account_destination() { + let initial_data = [([1; 32], 100), ([99; 32], 200)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data); + let from = Address::new(initial_data[1].0); + let from_key = PrivateKey(99); + let to = Address::new(initial_data[0].0); + assert_ne!(state.get_account_by_address(&to), Account::default()); + let balance_to_move = 8; + + let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move); + state.transition_from_public_transaction(&tx).unwrap(); + + assert_eq!(state.get_account_by_address(&from).balance, 192); + assert_eq!(state.get_account_by_address(&to).balance, 108); + assert_eq!(state.get_account_by_address(&from).nonce, 1); + assert_eq!(state.get_account_by_address(&to).nonce, 0); +} + +#[test] +fn transition_from_chained_authenticated_transfer_program_invocations() { + let initial_data = [([1; 32], 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data); + let address_1 = Address::new(initial_data[0].0); + let key_1 = PrivateKey(1); + let address_2 = Address::new([2; 32]); + let key_2 = PrivateKey(2); + let address_3 = Address::new([3; 32]); + let balance_to_move = 5; + + let tx = transfer_transaction( + address_1.clone(), + key_1, + 0, + address_2.clone(), + balance_to_move, + ); + state.transition_from_public_transaction(&tx).unwrap(); + let balance_to_move = 3; + let tx = transfer_transaction( + address_2.clone(), + key_2, + 0, + address_3.clone(), + balance_to_move, + ); + state.transition_from_public_transaction(&tx).unwrap(); + + assert_eq!(state.get_account_by_address(&address_1).balance, 95); + assert_eq!(state.get_account_by_address(&address_2).balance, 2); + assert_eq!(state.get_account_by_address(&address_3).balance, 3); + assert_eq!(state.get_account_by_address(&address_1).nonce, 1); + assert_eq!(state.get_account_by_address(&address_2).nonce, 1); + assert_eq!(state.get_account_by_address(&address_3).nonce, 0); +} diff --git a/nssa/test_program_methods/Cargo.toml b/nssa/test_program_methods/Cargo.toml new file mode 100644 index 0000000..7da210b --- /dev/null +++ b/nssa/test_program_methods/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "test-program-methods" +version = "0.1.0" +edition = "2021" + +[build-dependencies] +risc0-build = { version = "2.3.1" } + +[package.metadata.risc0] +methods = ["guest"] + diff --git a/nssa/test_program_methods/build.rs b/nssa/test_program_methods/build.rs new file mode 100644 index 0000000..08a8a4e --- /dev/null +++ b/nssa/test_program_methods/build.rs @@ -0,0 +1,3 @@ +fn main() { + risc0_build::embed_methods(); +} diff --git a/nssa/test_program_methods/guest/Cargo.toml b/nssa/test_program_methods/guest/Cargo.toml new file mode 100644 index 0000000..12bddbf --- /dev/null +++ b/nssa/test_program_methods/guest/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "programs" +version = "0.1.0" +edition = "2021" + +[workspace] + +[dependencies] +risc0-zkvm = { version = "2.3.1", default-features = false, features = ['std'] } +nssa-core = {path = "../../core"} + diff --git a/nssa/test_program_methods/guest/src/bin/nonce_changer.rs b/nssa/test_program_methods/guest/src/bin/nonce_changer.rs new file mode 100644 index 0000000..08007f6 --- /dev/null +++ b/nssa/test_program_methods/guest/src/bin/nonce_changer.rs @@ -0,0 +1,18 @@ +use nssa_core::account::AccountWithMetadata; +use risc0_zkvm::guest::env; + +fn main() { + let input_accounts: Vec = env::read(); + let _instruction_data: u128 = env::read(); + + let [pre] = match input_accounts.try_into() { + Ok(array) => array, + Err(_) => return, + }; + + let account_pre = pre.account; + let mut account_post = account_pre.clone(); + account_post.nonce += 1; + + env::commit(&vec![account_post]); +} diff --git a/nssa/test_program_methods/src/lib.rs b/nssa/test_program_methods/src/lib.rs new file mode 100644 index 0000000..1bdb308 --- /dev/null +++ b/nssa/test_program_methods/src/lib.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/methods.rs")); diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 04bd26b..9d52def 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -188,55 +188,11 @@ mod tests { setup_sequencer_config_variable_initial_accounts(initial_accounts) } - // fn create_dummy_transaction() -> nssa::PublicTransaction { - // let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id; - // let addresses = vec![]; - // let nonces = vec![]; - // let instruction_data = 0; - // let message = - // nssa::public_transaction::Message::new(program_id, addresses, nonces, instruction_data); - // let private_key = nssa::PrivateKey::new(1); - // let witness_set = - // nssa::public_transaction::WitnessSet::for_message(&message, &[&private_key]); - // nssa::PublicTransaction::new(message, witness_set) - // } - // - // fn create_dummy_transaction_native_token_transfer( - // from: [u8; 32], - // nonce: u128, - // to: [u8; 32], - // balance_to_move: u128, - // signing_key: nssa::PrivateKey, - // ) -> nssa::PublicTransaction { - // let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)]; - // let nonces = vec![nonce]; - // let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id; - // let message = - // nssa::public_transaction::Message::new(program_id, addresses, nonces, balance_to_move); - // let witness_set = - // nssa::public_transaction::WitnessSet::for_message(&message, &[&signing_key]); - // nssa::PublicTransaction::new(message, witness_set) - // } - fn create_signing_key_for_account1() -> nssa::PrivateKey { - // let pub_sign_key_acc1 = [ - // 133, 143, 177, 187, 252, 66, 237, 236, 234, 252, 244, 138, 5, 151, 3, 99, 217, 231, - // 112, 217, 77, 211, 58, 218, 176, 68, 99, 53, 152, 228, 198, 190, - // ]; - // - // let field_bytes = FieldBytes::from_slice(&pub_sign_key_acc1); - // SigningKey::from_bytes(field_bytes).unwrap() nssa::PrivateKey::new(1) } fn create_signing_key_for_account2() -> nssa::PrivateKey { - // let pub_sign_key_acc2 = [ - // 54, 90, 62, 225, 71, 225, 228, 148, 143, 53, 210, 23, 137, 158, 171, 156, 48, 7, 139, - // 52, 117, 242, 214, 7, 99, 29, 122, 184, 59, 116, 144, 107, - // ]; - // - // let field_bytes = FieldBytes::from_slice(&pub_sign_key_acc2); - // SigningKey::from_bytes(field_bytes).unwrap() nssa::PrivateKey::new(2) } diff --git a/sequencer_core/src/sequencer_store/block_store.rs b/sequencer_core/src/sequencer_store/block_store.rs index d07b4a4..da92b82 100644 --- a/sequencer_core/src/sequencer_store/block_store.rs +++ b/sequencer_core/src/sequencer_store/block_store.rs @@ -78,31 +78,7 @@ mod tests { use super::*; use tempfile::tempdir; - // - // fn create_dummy_block_with_transaction(block_id: u64) -> (Block, nssa::PublicTransaction) { - // let program_id = nssa::AUTHENTICATED_TRANSFER_PROGRAM.id; - // let addresses = vec![]; - // let nonces = vec![]; - // let instruction_data = 0; - // let message = - // nssa::public_transaction::Message::new(program_id, addresses, nonces, instruction_data); - // let private_key = nssa::PrivateKey::new(1); - // let witness_set = - // nssa::public_transaction::WitnessSet::for_message(&message, &[private_key]); - // let tx = nssa::PublicTransaction::new(message, witness_set); - // ( - // Block { - // block_id, - // prev_block_id: block_id - 1, - // prev_block_hash: [0; 32], - // hash: [1; 32], - // transactions: vec![tx.clone()], - // data: vec![], - // }, - // tx, - // ) - // } - // + #[test] fn test_get_transaction_by_hash() { let temp_dir = tempdir().unwrap(); diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 70f1986..1757636 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -498,7 +498,7 @@ mod tests { ], "instruction_data": 10, "nonces": [0], - "program_id": nssa::program::AUTHENTICATED_TRANSFER_PROGRAM.id(), + "program_id": nssa::program::Program::authenticated_transfer_program().id(), }, "witness_set": { "signatures_and_public_keys": [ diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 34f8cc2..b18b07f 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -86,7 +86,7 @@ impl NodeCore { if let Some(account) = account { let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)]; let nonces = vec![nonce]; - let program_id = nssa::program::AUTHENTICATED_TRANSFER_PROGRAM.id(); + let program_id = nssa::program::Program::authenticated_transfer_program().id(); let message = nssa::public_transaction::Message::new( program_id, addresses, From ecdb4ba130aab054daf571db791177aa46aeaf0a Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sun, 10 Aug 2025 09:57:10 -0300 Subject: [PATCH 20/58] add test for extra outputs --- nssa/core/src/program.rs | 2 +- nssa/src/program.rs | 10 ++++++ nssa/src/state.rs | 31 ++++++++-------- nssa/src/tests/mod.rs | 1 + nssa/src/tests/state_tests.rs | 15 -------- nssa/src/tests/valid_execution_tests.rs | 35 +++++++++++++++++++ .../guest/src/bin/extra_outputs.rs | 17 +++++++++ 7 files changed, 80 insertions(+), 31 deletions(-) create mode 100644 nssa/src/tests/valid_execution_tests.rs create mode 100644 nssa/test_program_methods/guest/src/bin/extra_outputs.rs diff --git a/nssa/core/src/program.rs b/nssa/core/src/program.rs index ce58a3b..d3d253a 100644 --- a/nssa/core/src/program.rs +++ b/nssa/core/src/program.rs @@ -9,7 +9,7 @@ pub const DEFAULT_PROGRAM_ID: ProgramId = [0; 8]; /// - `pre_states`: The list of input accounts, each annotated with authorization metadata. /// - `post_states`: The list of resulting accounts after executing the program logic. /// - `executing_program_id`: The identifier of the program that was executed. -pub fn validate_constraints( +pub fn validate_execution( pre_states: &[AccountWithMetadata], post_states: &[Account], executing_program_id: ProgramId, diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 6c25828..2b6c041 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -85,4 +85,14 @@ impl Program { elf: NONCE_CHANGER_ELF, } } + + /// A program that produces more output accounts than the inputs it received + pub fn extra_outputs_program() -> Self { + use test_program_methods::{EXTRA_OUTPUTS_ELF, EXTRA_OUTPUTS_ID}; + + Program { + id: EXTRA_OUTPUTS_ID, + elf: EXTRA_OUTPUTS_ELF, + } + } } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 3b7b77b..b699ab0 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -3,7 +3,7 @@ use crate::{ }; use nssa_core::{ account::{Account, AccountWithMetadata}, - program::{ProgramId, validate_constraints}, + program::{ProgramId, validate_execution}, }; use std::collections::{HashMap, HashSet}; @@ -29,15 +29,18 @@ impl V01State { }) .collect(); - let builtin_programs = HashMap::from([( - authenticated_transfer_program.id(), - authenticated_transfer_program, - )]); - - Self { + let mut this = Self { public_state, - builtin_programs, - } + builtin_programs: HashMap::new(), + }; + + this.insert_program(Program::authenticated_transfer_program()); + + this + } + + fn insert_program(&mut self, program: Program) { + self.builtin_programs.insert(program.id(), program); } pub fn transition_from_public_transaction( @@ -129,8 +132,8 @@ impl V01State { let post_states = program.execute(&pre_states, message.instruction_data)?; // Verify execution corresponds to a well-behaved program. - // See the # Programs section for the definition of the `validate_constraints` method. - if !validate_constraints(&pre_states, &post_states, message.program_id) { + // See the # Programs section for the definition of the `validate_execution` method. + if !validate_execution(&pre_states, &post_states, message.program_id) { return Err(NssaError::InvalidProgramBehavior); } @@ -143,10 +146,8 @@ impl V01State { impl V01State { /// Include test programs in the builtin programs map pub fn with_test_programs(mut self) -> Self { - self.builtin_programs.insert( - Program::nonce_changer_program().id(), - Program::nonce_changer_program(), - ); + self.insert_program(Program::nonce_changer_program()); + self.insert_program(Program::extra_outputs_program()); self } } diff --git a/nssa/src/tests/mod.rs b/nssa/src/tests/mod.rs index 8d49dbd..2c04e2d 100644 --- a/nssa/src/tests/mod.rs +++ b/nssa/src/tests/mod.rs @@ -1 +1,2 @@ mod state_tests; +mod valid_execution_tests; diff --git a/nssa/src/tests/state_tests.rs b/nssa/src/tests/state_tests.rs index cae157f..6763f17 100644 --- a/nssa/src/tests/state_tests.rs +++ b/nssa/src/tests/state_tests.rs @@ -4,21 +4,6 @@ use crate::{ }; use nssa_core::account::Account; -#[test] -fn test_programs_cant_change_account_nonces() { - let initial_data = [([1; 32], 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); - let addresses = vec![Address::new([1; 32])]; - let nonces = vec![]; - let program_id = Program::nonce_changer_program().id(); - let message = public_transaction::Message::new(program_id, addresses, nonces, 5); - let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); - let tx = PublicTransaction::new(message, witness_set); - - let result = state.transition_from_public_transaction(&tx); - - assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); -} fn transfer_transaction( from: Address, diff --git a/nssa/src/tests/valid_execution_tests.rs b/nssa/src/tests/valid_execution_tests.rs new file mode 100644 index 0000000..66ec49d --- /dev/null +++ b/nssa/src/tests/valid_execution_tests.rs @@ -0,0 +1,35 @@ +use crate::{ + Address, PublicTransaction, V01State, error::NssaError, program::Program, public_transaction, +}; + +#[test] +fn test_program_should_fail_if_it_modifies_nonces() { + let initial_data = [([1; 32], 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let addresses = vec![Address::new([1; 32])]; + let nonces = vec![]; + let program_id = Program::nonce_changer_program().id(); + let message = public_transaction::Message::new(program_id, addresses, nonces, 0); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); +} + +#[test] +fn test_program_should_fail_if_output_accounts_exceed_inputs() { + let initial_data = [([1; 32], 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let addresses = vec![Address::new([1; 32])]; + let nonces = vec![]; + let program_id = Program::extra_outputs_program().id(); + let message = public_transaction::Message::new(program_id, addresses, nonces, 0); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); +} diff --git a/nssa/test_program_methods/guest/src/bin/extra_outputs.rs b/nssa/test_program_methods/guest/src/bin/extra_outputs.rs new file mode 100644 index 0000000..f15c097 --- /dev/null +++ b/nssa/test_program_methods/guest/src/bin/extra_outputs.rs @@ -0,0 +1,17 @@ +use nssa_core::account::{Account, AccountWithMetadata}; +use risc0_zkvm::guest::env; + +fn main() { + let input_accounts: Vec = env::read(); + let _instruction_data: u128 = env::read(); + + let [pre] = match input_accounts.try_into() { + Ok(array) => array, + Err(_) => return, + }; + + let account_pre = pre.account; + + env::commit(&vec![account_pre, Account::default()]); +} + From a3b64e402106232261bb78ed1b9fa97f59fd92bf Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sun, 10 Aug 2025 10:09:23 -0300 Subject: [PATCH 21/58] add test for missing outputs --- nssa/src/program.rs | 18 ++++++++++++++---- nssa/src/state.rs | 3 ++- nssa/src/tests/valid_execution_tests.rs | 19 ++++++++++++++++++- .../bin/{extra_outputs.rs => extra_output.rs} | 0 .../guest/src/bin/missing_output.rs | 16 ++++++++++++++++ 5 files changed, 50 insertions(+), 6 deletions(-) rename nssa/test_program_methods/guest/src/bin/{extra_outputs.rs => extra_output.rs} (100%) create mode 100644 nssa/test_program_methods/guest/src/bin/missing_output.rs diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 2b6c041..45a42b7 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -87,12 +87,22 @@ impl Program { } /// A program that produces more output accounts than the inputs it received - pub fn extra_outputs_program() -> Self { - use test_program_methods::{EXTRA_OUTPUTS_ELF, EXTRA_OUTPUTS_ID}; + pub fn extra_output_program() -> Self { + use test_program_methods::{EXTRA_OUTPUT_ELF, EXTRA_OUTPUT_ID}; Program { - id: EXTRA_OUTPUTS_ID, - elf: EXTRA_OUTPUTS_ELF, + id: EXTRA_OUTPUT_ID, + elf: EXTRA_OUTPUT_ELF, + } + } + + /// A program that produces less output accounts than the inputs it received + pub fn missing_output_program() -> Self { + use test_program_methods::{MISSING_OUTPUT_ELF, MISSING_OUTPUT_ID}; + + Program { + id: MISSING_OUTPUT_ID, + elf: MISSING_OUTPUT_ELF, } } } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index b699ab0..195727f 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -147,7 +147,8 @@ impl V01State { /// Include test programs in the builtin programs map pub fn with_test_programs(mut self) -> Self { self.insert_program(Program::nonce_changer_program()); - self.insert_program(Program::extra_outputs_program()); + self.insert_program(Program::extra_output_program()); + self.insert_program(Program::missing_output_program()); self } } diff --git a/nssa/src/tests/valid_execution_tests.rs b/nssa/src/tests/valid_execution_tests.rs index 66ec49d..f010c06 100644 --- a/nssa/src/tests/valid_execution_tests.rs +++ b/nssa/src/tests/valid_execution_tests.rs @@ -24,7 +24,24 @@ fn test_program_should_fail_if_output_accounts_exceed_inputs() { let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32])]; let nonces = vec![]; - let program_id = Program::extra_outputs_program().id(); + let program_id = Program::extra_output_program().id(); + let message = public_transaction::Message::new(program_id, addresses, nonces, 0); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); +} + + +#[test] +fn test_program_should_fail_with_missing_output_accounts() { + let initial_data = [([1; 32], 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let addresses = vec![Address::new([1; 32]), Address::new([2; 32])]; + let nonces = vec![]; + let program_id = Program::missing_output_program().id(); let message = public_transaction::Message::new(program_id, addresses, nonces, 0); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); diff --git a/nssa/test_program_methods/guest/src/bin/extra_outputs.rs b/nssa/test_program_methods/guest/src/bin/extra_output.rs similarity index 100% rename from nssa/test_program_methods/guest/src/bin/extra_outputs.rs rename to nssa/test_program_methods/guest/src/bin/extra_output.rs diff --git a/nssa/test_program_methods/guest/src/bin/missing_output.rs b/nssa/test_program_methods/guest/src/bin/missing_output.rs new file mode 100644 index 0000000..7857ea5 --- /dev/null +++ b/nssa/test_program_methods/guest/src/bin/missing_output.rs @@ -0,0 +1,16 @@ +use nssa_core::account::AccountWithMetadata; +use risc0_zkvm::guest::env; + +fn main() { + let input_accounts: Vec = env::read(); + let _instruction_data: u128 = env::read(); + + let [pre1, _] = match input_accounts.try_into() { + Ok(array) => array, + Err(_) => return, + }; + + let account_pre1 = pre1.account; + + env::commit(&vec![account_pre1]); +} From 3594c846dab7bb2557de260d3883f74f13468a20 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sun, 10 Aug 2025 10:19:59 -0300 Subject: [PATCH 22/58] add test program owner changer --- nssa/src/program.rs | 10 ++ nssa/src/state.rs | 29 ++++++ nssa/src/tests/valid_execution_tests.rs | 93 ++++++++++++++++++- .../guest/src/bin/program_owner_changer.rs | 18 ++++ 4 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 nssa/test_program_methods/guest/src/bin/program_owner_changer.rs diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 45a42b7..2307da2 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -105,4 +105,14 @@ impl Program { elf: MISSING_OUTPUT_ELF, } } + + /// A program that changes the program owner of an account to [0, 1, 2, 3, 4, 5, 6, 7] + pub fn program_owner_changer() -> Self { + use test_program_methods::{PROGRAM_OWNER_CHANGER_ELF, PROGRAM_OWNER_CHANGER_ID}; + + Program { + id: PROGRAM_OWNER_CHANGER_ID, + elf: PROGRAM_OWNER_CHANGER_ELF, + } + } } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 195727f..278fab4 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -149,6 +149,35 @@ impl V01State { self.insert_program(Program::nonce_changer_program()); self.insert_program(Program::extra_output_program()); self.insert_program(Program::missing_output_program()); + self.insert_program(Program::program_owner_changer()); + self + } + + pub fn with_non_default_accounts_but_default_program_owners(mut self) -> Self { + let account_with_default_values_except_balance = Account { + balance: 100, + ..Account::default() + }; + let account_with_default_values_except_nonce = Account { + nonce: 37, + ..Account::default() + }; + let account_with_default_values_except_data = Account { + data: vec![0xca, 0xfe], + ..Account::default() + }; + self.public_state.insert( + Address::new([255; 32]), + account_with_default_values_except_balance, + ); + self.public_state.insert( + Address::new([254; 32]), + account_with_default_values_except_nonce, + ); + self.public_state.insert( + Address::new([253; 32]), + account_with_default_values_except_data, + ); self } } diff --git a/nssa/src/tests/valid_execution_tests.rs b/nssa/src/tests/valid_execution_tests.rs index f010c06..c2781a7 100644 --- a/nssa/src/tests/valid_execution_tests.rs +++ b/nssa/src/tests/valid_execution_tests.rs @@ -1,3 +1,5 @@ +use nssa_core::account::Account; + use crate::{ Address, PublicTransaction, V01State, error::NssaError, program::Program, public_transaction, }; @@ -34,7 +36,6 @@ fn test_program_should_fail_if_output_accounts_exceed_inputs() { assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); } - #[test] fn test_program_should_fail_with_missing_output_accounts() { let initial_data = [([1; 32], 100)]; @@ -50,3 +51,93 @@ fn test_program_should_fail_with_missing_output_accounts() { assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); } + +#[test] +fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_program_owner() { + let initial_data = [([1; 32], 0)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let address = Address::new([1; 32]); + let account = state.get_account_by_address(&address); + // Assert the target account only differs from the default account in the program owner field + assert_ne!(account.program_owner, Account::default().program_owner); + assert_eq!(account.balance, Account::default().balance); + assert_eq!(account.nonce, Account::default().nonce); + assert_eq!(account.data, Account::default().data); + let program_id = Program::program_owner_changer().id(); + let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); +} + +#[test] +fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_balance() { + let initial_data = []; + let mut state = V01State::new_with_genesis_accounts(&initial_data) + .with_test_programs() + .with_non_default_accounts_but_default_program_owners(); + let address = Address::new([255; 32]); + let account = state.get_account_by_address(&address); + // Assert the target account only differs from the default account in balance field + assert_eq!(account.program_owner, Account::default().program_owner); + assert_ne!(account.balance, Account::default().balance); + assert_eq!(account.nonce, Account::default().nonce); + assert_eq!(account.data, Account::default().data); + let program_id = Program::program_owner_changer().id(); + let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); +} + +#[test] +fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_nonce() { + let initial_data = []; + let mut state = V01State::new_with_genesis_accounts(&initial_data) + .with_test_programs() + .with_non_default_accounts_but_default_program_owners(); + let address = Address::new([254; 32]); + let account = state.get_account_by_address(&address); + // Assert the target account only differs from the default account in balance field + assert_eq!(account.program_owner, Account::default().program_owner); + assert_eq!(account.balance, Account::default().balance); + assert_ne!(account.nonce, Account::default().nonce); + assert_eq!(account.data, Account::default().data); + let program_id = Program::program_owner_changer().id(); + let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); +} + +#[test] +fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_data() { + let initial_data = []; + let mut state = V01State::new_with_genesis_accounts(&initial_data) + .with_test_programs() + .with_non_default_accounts_but_default_program_owners(); + let address = Address::new([253; 32]); + let account = state.get_account_by_address(&address); + // Assert the target account only differs from the default account in balance field + assert_eq!(account.program_owner, Account::default().program_owner); + assert_eq!(account.balance, Account::default().balance); + assert_eq!(account.nonce, Account::default().nonce); + assert_ne!(account.data, Account::default().data); + let program_id = Program::program_owner_changer().id(); + let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); +} diff --git a/nssa/test_program_methods/guest/src/bin/program_owner_changer.rs b/nssa/test_program_methods/guest/src/bin/program_owner_changer.rs new file mode 100644 index 0000000..156b4a4 --- /dev/null +++ b/nssa/test_program_methods/guest/src/bin/program_owner_changer.rs @@ -0,0 +1,18 @@ +use nssa_core::account::AccountWithMetadata; +use risc0_zkvm::guest::env; + +fn main() { + let input_accounts: Vec = env::read(); + let _instruction_data: u128 = env::read(); + + let [pre] = match input_accounts.try_into() { + Ok(array) => array, + Err(_) => return, + }; + + let account_pre = pre.account; + let mut account_post = account_pre.clone(); + account_post.program_owner = [0, 1, 2, 3, 4, 5, 6, 7]; + + env::commit(&vec![account_post]); +} From ad47f48a79541356ae5fb0a87ec0ce4cb561381b Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sun, 10 Aug 2025 11:02:59 -0300 Subject: [PATCH 23/58] add test for invalid balance transfers --- nssa/src/program.rs | 10 +++++ nssa/src/state.rs | 1 + nssa/src/tests/valid_execution_tests.rs | 45 ++++++++++++++----- .../guest/src/bin/simple_balance_transfer.rs | 20 +++++++++ 4 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 2307da2..ba239e9 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -115,4 +115,14 @@ impl Program { elf: PROGRAM_OWNER_CHANGER_ELF, } } + + /// A program that transfers balance without caring about authorizations + pub fn simple_balance_transfer() -> Self { + use test_program_methods::{SIMPLE_BALANCE_TRANSFER_ELF, SIMPLE_BALANCE_TRANSFER_ID}; + + Program { + id: SIMPLE_BALANCE_TRANSFER_ID, + elf: SIMPLE_BALANCE_TRANSFER_ELF, + } + } } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 278fab4..7b48c08 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -150,6 +150,7 @@ impl V01State { self.insert_program(Program::extra_output_program()); self.insert_program(Program::missing_output_program()); self.insert_program(Program::program_owner_changer()); + self.insert_program(Program::simple_balance_transfer()); self } diff --git a/nssa/src/tests/valid_execution_tests.rs b/nssa/src/tests/valid_execution_tests.rs index c2781a7..7ae5144 100644 --- a/nssa/src/tests/valid_execution_tests.rs +++ b/nssa/src/tests/valid_execution_tests.rs @@ -5,13 +5,12 @@ use crate::{ }; #[test] -fn test_program_should_fail_if_it_modifies_nonces() { +fn test_program_should_fail_if_modifies_nonces() { let initial_data = [([1; 32], 100)]; let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32])]; - let nonces = vec![]; let program_id = Program::nonce_changer_program().id(); - let message = public_transaction::Message::new(program_id, addresses, nonces, 0); + let message = public_transaction::Message::new(program_id, addresses, vec![], 0); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -25,9 +24,8 @@ fn test_program_should_fail_if_output_accounts_exceed_inputs() { let initial_data = [([1; 32], 100)]; let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32])]; - let nonces = vec![]; let program_id = Program::extra_output_program().id(); - let message = public_transaction::Message::new(program_id, addresses, nonces, 0); + let message = public_transaction::Message::new(program_id, addresses, vec![], 0); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -41,9 +39,8 @@ fn test_program_should_fail_with_missing_output_accounts() { let initial_data = [([1; 32], 100)]; let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32]), Address::new([2; 32])]; - let nonces = vec![]; let program_id = Program::missing_output_program().id(); - let message = public_transaction::Message::new(program_id, addresses, nonces, 0); + let message = public_transaction::Message::new(program_id, addresses, vec![], 0); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -53,7 +50,7 @@ fn test_program_should_fail_with_missing_output_accounts() { } #[test] -fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_program_owner() { +fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_program_owner() { let initial_data = [([1; 32], 0)]; let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let address = Address::new([1; 32]); @@ -74,7 +71,7 @@ fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_p } #[test] -fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_balance() { +fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_balance() { let initial_data = []; let mut state = V01State::new_with_genesis_accounts(&initial_data) .with_test_programs() @@ -97,7 +94,7 @@ fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_b } #[test] -fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_nonce() { +fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_nonce() { let initial_data = []; let mut state = V01State::new_with_genesis_accounts(&initial_data) .with_test_programs() @@ -120,7 +117,7 @@ fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_n } #[test] -fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_data() { +fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_data() { let initial_data = []; let mut state = V01State::new_with_genesis_accounts(&initial_data) .with_test_programs() @@ -141,3 +138,29 @@ fn test_program_should_fail_if_it_modifies_program_owner_with_only_non_default_d assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); } + +#[test] +fn test_program_should_fail_if_transfers_balance_from_non_owned_account() { + let initial_data = [([1; 32], 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let sender_address = Address::new([1; 32]); + let receiver_address = Address::new([2; 32]); + let balance_to_move = 1; + let program_id = Program::simple_balance_transfer().id(); + assert_ne!( + state.get_account_by_address(&sender_address).program_owner, + program_id + ); + let message = public_transaction::Message::new( + program_id, + vec![sender_address, receiver_address], + vec![], + balance_to_move, + ); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); +} diff --git a/nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs b/nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs new file mode 100644 index 0000000..1fe73ed --- /dev/null +++ b/nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs @@ -0,0 +1,20 @@ +use nssa_core::account::AccountWithMetadata; +use risc0_zkvm::guest::env; + +fn main() { + let input_accounts: Vec = env::read(); + let balance: u128 = env::read(); + + let [sender_pre, receiver_pre] = match input_accounts.try_into() { + Ok(array) => array, + Err(_) => return, + }; + + let mut sender_post = sender_pre.account.clone(); + let mut receiver_post = receiver_pre.account.clone(); + sender_post.balance -= balance; + receiver_post.balance += balance; + + env::commit(&vec![sender_post, receiver_post]); +} + From f86719f54c2671c084b5d3d9dd0c8776309fb7f7 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sun, 10 Aug 2025 11:17:15 -0300 Subject: [PATCH 24/58] add test for invalid data modification --- nssa/src/program.rs | 10 +++++++++ nssa/src/state.rs | 1 + nssa/src/tests/valid_execution_tests.rs | 22 +++++++++++++++++++ .../guest/src/bin/data_changer.rs | 19 ++++++++++++++++ 4 files changed, 52 insertions(+) create mode 100644 nssa/test_program_methods/guest/src/bin/data_changer.rs diff --git a/nssa/src/program.rs b/nssa/src/program.rs index ba239e9..2e1583a 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -125,4 +125,14 @@ impl Program { elf: SIMPLE_BALANCE_TRANSFER_ELF, } } + + /// A program that modifies the data of an account + pub fn data_changer() -> Self { + use test_program_methods::{DATA_CHANGER_ELF, DATA_CHANGER_ID}; + + Program { + id: DATA_CHANGER_ID, + elf: DATA_CHANGER_ELF, + } + } } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 7b48c08..370a88a 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -151,6 +151,7 @@ impl V01State { self.insert_program(Program::missing_output_program()); self.insert_program(Program::program_owner_changer()); self.insert_program(Program::simple_balance_transfer()); + self.insert_program(Program::data_changer()); self } diff --git a/nssa/src/tests/valid_execution_tests.rs b/nssa/src/tests/valid_execution_tests.rs index 7ae5144..a0c6e51 100644 --- a/nssa/src/tests/valid_execution_tests.rs +++ b/nssa/src/tests/valid_execution_tests.rs @@ -164,3 +164,25 @@ fn test_program_should_fail_if_transfers_balance_from_non_owned_account() { assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); } + +#[test] +fn test_program_should_fail_if_modifies_data_of_non_owned_account() { + let initial_data = []; + let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let address = Address::new([1; 32]); + let program_id = Program::data_changer().id(); + + // Consider the extreme case where the target account is the default account + assert_eq!(state.get_account_by_address(&address), Account::default()); + assert_ne!( + state.get_account_by_address(&address).program_owner, + program_id + ); + let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); +} diff --git a/nssa/test_program_methods/guest/src/bin/data_changer.rs b/nssa/test_program_methods/guest/src/bin/data_changer.rs new file mode 100644 index 0000000..9fd3850 --- /dev/null +++ b/nssa/test_program_methods/guest/src/bin/data_changer.rs @@ -0,0 +1,19 @@ +use nssa_core::account::AccountWithMetadata; +use risc0_zkvm::guest::env; + +fn main() { + let input_accounts: Vec = env::read(); + let _instruction_data: u128 = env::read(); + + let [pre] = match input_accounts.try_into() { + Ok(array) => array, + Err(_) => return, + }; + + let account_pre = pre.account; + let mut account_post = account_pre.clone(); + account_post.data.push(0); + + env::commit(&vec![account_post]); +} + From dacf880b8878d1d66a0615f6359b7e1ac0dc3476 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sun, 10 Aug 2025 11:58:25 -0300 Subject: [PATCH 25/58] add test for balance invariance after program execution --- nssa/src/program.rs | 20 +++++++++ nssa/src/state.rs | 12 +++++ nssa/src/tests/state_tests.rs | 1 - nssa/src/tests/valid_execution_tests.rs | 44 ++++++++++++++++++- .../guest/src/bin/burner.rs | 20 +++++++++ .../guest/src/bin/minter.rs | 19 ++++++++ 6 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 nssa/test_program_methods/guest/src/bin/burner.rs create mode 100644 nssa/test_program_methods/guest/src/bin/minter.rs diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 2e1583a..9ec02c5 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -135,4 +135,24 @@ impl Program { elf: DATA_CHANGER_ELF, } } + + /// A program that mints balance + pub fn minter() -> Self { + use test_program_methods::{MINTER_ELF, MINTER_ID}; + + Program { + id: MINTER_ID, + elf: MINTER_ELF, + } + } + + /// A program that mints balance + pub fn burner() -> Self { + use test_program_methods::{BURNER_ELF, BURNER_ID}; + + Program { + id: BURNER_ID, + elf: BURNER_ELF, + } + } } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 370a88a..ec0fc72 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -152,6 +152,8 @@ impl V01State { self.insert_program(Program::program_owner_changer()); self.insert_program(Program::simple_balance_transfer()); self.insert_program(Program::data_changer()); + self.insert_program(Program::minter()); + self.insert_program(Program::burner()); self } @@ -182,4 +184,14 @@ impl V01State { ); self } + + pub fn with_account_owned_by_burner_program(mut self) -> Self { + let account = Account { + program_owner: Program::burner().id(), + balance: 100, + ..Default::default() + }; + self.public_state.insert(Address::new([252; 32]), account); + self + } } diff --git a/nssa/src/tests/state_tests.rs b/nssa/src/tests/state_tests.rs index 6763f17..853b684 100644 --- a/nssa/src/tests/state_tests.rs +++ b/nssa/src/tests/state_tests.rs @@ -4,7 +4,6 @@ use crate::{ }; use nssa_core::account::Account; - fn transfer_transaction( from: Address, from_key: PrivateKey, diff --git a/nssa/src/tests/valid_execution_tests.rs b/nssa/src/tests/valid_execution_tests.rs index a0c6e51..306ca83 100644 --- a/nssa/src/tests/valid_execution_tests.rs +++ b/nssa/src/tests/valid_execution_tests.rs @@ -101,7 +101,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_nonc .with_non_default_accounts_but_default_program_owners(); let address = Address::new([254; 32]); let account = state.get_account_by_address(&address); - // Assert the target account only differs from the default account in balance field + // Assert the target account only differs from the default account in nonce field assert_eq!(account.program_owner, Account::default().program_owner); assert_eq!(account.balance, Account::default().balance); assert_ne!(account.nonce, Account::default().nonce); @@ -124,7 +124,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_data .with_non_default_accounts_but_default_program_owners(); let address = Address::new([253; 32]); let account = state.get_account_by_address(&address); - // Assert the target account only differs from the default account in balance field + // Assert the target account only differs from the default account in data field assert_eq!(account.program_owner, Account::default().program_owner); assert_eq!(account.balance, Account::default().balance); assert_eq!(account.nonce, Account::default().nonce); @@ -186,3 +186,43 @@ fn test_program_should_fail_if_modifies_data_of_non_owned_account() { assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); } + +#[test] +fn test_program_should_fail_if_does_not_preserve_total_balance_by_minting() { + let initial_data = []; + let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let address = Address::new([1; 32]); + let program_id = Program::minter().id(); + + let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); +} + +#[test] +fn test_program_should_fail_if_does_not_preserve_total_balance_by_burning() { + let initial_data = []; + let mut state = V01State::new_with_genesis_accounts(&initial_data) + .with_test_programs() + .with_account_owned_by_burner_program(); + let program_id = Program::burner().id(); + let address = Address::new([252; 32]); + assert_eq!( + state.get_account_by_address(&address).program_owner, + program_id + ); + let balance_to_burn = 1; + assert!(state.get_account_by_address(&address).balance > balance_to_burn); + + let message = + public_transaction::Message::new(program_id, vec![address], vec![], balance_to_burn); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); +} diff --git a/nssa/test_program_methods/guest/src/bin/burner.rs b/nssa/test_program_methods/guest/src/bin/burner.rs new file mode 100644 index 0000000..8ce1fb6 --- /dev/null +++ b/nssa/test_program_methods/guest/src/bin/burner.rs @@ -0,0 +1,20 @@ +use nssa_core::account::AccountWithMetadata; +use risc0_zkvm::guest::env; + +fn main() { + let input_accounts: Vec = env::read(); + let balance_to_burn: u128 = env::read(); + + let [pre] = match input_accounts.try_into() { + Ok(array) => array, + Err(_) => return, + }; + + let account_pre = pre.account; + let mut account_post = account_pre.clone(); + account_post.balance -= balance_to_burn; + + env::commit(&vec![account_post]); +} + + diff --git a/nssa/test_program_methods/guest/src/bin/minter.rs b/nssa/test_program_methods/guest/src/bin/minter.rs new file mode 100644 index 0000000..7c4ce45 --- /dev/null +++ b/nssa/test_program_methods/guest/src/bin/minter.rs @@ -0,0 +1,19 @@ +use nssa_core::account::AccountWithMetadata; +use risc0_zkvm::guest::env; + +fn main() { + let input_accounts: Vec = env::read(); + let _instruction_data: u128 = env::read(); + + let [pre] = match input_accounts.try_into() { + Ok(array) => array, + Err(_) => return, + }; + + let account_pre = pre.account; + let mut account_post = account_pre.clone(); + account_post.balance += 1; + + env::commit(&vec![account_post]); +} + From 15ca5ad4ec041ce63622232feaef885c776346df Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sun, 10 Aug 2025 14:10:11 -0300 Subject: [PATCH 26/58] refactor public_transaction module into folder --- nssa/src/public_transaction.rs | 98 ---------------------- nssa/src/public_transaction/message.rs | 29 +++++++ nssa/src/public_transaction/mod.rs | 52 ++++++++++++ nssa/src/public_transaction/witness_set.rs | 30 +++++++ 4 files changed, 111 insertions(+), 98 deletions(-) delete mode 100644 nssa/src/public_transaction.rs create mode 100644 nssa/src/public_transaction/message.rs create mode 100644 nssa/src/public_transaction/mod.rs create mode 100644 nssa/src/public_transaction/witness_set.rs diff --git a/nssa/src/public_transaction.rs b/nssa/src/public_transaction.rs deleted file mode 100644 index 33a054f..0000000 --- a/nssa/src/public_transaction.rs +++ /dev/null @@ -1,98 +0,0 @@ -use nssa_core::{account::Nonce, program::ProgramId}; -use serde::{Deserialize, Serialize}; -use sha2::{Digest, digest::FixedOutput}; - -use crate::{ - address::Address, - signature::{PrivateKey, PublicKey, Signature}, -}; - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct Message { - pub(crate) program_id: ProgramId, - pub(crate) addresses: Vec

, - pub(crate) nonces: Vec, - // TODO: change to Vec for general programs - pub(crate) instruction_data: u128, -} - -impl Message { - pub fn new( - program_id: ProgramId, - addresses: Vec
, - nonces: Vec, - instruction_data: u128, - ) -> Self { - Self { - program_id, - addresses, - nonces, - instruction_data, - } - } - - fn to_bytes(&self) -> Vec { - //TODO: implement - vec![0, 0] - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct WitnessSet { - pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>, -} - -impl WitnessSet { - pub fn for_message(message: &Message, private_keys: &[&PrivateKey]) -> Self { - let message_bytes = message.to_bytes(); - let signatures_and_public_keys = private_keys - .iter() - .map(|&key| (Signature::new(key, &message_bytes), PublicKey::new(key))) - .collect(); - Self { - signatures_and_public_keys, - } - } - - pub fn iter_signatures(&self) -> impl Iterator { - self.signatures_and_public_keys.iter() - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct PublicTransaction { - message: Message, - witness_set: WitnessSet, -} - -impl PublicTransaction { - pub fn message(&self) -> &Message { - &self.message - } - - pub fn witness_set(&self) -> &WitnessSet { - &self.witness_set - } - - pub(crate) fn signer_addresses(&self) -> Vec
{ - self.witness_set - .signatures_and_public_keys - .iter() - .map(|(_, public_key)| Address::from_public_key(public_key)) - .collect() - } - - pub fn new(message: Message, witness_set: WitnessSet) -> Self { - Self { - message, - witness_set, - } - } - - pub fn hash(&self) -> [u8; 32] { - let bytes = serde_cbor::to_vec(&self).unwrap(); - let mut hasher = sha2::Sha256::new(); - hasher.update(&bytes); - hasher.finalize_fixed().into() - } -} diff --git a/nssa/src/public_transaction/message.rs b/nssa/src/public_transaction/message.rs new file mode 100644 index 0000000..9dd7f2e --- /dev/null +++ b/nssa/src/public_transaction/message.rs @@ -0,0 +1,29 @@ +use nssa_core::{account::Nonce, program::ProgramId}; +use serde::{Deserialize, Serialize}; + +use crate::Address; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct Message { + pub(crate) program_id: ProgramId, + pub(crate) addresses: Vec
, + pub(crate) nonces: Vec, + // TODO: change to Vec for general programs + pub(crate) instruction_data: u128, +} + +impl Message { + pub fn new( + program_id: ProgramId, + addresses: Vec
, + nonces: Vec, + instruction_data: u128, + ) -> Self { + Self { + program_id, + addresses, + nonces, + instruction_data, + } + } +} diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs new file mode 100644 index 0000000..9249f3b --- /dev/null +++ b/nssa/src/public_transaction/mod.rs @@ -0,0 +1,52 @@ +use nssa_core::{account::Nonce, program::ProgramId}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, digest::FixedOutput}; + +use crate::{ + address::Address, + signature::{PrivateKey, PublicKey, Signature}, +}; + +mod message; +mod witness_set; + +pub use message::Message; +pub use witness_set::WitnessSet; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct PublicTransaction { + message: Message, + witness_set: WitnessSet, +} + +impl PublicTransaction { + pub fn message(&self) -> &Message { + &self.message + } + + pub fn witness_set(&self) -> &WitnessSet { + &self.witness_set + } + + pub(crate) fn signer_addresses(&self) -> Vec
{ + self.witness_set + .signatures_and_public_keys + .iter() + .map(|(_, public_key)| Address::from_public_key(public_key)) + .collect() + } + + pub fn new(message: Message, witness_set: WitnessSet) -> Self { + Self { + message, + witness_set, + } + } + + pub fn hash(&self) -> [u8; 32] { + let bytes = serde_cbor::to_vec(&self).unwrap(); + let mut hasher = sha2::Sha256::new(); + hasher.update(&bytes); + hasher.finalize_fixed().into() + } +} diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs new file mode 100644 index 0000000..9cdb312 --- /dev/null +++ b/nssa/src/public_transaction/witness_set.rs @@ -0,0 +1,30 @@ +use serde::{Deserialize, Serialize}; + +use crate::{PrivateKey, PublicKey, Signature, public_transaction::Message}; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct WitnessSet { + pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>, +} + +fn serialize_message_to_bytes(message: &Message) -> Vec { + //TODO: implement + vec![0, 0] +} + +impl WitnessSet { + pub fn for_message(message: &Message, private_keys: &[&PrivateKey]) -> Self { + let message_bytes = serialize_message_to_bytes(&message); + let signatures_and_public_keys = private_keys + .iter() + .map(|&key| (Signature::new(key, &message_bytes), PublicKey::new(key))) + .collect(); + Self { + signatures_and_public_keys, + } + } + + pub fn iter_signatures(&self) -> impl Iterator { + self.signatures_and_public_keys.iter() + } +} From 48fe6f27408dcc07067cd72424dff9a18fc414a4 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sun, 10 Aug 2025 14:33:40 -0300 Subject: [PATCH 27/58] refactor logic --- nssa/src/error.rs | 3 - nssa/src/program.rs | 2 +- nssa/src/public_transaction/mod.rs | 79 ++++++++++++++++++++-- nssa/src/public_transaction/witness_set.rs | 4 +- nssa/src/state.rs | 76 ++------------------- 5 files changed, 82 insertions(+), 82 deletions(-) diff --git a/nssa/src/error.rs b/nssa/src/error.rs index dd62570..decc248 100644 --- a/nssa/src/error.rs +++ b/nssa/src/error.rs @@ -5,9 +5,6 @@ pub enum NssaError { #[error("Invalid input: {0}")] InvalidInput(String), - #[error("Operation failed")] - OperationFailed, - #[error("Risc0 error: {0}")] ProgramExecutionFailed(String), diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 9ec02c5..d1ec4c3 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -146,7 +146,7 @@ impl Program { } } - /// A program that mints balance + /// A program that burns balance pub fn burner() -> Self { use test_program_methods::{BURNER_ELF, BURNER_ID}; diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs index 9249f3b..0ff56a9 100644 --- a/nssa/src/public_transaction/mod.rs +++ b/nssa/src/public_transaction/mod.rs @@ -1,11 +1,13 @@ -use nssa_core::{account::Nonce, program::ProgramId}; +use std::collections::{HashMap, HashSet}; + +use nssa_core::{ + account::{Account, AccountWithMetadata}, + program::validate_execution, +}; use serde::{Deserialize, Serialize}; use sha2::{Digest, digest::FixedOutput}; -use crate::{ - address::Address, - signature::{PrivateKey, PublicKey, Signature}, -}; +use crate::{V01State, address::Address, error::NssaError}; mod message; mod witness_set; @@ -49,4 +51,71 @@ impl PublicTransaction { hasher.update(&bytes); hasher.finalize_fixed().into() } + + pub(crate) fn validate_and_compute_post_states( + &self, + state: &V01State, + ) -> Result, NssaError> { + let message = self.message(); + let witness_set = self.witness_set(); + + // All addresses must be different + if message.addresses.iter().collect::>().len() != message.addresses.len() { + return Err(NssaError::InvalidInput( + "Duplicate addresses found in message".into(), + )); + } + + if message.nonces.len() != witness_set.signatures_and_public_keys.len() { + return Err(NssaError::InvalidInput( + "Mismatch between number of nonces and signatures/public keys".into(), + )); + } + + let mut authorized_addresses = Vec::new(); + for ((signature, public_key), nonce) in witness_set.iter_signatures().zip(&message.nonces) { + // Check the signature is valid + if !signature.is_valid_for(message, public_key) { + return Err(NssaError::InvalidInput( + "Invalid signature for given message and public key".into(), + )); + } + + // Check the nonce corresponds to the current nonce on the public state. + let address = Address::from_public_key(public_key); + let current_nonce = state.get_account_by_address(&address).nonce; + if current_nonce != *nonce { + return Err(NssaError::InvalidInput("Nonce mismatch".into())); + } + + authorized_addresses.push(address); + } + + // Build pre_states for execution + let pre_states: Vec<_> = message + .addresses + .iter() + .map(|address| AccountWithMetadata { + account: state.get_account_by_address(address), + is_authorized: authorized_addresses.contains(address), + }) + .collect(); + + // Check the `program_id` corresponds to a built-in program + // Only allowed program so far is the authenticated transfer program + let Some(program) = state.builtin_programs().get(&message.program_id) else { + return Err(NssaError::InvalidInput("Unknown program".into())); + }; + + // // Execute program + let post_states = program.execute(&pre_states, message.instruction_data)?; + + // Verify execution corresponds to a well-behaved program. + // See the # Programs section for the definition of the `validate_execution` method. + if !validate_execution(&pre_states, &post_states, message.program_id) { + return Err(NssaError::InvalidProgramBehavior); + } + + Ok(message.addresses.iter().cloned().zip(post_states).collect()) + } } diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs index 9cdb312..d5d5aea 100644 --- a/nssa/src/public_transaction/witness_set.rs +++ b/nssa/src/public_transaction/witness_set.rs @@ -7,14 +7,14 @@ pub struct WitnessSet { pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>, } -fn serialize_message_to_bytes(message: &Message) -> Vec { +fn message_to_bytes(_message: &Message) -> Vec { //TODO: implement vec![0, 0] } impl WitnessSet { pub fn for_message(message: &Message, private_keys: &[&PrivateKey]) -> Self { - let message_bytes = serialize_message_to_bytes(&message); + let message_bytes = message_to_bytes(message); let signatures_and_public_keys = private_keys .iter() .map(|&key| (Signature::new(key, &message_bytes), PublicKey::new(key))) diff --git a/nssa/src/state.rs b/nssa/src/state.rs index ec0fc72..04d9c97 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -1,11 +1,8 @@ use crate::{ address::Address, error::NssaError, program::Program, public_transaction::PublicTransaction, }; -use nssa_core::{ - account::{Account, AccountWithMetadata}, - program::{ProgramId, validate_execution}, -}; -use std::collections::{HashMap, HashSet}; +use nssa_core::{account::Account, program::ProgramId}; +use std::collections::HashMap; pub struct V01State { public_state: HashMap, @@ -47,7 +44,7 @@ impl V01State { &mut self, tx: &PublicTransaction, ) -> Result<(), NssaError> { - let state_diff = self.execute_and_verify_public_transaction(tx)?; + let state_diff = tx.validate_and_compute_post_states(self)?; for (address, post) in state_diff.into_iter() { let current_account = self.get_account_by_address_mut(address); @@ -73,71 +70,8 @@ impl V01State { .unwrap_or(Account::default()) } - fn execute_and_verify_public_transaction( - &mut self, - tx: &PublicTransaction, - ) -> Result, NssaError> { - let message = tx.message(); - let witness_set = tx.witness_set(); - - // All addresses must be different - if message.addresses.iter().collect::>().len() != message.addresses.len() { - return Err(NssaError::InvalidInput( - "Duplicate addresses found in message".into(), - )); - } - - if message.nonces.len() != witness_set.signatures_and_public_keys.len() { - return Err(NssaError::InvalidInput( - "Mismatch between number of nonces and signatures/public keys".into(), - )); - } - - let mut authorized_addresses = Vec::new(); - for ((signature, public_key), nonce) in witness_set.iter_signatures().zip(&message.nonces) { - // Check the signature is valid - if !signature.is_valid_for(message, public_key) { - return Err(NssaError::InvalidInput( - "Invalid signature for given message and public key".into(), - )); - } - - // Check the nonce corresponds to the current nonce on the public state. - let address = Address::from_public_key(public_key); - let current_nonce = self.get_account_by_address(&address).nonce; - if current_nonce != *nonce { - return Err(NssaError::InvalidInput("Nonce mismatch".into())); - } - - authorized_addresses.push(address); - } - - // Build pre_states for execution - let pre_states: Vec<_> = message - .addresses - .iter() - .map(|address| AccountWithMetadata { - account: self.get_account_by_address(address), - is_authorized: authorized_addresses.contains(address), - }) - .collect(); - - // Check the `program_id` corresponds to a built-in program - // Only allowed program so far is the authenticated transfer program - let Some(program) = self.builtin_programs.get(&message.program_id) else { - return Err(NssaError::InvalidInput("Unknown program".into())); - }; - - // // Execute program - let post_states = program.execute(&pre_states, message.instruction_data)?; - - // Verify execution corresponds to a well-behaved program. - // See the # Programs section for the definition of the `validate_execution` method. - if !validate_execution(&pre_states, &post_states, message.program_id) { - return Err(NssaError::InvalidProgramBehavior); - } - - Ok(message.addresses.iter().cloned().zip(post_states).collect()) + pub(crate) fn builtin_programs(&self) -> &HashMap { + &self.builtin_programs } } From 102d2e13f98d585843dfb46fd6e7042a870ce94a Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sun, 10 Aug 2025 18:51:55 -0300 Subject: [PATCH 28/58] add general instruction data --- nssa/core/src/program.rs | 9 +++++++ .../guest/src/bin/authenticated_transfer.rs | 5 ++-- nssa/src/error.rs | 3 +++ nssa/src/program.rs | 21 +++++++++------ nssa/src/public_transaction/message.rs | 10 ++++--- nssa/src/public_transaction/mod.rs | 2 +- nssa/src/tests/state_tests.rs | 3 ++- nssa/src/tests/valid_execution_tests.rs | 27 ++++++++++--------- .../guest/src/bin/burner.rs | 9 +++---- .../guest/src/bin/data_changer.rs | 7 ++--- .../guest/src/bin/extra_output.rs | 8 +++--- .../guest/src/bin/minter.rs | 7 ++--- .../guest/src/bin/missing_output.rs | 7 ++--- .../guest/src/bin/nonce_changer.rs | 7 ++--- .../guest/src/bin/program_owner_changer.rs | 7 ++--- .../guest/src/bin/simple_balance_transfer.rs | 8 +++--- 16 files changed, 83 insertions(+), 57 deletions(-) diff --git a/nssa/core/src/program.rs b/nssa/core/src/program.rs index d3d253a..9b99a61 100644 --- a/nssa/core/src/program.rs +++ b/nssa/core/src/program.rs @@ -1,8 +1,17 @@ use crate::account::{Account, AccountWithMetadata}; +use risc0_zkvm::serde::Deserializer; +use risc0_zkvm::{DeserializeOwned, guest::env}; pub type ProgramId = [u32; 8]; +pub type InstructionData = Vec; pub const DEFAULT_PROGRAM_ID: ProgramId = [0; 8]; +pub fn read_nssa_inputs() -> (Vec, T) { + let pre_states: Vec = env::read(); + let words: InstructionData = env::read(); + let instruction_data = T::deserialize(&mut Deserializer::new(words.as_ref())).unwrap(); + (pre_states, instruction_data) +} /// Validates well-behaved program execution /// /// # Parameters diff --git a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs index 8ed3fc4..e6adbb1 100644 --- a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs +++ b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs @@ -1,4 +1,4 @@ -use nssa_core::account::AccountWithMetadata; +use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; /// A transfer of balance program. @@ -6,8 +6,7 @@ use risc0_zkvm::guest::env; fn main() { // Read input accounts. // It is expected to receive only two accounts: [sender_account, receiver_account] - let input_accounts: Vec = env::read(); - let balance_to_move: u128 = env::read(); + let (input_accounts, balance_to_move) = read_nssa_inputs::(); // Continue only if input_accounts is an array of two elements let [sender, receiver] = match input_accounts.try_into() { diff --git a/nssa/src/error.rs b/nssa/src/error.rs index decc248..eefbc88 100644 --- a/nssa/src/error.rs +++ b/nssa/src/error.rs @@ -10,4 +10,7 @@ pub enum NssaError { #[error("Program violated execution rules")] InvalidProgramBehavior, + + #[error("Serialization error: {0}")] + InstructionDataSerializationError(String), } diff --git a/nssa/src/program.rs b/nssa/src/program.rs index d1ec4c3..055b779 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -1,9 +1,10 @@ use nssa_core::{ account::{Account, AccountWithMetadata}, - program::{DEFAULT_PROGRAM_ID, ProgramId}, + program::{DEFAULT_PROGRAM_ID, InstructionData, ProgramId}, }; use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID}; -use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor}; +use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor, serde::to_vec}; +use serde::{Deserialize, Serialize}; use crate::error::NssaError; @@ -17,10 +18,17 @@ impl Program { self.id } + pub fn serialize_instruction_data( + instruction_data: T, + ) -> Result { + to_vec(&instruction_data) + .map_err(|e| NssaError::InstructionDataSerializationError(e.to_string())) + } + pub(crate) fn execute( &self, pre_states: &[AccountWithMetadata], - instruction_data: u128, + instruction_data: &InstructionData, ) -> Result, NssaError> { // Write inputs to the program let mut env_builder = ExecutorEnv::builder(); @@ -52,15 +60,12 @@ impl Program { /// Writes inputs to `env_builder` in the order expected by the programs fn write_inputs( pre_states: &[AccountWithMetadata], - instruction_data: u128, + instruction_data: &[u32], env_builder: &mut ExecutorEnvBuilder, ) -> Result<(), NssaError> { let pre_states = pre_states.to_vec(); env_builder - .write(&pre_states) - .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; - env_builder - .write(&instruction_data) + .write(&(pre_states, instruction_data)) .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; Ok(()) } diff --git a/nssa/src/public_transaction/message.rs b/nssa/src/public_transaction/message.rs index 9dd7f2e..b62e3ad 100644 --- a/nssa/src/public_transaction/message.rs +++ b/nssa/src/public_transaction/message.rs @@ -1,4 +1,7 @@ -use nssa_core::{account::Nonce, program::ProgramId}; +use nssa_core::{ + account::Nonce, + program::{InstructionData, ProgramId}, +}; use serde::{Deserialize, Serialize}; use crate::Address; @@ -8,8 +11,7 @@ pub struct Message { pub(crate) program_id: ProgramId, pub(crate) addresses: Vec
, pub(crate) nonces: Vec, - // TODO: change to Vec for general programs - pub(crate) instruction_data: u128, + pub(crate) instruction_data: InstructionData, } impl Message { @@ -17,7 +19,7 @@ impl Message { program_id: ProgramId, addresses: Vec
, nonces: Vec, - instruction_data: u128, + instruction_data: InstructionData, ) -> Self { Self { program_id, diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs index 0ff56a9..64e051b 100644 --- a/nssa/src/public_transaction/mod.rs +++ b/nssa/src/public_transaction/mod.rs @@ -108,7 +108,7 @@ impl PublicTransaction { }; // // Execute program - let post_states = program.execute(&pre_states, message.instruction_data)?; + let post_states = program.execute(&pre_states, &message.instruction_data)?; // Verify execution corresponds to a well-behaved program. // See the # Programs section for the definition of the `validate_execution` method. diff --git a/nssa/src/tests/state_tests.rs b/nssa/src/tests/state_tests.rs index 853b684..c90f8e9 100644 --- a/nssa/src/tests/state_tests.rs +++ b/nssa/src/tests/state_tests.rs @@ -14,7 +14,8 @@ fn transfer_transaction( let addresses = vec![from, to]; let nonces = vec![nonce]; let program_id = Program::authenticated_transfer_program().id(); - let message = public_transaction::Message::new(program_id, addresses, nonces, balance); + let instruction_data = Program::serialize_instruction_data(&balance).unwrap(); + let message = public_transaction::Message::new(program_id, addresses, nonces, instruction_data); let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]); PublicTransaction::new(message, witness_set) } diff --git a/nssa/src/tests/valid_execution_tests.rs b/nssa/src/tests/valid_execution_tests.rs index 306ca83..6ef32d9 100644 --- a/nssa/src/tests/valid_execution_tests.rs +++ b/nssa/src/tests/valid_execution_tests.rs @@ -10,7 +10,7 @@ fn test_program_should_fail_if_modifies_nonces() { let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32])]; let program_id = Program::nonce_changer_program().id(); - let message = public_transaction::Message::new(program_id, addresses, vec![], 0); + let message = public_transaction::Message::new(program_id, addresses, vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -25,7 +25,7 @@ fn test_program_should_fail_if_output_accounts_exceed_inputs() { let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32])]; let program_id = Program::extra_output_program().id(); - let message = public_transaction::Message::new(program_id, addresses, vec![], 0); + let message = public_transaction::Message::new(program_id, addresses, vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -40,7 +40,7 @@ fn test_program_should_fail_with_missing_output_accounts() { let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32]), Address::new([2; 32])]; let program_id = Program::missing_output_program().id(); - let message = public_transaction::Message::new(program_id, addresses, vec![], 0); + let message = public_transaction::Message::new(program_id, addresses, vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -61,7 +61,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_prog assert_eq!(account.nonce, Account::default().nonce); assert_eq!(account.data, Account::default().data); let program_id = Program::program_owner_changer().id(); - let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + let message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -84,7 +84,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_bala assert_eq!(account.nonce, Account::default().nonce); assert_eq!(account.data, Account::default().data); let program_id = Program::program_owner_changer().id(); - let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + let message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -107,7 +107,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_nonc assert_ne!(account.nonce, Account::default().nonce); assert_eq!(account.data, Account::default().data); let program_id = Program::program_owner_changer().id(); - let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + let message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -130,7 +130,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_data assert_eq!(account.nonce, Account::default().nonce); assert_ne!(account.data, Account::default().data); let program_id = Program::program_owner_changer().id(); - let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + let message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -145,8 +145,9 @@ fn test_program_should_fail_if_transfers_balance_from_non_owned_account() { let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let sender_address = Address::new([1; 32]); let receiver_address = Address::new([2; 32]); - let balance_to_move = 1; + let balance_to_move: u128 = 1; let program_id = Program::simple_balance_transfer().id(); + let instruction_data = Program::serialize_instruction_data(balance_to_move).unwrap(); assert_ne!( state.get_account_by_address(&sender_address).program_owner, program_id @@ -155,12 +156,13 @@ fn test_program_should_fail_if_transfers_balance_from_non_owned_account() { program_id, vec![sender_address, receiver_address], vec![], - balance_to_move, + instruction_data, ); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); let result = state.transition_from_public_transaction(&tx); + println!("{:?}", result); assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); } @@ -178,7 +180,7 @@ fn test_program_should_fail_if_modifies_data_of_non_owned_account() { state.get_account_by_address(&address).program_owner, program_id ); - let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + let message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -194,7 +196,7 @@ fn test_program_should_fail_if_does_not_preserve_total_balance_by_minting() { let address = Address::new([1; 32]); let program_id = Program::minter().id(); - let message = public_transaction::Message::new(program_id, vec![address], vec![], 0); + let message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -218,8 +220,9 @@ fn test_program_should_fail_if_does_not_preserve_total_balance_by_burning() { let balance_to_burn = 1; assert!(state.get_account_by_address(&address).balance > balance_to_burn); + let instruction_data = Program::serialize_instruction_data(balance_to_burn).unwrap(); let message = - public_transaction::Message::new(program_id, vec![address], vec![], balance_to_burn); + public_transaction::Message::new(program_id, vec![address], vec![], instruction_data); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); let result = state.transition_from_public_transaction(&tx); diff --git a/nssa/test_program_methods/guest/src/bin/burner.rs b/nssa/test_program_methods/guest/src/bin/burner.rs index 8ce1fb6..4d89bff 100644 --- a/nssa/test_program_methods/guest/src/bin/burner.rs +++ b/nssa/test_program_methods/guest/src/bin/burner.rs @@ -1,9 +1,10 @@ -use nssa_core::account::AccountWithMetadata; +use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; +type InstructionData = u128; + fn main() { - let input_accounts: Vec = env::read(); - let balance_to_burn: u128 = env::read(); + let (input_accounts, balance_to_burn) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, @@ -16,5 +17,3 @@ fn main() { env::commit(&vec![account_post]); } - - diff --git a/nssa/test_program_methods/guest/src/bin/data_changer.rs b/nssa/test_program_methods/guest/src/bin/data_changer.rs index 9fd3850..bdafe32 100644 --- a/nssa/test_program_methods/guest/src/bin/data_changer.rs +++ b/nssa/test_program_methods/guest/src/bin/data_changer.rs @@ -1,9 +1,10 @@ -use nssa_core::account::AccountWithMetadata; +use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; +type InstructionData = (); + fn main() { - let input_accounts: Vec = env::read(); - let _instruction_data: u128 = env::read(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, diff --git a/nssa/test_program_methods/guest/src/bin/extra_output.rs b/nssa/test_program_methods/guest/src/bin/extra_output.rs index f15c097..815758c 100644 --- a/nssa/test_program_methods/guest/src/bin/extra_output.rs +++ b/nssa/test_program_methods/guest/src/bin/extra_output.rs @@ -1,9 +1,10 @@ -use nssa_core::account::{Account, AccountWithMetadata}; +use nssa_core::{account::Account, program::read_nssa_inputs}; use risc0_zkvm::guest::env; +type InstructionData = (); + fn main() { - let input_accounts: Vec = env::read(); - let _instruction_data: u128 = env::read(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, @@ -14,4 +15,3 @@ fn main() { env::commit(&vec![account_pre, Account::default()]); } - diff --git a/nssa/test_program_methods/guest/src/bin/minter.rs b/nssa/test_program_methods/guest/src/bin/minter.rs index 7c4ce45..c3ca997 100644 --- a/nssa/test_program_methods/guest/src/bin/minter.rs +++ b/nssa/test_program_methods/guest/src/bin/minter.rs @@ -1,9 +1,10 @@ -use nssa_core::account::AccountWithMetadata; +use nssa_core::{account::Account, program::read_nssa_inputs}; use risc0_zkvm::guest::env; +type InstructionData = (); + fn main() { - let input_accounts: Vec = env::read(); - let _instruction_data: u128 = env::read(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, diff --git a/nssa/test_program_methods/guest/src/bin/missing_output.rs b/nssa/test_program_methods/guest/src/bin/missing_output.rs index 7857ea5..a24b2b9 100644 --- a/nssa/test_program_methods/guest/src/bin/missing_output.rs +++ b/nssa/test_program_methods/guest/src/bin/missing_output.rs @@ -1,9 +1,10 @@ -use nssa_core::account::AccountWithMetadata; +use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; +type InstructionData = (); + fn main() { - let input_accounts: Vec = env::read(); - let _instruction_data: u128 = env::read(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre1, _] = match input_accounts.try_into() { Ok(array) => array, diff --git a/nssa/test_program_methods/guest/src/bin/nonce_changer.rs b/nssa/test_program_methods/guest/src/bin/nonce_changer.rs index 08007f6..e2b8f14 100644 --- a/nssa/test_program_methods/guest/src/bin/nonce_changer.rs +++ b/nssa/test_program_methods/guest/src/bin/nonce_changer.rs @@ -1,9 +1,10 @@ -use nssa_core::account::AccountWithMetadata; +use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; +type InstructionData = (); + fn main() { - let input_accounts: Vec = env::read(); - let _instruction_data: u128 = env::read(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, diff --git a/nssa/test_program_methods/guest/src/bin/program_owner_changer.rs b/nssa/test_program_methods/guest/src/bin/program_owner_changer.rs index 156b4a4..8e04e53 100644 --- a/nssa/test_program_methods/guest/src/bin/program_owner_changer.rs +++ b/nssa/test_program_methods/guest/src/bin/program_owner_changer.rs @@ -1,9 +1,10 @@ -use nssa_core::account::AccountWithMetadata; +use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; +type InstructionData = (); + fn main() { - let input_accounts: Vec = env::read(); - let _instruction_data: u128 = env::read(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, diff --git a/nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs b/nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs index 1fe73ed..7f6dbe8 100644 --- a/nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs +++ b/nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs @@ -1,9 +1,10 @@ -use nssa_core::account::AccountWithMetadata; +use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; +type InstructionData = u128; + fn main() { - let input_accounts: Vec = env::read(); - let balance: u128 = env::read(); + let (input_accounts, balance) = read_nssa_inputs::(); let [sender_pre, receiver_pre] = match input_accounts.try_into() { Ok(array) => array, @@ -17,4 +18,3 @@ fn main() { env::commit(&vec![sender_post, receiver_post]); } - From 9d19183786eac5e049c2eb7824023251e78136a9 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sun, 10 Aug 2025 18:59:29 -0300 Subject: [PATCH 29/58] minor refactor --- nssa/core/src/account/mod.rs | 2 +- .../guest/src/bin/authenticated_transfer.rs | 4 ++- nssa/src/program.rs | 2 +- nssa/src/public_transaction/message.rs | 7 ++--- nssa/src/tests/state_tests.rs | 3 +-- nssa/src/tests/valid_execution_tests.rs | 27 +++++++++---------- .../guest/src/bin/burner.rs | 4 +-- .../guest/src/bin/data_changer.rs | 4 +-- .../guest/src/bin/extra_output.rs | 4 +-- .../guest/src/bin/minter.rs | 4 +-- .../guest/src/bin/missing_output.rs | 4 +-- .../guest/src/bin/nonce_changer.rs | 4 +-- .../guest/src/bin/program_owner_changer.rs | 4 +-- .../guest/src/bin/simple_balance_transfer.rs | 4 +-- sequencer_rpc/src/process.rs | 2 +- 15 files changed, 39 insertions(+), 40 deletions(-) diff --git a/nssa/core/src/account/mod.rs b/nssa/core/src/account/mod.rs index 9947953..9d564c4 100644 --- a/nssa/core/src/account/mod.rs +++ b/nssa/core/src/account/mod.rs @@ -44,7 +44,7 @@ mod tests { fn test_empty_data_account_data_creation() { let new_acc = Account::default(); - assert_eq!(new_acc.data, vec![]); + assert!(new_acc.data.is_empty()); } #[test] diff --git a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs index e6adbb1..a89b090 100644 --- a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs +++ b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs @@ -1,12 +1,14 @@ use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; +type Instruction = u128; + /// A transfer of balance program. /// To be used both in public and private contexts. fn main() { // Read input accounts. // It is expected to receive only two accounts: [sender_account, receiver_account] - let (input_accounts, balance_to_move) = read_nssa_inputs::(); + let (input_accounts, balance_to_move) = read_nssa_inputs::(); // Continue only if input_accounts is an array of two elements let [sender, receiver] = match input_accounts.try_into() { diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 055b779..f4b8f84 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -4,7 +4,7 @@ use nssa_core::{ }; use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID}; use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor, serde::to_vec}; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use crate::error::NssaError; diff --git a/nssa/src/public_transaction/message.rs b/nssa/src/public_transaction/message.rs index b62e3ad..30f0ffd 100644 --- a/nssa/src/public_transaction/message.rs +++ b/nssa/src/public_transaction/message.rs @@ -4,7 +4,7 @@ use nssa_core::{ }; use serde::{Deserialize, Serialize}; -use crate::Address; +use crate::{Address, program::Program}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct Message { @@ -15,12 +15,13 @@ pub struct Message { } impl Message { - pub fn new( + pub fn new( program_id: ProgramId, addresses: Vec
, nonces: Vec, - instruction_data: InstructionData, + instruction: T, ) -> Self { + let instruction_data = Program::serialize_instruction_data(instruction).unwrap(); Self { program_id, addresses, diff --git a/nssa/src/tests/state_tests.rs b/nssa/src/tests/state_tests.rs index c90f8e9..853b684 100644 --- a/nssa/src/tests/state_tests.rs +++ b/nssa/src/tests/state_tests.rs @@ -14,8 +14,7 @@ fn transfer_transaction( let addresses = vec![from, to]; let nonces = vec![nonce]; let program_id = Program::authenticated_transfer_program().id(); - let instruction_data = Program::serialize_instruction_data(&balance).unwrap(); - let message = public_transaction::Message::new(program_id, addresses, nonces, instruction_data); + let message = public_transaction::Message::new(program_id, addresses, nonces, balance); let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]); PublicTransaction::new(message, witness_set) } diff --git a/nssa/src/tests/valid_execution_tests.rs b/nssa/src/tests/valid_execution_tests.rs index 6ef32d9..e77b975 100644 --- a/nssa/src/tests/valid_execution_tests.rs +++ b/nssa/src/tests/valid_execution_tests.rs @@ -10,7 +10,7 @@ fn test_program_should_fail_if_modifies_nonces() { let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32])]; let program_id = Program::nonce_changer_program().id(); - let message = public_transaction::Message::new(program_id, addresses, vec![], vec![]); + let message = public_transaction::Message::new(program_id, addresses, vec![], ()); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -25,7 +25,7 @@ fn test_program_should_fail_if_output_accounts_exceed_inputs() { let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32])]; let program_id = Program::extra_output_program().id(); - let message = public_transaction::Message::new(program_id, addresses, vec![], vec![]); + let message = public_transaction::Message::new(program_id, addresses, vec![], ()); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -40,7 +40,7 @@ fn test_program_should_fail_with_missing_output_accounts() { let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32]), Address::new([2; 32])]; let program_id = Program::missing_output_program().id(); - let message = public_transaction::Message::new(program_id, addresses, vec![], vec![]); + let message = public_transaction::Message::new(program_id, addresses, vec![], ()); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -61,7 +61,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_prog assert_eq!(account.nonce, Account::default().nonce); assert_eq!(account.data, Account::default().data); let program_id = Program::program_owner_changer().id(); - let message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); + let message = public_transaction::Message::new(program_id, vec![address], vec![], ()); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -84,7 +84,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_bala assert_eq!(account.nonce, Account::default().nonce); assert_eq!(account.data, Account::default().data); let program_id = Program::program_owner_changer().id(); - let message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); + let message = public_transaction::Message::new(program_id, vec![address], vec![], ()); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -107,7 +107,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_nonc assert_ne!(account.nonce, Account::default().nonce); assert_eq!(account.data, Account::default().data); let program_id = Program::program_owner_changer().id(); - let message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); + let message = public_transaction::Message::new(program_id, vec![address], vec![], ()); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -130,7 +130,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_data assert_eq!(account.nonce, Account::default().nonce); assert_ne!(account.data, Account::default().data); let program_id = Program::program_owner_changer().id(); - let message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); + let message = public_transaction::Message::new(program_id, vec![address], vec![], ()); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -147,7 +147,6 @@ fn test_program_should_fail_if_transfers_balance_from_non_owned_account() { let receiver_address = Address::new([2; 32]); let balance_to_move: u128 = 1; let program_id = Program::simple_balance_transfer().id(); - let instruction_data = Program::serialize_instruction_data(balance_to_move).unwrap(); assert_ne!( state.get_account_by_address(&sender_address).program_owner, program_id @@ -156,13 +155,12 @@ fn test_program_should_fail_if_transfers_balance_from_non_owned_account() { program_id, vec![sender_address, receiver_address], vec![], - instruction_data, + balance_to_move, ); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); let result = state.transition_from_public_transaction(&tx); - println!("{:?}", result); assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); } @@ -180,7 +178,7 @@ fn test_program_should_fail_if_modifies_data_of_non_owned_account() { state.get_account_by_address(&address).program_owner, program_id ); - let message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); + let message = public_transaction::Message::new(program_id, vec![address], vec![], ()); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -196,7 +194,7 @@ fn test_program_should_fail_if_does_not_preserve_total_balance_by_minting() { let address = Address::new([1; 32]); let program_id = Program::minter().id(); - let message = public_transaction::Message::new(program_id, vec![address], vec![], vec![]); + let message = public_transaction::Message::new(program_id, vec![address], vec![], ()); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -217,12 +215,11 @@ fn test_program_should_fail_if_does_not_preserve_total_balance_by_burning() { state.get_account_by_address(&address).program_owner, program_id ); - let balance_to_burn = 1; + let balance_to_burn: u128 = 1; assert!(state.get_account_by_address(&address).balance > balance_to_burn); - let instruction_data = Program::serialize_instruction_data(balance_to_burn).unwrap(); let message = - public_transaction::Message::new(program_id, vec![address], vec![], instruction_data); + public_transaction::Message::new(program_id, vec![address], vec![], balance_to_burn); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); let result = state.transition_from_public_transaction(&tx); diff --git a/nssa/test_program_methods/guest/src/bin/burner.rs b/nssa/test_program_methods/guest/src/bin/burner.rs index 4d89bff..018c203 100644 --- a/nssa/test_program_methods/guest/src/bin/burner.rs +++ b/nssa/test_program_methods/guest/src/bin/burner.rs @@ -1,10 +1,10 @@ use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; -type InstructionData = u128; +type Instruction = u128; fn main() { - let (input_accounts, balance_to_burn) = read_nssa_inputs::(); + let (input_accounts, balance_to_burn) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, diff --git a/nssa/test_program_methods/guest/src/bin/data_changer.rs b/nssa/test_program_methods/guest/src/bin/data_changer.rs index bdafe32..fa1efd3 100644 --- a/nssa/test_program_methods/guest/src/bin/data_changer.rs +++ b/nssa/test_program_methods/guest/src/bin/data_changer.rs @@ -1,10 +1,10 @@ use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; -type InstructionData = (); +type Instruction = (); fn main() { - let (input_accounts, _) = read_nssa_inputs::(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, diff --git a/nssa/test_program_methods/guest/src/bin/extra_output.rs b/nssa/test_program_methods/guest/src/bin/extra_output.rs index 815758c..c02c5e2 100644 --- a/nssa/test_program_methods/guest/src/bin/extra_output.rs +++ b/nssa/test_program_methods/guest/src/bin/extra_output.rs @@ -1,10 +1,10 @@ use nssa_core::{account::Account, program::read_nssa_inputs}; use risc0_zkvm::guest::env; -type InstructionData = (); +type Instruction = (); fn main() { - let (input_accounts, _) = read_nssa_inputs::(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, diff --git a/nssa/test_program_methods/guest/src/bin/minter.rs b/nssa/test_program_methods/guest/src/bin/minter.rs index c3ca997..9e089a5 100644 --- a/nssa/test_program_methods/guest/src/bin/minter.rs +++ b/nssa/test_program_methods/guest/src/bin/minter.rs @@ -1,10 +1,10 @@ use nssa_core::{account::Account, program::read_nssa_inputs}; use risc0_zkvm::guest::env; -type InstructionData = (); +type Instruction = (); fn main() { - let (input_accounts, _) = read_nssa_inputs::(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, diff --git a/nssa/test_program_methods/guest/src/bin/missing_output.rs b/nssa/test_program_methods/guest/src/bin/missing_output.rs index a24b2b9..61aa8c5 100644 --- a/nssa/test_program_methods/guest/src/bin/missing_output.rs +++ b/nssa/test_program_methods/guest/src/bin/missing_output.rs @@ -1,10 +1,10 @@ use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; -type InstructionData = (); +type Instruction = (); fn main() { - let (input_accounts, _) = read_nssa_inputs::(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre1, _] = match input_accounts.try_into() { Ok(array) => array, diff --git a/nssa/test_program_methods/guest/src/bin/nonce_changer.rs b/nssa/test_program_methods/guest/src/bin/nonce_changer.rs index e2b8f14..eb1365e 100644 --- a/nssa/test_program_methods/guest/src/bin/nonce_changer.rs +++ b/nssa/test_program_methods/guest/src/bin/nonce_changer.rs @@ -1,10 +1,10 @@ use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; -type InstructionData = (); +type Instruction = (); fn main() { - let (input_accounts, _) = read_nssa_inputs::(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, diff --git a/nssa/test_program_methods/guest/src/bin/program_owner_changer.rs b/nssa/test_program_methods/guest/src/bin/program_owner_changer.rs index 8e04e53..4d11438 100644 --- a/nssa/test_program_methods/guest/src/bin/program_owner_changer.rs +++ b/nssa/test_program_methods/guest/src/bin/program_owner_changer.rs @@ -1,10 +1,10 @@ use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; -type InstructionData = (); +type Instruction = (); fn main() { - let (input_accounts, _) = read_nssa_inputs::(); + let (input_accounts, _) = read_nssa_inputs::(); let [pre] = match input_accounts.try_into() { Ok(array) => array, diff --git a/nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs b/nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs index 7f6dbe8..047e252 100644 --- a/nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs +++ b/nssa/test_program_methods/guest/src/bin/simple_balance_transfer.rs @@ -1,10 +1,10 @@ use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; -type InstructionData = u128; +type Instruction = u128; fn main() { - let (input_accounts, balance) = read_nssa_inputs::(); + let (input_accounts, balance) = read_nssa_inputs::(); let [sender_pre, receiver_pre] = match input_accounts.try_into() { Ok(array) => array, diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 1757636..86cb373 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -496,7 +496,7 @@ mod tests { { "value": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, { "value": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] } ], - "instruction_data": 10, + "instruction_data": [10, 0, 0, 0], "nonces": [0], "program_id": nssa::program::Program::authenticated_transfer_program().id(), }, From 43058193df6c0193c29e4758bfe461485587389a Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sun, 10 Aug 2025 19:53:05 -0300 Subject: [PATCH 30/58] minor change --- nssa/src/program.rs | 2 +- nssa/src/public_transaction/message.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nssa/src/program.rs b/nssa/src/program.rs index f4b8f84..f6f18fa 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -18,7 +18,7 @@ impl Program { self.id } - pub fn serialize_instruction_data( + pub fn serialize_instruction( instruction_data: T, ) -> Result { to_vec(&instruction_data) diff --git a/nssa/src/public_transaction/message.rs b/nssa/src/public_transaction/message.rs index 30f0ffd..39989ae 100644 --- a/nssa/src/public_transaction/message.rs +++ b/nssa/src/public_transaction/message.rs @@ -21,7 +21,7 @@ impl Message { nonces: Vec, instruction: T, ) -> Self { - let instruction_data = Program::serialize_instruction_data(instruction).unwrap(); + let instruction_data = Program::serialize_instruction(instruction).unwrap(); Self { program_id, addresses, From 54dd9aa814952eed5e98136cbb460ef58569b71c Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 11 Aug 2025 12:07:30 -0300 Subject: [PATCH 31/58] minor refactor --- common/src/test_utils.rs | 4 ++-- nssa/src/error.rs | 2 +- nssa/src/program.rs | 6 +++--- nssa/src/public_transaction/message.rs | 12 ++++++------ nssa/src/tests/state_tests.rs | 2 +- nssa/src/tests/valid_execution_tests.rs | 24 ++++++++++++------------ wallet/src/lib.rs | 4 ++-- 7 files changed, 27 insertions(+), 27 deletions(-) diff --git a/common/src/test_utils.rs b/common/src/test_utils.rs index 8cc071f..7a75088 100644 --- a/common/src/test_utils.rs +++ b/common/src/test_utils.rs @@ -36,7 +36,7 @@ pub fn produce_dummy_empty_transaction() -> nssa::PublicTransaction { let nonces = vec![]; let instruction_data = 0; let message = - nssa::public_transaction::Message::new(program_id, addresses, nonces, instruction_data); + nssa::public_transaction::Message::try_new(program_id, addresses, nonces, instruction_data).unwrap(); let private_key = nssa::PrivateKey::new(1); let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[&private_key]); nssa::PublicTransaction::new(message, witness_set) @@ -53,7 +53,7 @@ pub fn create_transaction_native_token_transfer( let nonces = vec![nonce]; let program_id = nssa::program::Program::authenticated_transfer_program().id(); let message = - nssa::public_transaction::Message::new(program_id, addresses, nonces, balance_to_move); + nssa::public_transaction::Message::try_new(program_id, addresses, nonces, balance_to_move).unwrap(); let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[&signing_key]); nssa::PublicTransaction::new(message, witness_set) } diff --git a/nssa/src/error.rs b/nssa/src/error.rs index eefbc88..082f108 100644 --- a/nssa/src/error.rs +++ b/nssa/src/error.rs @@ -12,5 +12,5 @@ pub enum NssaError { InvalidProgramBehavior, #[error("Serialization error: {0}")] - InstructionDataSerializationError(String), + InstructionSerializationError(String), } diff --git a/nssa/src/program.rs b/nssa/src/program.rs index f6f18fa..c62310e 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -19,10 +19,10 @@ impl Program { } pub fn serialize_instruction( - instruction_data: T, + instruction: T, ) -> Result { - to_vec(&instruction_data) - .map_err(|e| NssaError::InstructionDataSerializationError(e.to_string())) + to_vec(&instruction) + .map_err(|e| NssaError::InstructionSerializationError(e.to_string())) } pub(crate) fn execute( diff --git a/nssa/src/public_transaction/message.rs b/nssa/src/public_transaction/message.rs index 39989ae..784777d 100644 --- a/nssa/src/public_transaction/message.rs +++ b/nssa/src/public_transaction/message.rs @@ -4,7 +4,7 @@ use nssa_core::{ }; use serde::{Deserialize, Serialize}; -use crate::{Address, program::Program}; +use crate::{Address, error::NssaError, program::Program}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct Message { @@ -15,18 +15,18 @@ pub struct Message { } impl Message { - pub fn new( + pub fn try_new( program_id: ProgramId, addresses: Vec
, nonces: Vec, instruction: T, - ) -> Self { - let instruction_data = Program::serialize_instruction(instruction).unwrap(); - Self { + ) -> Result { + let instruction_data = Program::serialize_instruction(instruction)?; + Ok(Self { program_id, addresses, nonces, instruction_data, - } + }) } } diff --git a/nssa/src/tests/state_tests.rs b/nssa/src/tests/state_tests.rs index 853b684..7b20f7d 100644 --- a/nssa/src/tests/state_tests.rs +++ b/nssa/src/tests/state_tests.rs @@ -14,7 +14,7 @@ fn transfer_transaction( let addresses = vec![from, to]; let nonces = vec![nonce]; let program_id = Program::authenticated_transfer_program().id(); - let message = public_transaction::Message::new(program_id, addresses, nonces, balance); + let message = public_transaction::Message::try_new(program_id, addresses, nonces, balance).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]); PublicTransaction::new(message, witness_set) } diff --git a/nssa/src/tests/valid_execution_tests.rs b/nssa/src/tests/valid_execution_tests.rs index e77b975..8870cdd 100644 --- a/nssa/src/tests/valid_execution_tests.rs +++ b/nssa/src/tests/valid_execution_tests.rs @@ -10,7 +10,7 @@ fn test_program_should_fail_if_modifies_nonces() { let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32])]; let program_id = Program::nonce_changer_program().id(); - let message = public_transaction::Message::new(program_id, addresses, vec![], ()); + let message = public_transaction::Message::try_new(program_id, addresses, vec![], ()).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -25,7 +25,7 @@ fn test_program_should_fail_if_output_accounts_exceed_inputs() { let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32])]; let program_id = Program::extra_output_program().id(); - let message = public_transaction::Message::new(program_id, addresses, vec![], ()); + let message = public_transaction::Message::try_new(program_id, addresses, vec![], ()).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -40,7 +40,7 @@ fn test_program_should_fail_with_missing_output_accounts() { let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); let addresses = vec![Address::new([1; 32]), Address::new([2; 32])]; let program_id = Program::missing_output_program().id(); - let message = public_transaction::Message::new(program_id, addresses, vec![], ()); + let message = public_transaction::Message::try_new(program_id, addresses, vec![], ()).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -61,7 +61,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_prog assert_eq!(account.nonce, Account::default().nonce); assert_eq!(account.data, Account::default().data); let program_id = Program::program_owner_changer().id(); - let message = public_transaction::Message::new(program_id, vec![address], vec![], ()); + let message = public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -84,7 +84,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_bala assert_eq!(account.nonce, Account::default().nonce); assert_eq!(account.data, Account::default().data); let program_id = Program::program_owner_changer().id(); - let message = public_transaction::Message::new(program_id, vec![address], vec![], ()); + let message = public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -107,7 +107,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_nonc assert_ne!(account.nonce, Account::default().nonce); assert_eq!(account.data, Account::default().data); let program_id = Program::program_owner_changer().id(); - let message = public_transaction::Message::new(program_id, vec![address], vec![], ()); + let message = public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -130,7 +130,7 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_data assert_eq!(account.nonce, Account::default().nonce); assert_ne!(account.data, Account::default().data); let program_id = Program::program_owner_changer().id(); - let message = public_transaction::Message::new(program_id, vec![address], vec![], ()); + let message = public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -151,12 +151,12 @@ fn test_program_should_fail_if_transfers_balance_from_non_owned_account() { state.get_account_by_address(&sender_address).program_owner, program_id ); - let message = public_transaction::Message::new( + let message = public_transaction::Message::try_new( program_id, vec![sender_address, receiver_address], vec![], balance_to_move, - ); + ).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -178,7 +178,7 @@ fn test_program_should_fail_if_modifies_data_of_non_owned_account() { state.get_account_by_address(&address).program_owner, program_id ); - let message = public_transaction::Message::new(program_id, vec![address], vec![], ()); + let message = public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -194,7 +194,7 @@ fn test_program_should_fail_if_does_not_preserve_total_balance_by_minting() { let address = Address::new([1; 32]); let program_id = Program::minter().id(); - let message = public_transaction::Message::new(program_id, vec![address], vec![], ()); + let message = public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -219,7 +219,7 @@ fn test_program_should_fail_if_does_not_preserve_total_balance_by_burning() { assert!(state.get_account_by_address(&address).balance > balance_to_burn); let message = - public_transaction::Message::new(program_id, vec![address], vec![], balance_to_burn); + public_transaction::Message::try_new(program_id, vec![address], vec![], balance_to_burn).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); let result = state.transition_from_public_transaction(&tx); diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 1d8c68b..19152ee 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -79,12 +79,12 @@ impl WalletCore { let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)]; let nonces = vec![nonce]; let program_id = nssa::program::Program::authenticated_transfer_program().id(); - let message = nssa::public_transaction::Message::new( + let message = nssa::public_transaction::Message::try_new( program_id, addresses, nonces, balance_to_move, - ); + ).unwrap(); let signing_key = account.key_holder.get_pub_account_signing_key(); let witness_set = From 703a127f0ebfa65913c5fe75985af4f309faabf9 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 11 Aug 2025 19:14:12 -0300 Subject: [PATCH 32/58] implement public and private keys --- nssa/Cargo.toml | 1 + nssa/src/address.rs | 2 +- nssa/src/error.rs | 3 + nssa/src/program.rs | 3 +- nssa/src/public_transaction/witness_set.rs | 36 +++++++++- nssa/src/signature.rs | 34 +++++++--- nssa/src/tests/state_tests.rs | 76 ++++++++++------------ nssa/src/tests/valid_execution_tests.rs | 24 ++++--- 8 files changed, 115 insertions(+), 64 deletions(-) diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index aea82b9..90bd34f 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -11,6 +11,7 @@ program-methods = { path = "program_methods" } serde = "1.0.219" serde_cbor = "0.11.2" sha2 = "0.10.9" +secp256k1 = "0.31.1" [dev-dependencies] test-program-methods = { path = "test_program_methods" } diff --git a/nssa/src/address.rs b/nssa/src/address.rs index c5f37a6..a8a9634 100644 --- a/nssa/src/address.rs +++ b/nssa/src/address.rs @@ -14,7 +14,7 @@ impl Address { pub fn from_public_key(public_key: &PublicKey) -> Self { // TODO: implement - Address::new([public_key.0; 32]) + Address::new(public_key.0) } pub fn value(&self) -> &[u8; 32] { diff --git a/nssa/src/error.rs b/nssa/src/error.rs index 082f108..c4c96c7 100644 --- a/nssa/src/error.rs +++ b/nssa/src/error.rs @@ -13,4 +13,7 @@ pub enum NssaError { #[error("Serialization error: {0}")] InstructionSerializationError(String), + + #[error("Invalid private key")] + InvalidPrivateKey, } diff --git a/nssa/src/program.rs b/nssa/src/program.rs index c62310e..b1ad8ca 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -21,8 +21,7 @@ impl Program { pub fn serialize_instruction( instruction: T, ) -> Result { - to_vec(&instruction) - .map_err(|e| NssaError::InstructionSerializationError(e.to_string())) + to_vec(&instruction).map_err(|e| NssaError::InstructionSerializationError(e.to_string())) } pub(crate) fn execute( diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs index d5d5aea..61e1683 100644 --- a/nssa/src/public_transaction/witness_set.rs +++ b/nssa/src/public_transaction/witness_set.rs @@ -7,9 +7,39 @@ pub struct WitnessSet { pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>, } -fn message_to_bytes(_message: &Message) -> Vec { - //TODO: implement - vec![0, 0] +const MESSAGE_ENCODING_PREFIX: &[u8; 19] = b"NSSA/v0.1/TxMessage"; + +/// Serializes a `Message` into bytes in the following layout: +/// TAG || (bytes LE) * 8 || addresses_len (4 bytes LE) || addresses (32 bytes * N) || nonces_len (4 bytes LE) || nonces (16 bytes * M) || instruction_data_len || instruction_data (4 bytes * K) +/// Integers and words are encoded in little-endian byte order, and fields appear in the above order. +fn message_to_bytes(message: &Message) -> Vec { + let mut bytes = MESSAGE_ENCODING_PREFIX.to_vec(); + // program_id: [u32; 8] + for word in &message.program_id { + bytes.extend_from_slice(&word.to_le_bytes()); + } + // addresses: Vec<[u8;32]> + // serialize length as u32 little endian, then all addresses concatenated + let addresses_len = message.addresses.len() as u32; + bytes.extend(&addresses_len.to_le_bytes()); + for addr in &message.addresses { + bytes.extend_from_slice(addr.value()); + } + // nonces: Vec + let nonces_len = message.nonces.len() as u32; + bytes.extend(&nonces_len.to_le_bytes()); + for nonce in &message.nonces { + bytes.extend(&nonce.to_le_bytes()); + } + // instruction_data: Vec + // serialize length as u32 little endian, then all addresses concatenated + let instr_len = message.instruction_data.len() as u32; + bytes.extend(&instr_len.to_le_bytes()); + for word in &message.instruction_data { + bytes.extend(&word.to_le_bytes()); + } + + bytes } impl WitnessSet { diff --git a/nssa/src/signature.rs b/nssa/src/signature.rs index 9e36a7c..02fe7b3 100644 --- a/nssa/src/signature.rs +++ b/nssa/src/signature.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::public_transaction::Message; +use crate::{error::NssaError, public_transaction::Message}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct Signature(pub(crate) u8); @@ -9,32 +9,46 @@ pub struct Signature(pub(crate) u8); // TODO: Remove Debug, Clone, Serialize, Deserialize, PartialEq and Eq for security reasons // TODO: Implement Zeroize #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct PrivateKey(pub(crate) u8); +pub struct PrivateKey(pub(crate) [u8; 32]); impl PrivateKey { - pub fn new(dummy_value: u8) -> Self { - Self(dummy_value) + fn is_valid_key(value: [u8; 32]) -> bool { + secp256k1::SecretKey::from_byte_array(value).is_ok() + } + + pub fn try_new(value: [u8; 32]) -> Result { + if Self::is_valid_key(value) { + Ok(Self(value)) + } else { + Err(NssaError::InvalidPrivateKey) + } } } // TODO: Dummy impl. Replace by actual public key. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct PublicKey(pub(crate) u8); +pub struct PublicKey(pub(crate) [u8; 32]); impl PublicKey { pub fn new(key: &PrivateKey) -> Self { - // TODO: implement - Self(key.0) + let value = { + let secret_key = secp256k1::SecretKey::from_byte_array(key.0).unwrap(); + let public_key = + secp256k1::PublicKey::from_secret_key(&secp256k1::Secp256k1::new(), &secret_key); + let (x_only, _) = public_key.x_only_public_key(); + x_only.serialize() + }; + Self(value) } } impl Signature { pub(crate) fn new(key: &PrivateKey, _message: &[u8]) -> Self { - Signature(key.0) + Signature(0) } - pub fn is_valid_for(&self, _message: &Message, public_key: &PublicKey) -> bool { + pub fn is_valid_for(&self, _message: &Message, _public_key: &PublicKey) -> bool { // TODO: implement - self.0 == public_key.0 + true } } diff --git a/nssa/src/tests/state_tests.rs b/nssa/src/tests/state_tests.rs index 7b20f7d..c0745f7 100644 --- a/nssa/src/tests/state_tests.rs +++ b/nssa/src/tests/state_tests.rs @@ -1,6 +1,6 @@ use crate::{ - Address, PublicTransaction, V01State, error::NssaError, program::Program, public_transaction, - signature::PrivateKey, + Address, PublicKey, PublicTransaction, V01State, error::NssaError, program::Program, + public_transaction, signature::PrivateKey, }; use nssa_core::account::Account; @@ -14,22 +14,24 @@ fn transfer_transaction( let addresses = vec![from, to]; let nonces = vec![nonce]; let program_id = Program::authenticated_transfer_program().id(); - let message = public_transaction::Message::try_new(program_id, addresses, nonces, balance).unwrap(); + let message = + public_transaction::Message::try_new(program_id, addresses, nonces, balance).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]); PublicTransaction::new(message, witness_set) } #[test] fn transition_from_authenticated_transfer_program_invocation_default_account_destination() { - let initial_data = [([1; 32], 100)]; + let key = PrivateKey::try_new([1; 32]).unwrap(); + let address = Address::from_public_key(&PublicKey::new(&key)); + let initial_data = [(*address.value(), 100)]; let mut state = V01State::new_with_genesis_accounts(&initial_data); - let from = Address::new(initial_data[0].0); - let from_key = PrivateKey(1); + let from = address; let to = Address::new([2; 32]); assert_eq!(state.get_account_by_address(&to), Account::default()); let balance_to_move = 5; - let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move); + let tx = transfer_transaction(from.clone(), key, 0, to.clone(), balance_to_move); state.transition_from_public_transaction(&tx).unwrap(); assert_eq!(state.get_account_by_address(&from).balance, 95); @@ -40,10 +42,12 @@ fn transition_from_authenticated_transfer_program_invocation_default_account_des #[test] fn transition_from_authenticated_transfer_program_invocation_insuficient_balance() { - let initial_data = [([1; 32], 100)]; + let key = PrivateKey::try_new([1; 32]).unwrap(); + let address = Address::from_public_key(&PublicKey::new(&key)); + let initial_data = [(*address.value(), 100)]; let mut state = V01State::new_with_genesis_accounts(&initial_data); - let from = Address::new(initial_data[0].0); - let from_key = PrivateKey(1); + let from = address; + let from_key = key; let to = Address::new([2; 32]); let balance_to_move = 101; assert!(state.get_account_by_address(&from).balance < balance_to_move); @@ -60,11 +64,15 @@ fn transition_from_authenticated_transfer_program_invocation_insuficient_balance #[test] fn transition_from_authenticated_transfer_program_invocation_non_default_account_destination() { - let initial_data = [([1; 32], 100), ([99; 32], 200)]; + let key1 = PrivateKey::try_new([1; 32]).unwrap(); + let key2 = PrivateKey::try_new([2; 32]).unwrap(); + let address1 = Address::from_public_key(&PublicKey::new(&key1)); + let address2 = Address::from_public_key(&PublicKey::new(&key2)); + let initial_data = [(*address1.value(), 100), (*address2.value(), 200)]; let mut state = V01State::new_with_genesis_accounts(&initial_data); - let from = Address::new(initial_data[1].0); - let from_key = PrivateKey(99); - let to = Address::new(initial_data[0].0); + let from = address2; + let from_key = key2; + let to = address1; assert_ne!(state.get_account_by_address(&to), Account::default()); let balance_to_move = 8; @@ -79,37 +87,25 @@ fn transition_from_authenticated_transfer_program_invocation_non_default_account #[test] fn transition_from_chained_authenticated_transfer_program_invocations() { - let initial_data = [([1; 32], 100)]; + let key1 = PrivateKey::try_new([1; 32]).unwrap(); + let address1 = Address::from_public_key(&PublicKey::new(&key1)); + let key2 = PrivateKey::try_new([2; 32]).unwrap(); + let address2 = Address::from_public_key(&PublicKey::new(&key2)); + let initial_data = [(*address1.value(), 100)]; let mut state = V01State::new_with_genesis_accounts(&initial_data); - let address_1 = Address::new(initial_data[0].0); - let key_1 = PrivateKey(1); - let address_2 = Address::new([2; 32]); - let key_2 = PrivateKey(2); - let address_3 = Address::new([3; 32]); + let address3 = Address::new([3; 32]); let balance_to_move = 5; - let tx = transfer_transaction( - address_1.clone(), - key_1, - 0, - address_2.clone(), - balance_to_move, - ); + let tx = transfer_transaction(address1.clone(), key1, 0, address2.clone(), balance_to_move); state.transition_from_public_transaction(&tx).unwrap(); let balance_to_move = 3; - let tx = transfer_transaction( - address_2.clone(), - key_2, - 0, - address_3.clone(), - balance_to_move, - ); + let tx = transfer_transaction(address2.clone(), key2, 0, address3.clone(), balance_to_move); state.transition_from_public_transaction(&tx).unwrap(); - assert_eq!(state.get_account_by_address(&address_1).balance, 95); - assert_eq!(state.get_account_by_address(&address_2).balance, 2); - assert_eq!(state.get_account_by_address(&address_3).balance, 3); - assert_eq!(state.get_account_by_address(&address_1).nonce, 1); - assert_eq!(state.get_account_by_address(&address_2).nonce, 1); - assert_eq!(state.get_account_by_address(&address_3).nonce, 0); + assert_eq!(state.get_account_by_address(&address1).balance, 95); + assert_eq!(state.get_account_by_address(&address2).balance, 2); + assert_eq!(state.get_account_by_address(&address3).balance, 3); + assert_eq!(state.get_account_by_address(&address1).nonce, 1); + assert_eq!(state.get_account_by_address(&address2).nonce, 1); + assert_eq!(state.get_account_by_address(&address3).nonce, 0); } diff --git a/nssa/src/tests/valid_execution_tests.rs b/nssa/src/tests/valid_execution_tests.rs index 8870cdd..399c47a 100644 --- a/nssa/src/tests/valid_execution_tests.rs +++ b/nssa/src/tests/valid_execution_tests.rs @@ -61,7 +61,8 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_prog assert_eq!(account.nonce, Account::default().nonce); assert_eq!(account.data, Account::default().data); let program_id = Program::program_owner_changer().id(); - let message = public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); + let message = + public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -84,7 +85,8 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_bala assert_eq!(account.nonce, Account::default().nonce); assert_eq!(account.data, Account::default().data); let program_id = Program::program_owner_changer().id(); - let message = public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); + let message = + public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -107,7 +109,8 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_nonc assert_ne!(account.nonce, Account::default().nonce); assert_eq!(account.data, Account::default().data); let program_id = Program::program_owner_changer().id(); - let message = public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); + let message = + public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -130,7 +133,8 @@ fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_data assert_eq!(account.nonce, Account::default().nonce); assert_ne!(account.data, Account::default().data); let program_id = Program::program_owner_changer().id(); - let message = public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); + let message = + public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -156,7 +160,8 @@ fn test_program_should_fail_if_transfers_balance_from_non_owned_account() { vec![sender_address, receiver_address], vec![], balance_to_move, - ).unwrap(); + ) + .unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -178,7 +183,8 @@ fn test_program_should_fail_if_modifies_data_of_non_owned_account() { state.get_account_by_address(&address).program_owner, program_id ); - let message = public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); + let message = + public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -194,7 +200,8 @@ fn test_program_should_fail_if_does_not_preserve_total_balance_by_minting() { let address = Address::new([1; 32]); let program_id = Program::minter().id(); - let message = public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); + let message = + public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); @@ -219,7 +226,8 @@ fn test_program_should_fail_if_does_not_preserve_total_balance_by_burning() { assert!(state.get_account_by_address(&address).balance > balance_to_burn); let message = - public_transaction::Message::try_new(program_id, vec![address], vec![], balance_to_burn).unwrap(); + public_transaction::Message::try_new(program_id, vec![address], vec![], balance_to_burn) + .unwrap(); let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); let tx = PublicTransaction::new(message, witness_set); let result = state.transition_from_public_transaction(&tx); From 5bc1c9c688f6e6cb0d2effa952a5d89fafc71c5d Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 11 Aug 2025 20:22:41 -0300 Subject: [PATCH 33/58] add signatures --- nssa/src/address.rs | 2 +- nssa/src/public_transaction/message.rs | 35 ++++++++++++++ nssa/src/public_transaction/mod.rs | 25 +++++----- nssa/src/public_transaction/witness_set.rs | 47 +++++------------- nssa/src/signature.rs | 54 --------------------- nssa/src/signature/mod.rs | 7 +++ nssa/src/signature/private_key.rs | 25 ++++++++++ nssa/src/signature/public_key.rs | 21 ++++++++ nssa/src/signature/signature.rs | 56 ++++++++++++++++++++++ nssa/src/tests/state_tests.rs | 2 +- 10 files changed, 168 insertions(+), 106 deletions(-) delete mode 100644 nssa/src/signature.rs create mode 100644 nssa/src/signature/mod.rs create mode 100644 nssa/src/signature/private_key.rs create mode 100644 nssa/src/signature/public_key.rs create mode 100644 nssa/src/signature/signature.rs diff --git a/nssa/src/address.rs b/nssa/src/address.rs index a8a9634..eeb9102 100644 --- a/nssa/src/address.rs +++ b/nssa/src/address.rs @@ -13,7 +13,7 @@ impl Address { } pub fn from_public_key(public_key: &PublicKey) -> Self { - // TODO: implement + // TODO: Check specs Address::new(public_key.0) } diff --git a/nssa/src/public_transaction/message.rs b/nssa/src/public_transaction/message.rs index 784777d..aaa04ed 100644 --- a/nssa/src/public_transaction/message.rs +++ b/nssa/src/public_transaction/message.rs @@ -29,4 +29,39 @@ impl Message { instruction_data, }) } + + /// Serializes a `Message` into bytes in the following layout: + /// TAG || (bytes LE) * 8 || addresses_len (4 bytes LE) || addresses (32 bytes * N) || nonces_len (4 bytes LE) || nonces (16 bytes * M) || instruction_data_len || instruction_data (4 bytes * K) + /// Integers and words are encoded in little-endian byte order, and fields appear in the above order. + pub(crate) fn message_to_bytes(&self) -> Vec { + const MESSAGE_ENCODING_PREFIX: &[u8; 19] = b"NSSA/v0.1/TxMessage"; + + let mut bytes = MESSAGE_ENCODING_PREFIX.to_vec(); + // program_id: [u32; 8] + for word in &self.program_id { + bytes.extend_from_slice(&word.to_le_bytes()); + } + // addresses: Vec<[u8;32]> + // serialize length as u32 little endian, then all addresses concatenated + let addresses_len = self.addresses.len() as u32; + bytes.extend(&addresses_len.to_le_bytes()); + for addr in &self.addresses { + bytes.extend_from_slice(addr.value()); + } + // nonces: Vec + let nonces_len = self.nonces.len() as u32; + bytes.extend(&nonces_len.to_le_bytes()); + for nonce in &self.nonces { + bytes.extend(&nonce.to_le_bytes()); + } + // instruction_data: Vec + // serialize length as u32 little endian, then all addresses concatenated + let instr_len = self.instruction_data.len() as u32; + bytes.extend(&instr_len.to_le_bytes()); + for word in &self.instruction_data { + bytes.extend(&word.to_le_bytes()); + } + + bytes + } } diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs index 64e051b..162257a 100644 --- a/nssa/src/public_transaction/mod.rs +++ b/nssa/src/public_transaction/mod.rs @@ -72,23 +72,20 @@ impl PublicTransaction { )); } - let mut authorized_addresses = Vec::new(); - for ((signature, public_key), nonce) in witness_set.iter_signatures().zip(&message.nonces) { - // Check the signature is valid - if !signature.is_valid_for(message, public_key) { - return Err(NssaError::InvalidInput( - "Invalid signature for given message and public key".into(), - )); - } + // Check the signatures are valid + if !witness_set.is_valid_for(message) { + return Err(NssaError::InvalidInput( + "Invalid signature for given message and public key".into(), + )); + } - // Check the nonce corresponds to the current nonce on the public state. - let address = Address::from_public_key(public_key); - let current_nonce = state.get_account_by_address(&address).nonce; + let signer_addresses = self.signer_addresses(); + // Check nonces corresponds to the current nonces on the public state. + for (address, nonce) in signer_addresses.iter().zip(&message.nonces) { + let current_nonce = state.get_account_by_address(address).nonce; if current_nonce != *nonce { return Err(NssaError::InvalidInput("Nonce mismatch".into())); } - - authorized_addresses.push(address); } // Build pre_states for execution @@ -97,7 +94,7 @@ impl PublicTransaction { .iter() .map(|address| AccountWithMetadata { account: state.get_account_by_address(address), - is_authorized: authorized_addresses.contains(address), + is_authorized: signer_addresses.contains(address), }) .collect(); diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs index 61e1683..05507e7 100644 --- a/nssa/src/public_transaction/witness_set.rs +++ b/nssa/src/public_transaction/witness_set.rs @@ -7,44 +7,9 @@ pub struct WitnessSet { pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>, } -const MESSAGE_ENCODING_PREFIX: &[u8; 19] = b"NSSA/v0.1/TxMessage"; - -/// Serializes a `Message` into bytes in the following layout: -/// TAG || (bytes LE) * 8 || addresses_len (4 bytes LE) || addresses (32 bytes * N) || nonces_len (4 bytes LE) || nonces (16 bytes * M) || instruction_data_len || instruction_data (4 bytes * K) -/// Integers and words are encoded in little-endian byte order, and fields appear in the above order. -fn message_to_bytes(message: &Message) -> Vec { - let mut bytes = MESSAGE_ENCODING_PREFIX.to_vec(); - // program_id: [u32; 8] - for word in &message.program_id { - bytes.extend_from_slice(&word.to_le_bytes()); - } - // addresses: Vec<[u8;32]> - // serialize length as u32 little endian, then all addresses concatenated - let addresses_len = message.addresses.len() as u32; - bytes.extend(&addresses_len.to_le_bytes()); - for addr in &message.addresses { - bytes.extend_from_slice(addr.value()); - } - // nonces: Vec - let nonces_len = message.nonces.len() as u32; - bytes.extend(&nonces_len.to_le_bytes()); - for nonce in &message.nonces { - bytes.extend(&nonce.to_le_bytes()); - } - // instruction_data: Vec - // serialize length as u32 little endian, then all addresses concatenated - let instr_len = message.instruction_data.len() as u32; - bytes.extend(&instr_len.to_le_bytes()); - for word in &message.instruction_data { - bytes.extend(&word.to_le_bytes()); - } - - bytes -} - impl WitnessSet { pub fn for_message(message: &Message, private_keys: &[&PrivateKey]) -> Self { - let message_bytes = message_to_bytes(message); + let message_bytes = message.message_to_bytes(); let signatures_and_public_keys = private_keys .iter() .map(|&key| (Signature::new(key, &message_bytes), PublicKey::new(key))) @@ -54,6 +19,16 @@ impl WitnessSet { } } + pub(crate) fn is_valid_for(&self, message: &Message) -> bool { + let message_bytes = message.message_to_bytes(); + for (signature, public_key) in self.iter_signatures() { + if !signature.is_valid_for(&message_bytes, &public_key) { + return false; + } + } + true + } + pub fn iter_signatures(&self) -> impl Iterator { self.signatures_and_public_keys.iter() } diff --git a/nssa/src/signature.rs b/nssa/src/signature.rs deleted file mode 100644 index 02fe7b3..0000000 --- a/nssa/src/signature.rs +++ /dev/null @@ -1,54 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use crate::{error::NssaError, public_transaction::Message}; - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct Signature(pub(crate) u8); - -// TODO: Dummy impl. Replace by actual private key. -// TODO: Remove Debug, Clone, Serialize, Deserialize, PartialEq and Eq for security reasons -// TODO: Implement Zeroize -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct PrivateKey(pub(crate) [u8; 32]); - -impl PrivateKey { - fn is_valid_key(value: [u8; 32]) -> bool { - secp256k1::SecretKey::from_byte_array(value).is_ok() - } - - pub fn try_new(value: [u8; 32]) -> Result { - if Self::is_valid_key(value) { - Ok(Self(value)) - } else { - Err(NssaError::InvalidPrivateKey) - } - } -} - -// TODO: Dummy impl. Replace by actual public key. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct PublicKey(pub(crate) [u8; 32]); - -impl PublicKey { - pub fn new(key: &PrivateKey) -> Self { - let value = { - let secret_key = secp256k1::SecretKey::from_byte_array(key.0).unwrap(); - let public_key = - secp256k1::PublicKey::from_secret_key(&secp256k1::Secp256k1::new(), &secret_key); - let (x_only, _) = public_key.x_only_public_key(); - x_only.serialize() - }; - Self(value) - } -} - -impl Signature { - pub(crate) fn new(key: &PrivateKey, _message: &[u8]) -> Self { - Signature(0) - } - - pub fn is_valid_for(&self, _message: &Message, _public_key: &PublicKey) -> bool { - // TODO: implement - true - } -} diff --git a/nssa/src/signature/mod.rs b/nssa/src/signature/mod.rs new file mode 100644 index 0000000..9539e45 --- /dev/null +++ b/nssa/src/signature/mod.rs @@ -0,0 +1,7 @@ +mod private_key; +mod public_key; +mod signature; + +pub use private_key::PrivateKey; +pub use public_key::PublicKey; +pub use signature::Signature; diff --git a/nssa/src/signature/private_key.rs b/nssa/src/signature/private_key.rs new file mode 100644 index 0000000..78a2ad5 --- /dev/null +++ b/nssa/src/signature/private_key.rs @@ -0,0 +1,25 @@ +use serde::{Deserialize, Serialize}; + +use crate::error::NssaError; + + +// TODO: Dummy impl. Replace by actual private key. +// TODO: Remove Debug, Clone, Serialize, Deserialize, PartialEq and Eq for security reasons +// TODO: Implement Zeroize +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct PrivateKey(pub(crate) [u8; 32]); + +impl PrivateKey { + fn is_valid_key(value: [u8; 32]) -> bool { + secp256k1::SecretKey::from_byte_array(value).is_ok() + } + + pub fn try_new(value: [u8; 32]) -> Result { + if Self::is_valid_key(value) { + Ok(Self(value)) + } else { + Err(NssaError::InvalidPrivateKey) + } + } +} + diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs new file mode 100644 index 0000000..1073cba --- /dev/null +++ b/nssa/src/signature/public_key.rs @@ -0,0 +1,21 @@ +use serde::{Deserialize, Serialize}; + +use crate::PrivateKey; + + +// TODO: Dummy impl. Replace by actual public key. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct PublicKey(pub(crate) [u8; 32]); + +impl PublicKey { + pub fn new(key: &PrivateKey) -> Self { + let value = { + let secret_key = secp256k1::SecretKey::from_byte_array(key.0).unwrap(); + let public_key = + secp256k1::PublicKey::from_secret_key(&secp256k1::Secp256k1::new(), &secret_key); + let (x_only, _) = public_key.x_only_public_key(); + x_only.serialize() + }; + Self(value) + } +} diff --git a/nssa/src/signature/signature.rs b/nssa/src/signature/signature.rs new file mode 100644 index 0000000..eb0fe24 --- /dev/null +++ b/nssa/src/signature/signature.rs @@ -0,0 +1,56 @@ +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::{PrivateKey, PublicKey, error::NssaError, public_transaction::Message}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Signature { + value: [u8; 64], +} + +impl Signature { + pub(crate) fn new(key: &PrivateKey, message: &[u8]) -> Self { + let value = { + let secp = secp256k1::Secp256k1::new(); + let secret_key = secp256k1::SecretKey::from_byte_array(key.0).unwrap(); + let keypair = secp256k1::Keypair::from_secret_key(&secp, &secret_key); + let signature = secp.sign_schnorr_no_aux_rand(message, &keypair); + signature.to_byte_array() + }; + Self { value } + } + + pub fn is_valid_for(&self, bytes: &[u8], public_key: &PublicKey) -> bool { + let pk = secp256k1::XOnlyPublicKey::from_byte_array(public_key.0).unwrap(); + let secp = secp256k1::Secp256k1::new(); + let sig = secp256k1::schnorr::Signature::from_byte_array(self.value); + secp.verify_schnorr(&sig, &bytes, &pk).is_ok() + } +} + +impl Serialize for Signature { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + // Serialize as a slice + serializer.serialize_bytes(&self.value) + } +} + +impl<'de> Deserialize<'de> for Signature { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let bytes: &[u8] = Deserialize::deserialize(deserializer)?; + if bytes.len() != 64 { + return Err(serde::de::Error::invalid_length( + bytes.len(), + &"expected 64 bytes", + )); + } + let mut value = [0u8; 64]; + value.copy_from_slice(bytes); + Ok(Signature { value }) + } +} diff --git a/nssa/src/tests/state_tests.rs b/nssa/src/tests/state_tests.rs index c0745f7..5acbed8 100644 --- a/nssa/src/tests/state_tests.rs +++ b/nssa/src/tests/state_tests.rs @@ -87,7 +87,7 @@ fn transition_from_authenticated_transfer_program_invocation_non_default_account #[test] fn transition_from_chained_authenticated_transfer_program_invocations() { - let key1 = PrivateKey::try_new([1; 32]).unwrap(); + let key1 = PrivateKey::try_new([8; 32]).unwrap(); let address1 = Address::from_public_key(&PublicKey::new(&key1)); let key2 = PrivateKey::try_new([2; 32]).unwrap(); let address2 = Address::from_public_key(&PublicKey::new(&key2)); From a5d0c27d6be3ba72956d431c0f46e02a7d860127 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 11 Aug 2025 20:38:29 -0300 Subject: [PATCH 34/58] fix --- accounts/src/key_management/mod.rs | 18 ++++++++++++------ common/src/test_utils.rs | 2 +- nssa/src/public_transaction/witness_set.rs | 2 +- sequencer_core/src/lib.rs | 10 +++------- sequencer_rpc/src/process.rs | 2 +- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/accounts/src/key_management/mod.rs b/accounts/src/key_management/mod.rs index c0d26fb..e8444e7 100644 --- a/accounts/src/key_management/mod.rs +++ b/accounts/src/key_management/mod.rs @@ -37,9 +37,12 @@ impl AddressKeyHolder { let nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key(); let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); - let pub_account_signing_key = { - let mut rng = OsRng; - nssa::PrivateKey::new(rng.gen()) + let mut rng = OsRng; + let pub_account_signing_key = loop { + match nssa::PrivateKey::try_new(rng.gen()) { + Ok(key) => break key, + Err(_) => continue, + } }; Self { @@ -330,9 +333,12 @@ mod tests { let nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key(); let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); - let pub_account_signing_key = { - let mut rng = OsRng; - nssa::PrivateKey::new(rng.gen()) + let mut rng = OsRng; + let pub_account_signing_key = loop { + match nssa::PrivateKey::try_new(rng.gen()) { + Ok(key) => break key, + Err(_) => continue, + } }; let public_key = nssa::PublicKey::new(&pub_account_signing_key); diff --git a/common/src/test_utils.rs b/common/src/test_utils.rs index 7a75088..173886f 100644 --- a/common/src/test_utils.rs +++ b/common/src/test_utils.rs @@ -37,7 +37,7 @@ pub fn produce_dummy_empty_transaction() -> nssa::PublicTransaction { let instruction_data = 0; let message = nssa::public_transaction::Message::try_new(program_id, addresses, nonces, instruction_data).unwrap(); - let private_key = nssa::PrivateKey::new(1); + let private_key = nssa::PrivateKey::try_new([1; 32]).unwrap(); let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[&private_key]); nssa::PublicTransaction::new(message, witness_set) } diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs index 05507e7..262f976 100644 --- a/nssa/src/public_transaction/witness_set.rs +++ b/nssa/src/public_transaction/witness_set.rs @@ -19,7 +19,7 @@ impl WitnessSet { } } - pub(crate) fn is_valid_for(&self, message: &Message) -> bool { + pub fn is_valid_for(&self, message: &Message) -> bool { let message_bytes = message.message_to_bytes(); for (signature, public_key) in self.iter_signatures() { if !signature.is_valid_for(&message_bytes, &public_key) { diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 505baa6..2e69bfe 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -62,11 +62,7 @@ impl SequencerCore { tx: nssa::PublicTransaction, ) -> Result { // Stateless checks here - if tx - .witness_set() - .iter_signatures() - .all(|(signature, public_key)| signature.is_valid_for(tx.message(), public_key)) - { + if tx.witness_set().is_valid_for(tx.message()) { Ok(tx) } else { Err(TransactionMalformationErrorKind::InvalidSignature) @@ -188,11 +184,11 @@ mod tests { } fn create_signing_key_for_account1() -> nssa::PrivateKey { - nssa::PrivateKey::new(1) + nssa::PrivateKey::try_new([1; 32]).unwrap() } fn create_signing_key_for_account2() -> nssa::PrivateKey { - nssa::PrivateKey::new(2) + nssa::PrivateKey::try_new([2; 32]).unwrap() } fn common_setup(sequencer: &mut SequencerCore) { diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 86cb373..ca284ed 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -261,7 +261,7 @@ mod tests { let mut sequencer_core = SequencerCore::start_from_config(config); let initial_accounts = sequencer_core.sequencer_config.initial_accounts.clone(); - let signing_key = nssa::PrivateKey::new(1); + let signing_key = nssa::PrivateKey::try_new([1; 32]).unwrap(); let balance_to_move = 10; let tx = common::test_utils::create_transaction_native_token_transfer( [1; 32], From bab11122512dcd9e8b400e8e62abfd31d27de150 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 12 Aug 2025 10:16:04 -0300 Subject: [PATCH 35/58] refactor encoding/decoding for public transactions without serde --- common/src/test_utils.rs | 2 +- nssa/Cargo.toml | 1 - nssa/src/public_transaction/message.rs | 54 +++++++++++++++++++- nssa/src/public_transaction/mod.rs | 59 ++++++++++++++++++++-- nssa/src/public_transaction/witness_set.rs | 35 ++++++++++++- nssa/src/signature/public_key.rs | 14 ++++- nssa/src/signature/signature.rs | 33 +++--------- sequencer_core/src/lib.rs | 23 +++++---- 8 files changed, 172 insertions(+), 49 deletions(-) diff --git a/common/src/test_utils.rs b/common/src/test_utils.rs index 173886f..bd3fadc 100644 --- a/common/src/test_utils.rs +++ b/common/src/test_utils.rs @@ -34,7 +34,7 @@ pub fn produce_dummy_empty_transaction() -> nssa::PublicTransaction { let program_id = nssa::program::Program::authenticated_transfer_program().id(); let addresses = vec![]; let nonces = vec![]; - let instruction_data = 0; + let instruction_data: u128 = 0; let message = nssa::public_transaction::Message::try_new(program_id, addresses, nonces, instruction_data).unwrap(); let private_key = nssa::PrivateKey::try_new([1; 32]).unwrap(); diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index 90bd34f..5788e30 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -9,7 +9,6 @@ risc0-zkvm = "2.3.1" nssa-core = { path = "core" } program-methods = { path = "program_methods" } serde = "1.0.219" -serde_cbor = "0.11.2" sha2 = "0.10.9" secp256k1 = "0.31.1" diff --git a/nssa/src/public_transaction/message.rs b/nssa/src/public_transaction/message.rs index aaa04ed..6ae02d5 100644 --- a/nssa/src/public_transaction/message.rs +++ b/nssa/src/public_transaction/message.rs @@ -1,3 +1,5 @@ +use std::io::{Cursor, Read}; + use nssa_core::{ account::Nonce, program::{InstructionData, ProgramId}, @@ -5,6 +7,8 @@ use nssa_core::{ use serde::{Deserialize, Serialize}; use crate::{Address, error::NssaError, program::Program}; +const MESSAGE_ENCODING_PREFIX_LEN: usize = 19; +const MESSAGE_ENCODING_PREFIX: &[u8; MESSAGE_ENCODING_PREFIX_LEN] = b"NSSA/v0.1/TxMessage"; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct Message { @@ -34,8 +38,6 @@ impl Message { /// TAG || (bytes LE) * 8 || addresses_len (4 bytes LE) || addresses (32 bytes * N) || nonces_len (4 bytes LE) || nonces (16 bytes * M) || instruction_data_len || instruction_data (4 bytes * K) /// Integers and words are encoded in little-endian byte order, and fields appear in the above order. pub(crate) fn message_to_bytes(&self) -> Vec { - const MESSAGE_ENCODING_PREFIX: &[u8; 19] = b"NSSA/v0.1/TxMessage"; - let mut bytes = MESSAGE_ENCODING_PREFIX.to_vec(); // program_id: [u32; 8] for word in &self.program_id { @@ -64,4 +66,52 @@ impl Message { bytes } + + pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { + let prefix = { + let mut this = [0u8; MESSAGE_ENCODING_PREFIX_LEN]; + cursor.read_exact(&mut this).unwrap(); + this + }; + assert_eq!(&prefix, MESSAGE_ENCODING_PREFIX); + let program_id: ProgramId = { + let mut this = [0u32; 8]; + for i in 0..8 { + this[i] = u32_from_cursor(cursor); + } + this + }; + let addresses_len = u32_from_cursor(cursor); + let mut addresses = Vec::with_capacity(addresses_len as usize); + for _ in 0..addresses_len { + let mut value = [0u8; 32]; + cursor.read_exact(&mut value).unwrap(); + addresses.push(Address::new(value)) + } + let nonces_len = u32_from_cursor(cursor); + let mut nonces = Vec::with_capacity(nonces_len as usize); + for _ in 0..nonces_len { + let mut buf = [0u8; 16]; + cursor.read_exact(&mut buf).unwrap(); + nonces.push(u128::from_le_bytes(buf)) + } + let instruction_data_len = u32_from_cursor(cursor); + let mut instruction_data = Vec::with_capacity(instruction_data_len as usize); + for _ in 0..instruction_data_len { + let word = u32_from_cursor(cursor); + instruction_data.push(word) + } + Self { + program_id, + addresses, + nonces, + instruction_data, + } + } +} + +fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> u32 { + let mut word_buf = [0u8; 4]; + cursor.read_exact(&mut word_buf).unwrap(); + u32::from_le_bytes(word_buf) } diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs index 162257a..269f7d5 100644 --- a/nssa/src/public_transaction/mod.rs +++ b/nssa/src/public_transaction/mod.rs @@ -1,10 +1,12 @@ -use std::collections::{HashMap, HashSet}; +use std::{ + collections::{HashMap, HashSet}, + io::Cursor, +}; use nssa_core::{ account::{Account, AccountWithMetadata}, program::validate_execution, }; -use serde::{Deserialize, Serialize}; use sha2::{Digest, digest::FixedOutput}; use crate::{V01State, address::Address, error::NssaError}; @@ -15,7 +17,7 @@ mod witness_set; pub use message::Message; pub use witness_set::WitnessSet; -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct PublicTransaction { message: Message, witness_set: WitnessSet, @@ -45,8 +47,24 @@ impl PublicTransaction { } } + fn to_bytes(&self) -> Vec { + let mut bytes = self.message.message_to_bytes(); + bytes.extend_from_slice(&self.witness_set.to_bytes()); + bytes + } + + fn from_bytes(bytes: &[u8]) -> Self { + let mut cursor = Cursor::new(bytes); + let message = Message::from_cursor(&mut cursor); + let witness_set = WitnessSet::from_cursor(&mut cursor); + Self { + message, + witness_set, + } + } + pub fn hash(&self) -> [u8; 32] { - let bytes = serde_cbor::to_vec(&self).unwrap(); + let bytes = self.to_bytes(); let mut hasher = sha2::Sha256::new(); hasher.update(&bytes); hasher.finalize_fixed().into() @@ -116,3 +134,36 @@ impl PublicTransaction { Ok(message.addresses.iter().cloned().zip(post_states).collect()) } } + +#[cfg(test)] +mod tests { + use crate::{ + Address, PrivateKey, PublicTransaction, PublicKey, + program::Program, + public_transaction::{Message, WitnessSet}, + }; + + #[test] + fn test() { + let key1 = PrivateKey::try_new([1; 32]).unwrap(); + let key2 = PrivateKey::try_new([2; 32]).unwrap(); + let addr1 = Address::from_public_key(&PublicKey::new(&key1)); + let addr2 = Address::from_public_key(&PublicKey::new(&key2)); + let nonces = vec![5, 99]; + let instruction = 1337; + let message = Message::try_new( + Program::authenticated_transfer_program().id(), + vec![addr1, addr2], + nonces, + instruction, + ) + .unwrap(); + + let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); + let tx = PublicTransaction::new(message, witness_set); + + let bytes = tx.to_bytes(); + let recov_tx = PublicTransaction::from_bytes(&bytes); + assert_eq!(tx, recov_tx); + } +} diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs index 262f976..4477d46 100644 --- a/nssa/src/public_transaction/witness_set.rs +++ b/nssa/src/public_transaction/witness_set.rs @@ -1,8 +1,10 @@ +use std::io::{Cursor, Read}; + use serde::{Deserialize, Serialize}; -use crate::{PrivateKey, PublicKey, Signature, public_transaction::Message}; +use crate::{PrivateKey, PublicKey, Signature, error::NssaError, public_transaction::Message}; -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct WitnessSet { pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>, } @@ -32,4 +34,33 @@ impl WitnessSet { pub fn iter_signatures(&self) -> impl Iterator { self.signatures_and_public_keys.iter() } + + pub(crate) fn to_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + let size = self.signatures_and_public_keys.len() as u32; + bytes.extend_from_slice(&size.to_le_bytes()); + for (signature, public_key) in &self.signatures_and_public_keys { + bytes.extend_from_slice(&signature.value); + bytes.extend_from_slice(&public_key.0); + } + bytes + } + + // TODO: remove unwraps and return Result + pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { + let num_signatures: u32 = { + let mut buf = [0u8; 4]; + cursor.read_exact(&mut buf).unwrap(); + u32::from_le_bytes(buf) + }; + let mut signatures_and_public_keys = Vec::with_capacity(num_signatures as usize); + for i in 0..num_signatures { + let signature = Signature::from_cursor(cursor); + let public_key = PublicKey::from_cursor(cursor); + signatures_and_public_keys.push((signature, public_key)) + } + Self { + signatures_and_public_keys, + } + } } diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index 1073cba..a040f04 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -1,10 +1,11 @@ +use std::io::{Cursor, Read}; + use serde::{Deserialize, Serialize}; use crate::PrivateKey; - // TODO: Dummy impl. Replace by actual public key. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct PublicKey(pub(crate) [u8; 32]); impl PublicKey { @@ -19,3 +20,12 @@ impl PublicKey { Self(value) } } + +impl PublicKey { + // TODO: remove unwraps and return Result + pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { + let mut value = [0u8; 32]; + cursor.read_exact(&mut value).unwrap(); + Self(value) + } +} diff --git a/nssa/src/signature/signature.rs b/nssa/src/signature/signature.rs index eb0fe24..2f859a4 100644 --- a/nssa/src/signature/signature.rs +++ b/nssa/src/signature/signature.rs @@ -1,10 +1,10 @@ -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::io::{Cursor, Read}; use crate::{PrivateKey, PublicKey, error::NssaError, public_transaction::Message}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Signature { - value: [u8; 64], + pub(crate) value: [u8; 64], } impl Signature { @@ -27,30 +27,11 @@ impl Signature { } } -impl Serialize for Signature { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - // Serialize as a slice - serializer.serialize_bytes(&self.value) - } -} - -impl<'de> Deserialize<'de> for Signature { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let bytes: &[u8] = Deserialize::deserialize(deserializer)?; - if bytes.len() != 64 { - return Err(serde::de::Error::invalid_length( - bytes.len(), - &"expected 64 bytes", - )); - } +impl Signature { + // TODO: remove unwraps and return Result + pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { let mut value = [0u8; 64]; - value.copy_from_slice(bytes); - Ok(Signature { value }) + cursor.read_exact(&mut value).unwrap(); + Self { value } } } diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 2e69bfe..ac59b63 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -102,10 +102,13 @@ impl SequencerCore { .mempool .pop_size(self.sequencer_config.max_num_tx_in_block); - let valid_transactions = transactions + println!("##"); + println!("{:?}", transactions.len()); + let valid_transactions: Vec<_> = transactions .into_iter() .filter_map(|tx| self.execute_check_transaction_on_state(tx).ok()) .collect(); + println!("{:?}", valid_transactions.len()); let prev_block_hash = self .store @@ -157,15 +160,13 @@ mod tests { fn setup_sequencer_config() -> SequencerConfig { let acc1_addr = vec![ - // 13, 150, 223, 204, 65, 64, 25, 56, 12, 157, 222, 12, 211, 220, 229, 170, 201, 15, 181, - // 68, 59, 248, 113, 16, 135, 65, 174, 175, 222, 85, 42, 215, - 1; 32 + 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, + 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, ]; let acc2_addr = vec![ - // 151, 72, 112, 233, 190, 141, 10, 192, 138, 168, 59, 63, 199, 167, 166, 134, 41, 29, - // 135, 50, 80, 138, 186, 152, 179, 96, 128, 243, 156, 44, 243, 100, - 2; 32 + 77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, + 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102, ]; let initial_acc1 = AccountInitialData { @@ -236,13 +237,13 @@ mod tests { #[test] fn test_start_different_intial_accounts_balances() { let acc1_addr = vec![ - 13, 150, 223, 204, 65, 64, 25, 56, 12, 157, 222, 12, 211, 220, 229, 170, 201, 15, 181, - 68, 59, 248, 113, 16, 135, 65, 174, 175, 222, 42, 42, 42, + 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, + 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, ]; let acc2_addr = vec![ - 151, 72, 112, 233, 190, 141, 10, 192, 138, 168, 59, 63, 199, 167, 166, 134, 41, 29, - 135, 50, 80, 138, 186, 152, 179, 96, 128, 243, 156, 42, 42, 42, + 77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, + 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102, ]; let initial_acc1 = AccountInitialData { From 82907f5d8fe93aeb83d64d96e3671ebdc5dc499c Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 12 Aug 2025 12:18:13 -0300 Subject: [PATCH 36/58] refactor all --- Cargo.toml | 1 + common/src/block.rs | 94 +++++++++++++++++-- common/src/rpc_primitives/requests.rs | 6 +- common/src/sequencer_client/json.rs | 2 +- common/src/sequencer_client/mod.rs | 2 +- common/src/test_utils.rs | 4 - nssa/src/public_transaction/mod.rs | 14 +-- sequencer_core/src/lib.rs | 1 - .../src/sequencer_store/block_store.rs | 3 +- sequencer_core/src/sequencer_store/mod.rs | 1 - sequencer_rpc/Cargo.toml | 1 + sequencer_rpc/src/process.rs | 58 ++++++------ storage/src/lib.rs | 13 +-- wallet/src/chain_storage/mod.rs | 8 +- 14 files changed, 138 insertions(+), 70 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ac01a24..4764b7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ light-poseidon = "0.3.0" ark-bn254 = "0.5.0" ark-ff = "0.5.0" tiny-keccak = { version = "2.0.2", features = ["keccak"] } +base64 = "0.22.1" rocksdb = { version = "0.21.0", default-features = false, features = [ "snappy", diff --git a/common/src/block.rs b/common/src/block.rs index 65514e2..974c230 100644 --- a/common/src/block.rs +++ b/common/src/block.rs @@ -1,36 +1,33 @@ +use std::io::{Cursor, Read}; + use rs_merkle::Hasher; -use serde::{Deserialize, Serialize}; use crate::merkle_tree_public::hasher::OwnHasher; use nssa; pub type BlockHash = [u8; 32]; -pub type Data = Vec; pub type BlockId = u64; -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Clone)] pub struct Block { pub block_id: BlockId, pub prev_block_id: BlockId, pub prev_block_hash: BlockHash, pub hash: BlockHash, pub transactions: Vec, - pub data: Data, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq)] pub struct HashableBlockData { pub block_id: BlockId, pub prev_block_id: BlockId, pub prev_block_hash: BlockHash, pub transactions: Vec, - pub data: Data, } impl From for Block { fn from(value: HashableBlockData) -> Self { - let data = serde_json::to_vec(&value).unwrap(); - + let data = value.to_bytes(); let hash = OwnHasher::hash(&data); Self { @@ -38,8 +35,87 @@ impl From for Block { prev_block_id: value.prev_block_id, hash, transactions: value.transactions, - data: value.data, prev_block_hash: value.prev_block_hash, } } } + +impl From for HashableBlockData { + fn from(value: Block) -> Self { + Self { + block_id: value.block_id, + prev_block_id: value.prev_block_id, + prev_block_hash: value.prev_block_hash, + transactions: value.transactions, + } + } +} + +impl HashableBlockData { + pub fn to_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.extend_from_slice(&self.block_id.to_le_bytes()); + bytes.extend_from_slice(&self.prev_block_id.to_le_bytes()); + bytes.extend_from_slice(&self.prev_block_hash); + let num_transactions: u32 = self.transactions.len() as u32; + bytes.extend_from_slice(&num_transactions.to_le_bytes()); + for tx in &self.transactions { + bytes.extend_from_slice(&tx.to_bytes()); + } + bytes + } + + pub fn from_bytes(data: &[u8]) -> Self { + let mut cursor = Cursor::new(data); + + let block_id = u64_from_cursor(&mut cursor); + let prev_block_id = u64_from_cursor(&mut cursor); + + let mut prev_block_hash = [0u8; 32]; + cursor.read_exact(&mut prev_block_hash).unwrap(); + + let num_transactions = u32_from_cursor(&mut cursor) as usize; + + let mut transactions = Vec::with_capacity(num_transactions); + for _ in 0..num_transactions { + let tx = nssa::PublicTransaction::from_cursor(&mut cursor); + transactions.push(tx); + } + + Self { + block_id, + prev_block_id, + prev_block_hash, + transactions, + } + } +} + +fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> u32 { + let mut word_buf = [0u8; 4]; + cursor.read_exact(&mut word_buf).unwrap(); + u32::from_le_bytes(word_buf) +} + +fn u64_from_cursor(cursor: &mut Cursor<&[u8]>) -> u64 { + let mut word_buf = [0u8; 8]; + cursor.read_exact(&mut word_buf).unwrap(); + u64::from_le_bytes(word_buf) +} + + + +#[cfg(test)] +mod tests { + use crate::{block::HashableBlockData, test_utils}; + + #[test] + fn test() { + let transactions = vec![test_utils::produce_dummy_empty_transaction()]; + let block = test_utils::produce_dummy_block(1, Some([1; 32]), transactions); + let hashable = HashableBlockData::from(block); + let bytes = hashable.to_bytes(); + let recov = HashableBlockData::from_bytes(&bytes); + assert_eq!(hashable, recov); + } +} diff --git a/common/src/rpc_primitives/requests.rs b/common/src/rpc_primitives/requests.rs index ebfad60..1b2bbbe 100644 --- a/common/src/rpc_primitives/requests.rs +++ b/common/src/rpc_primitives/requests.rs @@ -17,7 +17,7 @@ pub struct RegisterAccountRequest { #[derive(Serialize, Deserialize, Debug)] pub struct SendTxRequest { - pub transaction: nssa::PublicTransaction, + pub transaction: Vec } #[derive(Serialize, Deserialize, Debug)] @@ -71,7 +71,7 @@ pub struct SendTxResponse { #[derive(Serialize, Deserialize, Debug)] pub struct GetBlockDataResponse { - pub block: Block, + pub block: Vec, } #[derive(Serialize, Deserialize, Debug)] @@ -91,5 +91,5 @@ pub struct GetAccountBalanceResponse { #[derive(Serialize, Deserialize, Debug)] pub struct GetTransactionByHashResponse { - pub transaction: Option, + pub transaction: Option, } diff --git a/common/src/sequencer_client/json.rs b/common/src/sequencer_client/json.rs index 119442b..71b1719 100644 --- a/common/src/sequencer_client/json.rs +++ b/common/src/sequencer_client/json.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] pub struct SendTxRequest { - pub transaction: nssa::PublicTransaction, + pub transaction: Vec, } //Responses diff --git a/common/src/sequencer_client/mod.rs b/common/src/sequencer_client/mod.rs index 82f30a9..44f0e23 100644 --- a/common/src/sequencer_client/mod.rs +++ b/common/src/sequencer_client/mod.rs @@ -91,7 +91,7 @@ impl SequencerClient { &self, transaction: nssa::PublicTransaction, ) -> Result { - let tx_req = SendTxRequest { transaction }; + let tx_req = SendTxRequest { transaction: transaction.to_bytes() }; let req = serde_json::to_value(tx_req)?; diff --git a/common/src/test_utils.rs b/common/src/test_utils.rs index bd3fadc..d982a39 100644 --- a/common/src/test_utils.rs +++ b/common/src/test_utils.rs @@ -11,20 +11,16 @@ use crate::block::{Block, HashableBlockData}; /// `prev_hash` - hash of previous block, provide None for genesis /// /// `transactions` - vector of `Transaction` objects -/// -/// `additional_data` - vector with additional data pub fn produce_dummy_block( id: u64, prev_hash: Option<[u8; 32]>, transactions: Vec, - additional_data: Vec, ) -> Block { let block_data = HashableBlockData { block_id: id, prev_block_id: id.saturating_sub(1), prev_block_hash: prev_hash.unwrap_or_default(), transactions, - data: additional_data, }; block_data.into() diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs index 269f7d5..e91baba 100644 --- a/nssa/src/public_transaction/mod.rs +++ b/nssa/src/public_transaction/mod.rs @@ -47,16 +47,15 @@ impl PublicTransaction { } } - fn to_bytes(&self) -> Vec { + pub fn to_bytes(&self) -> Vec { let mut bytes = self.message.message_to_bytes(); bytes.extend_from_slice(&self.witness_set.to_bytes()); bytes } - fn from_bytes(bytes: &[u8]) -> Self { - let mut cursor = Cursor::new(bytes); - let message = Message::from_cursor(&mut cursor); - let witness_set = WitnessSet::from_cursor(&mut cursor); + pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { + let message = Message::from_cursor(cursor); + let witness_set = WitnessSet::from_cursor(cursor); Self { message, witness_set, @@ -137,6 +136,8 @@ impl PublicTransaction { #[cfg(test)] mod tests { + use std::io::Cursor; + use crate::{ Address, PrivateKey, PublicTransaction, PublicKey, program::Program, @@ -163,7 +164,8 @@ mod tests { let tx = PublicTransaction::new(message, witness_set); let bytes = tx.to_bytes(); - let recov_tx = PublicTransaction::from_bytes(&bytes); + let mut cursor: Cursor<&[u8]> = Cursor::new(&bytes); + let recov_tx = PublicTransaction::from_cursor(&mut cursor); assert_eq!(tx, recov_tx); } } diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index ac59b63..8430b83 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -120,7 +120,6 @@ impl SequencerCore { block_id: new_block_height, prev_block_id: self.chain_height, transactions: valid_transactions, - data: vec![], prev_block_hash, }; diff --git a/sequencer_core/src/sequencer_store/block_store.rs b/sequencer_core/src/sequencer_store/block_store.rs index da92b82..3bd68cb 100644 --- a/sequencer_core/src/sequencer_store/block_store.rs +++ b/sequencer_core/src/sequencer_store/block_store.rs @@ -89,14 +89,13 @@ mod tests { prev_block_hash: [0; 32], hash: [1; 32], transactions: vec![], - data: vec![], }; // Start an empty node store let mut node_store = SequecerBlockStore::open_db_with_genesis(path, Some(genesis_block)).unwrap(); let tx = common::test_utils::produce_dummy_empty_transaction(); - let block = common::test_utils::produce_dummy_block(1, None, vec![tx.clone()], vec![]); + let block = common::test_utils::produce_dummy_block(1, None, vec![tx.clone()]); // Try retrieve a tx that's not in the chain yet. let retrieved_tx = node_store.get_transaction_by_hash(tx.hash()); diff --git a/sequencer_core/src/sequencer_store/mod.rs b/sequencer_core/src/sequencer_store/mod.rs index e0a15fc..fd39473 100644 --- a/sequencer_core/src/sequencer_store/mod.rs +++ b/sequencer_core/src/sequencer_store/mod.rs @@ -49,7 +49,6 @@ impl SequecerChainStore { block_id: genesis_id, prev_block_id: genesis_id.saturating_sub(1), transactions: vec![], - data: data.to_vec(), prev_block_hash, }; diff --git a/sequencer_rpc/Cargo.toml b/sequencer_rpc/Cargo.toml index ceb43f7..ce61f7e 100644 --- a/sequencer_rpc/Cargo.toml +++ b/sequencer_rpc/Cargo.toml @@ -14,6 +14,7 @@ actix-cors.workspace = true futures.workspace = true hex.workspace = true tempfile.workspace = true +base64.workspace = true actix-web.workspace = true tokio.workspace = true diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index ca284ed..69c9c8d 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -1,9 +1,13 @@ +use std::io::Cursor; + use actix_web::Error as HttpError; +use base64::{engine::general_purpose, Engine}; use nssa; use sequencer_core::config::AccountInitialData; use serde_json::Value; use common::{ + block::HashableBlockData, merkle_tree_public::TreeHashType, rpc_primitives::{ errors::RpcError, @@ -68,11 +72,15 @@ impl JsonHandler { async fn process_send_tx(&self, request: Request) -> Result { let send_tx_req = SendTxRequest::parse(Some(request.params))?; + let tx = { + let mut cursor: Cursor<&[u8]> = Cursor::new(&send_tx_req.transaction); + nssa::PublicTransaction::from_cursor(&mut cursor) + }; { let mut state = self.sequencer_state.lock().await; - state.push_tx_into_mempool_pre_check(send_tx_req.transaction)?; + state.push_tx_into_mempool_pre_check(tx)?; } let helperstruct = SendTxResponse { @@ -94,7 +102,9 @@ impl JsonHandler { .get_block_at_id(get_block_req.block_id)? }; - let helperstruct = GetBlockDataResponse { block }; + let helperstruct = GetBlockDataResponse { + block: HashableBlockData::from(block).to_bytes(), + }; respond(helperstruct) } @@ -177,9 +187,16 @@ impl JsonHandler { let transaction = { let state = self.sequencer_state.lock().await; - state.store.block_store.get_transaction_by_hash(hash) + state + .store + .block_store + .get_transaction_by_hash(hash) + .map(|tx| tx.to_bytes()) + }; + let base64_encoded = transaction.map(|tx| general_purpose::STANDARD.encode(tx)); + let helperstruct = GetTransactionByHashResponse { + transaction: base64_encoded, }; - let helperstruct = GetTransactionByHashResponse { transaction }; respond(helperstruct) } @@ -217,15 +234,13 @@ mod tests { let tempdir = tempdir().unwrap(); let home = tempdir.path().to_path_buf(); let acc1_addr = vec![ - // 13, 150, 223, 204, 65, 64, 25, 56, 12, 157, 222, 12, 211, 220, 229, 170, 201, 15, 181, - // 68, 59, 248, 113, 16, 135, 65, 174, 175, 222, 85, 42, 215, - 1; 32 + 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, + 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, ]; let acc2_addr = vec![ - // 151, 72, 112, 233, 190, 141, 10, 192, 138, 168, 59, 63, 199, 167, 166, 134, 41, 29, - // 135, 50, 80, 138, 186, 152, 179, 96, 128, 243, 156, 44, 243, 100, - 2; 32 + 77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, + 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102, ]; let initial_acc1 = AccountInitialData { @@ -264,7 +279,10 @@ mod tests { let signing_key = nssa::PrivateKey::try_new([1; 32]).unwrap(); let balance_to_move = 10; let tx = common::test_utils::create_transaction_native_token_transfer( - [1; 32], + [ + 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, + 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, + ], 0, [2; 32], balance_to_move, @@ -490,25 +508,9 @@ mod tests { "id": 1, "jsonrpc": "2.0", "result": { - "transaction": { - "message": { - "addresses": [ - { "value": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, - { "value": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] } - ], - "instruction_data": [10, 0, 0, 0], - "nonces": [0], - "program_id": nssa::program::Program::authenticated_transfer_program().id(), - }, - "witness_set": { - "signatures_and_public_keys": [ - [1, 1] - ] - } - } + "transaction": "TlNTQS92MC4xL1R4TWVzc2FnZTYHPd+eRGuYF2kuC9CQp8t7bp1UuMIyqCp4yOzP4zCBAgAAABuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QePAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAKAAAAAAAAAAAAAAAAAAAAAQAAAKvsz0Lg2apthMPOsOhYarLVuZmMeUYDneKtr95L7Ia6C+Z3/dw3CAtb2wIa4/Ow5JwatpstOGwC9uS2mySzf9UbhMVWexJkQJldPtWqugVl1x4YNGBIGf+cF/Xp1d0Hjw==", } }); - let response = call_rpc_handler_with_json(json_handler, request).await; assert_eq!(response, expected_response); diff --git a/storage/src/lib.rs b/storage/src/lib.rs index ac54406..0dd12b7 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -1,6 +1,6 @@ use std::{path::Path, sync::Arc}; -use common::block::Block; +use common::block::{Block, HashableBlockData}; use error::DbError; use rocksdb::{ BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, Options, @@ -242,12 +242,7 @@ impl RocksDBIO { .put_cf( &cf_block, block.block_id.to_be_bytes(), - serde_json::to_vec(&block).map_err(|serr| { - DbError::serde_cast_message( - serr, - Some("Block Serialization failed".to_string()), - ) - })?, + HashableBlockData::from(block).to_bytes(), ) .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; Ok(()) @@ -261,9 +256,7 @@ impl RocksDBIO { .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; if let Some(data) = res { - Ok(serde_json::from_slice::(&data).map_err(|serr| { - DbError::serde_cast_message(serr, Some("Block Deserialization failed".to_string())) - })?) + Ok(HashableBlockData::from_bytes(&data).into()) } else { Err(DbError::db_interaction_error( "Block on this id not found".to_string(), diff --git a/wallet/src/chain_storage/mod.rs b/wallet/src/chain_storage/mod.rs index c117157..4467162 100644 --- a/wallet/src/chain_storage/mod.rs +++ b/wallet/src/chain_storage/mod.rs @@ -80,11 +80,11 @@ mod tests { fn create_initial_accounts() -> Vec { let initial_acc1 = serde_json::from_str(r#"{ - "address": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "address": [27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143], "balance": 100, "key_holder": { "nullifer_public_key": "03A340BECA9FAAB444CED0140681D72EA1318B5C611704FEE017DA9836B17DB718", - "pub_account_signing_key": 1, + "pub_account_signing_key": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], "top_secret_key_holder": { "secret_spending_key": "7BC46784DB1BC67825D8F029436846712BFDF9B5D79EA3AB11D39A52B9B229D4" }, @@ -98,11 +98,11 @@ mod tests { }"#).unwrap(); let initial_acc2 = serde_json::from_str(r#"{ - "address": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + "address": [77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102], "balance": 200, "key_holder": { "nullifer_public_key": "02172F50274DE67C4087C344F5D58E11DF761D90285B095060E0994FAA6BCDE271", - "pub_account_signing_key": 2, + "pub_account_signing_key": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], "top_secret_key_holder": { "secret_spending_key": "80A186737C8D38B4288A03F0F589957D9C040D79C19F3E0CC4BA80F8494E5179" }, From e8e565fe8c0cfdab53067a3868249411271e8bd8 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 12 Aug 2025 13:10:46 -0300 Subject: [PATCH 37/58] refactor encoding --- nssa/src/public_transaction/encoding.rs | 147 +++++++++++++++++++++ nssa/src/public_transaction/message.rs | 82 ------------ nssa/src/public_transaction/mod.rs | 17 +-- nssa/src/public_transaction/witness_set.rs | 28 ---- nssa/src/signature/encoding.rs | 22 +++ nssa/src/signature/mod.rs | 1 + nssa/src/signature/public_key.rs | 8 -- nssa/src/signature/signature.rs | 8 -- sequencer_rpc/src/process.rs | 5 +- 9 files changed, 173 insertions(+), 145 deletions(-) create mode 100644 nssa/src/public_transaction/encoding.rs create mode 100644 nssa/src/signature/encoding.rs diff --git a/nssa/src/public_transaction/encoding.rs b/nssa/src/public_transaction/encoding.rs new file mode 100644 index 0000000..01483a5 --- /dev/null +++ b/nssa/src/public_transaction/encoding.rs @@ -0,0 +1,147 @@ +use std::io::{Cursor, Read}; + +use nssa_core::program::ProgramId; + +use crate::{ + public_transaction::{Message, WitnessSet}, Address, PublicKey, PublicTransaction, Signature +}; + +const MESSAGE_ENCODING_PREFIX_LEN: usize = 19; +const MESSAGE_ENCODING_PREFIX: &[u8; MESSAGE_ENCODING_PREFIX_LEN] = b"NSSA/v0.1/TxMessage"; + +impl Message { + /// Serializes a `Message` into bytes in the following layout: + /// TAG || (bytes LE) * 8 || addresses_len (4 bytes LE) || addresses (32 bytes * N) || nonces_len (4 bytes LE) || nonces (16 bytes * M) || instruction_data_len || instruction_data (4 bytes * K) + /// Integers and words are encoded in little-endian byte order, and fields appear in the above order. + pub(crate) fn message_to_bytes(&self) -> Vec { + let mut bytes = MESSAGE_ENCODING_PREFIX.to_vec(); + // program_id: [u32; 8] + for word in &self.program_id { + bytes.extend_from_slice(&word.to_le_bytes()); + } + // addresses: Vec<[u8;32]> + // serialize length as u32 little endian, then all addresses concatenated + let addresses_len = self.addresses.len() as u32; + bytes.extend(&addresses_len.to_le_bytes()); + for addr in &self.addresses { + bytes.extend_from_slice(addr.value()); + } + // nonces: Vec + let nonces_len = self.nonces.len() as u32; + bytes.extend(&nonces_len.to_le_bytes()); + for nonce in &self.nonces { + bytes.extend(&nonce.to_le_bytes()); + } + // instruction_data: Vec + // serialize length as u32 little endian, then all addresses concatenated + let instr_len = self.instruction_data.len() as u32; + bytes.extend(&instr_len.to_le_bytes()); + for word in &self.instruction_data { + bytes.extend(&word.to_le_bytes()); + } + + bytes + } + + pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { + let prefix = { + let mut this = [0u8; MESSAGE_ENCODING_PREFIX_LEN]; + cursor.read_exact(&mut this).unwrap(); + this + }; + assert_eq!(&prefix, MESSAGE_ENCODING_PREFIX); + let program_id: ProgramId = { + let mut this = [0u32; 8]; + for i in 0..8 { + this[i] = u32_from_cursor(cursor); + } + this + }; + let addresses_len = u32_from_cursor(cursor); + let mut addresses = Vec::with_capacity(addresses_len as usize); + for _ in 0..addresses_len { + let mut value = [0u8; 32]; + cursor.read_exact(&mut value).unwrap(); + addresses.push(Address::new(value)) + } + let nonces_len = u32_from_cursor(cursor); + let mut nonces = Vec::with_capacity(nonces_len as usize); + for _ in 0..nonces_len { + let mut buf = [0u8; 16]; + cursor.read_exact(&mut buf).unwrap(); + nonces.push(u128::from_le_bytes(buf)) + } + let instruction_data_len = u32_from_cursor(cursor); + let mut instruction_data = Vec::with_capacity(instruction_data_len as usize); + for _ in 0..instruction_data_len { + let word = u32_from_cursor(cursor); + instruction_data.push(word) + } + Self { + program_id, + addresses, + nonces, + instruction_data, + } + } +} + + +impl WitnessSet { + pub(crate) fn to_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + let size = self.signatures_and_public_keys.len() as u32; + bytes.extend_from_slice(&size.to_le_bytes()); + for (signature, public_key) in &self.signatures_and_public_keys { + bytes.extend_from_slice(&signature.value); + bytes.extend_from_slice(&public_key.0); + } + bytes + } + + // TODO: remove unwraps and return Result + pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { + let num_signatures: u32 = { + let mut buf = [0u8; 4]; + cursor.read_exact(&mut buf).unwrap(); + u32::from_le_bytes(buf) + }; + let mut signatures_and_public_keys = Vec::with_capacity(num_signatures as usize); + for i in 0..num_signatures { + let signature = Signature::from_cursor(cursor); + let public_key = PublicKey::from_cursor(cursor); + signatures_and_public_keys.push((signature, public_key)) + } + Self { + signatures_and_public_keys, + } + } +} + +impl PublicTransaction { + pub fn to_bytes(&self) -> Vec { + let mut bytes = self.message.message_to_bytes(); + bytes.extend_from_slice(&self.witness_set.to_bytes()); + bytes + } + + pub fn from_bytes(bytes: &[u8]) -> Self { + let mut cursor = Cursor::new(bytes); + Self::from_cursor(&mut cursor) + } + + pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { + let message = Message::from_cursor(cursor); + let witness_set = WitnessSet::from_cursor(cursor); + Self { + message, + witness_set, + } + } +} + +fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> u32 { + let mut word_buf = [0u8; 4]; + cursor.read_exact(&mut word_buf).unwrap(); + u32::from_le_bytes(word_buf) +} diff --git a/nssa/src/public_transaction/message.rs b/nssa/src/public_transaction/message.rs index 6ae02d5..4b4e72a 100644 --- a/nssa/src/public_transaction/message.rs +++ b/nssa/src/public_transaction/message.rs @@ -7,8 +7,6 @@ use nssa_core::{ use serde::{Deserialize, Serialize}; use crate::{Address, error::NssaError, program::Program}; -const MESSAGE_ENCODING_PREFIX_LEN: usize = 19; -const MESSAGE_ENCODING_PREFIX: &[u8; MESSAGE_ENCODING_PREFIX_LEN] = b"NSSA/v0.1/TxMessage"; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct Message { @@ -33,85 +31,5 @@ impl Message { instruction_data, }) } - - /// Serializes a `Message` into bytes in the following layout: - /// TAG || (bytes LE) * 8 || addresses_len (4 bytes LE) || addresses (32 bytes * N) || nonces_len (4 bytes LE) || nonces (16 bytes * M) || instruction_data_len || instruction_data (4 bytes * K) - /// Integers and words are encoded in little-endian byte order, and fields appear in the above order. - pub(crate) fn message_to_bytes(&self) -> Vec { - let mut bytes = MESSAGE_ENCODING_PREFIX.to_vec(); - // program_id: [u32; 8] - for word in &self.program_id { - bytes.extend_from_slice(&word.to_le_bytes()); - } - // addresses: Vec<[u8;32]> - // serialize length as u32 little endian, then all addresses concatenated - let addresses_len = self.addresses.len() as u32; - bytes.extend(&addresses_len.to_le_bytes()); - for addr in &self.addresses { - bytes.extend_from_slice(addr.value()); - } - // nonces: Vec - let nonces_len = self.nonces.len() as u32; - bytes.extend(&nonces_len.to_le_bytes()); - for nonce in &self.nonces { - bytes.extend(&nonce.to_le_bytes()); - } - // instruction_data: Vec - // serialize length as u32 little endian, then all addresses concatenated - let instr_len = self.instruction_data.len() as u32; - bytes.extend(&instr_len.to_le_bytes()); - for word in &self.instruction_data { - bytes.extend(&word.to_le_bytes()); - } - - bytes - } - - pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { - let prefix = { - let mut this = [0u8; MESSAGE_ENCODING_PREFIX_LEN]; - cursor.read_exact(&mut this).unwrap(); - this - }; - assert_eq!(&prefix, MESSAGE_ENCODING_PREFIX); - let program_id: ProgramId = { - let mut this = [0u32; 8]; - for i in 0..8 { - this[i] = u32_from_cursor(cursor); - } - this - }; - let addresses_len = u32_from_cursor(cursor); - let mut addresses = Vec::with_capacity(addresses_len as usize); - for _ in 0..addresses_len { - let mut value = [0u8; 32]; - cursor.read_exact(&mut value).unwrap(); - addresses.push(Address::new(value)) - } - let nonces_len = u32_from_cursor(cursor); - let mut nonces = Vec::with_capacity(nonces_len as usize); - for _ in 0..nonces_len { - let mut buf = [0u8; 16]; - cursor.read_exact(&mut buf).unwrap(); - nonces.push(u128::from_le_bytes(buf)) - } - let instruction_data_len = u32_from_cursor(cursor); - let mut instruction_data = Vec::with_capacity(instruction_data_len as usize); - for _ in 0..instruction_data_len { - let word = u32_from_cursor(cursor); - instruction_data.push(word) - } - Self { - program_id, - addresses, - nonces, - instruction_data, - } - } } -fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> u32 { - let mut word_buf = [0u8; 4]; - cursor.read_exact(&mut word_buf).unwrap(); - u32::from_le_bytes(word_buf) -} diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs index e91baba..6111bae 100644 --- a/nssa/src/public_transaction/mod.rs +++ b/nssa/src/public_transaction/mod.rs @@ -11,6 +11,7 @@ use sha2::{Digest, digest::FixedOutput}; use crate::{V01State, address::Address, error::NssaError}; +mod encoding; mod message; mod witness_set; @@ -47,20 +48,6 @@ impl PublicTransaction { } } - pub fn to_bytes(&self) -> Vec { - let mut bytes = self.message.message_to_bytes(); - bytes.extend_from_slice(&self.witness_set.to_bytes()); - bytes - } - - pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { - let message = Message::from_cursor(cursor); - let witness_set = WitnessSet::from_cursor(cursor); - Self { - message, - witness_set, - } - } pub fn hash(&self) -> [u8; 32] { let bytes = self.to_bytes(); @@ -145,7 +132,7 @@ mod tests { }; #[test] - fn test() { + fn test_to_bytes() { let key1 = PrivateKey::try_new([1; 32]).unwrap(); let key2 = PrivateKey::try_new([2; 32]).unwrap(); let addr1 = Address::from_public_key(&PublicKey::new(&key1)); diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs index 4477d46..7d8b5bc 100644 --- a/nssa/src/public_transaction/witness_set.rs +++ b/nssa/src/public_transaction/witness_set.rs @@ -35,32 +35,4 @@ impl WitnessSet { self.signatures_and_public_keys.iter() } - pub(crate) fn to_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - let size = self.signatures_and_public_keys.len() as u32; - bytes.extend_from_slice(&size.to_le_bytes()); - for (signature, public_key) in &self.signatures_and_public_keys { - bytes.extend_from_slice(&signature.value); - bytes.extend_from_slice(&public_key.0); - } - bytes - } - - // TODO: remove unwraps and return Result - pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { - let num_signatures: u32 = { - let mut buf = [0u8; 4]; - cursor.read_exact(&mut buf).unwrap(); - u32::from_le_bytes(buf) - }; - let mut signatures_and_public_keys = Vec::with_capacity(num_signatures as usize); - for i in 0..num_signatures { - let signature = Signature::from_cursor(cursor); - let public_key = PublicKey::from_cursor(cursor); - signatures_and_public_keys.push((signature, public_key)) - } - Self { - signatures_and_public_keys, - } - } } diff --git a/nssa/src/signature/encoding.rs b/nssa/src/signature/encoding.rs new file mode 100644 index 0000000..005cea9 --- /dev/null +++ b/nssa/src/signature/encoding.rs @@ -0,0 +1,22 @@ +use std::io::{Cursor, Read}; + +use crate::{PublicKey, Signature}; + + +impl PublicKey { + // TODO: remove unwraps and return Result + pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { + let mut value = [0u8; 32]; + cursor.read_exact(&mut value).unwrap(); + Self(value) + } +} + +impl Signature { + // TODO: remove unwraps and return Result + pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { + let mut value = [0u8; 64]; + cursor.read_exact(&mut value).unwrap(); + Self { value } + } +} diff --git a/nssa/src/signature/mod.rs b/nssa/src/signature/mod.rs index 9539e45..13a0fe3 100644 --- a/nssa/src/signature/mod.rs +++ b/nssa/src/signature/mod.rs @@ -1,6 +1,7 @@ mod private_key; mod public_key; mod signature; +mod encoding; pub use private_key::PrivateKey; pub use public_key::PublicKey; diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index a040f04..f7a8c25 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -21,11 +21,3 @@ impl PublicKey { } } -impl PublicKey { - // TODO: remove unwraps and return Result - pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { - let mut value = [0u8; 32]; - cursor.read_exact(&mut value).unwrap(); - Self(value) - } -} diff --git a/nssa/src/signature/signature.rs b/nssa/src/signature/signature.rs index 2f859a4..8e4756d 100644 --- a/nssa/src/signature/signature.rs +++ b/nssa/src/signature/signature.rs @@ -27,11 +27,3 @@ impl Signature { } } -impl Signature { - // TODO: remove unwraps and return Result - pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { - let mut value = [0u8; 64]; - cursor.read_exact(&mut value).unwrap(); - Self { value } - } -} diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 69c9c8d..8a880db 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -72,10 +72,7 @@ impl JsonHandler { async fn process_send_tx(&self, request: Request) -> Result { let send_tx_req = SendTxRequest::parse(Some(request.params))?; - let tx = { - let mut cursor: Cursor<&[u8]> = Cursor::new(&send_tx_req.transaction); - nssa::PublicTransaction::from_cursor(&mut cursor) - }; + let tx = nssa::PublicTransaction::from_bytes(&send_tx_req.transaction); { let mut state = self.sequencer_state.lock().await; From 927254f6ce061ff2ce10dbad65234721d600b9a1 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 12 Aug 2025 15:20:35 -0300 Subject: [PATCH 38/58] add bip340 test vectors for public keys --- nssa/Cargo.toml | 1 + nssa/src/public_transaction/encoding.rs | 11 +++--- nssa/src/public_transaction/witness_set.rs | 4 +- nssa/src/signature/encoding.rs | 8 ++++ nssa/src/signature/public_key.rs | 44 ++++++++++++++++++++++ 5 files changed, 61 insertions(+), 7 deletions(-) diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index 5788e30..7e1a1ad 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -14,3 +14,4 @@ secp256k1 = "0.31.1" [dev-dependencies] test-program-methods = { path = "test_program_methods" } +hex = "0.4.3" diff --git a/nssa/src/public_transaction/encoding.rs b/nssa/src/public_transaction/encoding.rs index 01483a5..eb7fbfc 100644 --- a/nssa/src/public_transaction/encoding.rs +++ b/nssa/src/public_transaction/encoding.rs @@ -11,9 +11,9 @@ const MESSAGE_ENCODING_PREFIX: &[u8; MESSAGE_ENCODING_PREFIX_LEN] = b"NSSA/v0.1/ impl Message { /// Serializes a `Message` into bytes in the following layout: - /// TAG || (bytes LE) * 8 || addresses_len (4 bytes LE) || addresses (32 bytes * N) || nonces_len (4 bytes LE) || nonces (16 bytes * M) || instruction_data_len || instruction_data (4 bytes * K) + /// PREFIX || (4 bytes LE) * 8 || addresses_len (4 bytes LE) || addresses (32 bytes * N) || nonces_len (4 bytes LE) || nonces (16 bytes LE * M) || instruction_data_len || instruction_data (4 bytes LE * K) /// Integers and words are encoded in little-endian byte order, and fields appear in the above order. - pub(crate) fn message_to_bytes(&self) -> Vec { + pub(crate) fn to_bytes(&self) -> Vec { let mut bytes = MESSAGE_ENCODING_PREFIX.to_vec(); // program_id: [u32; 8] for word in &self.program_id { @@ -27,6 +27,7 @@ impl Message { bytes.extend_from_slice(addr.value()); } // nonces: Vec + // serialize length as u32 little endian, then all nonces concatenated in LE let nonces_len = self.nonces.len() as u32; bytes.extend(&nonces_len.to_le_bytes()); for nonce in &self.nonces { @@ -93,8 +94,8 @@ impl WitnessSet { let size = self.signatures_and_public_keys.len() as u32; bytes.extend_from_slice(&size.to_le_bytes()); for (signature, public_key) in &self.signatures_and_public_keys { - bytes.extend_from_slice(&signature.value); - bytes.extend_from_slice(&public_key.0); + bytes.extend_from_slice(signature.to_bytes()); + bytes.extend_from_slice(public_key.to_bytes()); } bytes } @@ -120,7 +121,7 @@ impl WitnessSet { impl PublicTransaction { pub fn to_bytes(&self) -> Vec { - let mut bytes = self.message.message_to_bytes(); + let mut bytes = self.message.to_bytes(); bytes.extend_from_slice(&self.witness_set.to_bytes()); bytes } diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs index 7d8b5bc..83edf66 100644 --- a/nssa/src/public_transaction/witness_set.rs +++ b/nssa/src/public_transaction/witness_set.rs @@ -11,7 +11,7 @@ pub struct WitnessSet { impl WitnessSet { pub fn for_message(message: &Message, private_keys: &[&PrivateKey]) -> Self { - let message_bytes = message.message_to_bytes(); + let message_bytes = message.to_bytes(); let signatures_and_public_keys = private_keys .iter() .map(|&key| (Signature::new(key, &message_bytes), PublicKey::new(key))) @@ -22,7 +22,7 @@ impl WitnessSet { } pub fn is_valid_for(&self, message: &Message) -> bool { - let message_bytes = message.message_to_bytes(); + let message_bytes = message.to_bytes(); for (signature, public_key) in self.iter_signatures() { if !signature.is_valid_for(&message_bytes, &public_key) { return false; diff --git a/nssa/src/signature/encoding.rs b/nssa/src/signature/encoding.rs index 005cea9..a7f112e 100644 --- a/nssa/src/signature/encoding.rs +++ b/nssa/src/signature/encoding.rs @@ -10,6 +10,10 @@ impl PublicKey { cursor.read_exact(&mut value).unwrap(); Self(value) } + + pub(crate) fn to_bytes(&self) -> &[u8] { + &self.0 + } } impl Signature { @@ -19,4 +23,8 @@ impl Signature { cursor.read_exact(&mut value).unwrap(); Self { value } } + + pub(crate) fn to_bytes(&self) -> &[u8] { + &self.value + } } diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index f7a8c25..830f6b1 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -21,3 +21,47 @@ impl PublicKey { } } +#[cfg(test)] +mod tests { + use crate::{PrivateKey, PublicKey}; + fn hex_to_32_bytes(hex: &str) -> [u8; 32] { + hex::decode(hex).unwrap().try_into().unwrap() + } + + /// Test vectors from + /// https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv + const BIP340_PK_TEST_VECTORS: [(&str, &str); 5] = [ + ( + "0000000000000000000000000000000000000000000000000000000000000003", + "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + ), + ( + "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF", + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + ), + ( + "C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9", + "DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", + ), + ( + "0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710", + "25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", + ), + ( + "0340034003400340034003400340034003400340034003400340034003400340", + "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", + ), + ]; + + #[test] + fn test_bip340_pk_test_vectors() { + for (i, (private_key_hex, expected_public_key_hex)) in + BIP340_PK_TEST_VECTORS.iter().enumerate() + { + let key = PrivateKey::try_new(hex_to_32_bytes(private_key_hex)).unwrap(); + let public_key = PublicKey::new(&key); + let expected_public_key = PublicKey(hex_to_32_bytes(expected_public_key_hex)); + assert_eq!(public_key, expected_public_key, "Failed test vector at index {i}"); + } + } +} From 066aa8f3f7a2dcd55555b24832d5ba89f490efc7 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 12 Aug 2025 16:45:07 -0300 Subject: [PATCH 39/58] add test vectors --- nssa/Cargo.toml | 1 + nssa/src/signature/mod.rs | 70 +++++++++++++++++++++++++++++++- nssa/src/signature/public_key.rs | 50 ++++++----------------- nssa/src/signature/signature.rs | 51 ++++++++++++++++++++++- 4 files changed, 133 insertions(+), 39 deletions(-) diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index 7e1a1ad..fef52ef 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -11,6 +11,7 @@ program-methods = { path = "program_methods" } serde = "1.0.219" sha2 = "0.10.9" secp256k1 = "0.31.1" +rand = "0.8" [dev-dependencies] test-program-methods = { path = "test_program_methods" } diff --git a/nssa/src/signature/mod.rs b/nssa/src/signature/mod.rs index 13a0fe3..1204ed5 100644 --- a/nssa/src/signature/mod.rs +++ b/nssa/src/signature/mod.rs @@ -1,8 +1,76 @@ +mod encoding; mod private_key; mod public_key; mod signature; -mod encoding; pub use private_key::PrivateKey; pub use public_key::PublicKey; pub use signature::Signature; + +#[cfg(test)] +mod tests { + use crate::{PrivateKey, PublicKey, Signature}; + + fn hex_to_bytes(hex: &str) -> [u8; N] { + hex::decode(hex).unwrap().try_into().unwrap() + } + + pub struct TestVector { + pub seckey: Option, + pub pubkey: PublicKey, + pub aux_rand: Option<[u8; 32]>, + pub message: Option>, + pub signature: Signature, + pub verification_result: bool, + } + + /// Test vectors from + /// https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv + // + pub fn test_vectors() -> Vec { + vec![ + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "0000000000000000000000000000000000000000000000000000000000000003", + ))), + pubkey: PublicKey(hex_to_bytes( + "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + )), + aux_rand: Some(hex_to_bytes::<32>( + "0000000000000000000000000000000000000000000000000000000000000000", + )), + message: Some( + hex::decode("0000000000000000000000000000000000000000000000000000000000000000") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0", + ), + }, + verification_result: true, + }, + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF", + ))), + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: Some(hex_to_bytes::<32>( + "0000000000000000000000000000000000000000000000000000000000000001", + )), + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A", + ), + }, + verification_result: true, + }, + ] + } +} diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index 830f6b1..0ba4b44 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -23,45 +23,21 @@ impl PublicKey { #[cfg(test)] mod tests { - use crate::{PrivateKey, PublicKey}; - fn hex_to_32_bytes(hex: &str) -> [u8; 32] { - hex::decode(hex).unwrap().try_into().unwrap() - } - - /// Test vectors from - /// https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv - const BIP340_PK_TEST_VECTORS: [(&str, &str); 5] = [ - ( - "0000000000000000000000000000000000000000000000000000000000000003", - "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", - ), - ( - "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF", - "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - ), - ( - "C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9", - "DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", - ), - ( - "0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710", - "25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", - ), - ( - "0340034003400340034003400340034003400340034003400340034003400340", - "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", - ), - ]; + use crate::{PrivateKey, PublicKey, signature::tests::test_vectors}; #[test] - fn test_bip340_pk_test_vectors() { - for (i, (private_key_hex, expected_public_key_hex)) in - BIP340_PK_TEST_VECTORS.iter().enumerate() - { - let key = PrivateKey::try_new(hex_to_32_bytes(private_key_hex)).unwrap(); - let public_key = PublicKey::new(&key); - let expected_public_key = PublicKey(hex_to_32_bytes(expected_public_key_hex)); - assert_eq!(public_key, expected_public_key, "Failed test vector at index {i}"); + fn test_public_key_generation_from_bip340_test_vectors() { + for (i, test_vector) in test_vectors().iter().enumerate() { + let Some(private_key) = &test_vector.seckey else { + continue; + }; + let public_key = PublicKey::new(private_key); + let expected_public_key = &test_vector.pubkey; + assert_eq!( + &public_key, expected_public_key, + "Failed test vector at index {i}" + ); } } + } diff --git a/nssa/src/signature/signature.rs b/nssa/src/signature/signature.rs index 8e4756d..0f956af 100644 --- a/nssa/src/signature/signature.rs +++ b/nssa/src/signature/signature.rs @@ -1,5 +1,7 @@ use std::io::{Cursor, Read}; +use rand::{rngs::OsRng, RngCore}; + use crate::{PrivateKey, PublicKey, error::NssaError, public_transaction::Message}; #[derive(Debug, Clone, PartialEq, Eq)] @@ -9,11 +11,17 @@ pub struct Signature { impl Signature { pub(crate) fn new(key: &PrivateKey, message: &[u8]) -> Self { + let mut aux_random = [0u8; 32]; + OsRng.fill_bytes(&mut aux_random); + Self::new_with_aux_random(key, message, aux_random) + } + + fn new_with_aux_random(key: &PrivateKey, message: &[u8], aux_random: [u8; 32]) -> Self { let value = { let secp = secp256k1::Secp256k1::new(); let secret_key = secp256k1::SecretKey::from_byte_array(key.0).unwrap(); let keypair = secp256k1::Keypair::from_secret_key(&secp, &secret_key); - let signature = secp.sign_schnorr_no_aux_rand(message, &keypair); + let signature = secp.sign_schnorr_with_aux_rand(message, &keypair, &aux_random); signature.to_byte_array() }; Self { value } @@ -27,3 +35,44 @@ impl Signature { } } +#[cfg(test)] +mod tests { + use crate::{Signature, signature::tests::test_vectors}; + + #[test] + fn test_signature_generation_from_bip340_test_vectors() { + for (i, test_vector) in test_vectors().into_iter().enumerate() { + let Some(private_key) = test_vector.seckey else { + continue; + }; + let Some(aux_random) = test_vector.aux_rand else { + continue; + }; + let Some(message) = test_vector.message else { + continue; + }; + if !test_vector.verification_result { + continue; + } + let expected_signature = &test_vector.signature; + + let signature = Signature::new_with_aux_random(&private_key, &message, aux_random); + + assert_eq!(&signature, expected_signature, "Failed test vector {i}"); + } + } + + #[test] + fn test_signature_verification_from_bip340_test_vectors() { + for (i, test_vector) in test_vectors().into_iter().enumerate() { + let message = test_vector.message.unwrap_or(vec![]); + let expected_result = test_vector.verification_result; + + let result = test_vector + .signature + .is_valid_for(&message, &test_vector.pubkey); + + assert_eq!(result, expected_result, "Failed test vector {i}"); + } + } +} From 82818db87306c485565721e5da8b251225451806 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 12 Aug 2025 16:48:32 -0300 Subject: [PATCH 40/58] fix test --- sequencer_rpc/src/process.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 8a880db..d308659 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -217,6 +217,7 @@ mod tests { use std::sync::Arc; use crate::{rpc_handler, JsonHandler}; + use base64::{engine::general_purpose, Engine}; use common::rpc_primitives::RpcPollingConfig; use sequencer_core::{ @@ -494,6 +495,8 @@ mod tests { async fn test_get_transaction_by_hash_for_existing_transaction() { let (json_handler, _, tx) = components_for_tests(); let tx_hash_hex = hex::encode(tx.hash()); + let expected_base64_encoded = general_purpose::STANDARD.encode(tx.to_bytes()); + let request = serde_json::json!({ "jsonrpc": "2.0", "method": "get_transaction_by_hash", @@ -505,7 +508,7 @@ mod tests { "id": 1, "jsonrpc": "2.0", "result": { - "transaction": "TlNTQS92MC4xL1R4TWVzc2FnZTYHPd+eRGuYF2kuC9CQp8t7bp1UuMIyqCp4yOzP4zCBAgAAABuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QePAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAKAAAAAAAAAAAAAAAAAAAAAQAAAKvsz0Lg2apthMPOsOhYarLVuZmMeUYDneKtr95L7Ia6C+Z3/dw3CAtb2wIa4/Ow5JwatpstOGwC9uS2mySzf9UbhMVWexJkQJldPtWqugVl1x4YNGBIGf+cF/Xp1d0Hjw==", + "transaction": expected_base64_encoded, } }); let response = call_rpc_handler_with_json(json_handler, request).await; From 2884fdd00ae0908af6e0683982bc7cc7258d7288 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 12 Aug 2025 16:48:53 -0300 Subject: [PATCH 41/58] fmt --- common/src/block.rs | 2 -- common/src/rpc_primitives/requests.rs | 2 +- common/src/sequencer_client/mod.rs | 4 +++- common/src/test_utils.rs | 6 ++++-- nssa/src/public_transaction/encoding.rs | 4 ++-- nssa/src/public_transaction/message.rs | 1 - nssa/src/public_transaction/mod.rs | 3 +-- nssa/src/public_transaction/witness_set.rs | 1 - nssa/src/signature/encoding.rs | 1 - nssa/src/signature/private_key.rs | 2 -- nssa/src/signature/public_key.rs | 1 - nssa/src/signature/signature.rs | 2 +- wallet/src/lib.rs | 3 ++- 13 files changed, 14 insertions(+), 18 deletions(-) diff --git a/common/src/block.rs b/common/src/block.rs index 974c230..3d39e58 100644 --- a/common/src/block.rs +++ b/common/src/block.rs @@ -103,8 +103,6 @@ fn u64_from_cursor(cursor: &mut Cursor<&[u8]>) -> u64 { u64::from_le_bytes(word_buf) } - - #[cfg(test)] mod tests { use crate::{block::HashableBlockData, test_utils}; diff --git a/common/src/rpc_primitives/requests.rs b/common/src/rpc_primitives/requests.rs index 1b2bbbe..085051b 100644 --- a/common/src/rpc_primitives/requests.rs +++ b/common/src/rpc_primitives/requests.rs @@ -17,7 +17,7 @@ pub struct RegisterAccountRequest { #[derive(Serialize, Deserialize, Debug)] pub struct SendTxRequest { - pub transaction: Vec + pub transaction: Vec, } #[derive(Serialize, Deserialize, Debug)] diff --git a/common/src/sequencer_client/mod.rs b/common/src/sequencer_client/mod.rs index 44f0e23..a131033 100644 --- a/common/src/sequencer_client/mod.rs +++ b/common/src/sequencer_client/mod.rs @@ -91,7 +91,9 @@ impl SequencerClient { &self, transaction: nssa::PublicTransaction, ) -> Result { - let tx_req = SendTxRequest { transaction: transaction.to_bytes() }; + let tx_req = SendTxRequest { + transaction: transaction.to_bytes(), + }; let req = serde_json::to_value(tx_req)?; diff --git a/common/src/test_utils.rs b/common/src/test_utils.rs index d982a39..b956cd7 100644 --- a/common/src/test_utils.rs +++ b/common/src/test_utils.rs @@ -32,7 +32,8 @@ pub fn produce_dummy_empty_transaction() -> nssa::PublicTransaction { let nonces = vec![]; let instruction_data: u128 = 0; let message = - nssa::public_transaction::Message::try_new(program_id, addresses, nonces, instruction_data).unwrap(); + nssa::public_transaction::Message::try_new(program_id, addresses, nonces, instruction_data) + .unwrap(); let private_key = nssa::PrivateKey::try_new([1; 32]).unwrap(); let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[&private_key]); nssa::PublicTransaction::new(message, witness_set) @@ -49,7 +50,8 @@ pub fn create_transaction_native_token_transfer( let nonces = vec![nonce]; let program_id = nssa::program::Program::authenticated_transfer_program().id(); let message = - nssa::public_transaction::Message::try_new(program_id, addresses, nonces, balance_to_move).unwrap(); + nssa::public_transaction::Message::try_new(program_id, addresses, nonces, balance_to_move) + .unwrap(); let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[&signing_key]); nssa::PublicTransaction::new(message, witness_set) } diff --git a/nssa/src/public_transaction/encoding.rs b/nssa/src/public_transaction/encoding.rs index eb7fbfc..e687bbe 100644 --- a/nssa/src/public_transaction/encoding.rs +++ b/nssa/src/public_transaction/encoding.rs @@ -3,7 +3,8 @@ use std::io::{Cursor, Read}; use nssa_core::program::ProgramId; use crate::{ - public_transaction::{Message, WitnessSet}, Address, PublicKey, PublicTransaction, Signature + Address, PublicKey, PublicTransaction, Signature, + public_transaction::{Message, WitnessSet}, }; const MESSAGE_ENCODING_PREFIX_LEN: usize = 19; @@ -87,7 +88,6 @@ impl Message { } } - impl WitnessSet { pub(crate) fn to_bytes(&self) -> Vec { let mut bytes = Vec::new(); diff --git a/nssa/src/public_transaction/message.rs b/nssa/src/public_transaction/message.rs index 4b4e72a..28322c2 100644 --- a/nssa/src/public_transaction/message.rs +++ b/nssa/src/public_transaction/message.rs @@ -32,4 +32,3 @@ impl Message { }) } } - diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs index 6111bae..769be5a 100644 --- a/nssa/src/public_transaction/mod.rs +++ b/nssa/src/public_transaction/mod.rs @@ -48,7 +48,6 @@ impl PublicTransaction { } } - pub fn hash(&self) -> [u8; 32] { let bytes = self.to_bytes(); let mut hasher = sha2::Sha256::new(); @@ -126,7 +125,7 @@ mod tests { use std::io::Cursor; use crate::{ - Address, PrivateKey, PublicTransaction, PublicKey, + Address, PrivateKey, PublicKey, PublicTransaction, program::Program, public_transaction::{Message, WitnessSet}, }; diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs index 83edf66..e9b0f59 100644 --- a/nssa/src/public_transaction/witness_set.rs +++ b/nssa/src/public_transaction/witness_set.rs @@ -34,5 +34,4 @@ impl WitnessSet { pub fn iter_signatures(&self) -> impl Iterator { self.signatures_and_public_keys.iter() } - } diff --git a/nssa/src/signature/encoding.rs b/nssa/src/signature/encoding.rs index a7f112e..7e6ce58 100644 --- a/nssa/src/signature/encoding.rs +++ b/nssa/src/signature/encoding.rs @@ -2,7 +2,6 @@ use std::io::{Cursor, Read}; use crate::{PublicKey, Signature}; - impl PublicKey { // TODO: remove unwraps and return Result pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { diff --git a/nssa/src/signature/private_key.rs b/nssa/src/signature/private_key.rs index 78a2ad5..a58d363 100644 --- a/nssa/src/signature/private_key.rs +++ b/nssa/src/signature/private_key.rs @@ -2,7 +2,6 @@ use serde::{Deserialize, Serialize}; use crate::error::NssaError; - // TODO: Dummy impl. Replace by actual private key. // TODO: Remove Debug, Clone, Serialize, Deserialize, PartialEq and Eq for security reasons // TODO: Implement Zeroize @@ -22,4 +21,3 @@ impl PrivateKey { } } } - diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index 0ba4b44..bda1548 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -39,5 +39,4 @@ mod tests { ); } } - } diff --git a/nssa/src/signature/signature.rs b/nssa/src/signature/signature.rs index 0f956af..0bfcbd7 100644 --- a/nssa/src/signature/signature.rs +++ b/nssa/src/signature/signature.rs @@ -1,6 +1,6 @@ use std::io::{Cursor, Read}; -use rand::{rngs::OsRng, RngCore}; +use rand::{RngCore, rngs::OsRng}; use crate::{PrivateKey, PublicKey, error::NssaError, public_transaction::Message}; diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 19152ee..b464ede 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -84,7 +84,8 @@ impl WalletCore { addresses, nonces, balance_to_move, - ).unwrap(); + ) + .unwrap(); let signing_key = account.key_holder.get_pub_account_signing_key(); let witness_set = From 0b2650127ac4b25eecdfd453e7582bb5295dce23 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 12 Aug 2025 16:55:56 -0300 Subject: [PATCH 42/58] clippy --- common/src/rpc_primitives/requests.rs | 1 - nssa/src/public_transaction/message.rs | 1 - nssa/src/public_transaction/mod.rs | 5 +---- nssa/src/public_transaction/witness_set.rs | 6 ++---- nssa/src/signature/public_key.rs | 5 ++--- nssa/src/signature/signature.rs | 5 ++--- sequencer_rpc/src/process.rs | 1 - wallet/src/lib.rs | 1 - 8 files changed, 7 insertions(+), 18 deletions(-) diff --git a/common/src/rpc_primitives/requests.rs b/common/src/rpc_primitives/requests.rs index 085051b..2e67f16 100644 --- a/common/src/rpc_primitives/requests.rs +++ b/common/src/rpc_primitives/requests.rs @@ -1,4 +1,3 @@ -use crate::block::Block; use crate::parse_request; use super::errors::RpcParseError; diff --git a/nssa/src/public_transaction/message.rs b/nssa/src/public_transaction/message.rs index 28322c2..e137da3 100644 --- a/nssa/src/public_transaction/message.rs +++ b/nssa/src/public_transaction/message.rs @@ -1,4 +1,3 @@ -use std::io::{Cursor, Read}; use nssa_core::{ account::Nonce, diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs index 769be5a..efb53ef 100644 --- a/nssa/src/public_transaction/mod.rs +++ b/nssa/src/public_transaction/mod.rs @@ -1,7 +1,4 @@ -use std::{ - collections::{HashMap, HashSet}, - io::Cursor, -}; +use std::collections::{HashMap, HashSet}; use nssa_core::{ account::{Account, AccountWithMetadata}, diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs index e9b0f59..8bbce86 100644 --- a/nssa/src/public_transaction/witness_set.rs +++ b/nssa/src/public_transaction/witness_set.rs @@ -1,8 +1,6 @@ -use std::io::{Cursor, Read}; -use serde::{Deserialize, Serialize}; -use crate::{PrivateKey, PublicKey, Signature, error::NssaError, public_transaction::Message}; +use crate::{PrivateKey, PublicKey, Signature, public_transaction::Message}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct WitnessSet { @@ -24,7 +22,7 @@ impl WitnessSet { pub fn is_valid_for(&self, message: &Message) -> bool { let message_bytes = message.to_bytes(); for (signature, public_key) in self.iter_signatures() { - if !signature.is_valid_for(&message_bytes, &public_key) { + if !signature.is_valid_for(&message_bytes, public_key) { return false; } } diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index bda1548..18dc830 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -1,6 +1,5 @@ -use std::io::{Cursor, Read}; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use crate::PrivateKey; @@ -23,7 +22,7 @@ impl PublicKey { #[cfg(test)] mod tests { - use crate::{PrivateKey, PublicKey, signature::tests::test_vectors}; + use crate::{PublicKey, signature::tests::test_vectors}; #[test] fn test_public_key_generation_from_bip340_test_vectors() { diff --git a/nssa/src/signature/signature.rs b/nssa/src/signature/signature.rs index 0bfcbd7..dcb8b7c 100644 --- a/nssa/src/signature/signature.rs +++ b/nssa/src/signature/signature.rs @@ -1,8 +1,7 @@ -use std::io::{Cursor, Read}; use rand::{RngCore, rngs::OsRng}; -use crate::{PrivateKey, PublicKey, error::NssaError, public_transaction::Message}; +use crate::{PrivateKey, PublicKey}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Signature { @@ -31,7 +30,7 @@ impl Signature { let pk = secp256k1::XOnlyPublicKey::from_byte_array(public_key.0).unwrap(); let secp = secp256k1::Secp256k1::new(); let sig = secp256k1::schnorr::Signature::from_byte_array(self.value); - secp.verify_schnorr(&sig, &bytes, &pk).is_ok() + secp.verify_schnorr(&sig, bytes, &pk).is_ok() } } diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index d308659..4d7ba83 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -1,4 +1,3 @@ -use std::io::Cursor; use actix_web::Error as HttpError; use base64::{engine::general_purpose, Engine}; diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index b464ede..f4add81 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -8,7 +8,6 @@ use common::{ use accounts::account_core::{address::AccountAddress, Account}; use anyhow::Result; use chain_storage::WalletChainStore; -use common::transaction::TransactionBody; use config::WalletConfig; use log::info; use tokio::sync::RwLock; From b2f79e29651fcdf1d4eb59c16a8756ddf7332076 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 12 Aug 2025 17:58:48 -0300 Subject: [PATCH 43/58] add all tests --- nssa/src/public_transaction/encoding.rs | 2 +- nssa/src/public_transaction/message.rs | 1 - nssa/src/public_transaction/witness_set.rs | 2 - nssa/src/signature/mod.rs | 366 +++++++++++++++++++-- nssa/src/signature/public_key.rs | 3 - nssa/src/signature/signature.rs | 1 - 6 files changed, 340 insertions(+), 35 deletions(-) diff --git a/nssa/src/public_transaction/encoding.rs b/nssa/src/public_transaction/encoding.rs index e687bbe..0f9e80c 100644 --- a/nssa/src/public_transaction/encoding.rs +++ b/nssa/src/public_transaction/encoding.rs @@ -108,7 +108,7 @@ impl WitnessSet { u32::from_le_bytes(buf) }; let mut signatures_and_public_keys = Vec::with_capacity(num_signatures as usize); - for i in 0..num_signatures { + for _i in 0..num_signatures { let signature = Signature::from_cursor(cursor); let public_key = PublicKey::from_cursor(cursor); signatures_and_public_keys.push((signature, public_key)) diff --git a/nssa/src/public_transaction/message.rs b/nssa/src/public_transaction/message.rs index e137da3..784777d 100644 --- a/nssa/src/public_transaction/message.rs +++ b/nssa/src/public_transaction/message.rs @@ -1,4 +1,3 @@ - use nssa_core::{ account::Nonce, program::{InstructionData, ProgramId}, diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs index 8bbce86..f76de70 100644 --- a/nssa/src/public_transaction/witness_set.rs +++ b/nssa/src/public_transaction/witness_set.rs @@ -1,5 +1,3 @@ - - use crate::{PrivateKey, PublicKey, Signature, public_transaction::Message}; #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/nssa/src/signature/mod.rs b/nssa/src/signature/mod.rs index 1204ed5..efbfe93 100644 --- a/nssa/src/signature/mod.rs +++ b/nssa/src/signature/mod.rs @@ -31,46 +31,358 @@ mod tests { vec![ TestVector { seckey: Some(PrivateKey(hex_to_bytes( - "0000000000000000000000000000000000000000000000000000000000000003", - ))), + "0000000000000000000000000000000000000000000000000000000000000003", + ))), pubkey: PublicKey(hex_to_bytes( - "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", - )), + "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + )), aux_rand: Some(hex_to_bytes::<32>( - "0000000000000000000000000000000000000000000000000000000000000000", - )), - message: Some( - hex::decode("0000000000000000000000000000000000000000000000000000000000000000") - .unwrap(), - ), + "0000000000000000000000000000000000000000000000000000000000000000", + )), + message: Some( hex::decode("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), + ), signature: Signature { - value: hex_to_bytes( - "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0", - ), - }, + value: hex_to_bytes( + "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0", + ), + }, verification_result: true, }, + TestVector { seckey: Some(PrivateKey(hex_to_bytes( - "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF", - ))), + "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF", + ))), pubkey: PublicKey(hex_to_bytes( - "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), aux_rand: Some(hex_to_bytes::<32>( - "0000000000000000000000000000000000000000000000000000000000000001", - )), + "0000000000000000000000000000000000000000000000000000000000000001", + )), message: Some( - hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") - .unwrap(), - ), + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), + ), signature: Signature { - value: hex_to_bytes( - "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A", - ), - }, + value: hex_to_bytes( + "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A", + ), + }, verification_result: true, }, + + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9", + ))), + pubkey: PublicKey(hex_to_bytes( + "DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", + )), + aux_rand: Some(hex_to_bytes::<32>( + "C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906", + )), + message: Some( + hex::decode("7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C").unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7", + ), + }, + verification_result: true, + }, + + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710", + ))), + pubkey: PublicKey(hex_to_bytes( + "25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", + )), + aux_rand: Some(hex_to_bytes::<32>( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + )), + message: Some( + hex::decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3", + ), + }, + verification_result: true, + }, + + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9", + )), + aux_rand: None, + message: Some( + hex::decode("4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703").unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4", + ), + }, + verification_result: true, + }, + + // Test with invalid public key + // TestVector { + // seckey: None, + // pubkey: PublicKey(hex_to_bytes( + // "EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34", + // )), + // aux_rand: None, + // message: Some( + // hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), + // ), + // signature: Signature { + // value: hex_to_bytes( + // "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", + // ), + // }, + // verification_result: false, + // }, + + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: None, + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2", + ), + }, + verification_result: false, + }, + + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: None, + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD", + ), + }, + verification_result: false, + }, + + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: None, + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6", + ), + }, + verification_result: false, + }, + + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: None, + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051", + ), + }, + verification_result: false, + }, + + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: None, + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197", + ), + }, + verification_result: false, + }, + + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: None, + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", + ), + }, + verification_result: false, + }, + + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: None, + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", + ), + }, + verification_result: false, + }, + + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: None, + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", + ), + }, + verification_result: false, + }, + + // Test with invalid public key + // TestVector { + // seckey: None, + // pubkey: PublicKey(hex_to_bytes( + // "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30", + // )), + // aux_rand: None, + // message: Some( + // hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), + // ), + // signature: Signature { + // value: hex_to_bytes( + // "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", + // ), + // }, + // verification_result: false, + // }, + + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "0340034003400340034003400340034003400340034003400340034003400340", + ))), + pubkey: PublicKey(hex_to_bytes( + "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", + )), + aux_rand: Some(hex_to_bytes::<32>( + "0000000000000000000000000000000000000000000000000000000000000000", + )), + message: None, + signature: Signature { + value: hex_to_bytes( + "71535DB165ECD9FBBC046E5FFAEA61186BB6AD436732FCCC25291A55895464CF6069CE26BF03466228F19A3A62DB8A649F2D560FAC652827D1AF0574E427AB63", + ), + }, + verification_result: true, + }, + + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "0340034003400340034003400340034003400340034003400340034003400340", + ))), + pubkey: PublicKey(hex_to_bytes( + "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", + )), + aux_rand: Some(hex_to_bytes::<32>( + "0000000000000000000000000000000000000000000000000000000000000000", + )), + message: Some( + hex::decode("11").unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "08A20A0AFEF64124649232E0693C583AB1B9934AE63B4C3511F3AE1134C6A303EA3173BFEA6683BD101FA5AA5DBC1996FE7CACFC5A577D33EC14564CEC2BACBF", + ), + }, + verification_result: true, + }, + + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "0340034003400340034003400340034003400340034003400340034003400340", + ))), + pubkey: PublicKey(hex_to_bytes( + "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", + )), + aux_rand: Some(hex_to_bytes::<32>( + "0000000000000000000000000000000000000000000000000000000000000000", + )), + message: Some( + hex::decode("0102030405060708090A0B0C0D0E0F1011").unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "5130F39A4059B43BC7CAC09A19ECE52B5D8699D1A71E3C52DA9AFDB6B50AC370C4A482B77BF960F8681540E25B6771ECE1E5A37FD80E5A51897C5566A97EA5A5", + ), + }, + verification_result: true, + }, + + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "0340034003400340034003400340034003400340034003400340034003400340", + ))), + pubkey: PublicKey(hex_to_bytes( + "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", + )), + aux_rand: Some(hex_to_bytes::<32>( + "0000000000000000000000000000000000000000000000000000000000000000", + )), + message: Some( + hex::decode("99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999").unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "403B12B0D8555A344175EA7EC746566303321E5DBFA8BE6F091635163ECA79A8585ED3E3170807E7C03B720FC54C7B23897FCBA0E9D0B4A06894CFD249F22367", + ), + }, + verification_result: true, + }, + ] } } diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index 18dc830..a036367 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -1,6 +1,3 @@ - -use serde::Serialize; - use crate::PrivateKey; // TODO: Dummy impl. Replace by actual public key. diff --git a/nssa/src/signature/signature.rs b/nssa/src/signature/signature.rs index dcb8b7c..0a865ee 100644 --- a/nssa/src/signature/signature.rs +++ b/nssa/src/signature/signature.rs @@ -1,4 +1,3 @@ - use rand::{RngCore, rngs::OsRng}; use crate::{PrivateKey, PublicKey}; From 0dcdb2188a78f5bd0e1fcb209433fec9267fce0d Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 12 Aug 2025 21:27:27 -0300 Subject: [PATCH 44/58] refactor. add tests --- nssa/src/program.rs | 84 ------ nssa/src/signature/mod.rs | 380 ------------------------ nssa/src/signature/public_key.rs | 20 -- nssa/src/signature/signature.rs | 50 +--- nssa/src/state.rs | 58 +--- nssa/src/tests/bip340_test_vectors.rs | 367 +++++++++++++++++++++++ nssa/src/tests/mod.rs | 3 + nssa/src/tests/program_tests.rs | 123 ++++++++ nssa/src/tests/signature_tests.rs | 53 ++++ nssa/src/tests/valid_execution_tests.rs | 53 ++++ 10 files changed, 609 insertions(+), 582 deletions(-) create mode 100644 nssa/src/tests/bip340_test_vectors.rs create mode 100644 nssa/src/tests/program_tests.rs create mode 100644 nssa/src/tests/signature_tests.rs diff --git a/nssa/src/program.rs b/nssa/src/program.rs index b1ad8ca..5b4a1e8 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -76,87 +76,3 @@ impl Program { } } } - -// Test utils -#[cfg(test)] -impl Program { - /// A program that changes the nonce of an account - pub fn nonce_changer_program() -> Self { - use test_program_methods::{NONCE_CHANGER_ELF, NONCE_CHANGER_ID}; - - Program { - id: NONCE_CHANGER_ID, - elf: NONCE_CHANGER_ELF, - } - } - - /// A program that produces more output accounts than the inputs it received - pub fn extra_output_program() -> Self { - use test_program_methods::{EXTRA_OUTPUT_ELF, EXTRA_OUTPUT_ID}; - - Program { - id: EXTRA_OUTPUT_ID, - elf: EXTRA_OUTPUT_ELF, - } - } - - /// A program that produces less output accounts than the inputs it received - pub fn missing_output_program() -> Self { - use test_program_methods::{MISSING_OUTPUT_ELF, MISSING_OUTPUT_ID}; - - Program { - id: MISSING_OUTPUT_ID, - elf: MISSING_OUTPUT_ELF, - } - } - - /// A program that changes the program owner of an account to [0, 1, 2, 3, 4, 5, 6, 7] - pub fn program_owner_changer() -> Self { - use test_program_methods::{PROGRAM_OWNER_CHANGER_ELF, PROGRAM_OWNER_CHANGER_ID}; - - Program { - id: PROGRAM_OWNER_CHANGER_ID, - elf: PROGRAM_OWNER_CHANGER_ELF, - } - } - - /// A program that transfers balance without caring about authorizations - pub fn simple_balance_transfer() -> Self { - use test_program_methods::{SIMPLE_BALANCE_TRANSFER_ELF, SIMPLE_BALANCE_TRANSFER_ID}; - - Program { - id: SIMPLE_BALANCE_TRANSFER_ID, - elf: SIMPLE_BALANCE_TRANSFER_ELF, - } - } - - /// A program that modifies the data of an account - pub fn data_changer() -> Self { - use test_program_methods::{DATA_CHANGER_ELF, DATA_CHANGER_ID}; - - Program { - id: DATA_CHANGER_ID, - elf: DATA_CHANGER_ELF, - } - } - - /// A program that mints balance - pub fn minter() -> Self { - use test_program_methods::{MINTER_ELF, MINTER_ID}; - - Program { - id: MINTER_ID, - elf: MINTER_ELF, - } - } - - /// A program that burns balance - pub fn burner() -> Self { - use test_program_methods::{BURNER_ELF, BURNER_ID}; - - Program { - id: BURNER_ID, - elf: BURNER_ELF, - } - } -} diff --git a/nssa/src/signature/mod.rs b/nssa/src/signature/mod.rs index efbfe93..55ed41e 100644 --- a/nssa/src/signature/mod.rs +++ b/nssa/src/signature/mod.rs @@ -6,383 +6,3 @@ mod signature; pub use private_key::PrivateKey; pub use public_key::PublicKey; pub use signature::Signature; - -#[cfg(test)] -mod tests { - use crate::{PrivateKey, PublicKey, Signature}; - - fn hex_to_bytes(hex: &str) -> [u8; N] { - hex::decode(hex).unwrap().try_into().unwrap() - } - - pub struct TestVector { - pub seckey: Option, - pub pubkey: PublicKey, - pub aux_rand: Option<[u8; 32]>, - pub message: Option>, - pub signature: Signature, - pub verification_result: bool, - } - - /// Test vectors from - /// https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv - // - pub fn test_vectors() -> Vec { - vec![ - TestVector { - seckey: Some(PrivateKey(hex_to_bytes( - "0000000000000000000000000000000000000000000000000000000000000003", - ))), - pubkey: PublicKey(hex_to_bytes( - "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", - )), - aux_rand: Some(hex_to_bytes::<32>( - "0000000000000000000000000000000000000000000000000000000000000000", - )), - message: Some( hex::decode("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - ), - signature: Signature { - value: hex_to_bytes( - "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0", - ), - }, - verification_result: true, - }, - - TestVector { - seckey: Some(PrivateKey(hex_to_bytes( - "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF", - ))), - pubkey: PublicKey(hex_to_bytes( - "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), - aux_rand: Some(hex_to_bytes::<32>( - "0000000000000000000000000000000000000000000000000000000000000001", - )), - message: Some( - hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), - ), - signature: Signature { - value: hex_to_bytes( - "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A", - ), - }, - verification_result: true, - }, - - TestVector { - seckey: Some(PrivateKey(hex_to_bytes( - "C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9", - ))), - pubkey: PublicKey(hex_to_bytes( - "DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", - )), - aux_rand: Some(hex_to_bytes::<32>( - "C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906", - )), - message: Some( - hex::decode("7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C").unwrap(), - ), - signature: Signature { - value: hex_to_bytes( - "5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7", - ), - }, - verification_result: true, - }, - - TestVector { - seckey: Some(PrivateKey(hex_to_bytes( - "0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710", - ))), - pubkey: PublicKey(hex_to_bytes( - "25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", - )), - aux_rand: Some(hex_to_bytes::<32>( - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - )), - message: Some( - hex::decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(), - ), - signature: Signature { - value: hex_to_bytes( - "7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3", - ), - }, - verification_result: true, - }, - - TestVector { - seckey: None, - pubkey: PublicKey(hex_to_bytes( - "D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9", - )), - aux_rand: None, - message: Some( - hex::decode("4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703").unwrap(), - ), - signature: Signature { - value: hex_to_bytes( - "00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4", - ), - }, - verification_result: true, - }, - - // Test with invalid public key - // TestVector { - // seckey: None, - // pubkey: PublicKey(hex_to_bytes( - // "EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34", - // )), - // aux_rand: None, - // message: Some( - // hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), - // ), - // signature: Signature { - // value: hex_to_bytes( - // "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", - // ), - // }, - // verification_result: false, - // }, - - TestVector { - seckey: None, - pubkey: PublicKey(hex_to_bytes( - "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), - aux_rand: None, - message: Some( - hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), - ), - signature: Signature { - value: hex_to_bytes( - "FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2", - ), - }, - verification_result: false, - }, - - TestVector { - seckey: None, - pubkey: PublicKey(hex_to_bytes( - "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), - aux_rand: None, - message: Some( - hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), - ), - signature: Signature { - value: hex_to_bytes( - "1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD", - ), - }, - verification_result: false, - }, - - TestVector { - seckey: None, - pubkey: PublicKey(hex_to_bytes( - "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), - aux_rand: None, - message: Some( - hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), - ), - signature: Signature { - value: hex_to_bytes( - "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6", - ), - }, - verification_result: false, - }, - - TestVector { - seckey: None, - pubkey: PublicKey(hex_to_bytes( - "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), - aux_rand: None, - message: Some( - hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), - ), - signature: Signature { - value: hex_to_bytes( - "0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051", - ), - }, - verification_result: false, - }, - - TestVector { - seckey: None, - pubkey: PublicKey(hex_to_bytes( - "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), - aux_rand: None, - message: Some( - hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), - ), - signature: Signature { - value: hex_to_bytes( - "00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197", - ), - }, - verification_result: false, - }, - - TestVector { - seckey: None, - pubkey: PublicKey(hex_to_bytes( - "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), - aux_rand: None, - message: Some( - hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), - ), - signature: Signature { - value: hex_to_bytes( - "4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", - ), - }, - verification_result: false, - }, - - TestVector { - seckey: None, - pubkey: PublicKey(hex_to_bytes( - "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), - aux_rand: None, - message: Some( - hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), - ), - signature: Signature { - value: hex_to_bytes( - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", - ), - }, - verification_result: false, - }, - - TestVector { - seckey: None, - pubkey: PublicKey(hex_to_bytes( - "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), - aux_rand: None, - message: Some( - hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), - ), - signature: Signature { - value: hex_to_bytes( - "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", - ), - }, - verification_result: false, - }, - - // Test with invalid public key - // TestVector { - // seckey: None, - // pubkey: PublicKey(hex_to_bytes( - // "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30", - // )), - // aux_rand: None, - // message: Some( - // hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), - // ), - // signature: Signature { - // value: hex_to_bytes( - // "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", - // ), - // }, - // verification_result: false, - // }, - - TestVector { - seckey: Some(PrivateKey(hex_to_bytes( - "0340034003400340034003400340034003400340034003400340034003400340", - ))), - pubkey: PublicKey(hex_to_bytes( - "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", - )), - aux_rand: Some(hex_to_bytes::<32>( - "0000000000000000000000000000000000000000000000000000000000000000", - )), - message: None, - signature: Signature { - value: hex_to_bytes( - "71535DB165ECD9FBBC046E5FFAEA61186BB6AD436732FCCC25291A55895464CF6069CE26BF03466228F19A3A62DB8A649F2D560FAC652827D1AF0574E427AB63", - ), - }, - verification_result: true, - }, - - TestVector { - seckey: Some(PrivateKey(hex_to_bytes( - "0340034003400340034003400340034003400340034003400340034003400340", - ))), - pubkey: PublicKey(hex_to_bytes( - "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", - )), - aux_rand: Some(hex_to_bytes::<32>( - "0000000000000000000000000000000000000000000000000000000000000000", - )), - message: Some( - hex::decode("11").unwrap(), - ), - signature: Signature { - value: hex_to_bytes( - "08A20A0AFEF64124649232E0693C583AB1B9934AE63B4C3511F3AE1134C6A303EA3173BFEA6683BD101FA5AA5DBC1996FE7CACFC5A577D33EC14564CEC2BACBF", - ), - }, - verification_result: true, - }, - - TestVector { - seckey: Some(PrivateKey(hex_to_bytes( - "0340034003400340034003400340034003400340034003400340034003400340", - ))), - pubkey: PublicKey(hex_to_bytes( - "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", - )), - aux_rand: Some(hex_to_bytes::<32>( - "0000000000000000000000000000000000000000000000000000000000000000", - )), - message: Some( - hex::decode("0102030405060708090A0B0C0D0E0F1011").unwrap(), - ), - signature: Signature { - value: hex_to_bytes( - "5130F39A4059B43BC7CAC09A19ECE52B5D8699D1A71E3C52DA9AFDB6B50AC370C4A482B77BF960F8681540E25B6771ECE1E5A37FD80E5A51897C5566A97EA5A5", - ), - }, - verification_result: true, - }, - - TestVector { - seckey: Some(PrivateKey(hex_to_bytes( - "0340034003400340034003400340034003400340034003400340034003400340", - ))), - pubkey: PublicKey(hex_to_bytes( - "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", - )), - aux_rand: Some(hex_to_bytes::<32>( - "0000000000000000000000000000000000000000000000000000000000000000", - )), - message: Some( - hex::decode("99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999").unwrap(), - ), - signature: Signature { - value: hex_to_bytes( - "403B12B0D8555A344175EA7EC746566303321E5DBFA8BE6F091635163ECA79A8585ED3E3170807E7C03B720FC54C7B23897FCBA0E9D0B4A06894CFD249F22367", - ), - }, - verification_result: true, - }, - - ] - } -} diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index a036367..2f7f4c8 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -16,23 +16,3 @@ impl PublicKey { Self(value) } } - -#[cfg(test)] -mod tests { - use crate::{PublicKey, signature::tests::test_vectors}; - - #[test] - fn test_public_key_generation_from_bip340_test_vectors() { - for (i, test_vector) in test_vectors().iter().enumerate() { - let Some(private_key) = &test_vector.seckey else { - continue; - }; - let public_key = PublicKey::new(private_key); - let expected_public_key = &test_vector.pubkey; - assert_eq!( - &public_key, expected_public_key, - "Failed test vector at index {i}" - ); - } - } -} diff --git a/nssa/src/signature/signature.rs b/nssa/src/signature/signature.rs index 0a865ee..c49e18b 100644 --- a/nssa/src/signature/signature.rs +++ b/nssa/src/signature/signature.rs @@ -8,13 +8,17 @@ pub struct Signature { } impl Signature { - pub(crate) fn new(key: &PrivateKey, message: &[u8]) -> Self { + pub fn new(key: &PrivateKey, message: &[u8]) -> Self { let mut aux_random = [0u8; 32]; OsRng.fill_bytes(&mut aux_random); Self::new_with_aux_random(key, message, aux_random) } - fn new_with_aux_random(key: &PrivateKey, message: &[u8], aux_random: [u8; 32]) -> Self { + pub(crate) fn new_with_aux_random( + key: &PrivateKey, + message: &[u8], + aux_random: [u8; 32], + ) -> Self { let value = { let secp = secp256k1::Secp256k1::new(); let secret_key = secp256k1::SecretKey::from_byte_array(key.0).unwrap(); @@ -32,45 +36,3 @@ impl Signature { secp.verify_schnorr(&sig, bytes, &pk).is_ok() } } - -#[cfg(test)] -mod tests { - use crate::{Signature, signature::tests::test_vectors}; - - #[test] - fn test_signature_generation_from_bip340_test_vectors() { - for (i, test_vector) in test_vectors().into_iter().enumerate() { - let Some(private_key) = test_vector.seckey else { - continue; - }; - let Some(aux_random) = test_vector.aux_rand else { - continue; - }; - let Some(message) = test_vector.message else { - continue; - }; - if !test_vector.verification_result { - continue; - } - let expected_signature = &test_vector.signature; - - let signature = Signature::new_with_aux_random(&private_key, &message, aux_random); - - assert_eq!(&signature, expected_signature, "Failed test vector {i}"); - } - } - - #[test] - fn test_signature_verification_from_bip340_test_vectors() { - for (i, test_vector) in test_vectors().into_iter().enumerate() { - let message = test_vector.message.unwrap_or(vec![]); - let expected_result = test_vector.verification_result; - - let result = test_vector - .signature - .is_valid_for(&message, &test_vector.pubkey); - - assert_eq!(result, expected_result, "Failed test vector {i}"); - } - } -} diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 04d9c97..690919f 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -36,7 +36,7 @@ impl V01State { this } - fn insert_program(&mut self, program: Program) { + pub(crate) fn insert_program(&mut self, program: Program) { self.builtin_programs.insert(program.id(), program); } @@ -73,59 +73,9 @@ impl V01State { pub(crate) fn builtin_programs(&self) -> &HashMap { &self.builtin_programs } -} -// Test utils -#[cfg(test)] -impl V01State { - /// Include test programs in the builtin programs map - pub fn with_test_programs(mut self) -> Self { - self.insert_program(Program::nonce_changer_program()); - self.insert_program(Program::extra_output_program()); - self.insert_program(Program::missing_output_program()); - self.insert_program(Program::program_owner_changer()); - self.insert_program(Program::simple_balance_transfer()); - self.insert_program(Program::data_changer()); - self.insert_program(Program::minter()); - self.insert_program(Program::burner()); - self - } - - pub fn with_non_default_accounts_but_default_program_owners(mut self) -> Self { - let account_with_default_values_except_balance = Account { - balance: 100, - ..Account::default() - }; - let account_with_default_values_except_nonce = Account { - nonce: 37, - ..Account::default() - }; - let account_with_default_values_except_data = Account { - data: vec![0xca, 0xfe], - ..Account::default() - }; - self.public_state.insert( - Address::new([255; 32]), - account_with_default_values_except_balance, - ); - self.public_state.insert( - Address::new([254; 32]), - account_with_default_values_except_nonce, - ); - self.public_state.insert( - Address::new([253; 32]), - account_with_default_values_except_data, - ); - self - } - - pub fn with_account_owned_by_burner_program(mut self) -> Self { - let account = Account { - program_owner: Program::burner().id(), - balance: 100, - ..Default::default() - }; - self.public_state.insert(Address::new([252; 32]), account); - self + #[cfg(test)] + pub fn force_insert_account(&mut self, address: Address, account: Account) { + self.public_state.insert(address, account); } } diff --git a/nssa/src/tests/bip340_test_vectors.rs b/nssa/src/tests/bip340_test_vectors.rs new file mode 100644 index 0000000..6b6a3bc --- /dev/null +++ b/nssa/src/tests/bip340_test_vectors.rs @@ -0,0 +1,367 @@ +use crate::{PrivateKey, PublicKey, Signature}; + +fn hex_to_bytes(hex: &str) -> [u8; N] { + hex::decode(hex).unwrap().try_into().unwrap() +} + +pub struct TestVector { + pub seckey: Option, + pub pubkey: PublicKey, + pub aux_rand: Option<[u8; 32]>, + pub message: Option>, + pub signature: Signature, + pub verification_result: bool, +} + +/// Test vectors from +/// https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv +// +pub fn test_vectors() -> Vec { + vec![ + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "0000000000000000000000000000000000000000000000000000000000000003", + ))), + pubkey: PublicKey(hex_to_bytes( + "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + )), + aux_rand: Some(hex_to_bytes::<32>( + "0000000000000000000000000000000000000000000000000000000000000000", + )), + message: Some( + hex::decode("0000000000000000000000000000000000000000000000000000000000000000") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0", + ), + }, + verification_result: true, + }, + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF", + ))), + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: Some(hex_to_bytes::<32>( + "0000000000000000000000000000000000000000000000000000000000000001", + )), + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A", + ), + }, + verification_result: true, + }, + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9", + ))), + pubkey: PublicKey(hex_to_bytes( + "DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", + )), + aux_rand: Some(hex_to_bytes::<32>( + "C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906", + )), + message: Some( + hex::decode("7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7", + ), + }, + verification_result: true, + }, + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710", + ))), + pubkey: PublicKey(hex_to_bytes( + "25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", + )), + aux_rand: Some(hex_to_bytes::<32>( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + )), + message: Some( + hex::decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3", + ), + }, + verification_result: true, + }, + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9", + )), + aux_rand: None, + message: Some( + hex::decode("4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4", + ), + }, + verification_result: true, + }, + // Test with invalid public key + // TestVector { + // seckey: None, + // pubkey: PublicKey(hex_to_bytes( + // "EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34", + // )), + // aux_rand: None, + // message: Some( + // hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), + // ), + // signature: Signature { + // value: hex_to_bytes( + // "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", + // ), + // }, + // verification_result: false, + // }, + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: None, + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2", + ), + }, + verification_result: false, + }, + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: None, + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD", + ), + }, + verification_result: false, + }, + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: None, + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6", + ), + }, + verification_result: false, + }, + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: None, + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051", + ), + }, + verification_result: false, + }, + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: None, + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197", + ), + }, + verification_result: false, + }, + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: None, + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", + ), + }, + verification_result: false, + }, + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: None, + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", + ), + }, + verification_result: false, + }, + TestVector { + seckey: None, + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: None, + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", + ), + }, + verification_result: false, + }, + // Test with invalid public key + // TestVector { + // seckey: None, + // pubkey: PublicKey(hex_to_bytes( + // "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30", + // )), + // aux_rand: None, + // message: Some( + // hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), + // ), + // signature: Signature { + // value: hex_to_bytes( + // "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", + // ), + // }, + // verification_result: false, + // }, + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "0340034003400340034003400340034003400340034003400340034003400340", + ))), + pubkey: PublicKey(hex_to_bytes( + "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", + )), + aux_rand: Some(hex_to_bytes::<32>( + "0000000000000000000000000000000000000000000000000000000000000000", + )), + message: None, + signature: Signature { + value: hex_to_bytes( + "71535DB165ECD9FBBC046E5FFAEA61186BB6AD436732FCCC25291A55895464CF6069CE26BF03466228F19A3A62DB8A649F2D560FAC652827D1AF0574E427AB63", + ), + }, + verification_result: true, + }, + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "0340034003400340034003400340034003400340034003400340034003400340", + ))), + pubkey: PublicKey(hex_to_bytes( + "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", + )), + aux_rand: Some(hex_to_bytes::<32>( + "0000000000000000000000000000000000000000000000000000000000000000", + )), + message: Some(hex::decode("11").unwrap()), + signature: Signature { + value: hex_to_bytes( + "08A20A0AFEF64124649232E0693C583AB1B9934AE63B4C3511F3AE1134C6A303EA3173BFEA6683BD101FA5AA5DBC1996FE7CACFC5A577D33EC14564CEC2BACBF", + ), + }, + verification_result: true, + }, + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "0340034003400340034003400340034003400340034003400340034003400340", + ))), + pubkey: PublicKey(hex_to_bytes( + "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", + )), + aux_rand: Some(hex_to_bytes::<32>( + "0000000000000000000000000000000000000000000000000000000000000000", + )), + message: Some(hex::decode("0102030405060708090A0B0C0D0E0F1011").unwrap()), + signature: Signature { + value: hex_to_bytes( + "5130F39A4059B43BC7CAC09A19ECE52B5D8699D1A71E3C52DA9AFDB6B50AC370C4A482B77BF960F8681540E25B6771ECE1E5A37FD80E5A51897C5566A97EA5A5", + ), + }, + verification_result: true, + }, + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "0340034003400340034003400340034003400340034003400340034003400340", + ))), + pubkey: PublicKey(hex_to_bytes( + "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", + )), + aux_rand: Some(hex_to_bytes::<32>( + "0000000000000000000000000000000000000000000000000000000000000000", + )), + message: Some( + hex::decode("99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999").unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "403B12B0D8555A344175EA7EC746566303321E5DBFA8BE6F091635163ECA79A8585ED3E3170807E7C03B720FC54C7B23897FCBA0E9D0B4A06894CFD249F22367", + ), + }, + verification_result: true, + }, + ] +} diff --git a/nssa/src/tests/mod.rs b/nssa/src/tests/mod.rs index 2c04e2d..70ae5bd 100644 --- a/nssa/src/tests/mod.rs +++ b/nssa/src/tests/mod.rs @@ -1,2 +1,5 @@ +mod bip340_test_vectors; +mod program_tests; +mod signature_tests; mod state_tests; mod valid_execution_tests; diff --git a/nssa/src/tests/program_tests.rs b/nssa/src/tests/program_tests.rs new file mode 100644 index 0000000..2249c81 --- /dev/null +++ b/nssa/src/tests/program_tests.rs @@ -0,0 +1,123 @@ +use nssa_core::account::{Account, AccountWithMetadata}; + +use crate::program::Program; + +impl Program { + /// A program that changes the nonce of an account + pub fn nonce_changer_program() -> Self { + use test_program_methods::{NONCE_CHANGER_ELF, NONCE_CHANGER_ID}; + + Program { + id: NONCE_CHANGER_ID, + elf: NONCE_CHANGER_ELF, + } + } + + /// A program that produces more output accounts than the inputs it received + pub fn extra_output_program() -> Self { + use test_program_methods::{EXTRA_OUTPUT_ELF, EXTRA_OUTPUT_ID}; + + Program { + id: EXTRA_OUTPUT_ID, + elf: EXTRA_OUTPUT_ELF, + } + } + + /// A program that produces less output accounts than the inputs it received + pub fn missing_output_program() -> Self { + use test_program_methods::{MISSING_OUTPUT_ELF, MISSING_OUTPUT_ID}; + + Program { + id: MISSING_OUTPUT_ID, + elf: MISSING_OUTPUT_ELF, + } + } + + /// A program that changes the program owner of an account to [0, 1, 2, 3, 4, 5, 6, 7] + pub fn program_owner_changer() -> Self { + use test_program_methods::{PROGRAM_OWNER_CHANGER_ELF, PROGRAM_OWNER_CHANGER_ID}; + + Program { + id: PROGRAM_OWNER_CHANGER_ID, + elf: PROGRAM_OWNER_CHANGER_ELF, + } + } + + /// A program that transfers balance without caring about authorizations + pub fn simple_balance_transfer() -> Self { + use test_program_methods::{SIMPLE_BALANCE_TRANSFER_ELF, SIMPLE_BALANCE_TRANSFER_ID}; + + Program { + id: SIMPLE_BALANCE_TRANSFER_ID, + elf: SIMPLE_BALANCE_TRANSFER_ELF, + } + } + + /// A program that modifies the data of an account + pub fn data_changer() -> Self { + use test_program_methods::{DATA_CHANGER_ELF, DATA_CHANGER_ID}; + + Program { + id: DATA_CHANGER_ID, + elf: DATA_CHANGER_ELF, + } + } + + /// A program that mints balance + pub fn minter() -> Self { + use test_program_methods::{MINTER_ELF, MINTER_ID}; + + Program { + id: MINTER_ID, + elf: MINTER_ELF, + } + } + + /// A program that burns balance + pub fn burner() -> Self { + use test_program_methods::{BURNER_ELF, BURNER_ID}; + + Program { + id: BURNER_ID, + elf: BURNER_ELF, + } + } +} + +#[test] +fn test_program_execution() { + let program = Program::simple_balance_transfer(); + let balance_to_move: u128 = 11223344556677; + let instruction_data = Program::serialize_instruction(balance_to_move).unwrap(); + let sender = AccountWithMetadata { + account: Account { + balance: 77665544332211, + ..Account::default() + }, + is_authorized: false, + }; + let recipient = AccountWithMetadata { + account: Account::default(), + is_authorized: false, + }; + + let expected_sender_post = Account { + balance: 77665544332211 - balance_to_move, + program_owner: program.id(), + ..Account::default() + }; + let expected_recipient_post = Account { + balance: balance_to_move, + // Program claims the account since the pre_state has default prorgam owner + program_owner: program.id(), + ..Account::default() + }; + let [sender_post, recipient_post] = program + .execute(&[sender, recipient], &instruction_data) + .unwrap() + .try_into() + .unwrap(); + + assert_eq!(sender_post, expected_sender_post); + assert_eq!(recipient_post, expected_recipient_post); +} diff --git a/nssa/src/tests/signature_tests.rs b/nssa/src/tests/signature_tests.rs new file mode 100644 index 0000000..fb4d937 --- /dev/null +++ b/nssa/src/tests/signature_tests.rs @@ -0,0 +1,53 @@ +use crate::{PublicKey, Signature, tests::bip340_test_vectors}; + +#[test] +fn test_signature_generation_from_bip340_test_vectors() { + for (i, test_vector) in bip340_test_vectors::test_vectors().into_iter().enumerate() { + let Some(private_key) = test_vector.seckey else { + continue; + }; + let Some(aux_random) = test_vector.aux_rand else { + continue; + }; + let Some(message) = test_vector.message else { + continue; + }; + if !test_vector.verification_result { + continue; + } + let expected_signature = &test_vector.signature; + + let signature = Signature::new_with_aux_random(&private_key, &message, aux_random); + + assert_eq!(&signature, expected_signature, "Failed test vector {i}"); + } +} + +#[test] +fn test_signature_verification_from_bip340_test_vectors() { + for (i, test_vector) in bip340_test_vectors::test_vectors().into_iter().enumerate() { + let message = test_vector.message.unwrap_or(vec![]); + let expected_result = test_vector.verification_result; + + let result = test_vector + .signature + .is_valid_for(&message, &test_vector.pubkey); + + assert_eq!(result, expected_result, "Failed test vector {i}"); + } +} + +#[test] +fn test_public_key_generation_from_bip340_test_vectors() { + for (i, test_vector) in bip340_test_vectors::test_vectors().into_iter().enumerate() { + let Some(private_key) = &test_vector.seckey else { + continue; + }; + let public_key = PublicKey::new(private_key); + let expected_public_key = &test_vector.pubkey; + assert_eq!( + &public_key, expected_public_key, + "Failed test vector at index {i}" + ); + } +} diff --git a/nssa/src/tests/valid_execution_tests.rs b/nssa/src/tests/valid_execution_tests.rs index 399c47a..0d35b02 100644 --- a/nssa/src/tests/valid_execution_tests.rs +++ b/nssa/src/tests/valid_execution_tests.rs @@ -4,6 +4,59 @@ use crate::{ Address, PublicTransaction, V01State, error::NssaError, program::Program, public_transaction, }; +impl V01State { + /// Include test programs in the builtin programs map + pub fn with_test_programs(mut self) -> Self { + self.insert_program(Program::nonce_changer_program()); + self.insert_program(Program::extra_output_program()); + self.insert_program(Program::missing_output_program()); + self.insert_program(Program::program_owner_changer()); + self.insert_program(Program::simple_balance_transfer()); + self.insert_program(Program::data_changer()); + self.insert_program(Program::minter()); + self.insert_program(Program::burner()); + self + } + + pub fn with_non_default_accounts_but_default_program_owners(mut self) -> Self { + let account_with_default_values_except_balance = Account { + balance: 100, + ..Account::default() + }; + let account_with_default_values_except_nonce = Account { + nonce: 37, + ..Account::default() + }; + let account_with_default_values_except_data = Account { + data: vec![0xca, 0xfe], + ..Account::default() + }; + self.force_insert_account( + Address::new([255; 32]), + account_with_default_values_except_balance, + ); + self.force_insert_account( + Address::new([254; 32]), + account_with_default_values_except_nonce, + ); + self.force_insert_account( + Address::new([253; 32]), + account_with_default_values_except_data, + ); + self + } + + pub fn with_account_owned_by_burner_program(mut self) -> Self { + let account = Account { + program_owner: Program::burner().id(), + balance: 100, + ..Default::default() + }; + self.force_insert_account(Address::new([252; 32]), account); + self + } +} + #[test] fn test_program_should_fail_if_modifies_nonces() { let initial_data = [([1; 32], 100)]; From 2d9896f6eda270fd0175ae4fcf22429eca171563 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 12 Aug 2025 22:35:07 -0300 Subject: [PATCH 45/58] error handling for encoding --- common/src/block.rs | 2 +- nssa/src/error.rs | 5 +++ nssa/src/public_transaction/encoding.rs | 56 ++++++++++++------------- nssa/src/public_transaction/mod.rs | 7 ++-- nssa/src/signature/encoding.rs | 16 ++++--- sequencer_core/src/lib.rs | 3 -- sequencer_rpc/src/process.rs | 4 +- 7 files changed, 46 insertions(+), 47 deletions(-) diff --git a/common/src/block.rs b/common/src/block.rs index 3d39e58..55490b4 100644 --- a/common/src/block.rs +++ b/common/src/block.rs @@ -78,7 +78,7 @@ impl HashableBlockData { let mut transactions = Vec::with_capacity(num_transactions); for _ in 0..num_transactions { - let tx = nssa::PublicTransaction::from_cursor(&mut cursor); + let tx = nssa::PublicTransaction::from_cursor(&mut cursor).unwrap(); transactions.push(tx); } diff --git a/nssa/src/error.rs b/nssa/src/error.rs index c4c96c7..5e173b5 100644 --- a/nssa/src/error.rs +++ b/nssa/src/error.rs @@ -1,3 +1,5 @@ +use std::io; + use thiserror::Error; #[derive(Error, Debug)] @@ -16,4 +18,7 @@ pub enum NssaError { #[error("Invalid private key")] InvalidPrivateKey, + + #[error("IO error: {0}")] + Io(#[from] io::Error), } diff --git a/nssa/src/public_transaction/encoding.rs b/nssa/src/public_transaction/encoding.rs index 0f9e80c..6d561ef 100644 --- a/nssa/src/public_transaction/encoding.rs +++ b/nssa/src/public_transaction/encoding.rs @@ -4,6 +4,7 @@ use nssa_core::program::ProgramId; use crate::{ Address, PublicKey, PublicTransaction, Signature, + error::NssaError, public_transaction::{Message, WitnessSet}, }; @@ -45,46 +46,46 @@ impl Message { bytes } - pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { + pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { let prefix = { let mut this = [0u8; MESSAGE_ENCODING_PREFIX_LEN]; - cursor.read_exact(&mut this).unwrap(); + cursor.read_exact(&mut this)?; this }; assert_eq!(&prefix, MESSAGE_ENCODING_PREFIX); let program_id: ProgramId = { let mut this = [0u32; 8]; for i in 0..8 { - this[i] = u32_from_cursor(cursor); + this[i] = u32_from_cursor(cursor)?; } this }; - let addresses_len = u32_from_cursor(cursor); + let addresses_len = u32_from_cursor(cursor)?; let mut addresses = Vec::with_capacity(addresses_len as usize); for _ in 0..addresses_len { let mut value = [0u8; 32]; - cursor.read_exact(&mut value).unwrap(); + cursor.read_exact(&mut value)?; addresses.push(Address::new(value)) } - let nonces_len = u32_from_cursor(cursor); + let nonces_len = u32_from_cursor(cursor)?; let mut nonces = Vec::with_capacity(nonces_len as usize); for _ in 0..nonces_len { let mut buf = [0u8; 16]; - cursor.read_exact(&mut buf).unwrap(); + cursor.read_exact(&mut buf)?; nonces.push(u128::from_le_bytes(buf)) } - let instruction_data_len = u32_from_cursor(cursor); + let instruction_data_len = u32_from_cursor(cursor)?; let mut instruction_data = Vec::with_capacity(instruction_data_len as usize); for _ in 0..instruction_data_len { - let word = u32_from_cursor(cursor); + let word = u32_from_cursor(cursor)?; instruction_data.push(word) } - Self { + Ok(Self { program_id, addresses, nonces, instruction_data, - } + }) } } @@ -100,22 +101,21 @@ impl WitnessSet { bytes } - // TODO: remove unwraps and return Result - pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { + pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { let num_signatures: u32 = { let mut buf = [0u8; 4]; - cursor.read_exact(&mut buf).unwrap(); + cursor.read_exact(&mut buf)?; u32::from_le_bytes(buf) }; let mut signatures_and_public_keys = Vec::with_capacity(num_signatures as usize); for _i in 0..num_signatures { - let signature = Signature::from_cursor(cursor); - let public_key = PublicKey::from_cursor(cursor); + let signature = Signature::from_cursor(cursor)?; + let public_key = PublicKey::from_cursor(cursor)?; signatures_and_public_keys.push((signature, public_key)) } - Self { + Ok(Self { signatures_and_public_keys, - } + }) } } @@ -126,23 +126,23 @@ impl PublicTransaction { bytes } - pub fn from_bytes(bytes: &[u8]) -> Self { + pub fn from_bytes(bytes: &[u8]) -> Result { let mut cursor = Cursor::new(bytes); - Self::from_cursor(&mut cursor) + Ok(Self::from_cursor(&mut cursor)?) } - pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { - let message = Message::from_cursor(cursor); - let witness_set = WitnessSet::from_cursor(cursor); - Self { + pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { + let message = Message::from_cursor(cursor)?; + let witness_set = WitnessSet::from_cursor(cursor)?; + Ok(Self { message, witness_set, - } + }) } } -fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> u32 { +fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { let mut word_buf = [0u8; 4]; - cursor.read_exact(&mut word_buf).unwrap(); - u32::from_le_bytes(word_buf) + cursor.read_exact(&mut word_buf)?; + Ok(u32::from_le_bytes(word_buf)) } diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs index efb53ef..44eee56 100644 --- a/nssa/src/public_transaction/mod.rs +++ b/nssa/src/public_transaction/mod.rs @@ -128,7 +128,7 @@ mod tests { }; #[test] - fn test_to_bytes() { + fn test_public_transaction_encoding_bytes_roundtrip() { let key1 = PrivateKey::try_new([1; 32]).unwrap(); let key2 = PrivateKey::try_new([2; 32]).unwrap(); let addr1 = Address::from_public_key(&PublicKey::new(&key1)); @@ -147,8 +147,7 @@ mod tests { let tx = PublicTransaction::new(message, witness_set); let bytes = tx.to_bytes(); - let mut cursor: Cursor<&[u8]> = Cursor::new(&bytes); - let recov_tx = PublicTransaction::from_cursor(&mut cursor); - assert_eq!(tx, recov_tx); + let tx_from_bytes = PublicTransaction::from_bytes(&bytes).unwrap(); + assert_eq!(tx, tx_from_bytes); } } diff --git a/nssa/src/signature/encoding.rs b/nssa/src/signature/encoding.rs index 7e6ce58..7760c00 100644 --- a/nssa/src/signature/encoding.rs +++ b/nssa/src/signature/encoding.rs @@ -1,13 +1,12 @@ use std::io::{Cursor, Read}; -use crate::{PublicKey, Signature}; +use crate::{PublicKey, Signature, error::NssaError}; impl PublicKey { - // TODO: remove unwraps and return Result - pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { + pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { let mut value = [0u8; 32]; - cursor.read_exact(&mut value).unwrap(); - Self(value) + cursor.read_exact(&mut value)?; + Ok(Self(value)) } pub(crate) fn to_bytes(&self) -> &[u8] { @@ -16,11 +15,10 @@ impl PublicKey { } impl Signature { - // TODO: remove unwraps and return Result - pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { + pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { let mut value = [0u8; 64]; - cursor.read_exact(&mut value).unwrap(); - Self { value } + cursor.read_exact(&mut value)?; + Ok(Self { value }) } pub(crate) fn to_bytes(&self) -> &[u8] { diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 8430b83..b56b34cc 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -102,13 +102,10 @@ impl SequencerCore { .mempool .pop_size(self.sequencer_config.max_num_tx_in_block); - println!("##"); - println!("{:?}", transactions.len()); let valid_transactions: Vec<_> = transactions .into_iter() .filter_map(|tx| self.execute_check_transaction_on_state(tx).ok()) .collect(); - println!("{:?}", valid_transactions.len()); let prev_block_hash = self .store diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 4d7ba83..2d5a29a 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -1,4 +1,3 @@ - use actix_web::Error as HttpError; use base64::{engine::general_purpose, Engine}; use nssa; @@ -71,7 +70,8 @@ impl JsonHandler { async fn process_send_tx(&self, request: Request) -> Result { let send_tx_req = SendTxRequest::parse(Some(request.params))?; - let tx = nssa::PublicTransaction::from_bytes(&send_tx_req.transaction); + let tx = nssa::PublicTransaction::from_bytes(&send_tx_req.transaction) + .map_err(|e| RpcError::serialization_error(&e.to_string()))?; { let mut state = self.sequencer_state.lock().await; From d3827fc82342a012981b63920e1435464bc9cfa5 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 12 Aug 2025 23:31:41 -0300 Subject: [PATCH 46/58] add tests. refactor --- common/src/block.rs | 9 +- nssa/src/public_transaction/encoding.rs | 9 +- nssa/src/public_transaction/mod.rs | 146 +--------------- nssa/src/public_transaction/transaction.rs | 183 +++++++++++++++++++++ nssa/src/tests/mod.rs | 1 + nssa/src/tests/public_transaction_tests.rs | 28 ++++ 6 files changed, 224 insertions(+), 152 deletions(-) create mode 100644 nssa/src/public_transaction/transaction.rs create mode 100644 nssa/src/tests/public_transaction_tests.rs diff --git a/common/src/block.rs b/common/src/block.rs index 55490b4..2e4e9b9 100644 --- a/common/src/block.rs +++ b/common/src/block.rs @@ -65,6 +65,7 @@ impl HashableBlockData { bytes } + // TODO: Improve error handling. Remove unwraps. pub fn from_bytes(data: &[u8]) -> Self { let mut cursor = Cursor::new(data); @@ -91,12 +92,14 @@ impl HashableBlockData { } } +// TODO: Improve error handling. Remove unwraps. fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> u32 { let mut word_buf = [0u8; 4]; cursor.read_exact(&mut word_buf).unwrap(); u32::from_le_bytes(word_buf) } +// TODO: Improve error handling. Remove unwraps. fn u64_from_cursor(cursor: &mut Cursor<&[u8]>) -> u64 { let mut word_buf = [0u8; 8]; cursor.read_exact(&mut word_buf).unwrap(); @@ -108,12 +111,12 @@ mod tests { use crate::{block::HashableBlockData, test_utils}; #[test] - fn test() { + fn test_encoding_roundtrip() { let transactions = vec![test_utils::produce_dummy_empty_transaction()]; let block = test_utils::produce_dummy_block(1, Some([1; 32]), transactions); let hashable = HashableBlockData::from(block); let bytes = hashable.to_bytes(); - let recov = HashableBlockData::from_bytes(&bytes); - assert_eq!(hashable, recov); + let block_from_bytes = HashableBlockData::from_bytes(&bytes); + assert_eq!(hashable, block_from_bytes); } } diff --git a/nssa/src/public_transaction/encoding.rs b/nssa/src/public_transaction/encoding.rs index 6d561ef..371f1b5 100644 --- a/nssa/src/public_transaction/encoding.rs +++ b/nssa/src/public_transaction/encoding.rs @@ -121,8 +121,8 @@ impl WitnessSet { impl PublicTransaction { pub fn to_bytes(&self) -> Vec { - let mut bytes = self.message.to_bytes(); - bytes.extend_from_slice(&self.witness_set.to_bytes()); + let mut bytes = self.message().to_bytes(); + bytes.extend_from_slice(&self.witness_set().to_bytes()); bytes } @@ -134,10 +134,7 @@ impl PublicTransaction { pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { let message = Message::from_cursor(cursor)?; let witness_set = WitnessSet::from_cursor(cursor)?; - Ok(Self { - message, - witness_set, - }) + Ok(PublicTransaction::new(message, witness_set)) } } diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs index 44eee56..ce9ae9e 100644 --- a/nssa/src/public_transaction/mod.rs +++ b/nssa/src/public_transaction/mod.rs @@ -1,153 +1,13 @@ -use std::collections::{HashMap, HashSet}; - -use nssa_core::{ - account::{Account, AccountWithMetadata}, - program::validate_execution, -}; -use sha2::{Digest, digest::FixedOutput}; - -use crate::{V01State, address::Address, error::NssaError}; mod encoding; mod message; mod witness_set; +mod transaction; pub use message::Message; pub use witness_set::WitnessSet; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct PublicTransaction { - message: Message, - witness_set: WitnessSet, -} - -impl PublicTransaction { - pub fn message(&self) -> &Message { - &self.message - } - - pub fn witness_set(&self) -> &WitnessSet { - &self.witness_set - } - - pub(crate) fn signer_addresses(&self) -> Vec
{ - self.witness_set - .signatures_and_public_keys - .iter() - .map(|(_, public_key)| Address::from_public_key(public_key)) - .collect() - } - - pub fn new(message: Message, witness_set: WitnessSet) -> Self { - Self { - message, - witness_set, - } - } - - pub fn hash(&self) -> [u8; 32] { - let bytes = self.to_bytes(); - let mut hasher = sha2::Sha256::new(); - hasher.update(&bytes); - hasher.finalize_fixed().into() - } - - pub(crate) fn validate_and_compute_post_states( - &self, - state: &V01State, - ) -> Result, NssaError> { - let message = self.message(); - let witness_set = self.witness_set(); - - // All addresses must be different - if message.addresses.iter().collect::>().len() != message.addresses.len() { - return Err(NssaError::InvalidInput( - "Duplicate addresses found in message".into(), - )); - } - - if message.nonces.len() != witness_set.signatures_and_public_keys.len() { - return Err(NssaError::InvalidInput( - "Mismatch between number of nonces and signatures/public keys".into(), - )); - } - - // Check the signatures are valid - if !witness_set.is_valid_for(message) { - return Err(NssaError::InvalidInput( - "Invalid signature for given message and public key".into(), - )); - } - - let signer_addresses = self.signer_addresses(); - // Check nonces corresponds to the current nonces on the public state. - for (address, nonce) in signer_addresses.iter().zip(&message.nonces) { - let current_nonce = state.get_account_by_address(address).nonce; - if current_nonce != *nonce { - return Err(NssaError::InvalidInput("Nonce mismatch".into())); - } - } - - // Build pre_states for execution - let pre_states: Vec<_> = message - .addresses - .iter() - .map(|address| AccountWithMetadata { - account: state.get_account_by_address(address), - is_authorized: signer_addresses.contains(address), - }) - .collect(); - - // Check the `program_id` corresponds to a built-in program - // Only allowed program so far is the authenticated transfer program - let Some(program) = state.builtin_programs().get(&message.program_id) else { - return Err(NssaError::InvalidInput("Unknown program".into())); - }; - - // // Execute program - let post_states = program.execute(&pre_states, &message.instruction_data)?; - - // Verify execution corresponds to a well-behaved program. - // See the # Programs section for the definition of the `validate_execution` method. - if !validate_execution(&pre_states, &post_states, message.program_id) { - return Err(NssaError::InvalidProgramBehavior); - } - - Ok(message.addresses.iter().cloned().zip(post_states).collect()) - } -} +pub use transaction::PublicTransaction; #[cfg(test)] -mod tests { - use std::io::Cursor; +pub use transaction::tests; - use crate::{ - Address, PrivateKey, PublicKey, PublicTransaction, - program::Program, - public_transaction::{Message, WitnessSet}, - }; - - #[test] - fn test_public_transaction_encoding_bytes_roundtrip() { - let key1 = PrivateKey::try_new([1; 32]).unwrap(); - let key2 = PrivateKey::try_new([2; 32]).unwrap(); - let addr1 = Address::from_public_key(&PublicKey::new(&key1)); - let addr2 = Address::from_public_key(&PublicKey::new(&key2)); - let nonces = vec![5, 99]; - let instruction = 1337; - let message = Message::try_new( - Program::authenticated_transfer_program().id(), - vec![addr1, addr2], - nonces, - instruction, - ) - .unwrap(); - - let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); - let tx = PublicTransaction::new(message, witness_set); - - let bytes = tx.to_bytes(); - let tx_from_bytes = PublicTransaction::from_bytes(&bytes).unwrap(); - assert_eq!(tx, tx_from_bytes); - } -} diff --git a/nssa/src/public_transaction/transaction.rs b/nssa/src/public_transaction/transaction.rs new file mode 100644 index 0000000..8d6bc47 --- /dev/null +++ b/nssa/src/public_transaction/transaction.rs @@ -0,0 +1,183 @@ +use std::collections::{HashMap, HashSet}; + +use nssa_core::{ + account::{Account, AccountWithMetadata}, + program::validate_execution, +}; +use sha2::{Digest, digest::FixedOutput}; + +use crate::{ + V01State, + address::Address, + error::NssaError, + public_transaction::{Message, WitnessSet}, +}; + + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PublicTransaction { + message: Message, + witness_set: WitnessSet, +} + +impl PublicTransaction { + pub fn new(message: Message, witness_set: WitnessSet) -> Self { + Self { + message, + witness_set, + } + } + + pub fn message(&self) -> &Message { + &self.message + } + + pub fn witness_set(&self) -> &WitnessSet { + &self.witness_set + } + + pub(crate) fn signer_addresses(&self) -> Vec
{ + self.witness_set + .iter_signatures() + .map(|(_, public_key)| Address::from_public_key(public_key)) + .collect() + } + + pub fn hash(&self) -> [u8; 32] { + let bytes = self.to_bytes(); + let mut hasher = sha2::Sha256::new(); + hasher.update(&bytes); + hasher.finalize_fixed().into() + } + + pub(crate) fn validate_and_compute_post_states( + &self, + state: &V01State, + ) -> Result, NssaError> { + let message = self.message(); + let witness_set = self.witness_set(); + + // All addresses must be different + if message.addresses.iter().collect::>().len() != message.addresses.len() { + return Err(NssaError::InvalidInput( + "Duplicate addresses found in message".into(), + )); + } + + if message.nonces.len() != witness_set.signatures_and_public_keys.len() { + return Err(NssaError::InvalidInput( + "Mismatch between number of nonces and signatures/public keys".into(), + )); + } + + // Check the signatures are valid + if !witness_set.is_valid_for(message) { + return Err(NssaError::InvalidInput( + "Invalid signature for given message and public key".into(), + )); + } + + let signer_addresses = self.signer_addresses(); + // Check nonces corresponds to the current nonces on the public state. + for (address, nonce) in signer_addresses.iter().zip(&message.nonces) { + let current_nonce = state.get_account_by_address(address).nonce; + if current_nonce != *nonce { + return Err(NssaError::InvalidInput("Nonce mismatch".into())); + } + } + + // Build pre_states for execution + let pre_states: Vec<_> = message + .addresses + .iter() + .map(|address| AccountWithMetadata { + account: state.get_account_by_address(address), + is_authorized: signer_addresses.contains(address), + }) + .collect(); + + // Check the `program_id` corresponds to a built-in program + // Only allowed program so far is the authenticated transfer program + let Some(program) = state.builtin_programs().get(&message.program_id) else { + return Err(NssaError::InvalidInput("Unknown program".into())); + }; + + // // Execute program + let post_states = program.execute(&pre_states, &message.instruction_data)?; + + // Verify execution corresponds to a well-behaved program. + // See the # Programs section for the definition of the `validate_execution` method. + if !validate_execution(&pre_states, &post_states, message.program_id) { + return Err(NssaError::InvalidProgramBehavior); + } + + Ok(message.addresses.iter().cloned().zip(post_states).collect()) + } +} + +#[cfg(test)] +pub mod tests { + use crate::{ + Address, PrivateKey, PublicKey, PublicTransaction, + program::Program, + public_transaction::{Message, WitnessSet, witness_set}, + }; + + pub fn transaction_for_tests() -> PublicTransaction { + let key1 = PrivateKey::try_new([1; 32]).unwrap(); + let key2 = PrivateKey::try_new([2; 32]).unwrap(); + let addr1 = Address::from_public_key(&PublicKey::new(&key1)); + let addr2 = Address::from_public_key(&PublicKey::new(&key2)); + let nonces = vec![5, 99]; + let instruction = 1337; + let message = Message::try_new( + Program::authenticated_transfer_program().id(), + vec![addr1, addr2], + nonces, + instruction, + ) + .unwrap(); + + let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); + PublicTransaction::new(message, witness_set) + } + + #[test] + fn test_new_constructor() { + let tx = transaction_for_tests(); + let message = tx.message().clone(); + let witness_set = tx.witness_set().clone(); + let tx_from_constructor = PublicTransaction::new(message.clone(), witness_set.clone()); + assert_eq!(tx_from_constructor.message, message); + assert_eq!(tx_from_constructor.witness_set, witness_set); + } + + #[test] + fn test_message_getter() { + let tx = transaction_for_tests(); + assert_eq!(&tx.message, tx.message()); + } + + #[test] + fn test_witness_set_getter() { + let tx = transaction_for_tests(); + assert_eq!(&tx.witness_set, tx.witness_set()); + } + + #[test] + fn test_signer_addresses() { + let tx = transaction_for_tests(); + let expected_signer_addresses = vec![ + Address::new([ + 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, + 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, + ]), + Address::new([ + 77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, + 234, 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102, + ]), + ]; + let signer_addresses = tx.signer_addresses(); + assert_eq!(signer_addresses, expected_signer_addresses); + } +} diff --git a/nssa/src/tests/mod.rs b/nssa/src/tests/mod.rs index 70ae5bd..46fd6f8 100644 --- a/nssa/src/tests/mod.rs +++ b/nssa/src/tests/mod.rs @@ -3,3 +3,4 @@ mod program_tests; mod signature_tests; mod state_tests; mod valid_execution_tests; +mod public_transaction_tests; diff --git a/nssa/src/tests/public_transaction_tests.rs b/nssa/src/tests/public_transaction_tests.rs new file mode 100644 index 0000000..3401857 --- /dev/null +++ b/nssa/src/tests/public_transaction_tests.rs @@ -0,0 +1,28 @@ +use sha2::{Digest, digest::FixedOutput}; + +use crate::{ + Address, PrivateKey, PublicKey, PublicTransaction, + program::Program, + public_transaction::{Message, WitnessSet, tests::transaction_for_tests}, +}; + +#[test] +fn test_public_transaction_encoding_bytes_roundtrip() { + let tx = transaction_for_tests(); + let bytes = tx.to_bytes(); + let tx_from_bytes = PublicTransaction::from_bytes(&bytes).unwrap(); + assert_eq!(tx, tx_from_bytes); +} + +#[test] +fn test_hash_is_sha256_of_transaction_bytes() { + let tx = transaction_for_tests(); + let hash = tx.hash(); + let expected_hash: [u8; 32] = { + let bytes = tx.to_bytes(); + let mut hasher = sha2::Sha256::new(); + hasher.update(&bytes); + hasher.finalize_fixed().into() + }; + assert_eq!(hash, expected_hash); +} From d0a1cd5fa09b2e91ee88409d9e08112bfd5f03c2 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 13 Aug 2025 00:15:22 -0300 Subject: [PATCH 47/58] add tests --- nssa/src/public_transaction/encoding.rs | 2 +- nssa/src/public_transaction/mod.rs | 4 - nssa/src/public_transaction/transaction.rs | 70 +-------- nssa/src/tests/public_transaction_tests.rs | 171 ++++++++++++++++++++- 4 files changed, 172 insertions(+), 75 deletions(-) diff --git a/nssa/src/public_transaction/encoding.rs b/nssa/src/public_transaction/encoding.rs index 371f1b5..9e7c550 100644 --- a/nssa/src/public_transaction/encoding.rs +++ b/nssa/src/public_transaction/encoding.rs @@ -121,7 +121,7 @@ impl WitnessSet { impl PublicTransaction { pub fn to_bytes(&self) -> Vec { - let mut bytes = self.message().to_bytes(); + let mut bytes = self.message.to_bytes(); bytes.extend_from_slice(&self.witness_set().to_bytes()); bytes } diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs index ce9ae9e..bd2f760 100644 --- a/nssa/src/public_transaction/mod.rs +++ b/nssa/src/public_transaction/mod.rs @@ -7,7 +7,3 @@ mod transaction; pub use message::Message; pub use witness_set::WitnessSet; pub use transaction::PublicTransaction; - -#[cfg(test)] -pub use transaction::tests; - diff --git a/nssa/src/public_transaction/transaction.rs b/nssa/src/public_transaction/transaction.rs index 8d6bc47..59f8bd8 100644 --- a/nssa/src/public_transaction/transaction.rs +++ b/nssa/src/public_transaction/transaction.rs @@ -16,8 +16,8 @@ use crate::{ #[derive(Debug, Clone, PartialEq, Eq)] pub struct PublicTransaction { - message: Message, - witness_set: WitnessSet, + pub(crate) message: Message, + pub(crate) witness_set: WitnessSet, } impl PublicTransaction { @@ -115,69 +115,3 @@ impl PublicTransaction { } } -#[cfg(test)] -pub mod tests { - use crate::{ - Address, PrivateKey, PublicKey, PublicTransaction, - program::Program, - public_transaction::{Message, WitnessSet, witness_set}, - }; - - pub fn transaction_for_tests() -> PublicTransaction { - let key1 = PrivateKey::try_new([1; 32]).unwrap(); - let key2 = PrivateKey::try_new([2; 32]).unwrap(); - let addr1 = Address::from_public_key(&PublicKey::new(&key1)); - let addr2 = Address::from_public_key(&PublicKey::new(&key2)); - let nonces = vec![5, 99]; - let instruction = 1337; - let message = Message::try_new( - Program::authenticated_transfer_program().id(), - vec![addr1, addr2], - nonces, - instruction, - ) - .unwrap(); - - let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); - PublicTransaction::new(message, witness_set) - } - - #[test] - fn test_new_constructor() { - let tx = transaction_for_tests(); - let message = tx.message().clone(); - let witness_set = tx.witness_set().clone(); - let tx_from_constructor = PublicTransaction::new(message.clone(), witness_set.clone()); - assert_eq!(tx_from_constructor.message, message); - assert_eq!(tx_from_constructor.witness_set, witness_set); - } - - #[test] - fn test_message_getter() { - let tx = transaction_for_tests(); - assert_eq!(&tx.message, tx.message()); - } - - #[test] - fn test_witness_set_getter() { - let tx = transaction_for_tests(); - assert_eq!(&tx.witness_set, tx.witness_set()); - } - - #[test] - fn test_signer_addresses() { - let tx = transaction_for_tests(); - let expected_signer_addresses = vec![ - Address::new([ - 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, - 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, - ]), - Address::new([ - 77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, - 234, 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102, - ]), - ]; - let signer_addresses = tx.signer_addresses(); - assert_eq!(signer_addresses, expected_signer_addresses); - } -} diff --git a/nssa/src/tests/public_transaction_tests.rs b/nssa/src/tests/public_transaction_tests.rs index 3401857..07c3c80 100644 --- a/nssa/src/tests/public_transaction_tests.rs +++ b/nssa/src/tests/public_transaction_tests.rs @@ -1,11 +1,81 @@ use sha2::{Digest, digest::FixedOutput}; use crate::{ - Address, PrivateKey, PublicKey, PublicTransaction, + Address, PrivateKey, PublicKey, PublicTransaction, Signature, V01State, + error::NssaError, program::Program, - public_transaction::{Message, WitnessSet, tests::transaction_for_tests}, + public_transaction::{Message, WitnessSet}, }; +fn keys_for_tests() -> (PrivateKey, PrivateKey, Address, Address) { + let key1 = PrivateKey::try_new([1; 32]).unwrap(); + let key2 = PrivateKey::try_new([2; 32]).unwrap(); + let addr1 = Address::from_public_key(&PublicKey::new(&key1)); + let addr2 = Address::from_public_key(&PublicKey::new(&key2)); + (key1, key2, addr1, addr2) +} + +fn state_for_tests() -> V01State { + let (key1, key2, addr1, addr2) = keys_for_tests(); + let initial_data = [(*addr1.value(), 10000), (*addr2.value(), 20000)]; + V01State::new_with_genesis_accounts(&initial_data) +} + +fn transaction_for_tests() -> PublicTransaction { + let (key1, key2, addr1, addr2) = keys_for_tests(); + let nonces = vec![0, 0]; + let instruction = 1337; + let message = Message::try_new( + Program::authenticated_transfer_program().id(), + vec![addr1, addr2], + nonces, + instruction, + ) + .unwrap(); + + let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); + PublicTransaction::new(message, witness_set) +} + +#[test] +fn test_new_constructor() { + let tx = transaction_for_tests(); + let message = tx.message().clone(); + let witness_set = tx.witness_set().clone(); + let tx_from_constructor = PublicTransaction::new(message.clone(), witness_set.clone()); + assert_eq!(tx_from_constructor.message, message); + assert_eq!(tx_from_constructor.witness_set, witness_set); +} + +#[test] +fn test_message_getter() { + let tx = transaction_for_tests(); + assert_eq!(&tx.message, tx.message()); +} + +#[test] +fn test_witness_set_getter() { + let tx = transaction_for_tests(); + assert_eq!(&tx.witness_set, tx.witness_set()); +} + +#[test] +fn test_signer_addresses() { + let tx = transaction_for_tests(); + let expected_signer_addresses = vec![ + Address::new([ + 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, + 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, + ]), + Address::new([ + 77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, + 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102, + ]), + ]; + let signer_addresses = tx.signer_addresses(); + assert_eq!(signer_addresses, expected_signer_addresses); +} + #[test] fn test_public_transaction_encoding_bytes_roundtrip() { let tx = transaction_for_tests(); @@ -26,3 +96,100 @@ fn test_hash_is_sha256_of_transaction_bytes() { }; assert_eq!(hash, expected_hash); } + +#[test] +fn test_address_list_cant_have_duplicates() { + let (key1, _, addr1, _) = keys_for_tests(); + let state = state_for_tests(); + let nonces = vec![0, 0]; + let instruction = 1337; + let message = Message::try_new( + Program::authenticated_transfer_program().id(), + vec![addr1.clone(), addr1], + nonces, + instruction, + ) + .unwrap(); + + let witness_set = WitnessSet::for_message(&message, &[&key1, &key1]); + let tx = PublicTransaction::new(message, witness_set); + let result = tx.validate_and_compute_post_states(&state); + assert!(matches!(result, Err(NssaError::InvalidInput(_)))) +} + +#[test] +fn test_number_of_nonces_must_match_number_of_signatures() { + let (key1, key2, addr1, addr2) = keys_for_tests(); + let state = state_for_tests(); + let nonces = vec![0]; + let instruction = 1337; + let message = Message::try_new( + Program::authenticated_transfer_program().id(), + vec![addr1, addr2], + nonces, + instruction, + ) + .unwrap(); + + let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); + let tx = PublicTransaction::new(message, witness_set); + let result = tx.validate_and_compute_post_states(&state); + assert!(matches!(result, Err(NssaError::InvalidInput(_)))) +} + +#[test] +fn test_all_signatures_must_be_valid() { + let (key1, key2, addr1, addr2) = keys_for_tests(); + let state = state_for_tests(); + let nonces = vec![0, 0]; + let instruction = 1337; + let message = Message::try_new( + Program::authenticated_transfer_program().id(), + vec![addr1, addr2], + nonces, + instruction, + ) + .unwrap(); + + let mut witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); + witness_set.signatures_and_public_keys[0].0 = Signature { value: [1; 64] }; + let tx = PublicTransaction::new(message, witness_set); + let result = tx.validate_and_compute_post_states(&state); + assert!(matches!(result, Err(NssaError::InvalidInput(_)))) +} + +#[test] +fn test_nonces_must_match_the_state_current_nonces() { + let (key1, key2, addr1, addr2) = keys_for_tests(); + let state = state_for_tests(); + let nonces = vec![0, 1]; + let instruction = 1337; + let message = Message::try_new( + Program::authenticated_transfer_program().id(), + vec![addr1, addr2], + nonces, + instruction, + ) + .unwrap(); + + let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); + let tx = PublicTransaction::new(message, witness_set); + let result = tx.validate_and_compute_post_states(&state); + assert!(matches!(result, Err(NssaError::InvalidInput(_)))) +} + +#[test] +fn test_program_id_must_belong_to_bulitin_program_ids() { + let (key1, key2, addr1, addr2) = keys_for_tests(); + let state = state_for_tests(); + let nonces = vec![0, 0]; + let instruction = 1337; + let unknown_program_id = [0xdeadbeef; 8]; + let message = + Message::try_new(unknown_program_id, vec![addr1, addr2], nonces, instruction).unwrap(); + + let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); + let tx = PublicTransaction::new(message, witness_set); + let result = tx.validate_and_compute_post_states(&state); + assert!(matches!(result, Err(NssaError::InvalidInput(_)))) +} From 24a3165634ab2a4599e14c5db8fffcfaf10b0b6d Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 13 Aug 2025 00:43:17 -0300 Subject: [PATCH 48/58] add tests --- nssa/src/program.rs | 1 + nssa/src/state.rs | 4 +- nssa/src/tests/state_tests.rs | 95 ++++++++++++++++++++++++++++++++++- 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 5b4a1e8..5baa996 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -8,6 +8,7 @@ use serde::Serialize; use crate::error::NssaError; +#[derive(Debug, PartialEq, Eq)] pub struct Program { pub(crate) id: ProgramId, pub(crate) elf: &'static [u8], diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 690919f..343fd6e 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -5,8 +5,8 @@ use nssa_core::{account::Account, program::ProgramId}; use std::collections::HashMap; pub struct V01State { - public_state: HashMap, - builtin_programs: HashMap, + pub(crate) public_state: HashMap, + pub(crate) builtin_programs: HashMap, } impl V01State { diff --git a/nssa/src/tests/state_tests.rs b/nssa/src/tests/state_tests.rs index 5acbed8..acf81a2 100644 --- a/nssa/src/tests/state_tests.rs +++ b/nssa/src/tests/state_tests.rs @@ -1,8 +1,10 @@ +use std::collections::HashMap; + use crate::{ Address, PublicKey, PublicTransaction, V01State, error::NssaError, program::Program, public_transaction, signature::PrivateKey, }; -use nssa_core::account::Account; +use nssa_core::{account::Account, program::ProgramId}; fn transfer_transaction( from: Address, @@ -20,6 +22,97 @@ fn transfer_transaction( PublicTransaction::new(message, witness_set) } +#[test] +fn test_new_with_genesis() { + let key1 = PrivateKey::try_new([1; 32]).unwrap(); + let key2 = PrivateKey::try_new([2; 32]).unwrap(); + let addr1 = Address::from_public_key(&PublicKey::new(&key1)); + let addr2 = Address::from_public_key(&PublicKey::new(&key2)); + let initial_data = [(*addr1.value(), 100u128), (*addr2.value(), 151u128)]; + let program = Program::authenticated_transfer_program(); + let expected_public_state = { + let mut this = HashMap::new(); + this.insert( + addr1, + Account { + balance: 100, + program_owner: program.id(), + ..Account::default() + }, + ); + this.insert( + addr2, + Account { + balance: 151, + program_owner: program.id(), + ..Account::default() + }, + ); + this + }; + let expected_builtin_programs = { + let mut this = HashMap::new(); + this.insert(program.id(), program); + this + }; + + let state = V01State::new_with_genesis_accounts(&initial_data); + + assert_eq!(state.public_state, expected_public_state); + assert_eq!(state.builtin_programs, expected_builtin_programs); +} + +#[test] +fn test_insert_program() { + let mut state = V01State::new_with_genesis_accounts(&[]); + let program_to_insert = Program::simple_balance_transfer(); + let program_id = program_to_insert.id(); + assert!(!state.builtin_programs.contains_key(&program_id)); + + state.insert_program(program_to_insert); + + assert!(state.builtin_programs.contains_key(&program_id)); +} + +#[test] +fn test_get_account_by_address_non_default_account() { + let key = PrivateKey::try_new([1; 32]).unwrap(); + let addr = Address::from_public_key(&PublicKey::new(&key)); + let initial_data = [(*addr.value(), 100u128)]; + let state = V01State::new_with_genesis_accounts(&initial_data); + let expected_account = state.public_state.get(&addr).unwrap(); + + let account = state.get_account_by_address(&addr); + + assert_eq!(&account, expected_account); +} + +#[test] +fn test_get_account_by_address_default_account() { + let addr2 = Address::new([0; 32]); + let state = V01State::new_with_genesis_accounts(&[]); + let expected_account = Account::default(); + + let account = state.get_account_by_address(&addr2); + + assert_eq!(account, expected_account); +} + +#[test] +fn test_builtin_programs_getter() { + let state = V01State::new_with_genesis_accounts(&[]); + let program = Program::authenticated_transfer_program(); + let expected_builtin_programs = { + let mut this = HashMap::new(); + this.insert(program.id(), program); + this + }; + + let builtin_programs = state.builtin_programs(); + + assert_eq!(builtin_programs, &state.builtin_programs); +} + #[test] fn transition_from_authenticated_transfer_program_invocation_default_account_destination() { let key = PrivateKey::try_new([1; 32]).unwrap(); From acaf62f31f32a7fbb20d059c8c21557b166b0ac2 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 13 Aug 2025 00:43:38 -0300 Subject: [PATCH 49/58] fmt clippy --- nssa/src/public_transaction/encoding.rs | 6 +-- nssa/src/public_transaction/mod.rs | 5 +-- nssa/src/public_transaction/transaction.rs | 2 - nssa/src/signature/mod.rs | 39 ++++++++++++++++++- nssa/src/signature/signature.rs | 38 ------------------ nssa/src/tests/mod.rs | 2 +- nssa/src/tests/public_transaction_tests.rs | 2 +- nssa/src/tests/state_tests.rs | 8 +--- .../guest/src/bin/minter.rs | 3 +- 9 files changed, 46 insertions(+), 59 deletions(-) delete mode 100644 nssa/src/signature/signature.rs diff --git a/nssa/src/public_transaction/encoding.rs b/nssa/src/public_transaction/encoding.rs index 9e7c550..d63a95e 100644 --- a/nssa/src/public_transaction/encoding.rs +++ b/nssa/src/public_transaction/encoding.rs @@ -55,8 +55,8 @@ impl Message { assert_eq!(&prefix, MESSAGE_ENCODING_PREFIX); let program_id: ProgramId = { let mut this = [0u32; 8]; - for i in 0..8 { - this[i] = u32_from_cursor(cursor)?; + for item in &mut this { + *item = u32_from_cursor(cursor)?; } this }; @@ -128,7 +128,7 @@ impl PublicTransaction { pub fn from_bytes(bytes: &[u8]) -> Result { let mut cursor = Cursor::new(bytes); - Ok(Self::from_cursor(&mut cursor)?) + Self::from_cursor(&mut cursor) } pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs index bd2f760..9ae24cf 100644 --- a/nssa/src/public_transaction/mod.rs +++ b/nssa/src/public_transaction/mod.rs @@ -1,9 +1,8 @@ - mod encoding; mod message; -mod witness_set; mod transaction; +mod witness_set; pub use message::Message; -pub use witness_set::WitnessSet; pub use transaction::PublicTransaction; +pub use witness_set::WitnessSet; diff --git a/nssa/src/public_transaction/transaction.rs b/nssa/src/public_transaction/transaction.rs index 59f8bd8..dccb4e3 100644 --- a/nssa/src/public_transaction/transaction.rs +++ b/nssa/src/public_transaction/transaction.rs @@ -13,7 +13,6 @@ use crate::{ public_transaction::{Message, WitnessSet}, }; - #[derive(Debug, Clone, PartialEq, Eq)] pub struct PublicTransaction { pub(crate) message: Message, @@ -114,4 +113,3 @@ impl PublicTransaction { Ok(message.addresses.iter().cloned().zip(post_states).collect()) } } - diff --git a/nssa/src/signature/mod.rs b/nssa/src/signature/mod.rs index 55ed41e..37b878c 100644 --- a/nssa/src/signature/mod.rs +++ b/nssa/src/signature/mod.rs @@ -1,8 +1,43 @@ mod encoding; mod private_key; mod public_key; -mod signature; pub use private_key::PrivateKey; pub use public_key::PublicKey; -pub use signature::Signature; + +use rand::{RngCore, rngs::OsRng}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Signature { + pub(crate) value: [u8; 64], +} + +impl Signature { + pub fn new(key: &PrivateKey, message: &[u8]) -> Self { + let mut aux_random = [0u8; 32]; + OsRng.fill_bytes(&mut aux_random); + Self::new_with_aux_random(key, message, aux_random) + } + + pub(crate) fn new_with_aux_random( + key: &PrivateKey, + message: &[u8], + aux_random: [u8; 32], + ) -> Self { + let value = { + let secp = secp256k1::Secp256k1::new(); + let secret_key = secp256k1::SecretKey::from_byte_array(key.0).unwrap(); + let keypair = secp256k1::Keypair::from_secret_key(&secp, &secret_key); + let signature = secp.sign_schnorr_with_aux_rand(message, &keypair, &aux_random); + signature.to_byte_array() + }; + Self { value } + } + + pub fn is_valid_for(&self, bytes: &[u8], public_key: &PublicKey) -> bool { + let pk = secp256k1::XOnlyPublicKey::from_byte_array(public_key.0).unwrap(); + let secp = secp256k1::Secp256k1::new(); + let sig = secp256k1::schnorr::Signature::from_byte_array(self.value); + secp.verify_schnorr(&sig, bytes, &pk).is_ok() + } +} diff --git a/nssa/src/signature/signature.rs b/nssa/src/signature/signature.rs deleted file mode 100644 index c49e18b..0000000 --- a/nssa/src/signature/signature.rs +++ /dev/null @@ -1,38 +0,0 @@ -use rand::{RngCore, rngs::OsRng}; - -use crate::{PrivateKey, PublicKey}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Signature { - pub(crate) value: [u8; 64], -} - -impl Signature { - pub fn new(key: &PrivateKey, message: &[u8]) -> Self { - let mut aux_random = [0u8; 32]; - OsRng.fill_bytes(&mut aux_random); - Self::new_with_aux_random(key, message, aux_random) - } - - pub(crate) fn new_with_aux_random( - key: &PrivateKey, - message: &[u8], - aux_random: [u8; 32], - ) -> Self { - let value = { - let secp = secp256k1::Secp256k1::new(); - let secret_key = secp256k1::SecretKey::from_byte_array(key.0).unwrap(); - let keypair = secp256k1::Keypair::from_secret_key(&secp, &secret_key); - let signature = secp.sign_schnorr_with_aux_rand(message, &keypair, &aux_random); - signature.to_byte_array() - }; - Self { value } - } - - pub fn is_valid_for(&self, bytes: &[u8], public_key: &PublicKey) -> bool { - let pk = secp256k1::XOnlyPublicKey::from_byte_array(public_key.0).unwrap(); - let secp = secp256k1::Secp256k1::new(); - let sig = secp256k1::schnorr::Signature::from_byte_array(self.value); - secp.verify_schnorr(&sig, bytes, &pk).is_ok() - } -} diff --git a/nssa/src/tests/mod.rs b/nssa/src/tests/mod.rs index 46fd6f8..a2d8eef 100644 --- a/nssa/src/tests/mod.rs +++ b/nssa/src/tests/mod.rs @@ -1,6 +1,6 @@ mod bip340_test_vectors; mod program_tests; +mod public_transaction_tests; mod signature_tests; mod state_tests; mod valid_execution_tests; -mod public_transaction_tests; diff --git a/nssa/src/tests/public_transaction_tests.rs b/nssa/src/tests/public_transaction_tests.rs index 07c3c80..dd5c1cb 100644 --- a/nssa/src/tests/public_transaction_tests.rs +++ b/nssa/src/tests/public_transaction_tests.rs @@ -16,7 +16,7 @@ fn keys_for_tests() -> (PrivateKey, PrivateKey, Address, Address) { } fn state_for_tests() -> V01State { - let (key1, key2, addr1, addr2) = keys_for_tests(); + let (_, _, addr1, addr2) = keys_for_tests(); let initial_data = [(*addr1.value(), 10000), (*addr2.value(), 20000)]; V01State::new_with_genesis_accounts(&initial_data) } diff --git a/nssa/src/tests/state_tests.rs b/nssa/src/tests/state_tests.rs index acf81a2..7324f8f 100644 --- a/nssa/src/tests/state_tests.rs +++ b/nssa/src/tests/state_tests.rs @@ -4,7 +4,7 @@ use crate::{ Address, PublicKey, PublicTransaction, V01State, error::NssaError, program::Program, public_transaction, signature::PrivateKey, }; -use nssa_core::{account::Account, program::ProgramId}; +use nssa_core::account::Account; fn transfer_transaction( from: Address, @@ -101,12 +101,6 @@ fn test_get_account_by_address_default_account() { #[test] fn test_builtin_programs_getter() { let state = V01State::new_with_genesis_accounts(&[]); - let program = Program::authenticated_transfer_program(); - let expected_builtin_programs = { - let mut this = HashMap::new(); - this.insert(program.id(), program); - this - }; let builtin_programs = state.builtin_programs(); diff --git a/nssa/test_program_methods/guest/src/bin/minter.rs b/nssa/test_program_methods/guest/src/bin/minter.rs index 9e089a5..b82d9e9 100644 --- a/nssa/test_program_methods/guest/src/bin/minter.rs +++ b/nssa/test_program_methods/guest/src/bin/minter.rs @@ -1,4 +1,4 @@ -use nssa_core::{account::Account, program::read_nssa_inputs}; +use nssa_core::program::read_nssa_inputs; use risc0_zkvm::guest::env; type Instruction = (); @@ -17,4 +17,3 @@ fn main() { env::commit(&vec![account_post]); } - From 504eb008191db7b6a479a9fe2f2fafb6b80b02b6 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 13 Aug 2025 01:33:11 -0300 Subject: [PATCH 50/58] refactor tests --- accounts/src/account_core/mod.rs | 4 +- accounts/src/key_management/mod.rs | 2 +- nssa/src/address.rs | 4 +- nssa/src/error.rs | 3 + nssa/src/lib.rs | 2 - nssa/src/program.rs | 131 ++++- nssa/src/public_transaction/encoding.rs | 2 +- nssa/src/public_transaction/mod.rs | 1 + nssa/src/public_transaction/transaction.rs | 203 ++++++- nssa/src/public_transaction/witness_set.rs | 2 +- .../bip340_test_vectors.rs | 108 ++-- nssa/src/signature/encoding.rs | 4 +- nssa/src/signature/mod.rs | 50 +- nssa/src/signature/private_key.rs | 6 +- nssa/src/signature/public_key.rs | 39 +- nssa/src/state.rs | 498 +++++++++++++++++- nssa/src/tests/mod.rs | 6 - nssa/src/tests/program_tests.rs | 123 ----- nssa/src/tests/public_transaction_tests.rs | 195 ------- nssa/src/tests/signature_tests.rs | 53 -- nssa/src/tests/state_tests.rs | 198 ------- nssa/src/tests/valid_execution_tests.rs | 289 ---------- 22 files changed, 981 insertions(+), 942 deletions(-) rename nssa/src/{tests => signature}/bip340_test_vectors.rs (86%) delete mode 100644 nssa/src/tests/mod.rs delete mode 100644 nssa/src/tests/program_tests.rs delete mode 100644 nssa/src/tests/public_transaction_tests.rs delete mode 100644 nssa/src/tests/signature_tests.rs delete mode 100644 nssa/src/tests/state_tests.rs delete mode 100644 nssa/src/tests/valid_execution_tests.rs diff --git a/accounts/src/account_core/mod.rs b/accounts/src/account_core/mod.rs index 58154f0..3d7fe88 100644 --- a/accounts/src/account_core/mod.rs +++ b/accounts/src/account_core/mod.rs @@ -117,7 +117,7 @@ impl AccountPublicMask { impl Account { pub fn new() -> Self { let key_holder = AddressKeyHolder::new_os_random(); - let public_key = nssa::PublicKey::new(key_holder.get_pub_account_signing_key()); + let public_key = nssa::PublicKey::new_from_private_key(key_holder.get_pub_account_signing_key()); let address = nssa::Address::from_public_key(&public_key); let balance = 0; let utxos = HashMap::new(); @@ -132,7 +132,7 @@ impl Account { pub fn new_with_balance(balance: u64) -> Self { let key_holder = AddressKeyHolder::new_os_random(); - let public_key = nssa::PublicKey::new(key_holder.get_pub_account_signing_key()); + let public_key = nssa::PublicKey::new_from_private_key(key_holder.get_pub_account_signing_key()); let address = nssa::Address::from_public_key(&public_key); let utxos = HashMap::new(); diff --git a/accounts/src/key_management/mod.rs b/accounts/src/key_management/mod.rs index e8444e7..a9cb196 100644 --- a/accounts/src/key_management/mod.rs +++ b/accounts/src/key_management/mod.rs @@ -341,7 +341,7 @@ mod tests { } }; - let public_key = nssa::PublicKey::new(&pub_account_signing_key); + let public_key = nssa::PublicKey::new_from_private_key(&pub_account_signing_key); let address = nssa::Address::from_public_key(&public_key); diff --git a/nssa/src/address.rs b/nssa/src/address.rs index eeb9102..4aa4c45 100644 --- a/nssa/src/address.rs +++ b/nssa/src/address.rs @@ -4,7 +4,7 @@ use crate::signature::PublicKey; #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] pub struct Address { - pub(crate) value: [u8; 32], + value: [u8; 32], } impl Address { @@ -14,7 +14,7 @@ impl Address { pub fn from_public_key(public_key: &PublicKey) -> Self { // TODO: Check specs - Address::new(public_key.0) + Address::new(*public_key.value()) } pub fn value(&self) -> &[u8; 32] { diff --git a/nssa/src/error.rs b/nssa/src/error.rs index 5e173b5..11a2f41 100644 --- a/nssa/src/error.rs +++ b/nssa/src/error.rs @@ -21,4 +21,7 @@ pub enum NssaError { #[error("IO error: {0}")] Io(#[from] io::Error), + + #[error("Invalid Public Key")] + InvalidPublicKey, } diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs index bde852c..bcf536e 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -12,5 +12,3 @@ pub use signature::PublicKey; pub use signature::Signature; pub use state::V01State; -#[cfg(test)] -mod tests; diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 5baa996..e2762ba 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -10,8 +10,8 @@ use crate::error::NssaError; #[derive(Debug, PartialEq, Eq)] pub struct Program { - pub(crate) id: ProgramId, - pub(crate) elf: &'static [u8], + id: ProgramId, + elf: &'static [u8], } impl Program { @@ -77,3 +77,130 @@ impl Program { } } } + +#[cfg(test)] +mod tests { + use nssa_core::account::{Account, AccountWithMetadata}; + + use crate::program::Program; + + impl Program { + /// A program that changes the nonce of an account + pub fn nonce_changer_program() -> Self { + use test_program_methods::{NONCE_CHANGER_ELF, NONCE_CHANGER_ID}; + + Program { + id: NONCE_CHANGER_ID, + elf: NONCE_CHANGER_ELF, + } + } + + /// A program that produces more output accounts than the inputs it received + pub fn extra_output_program() -> Self { + use test_program_methods::{EXTRA_OUTPUT_ELF, EXTRA_OUTPUT_ID}; + + Program { + id: EXTRA_OUTPUT_ID, + elf: EXTRA_OUTPUT_ELF, + } + } + + /// A program that produces less output accounts than the inputs it received + pub fn missing_output_program() -> Self { + use test_program_methods::{MISSING_OUTPUT_ELF, MISSING_OUTPUT_ID}; + + Program { + id: MISSING_OUTPUT_ID, + elf: MISSING_OUTPUT_ELF, + } + } + + /// A program that changes the program owner of an account to [0, 1, 2, 3, 4, 5, 6, 7] + pub fn program_owner_changer() -> Self { + use test_program_methods::{PROGRAM_OWNER_CHANGER_ELF, PROGRAM_OWNER_CHANGER_ID}; + + Program { + id: PROGRAM_OWNER_CHANGER_ID, + elf: PROGRAM_OWNER_CHANGER_ELF, + } + } + + /// A program that transfers balance without caring about authorizations + pub fn simple_balance_transfer() -> Self { + use test_program_methods::{SIMPLE_BALANCE_TRANSFER_ELF, SIMPLE_BALANCE_TRANSFER_ID}; + + Program { + id: SIMPLE_BALANCE_TRANSFER_ID, + elf: SIMPLE_BALANCE_TRANSFER_ELF, + } + } + + /// A program that modifies the data of an account + pub fn data_changer() -> Self { + use test_program_methods::{DATA_CHANGER_ELF, DATA_CHANGER_ID}; + + Program { + id: DATA_CHANGER_ID, + elf: DATA_CHANGER_ELF, + } + } + + /// A program that mints balance + pub fn minter() -> Self { + use test_program_methods::{MINTER_ELF, MINTER_ID}; + + Program { + id: MINTER_ID, + elf: MINTER_ELF, + } + } + + /// A program that burns balance + pub fn burner() -> Self { + use test_program_methods::{BURNER_ELF, BURNER_ID}; + + Program { + id: BURNER_ID, + elf: BURNER_ELF, + } + } + } + + #[test] + fn test_program_execution() { + let program = Program::simple_balance_transfer(); + let balance_to_move: u128 = 11223344556677; + let instruction_data = Program::serialize_instruction(balance_to_move).unwrap(); + let sender = AccountWithMetadata { + account: Account { + balance: 77665544332211, + ..Account::default() + }, + is_authorized: false, + }; + let recipient = AccountWithMetadata { + account: Account::default(), + is_authorized: false, + }; + + let expected_sender_post = Account { + balance: 77665544332211 - balance_to_move, + program_owner: program.id(), + ..Account::default() + }; + let expected_recipient_post = Account { + balance: balance_to_move, + // Program claims the account since the pre_state has default prorgam owner + program_owner: program.id(), + ..Account::default() + }; + let [sender_post, recipient_post] = program + .execute(&[sender, recipient], &instruction_data) + .unwrap() + .try_into() + .unwrap(); + + assert_eq!(sender_post, expected_sender_post); + assert_eq!(recipient_post, expected_recipient_post); + } +} diff --git a/nssa/src/public_transaction/encoding.rs b/nssa/src/public_transaction/encoding.rs index d63a95e..7f1ab62 100644 --- a/nssa/src/public_transaction/encoding.rs +++ b/nssa/src/public_transaction/encoding.rs @@ -121,7 +121,7 @@ impl WitnessSet { impl PublicTransaction { pub fn to_bytes(&self) -> Vec { - let mut bytes = self.message.to_bytes(); + let mut bytes = self.message().to_bytes(); bytes.extend_from_slice(&self.witness_set().to_bytes()); bytes } diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs index 9ae24cf..57456f3 100644 --- a/nssa/src/public_transaction/mod.rs +++ b/nssa/src/public_transaction/mod.rs @@ -6,3 +6,4 @@ mod witness_set; pub use message::Message; pub use transaction::PublicTransaction; pub use witness_set::WitnessSet; + diff --git a/nssa/src/public_transaction/transaction.rs b/nssa/src/public_transaction/transaction.rs index dccb4e3..7b5f065 100644 --- a/nssa/src/public_transaction/transaction.rs +++ b/nssa/src/public_transaction/transaction.rs @@ -15,8 +15,8 @@ use crate::{ #[derive(Debug, Clone, PartialEq, Eq)] pub struct PublicTransaction { - pub(crate) message: Message, - pub(crate) witness_set: WitnessSet, + message: Message, + witness_set: WitnessSet, } impl PublicTransaction { @@ -113,3 +113,202 @@ impl PublicTransaction { Ok(message.addresses.iter().cloned().zip(post_states).collect()) } } + +#[cfg(test)] +mod tests { + use sha2::{Digest, digest::FixedOutput}; + + use crate::{ + Address, PrivateKey, PublicKey, PublicTransaction, Signature, V01State, + error::NssaError, + program::Program, + public_transaction::{Message, WitnessSet}, + }; + + fn keys_for_tests() -> (PrivateKey, PrivateKey, Address, Address) { + let key1 = PrivateKey::try_new([1; 32]).unwrap(); + let key2 = PrivateKey::try_new([2; 32]).unwrap(); + let addr1 = Address::from_public_key(&PublicKey::new_from_private_key(&key1)); + let addr2 = Address::from_public_key(&PublicKey::new_from_private_key(&key2)); + (key1, key2, addr1, addr2) + } + + fn state_for_tests() -> V01State { + let (_, _, addr1, addr2) = keys_for_tests(); + let initial_data = [(*addr1.value(), 10000), (*addr2.value(), 20000)]; + V01State::new_with_genesis_accounts(&initial_data) + } + + fn transaction_for_tests() -> PublicTransaction { + let (key1, key2, addr1, addr2) = keys_for_tests(); + let nonces = vec![0, 0]; + let instruction = 1337; + let message = Message::try_new( + Program::authenticated_transfer_program().id(), + vec![addr1, addr2], + nonces, + instruction, + ) + .unwrap(); + + let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); + PublicTransaction::new(message, witness_set) + } + + #[test] + fn test_new_constructor() { + let tx = transaction_for_tests(); + let message = tx.message().clone(); + let witness_set = tx.witness_set().clone(); + let tx_from_constructor = PublicTransaction::new(message.clone(), witness_set.clone()); + assert_eq!(tx_from_constructor.message, message); + assert_eq!(tx_from_constructor.witness_set, witness_set); + } + + #[test] + fn test_message_getter() { + let tx = transaction_for_tests(); + assert_eq!(&tx.message, tx.message()); + } + + #[test] + fn test_witness_set_getter() { + let tx = transaction_for_tests(); + assert_eq!(&tx.witness_set, tx.witness_set()); + } + + #[test] + fn test_signer_addresses() { + let tx = transaction_for_tests(); + let expected_signer_addresses = vec![ + Address::new([ + 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, + 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, + ]), + Address::new([ + 77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, + 234, 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102, + ]), + ]; + let signer_addresses = tx.signer_addresses(); + assert_eq!(signer_addresses, expected_signer_addresses); + } + + #[test] + fn test_public_transaction_encoding_bytes_roundtrip() { + let tx = transaction_for_tests(); + let bytes = tx.to_bytes(); + let tx_from_bytes = PublicTransaction::from_bytes(&bytes).unwrap(); + assert_eq!(tx, tx_from_bytes); + } + + #[test] + fn test_hash_is_sha256_of_transaction_bytes() { + let tx = transaction_for_tests(); + let hash = tx.hash(); + let expected_hash: [u8; 32] = { + let bytes = tx.to_bytes(); + let mut hasher = sha2::Sha256::new(); + hasher.update(&bytes); + hasher.finalize_fixed().into() + }; + assert_eq!(hash, expected_hash); + } + + #[test] + fn test_address_list_cant_have_duplicates() { + let (key1, _, addr1, _) = keys_for_tests(); + let state = state_for_tests(); + let nonces = vec![0, 0]; + let instruction = 1337; + let message = Message::try_new( + Program::authenticated_transfer_program().id(), + vec![addr1.clone(), addr1], + nonces, + instruction, + ) + .unwrap(); + + let witness_set = WitnessSet::for_message(&message, &[&key1, &key1]); + let tx = PublicTransaction::new(message, witness_set); + let result = tx.validate_and_compute_post_states(&state); + assert!(matches!(result, Err(NssaError::InvalidInput(_)))) + } + + #[test] + fn test_number_of_nonces_must_match_number_of_signatures() { + let (key1, key2, addr1, addr2) = keys_for_tests(); + let state = state_for_tests(); + let nonces = vec![0]; + let instruction = 1337; + let message = Message::try_new( + Program::authenticated_transfer_program().id(), + vec![addr1, addr2], + nonces, + instruction, + ) + .unwrap(); + + let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); + let tx = PublicTransaction::new(message, witness_set); + let result = tx.validate_and_compute_post_states(&state); + assert!(matches!(result, Err(NssaError::InvalidInput(_)))) + } + + #[test] + fn test_all_signatures_must_be_valid() { + let (key1, key2, addr1, addr2) = keys_for_tests(); + let state = state_for_tests(); + let nonces = vec![0, 0]; + let instruction = 1337; + let message = Message::try_new( + Program::authenticated_transfer_program().id(), + vec![addr1, addr2], + nonces, + instruction, + ) + .unwrap(); + + let mut witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); + witness_set.signatures_and_public_keys[0].0 = Signature { value: [1; 64] }; + let tx = PublicTransaction::new(message, witness_set); + let result = tx.validate_and_compute_post_states(&state); + assert!(matches!(result, Err(NssaError::InvalidInput(_)))) + } + + #[test] + fn test_nonces_must_match_the_state_current_nonces() { + let (key1, key2, addr1, addr2) = keys_for_tests(); + let state = state_for_tests(); + let nonces = vec![0, 1]; + let instruction = 1337; + let message = Message::try_new( + Program::authenticated_transfer_program().id(), + vec![addr1, addr2], + nonces, + instruction, + ) + .unwrap(); + + let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); + let tx = PublicTransaction::new(message, witness_set); + let result = tx.validate_and_compute_post_states(&state); + assert!(matches!(result, Err(NssaError::InvalidInput(_)))) + } + + #[test] + fn test_program_id_must_belong_to_bulitin_program_ids() { + let (key1, key2, addr1, addr2) = keys_for_tests(); + let state = state_for_tests(); + let nonces = vec![0, 0]; + let instruction = 1337; + let unknown_program_id = [0xdeadbeef; 8]; + let message = + Message::try_new(unknown_program_id, vec![addr1, addr2], nonces, instruction).unwrap(); + + let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); + let tx = PublicTransaction::new(message, witness_set); + let result = tx.validate_and_compute_post_states(&state); + assert!(matches!(result, Err(NssaError::InvalidInput(_)))) + } +} diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs index f76de70..4574f56 100644 --- a/nssa/src/public_transaction/witness_set.rs +++ b/nssa/src/public_transaction/witness_set.rs @@ -10,7 +10,7 @@ impl WitnessSet { let message_bytes = message.to_bytes(); let signatures_and_public_keys = private_keys .iter() - .map(|&key| (Signature::new(key, &message_bytes), PublicKey::new(key))) + .map(|&key| (Signature::new(key, &message_bytes), PublicKey::new_from_private_key(key))) .collect(); Self { signatures_and_public_keys, diff --git a/nssa/src/tests/bip340_test_vectors.rs b/nssa/src/signature/bip340_test_vectors.rs similarity index 86% rename from nssa/src/tests/bip340_test_vectors.rs rename to nssa/src/signature/bip340_test_vectors.rs index 6b6a3bc..4ee008c 100644 --- a/nssa/src/tests/bip340_test_vectors.rs +++ b/nssa/src/signature/bip340_test_vectors.rs @@ -19,12 +19,12 @@ pub struct TestVector { pub fn test_vectors() -> Vec { vec![ TestVector { - seckey: Some(PrivateKey(hex_to_bytes( + seckey: Some(PrivateKey::try_new(hex_to_bytes( "0000000000000000000000000000000000000000000000000000000000000003", - ))), - pubkey: PublicKey(hex_to_bytes( + )).unwrap()), + pubkey: PublicKey::new(hex_to_bytes( "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", - )), + )).unwrap(), aux_rand: Some(hex_to_bytes::<32>( "0000000000000000000000000000000000000000000000000000000000000000", )), @@ -40,12 +40,12 @@ pub fn test_vectors() -> Vec { verification_result: true, }, TestVector { - seckey: Some(PrivateKey(hex_to_bytes( + seckey: Some(PrivateKey::try_new(hex_to_bytes( "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF", - ))), - pubkey: PublicKey(hex_to_bytes( + )).unwrap()), + pubkey: PublicKey::new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), + )).unwrap(), aux_rand: Some(hex_to_bytes::<32>( "0000000000000000000000000000000000000000000000000000000000000001", )), @@ -61,12 +61,12 @@ pub fn test_vectors() -> Vec { verification_result: true, }, TestVector { - seckey: Some(PrivateKey(hex_to_bytes( + seckey: Some(PrivateKey::try_new(hex_to_bytes( "C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9", - ))), - pubkey: PublicKey(hex_to_bytes( + )).unwrap()), + pubkey: PublicKey::new(hex_to_bytes( "DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", - )), + )).unwrap(), aux_rand: Some(hex_to_bytes::<32>( "C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906", )), @@ -82,12 +82,12 @@ pub fn test_vectors() -> Vec { verification_result: true, }, TestVector { - seckey: Some(PrivateKey(hex_to_bytes( + seckey: Some(PrivateKey::try_new(hex_to_bytes( "0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710", - ))), - pubkey: PublicKey(hex_to_bytes( + )).unwrap()), + pubkey: PublicKey::new(hex_to_bytes( "25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", - )), + )).unwrap(), aux_rand: Some(hex_to_bytes::<32>( "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", )), @@ -104,9 +104,9 @@ pub fn test_vectors() -> Vec { }, TestVector { seckey: None, - pubkey: PublicKey(hex_to_bytes( + pubkey: PublicKey::new(hex_to_bytes( "D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9", - )), + )).unwrap(), aux_rand: None, message: Some( hex::decode("4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703") @@ -122,9 +122,9 @@ pub fn test_vectors() -> Vec { // Test with invalid public key // TestVector { // seckey: None, - // pubkey: PublicKey(hex_to_bytes( + // pubkey: PublicKey::new(hex_to_bytes( // "EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34", - // )), + // )).unwrap(), // aux_rand: None, // message: Some( // hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), @@ -138,9 +138,9 @@ pub fn test_vectors() -> Vec { // }, TestVector { seckey: None, - pubkey: PublicKey(hex_to_bytes( + pubkey: PublicKey::new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), + )).unwrap(), aux_rand: None, message: Some( hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") @@ -155,9 +155,9 @@ pub fn test_vectors() -> Vec { }, TestVector { seckey: None, - pubkey: PublicKey(hex_to_bytes( + pubkey: PublicKey::new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), + )).unwrap(), aux_rand: None, message: Some( hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") @@ -172,9 +172,9 @@ pub fn test_vectors() -> Vec { }, TestVector { seckey: None, - pubkey: PublicKey(hex_to_bytes( + pubkey: PublicKey::new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), + )).unwrap(), aux_rand: None, message: Some( hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") @@ -189,9 +189,9 @@ pub fn test_vectors() -> Vec { }, TestVector { seckey: None, - pubkey: PublicKey(hex_to_bytes( + pubkey: PublicKey::new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), + )).unwrap(), aux_rand: None, message: Some( hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") @@ -206,9 +206,9 @@ pub fn test_vectors() -> Vec { }, TestVector { seckey: None, - pubkey: PublicKey(hex_to_bytes( + pubkey: PublicKey::new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), + )).unwrap(), aux_rand: None, message: Some( hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") @@ -223,9 +223,9 @@ pub fn test_vectors() -> Vec { }, TestVector { seckey: None, - pubkey: PublicKey(hex_to_bytes( + pubkey: PublicKey::new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), + )).unwrap(), aux_rand: None, message: Some( hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") @@ -240,9 +240,9 @@ pub fn test_vectors() -> Vec { }, TestVector { seckey: None, - pubkey: PublicKey(hex_to_bytes( + pubkey: PublicKey::new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), + )).unwrap(), aux_rand: None, message: Some( hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") @@ -257,9 +257,9 @@ pub fn test_vectors() -> Vec { }, TestVector { seckey: None, - pubkey: PublicKey(hex_to_bytes( + pubkey: PublicKey::new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - )), + )).unwrap(), aux_rand: None, message: Some( hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") @@ -275,9 +275,9 @@ pub fn test_vectors() -> Vec { // Test with invalid public key // TestVector { // seckey: None, - // pubkey: PublicKey(hex_to_bytes( + // pubkey: PublicKey::new(hex_to_bytes( // "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30", - // )), + // )).unwrap(), // aux_rand: None, // message: Some( // hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89").unwrap(), @@ -290,12 +290,12 @@ pub fn test_vectors() -> Vec { // verification_result: false, // }, TestVector { - seckey: Some(PrivateKey(hex_to_bytes( + seckey: Some(PrivateKey::try_new(hex_to_bytes( "0340034003400340034003400340034003400340034003400340034003400340", - ))), - pubkey: PublicKey(hex_to_bytes( + )).unwrap()), + pubkey: PublicKey::new(hex_to_bytes( "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", - )), + )).unwrap(), aux_rand: Some(hex_to_bytes::<32>( "0000000000000000000000000000000000000000000000000000000000000000", )), @@ -308,12 +308,12 @@ pub fn test_vectors() -> Vec { verification_result: true, }, TestVector { - seckey: Some(PrivateKey(hex_to_bytes( + seckey: Some(PrivateKey::try_new(hex_to_bytes( "0340034003400340034003400340034003400340034003400340034003400340", - ))), - pubkey: PublicKey(hex_to_bytes( + )).unwrap()), + pubkey: PublicKey::new(hex_to_bytes( "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", - )), + )).unwrap(), aux_rand: Some(hex_to_bytes::<32>( "0000000000000000000000000000000000000000000000000000000000000000", )), @@ -326,12 +326,12 @@ pub fn test_vectors() -> Vec { verification_result: true, }, TestVector { - seckey: Some(PrivateKey(hex_to_bytes( + seckey: Some(PrivateKey::try_new(hex_to_bytes( "0340034003400340034003400340034003400340034003400340034003400340", - ))), - pubkey: PublicKey(hex_to_bytes( + )).unwrap()), + pubkey: PublicKey::new(hex_to_bytes( "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", - )), + )).unwrap(), aux_rand: Some(hex_to_bytes::<32>( "0000000000000000000000000000000000000000000000000000000000000000", )), @@ -344,12 +344,12 @@ pub fn test_vectors() -> Vec { verification_result: true, }, TestVector { - seckey: Some(PrivateKey(hex_to_bytes( + seckey: Some(PrivateKey::try_new(hex_to_bytes( "0340034003400340034003400340034003400340034003400340034003400340", - ))), - pubkey: PublicKey(hex_to_bytes( + )).unwrap()), + pubkey: PublicKey::new(hex_to_bytes( "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", - )), + )).unwrap(), aux_rand: Some(hex_to_bytes::<32>( "0000000000000000000000000000000000000000000000000000000000000000", )), diff --git a/nssa/src/signature/encoding.rs b/nssa/src/signature/encoding.rs index 7760c00..db5d67e 100644 --- a/nssa/src/signature/encoding.rs +++ b/nssa/src/signature/encoding.rs @@ -6,11 +6,11 @@ impl PublicKey { pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { let mut value = [0u8; 32]; cursor.read_exact(&mut value)?; - Ok(Self(value)) + Ok(Self::new(value)?) } pub(crate) fn to_bytes(&self) -> &[u8] { - &self.0 + self.value() } } diff --git a/nssa/src/signature/mod.rs b/nssa/src/signature/mod.rs index 37b878c..706bc60 100644 --- a/nssa/src/signature/mod.rs +++ b/nssa/src/signature/mod.rs @@ -26,7 +26,7 @@ impl Signature { ) -> Self { let value = { let secp = secp256k1::Secp256k1::new(); - let secret_key = secp256k1::SecretKey::from_byte_array(key.0).unwrap(); + let secret_key = secp256k1::SecretKey::from_byte_array(*key.value()).unwrap(); let keypair = secp256k1::Keypair::from_secret_key(&secp, &secret_key); let signature = secp.sign_schnorr_with_aux_rand(message, &keypair, &aux_random); signature.to_byte_array() @@ -35,9 +35,55 @@ impl Signature { } pub fn is_valid_for(&self, bytes: &[u8], public_key: &PublicKey) -> bool { - let pk = secp256k1::XOnlyPublicKey::from_byte_array(public_key.0).unwrap(); + let pk = secp256k1::XOnlyPublicKey::from_byte_array(*public_key.value()).unwrap(); let secp = secp256k1::Secp256k1::new(); let sig = secp256k1::schnorr::Signature::from_byte_array(self.value); secp.verify_schnorr(&sig, bytes, &pk).is_ok() } } + +#[cfg(test)] +mod bip340_test_vectors; + +#[cfg(test)] +mod tests { + + use crate::{PublicKey, Signature, signature::bip340_test_vectors}; + + #[test] + fn test_signature_generation_from_bip340_test_vectors() { + for (i, test_vector) in bip340_test_vectors::test_vectors().into_iter().enumerate() { + let Some(private_key) = test_vector.seckey else { + continue; + }; + let Some(aux_random) = test_vector.aux_rand else { + continue; + }; + let Some(message) = test_vector.message else { + continue; + }; + if !test_vector.verification_result { + continue; + } + let expected_signature = &test_vector.signature; + + let signature = Signature::new_with_aux_random(&private_key, &message, aux_random); + + assert_eq!(&signature, expected_signature, "Failed test vector {i}"); + } + } + + #[test] + fn test_signature_verification_from_bip340_test_vectors() { + for (i, test_vector) in bip340_test_vectors::test_vectors().into_iter().enumerate() { + let message = test_vector.message.unwrap_or(vec![]); + let expected_result = test_vector.verification_result; + + let result = test_vector + .signature + .is_valid_for(&message, &test_vector.pubkey); + + assert_eq!(result, expected_result, "Failed test vector {i}"); + } + } +} diff --git a/nssa/src/signature/private_key.rs b/nssa/src/signature/private_key.rs index a58d363..fb5a11c 100644 --- a/nssa/src/signature/private_key.rs +++ b/nssa/src/signature/private_key.rs @@ -6,7 +6,7 @@ use crate::error::NssaError; // TODO: Remove Debug, Clone, Serialize, Deserialize, PartialEq and Eq for security reasons // TODO: Implement Zeroize #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct PrivateKey(pub(crate) [u8; 32]); +pub struct PrivateKey([u8; 32]); impl PrivateKey { fn is_valid_key(value: [u8; 32]) -> bool { @@ -20,4 +20,8 @@ impl PrivateKey { Err(NssaError::InvalidPrivateKey) } } + + pub fn value(&self) -> &[u8; 32] { + &self.0 + } } diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index 2f7f4c8..55f6a00 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -1,13 +1,13 @@ -use crate::PrivateKey; +use crate::{PrivateKey, error::NssaError}; // TODO: Dummy impl. Replace by actual public key. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct PublicKey(pub(crate) [u8; 32]); +pub struct PublicKey([u8; 32]); impl PublicKey { - pub fn new(key: &PrivateKey) -> Self { + pub fn new_from_private_key(key: &PrivateKey) -> Self { let value = { - let secret_key = secp256k1::SecretKey::from_byte_array(key.0).unwrap(); + let secret_key = secp256k1::SecretKey::from_byte_array(*key.value()).unwrap(); let public_key = secp256k1::PublicKey::from_secret_key(&secp256k1::Secp256k1::new(), &secret_key); let (x_only, _) = public_key.x_only_public_key(); @@ -15,4 +15,35 @@ impl PublicKey { }; Self(value) } + + pub fn new(value: [u8; 32]) -> Result { + // Check point is valid + let _ = secp256k1::XOnlyPublicKey::from_byte_array(value) + .map_err(|_| NssaError::InvalidPublicKey)?; + Ok(Self(value)) + } + + pub fn value(&self) -> &[u8; 32] { + &self.0 + } +} + +#[cfg(test)] +mod test { + use crate::{PublicKey, signature::bip340_test_vectors}; + + #[test] + fn test_public_key_generation_from_bip340_test_vectors() { + for (i, test_vector) in bip340_test_vectors::test_vectors().into_iter().enumerate() { + let Some(private_key) = &test_vector.seckey else { + continue; + }; + let public_key = PublicKey::new_from_private_key(private_key); + let expected_public_key = &test_vector.pubkey; + assert_eq!( + &public_key, expected_public_key, + "Failed test vector at index {i}" + ); + } + } } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 343fd6e..9bcd0aa 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -5,8 +5,8 @@ use nssa_core::{account::Account, program::ProgramId}; use std::collections::HashMap; pub struct V01State { - pub(crate) public_state: HashMap, - pub(crate) builtin_programs: HashMap, + public_state: HashMap, + builtin_programs: HashMap, } impl V01State { @@ -79,3 +79,497 @@ impl V01State { self.public_state.insert(address, account); } } + +#[cfg(test)] +mod tests { + + use std::collections::HashMap; + + use crate::{ + Address, PublicKey, PublicTransaction, V01State, error::NssaError, program::Program, + public_transaction, signature::PrivateKey, + }; + use nssa_core::account::Account; + + fn transfer_transaction( + from: Address, + from_key: PrivateKey, + nonce: u128, + to: Address, + balance: u128, + ) -> PublicTransaction { + let addresses = vec![from, to]; + let nonces = vec![nonce]; + let program_id = Program::authenticated_transfer_program().id(); + let message = + public_transaction::Message::try_new(program_id, addresses, nonces, balance).unwrap(); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]); + PublicTransaction::new(message, witness_set) + } + + #[test] + fn test_new_with_genesis() { + let key1 = PrivateKey::try_new([1; 32]).unwrap(); + let key2 = PrivateKey::try_new([2; 32]).unwrap(); + let addr1 = Address::from_public_key(&PublicKey::new_from_private_key(&key1)); + let addr2 = Address::from_public_key(&PublicKey::new_from_private_key(&key2)); + let initial_data = [(*addr1.value(), 100u128), (*addr2.value(), 151u128)]; + let program = Program::authenticated_transfer_program(); + let expected_public_state = { + let mut this = HashMap::new(); + this.insert( + addr1, + Account { + balance: 100, + program_owner: program.id(), + ..Account::default() + }, + ); + this.insert( + addr2, + Account { + balance: 151, + program_owner: program.id(), + ..Account::default() + }, + ); + this + }; + let expected_builtin_programs = { + let mut this = HashMap::new(); + this.insert(program.id(), program); + this + }; + + let state = V01State::new_with_genesis_accounts(&initial_data); + + assert_eq!(state.public_state, expected_public_state); + assert_eq!(state.builtin_programs, expected_builtin_programs); + } + + #[test] + fn test_insert_program() { + let mut state = V01State::new_with_genesis_accounts(&[]); + let program_to_insert = Program::simple_balance_transfer(); + let program_id = program_to_insert.id(); + assert!(!state.builtin_programs.contains_key(&program_id)); + + state.insert_program(program_to_insert); + + assert!(state.builtin_programs.contains_key(&program_id)); + } + + #[test] + fn test_get_account_by_address_non_default_account() { + let key = PrivateKey::try_new([1; 32]).unwrap(); + let addr = Address::from_public_key(&PublicKey::new_from_private_key(&key)); + let initial_data = [(*addr.value(), 100u128)]; + let state = V01State::new_with_genesis_accounts(&initial_data); + let expected_account = state.public_state.get(&addr).unwrap(); + + let account = state.get_account_by_address(&addr); + + assert_eq!(&account, expected_account); + } + + #[test] + fn test_get_account_by_address_default_account() { + let addr2 = Address::new([0; 32]); + let state = V01State::new_with_genesis_accounts(&[]); + let expected_account = Account::default(); + + let account = state.get_account_by_address(&addr2); + + assert_eq!(account, expected_account); + } + + #[test] + fn test_builtin_programs_getter() { + let state = V01State::new_with_genesis_accounts(&[]); + + let builtin_programs = state.builtin_programs(); + + assert_eq!(builtin_programs, &state.builtin_programs); + } + + #[test] + fn transition_from_authenticated_transfer_program_invocation_default_account_destination() { + let key = PrivateKey::try_new([1; 32]).unwrap(); + let address = Address::from_public_key(&PublicKey::new_from_private_key(&key)); + let initial_data = [(*address.value(), 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data); + let from = address; + let to = Address::new([2; 32]); + assert_eq!(state.get_account_by_address(&to), Account::default()); + let balance_to_move = 5; + + let tx = transfer_transaction(from.clone(), key, 0, to.clone(), balance_to_move); + state.transition_from_public_transaction(&tx).unwrap(); + + assert_eq!(state.get_account_by_address(&from).balance, 95); + assert_eq!(state.get_account_by_address(&to).balance, 5); + assert_eq!(state.get_account_by_address(&from).nonce, 1); + assert_eq!(state.get_account_by_address(&to).nonce, 0); + } + + #[test] + fn transition_from_authenticated_transfer_program_invocation_insuficient_balance() { + let key = PrivateKey::try_new([1; 32]).unwrap(); + let address = Address::from_public_key(&PublicKey::new_from_private_key(&key)); + let initial_data = [(*address.value(), 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data); + let from = address; + let from_key = key; + let to = Address::new([2; 32]); + let balance_to_move = 101; + assert!(state.get_account_by_address(&from).balance < balance_to_move); + + let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move); + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::ProgramExecutionFailed(_)))); + assert_eq!(state.get_account_by_address(&from).balance, 100); + assert_eq!(state.get_account_by_address(&to).balance, 0); + assert_eq!(state.get_account_by_address(&from).nonce, 0); + assert_eq!(state.get_account_by_address(&to).nonce, 0); + } + + #[test] + fn transition_from_authenticated_transfer_program_invocation_non_default_account_destination() { + let key1 = PrivateKey::try_new([1; 32]).unwrap(); + let key2 = PrivateKey::try_new([2; 32]).unwrap(); + let address1 = Address::from_public_key(&PublicKey::new_from_private_key(&key1)); + let address2 = Address::from_public_key(&PublicKey::new_from_private_key(&key2)); + let initial_data = [(*address1.value(), 100), (*address2.value(), 200)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data); + let from = address2; + let from_key = key2; + let to = address1; + assert_ne!(state.get_account_by_address(&to), Account::default()); + let balance_to_move = 8; + + let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move); + state.transition_from_public_transaction(&tx).unwrap(); + + assert_eq!(state.get_account_by_address(&from).balance, 192); + assert_eq!(state.get_account_by_address(&to).balance, 108); + assert_eq!(state.get_account_by_address(&from).nonce, 1); + assert_eq!(state.get_account_by_address(&to).nonce, 0); + } + + #[test] + fn transition_from_chained_authenticated_transfer_program_invocations() { + let key1 = PrivateKey::try_new([8; 32]).unwrap(); + let address1 = Address::from_public_key(&PublicKey::new_from_private_key(&key1)); + let key2 = PrivateKey::try_new([2; 32]).unwrap(); + let address2 = Address::from_public_key(&PublicKey::new_from_private_key(&key2)); + let initial_data = [(*address1.value(), 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data); + let address3 = Address::new([3; 32]); + let balance_to_move = 5; + + let tx = transfer_transaction(address1.clone(), key1, 0, address2.clone(), balance_to_move); + state.transition_from_public_transaction(&tx).unwrap(); + let balance_to_move = 3; + let tx = transfer_transaction(address2.clone(), key2, 0, address3.clone(), balance_to_move); + state.transition_from_public_transaction(&tx).unwrap(); + + assert_eq!(state.get_account_by_address(&address1).balance, 95); + assert_eq!(state.get_account_by_address(&address2).balance, 2); + assert_eq!(state.get_account_by_address(&address3).balance, 3); + assert_eq!(state.get_account_by_address(&address1).nonce, 1); + assert_eq!(state.get_account_by_address(&address2).nonce, 1); + assert_eq!(state.get_account_by_address(&address3).nonce, 0); + } + + impl V01State { + /// Include test programs in the builtin programs map + pub fn with_test_programs(mut self) -> Self { + self.insert_program(Program::nonce_changer_program()); + self.insert_program(Program::extra_output_program()); + self.insert_program(Program::missing_output_program()); + self.insert_program(Program::program_owner_changer()); + self.insert_program(Program::simple_balance_transfer()); + self.insert_program(Program::data_changer()); + self.insert_program(Program::minter()); + self.insert_program(Program::burner()); + self + } + + pub fn with_non_default_accounts_but_default_program_owners(mut self) -> Self { + let account_with_default_values_except_balance = Account { + balance: 100, + ..Account::default() + }; + let account_with_default_values_except_nonce = Account { + nonce: 37, + ..Account::default() + }; + let account_with_default_values_except_data = Account { + data: vec![0xca, 0xfe], + ..Account::default() + }; + self.force_insert_account( + Address::new([255; 32]), + account_with_default_values_except_balance, + ); + self.force_insert_account( + Address::new([254; 32]), + account_with_default_values_except_nonce, + ); + self.force_insert_account( + Address::new([253; 32]), + account_with_default_values_except_data, + ); + self + } + + pub fn with_account_owned_by_burner_program(mut self) -> Self { + let account = Account { + program_owner: Program::burner().id(), + balance: 100, + ..Default::default() + }; + self.force_insert_account(Address::new([252; 32]), account); + self + } + } + + #[test] + fn test_program_should_fail_if_modifies_nonces() { + let initial_data = [([1; 32], 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let addresses = vec![Address::new([1; 32])]; + let program_id = Program::nonce_changer_program().id(); + let message = + public_transaction::Message::try_new(program_id, addresses, vec![], ()).unwrap(); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); + } + + #[test] + fn test_program_should_fail_if_output_accounts_exceed_inputs() { + let initial_data = [([1; 32], 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let addresses = vec![Address::new([1; 32])]; + let program_id = Program::extra_output_program().id(); + let message = + public_transaction::Message::try_new(program_id, addresses, vec![], ()).unwrap(); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); + } + + #[test] + fn test_program_should_fail_with_missing_output_accounts() { + let initial_data = [([1; 32], 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let addresses = vec![Address::new([1; 32]), Address::new([2; 32])]; + let program_id = Program::missing_output_program().id(); + let message = + public_transaction::Message::try_new(program_id, addresses, vec![], ()).unwrap(); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); + } + + #[test] + fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_program_owner() { + let initial_data = [([1; 32], 0)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let address = Address::new([1; 32]); + let account = state.get_account_by_address(&address); + // Assert the target account only differs from the default account in the program owner field + assert_ne!(account.program_owner, Account::default().program_owner); + assert_eq!(account.balance, Account::default().balance); + assert_eq!(account.nonce, Account::default().nonce); + assert_eq!(account.data, Account::default().data); + let program_id = Program::program_owner_changer().id(); + let message = + public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); + } + + #[test] + fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_balance() { + let initial_data = []; + let mut state = V01State::new_with_genesis_accounts(&initial_data) + .with_test_programs() + .with_non_default_accounts_but_default_program_owners(); + let address = Address::new([255; 32]); + let account = state.get_account_by_address(&address); + // Assert the target account only differs from the default account in balance field + assert_eq!(account.program_owner, Account::default().program_owner); + assert_ne!(account.balance, Account::default().balance); + assert_eq!(account.nonce, Account::default().nonce); + assert_eq!(account.data, Account::default().data); + let program_id = Program::program_owner_changer().id(); + let message = + public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); + } + + #[test] + fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_nonce() { + let initial_data = []; + let mut state = V01State::new_with_genesis_accounts(&initial_data) + .with_test_programs() + .with_non_default_accounts_but_default_program_owners(); + let address = Address::new([254; 32]); + let account = state.get_account_by_address(&address); + // Assert the target account only differs from the default account in nonce field + assert_eq!(account.program_owner, Account::default().program_owner); + assert_eq!(account.balance, Account::default().balance); + assert_ne!(account.nonce, Account::default().nonce); + assert_eq!(account.data, Account::default().data); + let program_id = Program::program_owner_changer().id(); + let message = + public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); + } + + #[test] + fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_data() { + let initial_data = []; + let mut state = V01State::new_with_genesis_accounts(&initial_data) + .with_test_programs() + .with_non_default_accounts_but_default_program_owners(); + let address = Address::new([253; 32]); + let account = state.get_account_by_address(&address); + // Assert the target account only differs from the default account in data field + assert_eq!(account.program_owner, Account::default().program_owner); + assert_eq!(account.balance, Account::default().balance); + assert_eq!(account.nonce, Account::default().nonce); + assert_ne!(account.data, Account::default().data); + let program_id = Program::program_owner_changer().id(); + let message = + public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); + } + + #[test] + fn test_program_should_fail_if_transfers_balance_from_non_owned_account() { + let initial_data = [([1; 32], 100)]; + let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let sender_address = Address::new([1; 32]); + let receiver_address = Address::new([2; 32]); + let balance_to_move: u128 = 1; + let program_id = Program::simple_balance_transfer().id(); + assert_ne!( + state.get_account_by_address(&sender_address).program_owner, + program_id + ); + let message = public_transaction::Message::try_new( + program_id, + vec![sender_address, receiver_address], + vec![], + balance_to_move, + ) + .unwrap(); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); + } + + #[test] + fn test_program_should_fail_if_modifies_data_of_non_owned_account() { + let initial_data = []; + let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let address = Address::new([1; 32]); + let program_id = Program::data_changer().id(); + + // Consider the extreme case where the target account is the default account + assert_eq!(state.get_account_by_address(&address), Account::default()); + assert_ne!( + state.get_account_by_address(&address).program_owner, + program_id + ); + let message = + public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); + } + + #[test] + fn test_program_should_fail_if_does_not_preserve_total_balance_by_minting() { + let initial_data = []; + let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let address = Address::new([1; 32]); + let program_id = Program::minter().id(); + + let message = + public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); + } + + #[test] + fn test_program_should_fail_if_does_not_preserve_total_balance_by_burning() { + let initial_data = []; + let mut state = V01State::new_with_genesis_accounts(&initial_data) + .with_test_programs() + .with_account_owned_by_burner_program(); + let program_id = Program::burner().id(); + let address = Address::new([252; 32]); + assert_eq!( + state.get_account_by_address(&address).program_owner, + program_id + ); + let balance_to_burn: u128 = 1; + assert!(state.get_account_by_address(&address).balance > balance_to_burn); + + let message = public_transaction::Message::try_new( + program_id, + vec![address], + vec![], + balance_to_burn, + ) + .unwrap(); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + let result = state.transition_from_public_transaction(&tx); + + assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); + } +} diff --git a/nssa/src/tests/mod.rs b/nssa/src/tests/mod.rs deleted file mode 100644 index a2d8eef..0000000 --- a/nssa/src/tests/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod bip340_test_vectors; -mod program_tests; -mod public_transaction_tests; -mod signature_tests; -mod state_tests; -mod valid_execution_tests; diff --git a/nssa/src/tests/program_tests.rs b/nssa/src/tests/program_tests.rs deleted file mode 100644 index 2249c81..0000000 --- a/nssa/src/tests/program_tests.rs +++ /dev/null @@ -1,123 +0,0 @@ -use nssa_core::account::{Account, AccountWithMetadata}; - -use crate::program::Program; - -impl Program { - /// A program that changes the nonce of an account - pub fn nonce_changer_program() -> Self { - use test_program_methods::{NONCE_CHANGER_ELF, NONCE_CHANGER_ID}; - - Program { - id: NONCE_CHANGER_ID, - elf: NONCE_CHANGER_ELF, - } - } - - /// A program that produces more output accounts than the inputs it received - pub fn extra_output_program() -> Self { - use test_program_methods::{EXTRA_OUTPUT_ELF, EXTRA_OUTPUT_ID}; - - Program { - id: EXTRA_OUTPUT_ID, - elf: EXTRA_OUTPUT_ELF, - } - } - - /// A program that produces less output accounts than the inputs it received - pub fn missing_output_program() -> Self { - use test_program_methods::{MISSING_OUTPUT_ELF, MISSING_OUTPUT_ID}; - - Program { - id: MISSING_OUTPUT_ID, - elf: MISSING_OUTPUT_ELF, - } - } - - /// A program that changes the program owner of an account to [0, 1, 2, 3, 4, 5, 6, 7] - pub fn program_owner_changer() -> Self { - use test_program_methods::{PROGRAM_OWNER_CHANGER_ELF, PROGRAM_OWNER_CHANGER_ID}; - - Program { - id: PROGRAM_OWNER_CHANGER_ID, - elf: PROGRAM_OWNER_CHANGER_ELF, - } - } - - /// A program that transfers balance without caring about authorizations - pub fn simple_balance_transfer() -> Self { - use test_program_methods::{SIMPLE_BALANCE_TRANSFER_ELF, SIMPLE_BALANCE_TRANSFER_ID}; - - Program { - id: SIMPLE_BALANCE_TRANSFER_ID, - elf: SIMPLE_BALANCE_TRANSFER_ELF, - } - } - - /// A program that modifies the data of an account - pub fn data_changer() -> Self { - use test_program_methods::{DATA_CHANGER_ELF, DATA_CHANGER_ID}; - - Program { - id: DATA_CHANGER_ID, - elf: DATA_CHANGER_ELF, - } - } - - /// A program that mints balance - pub fn minter() -> Self { - use test_program_methods::{MINTER_ELF, MINTER_ID}; - - Program { - id: MINTER_ID, - elf: MINTER_ELF, - } - } - - /// A program that burns balance - pub fn burner() -> Self { - use test_program_methods::{BURNER_ELF, BURNER_ID}; - - Program { - id: BURNER_ID, - elf: BURNER_ELF, - } - } -} - -#[test] -fn test_program_execution() { - let program = Program::simple_balance_transfer(); - let balance_to_move: u128 = 11223344556677; - let instruction_data = Program::serialize_instruction(balance_to_move).unwrap(); - let sender = AccountWithMetadata { - account: Account { - balance: 77665544332211, - ..Account::default() - }, - is_authorized: false, - }; - let recipient = AccountWithMetadata { - account: Account::default(), - is_authorized: false, - }; - - let expected_sender_post = Account { - balance: 77665544332211 - balance_to_move, - program_owner: program.id(), - ..Account::default() - }; - let expected_recipient_post = Account { - balance: balance_to_move, - // Program claims the account since the pre_state has default prorgam owner - program_owner: program.id(), - ..Account::default() - }; - let [sender_post, recipient_post] = program - .execute(&[sender, recipient], &instruction_data) - .unwrap() - .try_into() - .unwrap(); - - assert_eq!(sender_post, expected_sender_post); - assert_eq!(recipient_post, expected_recipient_post); -} diff --git a/nssa/src/tests/public_transaction_tests.rs b/nssa/src/tests/public_transaction_tests.rs deleted file mode 100644 index dd5c1cb..0000000 --- a/nssa/src/tests/public_transaction_tests.rs +++ /dev/null @@ -1,195 +0,0 @@ -use sha2::{Digest, digest::FixedOutput}; - -use crate::{ - Address, PrivateKey, PublicKey, PublicTransaction, Signature, V01State, - error::NssaError, - program::Program, - public_transaction::{Message, WitnessSet}, -}; - -fn keys_for_tests() -> (PrivateKey, PrivateKey, Address, Address) { - let key1 = PrivateKey::try_new([1; 32]).unwrap(); - let key2 = PrivateKey::try_new([2; 32]).unwrap(); - let addr1 = Address::from_public_key(&PublicKey::new(&key1)); - let addr2 = Address::from_public_key(&PublicKey::new(&key2)); - (key1, key2, addr1, addr2) -} - -fn state_for_tests() -> V01State { - let (_, _, addr1, addr2) = keys_for_tests(); - let initial_data = [(*addr1.value(), 10000), (*addr2.value(), 20000)]; - V01State::new_with_genesis_accounts(&initial_data) -} - -fn transaction_for_tests() -> PublicTransaction { - let (key1, key2, addr1, addr2) = keys_for_tests(); - let nonces = vec![0, 0]; - let instruction = 1337; - let message = Message::try_new( - Program::authenticated_transfer_program().id(), - vec![addr1, addr2], - nonces, - instruction, - ) - .unwrap(); - - let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); - PublicTransaction::new(message, witness_set) -} - -#[test] -fn test_new_constructor() { - let tx = transaction_for_tests(); - let message = tx.message().clone(); - let witness_set = tx.witness_set().clone(); - let tx_from_constructor = PublicTransaction::new(message.clone(), witness_set.clone()); - assert_eq!(tx_from_constructor.message, message); - assert_eq!(tx_from_constructor.witness_set, witness_set); -} - -#[test] -fn test_message_getter() { - let tx = transaction_for_tests(); - assert_eq!(&tx.message, tx.message()); -} - -#[test] -fn test_witness_set_getter() { - let tx = transaction_for_tests(); - assert_eq!(&tx.witness_set, tx.witness_set()); -} - -#[test] -fn test_signer_addresses() { - let tx = transaction_for_tests(); - let expected_signer_addresses = vec![ - Address::new([ - 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, - 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, - ]), - Address::new([ - 77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, - 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102, - ]), - ]; - let signer_addresses = tx.signer_addresses(); - assert_eq!(signer_addresses, expected_signer_addresses); -} - -#[test] -fn test_public_transaction_encoding_bytes_roundtrip() { - let tx = transaction_for_tests(); - let bytes = tx.to_bytes(); - let tx_from_bytes = PublicTransaction::from_bytes(&bytes).unwrap(); - assert_eq!(tx, tx_from_bytes); -} - -#[test] -fn test_hash_is_sha256_of_transaction_bytes() { - let tx = transaction_for_tests(); - let hash = tx.hash(); - let expected_hash: [u8; 32] = { - let bytes = tx.to_bytes(); - let mut hasher = sha2::Sha256::new(); - hasher.update(&bytes); - hasher.finalize_fixed().into() - }; - assert_eq!(hash, expected_hash); -} - -#[test] -fn test_address_list_cant_have_duplicates() { - let (key1, _, addr1, _) = keys_for_tests(); - let state = state_for_tests(); - let nonces = vec![0, 0]; - let instruction = 1337; - let message = Message::try_new( - Program::authenticated_transfer_program().id(), - vec![addr1.clone(), addr1], - nonces, - instruction, - ) - .unwrap(); - - let witness_set = WitnessSet::for_message(&message, &[&key1, &key1]); - let tx = PublicTransaction::new(message, witness_set); - let result = tx.validate_and_compute_post_states(&state); - assert!(matches!(result, Err(NssaError::InvalidInput(_)))) -} - -#[test] -fn test_number_of_nonces_must_match_number_of_signatures() { - let (key1, key2, addr1, addr2) = keys_for_tests(); - let state = state_for_tests(); - let nonces = vec![0]; - let instruction = 1337; - let message = Message::try_new( - Program::authenticated_transfer_program().id(), - vec![addr1, addr2], - nonces, - instruction, - ) - .unwrap(); - - let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); - let tx = PublicTransaction::new(message, witness_set); - let result = tx.validate_and_compute_post_states(&state); - assert!(matches!(result, Err(NssaError::InvalidInput(_)))) -} - -#[test] -fn test_all_signatures_must_be_valid() { - let (key1, key2, addr1, addr2) = keys_for_tests(); - let state = state_for_tests(); - let nonces = vec![0, 0]; - let instruction = 1337; - let message = Message::try_new( - Program::authenticated_transfer_program().id(), - vec![addr1, addr2], - nonces, - instruction, - ) - .unwrap(); - - let mut witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); - witness_set.signatures_and_public_keys[0].0 = Signature { value: [1; 64] }; - let tx = PublicTransaction::new(message, witness_set); - let result = tx.validate_and_compute_post_states(&state); - assert!(matches!(result, Err(NssaError::InvalidInput(_)))) -} - -#[test] -fn test_nonces_must_match_the_state_current_nonces() { - let (key1, key2, addr1, addr2) = keys_for_tests(); - let state = state_for_tests(); - let nonces = vec![0, 1]; - let instruction = 1337; - let message = Message::try_new( - Program::authenticated_transfer_program().id(), - vec![addr1, addr2], - nonces, - instruction, - ) - .unwrap(); - - let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); - let tx = PublicTransaction::new(message, witness_set); - let result = tx.validate_and_compute_post_states(&state); - assert!(matches!(result, Err(NssaError::InvalidInput(_)))) -} - -#[test] -fn test_program_id_must_belong_to_bulitin_program_ids() { - let (key1, key2, addr1, addr2) = keys_for_tests(); - let state = state_for_tests(); - let nonces = vec![0, 0]; - let instruction = 1337; - let unknown_program_id = [0xdeadbeef; 8]; - let message = - Message::try_new(unknown_program_id, vec![addr1, addr2], nonces, instruction).unwrap(); - - let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); - let tx = PublicTransaction::new(message, witness_set); - let result = tx.validate_and_compute_post_states(&state); - assert!(matches!(result, Err(NssaError::InvalidInput(_)))) -} diff --git a/nssa/src/tests/signature_tests.rs b/nssa/src/tests/signature_tests.rs deleted file mode 100644 index fb4d937..0000000 --- a/nssa/src/tests/signature_tests.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::{PublicKey, Signature, tests::bip340_test_vectors}; - -#[test] -fn test_signature_generation_from_bip340_test_vectors() { - for (i, test_vector) in bip340_test_vectors::test_vectors().into_iter().enumerate() { - let Some(private_key) = test_vector.seckey else { - continue; - }; - let Some(aux_random) = test_vector.aux_rand else { - continue; - }; - let Some(message) = test_vector.message else { - continue; - }; - if !test_vector.verification_result { - continue; - } - let expected_signature = &test_vector.signature; - - let signature = Signature::new_with_aux_random(&private_key, &message, aux_random); - - assert_eq!(&signature, expected_signature, "Failed test vector {i}"); - } -} - -#[test] -fn test_signature_verification_from_bip340_test_vectors() { - for (i, test_vector) in bip340_test_vectors::test_vectors().into_iter().enumerate() { - let message = test_vector.message.unwrap_or(vec![]); - let expected_result = test_vector.verification_result; - - let result = test_vector - .signature - .is_valid_for(&message, &test_vector.pubkey); - - assert_eq!(result, expected_result, "Failed test vector {i}"); - } -} - -#[test] -fn test_public_key_generation_from_bip340_test_vectors() { - for (i, test_vector) in bip340_test_vectors::test_vectors().into_iter().enumerate() { - let Some(private_key) = &test_vector.seckey else { - continue; - }; - let public_key = PublicKey::new(private_key); - let expected_public_key = &test_vector.pubkey; - assert_eq!( - &public_key, expected_public_key, - "Failed test vector at index {i}" - ); - } -} diff --git a/nssa/src/tests/state_tests.rs b/nssa/src/tests/state_tests.rs deleted file mode 100644 index 7324f8f..0000000 --- a/nssa/src/tests/state_tests.rs +++ /dev/null @@ -1,198 +0,0 @@ -use std::collections::HashMap; - -use crate::{ - Address, PublicKey, PublicTransaction, V01State, error::NssaError, program::Program, - public_transaction, signature::PrivateKey, -}; -use nssa_core::account::Account; - -fn transfer_transaction( - from: Address, - from_key: PrivateKey, - nonce: u128, - to: Address, - balance: u128, -) -> PublicTransaction { - let addresses = vec![from, to]; - let nonces = vec![nonce]; - let program_id = Program::authenticated_transfer_program().id(); - let message = - public_transaction::Message::try_new(program_id, addresses, nonces, balance).unwrap(); - let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]); - PublicTransaction::new(message, witness_set) -} - -#[test] -fn test_new_with_genesis() { - let key1 = PrivateKey::try_new([1; 32]).unwrap(); - let key2 = PrivateKey::try_new([2; 32]).unwrap(); - let addr1 = Address::from_public_key(&PublicKey::new(&key1)); - let addr2 = Address::from_public_key(&PublicKey::new(&key2)); - let initial_data = [(*addr1.value(), 100u128), (*addr2.value(), 151u128)]; - let program = Program::authenticated_transfer_program(); - let expected_public_state = { - let mut this = HashMap::new(); - this.insert( - addr1, - Account { - balance: 100, - program_owner: program.id(), - ..Account::default() - }, - ); - this.insert( - addr2, - Account { - balance: 151, - program_owner: program.id(), - ..Account::default() - }, - ); - this - }; - let expected_builtin_programs = { - let mut this = HashMap::new(); - this.insert(program.id(), program); - this - }; - - let state = V01State::new_with_genesis_accounts(&initial_data); - - assert_eq!(state.public_state, expected_public_state); - assert_eq!(state.builtin_programs, expected_builtin_programs); -} - -#[test] -fn test_insert_program() { - let mut state = V01State::new_with_genesis_accounts(&[]); - let program_to_insert = Program::simple_balance_transfer(); - let program_id = program_to_insert.id(); - assert!(!state.builtin_programs.contains_key(&program_id)); - - state.insert_program(program_to_insert); - - assert!(state.builtin_programs.contains_key(&program_id)); -} - -#[test] -fn test_get_account_by_address_non_default_account() { - let key = PrivateKey::try_new([1; 32]).unwrap(); - let addr = Address::from_public_key(&PublicKey::new(&key)); - let initial_data = [(*addr.value(), 100u128)]; - let state = V01State::new_with_genesis_accounts(&initial_data); - let expected_account = state.public_state.get(&addr).unwrap(); - - let account = state.get_account_by_address(&addr); - - assert_eq!(&account, expected_account); -} - -#[test] -fn test_get_account_by_address_default_account() { - let addr2 = Address::new([0; 32]); - let state = V01State::new_with_genesis_accounts(&[]); - let expected_account = Account::default(); - - let account = state.get_account_by_address(&addr2); - - assert_eq!(account, expected_account); -} - -#[test] -fn test_builtin_programs_getter() { - let state = V01State::new_with_genesis_accounts(&[]); - - let builtin_programs = state.builtin_programs(); - - assert_eq!(builtin_programs, &state.builtin_programs); -} - -#[test] -fn transition_from_authenticated_transfer_program_invocation_default_account_destination() { - let key = PrivateKey::try_new([1; 32]).unwrap(); - let address = Address::from_public_key(&PublicKey::new(&key)); - let initial_data = [(*address.value(), 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data); - let from = address; - let to = Address::new([2; 32]); - assert_eq!(state.get_account_by_address(&to), Account::default()); - let balance_to_move = 5; - - let tx = transfer_transaction(from.clone(), key, 0, to.clone(), balance_to_move); - state.transition_from_public_transaction(&tx).unwrap(); - - assert_eq!(state.get_account_by_address(&from).balance, 95); - assert_eq!(state.get_account_by_address(&to).balance, 5); - assert_eq!(state.get_account_by_address(&from).nonce, 1); - assert_eq!(state.get_account_by_address(&to).nonce, 0); -} - -#[test] -fn transition_from_authenticated_transfer_program_invocation_insuficient_balance() { - let key = PrivateKey::try_new([1; 32]).unwrap(); - let address = Address::from_public_key(&PublicKey::new(&key)); - let initial_data = [(*address.value(), 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data); - let from = address; - let from_key = key; - let to = Address::new([2; 32]); - let balance_to_move = 101; - assert!(state.get_account_by_address(&from).balance < balance_to_move); - - let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move); - let result = state.transition_from_public_transaction(&tx); - - assert!(matches!(result, Err(NssaError::ProgramExecutionFailed(_)))); - assert_eq!(state.get_account_by_address(&from).balance, 100); - assert_eq!(state.get_account_by_address(&to).balance, 0); - assert_eq!(state.get_account_by_address(&from).nonce, 0); - assert_eq!(state.get_account_by_address(&to).nonce, 0); -} - -#[test] -fn transition_from_authenticated_transfer_program_invocation_non_default_account_destination() { - let key1 = PrivateKey::try_new([1; 32]).unwrap(); - let key2 = PrivateKey::try_new([2; 32]).unwrap(); - let address1 = Address::from_public_key(&PublicKey::new(&key1)); - let address2 = Address::from_public_key(&PublicKey::new(&key2)); - let initial_data = [(*address1.value(), 100), (*address2.value(), 200)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data); - let from = address2; - let from_key = key2; - let to = address1; - assert_ne!(state.get_account_by_address(&to), Account::default()); - let balance_to_move = 8; - - let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move); - state.transition_from_public_transaction(&tx).unwrap(); - - assert_eq!(state.get_account_by_address(&from).balance, 192); - assert_eq!(state.get_account_by_address(&to).balance, 108); - assert_eq!(state.get_account_by_address(&from).nonce, 1); - assert_eq!(state.get_account_by_address(&to).nonce, 0); -} - -#[test] -fn transition_from_chained_authenticated_transfer_program_invocations() { - let key1 = PrivateKey::try_new([8; 32]).unwrap(); - let address1 = Address::from_public_key(&PublicKey::new(&key1)); - let key2 = PrivateKey::try_new([2; 32]).unwrap(); - let address2 = Address::from_public_key(&PublicKey::new(&key2)); - let initial_data = [(*address1.value(), 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data); - let address3 = Address::new([3; 32]); - let balance_to_move = 5; - - let tx = transfer_transaction(address1.clone(), key1, 0, address2.clone(), balance_to_move); - state.transition_from_public_transaction(&tx).unwrap(); - let balance_to_move = 3; - let tx = transfer_transaction(address2.clone(), key2, 0, address3.clone(), balance_to_move); - state.transition_from_public_transaction(&tx).unwrap(); - - assert_eq!(state.get_account_by_address(&address1).balance, 95); - assert_eq!(state.get_account_by_address(&address2).balance, 2); - assert_eq!(state.get_account_by_address(&address3).balance, 3); - assert_eq!(state.get_account_by_address(&address1).nonce, 1); - assert_eq!(state.get_account_by_address(&address2).nonce, 1); - assert_eq!(state.get_account_by_address(&address3).nonce, 0); -} diff --git a/nssa/src/tests/valid_execution_tests.rs b/nssa/src/tests/valid_execution_tests.rs deleted file mode 100644 index 0d35b02..0000000 --- a/nssa/src/tests/valid_execution_tests.rs +++ /dev/null @@ -1,289 +0,0 @@ -use nssa_core::account::Account; - -use crate::{ - Address, PublicTransaction, V01State, error::NssaError, program::Program, public_transaction, -}; - -impl V01State { - /// Include test programs in the builtin programs map - pub fn with_test_programs(mut self) -> Self { - self.insert_program(Program::nonce_changer_program()); - self.insert_program(Program::extra_output_program()); - self.insert_program(Program::missing_output_program()); - self.insert_program(Program::program_owner_changer()); - self.insert_program(Program::simple_balance_transfer()); - self.insert_program(Program::data_changer()); - self.insert_program(Program::minter()); - self.insert_program(Program::burner()); - self - } - - pub fn with_non_default_accounts_but_default_program_owners(mut self) -> Self { - let account_with_default_values_except_balance = Account { - balance: 100, - ..Account::default() - }; - let account_with_default_values_except_nonce = Account { - nonce: 37, - ..Account::default() - }; - let account_with_default_values_except_data = Account { - data: vec![0xca, 0xfe], - ..Account::default() - }; - self.force_insert_account( - Address::new([255; 32]), - account_with_default_values_except_balance, - ); - self.force_insert_account( - Address::new([254; 32]), - account_with_default_values_except_nonce, - ); - self.force_insert_account( - Address::new([253; 32]), - account_with_default_values_except_data, - ); - self - } - - pub fn with_account_owned_by_burner_program(mut self) -> Self { - let account = Account { - program_owner: Program::burner().id(), - balance: 100, - ..Default::default() - }; - self.force_insert_account(Address::new([252; 32]), account); - self - } -} - -#[test] -fn test_program_should_fail_if_modifies_nonces() { - let initial_data = [([1; 32], 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); - let addresses = vec![Address::new([1; 32])]; - let program_id = Program::nonce_changer_program().id(); - let message = public_transaction::Message::try_new(program_id, addresses, vec![], ()).unwrap(); - let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); - let tx = PublicTransaction::new(message, witness_set); - - let result = state.transition_from_public_transaction(&tx); - - assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); -} - -#[test] -fn test_program_should_fail_if_output_accounts_exceed_inputs() { - let initial_data = [([1; 32], 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); - let addresses = vec![Address::new([1; 32])]; - let program_id = Program::extra_output_program().id(); - let message = public_transaction::Message::try_new(program_id, addresses, vec![], ()).unwrap(); - let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); - let tx = PublicTransaction::new(message, witness_set); - - let result = state.transition_from_public_transaction(&tx); - - assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); -} - -#[test] -fn test_program_should_fail_with_missing_output_accounts() { - let initial_data = [([1; 32], 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); - let addresses = vec![Address::new([1; 32]), Address::new([2; 32])]; - let program_id = Program::missing_output_program().id(); - let message = public_transaction::Message::try_new(program_id, addresses, vec![], ()).unwrap(); - let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); - let tx = PublicTransaction::new(message, witness_set); - - let result = state.transition_from_public_transaction(&tx); - - assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); -} - -#[test] -fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_program_owner() { - let initial_data = [([1; 32], 0)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); - let address = Address::new([1; 32]); - let account = state.get_account_by_address(&address); - // Assert the target account only differs from the default account in the program owner field - assert_ne!(account.program_owner, Account::default().program_owner); - assert_eq!(account.balance, Account::default().balance); - assert_eq!(account.nonce, Account::default().nonce); - assert_eq!(account.data, Account::default().data); - let program_id = Program::program_owner_changer().id(); - let message = - public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); - let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); - let tx = PublicTransaction::new(message, witness_set); - - let result = state.transition_from_public_transaction(&tx); - - assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); -} - -#[test] -fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_balance() { - let initial_data = []; - let mut state = V01State::new_with_genesis_accounts(&initial_data) - .with_test_programs() - .with_non_default_accounts_but_default_program_owners(); - let address = Address::new([255; 32]); - let account = state.get_account_by_address(&address); - // Assert the target account only differs from the default account in balance field - assert_eq!(account.program_owner, Account::default().program_owner); - assert_ne!(account.balance, Account::default().balance); - assert_eq!(account.nonce, Account::default().nonce); - assert_eq!(account.data, Account::default().data); - let program_id = Program::program_owner_changer().id(); - let message = - public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); - let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); - let tx = PublicTransaction::new(message, witness_set); - - let result = state.transition_from_public_transaction(&tx); - - assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); -} - -#[test] -fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_nonce() { - let initial_data = []; - let mut state = V01State::new_with_genesis_accounts(&initial_data) - .with_test_programs() - .with_non_default_accounts_but_default_program_owners(); - let address = Address::new([254; 32]); - let account = state.get_account_by_address(&address); - // Assert the target account only differs from the default account in nonce field - assert_eq!(account.program_owner, Account::default().program_owner); - assert_eq!(account.balance, Account::default().balance); - assert_ne!(account.nonce, Account::default().nonce); - assert_eq!(account.data, Account::default().data); - let program_id = Program::program_owner_changer().id(); - let message = - public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); - let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); - let tx = PublicTransaction::new(message, witness_set); - - let result = state.transition_from_public_transaction(&tx); - - assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); -} - -#[test] -fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_data() { - let initial_data = []; - let mut state = V01State::new_with_genesis_accounts(&initial_data) - .with_test_programs() - .with_non_default_accounts_but_default_program_owners(); - let address = Address::new([253; 32]); - let account = state.get_account_by_address(&address); - // Assert the target account only differs from the default account in data field - assert_eq!(account.program_owner, Account::default().program_owner); - assert_eq!(account.balance, Account::default().balance); - assert_eq!(account.nonce, Account::default().nonce); - assert_ne!(account.data, Account::default().data); - let program_id = Program::program_owner_changer().id(); - let message = - public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); - let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); - let tx = PublicTransaction::new(message, witness_set); - - let result = state.transition_from_public_transaction(&tx); - - assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); -} - -#[test] -fn test_program_should_fail_if_transfers_balance_from_non_owned_account() { - let initial_data = [([1; 32], 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); - let sender_address = Address::new([1; 32]); - let receiver_address = Address::new([2; 32]); - let balance_to_move: u128 = 1; - let program_id = Program::simple_balance_transfer().id(); - assert_ne!( - state.get_account_by_address(&sender_address).program_owner, - program_id - ); - let message = public_transaction::Message::try_new( - program_id, - vec![sender_address, receiver_address], - vec![], - balance_to_move, - ) - .unwrap(); - let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); - let tx = PublicTransaction::new(message, witness_set); - - let result = state.transition_from_public_transaction(&tx); - - assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); -} - -#[test] -fn test_program_should_fail_if_modifies_data_of_non_owned_account() { - let initial_data = []; - let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); - let address = Address::new([1; 32]); - let program_id = Program::data_changer().id(); - - // Consider the extreme case where the target account is the default account - assert_eq!(state.get_account_by_address(&address), Account::default()); - assert_ne!( - state.get_account_by_address(&address).program_owner, - program_id - ); - let message = - public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); - let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); - let tx = PublicTransaction::new(message, witness_set); - - let result = state.transition_from_public_transaction(&tx); - - assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); -} - -#[test] -fn test_program_should_fail_if_does_not_preserve_total_balance_by_minting() { - let initial_data = []; - let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); - let address = Address::new([1; 32]); - let program_id = Program::minter().id(); - - let message = - public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap(); - let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); - let tx = PublicTransaction::new(message, witness_set); - - let result = state.transition_from_public_transaction(&tx); - - assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); -} - -#[test] -fn test_program_should_fail_if_does_not_preserve_total_balance_by_burning() { - let initial_data = []; - let mut state = V01State::new_with_genesis_accounts(&initial_data) - .with_test_programs() - .with_account_owned_by_burner_program(); - let program_id = Program::burner().id(); - let address = Address::new([252; 32]); - assert_eq!( - state.get_account_by_address(&address).program_owner, - program_id - ); - let balance_to_burn: u128 = 1; - assert!(state.get_account_by_address(&address).balance > balance_to_burn); - - let message = - public_transaction::Message::try_new(program_id, vec![address], vec![], balance_to_burn) - .unwrap(); - let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); - let tx = PublicTransaction::new(message, witness_set); - let result = state.transition_from_public_transaction(&tx); - - assert!(matches!(result, Err(NssaError::InvalidProgramBehavior))); -} From 2c0c417c2216e9594e2dc96905363dc14fc3d0b6 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 13 Aug 2025 01:36:19 -0300 Subject: [PATCH 51/58] improve attribute visibility --- nssa/src/public_transaction/transaction.rs | 2 +- nssa/src/signature/mod.rs | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/nssa/src/public_transaction/transaction.rs b/nssa/src/public_transaction/transaction.rs index 7b5f065..9315ba8 100644 --- a/nssa/src/public_transaction/transaction.rs +++ b/nssa/src/public_transaction/transaction.rs @@ -270,7 +270,7 @@ mod tests { .unwrap(); let mut witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); - witness_set.signatures_and_public_keys[0].0 = Signature { value: [1; 64] }; + witness_set.signatures_and_public_keys[0].0 = Signature::new_for_tests([1; 64]); let tx = PublicTransaction::new(message, witness_set); let result = tx.validate_and_compute_post_states(&state); assert!(matches!(result, Err(NssaError::InvalidInput(_)))) diff --git a/nssa/src/signature/mod.rs b/nssa/src/signature/mod.rs index 706bc60..9b318f4 100644 --- a/nssa/src/signature/mod.rs +++ b/nssa/src/signature/mod.rs @@ -9,7 +9,7 @@ use rand::{RngCore, rngs::OsRng}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Signature { - pub(crate) value: [u8; 64], + value: [u8; 64], } impl Signature { @@ -50,6 +50,12 @@ mod tests { use crate::{PublicKey, Signature, signature::bip340_test_vectors}; + impl Signature { + pub(crate) fn new_for_tests(value: [u8; 64]) -> Self { + Self { value } + } + } + #[test] fn test_signature_generation_from_bip340_test_vectors() { for (i, test_vector) in bip340_test_vectors::test_vectors().into_iter().enumerate() { From 4bdd2c6d9fa53b485369f4d0a11176a830edaee0 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 13 Aug 2025 01:49:17 -0300 Subject: [PATCH 52/58] add try_new test --- nssa/src/signature/bip340_test_vectors.rs | 34 ++++++++++----------- nssa/src/signature/encoding.rs | 2 +- nssa/src/signature/private_key.rs | 1 - nssa/src/signature/public_key.rs | 36 +++++++++++++++++++++-- 4 files changed, 51 insertions(+), 22 deletions(-) diff --git a/nssa/src/signature/bip340_test_vectors.rs b/nssa/src/signature/bip340_test_vectors.rs index 4ee008c..0e91bcb 100644 --- a/nssa/src/signature/bip340_test_vectors.rs +++ b/nssa/src/signature/bip340_test_vectors.rs @@ -22,7 +22,7 @@ pub fn test_vectors() -> Vec { seckey: Some(PrivateKey::try_new(hex_to_bytes( "0000000000000000000000000000000000000000000000000000000000000003", )).unwrap()), - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", )).unwrap(), aux_rand: Some(hex_to_bytes::<32>( @@ -43,7 +43,7 @@ pub fn test_vectors() -> Vec { seckey: Some(PrivateKey::try_new(hex_to_bytes( "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF", )).unwrap()), - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", )).unwrap(), aux_rand: Some(hex_to_bytes::<32>( @@ -64,7 +64,7 @@ pub fn test_vectors() -> Vec { seckey: Some(PrivateKey::try_new(hex_to_bytes( "C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9", )).unwrap()), - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", )).unwrap(), aux_rand: Some(hex_to_bytes::<32>( @@ -85,7 +85,7 @@ pub fn test_vectors() -> Vec { seckey: Some(PrivateKey::try_new(hex_to_bytes( "0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710", )).unwrap()), - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", )).unwrap(), aux_rand: Some(hex_to_bytes::<32>( @@ -104,7 +104,7 @@ pub fn test_vectors() -> Vec { }, TestVector { seckey: None, - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9", )).unwrap(), aux_rand: None, @@ -138,7 +138,7 @@ pub fn test_vectors() -> Vec { // }, TestVector { seckey: None, - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", )).unwrap(), aux_rand: None, @@ -155,7 +155,7 @@ pub fn test_vectors() -> Vec { }, TestVector { seckey: None, - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", )).unwrap(), aux_rand: None, @@ -172,7 +172,7 @@ pub fn test_vectors() -> Vec { }, TestVector { seckey: None, - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", )).unwrap(), aux_rand: None, @@ -189,7 +189,7 @@ pub fn test_vectors() -> Vec { }, TestVector { seckey: None, - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", )).unwrap(), aux_rand: None, @@ -206,7 +206,7 @@ pub fn test_vectors() -> Vec { }, TestVector { seckey: None, - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", )).unwrap(), aux_rand: None, @@ -223,7 +223,7 @@ pub fn test_vectors() -> Vec { }, TestVector { seckey: None, - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", )).unwrap(), aux_rand: None, @@ -240,7 +240,7 @@ pub fn test_vectors() -> Vec { }, TestVector { seckey: None, - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", )).unwrap(), aux_rand: None, @@ -257,7 +257,7 @@ pub fn test_vectors() -> Vec { }, TestVector { seckey: None, - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", )).unwrap(), aux_rand: None, @@ -293,7 +293,7 @@ pub fn test_vectors() -> Vec { seckey: Some(PrivateKey::try_new(hex_to_bytes( "0340034003400340034003400340034003400340034003400340034003400340", )).unwrap()), - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", )).unwrap(), aux_rand: Some(hex_to_bytes::<32>( @@ -311,7 +311,7 @@ pub fn test_vectors() -> Vec { seckey: Some(PrivateKey::try_new(hex_to_bytes( "0340034003400340034003400340034003400340034003400340034003400340", )).unwrap()), - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", )).unwrap(), aux_rand: Some(hex_to_bytes::<32>( @@ -329,7 +329,7 @@ pub fn test_vectors() -> Vec { seckey: Some(PrivateKey::try_new(hex_to_bytes( "0340034003400340034003400340034003400340034003400340034003400340", )).unwrap()), - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", )).unwrap(), aux_rand: Some(hex_to_bytes::<32>( @@ -347,7 +347,7 @@ pub fn test_vectors() -> Vec { seckey: Some(PrivateKey::try_new(hex_to_bytes( "0340034003400340034003400340034003400340034003400340034003400340", )).unwrap()), - pubkey: PublicKey::new(hex_to_bytes( + pubkey: PublicKey::try_new(hex_to_bytes( "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", )).unwrap(), aux_rand: Some(hex_to_bytes::<32>( diff --git a/nssa/src/signature/encoding.rs b/nssa/src/signature/encoding.rs index db5d67e..a3544be 100644 --- a/nssa/src/signature/encoding.rs +++ b/nssa/src/signature/encoding.rs @@ -6,7 +6,7 @@ impl PublicKey { pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { let mut value = [0u8; 32]; cursor.read_exact(&mut value)?; - Ok(Self::new(value)?) + Ok(Self::try_new(value)?) } pub(crate) fn to_bytes(&self) -> &[u8] { diff --git a/nssa/src/signature/private_key.rs b/nssa/src/signature/private_key.rs index fb5a11c..a0a7c0c 100644 --- a/nssa/src/signature/private_key.rs +++ b/nssa/src/signature/private_key.rs @@ -2,7 +2,6 @@ use serde::{Deserialize, Serialize}; use crate::error::NssaError; -// TODO: Dummy impl. Replace by actual private key. // TODO: Remove Debug, Clone, Serialize, Deserialize, PartialEq and Eq for security reasons // TODO: Implement Zeroize #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index 55f6a00..d83af30 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -1,6 +1,5 @@ use crate::{PrivateKey, error::NssaError}; -// TODO: Dummy impl. Replace by actual public key. #[derive(Debug, Clone, PartialEq, Eq)] pub struct PublicKey([u8; 32]); @@ -16,7 +15,7 @@ impl PublicKey { Self(value) } - pub fn new(value: [u8; 32]) -> Result { + pub fn try_new(value: [u8; 32]) -> Result { // Check point is valid let _ = secp256k1::XOnlyPublicKey::from_byte_array(value) .map_err(|_| NssaError::InvalidPublicKey)?; @@ -30,7 +29,38 @@ impl PublicKey { #[cfg(test)] mod test { - use crate::{PublicKey, signature::bip340_test_vectors}; + use crate::{PublicKey, error::NssaError, signature::bip340_test_vectors}; + + #[test] + fn test_try_new_invalid_public_key_from_bip340_test_vectors_5() { + let value_invalid_key = [ + 238, 253, 234, 76, 219, 103, 119, 80, 164, 32, 254, 232, 7, 234, 207, 33, 235, 152, + 152, 174, 121, 185, 118, 135, 102, 228, 250, 160, 74, 45, 74, 52, + ]; + + let result = PublicKey::try_new(value_invalid_key); + + assert!(matches!(result, Err(NssaError::InvalidPublicKey))); + } + + fn test_try_new_invalid_public_key_from_bip340_test_vector_14() { + let value_invalid_key = [ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 255, 255, 252, 48, + ]; + + let result = PublicKey::try_new(value_invalid_key); + + assert!(matches!(result, Err(NssaError::InvalidPublicKey))); + } + + fn test_try_new_valid_public_keys() { + for (i, test_vector) in bip340_test_vectors::test_vectors().into_iter().enumerate() { + let expected_public_key = test_vector.pubkey; + let public_key = PublicKey::try_new(*expected_public_key.value()).unwrap(); + assert_eq!(public_key, expected_public_key); + } + } #[test] fn test_public_key_generation_from_bip340_test_vectors() { From 63cca0f30f921ccaf3e9e86d4f475ec0de21431c Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 13 Aug 2025 01:55:56 -0300 Subject: [PATCH 53/58] add tests --- nssa/src/signature/private_key.rs | 10 ++++++++++ nssa/src/state.rs | 9 ++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/nssa/src/signature/private_key.rs b/nssa/src/signature/private_key.rs index a0a7c0c..b35d7da 100644 --- a/nssa/src/signature/private_key.rs +++ b/nssa/src/signature/private_key.rs @@ -24,3 +24,13 @@ impl PrivateKey { &self.0 } } + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_value_getter() { + let key = PrivateKey::try_new([1; 32]).unwrap(); + assert_eq!(key.value(), &key.0); + } +} diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 9bcd0aa..f6d932e 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -73,11 +73,6 @@ impl V01State { pub(crate) fn builtin_programs(&self) -> &HashMap { &self.builtin_programs } - - #[cfg(test)] - pub fn force_insert_account(&mut self, address: Address, account: Account) { - self.public_state.insert(address, account); - } } #[cfg(test)] @@ -283,6 +278,10 @@ mod tests { } impl V01State { + pub fn force_insert_account(&mut self, address: Address, account: Account) { + self.public_state.insert(address, account); + } + /// Include test programs in the builtin programs map pub fn with_test_programs(mut self) -> Self { self.insert_program(Program::nonce_changer_program()); From b22de5010703cc0ad85229403eee1dd8a15d232b Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 13 Aug 2025 01:56:18 -0300 Subject: [PATCH 54/58] fmt clippy --- accounts/src/account_core/mod.rs | 6 ++++-- nssa/src/lib.rs | 1 - nssa/src/public_transaction/mod.rs | 1 - nssa/src/public_transaction/witness_set.rs | 7 ++++++- nssa/src/signature/encoding.rs | 2 +- nssa/src/signature/mod.rs | 2 +- nssa/src/signature/public_key.rs | 4 +++- 7 files changed, 15 insertions(+), 8 deletions(-) diff --git a/accounts/src/account_core/mod.rs b/accounts/src/account_core/mod.rs index 3d7fe88..2447868 100644 --- a/accounts/src/account_core/mod.rs +++ b/accounts/src/account_core/mod.rs @@ -117,7 +117,8 @@ impl AccountPublicMask { impl Account { pub fn new() -> Self { let key_holder = AddressKeyHolder::new_os_random(); - let public_key = nssa::PublicKey::new_from_private_key(key_holder.get_pub_account_signing_key()); + let public_key = + nssa::PublicKey::new_from_private_key(key_holder.get_pub_account_signing_key()); let address = nssa::Address::from_public_key(&public_key); let balance = 0; let utxos = HashMap::new(); @@ -132,7 +133,8 @@ impl Account { pub fn new_with_balance(balance: u64) -> Self { let key_holder = AddressKeyHolder::new_os_random(); - let public_key = nssa::PublicKey::new_from_private_key(key_holder.get_pub_account_signing_key()); + let public_key = + nssa::PublicKey::new_from_private_key(key_holder.get_pub_account_signing_key()); let address = nssa::Address::from_public_key(&public_key); let utxos = HashMap::new(); diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs index bcf536e..ab4fe02 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -11,4 +11,3 @@ pub use signature::PrivateKey; pub use signature::PublicKey; pub use signature::Signature; pub use state::V01State; - diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs index 57456f3..9ae24cf 100644 --- a/nssa/src/public_transaction/mod.rs +++ b/nssa/src/public_transaction/mod.rs @@ -6,4 +6,3 @@ mod witness_set; pub use message::Message; pub use transaction::PublicTransaction; pub use witness_set::WitnessSet; - diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs index 4574f56..064887a 100644 --- a/nssa/src/public_transaction/witness_set.rs +++ b/nssa/src/public_transaction/witness_set.rs @@ -10,7 +10,12 @@ impl WitnessSet { let message_bytes = message.to_bytes(); let signatures_and_public_keys = private_keys .iter() - .map(|&key| (Signature::new(key, &message_bytes), PublicKey::new_from_private_key(key))) + .map(|&key| { + ( + Signature::new(key, &message_bytes), + PublicKey::new_from_private_key(key), + ) + }) .collect(); Self { signatures_and_public_keys, diff --git a/nssa/src/signature/encoding.rs b/nssa/src/signature/encoding.rs index a3544be..999e4a1 100644 --- a/nssa/src/signature/encoding.rs +++ b/nssa/src/signature/encoding.rs @@ -6,7 +6,7 @@ impl PublicKey { pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { let mut value = [0u8; 32]; cursor.read_exact(&mut value)?; - Ok(Self::try_new(value)?) + Self::try_new(value) } pub(crate) fn to_bytes(&self) -> &[u8] { diff --git a/nssa/src/signature/mod.rs b/nssa/src/signature/mod.rs index 9b318f4..2f18360 100644 --- a/nssa/src/signature/mod.rs +++ b/nssa/src/signature/mod.rs @@ -48,7 +48,7 @@ mod bip340_test_vectors; #[cfg(test)] mod tests { - use crate::{PublicKey, Signature, signature::bip340_test_vectors}; + use crate::{Signature, signature::bip340_test_vectors}; impl Signature { pub(crate) fn new_for_tests(value: [u8; 64]) -> Self { diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index d83af30..6e0035f 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -43,6 +43,7 @@ mod test { assert!(matches!(result, Err(NssaError::InvalidPublicKey))); } + #[test] fn test_try_new_invalid_public_key_from_bip340_test_vector_14() { let value_invalid_key = [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, @@ -54,11 +55,12 @@ mod test { assert!(matches!(result, Err(NssaError::InvalidPublicKey))); } + #[test] fn test_try_new_valid_public_keys() { for (i, test_vector) in bip340_test_vectors::test_vectors().into_iter().enumerate() { let expected_public_key = test_vector.pubkey; let public_key = PublicKey::try_new(*expected_public_key.value()).unwrap(); - assert_eq!(public_key, expected_public_key); + assert_eq!(public_key, expected_public_key, "Failed on test vector {i}"); } } From df78d5f162323a540c6400a16420059abb383abf Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 13 Aug 2025 02:25:19 -0300 Subject: [PATCH 55/58] fix integration tests --- .../configs/debug/sequencer/sequencer_config.json | 4 ++-- .../configs/debug/wallet/wallet_config.json | 12 ++++++------ integration_tests/src/lib.rs | 4 ++-- sequencer_core/src/lib.rs | 10 ++-------- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/integration_tests/configs/debug/sequencer/sequencer_config.json b/integration_tests/configs/debug/sequencer/sequencer_config.json index 5546a37..37bfd0a 100644 --- a/integration_tests/configs/debug/sequencer/sequencer_config.json +++ b/integration_tests/configs/debug/sequencer/sequencer_config.json @@ -8,11 +8,11 @@ "port": 3040, "initial_accounts": [ { - "addr": "0101010101010101010101010101010101010101010101010101010101010101", + "addr": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "balance": 10000 }, { - "addr": "0202020202020202020202020202020202020202020202020202020202020202", + "addr": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "balance": 20000 } ] diff --git a/integration_tests/configs/debug/wallet/wallet_config.json b/integration_tests/configs/debug/wallet/wallet_config.json index 00913c4..ab6191d 100644 --- a/integration_tests/configs/debug/wallet/wallet_config.json +++ b/integration_tests/configs/debug/wallet/wallet_config.json @@ -5,12 +5,12 @@ "seq_poll_timeout_secs": 10, "initial_accounts": [ { - "address": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "address": [27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143], "balance": 10000, "key_holder": { - "address": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "address": [27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143], "nullifer_public_key": "03A340BECA9FAAB444CED0140681D72EA1318B5C611704FEE017DA9836B17DB718", - "pub_account_signing_key": 1, + "pub_account_signing_key": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], "top_secret_key_holder": { "secret_spending_key": "7BC46784DB1BC67825D8F029436846712BFDF9B5D79EA3AB11D39A52B9B229D4" }, @@ -23,12 +23,12 @@ "utxos": {} }, { - "address": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + "address": [77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102], "balance": 20000, "key_holder": { - "address": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + "address": [77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102], "nullifer_public_key": "02172F50274DE67C4087C344F5D58E11DF761D90285B095060E0994FAA6BCDE271", - "pub_account_signing_key": 2, + "pub_account_signing_key": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], "top_secret_key_holder": { "secret_spending_key": "80A186737C8D38B4288A03F0F589957D9C040D79C19F3E0CC4BA80F8494E5179" }, diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index 604d108..d9efc35 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -20,8 +20,8 @@ struct Args { test_name: String, } -pub const ACC_SENDER: &str = "0101010101010101010101010101010101010101010101010101010101010101"; -pub const ACC_RECEIVER: &str = "0202020202020202020202020202020202020202020202020202020202020202"; +pub const ACC_SENDER: &str = "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"; +pub const ACC_RECEIVER: &str = "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766"; pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12; diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index b56b34cc..0f66b4a 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -232,15 +232,9 @@ mod tests { #[test] fn test_start_different_intial_accounts_balances() { - let acc1_addr = vec![ - 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, - 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, - ]; + let acc1_addr = vec![27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143]; - let acc2_addr = vec![ - 77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, - 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102, - ]; + let acc2_addr = vec![77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102]; let initial_acc1 = AccountInitialData { addr: hex::encode(acc1_addr), From 490ac908b8f77868f71bcc6ffe585a0b6f2699df Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 13 Aug 2025 03:01:54 -0300 Subject: [PATCH 56/58] nit --- nssa/src/public_transaction/encoding.rs | 4 +-- nssa/src/public_transaction/transaction.rs | 5 +-- nssa/src/public_transaction/witness_set.rs | 42 +++++++++++++++++++--- sequencer_core/src/lib.rs | 10 ++++-- 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/nssa/src/public_transaction/encoding.rs b/nssa/src/public_transaction/encoding.rs index 7f1ab62..3c004a0 100644 --- a/nssa/src/public_transaction/encoding.rs +++ b/nssa/src/public_transaction/encoding.rs @@ -92,9 +92,9 @@ impl Message { impl WitnessSet { pub(crate) fn to_bytes(&self) -> Vec { let mut bytes = Vec::new(); - let size = self.signatures_and_public_keys.len() as u32; + let size = self.signatures_and_public_keys().len() as u32; bytes.extend_from_slice(&size.to_le_bytes()); - for (signature, public_key) in &self.signatures_and_public_keys { + for (signature, public_key) in self.signatures_and_public_keys() { bytes.extend_from_slice(signature.to_bytes()); bytes.extend_from_slice(public_key.to_bytes()); } diff --git a/nssa/src/public_transaction/transaction.rs b/nssa/src/public_transaction/transaction.rs index 9315ba8..bd16644 100644 --- a/nssa/src/public_transaction/transaction.rs +++ b/nssa/src/public_transaction/transaction.rs @@ -37,7 +37,8 @@ impl PublicTransaction { pub(crate) fn signer_addresses(&self) -> Vec
{ self.witness_set - .iter_signatures() + .signatures_and_public_keys() + .iter() .map(|(_, public_key)| Address::from_public_key(public_key)) .collect() } @@ -115,7 +116,7 @@ impl PublicTransaction { } #[cfg(test)] -mod tests { +pub mod tests { use sha2::{Digest, digest::FixedOutput}; use crate::{ diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs index 064887a..ae87080 100644 --- a/nssa/src/public_transaction/witness_set.rs +++ b/nssa/src/public_transaction/witness_set.rs @@ -2,7 +2,7 @@ use crate::{PrivateKey, PublicKey, Signature, public_transaction::Message}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct WitnessSet { - pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>, + pub(super) signatures_and_public_keys: Vec<(Signature, PublicKey)>, } impl WitnessSet { @@ -24,7 +24,7 @@ impl WitnessSet { pub fn is_valid_for(&self, message: &Message) -> bool { let message_bytes = message.to_bytes(); - for (signature, public_key) in self.iter_signatures() { + for (signature, public_key) in self.signatures_and_public_keys() { if !signature.is_valid_for(&message_bytes, public_key) { return false; } @@ -32,7 +32,41 @@ impl WitnessSet { true } - pub fn iter_signatures(&self) -> impl Iterator { - self.signatures_and_public_keys.iter() + pub fn signatures_and_public_keys(&self) -> &[(Signature, PublicKey)] { + &self.signatures_and_public_keys + } +} + +#[cfg(test)] +mod tests { + use crate::Address; + + use super::*; + + #[test] + fn test_for_message_constructor() { + let key1 = PrivateKey::try_new([1; 32]).unwrap(); + let key2 = PrivateKey::try_new([2; 32]).unwrap(); + let pubkey1 = PublicKey::new_from_private_key(&key1); + let pubkey2 = PublicKey::new_from_private_key(&key2); + let addr1 = Address::from_public_key(&pubkey1); + let addr2 = Address::from_public_key(&pubkey2); + let nonces = vec![1, 2]; + let instruction = vec![1, 2, 3, 4]; + let message = Message::try_new([0; 8], vec![addr1, addr2], nonces, instruction).unwrap(); + + let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]); + + assert_eq!(witness_set.signatures_and_public_keys.len(), 2); + + let message_bytes = message.to_bytes(); + for ((signature, public_key), expected_public_key) in witness_set + .signatures_and_public_keys + .into_iter() + .zip([pubkey1, pubkey2]) + { + assert_eq!(public_key, expected_public_key); + assert!(signature.is_valid_for(&message_bytes, &expected_public_key)); + } } } diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 0f66b4a..b56b34cc 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -232,9 +232,15 @@ mod tests { #[test] fn test_start_different_intial_accounts_balances() { - let acc1_addr = vec![27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143]; + let acc1_addr = vec![ + 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, + 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, + ]; - let acc2_addr = vec![77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102]; + let acc2_addr = vec![ + 77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, + 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102, + ]; let initial_acc1 = AccountInitialData { addr: hex::encode(acc1_addr), From 9d8c74ec1e6a7bd756c106c72d36ef0a1033d594 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 13 Aug 2025 09:48:02 -0300 Subject: [PATCH 57/58] taplo --- ci_scripts/lint-ubuntu.sh | 0 nssa/program_methods/Cargo.toml | 1 - nssa/program_methods/guest/Cargo.toml | 3 +-- nssa/rust-toolchain.toml | 1 - nssa/src/signature/public_key.rs | 2 +- nssa/test_program_methods/Cargo.toml | 1 - nssa/test_program_methods/guest/Cargo.toml | 3 +-- 7 files changed, 3 insertions(+), 8 deletions(-) mode change 100644 => 100755 ci_scripts/lint-ubuntu.sh diff --git a/ci_scripts/lint-ubuntu.sh b/ci_scripts/lint-ubuntu.sh old mode 100644 new mode 100755 diff --git a/nssa/program_methods/Cargo.toml b/nssa/program_methods/Cargo.toml index 0f31c9b..ea7e36b 100644 --- a/nssa/program_methods/Cargo.toml +++ b/nssa/program_methods/Cargo.toml @@ -8,4 +8,3 @@ risc0-build = { version = "2.3.1" } [package.metadata.risc0] methods = ["guest"] - diff --git a/nssa/program_methods/guest/Cargo.toml b/nssa/program_methods/guest/Cargo.toml index 12bddbf..4b377c8 100644 --- a/nssa/program_methods/guest/Cargo.toml +++ b/nssa/program_methods/guest/Cargo.toml @@ -7,5 +7,4 @@ edition = "2021" [dependencies] risc0-zkvm = { version = "2.3.1", default-features = false, features = ['std'] } -nssa-core = {path = "../../core"} - +nssa-core = { path = "../../core" } diff --git a/nssa/rust-toolchain.toml b/nssa/rust-toolchain.toml index 8663d46..36614c3 100644 --- a/nssa/rust-toolchain.toml +++ b/nssa/rust-toolchain.toml @@ -2,4 +2,3 @@ channel = "stable" components = ["rustfmt", "rust-src"] profile = "minimal" - diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index 6e0035f..ac163d8 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -15,7 +15,7 @@ impl PublicKey { Self(value) } - pub fn try_new(value: [u8; 32]) -> Result { + pub(super) fn try_new(value: [u8; 32]) -> Result { // Check point is valid let _ = secp256k1::XOnlyPublicKey::from_byte_array(value) .map_err(|_| NssaError::InvalidPublicKey)?; diff --git a/nssa/test_program_methods/Cargo.toml b/nssa/test_program_methods/Cargo.toml index 7da210b..dded508 100644 --- a/nssa/test_program_methods/Cargo.toml +++ b/nssa/test_program_methods/Cargo.toml @@ -8,4 +8,3 @@ risc0-build = { version = "2.3.1" } [package.metadata.risc0] methods = ["guest"] - diff --git a/nssa/test_program_methods/guest/Cargo.toml b/nssa/test_program_methods/guest/Cargo.toml index 12bddbf..4b377c8 100644 --- a/nssa/test_program_methods/guest/Cargo.toml +++ b/nssa/test_program_methods/guest/Cargo.toml @@ -7,5 +7,4 @@ edition = "2021" [dependencies] risc0-zkvm = { version = "2.3.1", default-features = false, features = ['std'] } -nssa-core = {path = "../../core"} - +nssa-core = { path = "../../core" } From fe06a22589005e447bc59808baaafba1b1812ad3 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 13 Aug 2025 19:21:20 -0300 Subject: [PATCH 58/58] remove serialize and deserialize from message --- nssa/src/public_transaction/message.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nssa/src/public_transaction/message.rs b/nssa/src/public_transaction/message.rs index 784777d..68cb5fb 100644 --- a/nssa/src/public_transaction/message.rs +++ b/nssa/src/public_transaction/message.rs @@ -2,11 +2,11 @@ use nssa_core::{ account::Nonce, program::{InstructionData, ProgramId}, }; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use crate::{Address, error::NssaError, program::Program}; -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Message { pub(crate) program_id: ProgramId, pub(crate) addresses: Vec
,