From 7c24936b59443ad98ff6348e31ad60a97a33653b Mon Sep 17 00:00:00 2001 From: 0x8f701 Date: Mon, 12 Oct 2026 07:37:35 +0800 Subject: [PATCH 1/4] Use matching sibling node dependencies in compiler --- Cargo.lock | 139 ++++++++++++++++++++++++++++++----------------------- Cargo.toml | 37 +++++--------- 2 files changed, 92 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ede981e5..54b07d9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1266,7 +1266,6 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cf_utils" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", @@ -3327,6 +3326,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -4186,7 +4194,6 @@ checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" [[package]] name = "kvq" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "ambassador", "anyhow", @@ -4248,6 +4255,23 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +[[package]] +name = "libp2p-identity" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9525f3831544f7ae497bde79adf114ef127b0fbbb97edbbf692a80408636421c" +dependencies = [ + "bs58", + "ed25519-dalek", + "hkdf", + "multihash", + "prost", + "sha2", + "thiserror 2.0.18", + "tracing", + "zeroize", +] + [[package]] name = "libredox" version = "0.1.17" @@ -4468,6 +4492,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "multihash" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "577c63b00ad74d57e8c9aa870b5fccebf2fd64a308a5aee9f1bb88e4aea19447" +dependencies = [ + "unsigned-varint", +] + [[package]] name = "munge" version = "0.4.7" @@ -4814,7 +4847,6 @@ dependencies = [ [[package]] name = "parth_common" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", @@ -4839,7 +4871,6 @@ dependencies = [ [[package]] name = "parth_core" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", @@ -4874,7 +4905,6 @@ dependencies = [ [[package]] name = "parth_crypto" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "parth_core", @@ -4933,7 +4963,6 @@ dependencies = [ [[package]] name = "pderive" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "proc-macro2", "quote", @@ -5347,10 +5376,32 @@ dependencies = [ "unarray", ] +[[package]] +name = "prost" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "pser" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "bincode", @@ -5554,7 +5605,6 @@ dependencies = [ [[package]] name = "psy_api_core" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", @@ -5571,7 +5621,6 @@ dependencies = [ "parth_crypto", "pderive", "pser", - "psy_common", "psy_core", "psy_data", "psy_io", @@ -5590,7 +5639,6 @@ dependencies = [ [[package]] name = "psy_client_common" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", @@ -5621,7 +5669,6 @@ dependencies = [ [[package]] name = "psy_client_data" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", @@ -5640,47 +5687,21 @@ dependencies = [ "serde_json", "serde_repr", "thiserror 2.0.18", + "tiny-keccak", "tracing", "ts-rs", "zstd 0.13.3", ] -[[package]] -name = "psy_common" -version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" -dependencies = [ - "anyhow", - "async-trait", - "auto_impl", - "bincode", - "hashbrown 0.14.5", - "hex", - "hex-literal", - "parth_core", - "pser", - "psy_core", - "psy_data", - "rand 0.8.6", - "serde", - "serde_json", - "serde_repr", - "serde_with", - "strum 0.27.2", - "strum_macros 0.27.2", - "tokio", - "tracing", -] - [[package]] name = "psy_common_circuit" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", "bincode", "bitflags 2.13.0", + "futures", "hashbrown 0.14.5", "hex", "itertools 0.14.0", @@ -5707,7 +5728,6 @@ dependencies = [ [[package]] name = "psy_compiler" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "bincode", @@ -5737,7 +5757,6 @@ dependencies = [ [[package]] name = "psy_config" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "indexmap 2.14.0", @@ -5752,7 +5771,6 @@ dependencies = [ [[package]] name = "psy_core" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", @@ -5782,7 +5800,6 @@ dependencies = [ [[package]] name = "psy_crypto" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", @@ -5813,23 +5830,25 @@ dependencies = [ [[package]] name = "psy_data" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", "auto_impl", "bincode", + "blst", "bytemuck", "dashmap 6.2.1", "fastbloom-rs", "hex", "hex-literal", + "libp2p-identity", "lru", "parth_common", "parth_core", "pderive", "postcard", "pser", + "psy_config", "psy_core", "psy_io", "psy_serialize", @@ -5839,9 +5858,11 @@ dependencies = [ "serde_json", "serde_repr", "serde_with", + "sha2", "speedy", "strum 0.27.2", "strum_macros 0.27.2", + "thiserror 2.0.18", "tokio", "tracing", "ts-rs", @@ -5850,7 +5871,6 @@ dependencies = [ [[package]] name = "psy_dpn_circuit" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "alloy-primitives", "alloy-signer", @@ -5905,7 +5925,6 @@ dependencies = [ [[package]] name = "psy_dummy_prover" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", @@ -5925,7 +5944,6 @@ dependencies = [ "pderive", "pser", "psy_api_core", - "psy_common", "psy_core", "psy_data", "psy_io", @@ -5945,7 +5963,6 @@ dependencies = [ [[package]] name = "psy_io" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", @@ -5959,7 +5976,6 @@ dependencies = [ [[package]] name = "psy_network_circuit" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", @@ -5977,7 +5993,6 @@ dependencies = [ [[package]] name = "psy_plonky2_basic_helpers" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", @@ -6008,7 +6023,6 @@ dependencies = [ [[package]] name = "psy_plonky2_circuits" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", @@ -6025,12 +6039,18 @@ dependencies = [ "plonky2", "plonky2x", "pser", + "psy_client_data", + "psy_common_circuit", + "psy_config", "psy_core", + "psy_crypto", "psy_data", "psy_dummy_prover", + "psy_network_circuit", "psy_plonky2_basic_helpers", "psy_plonky2_common_circuits", "psy_serialize", + "psy_ups_circuit", "psy_worker_core", "rand 0.8.6", "serde", @@ -6050,7 +6070,6 @@ dependencies = [ [[package]] name = "psy_plonky2_common_circuits" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", @@ -6081,7 +6100,6 @@ dependencies = [ [[package]] name = "psy_prover" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "alloy-primitives", "alloy-signer", @@ -6135,6 +6153,7 @@ dependencies = [ "sodalite", "tempfile", "thiserror 2.0.18", + "tiny-keccak", "tokio", "tower 0.5.3", "tower-http", @@ -6145,12 +6164,12 @@ dependencies = [ "wasm-logger", "wasm-tracing", "web-sys", + "web-time", ] [[package]] name = "psy_provider" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "alloy-primitives", "alloy-signer", @@ -6206,7 +6225,6 @@ dependencies = [ [[package]] name = "psy_serialize" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "bytemuck", @@ -6217,7 +6235,6 @@ dependencies = [ [[package]] name = "psy_ups_circuit" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "alloy-primitives", "alloy-signer", @@ -6271,7 +6288,6 @@ dependencies = [ [[package]] name = "psy_vm" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", @@ -6298,7 +6314,6 @@ dependencies = [ [[package]] name = "psy_worker_core" version = "0.1.0" -source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7a86825f83ef298e2456089b3f40a8f9eaeb9841#7a86825f83ef298e2456089b3f40a8f9eaeb9841" dependencies = [ "anyhow", "async-trait", @@ -6318,13 +6333,13 @@ dependencies = [ "pderive", "pser", "psy_api_core", - "psy_common", "psy_core", "psy_data", "psy_io", "psy_provider", "psy_serialize", "rand 0.8.6", + "rayon", "serde", "serde_json", "serde_repr", @@ -8605,6 +8620,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "unsigned-varint" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" + [[package]] name = "untrusted" version = "0.7.1" diff --git a/Cargo.toml b/Cargo.toml index 85c76095..9169027d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,31 +27,18 @@ repository = "https://github.com/PsyProtocol/psy-compiler" [workspace.dependencies] zstd = { version = "0.13", features = ["zstdmt"] } -# Remote psy-node deps (active for CI/publish) -psy_vm = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7a86825f83ef298e2456089b3f40a8f9eaeb9841", package = "psy_vm", default-features = false } -psy_crypto = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7a86825f83ef298e2456089b3f40a8f9eaeb9841", package = "psy_crypto" } -psy_data = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7a86825f83ef298e2456089b3f40a8f9eaeb9841", package = "psy_client_data", default-features = false } -psy_common_core = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7a86825f83ef298e2456089b3f40a8f9eaeb9841", package = "psy_client_common" } -psy_config = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7a86825f83ef298e2456089b3f40a8f9eaeb9841", package = "psy_config" } -psy_prover = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7a86825f83ef298e2456089b3f40a8f9eaeb9841", package = "psy_prover", default-features = false } -psy_common_circuit = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7a86825f83ef298e2456089b3f40a8f9eaeb9841", package = "psy_common_circuit", default-features = false } -psy_dpn_circuit = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7a86825f83ef298e2456089b3f40a8f9eaeb9841", package = "psy_dpn_circuit", default-features = false } -psy_ups_circuit = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7a86825f83ef298e2456089b3f40a8f9eaeb9841", package = "psy_ups_circuit", default-features = false } -psy_network_circuit = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7a86825f83ef298e2456089b3f40a8f9eaeb9841", package = "psy_network_circuit", default-features = false } -kvq = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7a86825f83ef298e2456089b3f40a8f9eaeb9841", package = "kvq" } - -# Local deps for development (uncomment to use): -# psy_vm = { path = "../psy-node/client_prover/psy_vm", default-features = false } -# psy_crypto = { path = "../psy-node/client_prover/psy_core/psy_crypto" } -# psy_data = { package = "psy_client_data", path = "../psy-node/client_prover/psy_core/psy_data", default-features = false} -# psy_common_core = { package = "psy_client_common", path = "../psy-node/client_prover/psy_core/psy_common" } -# psy_config = { path = "../psy-node/client_prover/psy_core/psy_config" } -# psy_prover = { path = "../psy-node/client_prover/psy_prover", default-features = false } -# psy_common_circuit = { path = "../psy-node/client_prover/psy_circuit/psy_common_circuit", default-features = false} -# psy_dpn_circuit = { path = "../psy-node/client_prover/psy_circuit/psy_dpn_circuit", default-features = false} -# psy_ups_circuit = { path = "../psy-node/client_prover/psy_circuit/psy_ups_circuit", default-features = false} -# psy_network_circuit = { path = "../psy-node/client_prover/psy_circuit/psy_network_circuit", default-features = false} -# kvq = { path = "../psy-node/client_prover/psy_core/kvq" } +# Local psy-node deps. Preserve package aliases and feature selections. +psy_vm = { path = "../psy-node/client_prover/psy_vm", default-features = false } +psy_crypto = { path = "../psy-node/client_prover/psy_core/psy_crypto" } +psy_data = { package = "psy_client_data", path = "../psy-node/client_prover/psy_core/psy_data", default-features = false } +psy_common_core = { package = "psy_client_common", path = "../psy-node/client_prover/psy_core/psy_common" } +psy_config = { path = "../psy-node/client_prover/psy_core/psy_config" } +psy_prover = { path = "../psy-node/client_prover/psy_prover", default-features = false } +psy_common_circuit = { path = "../psy-node/client_prover/psy_circuit/psy_common_circuit", default-features = false } +psy_dpn_circuit = { path = "../psy-node/client_prover/psy_circuit/psy_dpn_circuit", default-features = false } +psy_ups_circuit = { path = "../psy-node/client_prover/psy_circuit/psy_ups_circuit", default-features = false } +psy_network_circuit = { path = "../psy-node/client_prover/psy_circuit/psy_network_circuit", default-features = false } +kvq = { path = "../psy-node/client_prover/psy_core/kvq" } # Local compiler packages psy_common = { package = "psy_compiler_common", path = "./psy-common" } From 0eeb3c60306601c1b37e4ab90e9f3cc1660ef441 Mon Sep 17 00:00:00 2001 From: 0x8f701 Date: Wed, 14 Oct 2026 14:03:26 +0800 Subject: [PATCH 2/4] Add multisig policy precompile required by node genesis --- Cargo.lock | 15 +++ Makefile | 16 ++- psy-precompiles/build.rs | 50 +++++++-- psy-precompiles/multisig_policy/Dargo.toml | 6 ++ psy-precompiles/multisig_policy/src/main.psy | 84 +++++++++++++++ psy-precompiles/precompiles.json | 10 ++ psy-precompiles/src/lib.rs | 105 +++++++++++++++++++ 7 files changed, 275 insertions(+), 11 deletions(-) create mode 100644 psy-precompiles/multisig_policy/Dargo.toml create mode 100644 psy-precompiles/multisig_policy/src/main.psy diff --git a/Cargo.lock b/Cargo.lock index 2ec63d6d..45d39a68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6731,12 +6731,14 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.2", "tokio", + "tokio-util", "tower 0.5.3", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", ] @@ -8824,6 +8826,19 @@ dependencies = [ "web-sys", ] +[[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 = "wasm-tracing" version = "2.1.0" diff --git a/Makefile b/Makefile index 3ae7507f..812c487e 100644 --- a/Makefile +++ b/Makefile @@ -79,6 +79,7 @@ WITHDRAWAL_TREE_CONTRACT_PATH := $(PWD)/psy-precompiles/withdrawal_tree DEPOSIT_TREE_CONTRACT_PATH := $(PWD)/psy-precompiles/deposit_tree FAUCET_CONTRACT_PATH := $(PWD)/psy-precompiles/faucet COUNTER_CONTRACT_PATH := $(PWD)/psy-precompiles/counter +MULTISIG_POLICY_CONTRACT_PATH := $(PWD)/psy-precompiles/multisig_policy ci: # @$(DARGO_CLI_TEST) tests/in_mod_attr_test.psy @@ -257,6 +258,12 @@ compile-counter-contract: compile-counter-abi: @cd $(COUNTER_CONTRACT_PATH) && $(DARGO) generate-abi -c PsyCounterContractRef --abi-name counter --method-names set_value increment +compile-multisig-policy-contract: + @cd $(MULTISIG_POLICY_CONTRACT_PATH) && $(DARGO) compile --contract-name=MultisigPolicyContractRef --method-names get_policy set_policy + +compile-multisig-policy-abi: + @cd $(MULTISIG_POLICY_CONTRACT_PATH) && $(DARGO) generate-abi -c MultisigPolicyContractRef --abi-name multisig_policy --output-dir $(ABI_OUTPUT_DIR) --method-names get_policy set_policy + PSY_GENESIS ?= $(PWD)/../psy-node/psy-genesis GENESIS_CONTRACTS_FILE := $(PSY_GENESIS)/genesis_contracts.json GENESIS_ABI_DIR := $(PSY_GENESIS)/genesis_abi @@ -268,7 +275,7 @@ ABI_OUTPUT_DIR := $(PWD)/target/genesis_abi gen-deploy-json: build \ compile-usdt-token-contract compile-mining-rewards-contract \ compile-deposit-tree-contract compile-withdrawal-tree-contract \ - compile-faucet-contract + compile-faucet-contract compile-multisig-policy-contract @cd $(TOKEN_CONTRACT_PATH) && $(DARGO) compile --contract-name=PsyTokenContractRef --method-names withdraw claim_deposit transfer claim batch_transfer_2 batch_transfer_5 burn claim_pow_rewards private_transfer private_claim @mv $(TOKEN_CONTRACT_PATH)/target/token.json $(TOKEN_CONTRACT_PATH)/target/token.update.json @cd $(TOKEN_CONTRACT_PATH) && $(DARGO) compile --contract-name=PsyTokenContractRef --method-names withdraw claim_deposit mint transfer claim batch_transfer_2 batch_transfer_5 burn claim_pow_rewards private_transfer private_claim @@ -285,7 +292,8 @@ gen-deploy-json: build \ $(DEPOSIT_TREE_CONTRACT_PATH)/target/deposit_tree.json \ $(WITHDRAWAL_TREE_CONTRACT_PATH)/target/withdrawal_tree.json \ $(USDT_TOKEN_CONTRACT_PATH)/target/usdt_token.json \ - $(FAUCET_CONTRACT_PATH)/target/faucet.json + $(FAUCET_CONTRACT_PATH)/target/faucet.json \ + $(MULTISIG_POLICY_CONTRACT_PATH)/target/multisig_policy.json @# Regenerate ABI files to a separate dir (avoids overwriting compiled .json) @mkdir -p $(ABI_OUTPUT_DIR) @cd $(TOKEN_CONTRACT_PATH) && $(DARGO) generate-abi -c PsyTokenContractRef --abi-name token --output-dir $(ABI_OUTPUT_DIR) --method-names withdraw claim_deposit mint transfer claim batch_transfer_2 batch_transfer_5 burn claim_pow_rewards private_transfer private_claim @@ -294,6 +302,7 @@ gen-deploy-json: build \ @cd $(DEPOSIT_TREE_CONTRACT_PATH) && $(DARGO) generate-abi -c PsyDepositTreeContractRef --abi-name deposit_tree --output-dir $(ABI_OUTPUT_DIR) --method-names get_root get_chain_root is_known_root_hash is_known_root set_chain_root append_leaf append_deposit batch_append_deposits_2 batch_append_deposits_5 @cd $(WITHDRAWAL_TREE_CONTRACT_PATH) && $(DARGO) generate-abi -c PsyWithdrawalTreeContractRef --abi-name withdrawal_tree --output-dir $(ABI_OUTPUT_DIR) --method-names get_root get_chain_root append_leaf append_withdrawal batch_append_withdrawals_2 batch_append_withdrawals_5 @cd $(FAUCET_CONTRACT_PATH) && $(DARGO) generate-abi -c PsyFaucetContractRef --abi-name faucet --output-dir $(ABI_OUTPUT_DIR) --method-names faucet + @cd $(MULTISIG_POLICY_CONTRACT_PATH) && $(DARGO) generate-abi -c MultisigPolicyContractRef --abi-name multisig_policy --output-dir $(ABI_OUTPUT_DIR) --method-names get_policy set_policy @# `compile` does not emit .abi.json — always source it from the freshly generated $(ABI_OUTPUT_DIR) @cargo run --release --package dargo --example gen_deploy_abi_json -- \ $(GENESIS_ABI_DIR) \ @@ -302,6 +311,7 @@ gen-deploy-json: build \ $(ABI_OUTPUT_DIR)/deposit_tree.abi.json:deposit_tree \ $(ABI_OUTPUT_DIR)/withdrawal_tree.abi.json:withdrawal_tree \ $(ABI_OUTPUT_DIR)/usdt_token.abi.json:usdt \ - $(ABI_OUTPUT_DIR)/faucet.abi.json:faucet + $(ABI_OUTPUT_DIR)/faucet.abi.json:faucet \ + $(ABI_OUTPUT_DIR)/multisig_policy.abi.json:multisig_policy @node -e 'const fs=require("fs"),cp=require("child_process"),crypto=require("crypto"),path=require("path"); const compilerRevision=cp.execFileSync("git",["rev-parse","HEAD"],{encoding:"utf8"}).trim(); const isSource=relativePath=>{const normalized=relativePath.replace(/\\/g,"/"); const lower=normalized.toLowerCase(); if(normalized===".compiler-artifact.json")return false; if(["Cargo.toml","Cargo.lock","rust-toolchain.toml","Makefile"].includes(normalized))return true; if(["build.rs","precompiles.json","package.json"].includes(path.posix.basename(normalized)))return true; return [".rs",".psy",".toml",".lock"].some(extension=>lower.endsWith(extension));}; const entries=cp.execFileSync("git",["ls-tree","-r","--full-tree",compilerRevision],{encoding:"utf8"}).trim().split("\n").filter(Boolean).map(line=>{const match=line.match(/^[0-9]+ blob ([0-9a-f]+)\t(.+)$$/); if(!match)throw new Error(`invalid tree entry: $${line}`); return {blob:match[1],path:match[2]};}).filter(entry=>isSource(entry.path)).sort((a,b)=>Buffer.compare(Buffer.from(a.path),Buffer.from(b.path))); const hash=crypto.createHash("sha256"); for(const entry of entries){const blob=cp.execFileSync("git",["cat-file","blob",entry.blob]); hash.update(entry.path); hash.update("\0"); hash.update(blob); hash.update("\0");} const digest=file=>{const bytes=fs.readFileSync(file); return {sha256:crypto.createHash("sha256").update(bytes).digest("hex"),byteSize:bytes.length};}; const genesis=digest("$(GENESIS_CONTRACTS_FILE)"),token=digest("$(PSY_GENESIS)/token.json"),tokenUpdate=digest("$(PSY_GENESIS)/token.update.json"); const sidecar={compilerRevision,compilerSourcesHash:hash.digest("hex"),artifactSha256:genesis.sha256,artifactByteSize:genesis.byteSize,tokenArtifactSha256:token.sha256,tokenArtifactByteSize:token.byteSize,tokenUpdateArtifactSha256:tokenUpdate.sha256,tokenUpdateArtifactByteSize:tokenUpdate.byteSize}; fs.writeFileSync("$(PSY_GENESIS)/.genesis_contracts.compiler-artifact.json",JSON.stringify(sidecar,null,2)+"\n");' @echo "psy-genesis contract artifact + genesis_abi/ + compiler provenance regenerated" diff --git a/psy-precompiles/build.rs b/psy-precompiles/build.rs index e9cb0224..8f08dc55 100644 --- a/psy-precompiles/build.rs +++ b/psy-precompiles/build.rs @@ -90,14 +90,47 @@ fn compile_one_contract(contract: &ContractConfigGoldilocks, manifest_dir: &str, &interpret_result.compile_results, ); - let state_tree_height = AbiExtractor::new(contract.contract_name.clone()) - .compute_state_tree_height(&mut interpret_result.ctx.program); + let extractor = AbiExtractor::new(contract.contract_name.clone()); + let inferred_height = extractor.compute_state_tree_height(&mut interpret_result.ctx.program); + let state_tree_height = match contract.declared_state_tree_height { + Some(declared) => { + if inferred_height > u16::from(declared) || declared > 32 { + return Err(anyhow::anyhow!("Invalid declared state tree height {} for {} (inferred {})", declared, contract.name, inferred_height).into()); + } + u16::from(declared) + } + None => inferred_height, + }; + if state_tree_height > 32 { + return Err(anyhow::anyhow!("Invalid state tree height for {}", contract.name).into()); + } + if interpret_result.compile_results.len() != contract.method_names.len() + || contract.method_names.iter().any(|name| interpret_result.compile_results.iter().filter(|function| &function.name == name).count() != 1) { + return Err(anyhow::anyhow!("Required precompile methods missing or duplicated for {}", contract.name).into()); + } + let method_metadata: std::collections::HashMap = interpret_result + .compile_results + .iter() + .map(|function| (function.name.clone(), (function.method_id, function.is_view_function()))) + .collect(); + let abi = extractor + .extract_abi(&mut interpret_result.ctx.program, state_tree_height, &method_metadata) + .map_err(|error| anyhow::anyhow!("Failed to extract ABI for {}: {}", contract.name, error))?; + if abi.contract.state_tree_height != state_tree_height { + return Err(anyhow::anyhow!("ABI/artifact state tree height mismatch for {}", contract.name).into()); + } let artifact = serde_json::json!({ "state_tree_height": state_tree_height, "circuit_definitions": &interpret_result.compile_results, + "abi": abi, }); - use dargo::cli::save_build_artifact_to_file; + // `compile` does not emit .abi.json — the artifact/ABI + // agreement test sources `.abi.json` from OUT_DIR. + if let Err(e) = save_build_artifact_to_file(&abi, &format!("{}.abi", contract.name), out_path) { + println!("cargo:warning=Failed to save ABI artifact to OUT_DIR for {}: {}", contract.name, e); + } + if let Err(e) = save_build_artifact_to_file(&artifact, &contract.name, &target_dir) { println!("cargo:warning=Failed to save build artifact to target dir for {}: {}", contract.name, e); } @@ -127,11 +160,12 @@ fn generate_precompile_api(config: &PrecompilesBuildConfigGoldilocks, out_path: // Runtime lazy loading for {{ contract.name }} contract fn load_{{ contract.name }}_functions() -> Vec { let json_data = include_str!(concat!(env!("OUT_DIR"), "/{{ contract.name }}.json")); - serde_json::from_str::(json_data) - .ok() - .and_then(|v| v.get("circuit_definitions").cloned()) - .and_then(|v| serde_json::from_value(v).ok()) - .unwrap_or_default() + let artifact: serde_json::Value = serde_json::from_str(json_data).expect("Invalid required precompile artifact"); + assert_eq!(artifact["state_tree_height"], artifact["abi"]["contract"]["state_tree_height"]); + {% if contract.declared_state_tree_height is defined and contract.declared_state_tree_height is not none %} + assert_eq!(artifact["state_tree_height"], {{ contract.declared_state_tree_height }}); + {% endif %} + serde_json::from_value(artifact["circuit_definitions"].clone()).expect("Invalid required precompile functions") } static {{ contract.name | upper }}_FUNCTIONS_ONCE: std::sync::OnceLock> = std::sync::OnceLock::new(); diff --git a/psy-precompiles/multisig_policy/Dargo.toml b/psy-precompiles/multisig_policy/Dargo.toml new file mode 100644 index 00000000..aadf2936 --- /dev/null +++ b/psy-precompiles/multisig_policy/Dargo.toml @@ -0,0 +1,6 @@ +[package] +name = "multisig_policy" +type = "bin" +authors = [""] + +[dependencies] diff --git a/psy-precompiles/multisig_policy/src/main.psy b/psy-precompiles/multisig_policy/src/main.psy new file mode 100644 index 00000000..86944a84 --- /dev/null +++ b/psy-precompiles/multisig_policy/src/main.psy @@ -0,0 +1,84 @@ +use std::prelude::*; + +#[contract] +#[derive(Storage)] +pub struct MultisigPolicyContract { + pub header: Hash, + pub members: [Hash; 3], +} + +impl MultisigPolicyContractRef { + fn canonical_limb_halves(limb: Felt) -> [Felt; 2] { + let bits = split_bits(limb, 64); + let mut lo: Felt = 0; + let mut hi: Felt = 0; + let mut factor: Felt = 1; + for i in 0..32 { + lo = lo + bits[i] * factor; + hi = hi + bits[i + 32] * factor; + factor = factor * 2; + } + assert(hi < 4294967295 || (hi == 4294967295 && lo == 0), "member limb is not canonical"); + [lo, hi] + } + + // DPN comparisons are 63-bit; compare canonical limbs through 32-bit halves. + fn limb_less(a: Felt, b: Felt) -> bool { + let a_halves = Self::canonical_limb_halves(a); + let b_halves = Self::canonical_limb_halves(b); + a_halves[1] < b_halves[1] || (a_halves[1] == b_halves[1] && a_halves[0] < b_halves[0]) + } + + fn member_less(a: Hash, b: Hash) -> bool { + Self::limb_less(a[0], b[0]) + || (a[0] == b[0] && Self::limb_less(a[1], b[1])) + || (a[0] == b[0] && a[1] == b[1] && Self::limb_less(a[2], b[2])) + || (a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && Self::limb_less(a[3], b[3])) + } + + fn validate_members(members: [Hash; 3]) { + let zero: Hash = [0, 0, 0, 0]; + for i in 0..3 { + assert(members[i] != zero, "member is zero"); + for j in 0..4 { + let halves = Self::canonical_limb_halves(members[i][j]); + assert(halves[0] + halves[1] * 4294967296 == members[i][j], "invalid member limb"); + } + } + assert(Self::member_less(members[0], members[1]), "members not strictly ordered"); + assert(Self::member_less(members[1], members[2]), "members not strictly ordered"); + } + + #[contract_method] + pub fn get_policy() -> (Hash, [Hash; 3]) { + let c = MultisigPolicyContractRef::new(ContractMetadata::current()); + (c.header.get(), [c.members[0].get(), c.members[1].get(), c.members[2].get()]) + } + + #[contract_method] + pub fn set_policy(expected_header: Hash, expected_members: [Hash; 3], next_members: [Hash; 3]) { + let c = MultisigPolicyContractRef::new(ContractMetadata::current()); + let header = c.header.get(); + let members = [c.members[0].get(), c.members[1].get(), c.members[2].get()]; + assert(header == expected_header, "expected header mismatch"); + for i in 0..3 { + assert(members[i] == expected_members[i], "expected members mismatch"); + } + Self::validate_members(next_members); + let zero: Hash = [0, 0, 0, 0]; + let mut version: Felt = 1; + if header == zero && members[0] == zero && members[1] == zero && members[2] == zero { + version = 1; + } else { + assert(header[1] == 2 && header[2] == 3 && header[3] == 0, "invalid policy header"); + let version_halves = Self::canonical_limb_halves(header[0]); + assert(version_halves[1] == 0 && version_halves[0] != 0, "invalid policy version"); + assert(version_halves[0] < 4294967295, "policy version overflow"); + Self::validate_members(members); + assert(members[0] != next_members[0] || members[1] != next_members[1] || members[2] != next_members[2], "policy members unchanged"); + version = header[0] + 1; + }; + c.header = [version, 2, 3, 0]; + c.members = next_members; + } +} diff --git a/psy-precompiles/precompiles.json b/psy-precompiles/precompiles.json index 82777e0c..b10862ae 100644 --- a/psy-precompiles/precompiles.json +++ b/psy-precompiles/precompiles.json @@ -85,6 +85,16 @@ "method_names": [ "faucet" ] + }, + { + "name": "multisig_policy", + "path": "multisig_policy", + "contract_name": "MultisigPolicyContractRef", + "declared_state_tree_height": 4, + "method_names": [ + "get_policy", + "set_policy" + ] } ] } diff --git a/psy-precompiles/src/lib.rs b/psy-precompiles/src/lib.rs index 0022e747..81d7c061 100644 --- a/psy-precompiles/src/lib.rs +++ b/psy-precompiles/src/lib.rs @@ -8,6 +8,110 @@ include!(concat!(env!("OUT_DIR"), "/precompiled_contracts.rs")); mod tests { use super::*; + use psy_vm::dpn::eval::executor::{ExecutionContext, InMemoryStateBackend, StateBackend, VmExecutor}; + + const USER_ID: u64 = 524288; + const MEMBERS: [[u64; 4]; 3] = [[1, 0, 0, 0], [1, 0, 0, 1], [0xffff_ffff_0000_0000, 0, 0, 0]]; + + fn policy_state(header: [u64; 4], members: [[u64; 4]; 3]) -> InMemoryStateBackend { + let mut state = InMemoryStateBackend::new(); + state.set_hash(USER_ID, 6, 0, header); + for (index, member) in members.into_iter().enumerate() { + state.set_hash(USER_ID, 6, index as u64 + 1, member); + } + state + } + + fn set_policy(state: InMemoryStateBackend, expected: [u64; 4], members: [[u64; 4]; 3], next: [[u64; 4]; 3]) -> (bool, InMemoryStateBackend) { + let context = ExecutionContext { + user_id: USER_ID, contract_id: 6, caller_contract_id: 6, + checkpoint_id: 1, nonce: 1, user_public_key_hash: [0; 4], + session_proof_tree_root: [0; 4], + }; + let inputs: Vec = expected.into_iter().chain(members.into_iter().flatten()).chain(next.into_iter().flatten()).collect(); + let mut executor = VmExecutor::new(state.clone()); + let function = get_precompiled_contract_function_by_name("multisig_policy", "set_policy").unwrap(); + let result = executor.execute(function, &context, &inputs).unwrap(); + let mut ending = state; + if result.success { + ending.apply_overlay(executor.write_overlay()); + } + (result.success, ending) + } + + #[test] + fn policy_bootstrap_rotation_and_stale_cas() { + assert_eq!(MULTISIG_POLICY_CONTRACT_ID, psy_vm::ups::multisig::MULTISIG_POLICY_CONTRACT_ID); + let (success, state) = set_policy(InMemoryStateBackend::new(), [0; 4], [[0; 4]; 3], MEMBERS); + assert!(success); + assert_eq!(state.get_contract_hash(USER_ID, 6, 0).unwrap(), [1, 2, 3, 0]); + for (index, member) in MEMBERS.into_iter().enumerate() { + assert_eq!(state.get_contract_hash(USER_ID, 6, index as u64 + 1).unwrap(), member); + } + for slot in 4..16 { + assert_eq!(state.get_contract_hash(USER_ID, 6, slot).unwrap(), [0; 4]); + } + let context = ExecutionContext { + user_id: USER_ID, contract_id: 6, caller_contract_id: 6, + checkpoint_id: 1, nonce: 1, user_public_key_hash: [0; 4], + session_proof_tree_root: [0; 4], + }; + let mut reader = VmExecutor::new(state.clone()); + let result = reader.execute(get_precompiled_contract_function_by_name("multisig_policy", "get_policy").unwrap(), &context, &[]).unwrap(); + assert!(result.success); + assert_eq!(result.outputs, [1, 2, 3, 0].into_iter().chain(MEMBERS.into_iter().flatten()).collect::>()); + assert!(result.state_writes.is_empty()); + let mut next = MEMBERS; + next[1][3] = 2; + let (success, rotated) = set_policy(state.clone(), [1, 2, 3, 0], MEMBERS, next); + assert!(success); + assert_eq!(rotated.get_contract_hash(USER_ID, 6, 0).unwrap(), [2, 2, 3, 0]); + assert_eq!(rotated.get_contract_hash(USER_ID, 6, 2).unwrap(), next[1]); + let last_header = [u32::MAX as u64 - 1, 2, 3, 0]; + let (success, last) = set_policy(policy_state(last_header, MEMBERS), last_header, MEMBERS, next); + assert!(success); + assert_eq!(last.get_contract_hash(USER_ID, 6, 0).unwrap(), [u32::MAX as u64, 2, 3, 0]); + assert!(!set_policy(rotated, [1, 2, 3, 0], MEMBERS, MEMBERS).0); + let mut stale_members = MEMBERS; + stale_members[0][0] = 2; + assert!(!set_policy(state, [1, 2, 3, 0], stale_members, next).0); + } + + #[test] + fn policy_rejects_invalid_members_and_versions() { + let header = [1, 2, 3, 0]; + let state = policy_state(header, MEMBERS); + assert!(!set_policy(state.clone(), header, MEMBERS, MEMBERS).0); + let mut zero = MEMBERS; + zero[0] = [0; 4]; + let mut duplicate = MEMBERS; + duplicate[1] = duplicate[0]; + let mut unordered = MEMBERS; + unordered.swap(0, 1); + let mut noncanonical = MEMBERS; + noncanonical[2][0] = 0xffff_ffff_0000_0001; + for next in [zero, duplicate, unordered, noncanonical] { + assert!(!set_policy(state.clone(), header, MEMBERS, next).0); + } + let mut next = MEMBERS; + next[1][3] = 2; + for invalid in [[0, 2, 3, 0], [1, 1, 3, 0], [1, 2, 4, 0], [1, 2, 3, 1], [u32::MAX as u64, 2, 3, 0], [1u64 << 32, 2, 3, 0], [0; 4]] { + assert!(!set_policy(policy_state(invalid, MEMBERS), invalid, MEMBERS, next).0); + } + } + + #[test] + fn policy_artifact_and_abi_agree() { + let artifact: serde_json::Value = serde_json::from_str(include_str!(concat!(env!("OUT_DIR"), "/multisig_policy.json"))).unwrap(); + let abi: serde_json::Value = serde_json::from_str(include_str!(concat!(env!("OUT_DIR"), "/multisig_policy.abi.json"))).unwrap(); + assert_eq!(artifact["state_tree_height"], 4); + assert_eq!(abi["contract"]["state_tree_height"], 4); + assert_eq!(artifact["abi"], abi); + let mut methods = list_contract_methods("multisig_policy"); + methods.sort(); + assert_eq!(methods, ["get_policy", "set_policy"]); + } + #[test] fn test_contract_id_lookup() { // Test contract ID lookup by name @@ -17,6 +121,7 @@ mod tests { assert_eq!(get_contract_id_by_name("withdrawal_tree"), Some(3)); assert_eq!(get_contract_id_by_name("usdt_token"), Some(4)); assert_eq!(get_contract_id_by_name("faucet"), Some(5)); + assert_eq!(get_contract_id_by_name("multisig_policy"), Some(6)); assert_eq!(get_contract_id_by_name("nonexistent"), None); // Test function lookup by contract ID From 43b1619cc7215395386912d546228dd4bf1eb12e Mon Sep 17 00:00:00 2001 From: 0x8f701 Date: Wed, 14 Oct 2026 18:35:55 +0800 Subject: [PATCH 3/4] checkpoint: preserve unfinished reward claim integration Save reviewed source before token-owned Ethereum reward minting. Proof boundary and end-to-end payout remain unfinished. --- Makefile | 28 +- psy-dargo-cli/examples/gen_deploy_abi_json.rs | 4 +- psy-dargo-cli/examples/gen_deploy_json.rs | 4 +- psy-precompiles/mining_rewards/Dargo.toml | 6 - psy-precompiles/mining_rewards/src/main.psy | 315 ------------------ psy-precompiles/precompiles.json | 26 +- psy-precompiles/src/lib.rs | 107 ++---- psy-precompiles/token/Dargo.toml | 1 - psy-precompiles/token/src/main.psy | 32 +- tests/Dargo.toml | 1 - tests/mining_rewards.psy | 246 -------------- 11 files changed, 53 insertions(+), 717 deletions(-) delete mode 100644 psy-precompiles/mining_rewards/Dargo.toml delete mode 100644 psy-precompiles/mining_rewards/src/main.psy delete mode 100644 tests/mining_rewards.psy diff --git a/Makefile b/Makefile index 812c487e..ae2dfc05 100644 --- a/Makefile +++ b/Makefile @@ -74,7 +74,6 @@ DARGO_CLI_TEST = RUST_LOG=${LOG_LEVEL} ./target/${PROFILE}/dargo test --file DARGO = RUST_LOG=$(LOG_LEVEL) $(PWD)/target/${PROFILE}/dargo USDT_TOKEN_CONTRACT_PATH := $(PWD)/psy-precompiles/usdt_token TOKEN_CONTRACT_PATH := $(PWD)/psy-precompiles/token -MINING_REWARDS_CONTRACT_PATH := $(PWD)/psy-precompiles/mining_rewards WITHDRAWAL_TREE_CONTRACT_PATH := $(PWD)/psy-precompiles/withdrawal_tree DEPOSIT_TREE_CONTRACT_PATH := $(PWD)/psy-precompiles/deposit_tree FAUCET_CONTRACT_PATH := $(PWD)/psy-precompiles/faucet @@ -219,11 +218,7 @@ interpret: @RUST_LOG=${LOG_LEVEL} ./target/${PROFILE}/dargo execute --program-dir $(dir ${FILE}) --debug --entry-path $(notdir ${FILE}) --parameters ${PARAMETERS} compile-token-contract: - @cd $(TOKEN_CONTRACT_PATH) && $(DARGO) compile --contract-name=PsyTokenContractRef --method-names withdraw claim_deposit mint transfer claim batch_transfer_2 batch_transfer_5 burn claim_pow_rewards private_transfer private_claim - - -compile-mining-rewards-contract: - @cd $(MINING_REWARDS_CONTRACT_PATH) && $(DARGO) compile --contract-name=PsyPOWMiningRewardsClaimContractRef --method-names start_session end_session claim_guta_rewards_1 claim_guta_rewards_2 claim_guta_rewards_5 claim_guta_rewards_10 + @cd $(TOKEN_CONTRACT_PATH) && $(DARGO) compile --contract-name=PsyTokenContractRef --method-names withdraw claim_deposit mint transfer claim batch_transfer_2 batch_transfer_5 burn private_transfer private_claim compile-usdt-token-contract: @cd $(USDT_TOKEN_CONTRACT_PATH) && $(DARGO) compile --contract-name=USDTTokenContractRef --method-names withdraw claim_deposit mint transfer claim batch_transfer_2 batch_transfer_5 burn private_transfer private_claim @@ -232,7 +227,7 @@ compile-usdt-token-abi: @cd $(USDT_TOKEN_CONTRACT_PATH) && $(DARGO) generate-abi -c USDTTokenContractRef --abi-name usdt_token --method-names withdraw claim_deposit mint transfer claim batch_transfer_2 batch_transfer_5 burn private_transfer private_claim compile-token-abi: - @cd $(TOKEN_CONTRACT_PATH) && $(DARGO) generate-abi -c PsyTokenContractRef --abi-name token --method-names withdraw claim_deposit mint transfer claim batch_transfer_2 batch_transfer_5 burn claim_pow_rewards private_transfer private_claim + @cd $(TOKEN_CONTRACT_PATH) && $(DARGO) generate-abi -c PsyTokenContractRef --abi-name token --method-names withdraw claim_deposit mint transfer claim batch_transfer_2 batch_transfer_5 burn private_transfer private_claim compile-withdrawal-tree-contract: @cd $(WITHDRAWAL_TREE_CONTRACT_PATH) && $(DARGO) compile --contract-name=PsyWithdrawalTreeContractRef --method-names set_chain_root get_root get_chain_root append_leaf append_withdrawal batch_append_withdrawals_2 batch_append_withdrawals_5 @@ -273,12 +268,12 @@ ABI_OUTPUT_DIR := $(PWD)/target/genesis_abi # psy-genesis contract artifact, then regenerate ABI files into # psy-genesis/genesis_abi/. psy-genesis owns all token artifacts. gen-deploy-json: build \ - compile-usdt-token-contract compile-mining-rewards-contract \ + compile-usdt-token-contract \ compile-deposit-tree-contract compile-withdrawal-tree-contract \ compile-faucet-contract compile-multisig-policy-contract - @cd $(TOKEN_CONTRACT_PATH) && $(DARGO) compile --contract-name=PsyTokenContractRef --method-names withdraw claim_deposit transfer claim batch_transfer_2 batch_transfer_5 burn claim_pow_rewards private_transfer private_claim + @cd $(TOKEN_CONTRACT_PATH) && $(DARGO) compile --contract-name=PsyTokenContractRef --method-names withdraw claim_deposit transfer claim batch_transfer_2 batch_transfer_5 burn private_transfer private_claim @mv $(TOKEN_CONTRACT_PATH)/target/token.json $(TOKEN_CONTRACT_PATH)/target/token.update.json - @cd $(TOKEN_CONTRACT_PATH) && $(DARGO) compile --contract-name=PsyTokenContractRef --method-names withdraw claim_deposit mint transfer claim batch_transfer_2 batch_transfer_5 burn claim_pow_rewards private_transfer private_claim + @cd $(TOKEN_CONTRACT_PATH) && $(DARGO) compile --contract-name=PsyTokenContractRef --method-names withdraw claim_deposit mint transfer claim batch_transfer_2 batch_transfer_5 burn private_transfer private_claim @mkdir -p $(PSY_GENESIS) $(GENESIS_ABI_DIR) @test -f $(TOKEN_CONTRACT_PATH)/target/token.json || { echo "error: missing compiled token artifact: $(TOKEN_CONTRACT_PATH)/target/token.json"; exit 1; } @test -f $(TOKEN_CONTRACT_PATH)/target/token.update.json || { echo "error: missing compiled token update artifact: $(TOKEN_CONTRACT_PATH)/target/token.update.json"; exit 1; } @@ -288,17 +283,15 @@ gen-deploy-json: build \ @cargo run --release --package dargo --example gen_deploy_json -- \ $(GENESIS_CONTRACTS_FILE) \ $(TOKEN_CONTRACT_PATH)/target/token.json \ - $(MINING_REWARDS_CONTRACT_PATH)/target/mining_rewards.json \ + $(MULTISIG_POLICY_CONTRACT_PATH)/target/multisig_policy.json \ $(DEPOSIT_TREE_CONTRACT_PATH)/target/deposit_tree.json \ $(WITHDRAWAL_TREE_CONTRACT_PATH)/target/withdrawal_tree.json \ $(USDT_TOKEN_CONTRACT_PATH)/target/usdt_token.json \ - $(FAUCET_CONTRACT_PATH)/target/faucet.json \ - $(MULTISIG_POLICY_CONTRACT_PATH)/target/multisig_policy.json + $(FAUCET_CONTRACT_PATH)/target/faucet.json @# Regenerate ABI files to a separate dir (avoids overwriting compiled .json) @mkdir -p $(ABI_OUTPUT_DIR) - @cd $(TOKEN_CONTRACT_PATH) && $(DARGO) generate-abi -c PsyTokenContractRef --abi-name token --output-dir $(ABI_OUTPUT_DIR) --method-names withdraw claim_deposit mint transfer claim batch_transfer_2 batch_transfer_5 burn claim_pow_rewards private_transfer private_claim + @cd $(TOKEN_CONTRACT_PATH) && $(DARGO) generate-abi -c PsyTokenContractRef --abi-name token --output-dir $(ABI_OUTPUT_DIR) --method-names withdraw claim_deposit mint transfer claim batch_transfer_2 batch_transfer_5 burn private_transfer private_claim @cd $(USDT_TOKEN_CONTRACT_PATH) && $(DARGO) generate-abi -c USDTTokenContractRef --abi-name usdt_token --output-dir $(ABI_OUTPUT_DIR) --method-names withdraw claim_deposit mint transfer claim batch_transfer_2 batch_transfer_5 burn private_transfer private_claim - @cd $(MINING_REWARDS_CONTRACT_PATH) && $(DARGO) generate-abi -c PsyPOWMiningRewardsClaimContractRef --abi-name mining_rewards --output-dir $(ABI_OUTPUT_DIR) --method-names start_session end_session claim_guta_rewards_1 claim_guta_rewards_2 claim_guta_rewards_5 claim_guta_rewards_10 @cd $(DEPOSIT_TREE_CONTRACT_PATH) && $(DARGO) generate-abi -c PsyDepositTreeContractRef --abi-name deposit_tree --output-dir $(ABI_OUTPUT_DIR) --method-names get_root get_chain_root is_known_root_hash is_known_root set_chain_root append_leaf append_deposit batch_append_deposits_2 batch_append_deposits_5 @cd $(WITHDRAWAL_TREE_CONTRACT_PATH) && $(DARGO) generate-abi -c PsyWithdrawalTreeContractRef --abi-name withdrawal_tree --output-dir $(ABI_OUTPUT_DIR) --method-names get_root get_chain_root append_leaf append_withdrawal batch_append_withdrawals_2 batch_append_withdrawals_5 @cd $(FAUCET_CONTRACT_PATH) && $(DARGO) generate-abi -c PsyFaucetContractRef --abi-name faucet --output-dir $(ABI_OUTPUT_DIR) --method-names faucet @@ -307,11 +300,10 @@ gen-deploy-json: build \ @cargo run --release --package dargo --example gen_deploy_abi_json -- \ $(GENESIS_ABI_DIR) \ $(ABI_OUTPUT_DIR)/token.abi.json:token \ - $(ABI_OUTPUT_DIR)/mining_rewards.abi.json:mining_rewards \ + $(ABI_OUTPUT_DIR)/multisig_policy.abi.json:multisig_policy \ $(ABI_OUTPUT_DIR)/deposit_tree.abi.json:deposit_tree \ $(ABI_OUTPUT_DIR)/withdrawal_tree.abi.json:withdrawal_tree \ $(ABI_OUTPUT_DIR)/usdt_token.abi.json:usdt \ - $(ABI_OUTPUT_DIR)/faucet.abi.json:faucet \ - $(ABI_OUTPUT_DIR)/multisig_policy.abi.json:multisig_policy + $(ABI_OUTPUT_DIR)/faucet.abi.json:faucet @node -e 'const fs=require("fs"),cp=require("child_process"),crypto=require("crypto"),path=require("path"); const compilerRevision=cp.execFileSync("git",["rev-parse","HEAD"],{encoding:"utf8"}).trim(); const isSource=relativePath=>{const normalized=relativePath.replace(/\\/g,"/"); const lower=normalized.toLowerCase(); if(normalized===".compiler-artifact.json")return false; if(["Cargo.toml","Cargo.lock","rust-toolchain.toml","Makefile"].includes(normalized))return true; if(["build.rs","precompiles.json","package.json"].includes(path.posix.basename(normalized)))return true; return [".rs",".psy",".toml",".lock"].some(extension=>lower.endsWith(extension));}; const entries=cp.execFileSync("git",["ls-tree","-r","--full-tree",compilerRevision],{encoding:"utf8"}).trim().split("\n").filter(Boolean).map(line=>{const match=line.match(/^[0-9]+ blob ([0-9a-f]+)\t(.+)$$/); if(!match)throw new Error(`invalid tree entry: $${line}`); return {blob:match[1],path:match[2]};}).filter(entry=>isSource(entry.path)).sort((a,b)=>Buffer.compare(Buffer.from(a.path),Buffer.from(b.path))); const hash=crypto.createHash("sha256"); for(const entry of entries){const blob=cp.execFileSync("git",["cat-file","blob",entry.blob]); hash.update(entry.path); hash.update("\0"); hash.update(blob); hash.update("\0");} const digest=file=>{const bytes=fs.readFileSync(file); return {sha256:crypto.createHash("sha256").update(bytes).digest("hex"),byteSize:bytes.length};}; const genesis=digest("$(GENESIS_CONTRACTS_FILE)"),token=digest("$(PSY_GENESIS)/token.json"),tokenUpdate=digest("$(PSY_GENESIS)/token.update.json"); const sidecar={compilerRevision,compilerSourcesHash:hash.digest("hex"),artifactSha256:genesis.sha256,artifactByteSize:genesis.byteSize,tokenArtifactSha256:token.sha256,tokenArtifactByteSize:token.byteSize,tokenUpdateArtifactSha256:tokenUpdate.sha256,tokenUpdateArtifactByteSize:tokenUpdate.byteSize}; fs.writeFileSync("$(PSY_GENESIS)/.genesis_contracts.compiler-artifact.json",JSON.stringify(sidecar,null,2)+"\n");' @echo "psy-genesis contract artifact + genesis_abi/ + compiler provenance regenerated" diff --git a/psy-dargo-cli/examples/gen_deploy_abi_json.rs b/psy-dargo-cli/examples/gen_deploy_abi_json.rs index 32426776..8c4b4e7c 100644 --- a/psy-dargo-cli/examples/gen_deploy_abi_json.rs +++ b/psy-dargo-cli/examples/gen_deploy_abi_json.rs @@ -18,7 +18,7 @@ /// cargo run --release --example gen_deploy_abi_json -- \ /// ../../psy-genesis/genesis_abi \ /// ../psy-precompiles/token/target/token.abi.json:token \ -/// ../psy-precompiles/mining_rewards/target/mining_rewards.abi.json:mining_rewards +/// ../../target/genesis_abi/multisig_policy.abi.json:multisig_policy use std::{env, fs, path::Path}; // Same default genesis deployer user id as gen_deploy_json: 0 is reserved, so @@ -138,7 +138,7 @@ mod tests { #[test] fn reads_state_tree_height_from_contract_abi() { let abi = serde_json::json!({"contract": {"state_tree_height": 23}}); - assert_eq!(state_tree_height_from_abi(&abi, "mining_rewards.abi.json").unwrap(), 23); + assert_eq!(state_tree_height_from_abi(&abi, "contract.abi.json").unwrap(), 23); } #[test] diff --git a/psy-dargo-cli/examples/gen_deploy_json.rs b/psy-dargo-cli/examples/gen_deploy_json.rs index ddcb13ba..0bdc7164 100644 --- a/psy-dargo-cli/examples/gen_deploy_json.rs +++ b/psy-dargo-cli/examples/gen_deploy_json.rs @@ -11,8 +11,8 @@ /// cargo run --release --example gen_deploy_json -- \ /// ../../psy-genesis/genesis_contracts.json \ /// ../psy-precompiles/token/target/token.json:token \ -/// ../psy-precompiles/mining_rewards/target/mining_rewards.json: -/// mining_rewards +/// ../psy-precompiles/multisig_policy/target/multisig_policy.json: +/// multisig_policy /// /// Each input can optionally specify deployer and name as: /// path:deployer_user_id:name If only deployer is given (path:deployer_user_id), diff --git a/psy-precompiles/mining_rewards/Dargo.toml b/psy-precompiles/mining_rewards/Dargo.toml deleted file mode 100644 index 873a66e6..00000000 --- a/psy-precompiles/mining_rewards/Dargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "mining_rewards" -type = "bin" -authors = [""] - -[dependencies] diff --git a/psy-precompiles/mining_rewards/src/main.psy b/psy-precompiles/mining_rewards/src/main.psy deleted file mode 100644 index 79a87cfc..00000000 --- a/psy-precompiles/mining_rewards/src/main.psy +++ /dev/null @@ -1,315 +0,0 @@ -struct SimpleMerkleProofSiblingLevel { - pub sibling: [Felt; 4], - pub parent_tag: [Felt; 4], -} - -struct MiningRewardMerkleProof { - pub root: [Felt; 4], - pub leaf_left: [Felt; 4], - pub leaf_right: [Felt; 4], - pub leaf_tag: [Felt; 4], - pub index: Felt, - pub proof_height: Felt, - pub reward_tree_tag_preimage: [Felt; 4], - pub siblings: [SimpleMerkleProofSiblingLevel; 21], -} - -#[derive(Storage)] -struct PersistentState { - pub claimed_rewards: Felt, - pub last_claimed_checkpoint_id: Felt, - pub last_sealed_checkpoint_id: Felt, -} - -#[contract] -#[derive(Storage)] -pub struct PsyPOWMiningRewardsClaimContract { - pub persistent_state: PersistentState, - pub claimed_guta_nullifiers: [Felt; 16777216], - pub claimed_contract_deployment_nullifiers: [Felt; 1048576], - pub claimed_user_registration_nullifiers: [Felt; 1048576] -} - -impl MiningRewardMerkleProof { - pub fn compute_root_and_nullifier_index(self) -> ([Felt; 4], Felt) { - let proof_height = self.proof_height; - let max_height = 21; - assert(proof_height <= max_height, "proof height too large"); - - let mut nullifier_base = 0; - let mut nullifier_level_start_index_multiplier = 1; - - let index = self.index; - let max_index = 1 << proof_height; - - assert(index < max_index, "index exceeds proof height"); - - // Step 1: Compute leaf hash from three components - // hash(hash(leaf_left, leaf_right), leaf_tag) - let left_right_hash = hash_two_to_one(self.leaf_left, self.leaf_right); - let leaf_hash = hash_two_to_one(left_right_hash, self.leaf_tag); - - // Step 2: Traverse up MAX_HEIGHT levels - let mut current_node_value = leaf_hash; - - for i in 0u32..(21 as u32) { - let level_index = i as Felt; - let sibling_level = self.siblings[level_index]; - let sibling = sibling_level.sibling; - let parent_tag = sibling_level.parent_tag; - let index_bit = (index >> level_index) & 1; - if level_index < proof_height { - // Combine current node with sibling - let branch_path_hash = if index_bit == 0 { - hash_two_to_one(current_node_value, sibling) - } else { - hash_two_to_one(sibling, current_node_value) - }; - - // Combine with parent_tag - current_node_value = hash_two_to_one(branch_path_hash, parent_tag); - nullifier_base = nullifier_base + nullifier_level_start_index_multiplier; - nullifier_level_start_index_multiplier = nullifier_level_start_index_multiplier * 2; - } else { - let zero_hash = [0, 0, 0, 0]; - assert(sibling == zero_hash, "unused sibling must be zero"); - assert(parent_tag == zero_hash, "unused parent_tag must be zero"); - - assert(index_bit == 0, "unused index bit must be zero"); - } - } - - // Step 3: Verify root matches - assert(current_node_value == self.root, "root mismatch"); - let nullifier_final_index = nullifier_base + index; - (current_node_value, nullifier_final_index) - } -} - -impl PsyPOWMiningRewardsClaimContractRef { - fn check_guta_reward(total_proofs_in_checkpoint: Felt, total_fees_paid: Felt, proposed_reward: Felt) -> bool { - let user_transaction_inclusion_fee = 5000000000; - - let total_reward_if_all_claim = proposed_reward * total_proofs_in_checkpoint; - proposed_reward < user_transaction_inclusion_fee && total_reward_if_all_claim <= total_fees_paid - } - - fn claim_single_guta_reward( - checkpoint_id: Felt, - proof: MiningRewardMerkleProof, - proposed_reward: Felt - ) { - let c = PsyPOWMiningRewardsClaimContractRef::new(ContractMetadata::current()); - let user_id_hash = hash([get_user_id()]); - - let expected_guta_rewards_tree_root = get_gutas_root(checkpoint_id); - let total_proofs = get_gutas_completed(checkpoint_id); - let total_fees = get_guta_fees_collected(checkpoint_id); - assert(check_guta_reward(total_proofs, total_fees, proposed_reward), "invalid reward amount"); - - let result: ([Felt; 4], Felt) = proof.compute_root_and_nullifier_index(); - let computed_root = result.0; - let nullifier_index = result.1; - - assert(computed_root == expected_guta_rewards_tree_root, "guta tree root mismatch"); - - assert_eq( - proof.reward_tree_tag_preimage[0], - get_user_id(), - "reward_tree_tag_preimage not for current user"); - - let expected_reward_tree_tag_hash = hash_two_to_one(proof.reward_tree_tag_preimage, proof.reward_tree_tag_preimage); - - assert(proof.leaf_tag == expected_reward_tree_tag_hash, "reward_tree_tag_preimage hash not for current proof leaf tag"); - - assert(c.claimed_guta_nullifiers[nullifier_index] == 0, "reward already claimed"); - - c.claimed_guta_nullifiers[nullifier_index] = 1; - } - - #[contract_method] - pub fn start_session( - claim_checkpoint_id: Felt - ) { - let c = PsyPOWMiningRewardsClaimContractRef::new(ContractMetadata::current()); - - let persistent_state = c.persistent_state.get(); - assert(persistent_state.last_claimed_checkpoint_id == persistent_state.last_sealed_checkpoint_id, "previous session must be sealed first"); - - assert(claim_checkpoint_id > persistent_state.last_sealed_checkpoint_id, "checkpoint is sealed"); - - c.persistent_state = PersistentState { - last_claimed_checkpoint_id: claim_checkpoint_id, - claimed_rewards: persistent_state.claimed_rewards, - last_sealed_checkpoint_id: persistent_state.last_sealed_checkpoint_id, - }; - } - - #[contract_method] - pub fn end_session(claim_checkpoint_id: Felt) { - let c = PsyPOWMiningRewardsClaimContractRef::new(ContractMetadata::current()); - let persistent_state = c.persistent_state.get(); - - assert( - claim_checkpoint_id == persistent_state.last_claimed_checkpoint_id - && persistent_state.last_claimed_checkpoint_id > persistent_state.last_sealed_checkpoint_id, - "no active session to end"); - - clear_entire_tree(); - - c.persistent_state = PersistentState { - last_claimed_checkpoint_id: persistent_state.last_claimed_checkpoint_id, - claimed_rewards: persistent_state.claimed_rewards, - last_sealed_checkpoint_id: claim_checkpoint_id, - }; - } - - #[contract_method] - pub fn claim_guta_rewards_1( - checkpoint_id: Felt, - proof: MiningRewardMerkleProof, - proposed_reward: Felt - ) { - let c = PsyPOWMiningRewardsClaimContractRef::new(ContractMetadata::current()); - let persistent_state = c.persistent_state.get(); - - if checkpoint_id != persistent_state.last_claimed_checkpoint_id { - if persistent_state.last_claimed_checkpoint_id != persistent_state.last_sealed_checkpoint_id { - Self::end_session(persistent_state.last_claimed_checkpoint_id); - }; - Self::start_session(checkpoint_id); - }; - - assert(checkpoint_id > persistent_state.last_sealed_checkpoint_id, "checkpoint is sealed"); - - Self::claim_single_guta_reward(checkpoint_id, proof, proposed_reward); - - let new_persistent_state = c.persistent_state.get(); - c.persistent_state = PersistentState { - last_claimed_checkpoint_id: checkpoint_id, - claimed_rewards: new_persistent_state.claimed_rewards + proposed_reward, - last_sealed_checkpoint_id: new_persistent_state.last_sealed_checkpoint_id, - }; - } - - #[contract_method] - pub fn claim_guta_rewards_2( - checkpoints: [Felt; 2], - proofs: [MiningRewardMerkleProof; 2], - proposed_rewards: [Felt; 2] - ) { - let c = PsyPOWMiningRewardsClaimContractRef::new(ContractMetadata::current()); - let initial_persistent_state = c.persistent_state.get(); - - let mut total_rewards_claimed = 0; - let mut last_checkpoint = initial_persistent_state.last_claimed_checkpoint_id; - - for i in 0u32..2u32 { - let idx = i as Felt; - let checkpoint_id = checkpoints[idx]; - let proof = proofs[idx]; - let proposed_reward = proposed_rewards[idx]; - - if checkpoint_id != last_checkpoint { - if last_checkpoint != initial_persistent_state.last_sealed_checkpoint_id { - Self::end_session(last_checkpoint); - }; - Self::start_session(checkpoint_id); - last_checkpoint = checkpoint_id; - }; - - assert(checkpoint_id > initial_persistent_state.last_sealed_checkpoint_id, "checkpoint is sealed"); - - Self::claim_single_guta_reward(checkpoint_id, proof, proposed_reward); - total_rewards_claimed = total_rewards_claimed + proposed_reward; - } - - let final_persistent_state = c.persistent_state.get(); - c.persistent_state = PersistentState { - last_claimed_checkpoint_id: last_checkpoint, - claimed_rewards: final_persistent_state.claimed_rewards + total_rewards_claimed, - last_sealed_checkpoint_id: final_persistent_state.last_sealed_checkpoint_id, - }; - } - - #[contract_method] - pub fn claim_guta_rewards_5( - checkpoints: [Felt; 5], - proofs: [MiningRewardMerkleProof; 5], - proposed_rewards: [Felt; 5] - ) { - let c = PsyPOWMiningRewardsClaimContractRef::new(ContractMetadata::current()); - let initial_persistent_state = c.persistent_state.get(); - - let mut total_rewards_claimed = 0; - let mut last_checkpoint = initial_persistent_state.last_claimed_checkpoint_id; - - for i in 0u32..5u32 { - let idx = i as Felt; - let checkpoint_id = checkpoints[idx]; - let proof = proofs[idx]; - let proposed_reward = proposed_rewards[idx]; - - if checkpoint_id != last_checkpoint { - if last_checkpoint != initial_persistent_state.last_sealed_checkpoint_id { - Self::end_session(last_checkpoint); - }; - Self::start_session(checkpoint_id); - last_checkpoint = checkpoint_id; - }; - - assert(checkpoint_id > initial_persistent_state.last_sealed_checkpoint_id, "checkpoint is sealed"); - - Self::claim_single_guta_reward(checkpoint_id, proof, proposed_reward); - total_rewards_claimed = total_rewards_claimed + proposed_reward; - } - - let final_persistent_state = c.persistent_state.get(); - c.persistent_state = PersistentState { - last_claimed_checkpoint_id: last_checkpoint, - claimed_rewards: final_persistent_state.claimed_rewards + total_rewards_claimed, - last_sealed_checkpoint_id: final_persistent_state.last_sealed_checkpoint_id, - }; - } - - #[contract_method] - pub fn claim_guta_rewards_10( - checkpoints: [Felt; 10], - proofs: [MiningRewardMerkleProof; 10], - proposed_rewards: [Felt; 10] - ) { - let c = PsyPOWMiningRewardsClaimContractRef::new(ContractMetadata::current()); - let initial_persistent_state = c.persistent_state.get(); - - let mut total_rewards_claimed = 0; - let mut last_checkpoint = initial_persistent_state.last_claimed_checkpoint_id; - - for i in 0u32..10u32 { - let idx = i as Felt; - let checkpoint_id = checkpoints[idx]; - let proof = proofs[idx]; - let proposed_reward = proposed_rewards[idx]; - - if checkpoint_id != last_checkpoint { - if last_checkpoint != initial_persistent_state.last_sealed_checkpoint_id { - Self::end_session(last_checkpoint); - }; - Self::start_session(checkpoint_id); - last_checkpoint = checkpoint_id; - }; - - assert(checkpoint_id > initial_persistent_state.last_sealed_checkpoint_id, "checkpoint is sealed"); - - Self::claim_single_guta_reward(checkpoint_id, proof, proposed_reward); - total_rewards_claimed = total_rewards_claimed + proposed_reward; - } - - let final_persistent_state = c.persistent_state.get(); - c.persistent_state = PersistentState { - last_claimed_checkpoint_id: last_checkpoint, - claimed_rewards: final_persistent_state.claimed_rewards + total_rewards_claimed, - last_sealed_checkpoint_id: final_persistent_state.last_sealed_checkpoint_id, - }; - } - -} diff --git a/psy-precompiles/precompiles.json b/psy-precompiles/precompiles.json index b10862ae..8958ab0c 100644 --- a/psy-precompiles/precompiles.json +++ b/psy-precompiles/precompiles.json @@ -13,22 +13,18 @@ "batch_transfer_2", "batch_transfer_5", "claim", - "claim_pow_rewards", "private_transfer", "private_claim" ] }, { - "name": "mining_rewards", - "path": "mining_rewards", - "contract_name": "PsyPOWMiningRewardsClaimContractRef", + "name": "multisig_policy", + "path": "multisig_policy", + "contract_name": "MultisigPolicyContractRef", + "declared_state_tree_height": 4, "method_names": [ - "start_session", - "end_session", - "claim_guta_rewards_1", - "claim_guta_rewards_2", - "claim_guta_rewards_5", - "claim_guta_rewards_10" + "get_policy", + "set_policy" ] }, { @@ -85,16 +81,6 @@ "method_names": [ "faucet" ] - }, - { - "name": "multisig_policy", - "path": "multisig_policy", - "contract_name": "MultisigPolicyContractRef", - "declared_state_tree_height": 4, - "method_names": [ - "get_policy", - "set_policy" - ] } ] } diff --git a/psy-precompiles/src/lib.rs b/psy-precompiles/src/lib.rs index 81d7c061..9f0a9a3d 100644 --- a/psy-precompiles/src/lib.rs +++ b/psy-precompiles/src/lib.rs @@ -15,16 +15,16 @@ mod tests { fn policy_state(header: [u64; 4], members: [[u64; 4]; 3]) -> InMemoryStateBackend { let mut state = InMemoryStateBackend::new(); - state.set_hash(USER_ID, 6, 0, header); + state.set_hash(USER_ID, MULTISIG_POLICY_CONTRACT_ID as u64, 0, header); for (index, member) in members.into_iter().enumerate() { - state.set_hash(USER_ID, 6, index as u64 + 1, member); + state.set_hash(USER_ID, MULTISIG_POLICY_CONTRACT_ID as u64, index as u64 + 1, member); } state } fn set_policy(state: InMemoryStateBackend, expected: [u64; 4], members: [[u64; 4]; 3], next: [[u64; 4]; 3]) -> (bool, InMemoryStateBackend) { let context = ExecutionContext { - user_id: USER_ID, contract_id: 6, caller_contract_id: 6, + user_id: USER_ID, contract_id: MULTISIG_POLICY_CONTRACT_ID as u64, caller_contract_id: MULTISIG_POLICY_CONTRACT_ID as u64, checkpoint_id: 1, nonce: 1, user_public_key_hash: [0; 4], session_proof_tree_root: [0; 4], }; @@ -44,15 +44,15 @@ mod tests { assert_eq!(MULTISIG_POLICY_CONTRACT_ID, psy_vm::ups::multisig::MULTISIG_POLICY_CONTRACT_ID); let (success, state) = set_policy(InMemoryStateBackend::new(), [0; 4], [[0; 4]; 3], MEMBERS); assert!(success); - assert_eq!(state.get_contract_hash(USER_ID, 6, 0).unwrap(), [1, 2, 3, 0]); + assert_eq!(state.get_contract_hash(USER_ID, MULTISIG_POLICY_CONTRACT_ID as u64, 0).unwrap(), [1, 2, 3, 0]); for (index, member) in MEMBERS.into_iter().enumerate() { - assert_eq!(state.get_contract_hash(USER_ID, 6, index as u64 + 1).unwrap(), member); + assert_eq!(state.get_contract_hash(USER_ID, MULTISIG_POLICY_CONTRACT_ID as u64, index as u64 + 1).unwrap(), member); } for slot in 4..16 { - assert_eq!(state.get_contract_hash(USER_ID, 6, slot).unwrap(), [0; 4]); + assert_eq!(state.get_contract_hash(USER_ID, MULTISIG_POLICY_CONTRACT_ID as u64, slot).unwrap(), [0; 4]); } let context = ExecutionContext { - user_id: USER_ID, contract_id: 6, caller_contract_id: 6, + user_id: USER_ID, contract_id: MULTISIG_POLICY_CONTRACT_ID as u64, caller_contract_id: MULTISIG_POLICY_CONTRACT_ID as u64, checkpoint_id: 1, nonce: 1, user_public_key_hash: [0; 4], session_proof_tree_root: [0; 4], }; @@ -65,12 +65,12 @@ mod tests { next[1][3] = 2; let (success, rotated) = set_policy(state.clone(), [1, 2, 3, 0], MEMBERS, next); assert!(success); - assert_eq!(rotated.get_contract_hash(USER_ID, 6, 0).unwrap(), [2, 2, 3, 0]); - assert_eq!(rotated.get_contract_hash(USER_ID, 6, 2).unwrap(), next[1]); + assert_eq!(rotated.get_contract_hash(USER_ID, MULTISIG_POLICY_CONTRACT_ID as u64, 0).unwrap(), [2, 2, 3, 0]); + assert_eq!(rotated.get_contract_hash(USER_ID, MULTISIG_POLICY_CONTRACT_ID as u64, 2).unwrap(), next[1]); let last_header = [u32::MAX as u64 - 1, 2, 3, 0]; let (success, last) = set_policy(policy_state(last_header, MEMBERS), last_header, MEMBERS, next); assert!(success); - assert_eq!(last.get_contract_hash(USER_ID, 6, 0).unwrap(), [u32::MAX as u64, 2, 3, 0]); + assert_eq!(last.get_contract_hash(USER_ID, MULTISIG_POLICY_CONTRACT_ID as u64, 0).unwrap(), [u32::MAX as u64, 2, 3, 0]); assert!(!set_policy(rotated, [1, 2, 3, 0], MEMBERS, MEMBERS).0); let mut stale_members = MEMBERS; stale_members[0][0] = 2; @@ -116,27 +116,26 @@ mod tests { fn test_contract_id_lookup() { // Test contract ID lookup by name assert_eq!(get_contract_id_by_name("token"), Some(0)); - assert_eq!(get_contract_id_by_name("mining_rewards"), Some(1)); + assert_eq!(get_contract_id_by_name("multisig_policy"), Some(1)); assert_eq!(get_contract_id_by_name("deposit_tree"), Some(2)); assert_eq!(get_contract_id_by_name("withdrawal_tree"), Some(3)); assert_eq!(get_contract_id_by_name("usdt_token"), Some(4)); assert_eq!(get_contract_id_by_name("faucet"), Some(5)); - assert_eq!(get_contract_id_by_name("multisig_policy"), Some(6)); assert_eq!(get_contract_id_by_name("nonexistent"), None); // Test function lookup by contract ID let token_functions = get_precompiled_contract_functions(0); - let rewards_functions = get_precompiled_contract_functions(1); + let policy_functions = get_precompiled_contract_functions(1); let usdt_functions = get_precompiled_contract_functions(4); let faucet_functions = get_precompiled_contract_functions(5); - println!("Rewards functions available: {}", rewards_functions.is_some()); + println!("Policy functions available: {}", policy_functions.is_some()); println!("USDT functions available: {}", usdt_functions.is_some()); println!("Token functions available: {}", token_functions.is_some()); println!("Faucet functions available: {}", faucet_functions.is_some()); - if let Some(functions) = rewards_functions { - println!("Rewards contract has {} functions", functions.len()); + if let Some(functions) = policy_functions { + println!("Policy contract has {} functions", functions.len()); for func in functions { println!(" - {} (method_id: {})", func.name, func.method_id); } @@ -154,13 +153,8 @@ mod tests { fn test_function_lookup_by_name() { // Test looking up specific functions by name let mint = get_precompiled_contract_function_by_name("token", "mint"); - let claim_pow_rewards = get_precompiled_contract_function_by_name("token", "claim_pow_rewards"); - let claim_reward = get_precompiled_contract_function_by_name("mining_rewards", "claim_guta_rewards_1"); - let claim_rewards_2 = get_precompiled_contract_function_by_name("mining_rewards", "claim_guta_rewards_2"); - let claim_rewards_5 = get_precompiled_contract_function_by_name("mining_rewards", "claim_guta_rewards_5"); - let claim_rewards_10 = get_precompiled_contract_function_by_name("mining_rewards", "claim_guta_rewards_10"); - let start_session = get_precompiled_contract_function_by_name("mining_rewards", "start_session"); - let end_session = get_precompiled_contract_function_by_name("mining_rewards", "end_session"); + let get_policy = get_precompiled_contract_function_by_name("multisig_policy", "get_policy"); + let set_policy = get_precompiled_contract_function_by_name("multisig_policy", "set_policy"); let faucet = get_precompiled_contract_function_by_name("faucet", "faucet"); if let Some(mint_func) = mint { @@ -168,47 +162,11 @@ mod tests { assert_eq!(mint_func.name, "mint"); } - if let Some(claim_func) = claim_pow_rewards { - println!("Found claim_pow_rewards function with method_id: {}", claim_func.method_id); - assert_eq!(claim_func.name, "claim_pow_rewards"); - } - - if let Some(claim_reward_func) = claim_reward { - println!("Found claim_guta_rewards_1 function with method_id: {}", claim_reward_func.method_id); - assert_eq!(claim_reward_func.name, "claim_guta_rewards_1"); - } - assert!(claim_rewards_2.is_some()); - assert!(claim_rewards_5.is_some()); - assert!(claim_rewards_10.is_some()); - assert!(start_session.is_some()); - assert!(end_session.is_some()); - - for method_name in [ - "claim_guta_rewards_1", - "claim_guta_rewards_2", - "claim_guta_rewards_5", - "claim_guta_rewards_10", - ] { - let function = get_precompiled_contract_function_by_name("mining_rewards", method_name) - .unwrap_or_else(|| panic!("missing mining rewards method {method_name}")); - let encoded = serde_json::to_string(function).expect("serialize compiled mining rewards method"); - assert!( - encoded.contains("GetSelfUserCurrentContractStateSlotSingle"), - "{method_name} must read the claimed_guta_nullifiers slot" - ); - assert!( - encoded.contains("SetContractStateSlotSingle"), - "{method_name} must mark the claimed_guta_nullifier slot as used" - ); - assert!( - encoded.contains("reward already claimed"), - "{method_name} must reject duplicate claims" - ); - assert!( - !encoded.contains("ContainsSelfUserCurrentIMTContractStateValue"), - "{method_name} must not use the obsolete claimed_jobs Map" - ); + if let Some(get_policy_func) = get_policy { + println!("Found get_policy function with method_id: {}", get_policy_func.method_id); + assert_eq!(get_policy_func.name, "get_policy"); } + assert!(set_policy.is_some()); if let Some(faucet_func) = faucet { println!("Found faucet function with method_id: {}", faucet_func.method_id); @@ -224,30 +182,25 @@ mod tests { fn test_list_operations() { let contracts = list_available_contracts(); println!("Available contracts: {:?}", contracts); - assert!(contracts.contains(&"mining_rewards".to_string())); + assert!(contracts.contains(&"multisig_policy".to_string())); assert!(contracts.contains(&"usdt_token".to_string())); assert!(contracts.contains(&"token".to_string())); assert!(contracts.contains(&"faucet".to_string())); - let rewards_methods = list_contract_methods("mining_rewards"); + let policy_methods = list_contract_methods("multisig_policy"); let usdt_methods = list_contract_methods("usdt_token"); let token_methods = list_contract_methods("token"); let faucet_methods = list_contract_methods("faucet"); - println!("Rewards methods: {:?}", rewards_methods); + println!("Policy methods: {:?}", policy_methods); println!("Token methods: {:?}", token_methods); println!("USDT methods: {:?}", usdt_methods); println!("Faucet methods: {:?}", faucet_methods); - // These should contain the methods defined in config.json - if !rewards_methods.is_empty() { - assert!(rewards_methods.contains(&"start_session".to_string())); - assert!(rewards_methods.contains(&"end_session".to_string())); - assert!(rewards_methods.contains(&"claim_guta_rewards_1".to_string())); - assert!(rewards_methods.contains(&"claim_guta_rewards_2".to_string())); - assert!(rewards_methods.contains(&"claim_guta_rewards_5".to_string())); - assert!(rewards_methods.contains(&"claim_guta_rewards_10".to_string())); - assert_eq!(rewards_methods.len(), 6); + if !policy_methods.is_empty() { + assert!(policy_methods.contains(&"get_policy".to_string())); + assert!(policy_methods.contains(&"set_policy".to_string())); + assert_eq!(policy_methods.len(), 2); } if !usdt_methods.is_empty() { @@ -256,7 +209,7 @@ mod tests { if !token_methods.is_empty() { assert!(token_methods.contains(&"mint".to_string())); - assert!(token_methods.contains(&"claim_pow_rewards".to_string())); + assert!(!token_methods.contains(&"claim_pow_rewards".to_string())); } if !faucet_methods.is_empty() { @@ -272,7 +225,7 @@ mod tests { // These operations should be very fast since they use pre-computed lookups for _ in 0..1000 { - let _ = get_contract_id_by_name("mining_rewards"); + let _ = get_contract_id_by_name("multisig_policy"); let _ = get_precompiled_contract_functions(0); let _ = get_precompiled_contract_function_by_name("token", "mint"); } diff --git a/psy-precompiles/token/Dargo.toml b/psy-precompiles/token/Dargo.toml index c7494aab..9c1d7d64 100644 --- a/psy-precompiles/token/Dargo.toml +++ b/psy-precompiles/token/Dargo.toml @@ -4,5 +4,4 @@ type = "bin" authors = [""] [dependencies] -mining_rewards = { path = "../mining_rewards" } deposit_tree = { path = "../deposit_tree" } diff --git a/psy-precompiles/token/src/main.psy b/psy-precompiles/token/src/main.psy index 6b9e6b79..de85fc16 100644 --- a/psy-precompiles/token/src/main.psy +++ b/psy-precompiles/token/src/main.psy @@ -1,5 +1,4 @@ use std::prelude::*; -use mining_rewards::PsyPOWMiningRewardsClaimContractRef; use deposit_tree::PsyDepositTreeContractRef; #[derive(Storage)] @@ -101,8 +100,9 @@ struct ShieldClaimPublicInputsPreimage { #[derive(Storage)] pub struct PsyTokenContract { pub balance: Felt, - pub last_claimed_pow_rewards_checkpoint_id: Felt, - pub claimed_rewards: Felt, + // Reserved to preserve the deployed storage layout. + pub reserved_1: Felt, + pub reserved_2: Felt, pub other_user_info: [OtherUserInfo; 4294967296], pub note_count: Felt, @@ -488,32 +488,6 @@ impl PsyTokenContractRef { claim_event.emit(); } - #[contract_method] - pub fn claim_pow_rewards(checkpoint_id: Felt) { - let c = PsyTokenContractRef::new(ContractMetadata::current()); - let psy_pow_mining_rewards_claim_contract = PsyPOWMiningRewardsClaimContractRef::new(ContractMetadata::new(1, get_user_id())); - let pow_rewards_persistent_state = psy_pow_mining_rewards_claim_contract.persistent_state.get(); - - assert( - pow_rewards_persistent_state.last_claimed_checkpoint_id == checkpoint_id, - "the claims must be from the latest ups"); - assert( - pow_rewards_persistent_state.last_sealed_checkpoint_id == checkpoint_id, - "the checkpoint must be sealed"); - assert( - c.last_claimed_pow_rewards_checkpoint_id.get() < checkpoint_id, - "must haven't already claimed during this ups"); - - let cumulative_rewards = pow_rewards_persistent_state.claimed_rewards; - let previously_settled_rewards = c.claimed_rewards.get(); - assert(cumulative_rewards >= previously_settled_rewards, "pow rewards total cannot decrease"); - let newly_settled_rewards = cumulative_rewards - previously_settled_rewards; - - c.last_claimed_pow_rewards_checkpoint_id = checkpoint_id; - c.claimed_rewards = cumulative_rewards; - c.balance += newly_settled_rewards; - } - #[contract_method] pub fn batch_transfer_2(recipients: [Felt; 2], amounts: [Felt; 2]) { let c = PsyTokenContractRef::new(ContractMetadata::current()); diff --git a/tests/Dargo.toml b/tests/Dargo.toml index dedee6d0..65c3ee9d 100644 --- a/tests/Dargo.toml +++ b/tests/Dargo.toml @@ -5,7 +5,6 @@ type = "bin" description = "test" [dependencies] -mining_rewards = { path = "../psy-precompiles/mining_rewards" } token = { path = "../psy-precompiles/token" } withdrawal_tree = { path = "../psy-precompiles/withdrawal_tree" } deposit_tree = { path = "../psy-precompiles/deposit_tree" } diff --git a/tests/mining_rewards.psy b/tests/mining_rewards.psy deleted file mode 100644 index 2d117b66..00000000 --- a/tests/mining_rewards.psy +++ /dev/null @@ -1,246 +0,0 @@ -struct WeirdMerkleProofSiblingLevel { - pub sibling_branch: [Felt; 4], - pub sibling_reward_leaf: [Felt; 4], -} - -struct VariableHeightRewardMerkleProof { - pub top_siblings: [WeirdMerkleProofSiblingLevel; 31], - pub left_branch: [Felt; 4], - pub right_branch: [Felt; 4], - pub reward_leaf: [Felt; 4], - pub proof_height: Felt, - pub index: Felt, -} - -#[derive(Storage)] -struct PersistantState { - pub min_allowed_claimed_checkpoint: Felt, - pub last_claimed_ups_checkpoint_id: Felt, - pub claimed_rewards: Felt, - pub sealed_for_ups_checkpoint_id: Felt, -} - -#[contract] -#[derive(Storage)] -struct Contract { - pub persistant_state: PersistantState, - pub claimed_guta_nullifiers: [Felt; 16777216], - pub claimed_contract_deployment_nullifiers: [Felt; 1048576], - pub claimed_user_registration_nullifiers: [Felt; 1048576] -} - -impl VariableHeightRewardMerkleProof { - pub fn compute_root_and_nullifier_index(self) -> ([Felt; 4], Felt) { - let proof_height = self.proof_height; - let index = self.index; - let max_height_minus_one = 31; - - assert(proof_height <= max_height_minus_one, "proof height too large"); - assert(index < 2147483648, "index too large"); - - let mut nullifier_base = 0; - let mut nullifier_level_start_index_multiplier = 1; - - let leaf_hash = hash_two_to_one(self.left_branch, self.right_branch); - let mut current_node_value = hash_two_to_one(leaf_hash, self.reward_leaf); - - for i in 0u32..31u32 { - let level_index = i as Felt; - let sibling = self.top_siblings[level_index]; - let sibling_node = sibling.sibling_branch; - let reward_leaf = sibling.sibling_reward_leaf; - - if level_index < proof_height { - let bit_position = level_index; - let index_bit = (index >> bit_position) & 1; - - let branch_path_hash = if index_bit == 0 { - hash_two_to_one(current_node_value, sibling_node) - } else { - hash_two_to_one(sibling_node, current_node_value) - }; - current_node_value = hash_two_to_one(branch_path_hash, reward_leaf); - - nullifier_base = nullifier_base + nullifier_level_start_index_multiplier; - nullifier_level_start_index_multiplier = nullifier_level_start_index_multiplier * 2; - } else { - let zero_hash = [0, 0, 0, 0]; - assert( - reward_leaf[0] == zero_hash[0] - && reward_leaf[1] == zero_hash[1] - && reward_leaf[2] == zero_hash[2] - && reward_leaf[3] == zero_hash[3], - "unused reward leaf must be zero"); - assert(sibling_node[0] == zero_hash[0] - && sibling_node[1] == zero_hash[1] - && sibling_node[2] == zero_hash[2] - && sibling_node[3] == zero_hash[3], - "unused sibling node must be zero"); - - let bit_position = level_index; - let index_bit = (index >> bit_position) & 1; - assert(index_bit == 0, "unused index bit must be zero"); - }; - } - - let nullifier_final_index = nullifier_base + index; - (current_node_value, nullifier_final_index) - } -} - -impl ContractRef { - #[contract_method] - pub fn check_guta_reward(total_proofs_in_checkpoint: Felt, total_fees_paid: Felt, proposed_reward: Felt) -> bool { - let user_transaction_inclusion_fee = 5000000000; - - let total_reward_if_all_claim = proposed_reward * total_proofs_in_checkpoint; - proposed_reward < user_transaction_inclusion_fee && total_reward_if_all_claim < total_fees_paid - } - - #[contract_method] - pub fn advance_to_checkpoint(new_claim_checkpoint_id: Felt) { - let c = ContractRef::new(ContractMetadata::current()); - let persistant_state = c.persistant_state; - assert(new_claim_checkpoint_id > persistant_state.min_allowed_claimed_checkpoint, "checkpoint must be greater"); - - clear_entire_tree(); - - c.persistant_state = PersistantState { - min_allowed_claimed_checkpoint: new_claim_checkpoint_id, - last_claimed_ups_checkpoint_id: get_checkpoint_id(), - claimed_rewards: 0, - sealed_for_ups_checkpoint_id: persistant_state.sealed_for_ups_checkpoint_id, - }; - } - - #[contract_method] - pub fn claim_guta_proof( - claim_checkpoint_id: Felt, - proof: VariableHeightRewardMerkleProof, - proposed_reward: Felt - ) { - let c = ContractRef::new(ContractMetadata::current()); - let current_checkpoint = get_checkpoint_id(); - let claim_rewards_cooldown = 2; - - assert(current_checkpoint > claim_checkpoint_id + claim_rewards_cooldown, "cooldown not satisfied"); - - let persistant_state = c.persistant_state; - - assert(current_checkpoint > persistant_state.sealed_for_ups_checkpoint_id, "checkpoint is sealed"); - - assert(persistant_state.min_allowed_claimed_checkpoint == claim_checkpoint_id, "invalid claim checkpoint"); - - let guta_rewards_tree_root = get_gutas_root(claim_checkpoint_id); - - let result: ([Felt; 4], Felt) = proof.compute_root_and_nullifier_index(); - - let guta_reward_tree_root = result.0; - let nullifier_index = result.1; - - assert( - guta_reward_tree_root[0] == guta_rewards_tree_root[0] - && guta_reward_tree_root[1] == guta_rewards_tree_root[1] - && guta_reward_tree_root[2] == guta_rewards_tree_root[2] - && guta_reward_tree_root[3] == guta_rewards_tree_root[3], - "guta tree root mismatch"); - - let user_public_key_hash = get_user_public_key_hash(); - assert( - proof.reward_leaf[0] == user_public_key_hash[0] - && proof.reward_leaf[1] == user_public_key_hash[1] - && proof.reward_leaf[2] == user_public_key_hash[2] - && proof.reward_leaf[3] == user_public_key_hash[3], - "reward not for current user"); - - assert(c.claimed_guta_nullifiers[nullifier_index] == 0, "reward already claimed"); - - let old_claimed_rewards = if persistant_state.last_claimed_ups_checkpoint_id == current_checkpoint { - persistant_state.claimed_rewards - } else { - 0 - }; - - let total_proofs = get_gutas_completed(claim_checkpoint_id); - let total_fees = get_guta_fees_collected(claim_checkpoint_id); - assert(Self::check_guta_reward(total_proofs, total_fees, proposed_reward), "invalid reward amount"); - - c.claimed_guta_nullifiers[nullifier_index] = 1; - - c.persistant_state = PersistantState { - min_allowed_claimed_checkpoint: persistant_state.min_allowed_claimed_checkpoint, - last_claimed_ups_checkpoint_id: current_checkpoint, - claimed_rewards: old_claimed_rewards + proposed_reward, - sealed_for_ups_checkpoint_id: persistant_state.sealed_for_ups_checkpoint_id, - }; - } - - #[contract_method] - pub fn advance_and_claim_guta_proof( - claim_checkpoint_id: Felt, - proof: VariableHeightRewardMerkleProof - ) { - Self::advance_to_checkpoint(claim_checkpoint_id); - - let total_proofs = get_gutas_completed(claim_checkpoint_id); - let total_fees = get_guta_fees_collected(claim_checkpoint_id); - let proposed_reward = if total_proofs > 0 { total_fees / total_proofs } else { 0 }; - - Self::claim_guta_proof(claim_checkpoint_id, proof, proposed_reward); - } - - #[contract_method] - pub fn advance_to_checkpoint_and_seal(new_claim_checkpoint_id: Felt) { - let c = ContractRef::new(ContractMetadata::current()); - let persistant_state = c.persistant_state; - assert(new_claim_checkpoint_id > persistant_state.min_allowed_claimed_checkpoint, "checkpoint must be greater"); - - clear_entire_tree(); - - c.persistant_state = PersistantState { - min_allowed_claimed_checkpoint: new_claim_checkpoint_id, - last_claimed_ups_checkpoint_id: get_checkpoint_id(), - claimed_rewards: 0, - sealed_for_ups_checkpoint_id: get_checkpoint_id(), - }; - } - - #[contract_method] - pub fn claim_reward(claim_checkpoint_id: Felt, proposed_reward: Felt) { - let c = ContractRef::new(ContractMetadata::current()); - let current_checkpoint = get_checkpoint_id(); - let claim_rewards_cooldown = 2; - - assert(current_checkpoint > claim_checkpoint_id + claim_rewards_cooldown, "cooldown not satisfied"); - - let persistant_state = c.persistant_state; - - assert(persistant_state.min_allowed_claimed_checkpoint == claim_checkpoint_id, "invalid claim checkpoint"); - - let old_claimed_rewards = if persistant_state.last_claimed_ups_checkpoint_id == current_checkpoint { - persistant_state.claimed_rewards - } else { - 0 - }; - - assert(proposed_reward > 0 && proposed_reward <= 1000000000, "invalid reward amount"); - - c.persistant_state = PersistantState { - min_allowed_claimed_checkpoint: persistant_state.min_allowed_claimed_checkpoint, - last_claimed_ups_checkpoint_id: current_checkpoint, - claimed_rewards: old_claimed_rewards + proposed_reward, - sealed_for_ups_checkpoint_id: persistant_state.sealed_for_ups_checkpoint_id, - }; - } -} - -fn main() { - let c = ContractRef::new(ContractMetadata::current()); - let initial_state = PersistantState { - min_allowed_claimed_checkpoint: 0, - last_claimed_ups_checkpoint_id: 0, - claimed_rewards: 0, - sealed_for_ups_checkpoint_id: 0, - }; - c.persistant_state = initial_state; -} From db0b5e2a2e0a132b8f2916aba6ab300d3de0da03 Mon Sep 17 00:00:00 2001 From: 0x8f701 Date: Thu, 15 Oct 2026 13:20:33 +0800 Subject: [PATCH 4/4] fix: pin published node dependencies and configure CI Git transport --- .github/workflows/test.yml | 11 +++++++++++ Cargo.lock | 29 +++++++++++++++++++++++++++++ Cargo.toml | 24 ++++++++++++------------ 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a03595b8..b37a2b35 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,6 +16,7 @@ concurrency: env: CARGO_TERM_COLOR: always CARGO_NET_GIT_FETCH_WITH_CLI: "true" + PSY_NETWORK: localhost DARGO_STD_PATH: ${{ github.workspace }}/psy-std/std.psy jobs: @@ -28,6 +29,11 @@ jobs: with: persist-credentials: false + - name: Use HTTPS for GitHub dependencies + run: | + git config --global url."https://github.com/".insteadOf "ssh://git@github.com/" + git config --global --add url."https://github.com/".insteadOf "git@github.com:" + - name: Install Rust toolchain run: rustup show active-toolchain @@ -43,6 +49,11 @@ jobs: with: persist-credentials: false + - name: Use HTTPS for GitHub dependencies + run: | + git config --global url."https://github.com/".insteadOf "ssh://git@github.com/" + git config --global --add url."https://github.com/".insteadOf "git@github.com:" + - name: Install Rust toolchain and coverage component run: | rustup show active-toolchain diff --git a/Cargo.lock b/Cargo.lock index 45d39a68..0ea8a267 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1266,6 +1266,7 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cf_utils" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -4194,6 +4195,7 @@ checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" [[package]] name = "kvq" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "ambassador", "anyhow", @@ -4847,6 +4849,7 @@ dependencies = [ [[package]] name = "parth_common" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -4871,6 +4874,7 @@ dependencies = [ [[package]] name = "parth_core" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -4905,6 +4909,7 @@ dependencies = [ [[package]] name = "parth_crypto" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "parth_core", @@ -4963,6 +4968,7 @@ dependencies = [ [[package]] name = "pderive" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "proc-macro2", "quote", @@ -5402,6 +5408,7 @@ dependencies = [ [[package]] name = "pser" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "bincode", @@ -5605,6 +5612,7 @@ dependencies = [ [[package]] name = "psy_api_core" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -5639,6 +5647,7 @@ dependencies = [ [[package]] name = "psy_client_common" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -5669,6 +5678,7 @@ dependencies = [ [[package]] name = "psy_client_data" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -5696,6 +5706,7 @@ dependencies = [ [[package]] name = "psy_common_circuit" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -5728,6 +5739,7 @@ dependencies = [ [[package]] name = "psy_compiler" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "bincode", @@ -5757,6 +5769,7 @@ dependencies = [ [[package]] name = "psy_config" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "indexmap 2.14.0", @@ -5771,6 +5784,7 @@ dependencies = [ [[package]] name = "psy_core" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -5800,6 +5814,7 @@ dependencies = [ [[package]] name = "psy_crypto" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -5830,6 +5845,7 @@ dependencies = [ [[package]] name = "psy_data" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -5871,6 +5887,7 @@ dependencies = [ [[package]] name = "psy_dpn_circuit" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "alloy-primitives", "alloy-signer", @@ -5925,6 +5942,7 @@ dependencies = [ [[package]] name = "psy_dummy_prover" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -5963,6 +5981,7 @@ dependencies = [ [[package]] name = "psy_io" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -5976,6 +5995,7 @@ dependencies = [ [[package]] name = "psy_network_circuit" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -5993,6 +6013,7 @@ dependencies = [ [[package]] name = "psy_plonky2_basic_helpers" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -6023,6 +6044,7 @@ dependencies = [ [[package]] name = "psy_plonky2_circuits" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -6070,6 +6092,7 @@ dependencies = [ [[package]] name = "psy_plonky2_common_circuits" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -6100,6 +6123,7 @@ dependencies = [ [[package]] name = "psy_prover" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "alloy-primitives", "alloy-signer", @@ -6170,6 +6194,7 @@ dependencies = [ [[package]] name = "psy_provider" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "alloy-primitives", "alloy-signer", @@ -6225,6 +6250,7 @@ dependencies = [ [[package]] name = "psy_serialize" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "bytemuck", @@ -6235,6 +6261,7 @@ dependencies = [ [[package]] name = "psy_ups_circuit" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "alloy-primitives", "alloy-signer", @@ -6288,6 +6315,7 @@ dependencies = [ [[package]] name = "psy_vm" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", @@ -6314,6 +6342,7 @@ dependencies = [ [[package]] name = "psy_worker_core" version = "0.1.0" +source = "git+https://github.com/PsyProtocol/psy-node.git?rev=7d6815d22728debbc71f6a928f85aa4b209ebf4f#7d6815d22728debbc71f6a928f85aa4b209ebf4f" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index c1576907..8b38e468 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,18 +27,18 @@ repository = "https://github.com/PsyProtocol/psy-compiler" [workspace.dependencies] zstd = { version = "0.13", features = ["zstdmt"] } -# Local psy-node deps. Preserve package aliases and feature selections. -psy_vm = { path = "../psy-node/client_prover/psy_vm", default-features = false } -psy_crypto = { path = "../psy-node/client_prover/psy_core/psy_crypto" } -psy_data = { package = "psy_client_data", path = "../psy-node/client_prover/psy_core/psy_data", default-features = false } -psy_common_core = { package = "psy_client_common", path = "../psy-node/client_prover/psy_core/psy_common" } -psy_config = { path = "../psy-node/client_prover/psy_core/psy_config" } -psy_prover = { path = "../psy-node/client_prover/psy_prover", default-features = false } -psy_common_circuit = { path = "../psy-node/client_prover/psy_circuit/psy_common_circuit", default-features = false } -psy_dpn_circuit = { path = "../psy-node/client_prover/psy_circuit/psy_dpn_circuit", default-features = false } -psy_ups_circuit = { path = "../psy-node/client_prover/psy_circuit/psy_ups_circuit", default-features = false } -psy_network_circuit = { path = "../psy-node/client_prover/psy_circuit/psy_network_circuit", default-features = false } -kvq = { path = "../psy-node/client_prover/psy_core/kvq" } +# Published psy-node source revision. Preserve package aliases and feature selections. +psy_vm = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7d6815d22728debbc71f6a928f85aa4b209ebf4f", default-features = false } +psy_crypto = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7d6815d22728debbc71f6a928f85aa4b209ebf4f" } +psy_data = { package = "psy_client_data", git = "https://github.com/PsyProtocol/psy-node.git", rev = "7d6815d22728debbc71f6a928f85aa4b209ebf4f", default-features = false } +psy_common_core = { package = "psy_client_common", git = "https://github.com/PsyProtocol/psy-node.git", rev = "7d6815d22728debbc71f6a928f85aa4b209ebf4f" } +psy_config = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7d6815d22728debbc71f6a928f85aa4b209ebf4f" } +psy_prover = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7d6815d22728debbc71f6a928f85aa4b209ebf4f", default-features = false } +psy_common_circuit = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7d6815d22728debbc71f6a928f85aa4b209ebf4f", default-features = false } +psy_dpn_circuit = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7d6815d22728debbc71f6a928f85aa4b209ebf4f", default-features = false } +psy_ups_circuit = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7d6815d22728debbc71f6a928f85aa4b209ebf4f", default-features = false } +psy_network_circuit = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7d6815d22728debbc71f6a928f85aa4b209ebf4f", default-features = false } +kvq = { git = "https://github.com/PsyProtocol/psy-node.git", rev = "7d6815d22728debbc71f6a928f85aa4b209ebf4f" } # Local compiler packages psy_common = { package = "psy_compiler_common", path = "./psy-common" }