-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
1,672 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
use thiserror::Error; | ||
|
||
use crate::Stem; | ||
|
||
#[derive(Debug, Error)] | ||
pub enum VerkleTrieError { | ||
#[error("Expected stem {expected}, but received {actual}")] | ||
UnexpectedStem { expected: Stem, actual: Stem }, | ||
#[error("Node not found at depth {depth} for stem {stem} during the trie traversal")] | ||
NodeNotFound { stem: Stem, depth: usize }, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
use std::collections::{BTreeMap, HashMap}; | ||
|
||
use alloy_primitives::{b256, keccak256, Address, Bytes, B256, U256}; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
use crate::{Stem, TrieKey, TrieValue}; | ||
|
||
use super::{storage::AccountStorageLayout, StateWrites, StemStateWrite}; | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
#[serde(deny_unknown_fields)] | ||
pub struct AccountAlloc { | ||
pub balance: U256, | ||
pub nonce: Option<U256>, | ||
pub code: Option<Bytes>, | ||
pub storage: Option<HashMap<U256, TrieValue>>, | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
pub struct GenesisConfig { | ||
pub alloc: HashMap<Address, AccountAlloc>, | ||
} | ||
|
||
impl GenesisConfig { | ||
pub const DEVNET6_BLOCK_HASH: B256 = | ||
b256!("3fe165c03e7a77d1e3759362ebeeb16fd964cb411ce11fbe35c7032fab5b9a8a"); | ||
pub const DEVNET6_STATE_ROOT: B256 = | ||
b256!("1fbf85345a3cbba9a6d44f991b721e55620a22397c2a93ee8d5011136ac300ee"); | ||
|
||
pub fn into_state_writes(self) -> StateWrites { | ||
let mut state_writes = BTreeMap::<Stem, StemStateWrite>::new(); | ||
let mut insert_state_write = |key: TrieKey, value: TrieValue| { | ||
let stem = key.stem(); | ||
state_writes | ||
.entry(stem) | ||
.or_insert_with(|| StemStateWrite { | ||
stem, | ||
writes: HashMap::new(), | ||
}) | ||
.writes | ||
.insert(key.suffix(), value); | ||
}; | ||
|
||
for (address, account_alloc) in self.alloc { | ||
let storage_layout = AccountStorageLayout::new(address); | ||
insert_state_write(storage_layout.version_key(), U256::ZERO.into()); | ||
insert_state_write(storage_layout.balance_key(), account_alloc.balance.into()); | ||
insert_state_write( | ||
storage_layout.nonce_key(), | ||
account_alloc.nonce.unwrap_or(U256::ZERO).into(), | ||
); | ||
|
||
match &account_alloc.code { | ||
None => insert_state_write(storage_layout.code_hash_key(), keccak256([]).into()), | ||
Some(code) => { | ||
insert_state_write(storage_layout.code_hash_key(), keccak256(code).into()); | ||
insert_state_write( | ||
storage_layout.code_size_key(), | ||
U256::from(code.len()).into(), | ||
); | ||
for (key, value) in storage_layout.chunkify_code(code) { | ||
insert_state_write(key, value); | ||
} | ||
} | ||
} | ||
|
||
if let Some(storage) = account_alloc.storage { | ||
for (storage_key, value) in storage { | ||
insert_state_write(storage_layout.storage_slot_key(storage_key), value); | ||
} | ||
} | ||
} | ||
|
||
StateWrites::new(state_writes.into_values().collect()) | ||
} | ||
} | ||
|
||
impl From<GenesisConfig> for StateWrites { | ||
fn from(genesis_config: GenesisConfig) -> Self { | ||
genesis_config.into_state_writes() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use std::{fs::File, io::BufReader}; | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn parse_genesis() -> anyhow::Result<()> { | ||
let reader = BufReader::new(File::open("../testdata/genesis.json")?); | ||
let genesis_config: GenesisConfig = serde_json::from_reader(reader)?; | ||
let alloc = genesis_config.alloc; | ||
assert_eq!(alloc.len(), 278); | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
use std::collections::{HashMap, HashSet}; | ||
|
||
use derive_more::{Constructor, Deref, Index}; | ||
|
||
use crate::{ssz::TriePath, Stem, TrieValue}; | ||
|
||
use nodes::{branch::BranchNode, leaf::LeafNode}; | ||
pub use trie::VerkleTrie; | ||
|
||
pub mod error; | ||
pub mod genesis_config; | ||
pub mod nodes; | ||
pub mod storage; | ||
mod trie; | ||
pub mod trie_printer; | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq, Constructor, Deref, Index)] | ||
pub struct StateWrites(Vec<StemStateWrite>); | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq, Constructor)] | ||
pub struct StemStateWrite { | ||
pub stem: Stem, | ||
pub writes: HashMap<u8, TrieValue>, | ||
} | ||
|
||
pub type NewBranchNode = Option<TriePath>; | ||
|
||
#[derive(Clone)] | ||
pub struct AuxiliaryTrieModifications { | ||
pub new_branch_nodes: HashSet<TriePath>, | ||
} | ||
|
||
#[derive(Clone)] | ||
pub struct PathToLeaf<'a> { | ||
pub branches: Vec<&'a BranchNode>, | ||
pub leaf: &'a LeafNode, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
use std::{array, mem}; | ||
|
||
use crate::{ | ||
constants::VERKLE_NODE_WIDTH, | ||
ssz::TriePath, | ||
verkle::{NewBranchNode, StemStateWrite}, | ||
Point, ScalarField, Stem, TrieKey, TrieValue, | ||
}; | ||
|
||
use super::{commitment::Commitment, leaf::LeafNode, Node}; | ||
|
||
pub struct BranchNode { | ||
depth: usize, | ||
commitment: Commitment, | ||
children: [Node; VERKLE_NODE_WIDTH], | ||
} | ||
|
||
impl BranchNode { | ||
pub fn new(depth: usize) -> Self { | ||
if depth >= Stem::len_bytes() { | ||
panic!("Invalid branch depth!") | ||
} | ||
Self { | ||
depth, | ||
commitment: Commitment::zero(), | ||
children: array::from_fn(|_| Node::Empty), | ||
} | ||
} | ||
|
||
pub fn commitment(&self) -> &Point { | ||
self.commitment.commitment() | ||
} | ||
|
||
pub fn commitment_hash(&mut self) -> ScalarField { | ||
self.commitment.commitment_hash() | ||
} | ||
|
||
pub fn get(&self, key: &TrieKey) -> Option<&TrieValue> { | ||
let index = key[self.depth] as usize; | ||
match &self.children[index] { | ||
Node::Empty => None, | ||
Node::Branch(branch_node) => branch_node.get(key), | ||
Node::Leaf(leaf_node) => { | ||
if key.starts_with_stem(leaf_node.stem()) { | ||
leaf_node.get(key.suffix() as usize) | ||
} else { | ||
None | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub(crate) fn get_child(&self, index: usize) -> &Node { | ||
&self.children[index] | ||
} | ||
|
||
fn set_child(&mut self, index: usize, mut child: Node) { | ||
self.commitment.update_single( | ||
index, | ||
child.commitment_hash() - self.children[index].commitment_hash(), | ||
); | ||
self.children[index] = child; | ||
} | ||
|
||
/// Returns by how much the commitmant hash has changed and the path to the new branch node if | ||
/// one was created. | ||
pub fn update(&mut self, state_write: &StemStateWrite) -> (ScalarField, NewBranchNode) { | ||
let index = state_write.stem[self.depth] as usize; | ||
let child = &mut self.children[index]; | ||
match child { | ||
Node::Empty => { | ||
let mut leaf_node = Box::new(LeafNode::new(state_write.stem)); | ||
leaf_node.update(&state_write.writes); | ||
*child = Node::Leaf(leaf_node); | ||
( | ||
self.commitment | ||
.update_single(index, child.commitment_hash()), | ||
None, | ||
) | ||
} | ||
Node::Branch(branch_node) => { | ||
let (commitment_hash_diff, new_branch_node) = branch_node.update(state_write); | ||
( | ||
self.commitment.update_single(index, commitment_hash_diff), | ||
new_branch_node, | ||
) | ||
} | ||
Node::Leaf(leaf_node) => { | ||
if leaf_node.stem() == &state_write.stem { | ||
let commitment_hash_diff = leaf_node.update(&state_write.writes); | ||
( | ||
self.commitment.update_single(index, commitment_hash_diff), | ||
None, | ||
) | ||
} else { | ||
let old_commitment_hash = leaf_node.commitment_hash(); | ||
|
||
let old_child_index_in_new_branch = leaf_node.stem()[self.depth + 1] as usize; | ||
let old_child = mem::replace(child, Node::Empty); | ||
|
||
let mut branch_node = Box::new(Self::new(self.depth + 1)); | ||
branch_node.set_child(old_child_index_in_new_branch, old_child); | ||
branch_node.update(state_write); | ||
|
||
let new_branch_node = Some(TriePath::from( | ||
state_write.stem[..branch_node.depth].to_vec(), | ||
)); | ||
*child = Node::Branch(branch_node); | ||
( | ||
self.commitment | ||
.update_single(index, child.commitment_hash() - old_commitment_hash), | ||
new_branch_node, | ||
) | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
use std::ops::AddAssign; | ||
|
||
use crate::{Point, ScalarField, CRS}; | ||
|
||
pub struct Commitment { | ||
commitment: Point, | ||
commitment_hash: Option<ScalarField>, | ||
} | ||
|
||
impl Commitment { | ||
pub fn new(commitment: Point) -> Self { | ||
Self { | ||
commitment, | ||
commitment_hash: None, | ||
} | ||
} | ||
|
||
pub fn commitment(&self) -> &Point { | ||
&self.commitment | ||
} | ||
|
||
pub fn commitment_hash(&mut self) -> ScalarField { | ||
self.commitment_hash | ||
.get_or_insert_with(|| self.commitment.map_to_scalar_field()) | ||
.clone() | ||
} | ||
|
||
/// Updates this commitment and returns by how much the commitment hash changed. | ||
/// | ||
/// @param diff By how much scalar changed. | ||
pub fn update_single(&mut self, index: usize, diff: ScalarField) -> ScalarField { | ||
let old_commitment_hash = self.commitment_hash(); | ||
*self += CRS::commit_single(index, diff); | ||
self.commitment_hash() - old_commitment_hash | ||
} | ||
|
||
/// Updates this commitment and returns by how much the commitment hash changed. | ||
/// | ||
/// @param diff By how much each inner scalar changed. | ||
pub fn update(&mut self, diff: &[(usize, ScalarField)]) -> ScalarField { | ||
let old_commitment_hash = self.commitment_hash(); | ||
*self += CRS::commit_sparse(diff); | ||
self.commitment_hash() - old_commitment_hash | ||
} | ||
|
||
pub fn zero() -> Self { | ||
Self::new(Point::zero()) | ||
} | ||
|
||
pub fn is_zero(&self) -> bool { | ||
self.commitment.is_zero() | ||
} | ||
} | ||
|
||
impl AddAssign<Point> for Commitment { | ||
fn add_assign(&mut self, rhs: Point) { | ||
self.commitment += rhs; | ||
self.commitment_hash = None; | ||
} | ||
} |
Oops, something went wrong.