Skip to content

Commit

Permalink
feat: add tests from github.com/jsign/verkle-test-vectors
Browse files Browse the repository at this point in the history
  • Loading branch information
morph-dev committed Apr 12, 2024
1 parent 3178b63 commit 377ed98
Show file tree
Hide file tree
Showing 10 changed files with 417 additions and 92 deletions.
115 changes: 115 additions & 0 deletions verkle/src/account.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use alloy_primitives::{Address, B128, B256, U256};
use banderwagon::Fr;

use crate::{
committer::DEFAULT_COMMITER,
constants::{
BALANCE_LEAF_KEY, CODE_KECCAK_LEAF_KEY, CODE_OFFSET, CODE_SIZE_LEAF_KEY,
HEADER_STORAGE_OFFSET, MAIN_STORAGE_OFFSET, NONCE_LEAF_KEY, VERKLE_NODE_WIDTH_U256,
VERSION_LEAF_KEY,
},
stem::Stem,
utils::fr_to_b256,
TrieKey, TrieValue,
};

pub struct AccountStorageLayout {
pub address: Address,
base_storage_stem: Stem,
}

impl AccountStorageLayout {
pub fn new(address: Address) -> Self {
Self {
address,
base_storage_stem: tree_key(address, U256::ZERO, 0).into(),
}
}

pub fn version_key(&self) -> TrieKey {
TrieKey::from_stem_and_last_byte(&self.base_storage_stem, VERSION_LEAF_KEY)
}

pub fn balance_key(&self) -> TrieKey {
TrieKey::from_stem_and_last_byte(&self.base_storage_stem, BALANCE_LEAF_KEY)
}

pub fn nonce_key(&self) -> TrieKey {
TrieKey::from_stem_and_last_byte(&self.base_storage_stem, NONCE_LEAF_KEY)
}

pub fn code_hash_key(&self) -> TrieKey {
TrieKey::from_stem_and_last_byte(&self.base_storage_stem, CODE_KECCAK_LEAF_KEY)
}

pub fn code_size_key(&self) -> TrieKey {
TrieKey::from_stem_and_last_byte(&self.base_storage_stem, CODE_SIZE_LEAF_KEY)
}

pub fn storage_slot_key(&self, storage_key: U256) -> TrieKey {
let pos = if storage_key < CODE_OFFSET - HEADER_STORAGE_OFFSET {
HEADER_STORAGE_OFFSET + storage_key
} else {
MAIN_STORAGE_OFFSET + storage_key
};
tree_key(
self.address,
pos / VERKLE_NODE_WIDTH_U256,
(pos % VERKLE_NODE_WIDTH_U256).byte(0),
)
}

pub fn code_key(&self, chunk_id: usize) -> TrieKey {
let pos = CODE_OFFSET + U256::from(chunk_id);
tree_key(
self.address,
pos / VERKLE_NODE_WIDTH_U256,
(pos % VERKLE_NODE_WIDTH_U256).byte(0),
)
}

pub fn chunkify_code(&self, code: &[u8]) -> Vec<(TrieKey, TrieValue)> {
const PUSH_OFFSET: u8 = 95;
const PUSH1: u8 = PUSH_OFFSET + 1;
const PUSH32: u8 = PUSH_OFFSET + 32;

let mut remaining_push_data = 0u8;
let mut result = vec![];
for (chunk_id, chunk) in code.chunks(31).enumerate() {
let mut value = Vec::with_capacity(32);
value.push(remaining_push_data.min(31));
value.extend(chunk);
value.resize(32, 0);
result.push((self.code_key(chunk_id), TrieValue::from_le_slice(&value)));

for chunk_byte in chunk {
if remaining_push_data > 0 {
remaining_push_data -= 1;
} else if (PUSH1..=PUSH32).contains(chunk_byte) {
remaining_push_data = chunk_byte - PUSH_OFFSET;
}
}
}
result
}
}

fn tree_key(address: Address, tree_index: U256, sub_index: u8) -> TrieKey {
let address_bytes = *B256::left_padding_from(address.as_slice());
let tree_index_bytes = tree_index.to_le_bytes::<32>();

let scalars = [
2u128 + 256 * 64,
u128::from_le_bytes(B128::from_slice(&address_bytes[..16]).0),
u128::from_le_bytes(B128::from_slice(&address_bytes[16..]).0),
u128::from_le_bytes(B128::from_slice(&tree_index_bytes[..16]).0),
u128::from_le_bytes(B128::from_slice(&tree_index_bytes[16..]).0),
]
.map(Fr::from);
let commitment = DEFAULT_COMMITER.commit_lagrange(&scalars);
let hash_commitment = commitment.map_to_scalar_field();

let mut key = fr_to_b256(&hash_commitment);
key[31] = sub_index;
key.into()
}
13 changes: 13 additions & 0 deletions verkle/src/constants.rs
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
use alloy_primitives::U256;

pub const VERKLE_NODE_WIDTH: usize = 256;
pub const VERKLE_NODE_WIDTH_U256: U256 = U256::from_limbs([256, 0, 0, 0]);

// Storage layout parameters
pub const VERSION_LEAF_KEY: u8 = 0;
pub const BALANCE_LEAF_KEY: u8 = 1;
pub const NONCE_LEAF_KEY: u8 = 2;
pub const CODE_KECCAK_LEAF_KEY: u8 = 3;
pub const CODE_SIZE_LEAF_KEY: u8 = 4;
pub const HEADER_STORAGE_OFFSET: U256 = U256::from_limbs([64, 0, 0, 0]);
pub const CODE_OFFSET: U256 = U256::from_limbs([128, 0, 0, 0]);
pub const MAIN_STORAGE_OFFSET: U256 = U256::from_limbs([0, 0, 0, 2u64.pow(56)]);
8 changes: 8 additions & 0 deletions verkle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ use alloy_primitives::{B256, U256};
use derive_more::{Constructor, Deref, From, Index};

use banderwagon::Fr;
use stem::Stem;
pub use trie::Trie;

pub mod account;
mod committer;
mod constants;
pub mod crs;
Expand All @@ -20,6 +22,12 @@ type Db = dyn db::Db<Fr, Vec<u8>>;
pub struct TrieKey(B256);

impl TrieKey {
pub fn from_stem_and_last_byte(stem: &Stem, last_byte: u8) -> Self {
let mut key = B256::right_padding_from(stem.as_slice());
key[B256::len_bytes() - 1] = last_byte;
key.into()
}

pub fn length() -> usize {
B256::len_bytes()
}
Expand Down
4 changes: 2 additions & 2 deletions verkle/src/nodes/branch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl Default for BranchNode {
}

impl NodeTrait for BranchNode {
fn commitment(&self) -> Fr {
fn hash_commitment(&self) -> Fr {
self.cp.map_to_scalar_field()
}
}
Expand All @@ -83,7 +83,7 @@ impl Encode for BranchNode {
if node.is_empty() {
None
} else {
Some((index as u8, fr_to_b256(&node.commitment())))
Some((index as u8, fr_to_b256(&node.hash_commitment())))
}
})
.collect();
Expand Down
14 changes: 7 additions & 7 deletions verkle/src/nodes/leaf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,12 @@ impl LeafNode {
&self.stem
}

fn calculate_commitment(&self) -> Fr {
let c = self.const_cp
fn calculate_commitment(&self) -> Element {
self.const_cp
+ DEFAULT_COMMITER.commit_sparse(vec![
(2, self.cp1.map_to_scalar_field()),
(3, self.cp2.map_to_scalar_field()),
]);
c.map_to_scalar_field()
])
}

pub fn get(&self, index: u8) -> Option<&TrieValue> {
Expand Down Expand Up @@ -119,12 +118,13 @@ impl LeafNode {
}

impl NodeTrait for LeafNode {
fn commitment(&self) -> Fr {
self.c.unwrap_or_else(|| self.calculate_commitment())
fn hash_commitment(&self) -> Fr {
self.c
.unwrap_or_else(|| self.calculate_commitment().map_to_scalar_field())
}

fn commit(&mut self) -> Fr {
self.c = self.c.or_else(|| Some(self.calculate_commitment()));
self.c = Some(self.hash_commitment());
self.c.expect("Value must be present")
}
}
Expand Down
14 changes: 7 additions & 7 deletions verkle/src/nodes/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ use crate::{Db, TrieKey, TrieValue};
use super::{BranchNode, LeafNode};

pub trait NodeTrait {
fn commitment(&self) -> Fr;
fn hash_commitment(&self) -> Fr;

fn commit(&mut self) -> Fr {
self.commitment()
self.hash_commitment()
}
}

Expand All @@ -25,11 +25,11 @@ pub enum Node {
}

impl NodeTrait for Node {
fn commitment(&self) -> Fr {
fn hash_commitment(&self) -> Fr {
match self {
Node::Empty => Fr::zero(),
Node::Branch(branch_node) => branch_node.commitment(),
Node::Leaf(leaf_node) => leaf_node.commitment(),
Node::Branch(branch_node) => branch_node.hash_commitment(),
Node::Leaf(leaf_node) => leaf_node.hash_commitment(),
Node::Commitment(c) => *c,
}
}
Expand All @@ -50,12 +50,12 @@ impl Node {
}

pub fn check(&self, commitment: &Fr) -> Result<()> {
if &self.commitment() == commitment {
if &self.hash_commitment() == commitment {
Ok(())
} else {
Err(anyhow!(
"Node's commitment {:?} doesn't match expected {commitment:?}",
self.commitment()
self.hash_commitment()
))
}
}
Expand Down
26 changes: 17 additions & 9 deletions verkle/src/stem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,42 @@ use ssz::{Decode, Encode};

use crate::TrieKey;

const STEM_LENGTH: usize = 31;

#[derive(PartialEq, Eq, AsRef, Deref, Index)]
pub struct Stem([u8; STEM_LENGTH]);
pub struct Stem([u8; Self::STEM_LENGTH]);

impl Stem {
const STEM_LENGTH: usize = 31;
}

impl From<&TrieKey> for Stem {
fn from(key: &TrieKey) -> Self {
let mut stem = [0u8; STEM_LENGTH];
stem.copy_from_slice(&key[..STEM_LENGTH]);
let mut stem = [0u8; Self::STEM_LENGTH];
stem.copy_from_slice(&key[..Self::STEM_LENGTH]);
Stem(stem)
}
}

impl From<TrieKey> for Stem {
fn from(key: TrieKey) -> Self {
Self::from(&key)
}
}

impl Encode for Stem {
fn is_ssz_fixed_len() -> bool {
true
}

fn ssz_fixed_len() -> usize {
STEM_LENGTH
Self::STEM_LENGTH
}

fn ssz_append(&self, buf: &mut Vec<u8>) {
buf.extend(self.as_ref())
}

fn ssz_bytes_len(&self) -> usize {
STEM_LENGTH
Self::STEM_LENGTH
}
}

Expand All @@ -40,15 +48,15 @@ impl Decode for Stem {
}

fn ssz_fixed_len() -> usize {
STEM_LENGTH
Self::STEM_LENGTH
}

fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
match <[u8; 31]>::try_from(bytes) {
Ok(stem) => Ok(Self(stem)),
Err(_) => Err(ssz::DecodeError::InvalidByteLength {
len: bytes.len(),
expected: STEM_LENGTH,
expected: Self::STEM_LENGTH,
}),
}
}
Expand Down
40 changes: 39 additions & 1 deletion verkle/src/trie.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use alloy_primitives::B256;
use alloy_primitives::{keccak256, Address, B256, U256};
use anyhow::Result;
use banderwagon::Fr;

use crate::{
account::AccountStorageLayout,
nodes::{BranchNode, Node},
utils::{b256_to_fr, fr_to_b256},
Db, TrieKey, TrieValue,
Expand Down Expand Up @@ -45,6 +46,43 @@ impl Trie {
pub fn root_hash_commitment(&mut self) -> Result<Fr> {
self.root.write_and_commit(self.db.as_mut())
}

pub fn create_eoa(&mut self, address: Address, balance: U256, nonce: u64) -> Result<()> {
let storage = AccountStorageLayout::new(address);
self.insert(storage.version_key(), TrieValue::ZERO)?;
self.insert(storage.balance_key(), balance)?;
self.insert(storage.nonce_key(), TrieValue::from(nonce))?;
self.insert(
storage.code_hash_key(),
TrieValue::from_le_slice(
hex::decode("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")?
.as_slice(),
),
)?;
Ok(())
}

pub fn create_sc(
&mut self,
address: Address,
balance: U256,
nonce: u64,
code: Vec<u8>,
) -> Result<()> {
let storage = AccountStorageLayout::new(address);
self.insert(storage.version_key(), TrieValue::ZERO)?;
self.insert(storage.balance_key(), balance)?;
self.insert(storage.nonce_key(), TrieValue::from(nonce))?;
self.insert(
storage.code_hash_key(),
TrieValue::from_le_bytes(keccak256(&code).0),
)?;
self.insert(storage.code_size_key(), TrieValue::from(code.len()))?;
for (chunk_key, chunk_value) in storage.chunkify_code(&code) {
self.insert(chunk_key, chunk_value)?;
}
Ok(())
}
}

#[cfg(test)]
Expand Down
Loading

0 comments on commit 377ed98

Please sign in to comment.