From 23eb2a4635654b2daa48fc6bf2762bdccbc4c0e5 Mon Sep 17 00:00:00 2001 From: thomaslavaur <82142582+thomaslavaur@users.noreply.github.com> Date: Thu, 6 Aug 2026 10:09:42 +0200 Subject: [PATCH] feat(zk): empower the poq (#3259) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- Cargo.lock | 52 ++-- Cargo.toml | 10 +- blend/message/src/crypto/proofs.rs | 7 +- blend/proofs/src/quota/fixtures.rs | 185 +++++++++----- blend/proofs/src/quota/inputs/prove/mod.rs | 22 +- .../proofs/src/quota/inputs/prove/private.rs | 42 +++- blend/proofs/src/quota/inputs/prove/public.rs | 42 +++- blend/proofs/src/quota/inputs/verify.rs | 2 + blend/proofs/src/quota/mod.rs | 8 + blend/proofs/src/quota/tests.rs | 30 ++- .../crypto/core_and_leader/send.rs | 3 +- .../core_and_leader/send_and_receive.rs | 3 +- .../src/message_blend/provers/core/mod.rs | 1 + .../provers/core_and_leader/mod.rs | 2 + .../src/message_blend/provers/leader/mod.rs | 1 + .../src/message_blend/provers/test_utils.rs | 32 ++- core/src/mantle/transactions/codec.rs | 11 +- flake.lock | 16 +- flake.nix | 7 +- .../mantle/sdp/rewards/blend/current_epoch.rs | 3 +- ledger/src/mantle/sdp/test_utils.rs | 3 +- services/blend/src/core/mod.rs | 4 +- services/blend/src/core/processor.rs | 3 +- services/blend/src/core/tests/utils.rs | 3 +- services/blend/src/edge/mod.rs | 3 +- zk/proofs/poq/benches/common/mod.rs | 30 ++- zk/proofs/poq/src/chain_inputs.rs | 2 + zk/proofs/poq/src/common_inputs.rs | 42 +++- zk/proofs/poq/src/inputs.rs | 76 ++++-- zk/proofs/poq/src/lib.rs | 225 +++++++++++++----- zk/proofs/poq/src/pow_inputs.rs | 49 ++++ zk/proofs/poq/src/wallet_inputs.rs | 10 +- 32 files changed, 699 insertions(+), 230 deletions(-) create mode 100644 zk/proofs/poq/src/pow_inputs.rs diff --git a/Cargo.lock b/Cargo.lock index 8e4c7ec7d..22d2e1a99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,7 +105,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -116,7 +116,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1567,7 +1567,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" dependencies = [ "data-encoding", - "syn 2.0.118", + "syn 1.0.109", ] [[package]] @@ -1752,7 +1752,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1968,7 +1968,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -3070,7 +3070,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4297,8 +4297,8 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-build" -version = "0.5.3" -source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.3#127626881faa975aa8e9868422cf6bbb08fcb512" +version = "0.5.5" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.5#32f1ac43f331f0eeff3a2a0aec0f153564d03583" dependencies = [ "dirs", "fd-lock", @@ -4309,16 +4309,16 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-common" -version = "0.5.3" -source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.3#127626881faa975aa8e9868422cf6bbb08fcb512" +version = "0.5.5" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.5#32f1ac43f331f0eeff3a2a0aec0f153564d03583" dependencies = [ "logos-blockchain-circuits-types", ] [[package]] name = "logos-blockchain-circuits-poc-sys" -version = "0.5.3" -source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.3#127626881faa975aa8e9868422cf6bbb08fcb512" +version = "0.5.5" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.5#32f1ac43f331f0eeff3a2a0aec0f153564d03583" dependencies = [ "logos-blockchain-circuits-build", "logos-blockchain-circuits-common", @@ -4327,8 +4327,8 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-pol-sys" -version = "0.5.3" -source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.3#127626881faa975aa8e9868422cf6bbb08fcb512" +version = "0.5.5" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.5#32f1ac43f331f0eeff3a2a0aec0f153564d03583" dependencies = [ "logos-blockchain-circuits-build", "logos-blockchain-circuits-common", @@ -4337,8 +4337,8 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-poq-sys" -version = "0.5.3" -source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.3#127626881faa975aa8e9868422cf6bbb08fcb512" +version = "0.5.5" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.5#32f1ac43f331f0eeff3a2a0aec0f153564d03583" dependencies = [ "logos-blockchain-circuits-build", "logos-blockchain-circuits-common", @@ -4354,8 +4354,8 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-signature-sys" -version = "0.5.3" -source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.3#127626881faa975aa8e9868422cf6bbb08fcb512" +version = "0.5.5" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.5#32f1ac43f331f0eeff3a2a0aec0f153564d03583" dependencies = [ "logos-blockchain-circuits-build", "logos-blockchain-circuits-common", @@ -4364,8 +4364,8 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-types" -version = "0.5.3" -source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.3#127626881faa975aa8e9868422cf6bbb08fcb512" +version = "0.5.5" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.5#32f1ac43f331f0eeff3a2a0aec0f153564d03583" dependencies = [ "bytes", "libc", @@ -5890,7 +5890,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -7457,7 +7457,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -7967,7 +7967,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -8262,7 +8262,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -8294,7 +8294,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -9525,7 +9525,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 24fca3dff..874c4fe6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -164,11 +164,11 @@ lb-zksign = { default-features = false, package = "logo lb-zone-sdk = { default-features = false, package = "logos-blockchain-zone-sdk", path = "./zone-sdk" } # When bumping any lbc-* tag, also update the logos-blockchain-circuits flake input tag in flake.nix # and run `nix flake lock --update-input logos-blockchain-circuits`. -lbc-poc-sys = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-circuits.git", package = "logos-blockchain-circuits-poc-sys", tag = "v0.5.3" } -lbc-pol-sys = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-circuits.git", package = "logos-blockchain-circuits-pol-sys", tag = "v0.5.3" } -lbc-poq-sys = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-circuits.git", package = "logos-blockchain-circuits-poq-sys", tag = "v0.5.3" } -lbc-signature-sys = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-circuits.git", package = "logos-blockchain-circuits-signature-sys", tag = "v0.5.3" } -lbc-types = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-circuits.git", package = "logos-blockchain-circuits-types", tag = "v0.5.3" } +lbc-poc-sys = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-circuits.git", package = "logos-blockchain-circuits-poc-sys", tag = "v0.5.5" } +lbc-pol-sys = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-circuits.git", package = "logos-blockchain-circuits-pol-sys", tag = "v0.5.5" } +lbc-poq-sys = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-circuits.git", package = "logos-blockchain-circuits-poq-sys", tag = "v0.5.5" } +lbc-signature-sys = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-circuits.git", package = "logos-blockchain-circuits-signature-sys", tag = "v0.5.5" } +lbc-types = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-circuits.git", package = "logos-blockchain-circuits-types", tag = "v0.5.5" } lbp-error = { default-features = false, package = "logos-blockchain-proofs-error", path = "./zk/proofs/error" } testing-framework-core = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-testing.git", rev = "db880d61155db3a4f32177ef16b0f1e4bf22b1f6" } testing-framework-runner-compose = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-testing.git", rev = "db880d61155db3a4f32177ef16b0f1e4bf22b1f6" } diff --git a/blend/message/src/crypto/proofs.rs b/blend/message/src/crypto/proofs.rs index 3ca0881f5..fc474c42c 100644 --- a/blend/message/src/crypto/proofs.rs +++ b/blend/message/src/crypto/proofs.rs @@ -5,7 +5,7 @@ use lb_blend_proofs::{ self, ProofOfQuota, VerifiedProofOfQuota, inputs::prove::{ PublicInputs, - public::{CoreInputs, LeaderInputs}, + public::{CoreInputs, LeaderInputs, PowInputs}, }, }, selection::{self, ProofOfSelection, VerifiedProofOfSelection, inputs::VerifyInputs}, @@ -24,6 +24,7 @@ use crate::encap::ProofsVerifier; pub struct PoQVerificationInputsMinusSigningKey { pub core: CoreInputs, pub leader: LeaderInputs, + pub pow: PowInputs, } #[cfg(test)] @@ -44,6 +45,7 @@ impl Default for PoQVerificationInputsMinusSigningKey { lottery_0: Fr::ZERO, lottery_1: Fr::ZERO, }, + pow: PowInputs::unwired_placeholder(), } } } @@ -77,7 +79,7 @@ impl ProofsVerifier for RealProofsVerifier { proof: ProofOfQuota, signing_key: &Ed25519PublicKey, ) -> Result { - let PoQVerificationInputsMinusSigningKey { core, leader } = self.current_inputs; + let PoQVerificationInputsMinusSigningKey { core, leader, pow } = self.current_inputs; // Try with current input, and if it fails, try with the previous one, if any // (i.e., within the epoch transition period). @@ -90,6 +92,7 @@ impl ProofsVerifier for RealProofsVerifier { .verify(&PublicInputs { core, leader, + pow, signing_key: *signing_key.as_inner(), }) .map_err(Error::ProofOfQuota); diff --git a/blend/proofs/src/quota/fixtures.rs b/blend/proofs/src/quota/fixtures.rs index cfdc57e05..e33030c44 100644 --- a/blend/proofs/src/quota/fixtures.rs +++ b/blend/proofs/src/quota/fixtures.rs @@ -6,8 +6,8 @@ use crate::quota::{ Ed25519PublicKey, inputs::prove::{ PublicInputs, - private::{ProofOfCoreQuotaInputs, ProofOfLeadershipQuotaInputs}, - public::{CoreInputs, LeaderInputs}, + private::{ProofOfCoreQuotaInputs, ProofOfLeadershipQuotaInputs, ProofOfWorkQuotaInputs}, + public::{CoreInputs, LeaderInputs, PowInputs}, }, }; @@ -28,11 +28,10 @@ pub fn valid_proof_of_core_quota_inputs( signing_key, core: CoreInputs { quota: core_quota, - zk_root: - "10774149910279330054096178616484626574938100628643657398591620611653283350567" - .parse::() - .unwrap() - .into(), + zk_root: "8648439177578503276872559887462337398415999326614652623958174178821393732006" + .parse::() + .unwrap() + .into(), }, leader: LeaderInputs { pol_epoch_nonce: BigUint::from(1u64).into(), @@ -41,93 +40,97 @@ pub fn valid_proof_of_core_quota_inputs( lottery_0, lottery_1, }, + pow: PowInputs { + pow_blend_difficulty: BigUint::from(1u64).into(), + pow_quota: 1, + }, }; let private_inputs = ProofOfCoreQuotaInputs { core_path_and_selectors: [ ( - "13664394408134714148474230123476522725121022841643115538772760906625244311247", - false, - ), - ( - "19991080109804084229148819955447485980685271866236940378355513894509403472825", - false, - ), - ( - "1719423980074249650282196742833822060536614400613876573240720330836944657046", + "7495929195953210541208591724669352166258167386402673863836582332959049741998", true, ), ( - "9139059421670197230454683564023894631544291367010097325710917953251896109820", - true, - ), - ( - "5364655248740251237070591917425080374871240308613634110599105641060308262480", + "8777307683015870709408351814333112152804794621246499110711643849444602886454", false, ), ( - "4321492523488456904492279703871677986518013774282254436225897785632983582392", + "2036778247346771710692375429289939913839125337381038976370669720942761418159", false, ), ( - "2990224148203398705081847530695215902031679797575769158839150593627687374477", - true, - ), - ( - "10115668570671746589151792513284313334215992216493690986604010084064526087897", + "5150238042344629386129384874056943525919076545561275756461664527118718260316", false, ), ( - "6861547258030107481535610157091754819359096245516659866713074178195667725661", + "4936482837140898160990313117063047116523911033722639295600657041261330950251", + true, + ), + ( + "8937835534203757508211957478554582398600683099364069427807070320868647534222", false, ), ( - "7263098007194293142502159691381240540663513841811088748578355471278661748398", - true, - ), - ( - "19870876389339998317953427375552419155167215853924118334240834433312282250010", - true, - ), - ( - "8986537640578106247026054824010082082887537390181434985471297569735311570527", - true, - ), - ( - "2677828888032047082692090535060454810065355495500936244017540612071241172074", + "20952980496079374121937279088975994236259798048934859728621453910938508315429", false, ), ( - "17555659938930335545491158932916791395314469587225130013830235195468776678581", - true, - ), - ( - "9071399479181997918431555676567419518917037685697025275563780306843446304571", + "3127567055231452038159138967497074106547716639376277019791213152208715502108", false, ), ( - "6281992864280083991852918191693022891803454129450254484599792207873685708287", - false, - ), - ( - "17218725285443376817865240748479799866804154572691475341349156521930190101705", - false, - ), - ( - "14439152042823579940992841514440450319175689090721997229482427278436880180442", + "4156278452715681991545944691699148599903237834447534444828105997249209183668", true, ), ( - "4375092350636859316334567236773062868645005901112681926345752432026107616905", + "18179601246737692255359026481063507429449403349479564041630890140769203876921", true, ), ( - "2391466682452790260991116539507901841740149925567473331054306831262572734122", + "20437752904871274044354648385284243748143753378271484669957282746297202768728", + false, + ), + ( + "20964128655175295722279612439890819641847965870342369257222152533521036200042", + false, + ), + ( + "8529481264681700999783095712819666338972397694516091342589511693767349600946", + true, + ), + ( + "11390366370077994090069924023908925478862142650386311632070176411828525668517", + true, + ), + ( + "5917548759338686071972714539493044537194763096388397090814246211223906547270", + false, + ), + ( + "20167409213677086350276694709897905285253180362287004012973310966314090006733", + true, + ), + ( + "2263236988262202014904251664760262847563737954962247776121488194923407644186", + false, + ), + ( + "19725021492526941122134445491655907123723766494432771594886452496452660956448", + false, + ), + ( + "5142311427791207713153244085802613121123367252788982353300919738927631606801", + false, + ), + ( + "9162839441909107733910143825064550229009975662836406280871655871464192283628", false, ), ] .map(|(value, selector)| (value.parse::().unwrap().into(), selector)), - core_sk: "6576495977526760241501499963811136028674473098047722736567052812753357178145" + core_sk: "12667207762918681737213577433372645554941956657477853383508894204444641435708" .parse::() .unwrap() .into(), @@ -170,6 +173,10 @@ pub fn valid_proof_of_leadership_quota_inputs( zk_root: BigUint::from(1u64).into(), quota: 1, }, + pow: PowInputs { + pow_blend_difficulty: BigUint::from(1u64).into(), + pow_quota: 1, + }, }; let private_inputs = ProofOfLeadershipQuotaInputs { @@ -320,3 +327,65 @@ pub fn valid_proof_of_leadership_quota_inputs( (public_inputs, private_inputs) } + +// Reference values taken from the tests within the `poq` crate. +#[must_use] +pub fn valid_proof_of_work_quota_inputs( + signing_key: Ed25519PublicKey, + pow_quota: u64, +) -> (PublicInputs, ProofOfWorkQuotaInputs) { + let public_inputs = PublicInputs { + signing_key, + pow: PowInputs { + pow_blend_difficulty: + "10631870504716456348838861774188160492563879712126054449569633827216160699117" + .parse::() + .unwrap() + .into(), + pow_quota, + }, + core: CoreInputs { + zk_root: "7196120816867742764244305920813414287385070139349175831448761050095304066684" + .parse::() + .unwrap() + .into(), + quota: 10, + }, + leader: LeaderInputs { + pol_epoch_nonce: + "10612486568558363932403470969435464905237449260307875593547304337083779862075" + .parse::() + .unwrap() + .into(), + pol_ledger_aged: + "12901234158445930888471913831760642108627105440164948073754288938400615804116" + .parse::() + .unwrap() + .into(), + message_quota: 15, + lottery_0: "148409079361904587837471709956458430342187235603420891378597419711486680" + .parse::() + .unwrap() + .into(), + lottery_1: + "21888242871336145414933615591437256729835795974444825699352339602896135814447" + .parse::() + .unwrap() + .into(), + }, + }; + + let private_inputs = ProofOfWorkQuotaInputs { + pow_sk: "10044758699144566038746293679996441958807939592793641056821682251877616662024" + .parse::() + .unwrap() + .into(), + pow_block_hash: + "17412116459874055221726429396167151400213699852365025340176158516975240665302" + .parse::() + .unwrap() + .into(), + }; + + (public_inputs, private_inputs) +} diff --git a/blend/proofs/src/quota/inputs/prove/mod.rs b/blend/proofs/src/quota/inputs/prove/mod.rs index 3e0c21b29..b9af59b52 100644 --- a/blend/proofs/src/quota/inputs/prove/mod.rs +++ b/blend/proofs/src/quota/inputs/prove/mod.rs @@ -1,11 +1,13 @@ use lb_groth16::fr_from_bytes; use lb_poq::{ PoQBlendInputsData, PoQChainInputsData, PoQCommonInputsData, PoQInputsFromDataError, - PoQWalletInputsData, PoQWitnessInputs, + PoQPowInputsData, PoQWalletInputsData, PoQWitnessInputs, }; use crate::quota::inputs::{ - prove::private::{ProofOfCoreQuotaInputs, ProofOfLeadershipQuotaInputs, ProofType}, + prove::private::{ + ProofOfCoreQuotaInputs, ProofOfLeadershipQuotaInputs, ProofOfWorkQuotaInputs, ProofType, + }, split_ephemeral_signing_key, }; @@ -36,6 +38,7 @@ impl TryFrom for PoQWitnessInputs { core_quota: value.public.core.quota, index: value.private.key_index, leader_quota: value.public.leader.message_quota, + pow_quota: value.public.pow.pow_quota, message_key: ( fr_from_bytes(&signing_key_first_half[..]).expect( "First half of signing public key does not represent a valid `Fr` point.", @@ -45,6 +48,7 @@ impl TryFrom for PoQWitnessInputs { ), ), selector: value.private.selector, + pow_difficulty: value.public.pow.pow_blend_difficulty, }; witness_input_for_proof_type( chain_input_data, @@ -91,7 +95,7 @@ fn witness_input_for_proof_type( note_value, output_number, slot, - secret_key, + pol_secret_key: secret_key, transaction_hash, }; PoQWitnessInputs::from_leader_data( @@ -100,5 +104,17 @@ fn witness_input_for_proof_type( wallet_input_data, ) } + ProofType::PowQuota(pow_quota_private_inputs) => { + let ProofOfWorkQuotaInputs { + pow_sk, + pow_block_hash, + } = *pow_quota_private_inputs; + + let pow_input_data = PoQPowInputsData { + pow_secret_key: pow_sk, + block_hash: pow_block_hash, + }; + PoQWitnessInputs::from_pow_data(chain_input_data, common_input_data, pow_input_data) + } } } diff --git a/blend/proofs/src/quota/inputs/prove/private.rs b/blend/proofs/src/quota/inputs/prove/private.rs index bda2cffbc..04f1d0f7a 100644 --- a/blend/proofs/src/quota/inputs/prove/private.rs +++ b/blend/proofs/src/quota/inputs/prove/private.rs @@ -1,6 +1,6 @@ use core::fmt::{self, Debug, Formatter}; -use lb_poq::AgedNotePathAndSelectors; +use lb_poq::{AgedNotePathAndSelectors, PoQSelector}; use zeroize::ZeroizeOnDrop; use crate::{ @@ -16,7 +16,7 @@ use crate::{ #[non_exhaustive] pub struct Inputs { pub key_index: u64, - pub selector: bool, + pub selector: PoQSelector, pub proof_type: ProofType, } @@ -47,6 +47,19 @@ impl Inputs { } } + #[must_use] + pub fn new_proof_of_work_quota_inputs( + key_index: u64, + proof_of_work_quota_inputs: ProofOfWorkQuotaInputs, + ) -> Self { + let proof_type: ProofType = proof_of_work_quota_inputs.into(); + Self { + key_index, + selector: proof_type.proof_selector(), + proof_type, + } + } + /// Return the right `sk` for a Proof of Quota depending on the proof type, as per the spec: . #[must_use] pub fn get_secret_selection_randomness_sk( @@ -71,6 +84,10 @@ impl Inputs { slot_number: leadership_quota_private_inputs.slot, } } + ProofType::PowQuota(pow_quota_private_inputs) => SelectionRandomnessSecretInput::Pow { + pow_sk: pow_quota_private_inputs.pow_sk, + epoch_nonce: *pol_epoch_nonce, + }, } } } @@ -79,6 +96,7 @@ impl Inputs { pub enum ProofType { CoreQuota(Box), LeadershipQuota(Box), + PowQuota(Box), } impl Debug for ProofType { @@ -86,16 +104,18 @@ impl Debug for ProofType { match self { Self::CoreQuota(_) => f.write_str("ProofType::CoreQuota"), Self::LeadershipQuota(_) => f.write_str("ProofType::LeadershipQuota"), + Self::PowQuota(_) => f.write_str("ProofType::PowQuota"), } } } impl ProofType { #[must_use] - pub const fn proof_selector(&self) -> bool { + pub const fn proof_selector(&self) -> PoQSelector { match self { - Self::CoreQuota(_) => false, - Self::LeadershipQuota(_) => true, + Self::CoreQuota(_) => PoQSelector::Core, + Self::LeadershipQuota(_) => PoQSelector::Leader, + Self::PowQuota(_) => PoQSelector::Pow, } } } @@ -127,3 +147,15 @@ impl From for ProofType { Self::LeadershipQuota(Box::new(value)) } } + +#[derive(Clone, PartialEq, Eq, ZeroizeOnDrop)] +pub struct ProofOfWorkQuotaInputs { + pub pow_sk: ZkHash, + pub pow_block_hash: ZkHash, +} + +impl From for ProofType { + fn from(value: ProofOfWorkQuotaInputs) -> Self { + Self::PowQuota(Box::new(value)) + } +} diff --git a/blend/proofs/src/quota/inputs/prove/public.rs b/blend/proofs/src/quota/inputs/prove/public.rs index 399704f41..22ba5c682 100644 --- a/blend/proofs/src/quota/inputs/prove/public.rs +++ b/blend/proofs/src/quota/inputs/prove/public.rs @@ -1,6 +1,6 @@ use core::fmt::{self, Debug, Formatter}; -use lb_groth16::{Fr, fr_to_bytes}; +use lb_groth16::{AdditiveGroup as _, Fr, fr_to_bytes}; use serde::{Deserialize, Serialize}; use crate::{ZkHash, quota::Ed25519PublicKey}; @@ -11,6 +11,7 @@ pub struct Inputs { pub signing_key: Ed25519PublicKey, pub core: CoreInputs, pub leader: LeaderInputs, + pub pow: PowInputs, } impl Debug for Inputs { @@ -19,6 +20,7 @@ impl Debug for Inputs { .field("signing_key", &hex::encode(self.signing_key.as_bytes())) .field("core", &self.core) .field("leader", &self.leader) + .field("pow", &self.pow) .finish() } } @@ -32,6 +34,7 @@ impl Default for Inputs { signing_key: Ed25519PublicKey::from_bytes(&[0; ED25519_PUBLIC_KEY_SIZE]).unwrap(), core: CoreInputs::default(), leader: LeaderInputs::default(), + pow: PowInputs::default(), } } } @@ -84,3 +87,40 @@ impl Debug for LeaderInputs { .finish() } } + +#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(test, derive(Default))] +pub struct PowInputs { + #[serde(with = "lb_groth16::serde::serde_fr")] + pub pow_blend_difficulty: ZkHash, + pub pow_quota: u64, +} + +impl PowInputs { + /// Placeholder inputs for the `PoW` quota parameters, which are not plumbed + /// through from the chain yet. + /// + /// These are public inputs, so a prover and the verifier checking its + /// proofs must use identical values. Every call site shares this single + /// definition so they cannot drift apart. + // TODO: Remove once the PoW quota parameters are sourced from the chain. + #[must_use] + pub const fn unwired_placeholder() -> Self { + Self { + pow_blend_difficulty: ZkHash::ZERO, + pow_quota: 0, + } + } +} + +impl Debug for PowInputs { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("PowInputs") + .field( + "pow_blend_difficulty", + &hex::encode(fr_to_bytes(&self.pow_blend_difficulty)), + ) + .field("pow_quota", &self.pow_quota) + .finish() + } +} diff --git a/blend/proofs/src/quota/inputs/verify.rs b/blend/proofs/src/quota/inputs/verify.rs index 7c5620aef..6deacbe8b 100644 --- a/blend/proofs/src/quota/inputs/verify.rs +++ b/blend/proofs/src/quota/inputs/verify.rs @@ -43,6 +43,8 @@ impl From for PoQVerifierInput { .expect("Second half of signing public key does not represent a valid `Fr` point."), key_nullifier: value.key_nullifier, leader_quota: value.prove_inputs.leader.message_quota, + pow_quota: value.prove_inputs.pow.pow_quota, + pow_blend_difficulty: value.prove_inputs.pow.pow_blend_difficulty, pol_epoch_nonce: value.prove_inputs.leader.pol_epoch_nonce, pol_ledger_aged: value.prove_inputs.leader.pol_ledger_aged, lottery_0: value.prove_inputs.leader.lottery_0, diff --git a/blend/proofs/src/quota/mod.rs b/blend/proofs/src/quota/mod.rs index 6e4c0385f..471c8300a 100644 --- a/blend/proofs/src/quota/mod.rs +++ b/blend/proofs/src/quota/mod.rs @@ -251,6 +251,10 @@ pub enum SelectionRandomnessSecretInput { note_secret_key: ZkHash, slot_number: u64, }, + Pow { + pow_sk: ZkHash, + epoch_nonce: ZkHash, + }, } const DOMAIN_SEPARATION_TAG: [u8; 23] = *b"SELECTION_RANDOMNESS_V1"; static DOMAIN_SEPARATION_TAG_FR: LazyLock = LazyLock::new(|| { @@ -268,6 +272,10 @@ fn generate_secret_selection_randomness( note_secret_key, slot_number, } => (note_secret_key, (*slot_number).into()), + SelectionRandomnessSecretInput::Pow { + pow_sk, + epoch_nonce, + } => (pow_sk, (*epoch_nonce)), }; [ *DOMAIN_SEPARATION_TAG_FR, diff --git a/blend/proofs/src/quota/tests.rs b/blend/proofs/src/quota/tests.rs index 96b2e5a14..afad64de1 100644 --- a/blend/proofs/src/quota/tests.rs +++ b/blend/proofs/src/quota/tests.rs @@ -6,11 +6,14 @@ use lb_key_management_system_keys::keys::UnsecuredZkKey; use crate::{ quota::{ DOMAIN_SEPARATION_TAG_FR, ED25519_PUBLIC_KEY_SIZE, Ed25519PublicKey, VerifiedProofOfQuota, - fixtures::{valid_proof_of_core_quota_inputs, valid_proof_of_leadership_quota_inputs}, + fixtures::{ + valid_proof_of_core_quota_inputs, valid_proof_of_leadership_quota_inputs, + valid_proof_of_work_quota_inputs, + }, inputs::prove::{ PrivateInputs, PublicInputs, private::ProofOfCoreQuotaInputs, - public::{CoreInputs, LeaderInputs}, + public::{CoreInputs, LeaderInputs, PowInputs}, }, }, selection::derive_key_nullifier_from_secret_selection_randomness, @@ -104,6 +107,26 @@ fn valid_proof_of_leadership_quota() { ); } +#[test] +fn valid_proof_of_work_quota() { + let (public_inputs, private_inputs) = valid_proof_of_work_quota_inputs( + Ed25519PublicKey::from_bytes(&[0; ED25519_PUBLIC_KEY_SIZE]).unwrap(), + 20, + ); + + let (proof, secret_selection_randomness) = VerifiedProofOfQuota::new( + &public_inputs, + PrivateInputs::new_proof_of_work_quota_inputs(0, private_inputs), + ) + .unwrap(); + + let verified_proof_of_quota = proof.into_inner().verify(&public_inputs).unwrap(); + assert_eq!( + derive_key_nullifier_from_secret_selection_randomness(secret_selection_randomness), + verified_proof_of_quota.key_nullifier() + ); +} + struct PoQInputs { public_inputs: PublicInputs, secret_inputs: [ProofOfCoreQuotaInputs; INPUTS], @@ -137,6 +160,7 @@ fn generate_inputs() -> PoQInputs { PublicInputs { core: core_inputs, leader: leader_inputs, + pow: PowInputs::default(), signing_key, } }; @@ -254,6 +278,7 @@ fn same_key_different_indices() { zk_root: merkle_tree.root(), }, leader: LeaderInputs::default(), + pow: PowInputs::default(), signing_key: Ed25519PublicKey::from_bytes(&[10; _]).unwrap(), }, secret_inputs: [ProofOfCoreQuotaInputs { @@ -306,6 +331,7 @@ fn different_keys_same_index() { zk_root: merkle_tree.root(), }, leader: LeaderInputs::default(), + pow: PowInputs::default(), signing_key: Ed25519PublicKey::from_bytes(&[1; _]).unwrap(), }, secret_inputs: [ProofOfCoreQuotaInputs { diff --git a/blend/scheduling/src/message_blend/crypto/core_and_leader/send.rs b/blend/scheduling/src/message_blend/crypto/core_and_leader/send.rs index ae330a43b..8b32ee4cb 100644 --- a/blend/scheduling/src/message_blend/crypto/core_and_leader/send.rs +++ b/blend/scheduling/src/message_blend/crypto/core_and_leader/send.rs @@ -212,7 +212,7 @@ mod test { use lb_blend_message::crypto::proofs::PoQVerificationInputsMinusSigningKey; use lb_blend_proofs::quota::inputs::prove::{ private::ProofOfLeadershipQuotaInputs, - public::{CoreInputs, LeaderInputs}, + public::{CoreInputs, LeaderInputs, PowInputs}, }; use lb_core::crypto::ZkHash; use lb_cryptarchia_engine::Epoch; @@ -257,6 +257,7 @@ mod test { zk_root: ZkHash::ZERO, }, leader: leader_inputs, + pow: PowInputs::unwired_placeholder(), }, MockCorePoQGenerator, Epoch::new(0), diff --git a/blend/scheduling/src/message_blend/crypto/core_and_leader/send_and_receive.rs b/blend/scheduling/src/message_blend/crypto/core_and_leader/send_and_receive.rs index 62a9c49e6..f7bf32c86 100644 --- a/blend/scheduling/src/message_blend/crypto/core_and_leader/send_and_receive.rs +++ b/blend/scheduling/src/message_blend/crypto/core_and_leader/send_and_receive.rs @@ -124,7 +124,7 @@ mod test { use lb_blend_message::crypto::proofs::PoQVerificationInputsMinusSigningKey; use lb_blend_proofs::quota::inputs::prove::{ private::ProofOfLeadershipQuotaInputs, - public::{CoreInputs, LeaderInputs}, + public::{CoreInputs, LeaderInputs, PowInputs}, }; use lb_core::crypto::ZkHash; use lb_cryptarchia_engine::Epoch; @@ -176,6 +176,7 @@ mod test { zk_root: ZkHash::ZERO, }, leader: initial_leader, + pow: PowInputs::unwired_placeholder(), }, MockCorePoQGenerator, Epoch::new(0), diff --git a/blend/scheduling/src/message_blend/provers/core/mod.rs b/blend/scheduling/src/message_blend/provers/core/mod.rs index c30fdf310..4c192af10 100644 --- a/blend/scheduling/src/message_blend/provers/core/mod.rs +++ b/blend/scheduling/src/message_blend/provers/core/mod.rs @@ -109,6 +109,7 @@ where signing_key: ephemeral_signing_key.public_key().into_inner(), core: public_inputs.core, leader: public_inputs.leader, + pow: public_inputs.pow, }, key_index, ).await.expect("Core PoQ generation should not fail."); diff --git a/blend/scheduling/src/message_blend/provers/core_and_leader/mod.rs b/blend/scheduling/src/message_blend/provers/core_and_leader/mod.rs index 6a2555516..0b8d6a1b8 100644 --- a/blend/scheduling/src/message_blend/provers/core_and_leader/mod.rs +++ b/blend/scheduling/src/message_blend/provers/core_and_leader/mod.rs @@ -104,6 +104,7 @@ where let current_epoch_membership_size = self.core_proofs_generator.settings.membership_size; let current_epoch_core_public_inputs = self.core_proofs_generator.settings.public_inputs.core; + let current_epoch_pow_public_inputs = self.core_proofs_generator.settings.public_inputs.pow; self.leader_proofs_generator = Some(RealLeaderProofsGenerator::new( ProofsGeneratorSettings { @@ -113,6 +114,7 @@ where public_inputs: PoQVerificationInputsMinusSigningKey { core: current_epoch_core_public_inputs, leader: current_leader_inputs, + pow: current_epoch_pow_public_inputs, }, encapsulation_layers: self.core_proofs_generator.settings.encapsulation_layers, }, diff --git a/blend/scheduling/src/message_blend/provers/leader/mod.rs b/blend/scheduling/src/message_blend/provers/leader/mod.rs index fd3c6cc4f..814be7f42 100644 --- a/blend/scheduling/src/message_blend/provers/leader/mod.rs +++ b/blend/scheduling/src/message_blend/provers/leader/mod.rs @@ -115,6 +115,7 @@ fn create_proof_stream( signing_key: ephemeral_signing_key.public_key().into_inner(), core: public_inputs.core, leader: public_inputs.leader, + pow: public_inputs.pow, }, PrivateInputs::new_proof_of_leadership_quota_inputs( message_release_index, diff --git a/blend/scheduling/src/message_blend/provers/test_utils.rs b/blend/scheduling/src/message_blend/provers/test_utils.rs index a501eae19..9a6dfabbc 100644 --- a/blend/scheduling/src/message_blend/provers/test_utils.rs +++ b/blend/scheduling/src/message_blend/provers/test_utils.rs @@ -14,7 +14,7 @@ use lb_key_management_system_keys::keys::{ED25519_PUBLIC_KEY_SIZE, Ed25519Public use crate::message_blend::CoreProofOfQuotaGenerator; pub const fn poq_public_inputs_from_epoch_public_inputs_and_signing_key( - (PoQVerificationInputsMinusSigningKey { core, leader }, signing_key): ( + (PoQVerificationInputsMinusSigningKey { core, leader, pow }, signing_key): ( PoQVerificationInputsMinusSigningKey, Ed25519PublicKey, ), @@ -23,20 +23,26 @@ pub const fn poq_public_inputs_from_epoch_public_inputs_and_signing_key( signing_key: signing_key.into_inner(), core, leader, + pow, } } pub fn valid_proof_of_quota_inputs( core_quota: u64, ) -> (PoQVerificationInputsMinusSigningKey, ProofOfCoreQuotaInputs) { - let (PoQPublicInputs { core, leader, .. }, private_inputs) = valid_proof_of_core_quota_inputs( + let ( + PoQPublicInputs { + core, leader, pow, .. + }, + private_inputs, + ) = valid_proof_of_core_quota_inputs( Ed25519PublicKey::from_bytes(&[0; ED25519_PUBLIC_KEY_SIZE]) .unwrap() .into_inner(), core_quota, ); ( - PoQVerificationInputsMinusSigningKey { core, leader }, + PoQVerificationInputsMinusSigningKey { core, leader, pow }, private_inputs, ) } @@ -47,15 +53,19 @@ pub fn valid_proof_of_leader_inputs( PoQVerificationInputsMinusSigningKey, ProofOfLeadershipQuotaInputs, ) { - let (PoQPublicInputs { core, leader, .. }, private_inputs) = - valid_proof_of_leadership_quota_inputs( - Ed25519PublicKey::from_bytes(&[0; ED25519_PUBLIC_KEY_SIZE]) - .unwrap() - .into_inner(), - leader_quota, - ); + let ( + PoQPublicInputs { + core, leader, pow, .. + }, + private_inputs, + ) = valid_proof_of_leadership_quota_inputs( + Ed25519PublicKey::from_bytes(&[0; ED25519_PUBLIC_KEY_SIZE]) + .unwrap() + .into_inner(), + leader_quota, + ); ( - PoQVerificationInputsMinusSigningKey { core, leader }, + PoQVerificationInputsMinusSigningKey { core, leader, pow }, private_inputs, ) } diff --git a/core/src/mantle/transactions/codec.rs b/core/src/mantle/transactions/codec.rs index a68668ac8..1f3485015 100644 --- a/core/src/mantle/transactions/codec.rs +++ b/core/src/mantle/transactions/codec.rs @@ -104,7 +104,10 @@ mod tests { use std::collections::HashMap; use ark_ff::AdditiveGroup as _; - use lb_blend_proofs::{quota::VerifiedProofOfQuota, selection::VerifiedProofOfSelection}; + use lb_blend_proofs::{ + quota::{PROOF_OF_QUOTA_SIZE, VerifiedProofOfQuota}, + selection::VerifiedProofOfSelection, + }; use lb_groth16::{CompressedGroth16Proof, Fr}; use lb_key_management_system_keys::keys::{Ed25519Key, Ed25519Signature, ZkKey, ZkPublicKey}; use lb_utils::bounded::BoundedError; @@ -575,7 +578,8 @@ mod tests { let blend_proof = ActivityProof { epoch: 42.into(), signing_key: signing_key.public_key(), - proof_of_quota: VerifiedProofOfQuota::from_bytes_unchecked([0u8; 160]).into(), + proof_of_quota: VerifiedProofOfQuota::from_bytes_unchecked([0u8; PROOF_OF_QUOTA_SIZE]) + .into(), proof_of_selection: VerifiedProofOfSelection::from_bytes_unchecked([0u8; 32]).into(), }; @@ -631,7 +635,8 @@ mod tests { let blend_proof = ActivityProof { epoch: u32::MAX.into(), signing_key: signing_key.public_key(), - proof_of_quota: VerifiedProofOfQuota::from_bytes_unchecked([0u8; 160]).into(), + proof_of_quota: VerifiedProofOfQuota::from_bytes_unchecked([0u8; PROOF_OF_QUOTA_SIZE]) + .into(), proof_of_selection: VerifiedProofOfSelection::from_bytes_unchecked([0u8; 32]).into(), }; diff --git a/flake.lock b/flake.lock index 978db4f27..6fc9d22cd 100644 --- a/flake.lock +++ b/flake.lock @@ -20,17 +20,17 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1781004244, - "narHash": "sha256-zd4XYY+HPOMJXigHQDmXDvt7D1988YNPUwPPznhpFdA=", + "lastModified": 1785937360, + "narHash": "sha256-Id4IZ1pQ5qcTabs4fFWbfSZPHEbyDAx/3CDKv4CawII=", "owner": "logos-blockchain", "repo": "logos-blockchain-circuits", - "rev": "2846ee7a4cfa24458bb8063412ab2e753b344d2f", + "rev": "32f1ac43f331f0eeff3a2a0aec0f153564d03583", "type": "github" }, "original": { "owner": "logos-blockchain", + "ref": "v0.5.5", "repo": "logos-blockchain-circuits", - "rev": "2846ee7a4cfa24458bb8063412ab2e753b344d2f", "type": "github" } }, @@ -98,17 +98,17 @@ ] }, "locked": { - "lastModified": 1780024773, - "narHash": "sha256-aU9nlrS9S+IJ2EiCzsaxzOXUhggogqTrJojBicE6Oeg=", + "lastModified": 1785302874, + "narHash": "sha256-fpKEww3TJoo1ANHO2q918ei+ayOrp0YEQAO1DuBLOB4=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "40b0a3a193e0840c76174b4a322874c8f6dd0a63", + "rev": "b99d48435bc3e34309d2c7ae6f7d45e77a156c38", "type": "github" }, "original": { "owner": "oxalica", "repo": "rust-overlay", - "rev": "40b0a3a193e0840c76174b4a322874c8f6dd0a63", + "rev": "b99d48435bc3e34309d2c7ae6f7d45e77a156c38", "type": "github" } }, diff --git a/flake.nix b/flake.nix index b786be37b..e0df3571d 100644 --- a/flake.nix +++ b/flake.nix @@ -12,13 +12,8 @@ crane.url = "github:ipetkov/crane"; # Must stay in sync with the lbc-* tags in Cargo.toml. - # Pinned to the commit right after the v0.5.3 tag (127626881faa975aa8e9868422cf6bbb08fcb512): - # the tag itself predates the CI job that registers its Nix hashes in - # circuits-nix-hashes.json, so building directly off the tag fails with - # `attribute '"0.5.3"' missing`. This commit adds that entry without - # otherwise changing the source. logos-blockchain-circuits = { - url = "github:logos-blockchain/logos-blockchain-circuits/2846ee7a4cfa24458bb8063412ab2e753b344d2f"; + url = "github:logos-blockchain/logos-blockchain-circuits/v0.5.5"; }; # Must stay in sync with the rust-rapidsnark rev in Cargo.toml. diff --git a/ledger/src/mantle/sdp/rewards/blend/current_epoch.rs b/ledger/src/mantle/sdp/rewards/blend/current_epoch.rs index 45a201ac6..503c9062b 100644 --- a/ledger/src/mantle/sdp/rewards/blend/current_epoch.rs +++ b/ledger/src/mantle/sdp/rewards/blend/current_epoch.rs @@ -5,7 +5,7 @@ use lb_blend_message::{ crypto::proofs::PoQVerificationInputsMinusSigningKey, encap::ProofsVerifier as ProofsVerifierTrait, reward::EpochRandomness, }; -use lb_blend_proofs::quota::inputs::prove::public::{CoreInputs, LeaderInputs}; +use lb_blend_proofs::quota::inputs::prove::public::{CoreInputs, LeaderInputs, PowInputs}; use lb_core::{ crypto::ZkHash, mantle::Value, @@ -218,6 +218,7 @@ impl CurrentEpochTracker { quota: core_quota, }, leader: leader_input, + pow: PowInputs::unwired_placeholder(), }) } } diff --git a/ledger/src/mantle/sdp/test_utils.rs b/ledger/src/mantle/sdp/test_utils.rs index f51149382..a5c21759b 100644 --- a/ledger/src/mantle/sdp/test_utils.rs +++ b/ledger/src/mantle/sdp/test_utils.rs @@ -9,7 +9,7 @@ use lb_blend_proofs::{ inputs::prove::{ PrivateInputs, PublicInputs, private::ProofOfCoreQuotaInputs, - public::{CoreInputs, LeaderInputs}, + public::{CoreInputs, LeaderInputs, PowInputs}, }, }, selection::VerifiedProofOfSelection, @@ -83,6 +83,7 @@ pub fn generate_activity_proof( signing_key: ephemeral.public_key().into_inner(), core: core_inputs, leader: leader_inputs, + pow: PowInputs::unwired_placeholder(), }; for message_release_index in 0u64.. { let private_inputs = PrivateInputs::new_proof_of_core_quota_inputs( diff --git a/services/blend/src/core/mod.rs b/services/blend/src/core/mod.rs index 80a3cc0dd..34d5310a4 100644 --- a/services/blend/src/core/mod.rs +++ b/services/blend/src/core/mod.rs @@ -30,7 +30,7 @@ use lb_blend::{ OldEpochBlendingTokenCollector, }, }, - proofs::quota::inputs::prove::public::{CoreInputs, LeaderInputs}, + proofs::quota::inputs::prove::public::{CoreInputs, LeaderInputs, PowInputs}, scheduling::{ EpochMessageScheduler, epoch::{EpochEvent, UninitializedEpochEventStream}, @@ -604,6 +604,7 @@ where PoQVerificationInputsMinusSigningKey { core: current_epoch_public_info.poq_core_public_inputs, leader: current_epoch_public_info.poq_leadership_public_inputs, + pow: PowInputs::unwired_placeholder(), }, current_epoch_core_poq_generator .expect("Core PoQ generator must be present at startup: the proxy service only launches CoreMode when the node is part of the core membership."), @@ -1050,6 +1051,7 @@ where PoQVerificationInputsMinusSigningKey { core: new_epoch_info.poq_core_public_inputs, leader: new_epoch_info.poq_leadership_public_inputs, + pow: PowInputs::unwired_placeholder(), }, core_poq_generator, new_epoch_info.epoch, diff --git a/services/blend/src/core/processor.rs b/services/blend/src/core/processor.rs index 0b3669d38..ddfc8f51a 100644 --- a/services/blend/src/core/processor.rs +++ b/services/blend/src/core/processor.rs @@ -271,7 +271,7 @@ mod tests { proofs::{ quota::{ VerifiedProofOfQuota, - inputs::prove::public::{CoreInputs, LeaderInputs}, + inputs::prove::public::{CoreInputs, LeaderInputs, PowInputs}, }, selection::{self, VerifiedProofOfSelection}, }, @@ -305,6 +305,7 @@ mod tests { lottery_0: Fr::ZERO, lottery_1: Fr::ZERO, }, + pow: PowInputs::unwired_placeholder(), } } diff --git a/services/blend/src/core/tests/utils.rs b/services/blend/src/core/tests/utils.rs index 41ce04816..ff5edf543 100644 --- a/services/blend/src/core/tests/utils.rs +++ b/services/blend/src/core/tests/utils.rs @@ -20,7 +20,7 @@ use lb_blend::{ ProofOfQuota, VerifiedProofOfQuota, inputs::prove::{ private::ProofOfLeadershipQuotaInputs, - public::{CoreInputs, LeaderInputs}, + public::{CoreInputs, LeaderInputs, PowInputs}, }, }, selection::{ProofOfSelection, VerifiedProofOfSelection, inputs::VerifyInputs}, @@ -330,6 +330,7 @@ pub fn new_crypto_processor( PoQVerificationInputsMinusSigningKey { core: epoch_info.poq_core_public_inputs, leader: epoch_info.poq_leadership_public_inputs, + pow: PowInputs::unwired_placeholder(), }, core_poq_generator, epoch_info.epoch, diff --git a/services/blend/src/edge/mod.rs b/services/blend/src/edge/mod.rs index a1e508512..8b108ec34 100644 --- a/services/blend/src/edge/mod.rs +++ b/services/blend/src/edge/mod.rs @@ -16,7 +16,7 @@ use backends::BlendBackend; use futures::{Stream, StreamExt as _}; use lb_blend::{ message::crypto::proofs::PoQVerificationInputsMinusSigningKey, - proofs::quota::inputs::prove::public::{CoreInputs, LeaderInputs}, + proofs::quota::inputs::prove::public::{CoreInputs, LeaderInputs, PowInputs}, scheduling::{ epoch::{EpochEvent, UninitializedEpochEventStream}, message_blend::provers::leader::LeaderProofsGenerator, @@ -425,6 +425,7 @@ where pol_ledger_aged: current_public_epoch_info.aged, message_quota: settings.epoch_leadership_quota(), }, + pow: PowInputs::unwired_placeholder(), }; debug!(target: LOG_TARGET, "Creating new handler for epoch {:?}", current_public_epoch_info.epoch); diff --git a/zk/proofs/poq/benches/common/mod.rs b/zk/proofs/poq/benches/common/mod.rs index 8a2c39cb1..50ccf7b9a 100644 --- a/zk/proofs/poq/benches/common/mod.rs +++ b/zk/proofs/poq/benches/common/mod.rs @@ -8,12 +8,12 @@ use std::str::FromStr as _; use lb_pol::LotteryConstants; use lb_utils::math::NonNegativeRatio; use logos_blockchain_poq::{ - PoQBlendInputsData, PoQChainInputsData, PoQCommonInputsData, PoQWalletInputsData, + PoQBlendInputsData, PoQChainInputsData, PoQCommonInputsData, PoQSelector, PoQWalletInputsData, PoQWitnessInputs, }; use num_bigint::BigUint; -/// Quota advertised by the fixtures, for both the core and the leader branch. +/// Quota advertised by the fixtures, for the core, the leader and pow branches. /// /// It has to stay above every key index the benchmarks prove for, since the /// circuit only accepts indices below the quota of the branch being proven. @@ -24,16 +24,22 @@ fn lottery() -> (lb_groth16::Fr, lb_groth16::Fr) { .compute_lottery_values(5000) } -fn common_data(selector: bool, key_index: u64) -> PoQCommonInputsData { +fn common_data(selector: PoQSelector, key_index: u64) -> PoQCommonInputsData { PoQCommonInputsData { core_quota: QUOTA, leader_quota: QUOTA, + pow_quota: QUOTA, message_key: ( BigUint::from(123_456u32).into(), BigUint::from(654_321u32).into(), ), selector, index: key_index, + pow_difficulty: BigUint::from_str( + "2334035772366381927456001473031809359552034265892309520954243351196711606017", + ) + .unwrap() + .into(), } } @@ -157,8 +163,12 @@ pub fn core_node_inputs(key_index: u64) -> PoQWitnessInputs { .map(|(v, s)| (BigUint::from_str(v).unwrap().into(), s)), }; - PoQWitnessInputs::from_core_node_data(chain_data, common_data(false, key_index), blend_data) - .unwrap() + PoQWitnessInputs::from_core_node_data( + chain_data, + common_data(PoQSelector::Core, key_index), + blend_data, + ) + .unwrap() } /// Witness inputs for a leadership Proof of Quota at the given key index. @@ -327,13 +337,17 @@ pub fn leader_inputs(key_index: u64) -> PoQWitnessInputs { ), ] .map(|(v, s)| (BigUint::from_str(v).unwrap().into(), s)), - secret_key: BigUint::from_str( + pol_secret_key: BigUint::from_str( "17558656186380761716563192806052137702710836103853413805442046164934618201080", ) .unwrap() .into(), }; - PoQWitnessInputs::from_leader_data(chain_data, common_data(true, key_index), wallet_data) - .unwrap() + PoQWitnessInputs::from_leader_data( + chain_data, + common_data(PoQSelector::Leader, key_index), + wallet_data, + ) + .unwrap() } diff --git a/zk/proofs/poq/src/chain_inputs.rs b/zk/proofs/poq/src/chain_inputs.rs index a33d4e61c..f6454e892 100644 --- a/zk/proofs/poq/src/chain_inputs.rs +++ b/zk/proofs/poq/src/chain_inputs.rs @@ -77,6 +77,8 @@ pub enum PoQInputsFromDataError { CoreQuotaMoreThan20Bits, #[error("Leader quota is greater than 20 bits")] LeaderQuotaMoreThan20Bits, + #[error("PoW quota is greater than 20 bits")] + PowQuotaMoreThan20Bits, } impl TryFrom for PoQChainInputs { diff --git a/zk/proofs/poq/src/common_inputs.rs b/zk/proofs/poq/src/common_inputs.rs index 66259dd8f..a77c5d6bf 100644 --- a/zk/proofs/poq/src/common_inputs.rs +++ b/zk/proofs/poq/src/common_inputs.rs @@ -1,41 +1,59 @@ -use lb_groth16::{AdditiveGroup as _, Field as _, Fr, Groth16Input, Groth16InputDeser}; +use lb_groth16::{Fr, Groth16Input, Groth16InputDeser}; use num_bigint::BigUint; use serde::{Deserialize, Serialize}; -use crate::chain_inputs::{ - PoQInputsFromDataError, - PoQInputsFromDataError::{CoreQuotaMoreThan20Bits, LeaderQuotaMoreThan20Bits}, +use crate::{ + PoQInputsFromDataError::PowQuotaMoreThan20Bits, + chain_inputs::{ + PoQInputsFromDataError, + PoQInputsFromDataError::{CoreQuotaMoreThan20Bits, LeaderQuotaMoreThan20Bits}, + }, }; #[derive(Copy, Clone)] pub struct PoQCommonInputs { pub core_quota: Groth16Input, pub leader_quota: Groth16Input, + pub pow_quota: Groth16Input, pub key_part_one: Groth16Input, pub key_part_two: Groth16Input, pub selector: Groth16Input, pub index: Groth16Input, + pub pow_difficulty: Groth16Input, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum PoQSelector { + Core = 0, + Leader = 1, + Pow = 2, } #[derive(Clone, Copy)] pub struct PoQCommonInputsData { pub core_quota: u64, pub leader_quota: u64, + pub pow_quota: u64, pub message_key: (Fr, Fr), - pub selector: bool, + pub selector: PoQSelector, pub index: u64, + pub pow_difficulty: Fr, } #[derive(Deserialize, Serialize)] pub struct PoQCommonInputsJson { core_quota: Groth16InputDeser, leader_quota: Groth16InputDeser, + pow_quota: Groth16InputDeser, #[serde(rename = "K_part_one")] key_part_one: Groth16InputDeser, #[serde(rename = "K_part_two")] key_part_two: Groth16InputDeser, selector: Groth16InputDeser, index: Groth16InputDeser, + #[serde(rename = "pow_blend_difficulty")] + pow_difficulty: Groth16InputDeser, } impl From<&PoQCommonInputs> for PoQCommonInputsJson { @@ -43,19 +61,23 @@ impl From<&PoQCommonInputs> for PoQCommonInputsJson { PoQCommonInputs { core_quota, leader_quota, + pow_quota, key_part_one, key_part_two, selector, index, + pow_difficulty, }: &PoQCommonInputs, ) -> Self { Self { core_quota: core_quota.into(), leader_quota: leader_quota.into(), + pow_quota: pow_quota.into(), key_part_one: key_part_one.into(), key_part_two: key_part_two.into(), selector: selector.into(), index: index.into(), + pow_difficulty: pow_difficulty.into(), } } } @@ -66,9 +88,11 @@ impl TryFrom for PoQCommonInputs { PoQCommonInputsData { core_quota, leader_quota, + pow_quota, message_key, selector, index, + pow_difficulty, }: PoQCommonInputsData, ) -> Result { let leader_quota_bits = leader_quota.checked_ilog2().map_or(0, |v| v + 1); @@ -79,13 +103,19 @@ impl TryFrom for PoQCommonInputs { if core_quota_bits > 20 { return Err(CoreQuotaMoreThan20Bits); } + let pow_quota_bits = pow_quota.checked_ilog2().map_or(0, |v| v + 1); + if pow_quota_bits > 20 { + return Err(PowQuotaMoreThan20Bits); + } Ok(Self { core_quota: Groth16Input::new(Fr::from(BigUint::from(core_quota))), leader_quota: Groth16Input::new(Fr::from(BigUint::from(leader_quota))), + pow_quota: Groth16Input::new(Fr::from(BigUint::from(pow_quota))), key_part_one: message_key.0.into(), key_part_two: message_key.1.into(), - selector: Groth16Input::new(if selector { Fr::ONE } else { Fr::ZERO }), + selector: Groth16Input::new(Fr::from(BigUint::from(selector as u8))), index: Groth16Input::new(Fr::from(BigUint::from(index))), + pow_difficulty: pow_difficulty.into(), }) } } diff --git a/zk/proofs/poq/src/inputs.rs b/zk/proofs/poq/src/inputs.rs index 843c6c5bc..0f943d613 100644 --- a/zk/proofs/poq/src/inputs.rs +++ b/zk/proofs/poq/src/inputs.rs @@ -6,6 +6,7 @@ use crate::{ blend_inputs::{PoQBlendInputs, PoQBlendInputsData, PoQBlendInputsJson}, chain_inputs::{PoQChainInputs, PoQChainInputsJson}, common_inputs::{PoQCommonInputs, PoQCommonInputsJson}, + pow_inputs::{PoQPowInputs, PoQPowInputsData, PoQPowInputsJson}, wallet_inputs::{PoQWalletInputs, PoQWalletInputsData, PoQWalletInputsJson}, }; @@ -16,6 +17,7 @@ pub struct PoQWitnessInputs { pub common: PoQCommonInputs, pub blend: PoQBlendInputs, pub wallet: PoQWalletInputs, + pub pow: PoQPowInputs, } impl PoQWitnessInputs { @@ -27,11 +29,9 @@ impl PoQWitnessInputs { Ok(Self { chain: chain.try_into()?, common: common.try_into()?, - blend: PoQBlendInputs::from(PoQBlendInputsData { - core_sk: Fr::ZERO, - core_path_and_selectors: [(Fr::ZERO, false); _], - }), + blend: Self::unused_blend_inputs(), wallet: wallet.into(), + pow: Self::unused_pow_inputs(), }) } @@ -44,14 +44,47 @@ impl PoQWitnessInputs { chain: chain.try_into()?, common: common.try_into()?, blend: blend.into(), - wallet: PoQWalletInputs::from(PoQWalletInputsData { - slot: 0, - note_value: 0, - transaction_hash: Fr::ZERO, - output_number: 0, - aged_path_and_selectors: [(Fr::ZERO, false); _], - secret_key: Fr::ZERO, - }), + wallet: Self::unused_wallet_inputs(), + pow: Self::unused_pow_inputs(), + }) + } + + pub fn from_pow_data( + chain: PoQChainInputsData, + common: PoQCommonInputsData, + pow: PoQPowInputsData, + ) -> Result>::Error> { + Ok(Self { + chain: chain.try_into()?, + common: common.try_into()?, + blend: Self::unused_blend_inputs(), + wallet: Self::unused_wallet_inputs(), + pow: pow.into(), + }) + } + + fn unused_blend_inputs() -> PoQBlendInputs { + PoQBlendInputs::from(PoQBlendInputsData { + core_sk: Fr::ZERO, + core_path_and_selectors: [(Fr::ZERO, false); _], + }) + } + + fn unused_wallet_inputs() -> PoQWalletInputs { + PoQWalletInputs::from(PoQWalletInputsData { + slot: 0, + note_value: 0, + transaction_hash: Fr::ZERO, + output_number: 0, + aged_path_and_selectors: [(Fr::ZERO, false); _], + pol_secret_key: Fr::ZERO, + }) + } + + fn unused_pow_inputs() -> PoQPowInputs { + PoQPowInputs::from(PoQPowInputsData { + pow_secret_key: Fr::ZERO, + block_hash: Fr::ZERO, }) } } @@ -77,6 +110,8 @@ pub struct PoQInputsJson { pub blend: PoQBlendInputsJson, #[serde(flatten)] pub wallet: PoQWalletInputsJson, + #[serde(flatten)] + pub pow: PoQPowInputsJson, } impl From for PoQInputsJson { @@ -86,12 +121,13 @@ impl From for PoQInputsJson { chain: (&inputs.chain).into(), common: (&inputs.common).into(), blend: inputs.blend.into(), + pow: inputs.pow.into(), } } } #[derive(Deserialize, Serialize)] -pub struct PoQVerifierInputJson([Groth16InputDeser; 10]); +pub struct PoQVerifierInputJson([Groth16InputDeser; 12]); #[derive(Clone)] pub struct PoQVerifierInput { @@ -99,8 +135,10 @@ pub struct PoQVerifierInput { pub core_quota: Groth16Input, pub leader_quota: Groth16Input, pub core_root: Groth16Input, + pub pow_quota: Groth16Input, pub k_part_one: Groth16Input, pub k_part_two: Groth16Input, + pub pow_blend_difficulty: Groth16Input, pub pol_epoch_nonce: Groth16Input, pub pol_t0: Groth16Input, pub pol_t1: Groth16Input, @@ -112,8 +150,10 @@ pub struct PoQVerifierInputData { pub core_quota: u64, pub leader_quota: u64, pub core_root: Fr, + pub pow_quota: u64, pub k_part_one: Fr, pub k_part_two: Fr, + pub pow_blend_difficulty: Fr, pub pol_epoch_nonce: Fr, pub lottery_0: Fr, pub lottery_1: Fr, @@ -129,9 +169,11 @@ impl TryFrom for PoQVerifierInput { core_quota, leader_quota, core_root, + pow_quota, pol_ledger_aged, k_part_one, k_part_two, + pow_blend_difficulty, pol_epoch_nonce, pol_t0, pol_t1, @@ -141,8 +183,10 @@ impl TryFrom for PoQVerifierInput { core_quota: core_quota.try_into()?, leader_quota: leader_quota.try_into()?, core_root: core_root.try_into()?, + pow_quota: pow_quota.try_into()?, k_part_one: k_part_one.try_into()?, k_part_two: k_part_two.try_into()?, + pow_blend_difficulty: pow_blend_difficulty.try_into()?, pol_epoch_nonce: pol_epoch_nonce.try_into()?, pol_t0: pol_t0.try_into()?, pol_t1: pol_t1.try_into()?, @@ -153,15 +197,17 @@ impl TryFrom for PoQVerifierInput { impl PoQVerifierInput { #[must_use] - pub const fn to_inputs(self) -> [Fr; 10] { + pub const fn to_inputs(self) -> [Fr; 12] { [ self.key_nullifier.into_inner(), self.core_quota.into_inner(), self.leader_quota.into_inner(), self.core_root.into_inner(), + self.pow_quota.into_inner(), self.pol_ledger_aged.into_inner(), self.k_part_one.into_inner(), self.k_part_two.into_inner(), + self.pow_blend_difficulty.into_inner(), self.pol_epoch_nonce.into_inner(), self.pol_t0.into_inner(), self.pol_t1.into_inner(), @@ -178,6 +224,8 @@ impl From for PoQVerifierInput { k_part_two: value.k_part_two.into(), key_nullifier: value.key_nullifier.into(), leader_quota: Groth16Input::new(value.leader_quota.into()), + pow_quota: Groth16Input::new(value.pow_quota.into()), + pow_blend_difficulty: value.pow_blend_difficulty.into(), pol_epoch_nonce: value.pol_epoch_nonce.into(), pol_ledger_aged: value.pol_ledger_aged.into(), pol_t0: Groth16Input::new(value.lottery_0), diff --git a/zk/proofs/poq/src/lib.rs b/zk/proofs/poq/src/lib.rs index 63cbc1767..d6f206667 100644 --- a/zk/proofs/poq/src/lib.rs +++ b/zk/proofs/poq/src/lib.rs @@ -2,6 +2,7 @@ mod blend_inputs; mod chain_inputs; mod common_inputs; mod inputs; +mod pow_inputs; mod verification_key; mod wallet_inputs; mod witness; @@ -12,7 +13,7 @@ pub use blend_inputs::{ CORE_MERKLE_TREE_HEIGHT, CorePathAndSelectors, PoQBlendInputs, PoQBlendInputsData, }; pub use chain_inputs::{PoQChainInputs, PoQChainInputsData, PoQInputsFromDataError}; -pub use common_inputs::{PoQCommonInputs, PoQCommonInputsData}; +pub use common_inputs::{PoQCommonInputs, PoQCommonInputsData, PoQSelector}; pub use inputs::{PoQVerifierInput, PoQVerifierInputData, PoQWitnessInputs}; use lb_circuits_prover::Prover as _; use lb_groth16::{ @@ -20,6 +21,7 @@ use lb_groth16::{ }; use lb_log_targets::proofs; pub use lb_pol::AGED_NOTE_MERKLE_TREE_HEIGHT; +pub use pow_inputs::{PoQPowInputs, PoQPowInputsData}; use tracing::error; pub use wallet_inputs::{AgedNotePathAndSelectors, PoQWalletInputs, PoQWalletInputsData}; @@ -141,125 +143,136 @@ mod tests { fn test_core_node_full_flow() { let blend_data = PoQBlendInputsData { core_sk: BigUint::from_str( - "8905547699320869461831852104817789469875723637735901345741281588946310324235", + "12667207762918681737213577433372645554941956657477853383508894204444641435708", ) .unwrap() .into(), core_path_and_selectors: [ ( - "16869616652852667189559016717243338331068120739649173884502336155264218010727", + "7495929195953210541208591724669352166258167386402673863836582332959049741998", + true, + ), + ( + "8777307683015870709408351814333112152804794621246499110711643849444602886454", false, ), ( - "13091718879067163858034522323644176312242009811093736972498389939059085177974", - true, - ), - ( - "5615497624396623086866765843762273938268745340944523291991473843820399587958", - true, - ), - ( - "12252618726807031528274527895794936410875679656522675436955173925070564886479", - true, - ), - ( - "15676171816835539786276219242768254181009067816239948700832551985003974594975", + "2036778247346771710692375429289939913839125337381038976370669720942761418159", false, ), ( - "19704272158800036501582000538191584664181486372889137807570676877252162790252", + "5150238042344629386129384874056943525919076545561275756461664527118718260316", false, ), ( - "17220466132075642970726646606687934619880159579542773841528618335448239077575", + "4936482837140898160990313117063047116523911033722639295600657041261330950251", + true, + ), + ( + "8937835534203757508211957478554582398600683099364069427807070320868647534222", false, ), ( - "19568444328548325046015987107341253380027495775774622463368229855377989226290", - true, - ), - ( - "21061581629256637244739982573784550742652688800467757972406229545047035347638", + "20952980496079374121937279088975994236259798048934859728621453910938508315429", false, ), ( - "21518036264660447465371694976644098419156368727076924435365409888210990153413", - true, - ), - ( - "18601741813400480889163627600644403937577783284065467384061180740490941766846", - true, - ), - ( - "3276819021875171213616879397115905648698624783190949445325727117619871062684", + "3127567055231452038159138967497074106547716639376277019791213152208715502108", false, ), ( - "16346413070923069365311460658994366491438423790174305844846934643885739701955", + "4156278452715681991545944691699148599903237834447534444828105997249209183668", + true, + ), + ( + "18179601246737692255359026481063507429449403349479564041630890140769203876921", + true, + ), + ( + "20437752904871274044354648385284243748143753378271484669957282746297202768728", false, ), ( - "18052187374311288196723181314946958960293262462466262108812751192264820802527", - true, - ), - ( - "18798652887540222143344482340074977888006884307412611551550673334473344453748", + "20964128655175295722279612439890819641847965870342369257222152533521036200042", false, ), ( - "6340439308577685187208962346420910386234054724707742363615331612477333766009", + "8529481264681700999783095712819666338972397694516091342589511693767349600946", true, ), ( - "6116045944554536720728524915454748535792176096846806949818056548601205224613", + "11390366370077994090069924023908925478862142650386311632070176411828525668517", true, ), ( - "7495766221993429009053819635630800227825524659472307268648873524676590491716", + "5917548759338686071972714539493044537194763096388397090814246211223906547270", + false, + ), + ( + "20167409213677086350276694709897905285253180362287004012973310966314090006733", true, ), ( - "4135588845309195152336413092515000771731525447810984287718633658115964724154", - true, + "2263236988262202014904251664760262847563737954962247776121488194923407644186", + false, ), ( - "16515196782727402790941217652547735699069943062019512321107139568403168848690", - true, + "19725021492526941122134445491655907123723766494432771594886452496452660956448", + false, + ), + ( + "5142311427791207713153244085802613121123367252788982353300919738927631606801", + false, + ), + ( + "9162839441909107733910143825064550229009975662836406280871655871464192283628", + false, ), ] .map(|(value, selector)| (BigUint::from_str(value).unwrap().into(), selector)), }; - let (lottery_0, lottery_1) = - LotteryConstants::new(NonNegativeRatio::new(1, 10.try_into().unwrap())) - .compute_lottery_values(5000); let chain_data = PoQChainInputsData { core_root: BigUint::from_str( - "12497635102173390657108276580981678137198622257613601634363274282193270703654", + "8648439177578503276872559887462337398415999326614652623958174178821393732006", ) .unwrap() .into(), pol_ledger_aged: BigUint::from_str( - "3285301153701124106247898475175231377413013906491501039690975241845353257824", + "17215820411670358459256686882934231030146782155124511326026608377144127196093", ) .unwrap() .into(), pol_epoch_nonce: BigUint::from_str( - "12441952563111122190593710132868692987264877387856913250622883462242318292882", + "16164570534004466859325445480705051126415612717114918921927075731584584730308", + ) + .unwrap() + .into(), + lottery_0: BigUint::from_str( + "148409079361904587837471709956458430342187235603420891378597419711486680", + ) + .unwrap() + .into(), + lottery_1: BigUint::from_str( + "21888242871336145414933615591437256729835795974444825699352339602896135814447", ) .unwrap() .into(), - lottery_0, - lottery_1, }; let common_data = PoQCommonInputsData { - core_quota: 15, - leader_quota: 10, + core_quota: 10, + leader_quota: 15, + pow_quota: 20, message_key: ( BigUint::from(123_456u32).into(), BigUint::from(654_321u32).into(), ), - selector: false, - index: 2, + selector: PoQSelector::Core, + index: 9, + pow_difficulty: BigUint::from_str( + "2334035772366381927456001473031809359552034265892309520954243351196711606017", + ) + .unwrap() + .into(), }; let witness_inputs = @@ -278,6 +291,8 @@ mod tests { k_part_two: common_data.message_key.1, key_nullifier, leader_quota: common_data.leader_quota, + pow_quota: common_data.pow_quota, + pow_blend_difficulty: common_data.pow_difficulty, pol_epoch_nonce: chain_data.pol_epoch_nonce, pol_ledger_aged: chain_data.pol_ledger_aged, lottery_0: chain_data.lottery_0, @@ -314,12 +329,18 @@ mod tests { let common_data = PoQCommonInputsData { core_quota: 15, leader_quota: 10, + pow_quota: 5, message_key: ( BigUint::from(123_456u32).into(), BigUint::from(654_321u32).into(), ), - selector: true, + selector: PoQSelector::Leader, index: 2, + pow_difficulty: BigUint::from_str( + "2334035772366381927456001473031809359552034265892309520954243351196711606017", + ) + .unwrap() + .into(), }; let wallet_data = PoQWalletInputsData { slot: 3_934_028_363, @@ -461,7 +482,7 @@ mod tests { ), ] .map(|(value, selector)| (BigUint::from_str(value).unwrap().into(), selector)), - secret_key: BigUint::from_str( + pol_secret_key: BigUint::from_str( "14029017592631272654768426994822986470491530234220356957377918534745805708829", ) .unwrap() @@ -484,6 +505,92 @@ mod tests { k_part_two: common_data.message_key.1, key_nullifier, leader_quota: common_data.leader_quota, + pow_quota: common_data.pow_quota, + pow_blend_difficulty: common_data.pow_difficulty, + pol_epoch_nonce: chain_data.pol_epoch_nonce, + pol_ledger_aged: chain_data.pol_ledger_aged, + lottery_0: chain_data.lottery_0, + lottery_1: chain_data.lottery_1, + }; + assert!(verify(&proof, recomputed_verify_inputs.into()).unwrap()); + } + + #[test] + fn test_pow_full_flow() { + let chain_data = PoQChainInputsData { + core_root: BigUint::from_str( + "7196120816867742764244305920813414287385070139349175831448761050095304066684", + ) + .unwrap() + .into(), + pol_ledger_aged: BigUint::from_str( + "12901234158445930888471913831760642108627105440164948073754288938400615804116", + ) + .unwrap() + .into(), + pol_epoch_nonce: BigUint::from_str( + "10612486568558363932403470969435464905237449260307875593547304337083779862075", + ) + .unwrap() + .into(), + lottery_0: BigUint::from_str( + "148409079361904587837471709956458430342187235603420891378597419711486680", + ) + .unwrap() + .into(), + lottery_1: BigUint::from_str( + "21888242871336145414933615591437256729835795974444825699352339602896135814447", + ) + .unwrap() + .into(), + }; + let common_data = PoQCommonInputsData { + core_quota: 10, + leader_quota: 15, + pow_quota: 20, + message_key: ( + BigUint::from(123_456u32).into(), + BigUint::from(654_321u32).into(), + ), + selector: PoQSelector::Pow, + index: 8, + pow_difficulty: BigUint::from_str( + "10631870504716456348838861774188160492563879712126054449569633827216160699117", + ) + .unwrap() + .into(), + }; + let pow_data = PoQPowInputsData { + pow_secret_key: BigUint::from_str( + "10044758699144566038746293679996441958807939592793641056821682251877616662024", + ) + .unwrap() + .into(), + block_hash: BigUint::from_str( + "17412116459874055221726429396167151400213699852365025340176158516975240665302", + ) + .unwrap() + .into(), + }; + + let witness_inputs = + PoQWitnessInputs::from_pow_data(chain_data, common_data, pow_data).unwrap(); + let (proof, inputs) = prove(witness_inputs).unwrap(); + let key_nullifier = inputs.key_nullifier.into_inner(); + // Test that verifying with the inputs returned by `prove` works. + assert!(verify(&proof, inputs).unwrap()); + + // Test that verifying with the reconstructed inputs inside the verifier context + // works. + let recomputed_verify_inputs = PoQVerifierInputData { + core_quota: common_data.core_quota, + core_root: chain_data.core_root, + k_part_one: common_data.message_key.0, + k_part_two: common_data.message_key.1, + key_nullifier, + leader_quota: common_data.leader_quota, + pow_quota: common_data.pow_quota, + pow_blend_difficulty: common_data.pow_difficulty, pol_epoch_nonce: chain_data.pol_epoch_nonce, pol_ledger_aged: chain_data.pol_ledger_aged, lottery_0: chain_data.lottery_0, diff --git a/zk/proofs/poq/src/pow_inputs.rs b/zk/proofs/poq/src/pow_inputs.rs new file mode 100644 index 000000000..e2c9748f1 --- /dev/null +++ b/zk/proofs/poq/src/pow_inputs.rs @@ -0,0 +1,49 @@ +use lb_groth16::{Fr, Groth16Input, Groth16InputDeser}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone)] +pub struct PoQPowInputs { + pub pow_secret_key: Groth16Input, + pub block_hash: Groth16Input, +} + +pub struct PoQPowInputsData { + pub pow_secret_key: Fr, + pub block_hash: Fr, +} + +#[derive(Deserialize, Serialize)] +pub struct PoQPowInputsJson { + #[serde(rename = "pow_sk")] + pow_secret_key: Groth16InputDeser, + #[serde(rename = "pow_block_hash")] + block_hash: Groth16InputDeser, +} + +impl From for PoQPowInputsJson { + fn from( + PoQPowInputs { + pow_secret_key, + block_hash, + }: PoQPowInputs, + ) -> Self { + Self { + pow_secret_key: (&pow_secret_key).into(), + block_hash: (&block_hash).into(), + } + } +} + +impl From for PoQPowInputs { + fn from( + PoQPowInputsData { + pow_secret_key, + block_hash, + }: PoQPowInputsData, + ) -> Self { + Self { + pow_secret_key: pow_secret_key.into(), + block_hash: block_hash.into(), + } + } +} diff --git a/zk/proofs/poq/src/wallet_inputs.rs b/zk/proofs/poq/src/wallet_inputs.rs index 059217674..49a892d3b 100644 --- a/zk/proofs/poq/src/wallet_inputs.rs +++ b/zk/proofs/poq/src/wallet_inputs.rs @@ -21,7 +21,7 @@ pub struct PoQWalletInputsData { pub transaction_hash: Fr, pub output_number: u64, pub aged_path_and_selectors: AgedNotePathAndSelectors, - pub secret_key: Fr, + pub pol_secret_key: Fr, } #[derive(Serialize)] @@ -39,7 +39,7 @@ pub struct PoQWalletInputsJson { #[serde(rename = "pol_noteid_path_selectors")] aged_selector: [Groth16InputDeser; AGED_NOTE_MERKLE_TREE_HEIGHT], #[serde(rename = "pol_secret_key")] - secret_key: Groth16InputDeser, + pol_secret_key: Groth16InputDeser, } impl From for PoQWalletInputsJson { fn from( @@ -66,7 +66,7 @@ impl From for PoQWalletInputsJson { output_number: (&pol_note_output_number).into(), aged_path, aged_selector, - secret_key: (&pol_secret_key).into(), + pol_secret_key: (&pol_secret_key).into(), } } } @@ -79,12 +79,12 @@ impl From for PoQWalletInputs { transaction_hash, output_number, aged_path_and_selectors, - secret_key, + pol_secret_key, }: PoQWalletInputsData, ) -> Self { Self { pol_slot: Groth16Input::new(Fr::from(BigUint::from(slot))), - pol_secret_key: Groth16Input::new(Fr::from(BigUint::from(secret_key))), + pol_secret_key: pol_secret_key.into(), pol_noteid_path_and_selectors: aged_path_and_selectors.map(|(hash, selector)| { ( hash.into(),