diff --git a/ethereum_history_api/circuits/lib/src/account.nr b/ethereum_history_api/circuits/lib/src/account.nr index 9458edaca..07e4fe4dc 100644 --- a/ethereum_history_api/circuits/lib/src/account.nr +++ b/ethereum_history_api/circuits/lib/src/account.nr @@ -8,6 +8,12 @@ struct AccountWithinBlock { block_hash: Bytes32, } +impl Eq for AccountWithinBlock { + fn eq(self, other: Self) -> bool { + (self.account == other.account) & (self.block_hash == other.block_hash) + } +} + type AccountWithStateProof = (Account, StateProof); pub fn get_account(chain_id: Field, block_no: u64, address: Address) -> AccountWithinBlock { diff --git a/ethereum_history_api/circuits/lib/src/account_with_storage.nr b/ethereum_history_api/circuits/lib/src/account_with_storage.nr index fbb19290b..c5bb2b219 100644 --- a/ethereum_history_api/circuits/lib/src/account_with_storage.nr +++ b/ethereum_history_api/circuits/lib/src/account_with_storage.nr @@ -15,6 +15,12 @@ struct Account { code_hash: Bytes32, } +impl Eq for Account { + fn eq(self, other: Self) -> bool { + (self.nonce == other.nonce) & (self.balance == other.balance) & (self.storage_root == other.storage_root) & (self.code_hash == other.code_hash) + } +} + struct StateProof { key: Address, value: [u8; MAX_ACCOUNT_STATE_LENGTH], @@ -43,6 +49,12 @@ struct StorageWithinBlock { values: [Bytes32; N], } +impl Eq for StorageWithinBlock<1> { + fn eq(self, other: Self) -> bool { + (self.block_hash == other.block_hash) & (self.account == other.account) & (self.values[0] == other.values[0]) + } +} + pub fn get_account_with_storage( chain_id: Field, block_number: u64, diff --git a/ethereum_history_api/circuits/lib/src/lib.nr b/ethereum_history_api/circuits/lib/src/lib.nr index d1cb6f86b..d124aef61 100644 --- a/ethereum_history_api/circuits/lib/src/lib.nr +++ b/ethereum_history_api/circuits/lib/src/lib.nr @@ -9,8 +9,8 @@ mod log; mod transaction; mod misc; mod chain; -mod serialize; -mod serialize_test; +mod serde; +mod serde_test; mod fixtures; mod verifiers; diff --git a/ethereum_history_api/circuits/lib/src/misc/fragment.nr b/ethereum_history_api/circuits/lib/src/misc/fragment.nr index 88fde4bd4..4361f2c7e 100644 --- a/ethereum_history_api/circuits/lib/src/misc/fragment.nr +++ b/ethereum_history_api/circuits/lib/src/misc/fragment.nr @@ -80,6 +80,12 @@ impl Fragment { self.data[self.offset - 1] } + pub fn pop_front_array(&mut self) -> [T; LEN] { + assert(self.length >= LEN as u64, "Cannot pop array: fragment is too short"); + let mut res: [T; LEN] = zeroed(); + res.map(|_| self.pop_front()) + } + pub fn push_back(&mut self, value: T) { assert(self.offset + self.length + 1 <= MAX_DATA_LEN, "Cannot push: fragment is full"); self.data[self.offset + self.length] = value; diff --git a/ethereum_history_api/circuits/lib/src/misc/fragment_test.nr b/ethereum_history_api/circuits/lib/src/misc/fragment_test.nr index 0ec4e680f..1b10cc341 100644 --- a/ethereum_history_api/circuits/lib/src/misc/fragment_test.nr +++ b/ethereum_history_api/circuits/lib/src/misc/fragment_test.nr @@ -185,6 +185,31 @@ mod pop_front { } } +mod pop_front_array { + + use crate::misc::fragment::Fragment; + + #[test] + fn success() { + let mut fragment = Fragment::new(1, 2, [1, 2, 3]); + assert(fragment.pop_front_array() == [2, 3]); + assert(fragment.length == 0); + } + + #[test(should_fail_with="Cannot pop array: fragment is too short")] + fn fail() { + let mut fragment = Fragment::new(1, 1, [1, 2, 3]); + assert(fragment.pop_front_array() == [2]); + assert(fragment.pop_front_array() == [3]); + } + + #[test(should_fail_with="Cannot pop array: fragment is too short")] + fn partial_fail() { + let mut fragment = Fragment::new(1, 1, [1, 2, 3]); + assert(fragment.pop_front_array() == [2, 3]); + } +} + mod push_back { use crate::misc::fragment::Fragment; diff --git a/ethereum_history_api/circuits/lib/src/serde.nr b/ethereum_history_api/circuits/lib/src/serde.nr new file mode 100644 index 000000000..487cbcfac --- /dev/null +++ b/ethereum_history_api/circuits/lib/src/serde.nr @@ -0,0 +1,113 @@ +use crate::account::AccountWithinBlock; +use crate::account_with_storage::{StorageWithinBlock, Account}; +use crate::misc::{fragment::Fragment, types::{BYTES32_LENGTH, Bytes32, ADDRESS_LENGTH, Address}}; +use dep::std::unsafe::zeroed; + +trait Serde { + fn serialize(self) -> [Field; LEN]; + fn deserialize(data: [Field; LEN]) -> Self; +} + +global U128_SERIALIZED_LEN = 2; + +impl Serde for U128 { + fn serialize(self) -> [Field; U128_SERIALIZED_LEN] { + [self.lo, self.hi] + } + + fn deserialize(data: [Field; U128_SERIALIZED_LEN]) -> Self { + U128 { lo: data[0], hi: data[1] } + } +} + +impl Serde for Bytes32 { + fn serialize(self) -> [Field; BYTES32_LENGTH] { + self.map(|x: u8| x as Field) + } + + fn deserialize(data: [Field; BYTES32_LENGTH]) -> Self { + data.map(|x: Field| x as u8) + } +} + +impl Serde for Address { + fn serialize(self) -> [Field; ADDRESS_LENGTH] { + self.map(|x: u8| x as Field) + } + + fn deserialize(data: [Field; ADDRESS_LENGTH]) -> Self { + data.map(|x: Field| x as u8) + } +} + +global ACCOUNT_LEN = 1 + 1 + BYTES32_LENGTH + BYTES32_LENGTH; + +impl Serde for Account { + fn serialize(self) -> [Field; ACCOUNT_LEN] { + let mut data: BoundedVec = BoundedVec::new(); + data.push(self.nonce as Field); + data.push(self.balance); + data.extend_from_array(self.storage_root.serialize()); + data.extend_from_array(self.code_hash.serialize()); + data.storage + } + + fn deserialize(data: [Field; ACCOUNT_LEN]) -> Self { + let mut fragment = Fragment::new_focused(data); + let nonce = fragment.pop_front() as u64; + let balance = fragment.pop_front(); + let storage_root = fragment.pop_front_array().deserialize(); + let code_hash = fragment.pop_front_array().deserialize(); + Account { + nonce, + balance, + storage_root, + code_hash, + } + } +} + +global ACCOUNT_BLOCK_LEN = ACCOUNT_LEN + BYTES32_LENGTH; + +impl Serde for AccountWithinBlock { + fn serialize(self) -> [Field; ACCOUNT_BLOCK_LEN] { + let mut data: BoundedVec = BoundedVec::new(); + data.extend_from_array(self.account.serialize()); + data.extend_from_array(self.block_hash.serialize()); + data.storage + } + + fn deserialize(data: [Field; ACCOUNT_BLOCK_LEN]) -> Self { + let mut fragment = Fragment::new_focused(data); + let account = Account::deserialize(fragment.pop_front_array()); + let block_hash = fragment.pop_front_array().deserialize(); + AccountWithinBlock { + account, + block_hash, + } + } +} + +global STORAGE_BLOCK_LEN = BYTES32_LENGTH + ACCOUNT_LEN + BYTES32_LENGTH; + +impl Serde for StorageWithinBlock<1> { + fn serialize(self) -> [Field; STORAGE_BLOCK_LEN] { + let mut data: BoundedVec = BoundedVec::new(); + data.extend_from_array(self.block_hash.serialize()); + data.extend_from_array(self.account.serialize()); + data.extend_from_array(self.values[0].serialize()); + data.storage + } + + fn deserialize(data: [Field; STORAGE_BLOCK_LEN]) -> Self { + let mut fragment = Fragment::new_focused(data); + let block_hash = fragment.pop_front_array().deserialize(); + let account = Account::deserialize(fragment.pop_front_array()); + let values = [fragment.pop_front_array().deserialize()]; + StorageWithinBlock { + block_hash, + account, + values, + } + } +} diff --git a/ethereum_history_api/circuits/lib/src/serialize_test.nr b/ethereum_history_api/circuits/lib/src/serde_test.nr similarity index 76% rename from ethereum_history_api/circuits/lib/src/serialize_test.nr rename to ethereum_history_api/circuits/lib/src/serde_test.nr index 07908992a..7514632d3 100644 --- a/ethereum_history_api/circuits/lib/src/serialize_test.nr +++ b/ethereum_history_api/circuits/lib/src/serde_test.nr @@ -1,6 +1,6 @@ mod U128_ { - use crate::serialize::U128_SERIALIZED_LEN; + use crate::serde::U128_SERIALIZED_LEN; #[test] fn simple() { @@ -9,6 +9,7 @@ mod U128_ { assert_eq(serialized[0], value.lo); assert_eq(serialized[1], value.hi); + assert_eq(U128::deserialize(serialized), value); } } @@ -21,6 +22,7 @@ mod bytes32 { let serialized: [Field; BYTES32_LENGTH] = bytes.serialize(); let expected: [Field; BYTES32_LENGTH] = [1; BYTES32_LENGTH]; assert_eq(serialized, expected); + assert_eq(serialized.deserialize(), bytes); } } @@ -33,28 +35,30 @@ mod address { let serialized: [Field; ADDRESS_LENGTH] = address.serialize(); let expected: [Field; ADDRESS_LENGTH] = [1; ADDRESS_LENGTH]; assert_eq(serialized, expected); + assert_eq(serialized.deserialize(), address); } } mod account { use crate::account_with_storage::Account; - use crate::serialize::ACCOUNT_SERIALIZED_LEN; + use crate::serde::ACCOUNT_LEN; use crate::misc::arrays::sub_array_equals; use crate::fixtures::mainnet::london::vitalik_balance::account::account; #[test] fn simple() { - let serialized: [Field; ACCOUNT_SERIALIZED_LEN] = account.serialize(); + let serialized: [Field; ACCOUNT_LEN] = account.serialize(); assert_eq(serialized[0], account.nonce as Field); assert_eq(serialized[1], account.balance); assert(sub_array_equals(account.storage_root.serialize(), serialized, 2)); assert(sub_array_equals(account.code_hash.serialize(), serialized, 34)); + assert_eq(Account::deserialize(serialized), account); } } mod account_within_block { - use crate::serialize::{ACCOUNT_WITHIN_BLOCK_SERIALIZED_LEN, ACCOUNT_SERIALIZED_LEN}; + use crate::serde::{ACCOUNT_BLOCK_LEN, ACCOUNT_LEN}; use crate::account::AccountWithinBlock; use crate::fixtures::mainnet::london::vitalik_balance::{account::account, header::hash}; use crate::misc::arrays::sub_array_equals; @@ -62,21 +66,22 @@ mod account_within_block { #[test] fn simple() { let account_within_block = AccountWithinBlock { account, block_hash: hash }; - let serialized: [Field; ACCOUNT_WITHIN_BLOCK_SERIALIZED_LEN] = account_within_block.serialize(); + let serialized: [Field; ACCOUNT_BLOCK_LEN] = account_within_block.serialize(); assert(sub_array_equals(account_within_block.account.serialize(), serialized, 0)); assert( sub_array_equals( account_within_block.block_hash.serialize(), serialized, - ACCOUNT_SERIALIZED_LEN + ACCOUNT_LEN ) ); + assert_eq(AccountWithinBlock::deserialize(serialized), account_within_block); } } mod storage_within_block { - use crate::serialize::{STORAGE_WITHIN_BLOCK_1_SERIALIZED_LEN, ACCOUNT_SERIALIZED_LEN}; + use crate::serde::{STORAGE_BLOCK_LEN, ACCOUNT_LEN}; use crate::account_with_storage::StorageWithinBlock; use crate::fixtures::mainnet::paris::usdc_circle::{account::account, header::hash, storage_proof_new::proofs, storage::values}; use crate::misc::{types::BYTES32_LENGTH, arrays::sub_array_equals}; @@ -84,7 +89,7 @@ mod storage_within_block { #[test] fn simple() { let storage_within_block = StorageWithinBlock { block_hash: hash, account, values }; - let serialized: [Field; STORAGE_WITHIN_BLOCK_1_SERIALIZED_LEN] = storage_within_block.serialize(); + let serialized: [Field; STORAGE_BLOCK_LEN] = storage_within_block.serialize(); assert(sub_array_equals(storage_within_block.block_hash.serialize(), serialized, 0)); assert( @@ -98,8 +103,9 @@ mod storage_within_block { sub_array_equals( storage_within_block.values[0].serialize(), serialized, - BYTES32_LENGTH + ACCOUNT_SERIALIZED_LEN + BYTES32_LENGTH + ACCOUNT_LEN ) ); + assert_eq(StorageWithinBlock::deserialize(serialized), storage_within_block); } } diff --git a/ethereum_history_api/circuits/lib/src/serialize.nr b/ethereum_history_api/circuits/lib/src/serialize.nr deleted file mode 100644 index 0cb14cb42..000000000 --- a/ethereum_history_api/circuits/lib/src/serialize.nr +++ /dev/null @@ -1,64 +0,0 @@ -use crate::account::AccountWithinBlock; -use crate::account_with_storage::{StorageWithinBlock, Account}; -use crate::misc::{fragment::Fragment, types::{BYTES32_LENGTH, Bytes32, ADDRESS_LENGTH, Address}}; -use dep::std::unsafe::zeroed; - -trait Serialize { - fn serialize(self) -> [Field; LEN]; -} - -global U128_SERIALIZED_LEN = 2; - -impl Serialize for U128 { - fn serialize(self) -> [Field; U128_SERIALIZED_LEN] { - [self.lo, self.hi] - } -} - -impl Serialize for Bytes32 { - fn serialize(self) -> [Field; BYTES32_LENGTH] { - self.map(|x: u8| x as Field) - } -} - -impl Serialize for Address { - fn serialize(self) -> [Field; ADDRESS_LENGTH] { - self.map(|x: u8| x as Field) - } -} - -global ACCOUNT_SERIALIZED_LEN = 1 + 1 + BYTES32_LENGTH + BYTES32_LENGTH; - -impl Serialize for Account { - fn serialize(self) -> [Field; ACCOUNT_SERIALIZED_LEN] { - let mut data: Fragment = Fragment::empty(); - data.push_back(self.nonce as Field); - data.push_back(self.balance); - data.extend_back(self.storage_root.serialize()); - data.extend_back(self.code_hash.serialize()); - data.to_array() - } -} - -global ACCOUNT_WITHIN_BLOCK_SERIALIZED_LEN = ACCOUNT_SERIALIZED_LEN + BYTES32_LENGTH; - -impl Serialize for AccountWithinBlock { - fn serialize(self) -> [Field; ACCOUNT_WITHIN_BLOCK_SERIALIZED_LEN] { - let mut data: Fragment = Fragment::empty(); - data.extend_back(self.account.serialize()); - data.extend_back(self.block_hash.serialize()); - data.to_array() - } -} - -global STORAGE_WITHIN_BLOCK_1_SERIALIZED_LEN = BYTES32_LENGTH + ACCOUNT_SERIALIZED_LEN + BYTES32_LENGTH; - -impl Serialize for StorageWithinBlock<1> { - fn serialize(self) -> [Field; STORAGE_WITHIN_BLOCK_1_SERIALIZED_LEN] { - let mut data: Fragment = Fragment::empty(); - data.extend_back(self.block_hash.serialize()); - data.extend_back(self.account.serialize()); - data.extend_back(self.values[0].serialize()); - data.to_array() - } -}