Skip to content
This repository has been archived by the owner on Feb 21, 2024. It is now read-only.

Commit

Permalink
Add Checkpoint heights (0xPolygonZero#1418)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nashtare authored Dec 9, 2023
1 parent 5607faf commit bfcfcdb
Show file tree
Hide file tree
Showing 14 changed files with 233 additions and 85 deletions.
143 changes: 107 additions & 36 deletions evm/src/fixed_recursive_verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use crate::generation::GenerationInputs;
use crate::get_challenges::observe_public_values_target;
use crate::proof::{
BlockHashesTarget, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, PublicValues,
PublicValuesTarget, StarkProofWithMetadata, TrieRootsTarget,
PublicValuesTarget, StarkProofWithMetadata, TrieRoots, TrieRootsTarget,
};
use crate::prover::prove;
use crate::recursive_verifier::{
Expand Down Expand Up @@ -715,18 +715,18 @@ where
lhs: &ExtraBlockDataTarget,
rhs: &ExtraBlockDataTarget,
) {
// Connect genesis state root values.
// Connect checkpoint state root values.
for (&limb0, &limb1) in pvs
.genesis_state_trie_root
.checkpoint_state_trie_root
.iter()
.zip(&rhs.genesis_state_trie_root)
.zip(&rhs.checkpoint_state_trie_root)
{
builder.connect(limb0, limb1);
}
for (&limb0, &limb1) in pvs
.genesis_state_trie_root
.checkpoint_state_trie_root
.iter()
.zip(&lhs.genesis_state_trie_root)
.zip(&lhs.checkpoint_state_trie_root)
{
builder.connect(limb0, limb1);
}
Expand Down Expand Up @@ -790,6 +790,34 @@ where
let parent_pv = PublicValuesTarget::from_public_inputs(&parent_block_proof.public_inputs);
let agg_pv = PublicValuesTarget::from_public_inputs(&agg_root_proof.public_inputs);

// Connect block `trie_roots_before` with parent_pv `trie_roots_before`.
TrieRootsTarget::connect(
&mut builder,
public_values.trie_roots_before,
parent_pv.trie_roots_before,
);
// Connect the rest of block `public_values` with agg_pv.
TrieRootsTarget::connect(
&mut builder,
public_values.trie_roots_after,
agg_pv.trie_roots_after,
);
BlockMetadataTarget::connect(
&mut builder,
public_values.block_metadata,
agg_pv.block_metadata,
);
BlockHashesTarget::connect(
&mut builder,
public_values.block_hashes,
agg_pv.block_hashes,
);
ExtraBlockDataTarget::connect(
&mut builder,
public_values.extra_block_data,
agg_pv.extra_block_data,
);

// Make connections between block proofs, and check initial and final block values.
Self::connect_block_proof(&mut builder, has_parent_block, &parent_pv, &agg_pv);

Expand Down Expand Up @@ -855,12 +883,12 @@ where
builder.connect(limb0, limb1);
}

// Between blocks, the genesis state trie remains unchanged.
// Between blocks, the checkpoint state trie remains unchanged.
for (&limb0, limb1) in lhs
.extra_block_data
.genesis_state_trie_root
.checkpoint_state_trie_root
.iter()
.zip(rhs.extra_block_data.genesis_state_trie_root)
.zip(rhs.extra_block_data.checkpoint_state_trie_root)
{
builder.connect(limb0, limb1);
}
Expand All @@ -878,15 +906,11 @@ where

let has_not_parent_block = builder.sub(one, has_parent_block.target);

// Check that the genesis block number is 0.
let gen_block_constr = builder.mul(has_not_parent_block, lhs.block_metadata.block_number);
builder.assert_zero(gen_block_constr);

// Check that the genesis block has the predetermined state trie root in `ExtraBlockData`.
Self::connect_genesis_block(builder, rhs, has_not_parent_block);
// Check that the checkpoint block has the predetermined state trie root in `ExtraBlockData`.
Self::connect_checkpoint_block(builder, rhs, has_not_parent_block);
}

fn connect_genesis_block(
fn connect_checkpoint_block(
builder: &mut CircuitBuilder<F, D>,
x: &PublicValuesTarget,
has_not_parent_block: Target,
Expand All @@ -897,7 +921,7 @@ where
.trie_roots_before
.state_root
.iter()
.zip(x.extra_block_data.genesis_state_trie_root)
.zip(x.extra_block_data.checkpoint_state_trie_root)
{
let mut constr = builder.sub(limb0, limb1);
constr = builder.mul(has_not_parent_block, constr);
Expand Down Expand Up @@ -1026,7 +1050,9 @@ where
trie_roots_before: lhs_public_values.trie_roots_before,
trie_roots_after: rhs_public_values.trie_roots_after,
extra_block_data: ExtraBlockData {
genesis_state_trie_root: lhs_public_values.extra_block_data.genesis_state_trie_root,
checkpoint_state_trie_root: lhs_public_values
.extra_block_data
.checkpoint_state_trie_root,
txn_number_before: lhs_public_values.extra_block_data.txn_number_before,
txn_number_after: rhs_public_values.extra_block_data.txn_number_after,
gas_used_before: lhs_public_values.extra_block_data.gas_used_before,
Expand Down Expand Up @@ -1077,42 +1103,66 @@ where
block_inputs
.set_proof_with_pis_target(&self.block.parent_block_proof, parent_block_proof);
} else {
// Initialize genesis_state_trie, state_root_after, and the previous block hashes for correct connection between blocks.
// Block number does not need to be initialized as genesis block is constrained to have number 0.

if public_values.trie_roots_before.state_root
!= public_values.extra_block_data.genesis_state_trie_root
!= public_values.extra_block_data.checkpoint_state_trie_root
{
return Err(anyhow::Error::msg(format!(
"Inconsistent pre-state for first block {:?} with genesis state {:?}.",
"Inconsistent pre-state for first block {:?} with checkpoint state {:?}.",
public_values.trie_roots_before.state_root,
public_values.extra_block_data.genesis_state_trie_root,
public_values.extra_block_data.checkpoint_state_trie_root,
)));
}
// Initialize `state_root_after`.

// Initialize some public inputs for correct connection between the checkpoint block and the current one.
let mut nonzero_pis = HashMap::new();

// Initialize the checkpoint block roots before, and state root after.
let state_trie_root_before_keys = 0..TrieRootsTarget::HASH_SIZE;
for (key, &value) in state_trie_root_before_keys
.zip_eq(&h256_limbs::<F>(public_values.trie_roots_before.state_root))
{
nonzero_pis.insert(key, value);
}
let txn_trie_root_before_keys =
TrieRootsTarget::HASH_SIZE..TrieRootsTarget::HASH_SIZE * 2;
for (key, &value) in txn_trie_root_before_keys.clone().zip_eq(&h256_limbs::<F>(
public_values.trie_roots_before.transactions_root,
)) {
nonzero_pis.insert(key, value);
}
let receipts_trie_root_before_keys =
TrieRootsTarget::HASH_SIZE * 2..TrieRootsTarget::HASH_SIZE * 3;
for (key, &value) in receipts_trie_root_before_keys
.clone()
.zip_eq(&h256_limbs::<F>(
public_values.trie_roots_before.receipts_root,
))
{
nonzero_pis.insert(key, value);
}
let state_trie_root_after_keys =
TrieRootsTarget::SIZE..TrieRootsTarget::SIZE + TrieRootsTarget::HASH_SIZE;
let mut nonzero_pis = HashMap::new();
for (key, &value) in state_trie_root_after_keys
.zip_eq(&h256_limbs::<F>(public_values.trie_roots_before.state_root))
{
nonzero_pis.insert(key, value);
}

// Initialize the genesis state trie digest.
let genesis_state_trie_keys =
// Initialize the checkpoint state root extra data.
let checkpoint_state_trie_keys =
TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE
..TrieRootsTarget::SIZE * 2
+ BlockMetadataTarget::SIZE
+ BlockHashesTarget::SIZE
+ 8;
for (key, &value) in genesis_state_trie_keys.zip_eq(&h256_limbs::<F>(
public_values.extra_block_data.genesis_state_trie_root,
for (key, &value) in checkpoint_state_trie_keys.zip_eq(&h256_limbs::<F>(
public_values.extra_block_data.checkpoint_state_trie_root,
)) {
nonzero_pis.insert(key, value);
}

// Initialize block hashes.
// Initialize checkpoint block hashes.
// These will be all zeros the initial genesis checkpoint.
let block_hashes_keys = TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE
..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE
- 8;
Expand All @@ -1130,6 +1180,14 @@ where
nonzero_pis.insert(block_hashes_current_start + i, cur_targets[i]);
}

// Initialize the checkpoint block number.
// Subtraction would result in an invalid proof for genesis, but we shouldn't try proving this block anyway.
let block_number_key = TrieRootsTarget::SIZE * 2 + 6;
nonzero_pis.insert(
block_number_key,
F::from_canonical_u64(public_values.block_metadata.block_number.low_u64() - 1),
);

block_inputs.set_proof_with_pis_target(
&self.block.parent_block_proof,
&cyclic_base_proof(
Expand All @@ -1145,13 +1203,26 @@ where
block_inputs
.set_verifier_data_target(&self.block.cyclic_vk, &self.block.circuit.verifier_only);

set_public_value_targets(&mut block_inputs, &self.block.public_values, &public_values)
.map_err(|_| {
anyhow::Error::msg("Invalid conversion when setting public values targets.")
})?;
// This is basically identical to this block public values, apart from the `trie_roots_before`
// that may come from the previous proof, if any.
let block_public_values = PublicValues {
trie_roots_before: opt_parent_block_proof
.map(|p| TrieRoots::from_public_inputs(&p.public_inputs[0..TrieRootsTarget::SIZE]))
.unwrap_or(public_values.trie_roots_before),
..public_values
};

set_public_value_targets(
&mut block_inputs,
&self.block.public_values,
&block_public_values,
)
.map_err(|_| {
anyhow::Error::msg("Invalid conversion when setting public values targets.")
})?;

let block_proof = self.block.circuit.prove(block_inputs)?;
Ok((block_proof, public_values))
Ok((block_proof, block_public_values))
}

pub fn verify_block(&self, block_proof: &ProofWithPublicInputs<F, C, D>) -> anyhow::Result<()> {
Expand Down
9 changes: 6 additions & 3 deletions evm/src/generation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,11 @@ pub struct GenerationInputs {
pub tries: TrieInputs,
/// Expected trie roots after the transactions are executed.
pub trie_roots_after: TrieRoots,
/// State trie root of the genesis block.
pub genesis_state_trie_root: H256,

/// State trie root of the checkpoint block.
/// This could always be the genesis block of the chain, but it allows a prover to continue proving blocks
/// from certain checkpoint heights without requiring proofs for blocks past this checkpoint.
pub checkpoint_state_trie_root: H256,

/// Mapping between smart contract code hashes and the contract byte code.
/// All account smart contracts that are invoked will have an entry present.
Expand Down Expand Up @@ -218,7 +221,7 @@ pub fn generate_traces<F: RichField + Extendable<D>, const D: usize>(

let trie_root_ptrs = state.trie_root_ptrs;
let extra_block_data = ExtraBlockData {
genesis_state_trie_root: inputs.genesis_state_trie_root,
checkpoint_state_trie_root: inputs.checkpoint_state_trie_root,
txn_number_before: inputs.txn_number_before,
txn_number_after,
gas_used_before: inputs.gas_used_before,
Expand Down
4 changes: 2 additions & 2 deletions evm/src/get_challenges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ fn observe_extra_block_data<
challenger: &mut Challenger<F, C::Hasher>,
extra_data: &ExtraBlockData,
) -> Result<(), ProgramError> {
challenger.observe_elements(&h256_limbs(extra_data.genesis_state_trie_root));
challenger.observe_elements(&h256_limbs(extra_data.checkpoint_state_trie_root));
challenger.observe_element(u256_to_u32(extra_data.txn_number_before)?);
challenger.observe_element(u256_to_u32(extra_data.txn_number_after)?);
challenger.observe_element(u256_to_u32(extra_data.gas_used_before)?);
Expand All @@ -123,7 +123,7 @@ fn observe_extra_block_data_target<
) where
C::Hasher: AlgebraicHasher<F>,
{
challenger.observe_elements(&extra_data.genesis_state_trie_root);
challenger.observe_elements(&extra_data.checkpoint_state_trie_root);
challenger.observe_element(extra_data.txn_number_before);
challenger.observe_element(extra_data.txn_number_after);
challenger.observe_element(extra_data.gas_used_before);
Expand Down
32 changes: 16 additions & 16 deletions evm/src/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,8 @@ impl BlockMetadata {
/// unlike `BlockMetadata`.
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
pub struct ExtraBlockData {
/// The state trie digest of the genesis block.
pub genesis_state_trie_root: H256,
/// The state trie digest of the checkpoint block.
pub checkpoint_state_trie_root: H256,
/// The transaction count prior execution of the local state transition, starting
/// at 0 for the initial transaction of a block.
pub txn_number_before: U256,
Expand All @@ -251,14 +251,14 @@ impl ExtraBlockData {
pub fn from_public_inputs<F: RichField>(pis: &[F]) -> Self {
assert!(pis.len() == ExtraBlockDataTarget::SIZE);

let genesis_state_trie_root = get_h256(&pis[0..8]);
let checkpoint_state_trie_root = get_h256(&pis[0..8]);
let txn_number_before = pis[8].to_canonical_u64().into();
let txn_number_after = pis[9].to_canonical_u64().into();
let gas_used_before = pis[10].to_canonical_u64().into();
let gas_used_after = pis[11].to_canonical_u64().into();

Self {
genesis_state_trie_root,
checkpoint_state_trie_root,
txn_number_before,
txn_number_after,
gas_used_before,
Expand Down Expand Up @@ -338,13 +338,13 @@ impl PublicValuesTarget {
buffer.write_target_array(&cur_hash)?;

let ExtraBlockDataTarget {
genesis_state_trie_root: genesis_state_root,
checkpoint_state_trie_root,
txn_number_before,
txn_number_after,
gas_used_before,
gas_used_after,
} = self.extra_block_data;
buffer.write_target_array(&genesis_state_root)?;
buffer.write_target_array(&checkpoint_state_trie_root)?;
buffer.write_target(txn_number_before)?;
buffer.write_target(txn_number_after)?;
buffer.write_target(gas_used_before)?;
Expand Down Expand Up @@ -386,7 +386,7 @@ impl PublicValuesTarget {
};

let extra_block_data = ExtraBlockDataTarget {
genesis_state_trie_root: buffer.read_target_array()?,
checkpoint_state_trie_root: buffer.read_target_array()?,
txn_number_before: buffer.read_target()?,
txn_number_after: buffer.read_target()?,
gas_used_before: buffer.read_target()?,
Expand Down Expand Up @@ -740,8 +740,8 @@ impl BlockHashesTarget {
/// unlike `BlockMetadata`.
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub(crate) struct ExtraBlockDataTarget {
/// `Target`s for the state trie digest of the genesis block.
pub genesis_state_trie_root: [Target; 8],
/// `Target`s for the state trie digest of the checkpoint block.
pub checkpoint_state_trie_root: [Target; 8],
/// `Target` for the transaction count prior execution of the local state transition, starting
/// at 0 for the initial trnasaction of a block.
pub txn_number_before: Target,
Expand All @@ -762,14 +762,14 @@ impl ExtraBlockDataTarget {
/// Extracts the extra block data `Target`s from the public input `Target`s.
/// The provided `pis` should start with the extra vblock data.
pub(crate) fn from_public_inputs(pis: &[Target]) -> Self {
let genesis_state_trie_root = pis[0..8].try_into().unwrap();
let checkpoint_state_trie_root = pis[0..8].try_into().unwrap();
let txn_number_before = pis[8];
let txn_number_after = pis[9];
let gas_used_before = pis[10];
let gas_used_after = pis[11];

Self {
genesis_state_trie_root,
checkpoint_state_trie_root,
txn_number_before,
txn_number_after,
gas_used_before,
Expand All @@ -786,11 +786,11 @@ impl ExtraBlockDataTarget {
ed1: Self,
) -> Self {
Self {
genesis_state_trie_root: core::array::from_fn(|i| {
checkpoint_state_trie_root: core::array::from_fn(|i| {
builder.select(
condition,
ed0.genesis_state_trie_root[i],
ed1.genesis_state_trie_root[i],
ed0.checkpoint_state_trie_root[i],
ed1.checkpoint_state_trie_root[i],
)
}),
txn_number_before: builder.select(
Expand All @@ -812,8 +812,8 @@ impl ExtraBlockDataTarget {
) {
for i in 0..8 {
builder.connect(
ed0.genesis_state_trie_root[i],
ed1.genesis_state_trie_root[i],
ed0.checkpoint_state_trie_root[i],
ed1.checkpoint_state_trie_root[i],
);
}
builder.connect(ed0.txn_number_before, ed1.txn_number_before);
Expand Down
Loading

0 comments on commit bfcfcdb

Please sign in to comment.