Skip to content

Commit

Permalink
Write assert_storage_key_equals & get_storage_value functions in acco…
Browse files Browse the repository at this point in the history
…unt_with_storage.nr
  • Loading branch information
Pasifaee committed May 24, 2024
1 parent dec7bf8 commit 96b2988
Show file tree
Hide file tree
Showing 11 changed files with 80 additions and 78 deletions.
6 changes: 1 addition & 5 deletions ethereum/circuits/lib/src/account_int_test.nr
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,5 @@ fn test_get_account_wrong_state_root() {
let _ = OracleMock::mock("get_header").returns((block_header_partial, block_header_rlp));
let _ = OracleMock::mock("get_account").returns((account_from_different_header, state_proof_input_from_different_header_serialized));

let _ = get_account(
ETHEREUM_MAINNET_ID,
number,
address_from_different_header
);
let _ = get_account(ETHEREUM_MAINNET_ID, number, address_from_different_header);
}
50 changes: 44 additions & 6 deletions ethereum/circuits/lib/src/account_with_storage.nr
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use crate::header::{get_header, BlockHeaderPartial};
use crate::account::{MAX_ACCOUNT_STATE_LEN, MAX_ACCOUNT_DEPTH_NO_LEAF_M, MAX_ACCOUNT_LEAF_LEN};
use crate::misc::{types::{Address, Bytes32}, bytes::right_pad, bounded_vecs::bounded_vec_eq};
use crate::misc::{types::{Address, Bytes32, BYTES32_LENGTH, HASH_LEN}, bytes::right_pad, fragment::Fragment};
use crate::serde::Serde;
use crate::verifiers::account::verify_account;
use crate::verifiers::storage::verify_storage_values;
use crate::merkle_patricia_proofs::proof::ProofInput;
use crate::rlp::decode::decode_string;

use dep::std::hash::keccak256;

global MAX_KEY_LEN = 32;
global MAX_PREFIXED_KEY_NIBBLE_LEN = 66; // (MAX_KEY_LEN + 1) * 2
Expand Down Expand Up @@ -48,12 +52,42 @@ struct StorageWithinBlock<N> {
values: [Bytes32; N],
}

type ProofInputSerialized<LEN> = [Field; LEN];

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])
}
}

fn assert_storage_key_equals(
storage_key: Bytes32,
storage_key_hash: [u8; MAX_PREFIXED_KEY_NIBBLE_LEN]
) {
let storage_key_hash_fragment = Fragment::new(
MAX_PREFIXED_KEY_NIBBLE_LEN - HASH_LEN,
HASH_LEN,
storage_key_hash
);
let other_storage_key_hash_fragment = Fragment::from_array(keccak256(storage_key, BYTES32_LENGTH as u32));
assert(
storage_key_hash_fragment.eq(other_storage_key_hash_fragment), "Storage key does not match the argument"
);
}

fn get_storage_value(rlp_encoded_value: [u8; MAX_STORAGE_VALUE_LEN]) -> [u8; MAX_STORAGE_VALUE_LEN] {
let mut storage_value = rlp_encoded_value;
let rlp_value_len = right_pad(rlp_encoded_value).len();
let left_pad_len = MAX_STORAGE_VALUE_LEN - rlp_value_len;
let rlp_fragment = decode_string(Fragment::new(left_pad_len, rlp_value_len, rlp_encoded_value));
if rlp_fragment.offset > 0 {
assert_eq(rlp_fragment.offset, 1, "Expected RLP header to be maximum 1 byte long");
storage_value[left_pad_len] = 0;
}

storage_value
}

pub fn get_account_with_storage(
chain_id: Field,
block_number: u64,
Expand All @@ -66,24 +100,28 @@ pub fn get_account_with_storage(
verify_account(address, account, state_proof_input, state_root);
verify_storage_values([storage_proof_input], account.storage_root);

assert(bounded_vec_eq(right_pad(storage_key), right_pad(storage_proof_input.key)), "Storage key does not match the argument");
assert_storage_key_equals(storage_key, storage_proof_input.key);

StorageWithinBlock { block_hash: hash, account, values: [storage_proof_input.value] }
StorageWithinBlock { block_hash: hash, account, values: [get_storage_value(storage_proof_input.value)] }
}

#[oracle(get_proof)]
unconstrained fn get_proof_oracle(
unconstrained fn get_proof_oracle<STATE_PROOF_INPUT_LEN, STORAGE_PROOF_INPUT>(
_chain_id: Field,
_block_no: u64,
_address: Address,
_storage_key: Bytes32
) -> StateAndStorageProofInput {}
) -> (Account, ProofInputSerialized<STATE_PROOF_INPUT_LEN>, ProofInputSerialized<STORAGE_PROOF_INPUT>) {}

unconstrained fn get_proof_unconstrained(
chain_id: Field,
block_no: u64,
address: Address,
storage_key: Bytes32
) -> StateAndStorageProofInput {
get_proof_oracle(chain_id, block_no, address, storage_key)
let (account, state_proof_input, storage_proof_input) = get_proof_oracle(chain_id, block_no, address, storage_key);
let state_proof_input: ProofInput<MAX_PREFIXED_KEY_NIBBLE_LEN, MAX_ACCOUNT_STATE_LEN, MAX_ACCOUNT_DEPTH_NO_LEAF_M, MAX_ACCOUNT_LEAF_LEN> = Serde::deserialize(state_proof_input);
let storage_proof_input: ProofInput<MAX_PREFIXED_KEY_NIBBLE_LEN, MAX_STORAGE_VALUE_LEN, MAX_STORAGE_DEPTH_NO_LEAF_M, MAX_STORAGE_LEAF_LEN> = Serde::deserialize(storage_proof_input);

StateAndStorageProofInput { account, state_proof_input, storage_proof_input }
}
17 changes: 9 additions & 8 deletions ethereum/circuits/lib/src/account_with_storage_int_test.nr
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use crate::fixtures::mainnet::{
paris::{
usdc_circle::{
header::{number, block_header_partial, block_header_rlp}, account::{account, address},
state_proof::state_proof, storage_proof::proofs, storage::{values, keys as storage_keys}
state_proof_new::proof_input_serialized as state_proof_input_serialized,
storage_proof_new::proofs_serialized, storage::{values, keys as storage_keys}
},
usdc_uniswap::{storage::keys as usdc_uniswap_storage_keys}
},
Expand All @@ -16,7 +17,7 @@ use crate::fixtures::mainnet::{
block_header_rlp as crypto_punks_block_header_rlp, number as crypto_punks_number
},
account::{address as crypto_punks_address, account as crypto_punks_account},
state_proof::state_proof as crypto_punks_state_proof
state_proof_new::proof_input_serialized as crypto_punks_state_proof_input_serialized
}
}
};
Expand All @@ -25,7 +26,7 @@ use dep::std::test::OracleMock;
#[test]
fn test_get_account_with_storage_success() {
let _ = OracleMock::mock("get_header").returns((block_header_partial, block_header_rlp));
let _ = OracleMock::mock("get_proof").returns((account, state_proof, proofs[0]));
let _ = OracleMock::mock("get_proof").returns((account, state_proof_input_serialized, proofs_serialized[0]));

let account_with_storage = get_account_with_storage(ETHEREUM_MAINNET_ID, number, address, storage_keys[0]);

Expand All @@ -39,10 +40,10 @@ fn test_get_account_with_storage_success() {
assert_eq(values[0], account_with_storage.values[0]);
}

#[test(should_fail_with = "Internal node hash does not match the hash extracted from the preceding node")]
#[test(should_fail_with = "Invalid node hash")]
fn test_get_account_with_storage_invalid_state_root() {
let _ = OracleMock::mock("get_header").returns((crypto_punks_block_header_partial, crypto_punks_block_header_rlp));
let _ = OracleMock::mock("get_proof").returns((account, state_proof, proofs[0]));
let _ = OracleMock::mock("get_proof").returns((account, state_proof_input_serialized, proofs_serialized[0]));

let _ = get_account_with_storage(
ETHEREUM_MAINNET_ID,
Expand All @@ -52,10 +53,10 @@ fn test_get_account_with_storage_invalid_state_root() {
);
}

#[test(should_fail_with = "Internal node hash does not match the hash extracted from the preceding node")]
#[test(should_fail_with = "Invalid node hash")]
fn test_get_account_with_storage_invalid_storage_root() {
let _ = OracleMock::mock("get_header").returns((crypto_punks_block_header_partial, crypto_punks_block_header_rlp));
let _ = OracleMock::mock("get_proof").returns((crypto_punks_account, crypto_punks_state_proof, proofs[0]));
let _ = OracleMock::mock("get_proof").returns((crypto_punks_account, crypto_punks_state_proof_input_serialized, proofs_serialized[0]));

let _ = get_account_with_storage(
ETHEREUM_MAINNET_ID,
Expand All @@ -68,7 +69,7 @@ fn test_get_account_with_storage_invalid_storage_root() {
#[test(should_fail_with = "Storage key does not match the argument")]
fn test_get_account_with_storage_storage_key_does_not_match_the_argument() {
let _ = OracleMock::mock("get_header").returns((block_header_partial, block_header_rlp));
let _ = OracleMock::mock("get_proof").returns((account, state_proof, proofs[0]));
let _ = OracleMock::mock("get_proof").returns((account, state_proof_input_serialized, proofs_serialized[0]));

let _ = get_account_with_storage(
ETHEREUM_MAINNET_ID,
Expand Down
15 changes: 0 additions & 15 deletions ethereum/circuits/lib/src/misc/bounded_vecs.nr
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,6 @@ pub fn bounded_vec_from_array<T, N, M>(array: [T; N]) -> BoundedVec<T, M> {
vec
}

pub fn bounded_vec_eq<T, N, M>(vec: BoundedVec<T, N>, other_vec: BoundedVec<T, M>) -> bool where T: Eq{
let mut res = true;
if vec.len != other_vec.len {
res = false;
}

for i in 0..N {
if i < vec.len & i < other_vec.len {
res &= vec.get(i) == other_vec.get(i);
}
}

true
}

pub fn bounded_vec_map<U, T, N, Env>(
bounded_vec: BoundedVec<T, N> ,
f: fn[Env](T) -> U
Expand Down
26 changes: 0 additions & 26 deletions ethereum/circuits/lib/src/misc/bounded_vecs_test.nr
Original file line number Diff line number Diff line change
Expand Up @@ -28,32 +28,6 @@ mod bounded_vec_from_array {
}
}

mod bounded_vec_eq {
use crate::misc::bounded_vecs::{bounded_vec_eq, bounded_vec_from_array};

#[test]
fn eq() {
let vec1 = bounded_vec_from_array([1, 2, 3]);
let mut vec2: BoundedVec<Field, 4> = BoundedVec::new();
vec2.push(1);
vec2.push(2);
vec2.push(3);
assert(bounded_vec_eq(vec1, vec2));
assert(bounded_vec_eq(vec2, vec1));
}

#[test]
fn not_eq() {
let vec1 = bounded_vec_from_array([1, 2, 4]);
let mut vec2: BoundedVec<Field, 4> = BoundedVec::new();
vec2.push(1);
vec2.push(2);
vec2.push(3);
assert(!bounded_vec_eq(vec1, vec2));
assert(!bounded_vec_eq(vec2, vec1));
}
}

mod bounded_vec_map {
use crate::misc::bounded_vecs::{bounded_vec_from_array, bounded_vec_map};

Expand Down
18 changes: 13 additions & 5 deletions ethereum/circuits/lib/src/verifiers/account.nr
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use crate::account::{Account, MAX_PREFIXED_KEY_NIBBLE_LEN, MAX_ACCOUNT_STATE_LEN, MAX_ACCOUNT_LEAF_LEN};
use crate::misc::{types::Address, bytes::right_pad, fragment::Fragment, bounded_vecs::bounded_vec_eq};
use crate::misc::{types::{Address, ADDRESS_LENGTH}, bytes::right_pad, fragment::Fragment};
use crate::rlp::decode::decode_list_of_small_strings;
use crate::rlp::types::RlpList;
use crate::merkle_patricia_proofs::proof::{ProofInput, verify_merkle_proof};

use crate::HASH_LEN;

use dep::std::hash::keccak256;

global ACCOUNT_FIELDS_COUNT = 4;
global NONCE_INDEX = 0;
global BALANCE_INDEX = 1;
Expand All @@ -25,8 +26,10 @@ pub(crate) fn assert_account_equals(account_rlp_left_padded: [u8; MAX_ACCOUNT_ST
account_rlp_list.get(CODE_HASH_INDEX).assert_eq_bytes32("Code hash", account_rlp, account.code_hash);
}

fn assert_address_equals(address1: [u8; MAX_PREFIXED_KEY_NIBBLE_LEN], address2: Address) {
assert(bounded_vec_eq(right_pad(address1), right_pad(address2)), "Address mismatch");
fn assert_address_equals(address_hash: [u8; MAX_PREFIXED_KEY_NIBBLE_LEN], address: Address) {
let address_hash_fragment = Fragment::new(MAX_PREFIXED_KEY_NIBBLE_LEN - HASH_LEN, HASH_LEN, address_hash);
let other_address_hash_fragment = Fragment::from_array(keccak256(address, ADDRESS_LENGTH as u32));
assert(address_hash_fragment.eq(other_address_hash_fragment), "Address mismatch");
}

pub fn verify_account<MAX_DEPTH_NO_LEAF>(
Expand All @@ -37,5 +40,10 @@ pub fn verify_account<MAX_DEPTH_NO_LEAF>(
) {
assert_address_equals(state_proof_input.key, address);
assert_account_equals(state_proof_input.value, account);
verify_merkle_proof(state_proof_input.key, state_proof_input.value, state_root, state_proof_input.proof);
verify_merkle_proof(
state_proof_input.key,
state_proof_input.value,
state_root,
state_proof_input.proof
);
}
2 changes: 1 addition & 1 deletion ethereum/circuits/lib/src/verifiers/storage_test.nr
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ fn test_verify_storage_values_invalid_storage_root() {
root[0] += 1;

let _ = verify_storage_values(proofs, root);
}
}
6 changes: 3 additions & 3 deletions vlayer/ethereum/circuits/lib/src/token_int_test.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ mod test_ERC20Token {
use dep::ethereum::fixtures::mainnet::{
paris::usdc_circle::header::{block_header_partial as paris_block_header_partial, block_header_rlp as paris_block_header_rlp},
paris::usdc_circle::header::{hash, number, state_root, transactions_root, receipts_root},
paris::usdc_circle::account::account, paris::usdc_circle::state_proof::state_proof,
paris::usdc_circle::storage_proof::proofs
paris::usdc_circle::account::account, paris::usdc_circle::state_proof_new::proof_input_serialized as state_proof_input_serialized,
paris::usdc_circle::storage_proof_new::proofs_serialized
};
use crate::chain_id::MAINNET;

#[test]
fn success() {
let _ = OracleMock::mock("get_header").returns((paris_block_header_partial, paris_block_header_rlp));
let _ = OracleMock::mock("get_proof").returns((account, state_proof, proofs[0]));
let _ = OracleMock::mock("get_proof").returns((account, state_proof_input_serialized, proofs_serialized[0]));

let usdc_token = ERC20Token {
address: [
Expand Down
6 changes: 3 additions & 3 deletions vlayer/examples/circuits/is_ape_owner/src/main_test.nr
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod is_ape_owner_main {
use dep::ethereum::{
fixtures::mainnet::paris::bored_ape_yacht_club::{
header::{block_header_partial as paris_block_header_partial, block_header_rlp as paris_block_header_rlp},
account::account, state_proof::state_proof, storage_proof::proofs
account::account, state_proof_new::proof_input_serialized as state_proof_input_serialized, storage_proof_new::proofs_serialized
},
misc::arrays::alter_array
};
Expand All @@ -17,7 +17,7 @@ mod is_ape_owner_main {
#[test]
fn success() {
let _ = OracleMock::mock("get_header").returns((paris_block_header_partial, paris_block_header_rlp));
let _ = OracleMock::mock("get_proof").returns((account, state_proof, proofs[0]));
let _ = OracleMock::mock("get_proof").returns((account, state_proof_input_serialized, proofs_serialized[0]));

let token_id = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7
Expand All @@ -28,7 +28,7 @@ mod is_ape_owner_main {
#[test(should_fail_with = "Owner is not the same as the wallet address")]
fn invalid_wallet() {
let _ = OracleMock::mock("get_header").returns((paris_block_header_partial, paris_block_header_rlp));
let _ = OracleMock::mock("get_proof").returns((account, state_proof, proofs[0]));
let _ = OracleMock::mock("get_proof").returns((account, state_proof_input_serialized, proofs_serialized[0]));

let token_id = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod is_crypto_punk_owner_main {
use dep::ethereum::{
fixtures::mainnet::london::crypto_punks::{
header::{block_header_partial as london_block_header_partial, block_header_rlp as london_block_header_rlp},
account::account, state_proof::state_proof, storage_proof::proofs
account::account, state_proof_new::proof_input_serialized as state_proof_input_serialized, storage_proof_new::proofs_serialized
},
misc::arrays::alter_array
};
Expand All @@ -17,15 +17,15 @@ mod is_crypto_punk_owner_main {
#[test]
fn success() {
let _ = OracleMock::mock("get_header").returns((london_block_header_partial, london_block_header_rlp));
let _ = OracleMock::mock("get_proof").returns((account, state_proof, proofs[0]));
let _ = OracleMock::mock("get_proof").returns((account, state_proof_input_serialized, proofs_serialized[0]));
let token_id = 9;
main(CRYPTO_PUNK_OWNER_WALLER_ADDRESS, token_id, BLOCK_NUMBER);
}

#[test(should_fail_with = "Owner is not the same as the wallet address")]
fn invalid_wallet() {
let _ = OracleMock::mock("get_header").returns((london_block_header_partial, london_block_header_rlp));
let _ = OracleMock::mock("get_proof").returns((account, state_proof, proofs[0]));
let _ = OracleMock::mock("get_proof").returns((account, state_proof_input_serialized, proofs_serialized[0]));

let token_id = 9;
main(
Expand Down
6 changes: 3 additions & 3 deletions vlayer/examples/circuits/is_dao_worthy/src/main_test.nr
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ mod is_dao_worthy_main {
use dep::std::test::OracleMock;
use dep::ethereum::fixtures::mainnet::{
paris::usdc_circle::header::{block_header_partial as paris_block_header_partial, block_header_rlp as paris_block_header_rlp},
paris::usdc_circle::account::account, paris::usdc_circle::state_proof::state_proof,
paris::usdc_circle::storage_proof::proofs
paris::usdc_circle::account::account, paris::usdc_circle::state_proof_new::proof_input_serialized as state_proof_input_serialized,
paris::usdc_circle::storage_proof_new::proofs_serialized
};
use crate::main;

#[test]
fn success_greater_then() {
let _ = OracleMock::mock("get_header").returns((paris_block_header_partial, paris_block_header_rlp));
let _ = OracleMock::mock("get_proof").returns((account, state_proof, proofs[0]));
let _ = OracleMock::mock("get_proof").returns((account, state_proof_input_serialized, proofs_serialized[0]));

let block_number = 19_000_000;
let circle_address = [
Expand Down

0 comments on commit 96b2988

Please sign in to comment.