-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #320 from 0xPolygonZero/cleanup/smt_trie_to_develop
- Loading branch information
Showing
12 changed files
with
1,419 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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,39 @@ | ||
[package] | ||
name = "smt_trie" | ||
description = "Types and utility functions for building/working with Polygon Hermez Sparse Merkle Trees." | ||
version = "0.1.0" | ||
authors = ["William Borgeaud <williamborgeaud@gmail.com>"] | ||
readme = "README.md" | ||
categories = ["cryptography"] | ||
edition.workspace = true | ||
license.workspace = true | ||
repository.workspace = true | ||
homepage.workspace = true | ||
keywords.workspace = true | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
bytes = { workspace = true } | ||
enum-as-inner = { workspace = true } | ||
ethereum-types = { workspace = true } | ||
hex = { workspace = true } | ||
hex-literal = { workspace = true } | ||
keccak-hash = { workspace = true } | ||
log = { workspace = true } | ||
num-traits = { workspace = true } | ||
parking_lot = { workspace = true, features = ["serde"] } | ||
plonky2 = { workspace = true } | ||
rand = { workspace = true } | ||
rlp = { workspace = true } | ||
serde = { workspace = true, features = ["derive", "rc"] } | ||
thiserror = { workspace = true } | ||
uint = { workspace = true } | ||
|
||
|
||
[dev-dependencies] | ||
eth_trie = "0.4.0" | ||
pretty_env_logger = "0.5.0" | ||
rlp-derive = { workspace = true } | ||
serde = { workspace = true, features = ["derive"] } | ||
serde_json = { workspace = true } |
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,2 @@ | ||
Types and functions to work with the Hermez/Polygon zkEVM sparse Merkle tree (SMT) format. | ||
See https://github.com/0xPolygonHermez/zkevm-commonjs for reference implementation. |
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,103 @@ | ||
use std::ops::Add; | ||
|
||
use ethereum_types::{BigEndianHash, H256, U256}; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
pub type Bit = bool; | ||
|
||
#[derive( | ||
Copy, Clone, Deserialize, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Debug, | ||
)] | ||
pub struct Bits { | ||
/// The number of bits in this sequence. | ||
pub count: usize, | ||
/// A packed encoding of these bits. Only the first (least significant) | ||
/// `count` bits are used. The rest are unused and should be zero. | ||
pub packed: U256, | ||
} | ||
|
||
impl From<U256> for Bits { | ||
fn from(packed: U256) -> Self { | ||
Bits { count: 256, packed } | ||
} | ||
} | ||
|
||
impl From<H256> for Bits { | ||
fn from(packed: H256) -> Self { | ||
Bits { | ||
count: 256, | ||
packed: packed.into_uint(), | ||
} | ||
} | ||
} | ||
|
||
impl Add for Bits { | ||
type Output = Self; | ||
|
||
fn add(self, rhs: Self) -> Self::Output { | ||
assert!(self.count + rhs.count <= 256, "Overflow"); | ||
Self { | ||
count: self.count + rhs.count, | ||
packed: self.packed * (U256::one() << rhs.count) + rhs.packed, | ||
} | ||
} | ||
} | ||
|
||
impl Bits { | ||
pub fn empty() -> Self { | ||
Bits { | ||
count: 0, | ||
packed: U256::zero(), | ||
} | ||
} | ||
|
||
pub fn is_empty(&self) -> bool { | ||
self.count == 0 | ||
} | ||
|
||
pub fn pop_next_bit(&mut self) -> Bit { | ||
assert!(!self.is_empty(), "Cannot pop from empty bits"); | ||
let b = !(self.packed & U256::one()).is_zero(); | ||
self.packed >>= 1; | ||
self.count -= 1; | ||
b | ||
} | ||
|
||
pub fn get_bit(&self, i: usize) -> Bit { | ||
assert!(i < self.count, "Index out of bounds"); | ||
!(self.packed & (U256::one() << (self.count - 1 - i))).is_zero() | ||
} | ||
|
||
pub fn push_bit(&mut self, bit: Bit) { | ||
self.packed = self.packed * 2 + U256::from(bit as u64); | ||
self.count += 1; | ||
} | ||
|
||
pub fn add_bit(&self, bit: Bit) -> Self { | ||
let mut x = *self; | ||
x.push_bit(bit); | ||
x | ||
} | ||
|
||
pub fn common_prefix(&self, k: &Bits) -> (Self, Option<(Bit, Bit)>) { | ||
let mut a = *self; | ||
let mut b = *k; | ||
while a.count > b.count { | ||
a.pop_next_bit(); | ||
} | ||
while a.count < b.count { | ||
b.pop_next_bit(); | ||
} | ||
if a == b { | ||
return (a, None); | ||
} | ||
let mut a_bit = a.pop_next_bit(); | ||
let mut b_bit = b.pop_next_bit(); | ||
while a != b { | ||
a_bit = a.pop_next_bit(); | ||
b_bit = b.pop_next_bit(); | ||
} | ||
assert_ne!(a_bit, b_bit, "Sanity check."); | ||
(a, Some((a_bit, b_bit))) | ||
} | ||
} |
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,85 @@ | ||
/// Functions to hash contract bytecode using Poseidon. | ||
/// See `hashContractBytecode()` in https://github.com/0xPolygonHermez/zkevm-commonjs/blob/main/src/smt-utils.js for reference implementation. | ||
use ethereum_types::U256; | ||
use plonky2::field::types::Field; | ||
use plonky2::hash::poseidon::{self, Poseidon}; | ||
|
||
use crate::smt::{HashOut, F}; | ||
use crate::utils::hashout2u; | ||
|
||
pub fn hash_contract_bytecode(mut code: Vec<u8>) -> HashOut { | ||
poseidon_pad_byte_vec(&mut code); | ||
|
||
poseidon_hash_padded_byte_vec(code) | ||
} | ||
|
||
pub fn poseidon_hash_padded_byte_vec(bytes: Vec<u8>) -> HashOut { | ||
let mut capacity = [F::ZERO; poseidon::SPONGE_CAPACITY]; | ||
let mut arr = [F::ZERO; poseidon::SPONGE_WIDTH]; | ||
for blocks in bytes.chunks_exact(poseidon::SPONGE_RATE * 7) { | ||
arr[..poseidon::SPONGE_RATE].copy_from_slice( | ||
&blocks | ||
.chunks_exact(7) | ||
.map(|block| { | ||
let mut bytes = [0u8; poseidon::SPONGE_RATE]; | ||
bytes[..7].copy_from_slice(block); | ||
F::from_canonical_u64(u64::from_le_bytes(bytes)) | ||
}) | ||
.collect::<Vec<F>>(), | ||
); | ||
arr[poseidon::SPONGE_RATE..poseidon::SPONGE_WIDTH].copy_from_slice(&capacity); | ||
capacity = F::poseidon(arr)[0..poseidon::SPONGE_CAPACITY] | ||
.try_into() | ||
.unwrap(); | ||
} | ||
HashOut { elements: capacity } | ||
} | ||
|
||
pub fn poseidon_pad_byte_vec(bytes: &mut Vec<u8>) { | ||
bytes.push(0x01); | ||
while bytes.len() % 56 != 0 { | ||
bytes.push(0x00); | ||
} | ||
*bytes.last_mut().unwrap() |= 0x80; | ||
} | ||
|
||
pub fn hash_bytecode_u256(code: Vec<u8>) -> U256 { | ||
hashout2u(hash_contract_bytecode(code)) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use hex_literal::hex; | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn test_empty_code() { | ||
assert_eq!( | ||
hash_contract_bytecode(vec![]).elements, | ||
[ | ||
10052403398432742521, | ||
15195891732843337299, | ||
2019258788108304834, | ||
4300613462594703212, | ||
] | ||
.map(F::from_canonical_u64) | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_some_code() { | ||
let code = hex!("60806040526004361061003f5760003560e01c80632b68b9c6146100445780633fa4f2451461005b5780635cfb28e714610086578063718da7ee14610090575b600080fd5b34801561005057600080fd5b506100596100b9565b005b34801561006757600080fd5b506100706100f2565b60405161007d9190610195565b60405180910390f35b61008e6100f8565b005b34801561009c57600080fd5b506100b760048036038101906100b29190610159565b610101565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b60015481565b34600181905550565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600081359050610153816101f1565b92915050565b60006020828403121561016f5761016e6101ec565b5b600061017d84828501610144565b91505092915050565b61018f816101e2565b82525050565b60006020820190506101aa6000830184610186565b92915050565b60006101bb826101c2565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600080fd5b6101fa816101b0565b811461020557600080fd5b5056fea26469706673582212207ae6e5d5feddef608b24cca98990c37cf78f8b377163a7c4951a429d90d6120464736f6c63430008070033"); | ||
|
||
assert_eq!( | ||
hash_contract_bytecode(code.to_vec()).elements, | ||
[ | ||
13311281292453978464, | ||
8384462470517067887, | ||
14733964407220681187, | ||
13541155386998871195 | ||
] | ||
.map(F::from_canonical_u64) | ||
); | ||
} | ||
} |
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,23 @@ | ||
use std::collections::HashMap; | ||
|
||
use crate::smt::{Key, Node}; | ||
|
||
pub trait Db: Default { | ||
fn get_node(&self, key: &Key) -> Option<&Node>; | ||
fn set_node(&mut self, key: Key, value: Node); | ||
} | ||
|
||
#[derive(Debug, Clone, Default)] | ||
pub struct MemoryDb { | ||
pub db: HashMap<Key, Node>, | ||
} | ||
|
||
impl Db for MemoryDb { | ||
fn get_node(&self, key: &Key) -> Option<&Node> { | ||
self.db.get(key) | ||
} | ||
|
||
fn set_node(&mut self, key: Key, value: Node) { | ||
self.db.insert(key, value); | ||
} | ||
} |
Oops, something went wrong.