Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSZ Multiproof #57

Open
wants to merge 37 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
700583e
multiproof helpers
ec2 Jan 18, 2024
5ca9cdd
leaves for block header
ec2 Jan 18, 2024
282dd5e
in circuit multiproofs and step circuit integration
ec2 Jan 18, 2024
f195e04
some comments
ec2 Jan 18, 2024
67a9446
gen test data using rust
ec2 Jan 23, 2024
9ff59e1
add new test data
ec2 Jan 23, 2024
b91c039
clippy
ec2 Jan 23, 2024
1a00a19
more lints
ec2 Jan 23, 2024
7a136e6
remove typescript
ec2 Jan 23, 2024
35ec5c7
Feature gate test gen to pull in less deps
ec2 Jan 23, 2024
a44fa8d
Merge branch 'ec2/unit-test-gen' into ec2/ssz-multiproof
ec2 Jan 23, 2024
8784d0d
Merge branch 'main' into ec2/ssz-multiproof
ec2 Jan 23, 2024
6582bf9
fix unit tests for step circuit
ec2 Jan 23, 2024
ad80976
fix unit tests for committee update circuits
ec2 Jan 23, 2024
62c96be
Fix spec tests
ec2 Jan 24, 2024
27bdb04
some cleanup
ec2 Jan 24, 2024
4ef20b3
clean up
ec2 Jan 24, 2024
dcc5f2c
lint
ec2 Jan 24, 2024
17d03ff
remove preprocessor mock circuit tests in favor of testing both at th…
ec2 Jan 24, 2024
1183658
fix default trait impl
ec2 Jan 24, 2024
b227de1
Fix cargo.toml for feature flags
ec2 Jan 25, 2024
e36da2e
fix pr comments
ec2 Feb 2, 2024
476a9b7
change config in test
ec2 Feb 2, 2024
f67efe8
fix the rest of pr review
ec2 Feb 2, 2024
335a38c
Merge branch 'main' into ec2/ssz-multiproof
ec2 Feb 20, 2024
44104c0
Merge branch 'develop' into ec2/ssz-multiproof
ec2 Mar 20, 2024
e19071e
finish merge conflicts
ec2 Mar 20, 2024
b4eb920
uipdate submodule
ec2 Mar 20, 2024
2a655ac
Merge branch 'develop' into ec2/ssz-multiproof
ec2 Mar 20, 2024
faae48e
Merge branch 'develop' into ec2/ssz-multiproof
ec2 Apr 5, 2024
b9a5fd6
compiles
ec2 Apr 5, 2024
2765192
fix test to compile
ec2 Apr 5, 2024
e21399d
fmt
ec2 Apr 5, 2024
26e925d
dont pin
ec2 Apr 5, 2024
8d40442
revert some tests
ec2 Apr 5, 2024
9e95e4e
update test params
ec2 Apr 5, 2024
2515ad9
need to finish fixing unit_test_gen
ec2 Apr 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion eth-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ halo2curves.workspace = true
ethereum-types.workspace = true
regex = "1.5.4"
lazy_static = "1.4"
subtle = "2.4"
subtle = "2.5"
num = "0.4"
num-bigint.workspace = true
strum_macros = "0.24"
strum = "0.24"
4 changes: 4 additions & 0 deletions eth-types/src/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ pub trait Spec: 'static + Sized + Copy + Default + Debug {
const EXECUTION_STATE_ROOT_DEPTH: usize;
const FINALIZED_HEADER_INDEX: usize;
const FINALIZED_HEADER_DEPTH: usize;

const HEADER_SLOT_INDEX: usize = 8;
const HEADER_STATE_ROOT_INDEX: usize = 11;
const HEADER_BODY_ROOT_INDEX: usize = 12;
}

#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
Expand Down
26 changes: 17 additions & 9 deletions lightclient-circuits/src/committee_update_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use crate::{
gadget::crypto::{HashInstructions, Sha256ChipWide, ShaBitGateManager, ShaCircuitBuilder},
poseidon::{g1_array_poseidon, poseidon_committee_commitment_from_compressed},
ssz_merkle::{ssz_merkleize_chunks, verify_merkle_proof},
ssz_merkle::{ssz_merkleize_chunks, verify_merkle_multiproof, verify_merkle_proof},
util::{bytes_be_to_u128, AppCircuit, CommonGateManager, Eth2ConfigPinning, IntoWitness},
witness::{self, HashInput, HashInputChunk},
Eth2CircuitBuilder,
Expand Down Expand Up @@ -86,16 +86,24 @@ impl<S: Spec, F: Field> CommitteeUpdateCircuit<S, F> {
.iter()
.map(|v| builder.main().load_witness(F::from(*v as u64)))
.collect_vec();
let finalized_header_root = ssz_merkleize_chunks(
let finalized_header_root = args
.finalized_header
.tree_hash_root()
.0
.iter()
.map(|v| builder.main().load_witness(F::from(*v as u64)))
.collect_vec();

verify_merkle_multiproof(
builder,
&sha256_chip,
[
args.finalized_header.slot.as_u64().into_witness(),
args.finalized_header.proposer_index.into_witness(),
args.finalized_header.parent_root.as_ref().into_witness(),
finalized_state_root.clone().into(),
args.finalized_header.body_root.as_ref().into_witness(),
],
args.finalized_header_multiproof
.iter()
.map(|w| w.clone().into_witness()),
[finalized_state_root.clone().into()],
&finalized_header_root,
[S::HEADER_STATE_ROOT_INDEX],
args.finalized_header_helper_indices.clone(),
)?;

// Verify that the sync committee root is in the finalized state root
Expand Down
56 changes: 56 additions & 0 deletions lightclient-circuits/src/ssz_merkle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Code: https://github.com/ChainSafe/Spectre
// SPDX-License-Identifier: LGPL-3.0-only

use std::collections::HashMap;

use crate::{
gadget::crypto::HashInstructions,
util::IntoConstant,
Expand Down Expand Up @@ -111,6 +113,60 @@ pub fn verify_merkle_proof<F: Field, CircuitBuilder: CommonCircuitBuilder<F>>(
Ok(())
}

// Implements following https://github.com/ethereum/consensus-specs/blob/dev/ssz/merkle-proofs.md#merkle-multiproofs
pub fn verify_merkle_multiproof<F: Field, CircuitBuilder: CommonCircuitBuilder<F>>(
builder: &mut CircuitBuilder,
hasher: &impl HashInstructions<F, CircuitBuilder = CircuitBuilder>,
branch: impl IntoIterator<Item = HashInputChunk<QuantumCell<F>>>,
leaves: impl IntoIterator<Item = HashInputChunk<QuantumCell<F>>>,
root: &[AssignedValue<F>],
gindices: impl IntoIterator<Item = usize>,
helper_indices: impl IntoIterator<Item = usize>,
) -> Result<(), Error> {
let mut objects: HashMap<usize, _> = gindices
.into_iter()
.zip(leaves)
.chain(helper_indices.into_iter().zip(branch))
.collect();
let mut keys = objects.keys().copied().collect_vec();
keys.sort_by(|a, b| b.cmp(a));

let mut pos = 0;
while pos < keys.len() {
let k = keys[pos];
// if the sibling exists AND the parent does NOT, we hash
if objects.contains_key(&k)
&& objects.contains_key(&(k ^ 1))
&& !objects.contains_key(&(k / 2))
{
let left = objects[&((k | 1) ^ 1)].clone();
let right = objects[&(k | 1)].clone();
let computed_hash = hasher
.digest(builder, HashInput::TwoToOne(left, right))?
.into();
objects.insert(k / 2, computed_hash);
keys.push(k / 2);
}
pos += 1;
}

let computed_root = objects
.get(&1)
.unwrap()
.clone()
.into_iter()
.map(|b| match b {
QuantumCell::Existing(av) => av,
_ => unreachable!(),
});

computed_root.zip(root.iter()).for_each(|(a, b)| {
builder.main().constrain_equal(&a, b);
});

Ok(())
}

lazy_static! {
// Calculates padding Merkle notes for the 2 first levels of the Merkle tree.
// Used to pad the input to a power of two. Only 2 levels are precomputed because the number of not even inputs is limited.
Expand Down
43 changes: 32 additions & 11 deletions lightclient-circuits/src/sync_step_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
to_bytes_le,
},
poseidon::{g1_array_poseidon, poseidon_committee_commitment_from_uncompressed},
ssz_merkle::{ssz_merkleize_chunks, verify_merkle_proof},
ssz_merkle::{verify_merkle_multiproof, verify_merkle_proof},
util::{AppCircuit, Eth2ConfigPinning, IntoWitness},
witness::{self, HashInput, HashInputChunk, SyncStepArgs},
Eth2CircuitBuilder,
Expand Down Expand Up @@ -109,7 +109,6 @@ impl<S: Spec, F: Field> StepCircuit<S, F> {
y_signs_packed,
)?;

// Compute attested header root
let attested_slot_bytes: HashInputChunk<_> =
args.attested_header.slot.as_u64().into_witness();
let attested_header_state_root = args
Expand All @@ -119,19 +118,29 @@ impl<S: Spec, F: Field> StepCircuit<S, F> {
.iter()
.map(|v| builder.main().load_witness(F::from(*v as u64)))
.collect_vec();
let attested_header_root = ssz_merkleize_chunks(
let attested_header_root = args
.attested_header
.tree_hash_root()
.0
.iter()
.map(|v| builder.main().load_witness(F::from(*v as u64)))
.collect_vec();

verify_merkle_multiproof(
builder,
&sha256_chip,
args.attested_header_multiproof
.iter()
.map(|w| w.clone().into_witness()),
[
attested_slot_bytes.clone(),
args.attested_header.proposer_index.into_witness(),
args.attested_header.parent_root.as_ref().into_witness(),
attested_header_state_root.clone().into(),
args.attested_header.body_root.as_ref().into_witness(),
],
&attested_header_root,
[S::HEADER_SLOT_INDEX, S::HEADER_STATE_ROOT_INDEX],
args.attested_header_helper_indices.clone(),
)?;

// Compute finalized header root
let finalized_block_body_root = args
.finalized_header
.body_root
Expand All @@ -141,16 +150,28 @@ impl<S: Spec, F: Field> StepCircuit<S, F> {
.collect_vec();
let finalized_slot_bytes: HashInputChunk<_> =
args.finalized_header.slot.as_u64().into_witness();
let finalized_header_root = ssz_merkleize_chunks(

let finalized_header_root = args
.finalized_header
.tree_hash_root()
.0
.iter()
.map(|v| builder.main().load_witness(F::from(*v as u64)))
.collect_vec();

verify_merkle_multiproof(
builder,
&sha256_chip,
args.finalized_header_multiproof
.iter()
.map(|w| w.clone().into_witness()),
[
finalized_slot_bytes.clone(),
args.finalized_header.proposer_index.into_witness(),
args.finalized_header.parent_root.as_ref().into_witness(),
args.finalized_header.state_root.as_ref().into_witness(),
finalized_block_body_root.clone().into(),
],
&finalized_header_root,
[S::HEADER_SLOT_INDEX, S::HEADER_BODY_ROOT_INDEX],
args.finalized_header_helper_indices.clone(),
)?;

let signing_root = sha256_chip.digest(
Expand Down
31 changes: 30 additions & 1 deletion lightclient-circuits/src/witness/multiproof.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// TODO: A lot if not all/most of this code is copy pasta from: https://github.com/ralexstokes/ssz-rs/pull/118 which is mostly implemented w.r.t. the spec
// TODO: Remove this once the above PR lands in ssz-rs

use ethereum_types::Hash256;
use ethereum_types::{BeaconBlockHeader, Hash256};
use sha2::{Digest, Sha256};
use std::collections::{HashMap, HashSet};
use tree_hash::TreeHash;

pub type GeneralizedIndex = usize;

Expand Down Expand Up @@ -187,3 +188,31 @@ pub fn create_multiproof(
.map(|i| merkle_tree[i])
.collect()
}

/// Returns nodes representing the leaves a BeaconBlockHeader in merkleized representation.
pub fn block_header_to_leaves(header: &BeaconBlockHeader) -> [Hash256; 5] {
[
header.slot.tree_hash_root(),
header.proposer_index.tree_hash_root(),
header.parent_root.tree_hash_root(),
header.state_root.tree_hash_root(),
header.body_root.tree_hash_root(),
]
}

pub fn beacon_header_multiproof_and_helper_indices(
header: &BeaconBlockHeader,
gindices: &[usize],
) -> (Vec<Vec<u8>>, Vec<usize>) {
let header_leaves = block_header_to_leaves(header);
let merkle_tree = merkle_tree(&header_leaves);
let helper_indices = get_helper_indices(gindices);
let proof = helper_indices
.iter()
.copied()
.map(|i| merkle_tree[i])
.map(|n| n.as_ref().to_vec())
.collect::<Vec<_>>();
assert_eq!(proof.len(), helper_indices.len());
(proof, helper_indices)
}
11 changes: 11 additions & 0 deletions lightclient-circuits/src/witness/rotation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::{iter, marker::PhantomData};

use crate::witness::beacon_header_multiproof_and_helper_indices;

/// Input datum for the `CommitteeUpdateCircuit` to map next sync committee SSZ root in the finalized state root to the corresponding Poseidon commitment to the public keys.
///
/// Assumes that public keys are BLS12-381 points on G1; `sync_committee_branch` is exactly `S::SYNC_COMMITTEE_PUBKEYS_DEPTH` hashes in lenght.
Expand All @@ -20,6 +22,8 @@ pub struct CommitteeUpdateArgs<S: Spec> {

pub sync_committee_branch: Vec<Vec<u8>>,

pub finalized_header_multiproof: Vec<Vec<u8>>,
pub finalized_header_helper_indices: Vec<usize>,
#[serde(skip)]
pub _spec: PhantomData<S>,
}
Expand Down Expand Up @@ -63,13 +67,20 @@ impl<S: Spec> Default for CommitteeUpdateArgs<S> {
let mut finalized_header = BeaconBlockHeader::empty();
finalized_header.state_root = state_root.into();

let (finalized_header_multiproof, finalized_header_helper_indices) =
beacon_header_multiproof_and_helper_indices(
&mut finalized_header.clone(),
&[S::HEADER_STATE_ROOT_INDEX],
);
Self {
pubkeys_compressed: iter::repeat(dummy_x_bytes)
.take(S::SYNC_COMMITTEE_SIZE)
.collect_vec(),
sync_committee_branch,
finalized_header,
_spec: PhantomData,
finalized_header_multiproof,
finalized_header_helper_indices,
}
}
}
Expand Down
25 changes: 25 additions & 0 deletions lightclient-circuits/src/witness/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use tree_hash::TreeHash;

use crate::witness::beacon_header_multiproof_and_helper_indices;

use super::mock_root;

/// Input datum for the `StepCircuit` to verify `attested_header` singed by the lightclient sync committee,
Expand Down Expand Up @@ -42,6 +44,11 @@ pub struct SyncStepArgs<S: Spec> {

pub domain: [u8; 32],

pub attested_header_multiproof: Vec<Vec<u8>>,
pub attested_header_helper_indices: Vec<usize>,
pub finalized_header_multiproof: Vec<Vec<u8>>,
pub finalized_header_helper_indices: Vec<usize>,

#[serde(skip)]
pub _spec: PhantomData<S>,
}
Expand Down Expand Up @@ -109,6 +116,19 @@ impl<S: Spec> Default for SyncStepArgs<S> {
})
.collect_vec();

// Proof length is 3
let (attested_header_multiproof, attested_header_helper_indices) =
beacon_header_multiproof_and_helper_indices(
&mut attested_header.clone(),
&[S::HEADER_SLOT_INDEX, S::HEADER_STATE_ROOT_INDEX],
);
// Proof length is 4
let (finalized_header_multiproof, finalized_header_helper_indices) =
beacon_header_multiproof_and_helper_indices(
&mut finalized_header.clone(),
&[S::HEADER_SLOT_INDEX, S::HEADER_BODY_ROOT_INDEX],
);

Self {
signature_compressed,
pubkeys_uncompressed,
Expand All @@ -120,6 +140,11 @@ impl<S: Spec> Default for SyncStepArgs<S> {
execution_payload_branch: execution_branch,
execution_payload_root: execution_root,
_spec: PhantomData,

attested_header_multiproof,
attested_header_helper_indices,
finalized_header_multiproof,
finalized_header_helper_indices,
}
}
}
Expand Down
9 changes: 3 additions & 6 deletions preprocessor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ mod tests {
.message;
let slot = block.slot;
let period = slot / (32 * 256);
const ROTATE_CONFIG_PATH: &str = "../lightclient-circuits/config/committee_update_20.json";
const STEP_CONFIG_PATH: &str = "../lightclient-circuits/config/sync_step_20.json";

println!(
"Fetching light client update at current Slot: {} at Period: {}",
Expand Down Expand Up @@ -198,11 +200,8 @@ mod tests {
finalized_sync_committee_branch.insert(0, c.sync_committee_branch[0].clone());
finalized_sync_committee_branch[1].clone_from(&c.sync_committee_branch[1]);
c.sync_committee_branch = finalized_sync_committee_branch;
// Replaces the attested header with step circuits finalized header
c.finalized_header = s.finalized_header.clone();

let params: ParamsKZG<Bn256> = gen_srs(K);

let circuit = StepCircuit::<Testnet, Fr>::create_circuit(
CircuitBuilderStage::Mock,
None,
Expand All @@ -214,9 +213,7 @@ mod tests {
let prover = MockProver::<Fr>::run(K, &circuit, circuit.instances()).unwrap();
prover.assert_satisfied();

const CONFIG_PATH: &str = "../lightclient-circuits/config/committee_update_testnet.json";

let pinning = Eth2ConfigPinning::from_path(CONFIG_PATH);
let pinning = Eth2ConfigPinning::from_path(ROTATE_CONFIG_PATH);
let circuit = CommitteeUpdateCircuit::<Testnet, Fr>::create_circuit(
CircuitBuilderStage::Mock,
Some(pinning),
Expand Down
Loading