From 748009d0a0c520027e88bf254e6602b01fac20b5 Mon Sep 17 00:00:00 2001 From: thomaslavaur Date: Tue, 2 Sep 2025 10:13:07 +0200 Subject: [PATCH 1/7] fixed the generation of test that was miscalculating t0 and t1 --- circom_circuits/Blend/generate_inputs_for_poq.py | 7 +++++-- circom_circuits/Mantle/generate_inputs_for_pol.py | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/circom_circuits/Blend/generate_inputs_for_poq.py b/circom_circuits/Blend/generate_inputs_for_poq.py index c47aa53..7be2740 100644 --- a/circom_circuits/Blend/generate_inputs_for_poq.py +++ b/circom_circuits/Blend/generate_inputs_for_poq.py @@ -239,8 +239,11 @@ epoch_nonce = F(randrange(0, p,1)) slot_number = F(randrange(0, 2**32,1)) total_stake = F(5000) -t0 = F(0x27b6fe27507ca57ca369280400c79b5d2f58ff94d87cb0fbfc8294eb69eb1ea) -t1 = F(0x104bfd09ebdd0a57772289d0973489b62662a4dc6f09da8b4af3c5cfb1dcdd) +t0_constant = F(0x27b6fe27507ca57ca369280400c79b5d2f58ff94d87cb0fbfc8294eb69eb1ea) +t1_constant = F(0x104bfd09ebdd0a57772289d0973489b62662a4dc6f09da8b4af3c5cfb1dcdd) + +t0 = F(int(t0_constant) / total_stake) +t1 = F(p- (int(t1_constant) / total_stake**2)) value = F(total_stake / 100) threshold = (t0 + t1 * value) * value diff --git a/circom_circuits/Mantle/generate_inputs_for_pol.py b/circom_circuits/Mantle/generate_inputs_for_pol.py index 4f24c4e..7a38f65 100755 --- a/circom_circuits/Mantle/generate_inputs_for_pol.py +++ b/circom_circuits/Mantle/generate_inputs_for_pol.py @@ -219,8 +219,11 @@ if total_stake >= p: print("total stake must be less than p") exit() -t0 = F(0x27b6fe27507ca57ca369280400c79b5d2f58ff94d87cb0fbfc8294eb69eb1ea) -t1 = F(0x104bfd09ebdd0a57772289d0973489b62662a4dc6f09da8b4af3c5cfb1dcdd) +t0_constant = F(0x27b6fe27507ca57ca369280400c79b5d2f58ff94d87cb0fbfc8294eb69eb1ea) +t1_constant = F(0x104bfd09ebdd0a57772289d0973489b62662a4dc6f09da8b4af3c5cfb1dcdd) + +t0 = F(int(t0_constant) / total_stake) +t1 = F(p- (int(t1_constant) / total_stake**2)) value = F(total_stake / 100) From 3f12e9c2791df637b06851d1ee1de80f3d0a8643 Mon Sep 17 00:00:00 2001 From: thomaslavaur Date: Tue, 2 Sep 2025 10:33:02 +0200 Subject: [PATCH 2/7] use euclidean division to match the rust implementation (the difference is negligible as it would make you lose 1 block every 10^16 blocks that you should have win --- circom_circuits/Blend/generate_inputs_for_poq.py | 4 ++-- circom_circuits/Mantle/generate_inputs_for_pol.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/circom_circuits/Blend/generate_inputs_for_poq.py b/circom_circuits/Blend/generate_inputs_for_poq.py index 7be2740..5c2e881 100644 --- a/circom_circuits/Blend/generate_inputs_for_poq.py +++ b/circom_circuits/Blend/generate_inputs_for_poq.py @@ -242,8 +242,8 @@ total_stake = F(5000) t0_constant = F(0x27b6fe27507ca57ca369280400c79b5d2f58ff94d87cb0fbfc8294eb69eb1ea) t1_constant = F(0x104bfd09ebdd0a57772289d0973489b62662a4dc6f09da8b4af3c5cfb1dcdd) -t0 = F(int(t0_constant) / total_stake) -t1 = F(p- (int(t1_constant) / total_stake**2)) +t0 = F(int(t0_constant) // total_stake) +t1 = F(p- (int(t1_constant) // total_stake**2)) value = F(total_stake / 100) threshold = (t0 + t1 * value) * value diff --git a/circom_circuits/Mantle/generate_inputs_for_pol.py b/circom_circuits/Mantle/generate_inputs_for_pol.py index 7a38f65..3899005 100755 --- a/circom_circuits/Mantle/generate_inputs_for_pol.py +++ b/circom_circuits/Mantle/generate_inputs_for_pol.py @@ -222,8 +222,8 @@ if total_stake >= p: t0_constant = F(0x27b6fe27507ca57ca369280400c79b5d2f58ff94d87cb0fbfc8294eb69eb1ea) t1_constant = F(0x104bfd09ebdd0a57772289d0973489b62662a4dc6f09da8b4af3c5cfb1dcdd) -t0 = F(int(t0_constant) / total_stake) -t1 = F(p- (int(t1_constant) / total_stake**2)) +t0 = F(int(t0_constant) // total_stake) +t1 = F(p- (int(t1_constant) // total_stake**2)) value = F(total_stake / 100) From ef6d419aa144eaceb5df38c67c0a7f0f9b8278c2 Mon Sep 17 00:00:00 2001 From: thomaslavaur Date: Wed, 3 Sep 2025 15:48:58 +0200 Subject: [PATCH 3/7] add string to modified last value for core/leader case --- circom_circuits/Blend/generate_inputs_for_poq.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/circom_circuits/Blend/generate_inputs_for_poq.py b/circom_circuits/Blend/generate_inputs_for_poq.py index 5c2e881..d7ba754 100644 --- a/circom_circuits/Blend/generate_inputs_for_poq.py +++ b/circom_circuits/Blend/generate_inputs_for_poq.py @@ -319,9 +319,9 @@ inp = { } if core_or_leader == 0: - inp["pol_ledger_aged"] = randrange(0,p,1) + inp["pol_ledger_aged"] = str(randrange(0,p,1)) else: - inp["core_root"] = randrange(0,p,1) + inp["core_root"] = str(randrange(0,p,1)) import json From 0a989fe7117e92b39a5c78d203f284b5f04c1ccc Mon Sep 17 00:00:00 2001 From: thomaslavaur Date: Wed, 3 Sep 2025 16:03:37 +0200 Subject: [PATCH 4/7] put credible values for key --- circom_circuits/Blend/generate_inputs_for_poq.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/circom_circuits/Blend/generate_inputs_for_poq.py b/circom_circuits/Blend/generate_inputs_for_poq.py index d7ba754..f3772f2 100644 --- a/circom_circuits/Blend/generate_inputs_for_poq.py +++ b/circom_circuits/Blend/generate_inputs_for_poq.py @@ -287,8 +287,8 @@ for i in range(32): index = randrange(0, Ql if core_or_leader else Qc,1) # 4) One‐time key -K_one = F(randrange(0,p,1)) -K_two = F(randrange(0,p,1)) +K_one = F(123456) +K_two = F(654321) # 5) Assemble JSON inp = { From 7d9ba0caa6b81efe603779a3174036652bd96543 Mon Sep 17 00:00:00 2001 From: thomaslavaur Date: Fri, 5 Sep 2025 12:30:02 +0200 Subject: [PATCH 5/7] fix something that was computing t0 and t1 wrong --- circom_circuits/Blend/generate_inputs_for_poq.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circom_circuits/Blend/generate_inputs_for_poq.py b/circom_circuits/Blend/generate_inputs_for_poq.py index f3772f2..77e353a 100644 --- a/circom_circuits/Blend/generate_inputs_for_poq.py +++ b/circom_circuits/Blend/generate_inputs_for_poq.py @@ -237,7 +237,7 @@ for i in range(20): # 2) PoL inputs (broadened from pol script) epoch_nonce = F(randrange(0, p,1)) slot_number = F(randrange(0, 2**32,1)) -total_stake = F(5000) +total_stake = int(5000) t0_constant = F(0x27b6fe27507ca57ca369280400c79b5d2f58ff94d87cb0fbfc8294eb69eb1ea) t1_constant = F(0x104bfd09ebdd0a57772289d0973489b62662a4dc6f09da8b4af3c5cfb1dcdd) From f8a2cba8be6cc807a8488a918e75108c8db82c47 Mon Sep 17 00:00:00 2001 From: thomaslavaur Date: Mon, 8 Sep 2025 11:38:26 +0200 Subject: [PATCH 6/7] minor fix in names and python --- circom_circuits/Blend/poq.circom | 2 +- .../Mantle/generate_inputs_for_pol.py | 2 + circom_circuits/Mantle/poc.circom | 74 +++++++++++++++++++ circom_circuits/Mantle/pol.circom | 2 +- 4 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 circom_circuits/Mantle/poc.circom diff --git a/circom_circuits/Blend/poq.circom b/circom_circuits/Blend/poq.circom index 2cb4a17..a8385f1 100644 --- a/circom_circuits/Blend/poq.circom +++ b/circom_circuits/Blend/poq.circom @@ -133,5 +133,5 @@ template ProofOfQuota(nLevelsPK, nLevelsPol, bitsQuota) { } // Instantiate with chosen depths: 20 for core PK tree, 25 for PoL secret slot tree -component main { public [ session, core_quota, leader_quota, core_root, pol_ledger_aged, K_part_one, K_part_two ] } +component main { public [ session, core_quota, leader_quota, core_root, K_part_one, K_part_two, pol_epoch_nonce, pol_t0, pol_t1, pol_ledger_aged ] } = ProofOfQuota(20, 25, 20); \ No newline at end of file diff --git a/circom_circuits/Mantle/generate_inputs_for_pol.py b/circom_circuits/Mantle/generate_inputs_for_pol.py index 3899005..2925cf5 100755 --- a/circom_circuits/Mantle/generate_inputs_for_pol.py +++ b/circom_circuits/Mantle/generate_inputs_for_pol.py @@ -225,6 +225,8 @@ t1_constant = F(0x104bfd09ebdd0a57772289d0973489b62662a4dc6f09da8b4af3c5cfb1dcdd t0 = F(int(t0_constant) // total_stake) t1 = F(p- (int(t1_constant) // total_stake**2)) +print(t0) + value = F(total_stake / 100) threshold = (t0 + t1 * value) * value diff --git a/circom_circuits/Mantle/poc.circom b/circom_circuits/Mantle/poc.circom new file mode 100644 index 0000000..5a79e2f --- /dev/null +++ b/circom_circuits/Mantle/poc.circom @@ -0,0 +1,74 @@ +//test +pragma circom 2.1.9; + +include "../hash_bn/poseidon2_hash.circom"; +include "../hash_bn/merkle.circom"; +include "../misc/constants.circom"; + +template derive_voucher_nullifier(){ + signal input secret_voucher; + signal output out; + + component hash = Poseidon2_hash(2); + component dst = VOUCHER_NF(); + hash.inp[0] <== dst.out; + hash.inp[1] <== secret_voucher; + + out <== hash.out; +} + +template derive_reward_voucher(){ + signal input secret_voucher; + signal output out; + + component hash = Poseidon2_hash(2); + component dst = REWARD_VOUCHER(); + hash.inp[0] <== dst.out; + hash.inp[1] <== secret_voucher; + + out <== hash.out; +} + +template proof_of_claim(){ + signal input secret_voucher; + signal input voucher_merkle_path[32]; + signal input voucher_merkle_path_selectors[32]; + signal input mantle_tx_hash; + signal input voucher_root; + + signal output voucher_nullifier; + + //derive the reward voucher + component reward_voucher = derive_reward_voucher(); + reward_voucher.secret_voucher <== secret_voucher; + + //Check reward voucher membership + //First check selectors are indeed bits + for(var i = 0; i < 32; i++){ + voucher_merkle_path_selectors[i] * (1 - voucher_merkle_path_selectors[i]) === 0; + } + //Then check the proof of membership + component reward_membership = proof_of_membership(32); + for(var i = 0; i < 32; i++){ + reward_membership.nodes[i] <== voucher_merkle_path[i]; + reward_membership.selector[i] <== voucher_merkle_path_selectors[i]; + } + reward_membership.root <== voucher_root; + reward_membership.leaf <== reward_voucher.out; + + reward_membership.out === 1; + + + //derive the reward nullifier + component reward_nullifier = derive_voucher_nullifier(); + reward_nullifier.secret_voucher <== secret_voucher; + voucher_nullifier <== reward_nullifier.out; + + + + // dummy constraint to avoid unused public input to be erased after compilation optimisation + signal dummy; + dummy <== mantle_tx_hash * mantle_tx_hash; +} + +component main {public [voucher_root,mantle_tx_hash]}= proof_of_claim(); \ No newline at end of file diff --git a/circom_circuits/Mantle/pol.circom b/circom_circuits/Mantle/pol.circom index 719fe33..f4c24fb 100644 --- a/circom_circuits/Mantle/pol.circom +++ b/circom_circuits/Mantle/pol.circom @@ -247,4 +247,4 @@ template proof_of_leadership(secret_depth){ } -component main {public [sl,epoch_nonce,t0,t1,ledger_aged,ledger_latest,P_lead_part_one,P_lead_part_two]}= proof_of_leadership(25); \ No newline at end of file +//component main {public [sl,epoch_nonce,t0,t1,ledger_aged,ledger_latest,P_lead_part_one,P_lead_part_two]}= proof_of_leadership(25); \ No newline at end of file From ce997d91f56ae7ec51cf616c8366d85d04f0910a Mon Sep 17 00:00:00 2001 From: thomaslavaur <82142582+thomaslavaur@users.noreply.github.com> Date: Mon, 8 Sep 2025 11:44:20 +0200 Subject: [PATCH 7/7] Delete circom_circuits/Mantle/proof_of_claim.circom renamed poc --- circom_circuits/Mantle/proof_of_claim.circom | 74 -------------------- 1 file changed, 74 deletions(-) delete mode 100644 circom_circuits/Mantle/proof_of_claim.circom diff --git a/circom_circuits/Mantle/proof_of_claim.circom b/circom_circuits/Mantle/proof_of_claim.circom deleted file mode 100644 index 5a79e2f..0000000 --- a/circom_circuits/Mantle/proof_of_claim.circom +++ /dev/null @@ -1,74 +0,0 @@ -//test -pragma circom 2.1.9; - -include "../hash_bn/poseidon2_hash.circom"; -include "../hash_bn/merkle.circom"; -include "../misc/constants.circom"; - -template derive_voucher_nullifier(){ - signal input secret_voucher; - signal output out; - - component hash = Poseidon2_hash(2); - component dst = VOUCHER_NF(); - hash.inp[0] <== dst.out; - hash.inp[1] <== secret_voucher; - - out <== hash.out; -} - -template derive_reward_voucher(){ - signal input secret_voucher; - signal output out; - - component hash = Poseidon2_hash(2); - component dst = REWARD_VOUCHER(); - hash.inp[0] <== dst.out; - hash.inp[1] <== secret_voucher; - - out <== hash.out; -} - -template proof_of_claim(){ - signal input secret_voucher; - signal input voucher_merkle_path[32]; - signal input voucher_merkle_path_selectors[32]; - signal input mantle_tx_hash; - signal input voucher_root; - - signal output voucher_nullifier; - - //derive the reward voucher - component reward_voucher = derive_reward_voucher(); - reward_voucher.secret_voucher <== secret_voucher; - - //Check reward voucher membership - //First check selectors are indeed bits - for(var i = 0; i < 32; i++){ - voucher_merkle_path_selectors[i] * (1 - voucher_merkle_path_selectors[i]) === 0; - } - //Then check the proof of membership - component reward_membership = proof_of_membership(32); - for(var i = 0; i < 32; i++){ - reward_membership.nodes[i] <== voucher_merkle_path[i]; - reward_membership.selector[i] <== voucher_merkle_path_selectors[i]; - } - reward_membership.root <== voucher_root; - reward_membership.leaf <== reward_voucher.out; - - reward_membership.out === 1; - - - //derive the reward nullifier - component reward_nullifier = derive_voucher_nullifier(); - reward_nullifier.secret_voucher <== secret_voucher; - voucher_nullifier <== reward_nullifier.out; - - - - // dummy constraint to avoid unused public input to be erased after compilation optimisation - signal dummy; - dummy <== mantle_tx_hash * mantle_tx_hash; -} - -component main {public [voucher_root,mantle_tx_hash]}= proof_of_claim(); \ No newline at end of file