Skip to content

Commit

Permalink
Migrate slots to U256 (#328)
Browse files Browse the repository at this point in the history
- Migrate slot library to U256
- Use 256 in ERC721Token & ERC20Token
- Implement From and Into for U256 type (Bytes32)
- Add from_field function for U256
  • Loading branch information
aajj999 authored Jun 10, 2024
2 parents 847bb1b + a367ae4 commit a84b535
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 105 deletions.
35 changes: 35 additions & 0 deletions ethereum/circuits/lib/src/uint256.nr
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use dep::std::ops::Add;
use crate::misc::types::Bytes32;
use crate::misc::bytes32::field_to_bytes32;
use crate::misc::arrays::memcpy_up_to_length;

global uint128_overflow_value = 340282366920938463463374607431768211456; // 2^128

Expand All @@ -19,6 +22,38 @@ impl U256 {
fn one() -> Self {
Self { high: U128::from_integer(0), low: U128::from_integer(1) }
}

fn from_field(field: Field) -> Self {
U256::from(field_to_bytes32(field))
}
}

impl From<Bytes32> for U256 {
fn from(bytes: Bytes32) -> Self {
let mut high_bytes = [0; 16];
memcpy_up_to_length(&mut high_bytes, bytes, 16, 16);
let high = U128::from_le_bytes(high_bytes);

let mut low_bytes = [0; 16];
memcpy_up_to_length(&mut low_bytes, bytes, 0, 16);
let low = U128::from_le_bytes(low_bytes);

U256::new(high, low)
}
}

impl Into<Bytes32> for U256 {
fn into(self) -> Bytes32 {
let mut bytes = [0; 32];
memcpy_up_to_length(&mut bytes, self.low.to_le_bytes(), 0, 16);

let high_bytes = self.high.to_le_bytes();
for i in 0..16 {
bytes[i + 16] = high_bytes[i];
}

bytes
}
}

impl Eq for U256 {
Expand Down
97 changes: 82 additions & 15 deletions ethereum/circuits/lib/src/uint256_test.nr
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,64 @@ fn zero_and_one() {
assert_eq(one.low, U128::one());
}

mod from_bytes32 {
use crate::uint256::U256;
global high = U128::from_integer(0x10000000000000000000000000000000);
global low = U128::zero();
global limit = U128::from_integer(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

global big_number = U256 { high, low };
global limit_u256 = U256 {high: limit, low:limit};

#[test]
fn zero() {
let bytes = [0x00; 32];
assert_eq(U256::from(bytes), U256::zero());
}

#[test]
fn success() {
let mut bytes = [0x00; 32];
bytes[31] = 0x10;
assert_eq(U256::from(bytes), big_number);
}

#[test]
fn u256_limit() {
let bytes = [0xff; 32];
assert_eq(U256::from(bytes), limit_u256);
}
}

mod into_bytes32 {
use crate::uint256::U256;
global high = U128::from_integer(0x10000000000000000000000000000000);
global low = U128::zero();
global limit = U128::from_integer(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

global big_number = U256 { high, low };
global limit_u256 = U256 {high: limit, low:limit};

#[test]
fn zero() {
let bytes = [0x00; 32];
assert_eq(U256::into(U256::zero()), bytes);
}

#[test]
fn success() {
let mut bytes = [0x00; 32];
bytes[31] = 0x10;
assert_eq(U256::into(big_number), bytes);
}

#[test]
fn u256_limit() {
let bytes = [0xff; 32];
assert_eq(U256::into(limit_u256), bytes);
}
}

#[test]
fn eq() {
assert_eq(big_number, big_number);
Expand All @@ -47,23 +105,32 @@ fn not_eq() {
assert(big_number != big_number3);
}

#[test]
fn sum() {
let sum = big_number + big_number;
mod trait_add {
use crate::uint256::U256;
global high = U128::from_integer(0x10000000000000000000000000000000);
global low = U128::zero();
global limit = U128::from_integer(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

assert_eq(sum, U256 { high: U128::from_integer(0x20000000000000000000000000000000), low });
}
global big_number = U256 { high, low };
global limit_u256 = U256 {high: limit, low:limit};

#[test]
fn sum_with_carry() {
let big_number = U256 { high, low: limit };
let sum = big_number + U256::one();
#[test]
fn sum() {
let sum = big_number + big_number;

assert_eq(sum, U256 { high: U128::from_integer(0x10000000000000000000000000000001), low });
}
assert_eq(sum, U256 { high: U128::from_integer(0x20000000000000000000000000000000), low });
}

#[test]
fn sum_with_carry() {
let big_number = U256 { high, low: limit };
let sum = big_number + U256::one();

assert_eq(sum, U256 { high: U128::from_integer(0x10000000000000000000000000000001), low });
}

#[test(should_fail_with="attempt to add with overflow")]
fn sum_overflow() {
let limit_number = U256 { high: limit, low: limit };
let _ = limit_number + U256::one();
#[test(should_fail_with="attempt to add with overflow")]
fn sum_overflow() {
let _ = limit_u256 + U256::one();
}
}
7 changes: 4 additions & 3 deletions vlayer/ethereum/circuits/lib/src/nft.nr
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use dep::ethereum::{
account_with_storage::get_account_with_storage,
misc::types::{Address, Bytes32, ADDRESS_LENGTH, BYTES32_LENGTH}, misc::arrays::subarray_inferred_len
misc::{types::{Address, Bytes32, ADDRESS_LENGTH, BYTES32_LENGTH}, arrays::subarray_inferred_len},
uint256::U256
};

struct ERC721Token {
address: Address,
token_id_to_slot: fn (Bytes32) -> Bytes32,
token_id_to_slot: fn (Bytes32) -> U256,
chain_id: Field
}

Expand All @@ -16,7 +17,7 @@ trait ERC721 {
impl ERC721 for ERC721Token {
fn get_owner(self, token_id: Bytes32, block_number: u64) -> Address {
let storage_key = (self.token_id_to_slot)(token_id);
let account = get_account_with_storage(self.chain_id, block_number, self.address, storage_key);
let account = get_account_with_storage(self.chain_id, block_number, self.address, U256::into(storage_key));
subarray_inferred_len(account.values[0], BYTES32_LENGTH - ADDRESS_LENGTH)
}
}
14 changes: 7 additions & 7 deletions vlayer/ethereum/circuits/lib/src/nft_list.nr
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod mainnet {
use crate::chain_id;
use crate::slot::{struct_slot, dynamic_array_with_precalculated_slot, mapping};
use dep::std::field::bytes32_to_field;
use dep::ethereum::misc::{types::Bytes32, bytes32::field_to_bytes32};
use dep::ethereum::{misc::{types::Bytes32, bytes32::field_to_bytes32}, uint256::U256};

pub fn BORED_APE_YACHT_CLUB() -> ERC721Token {
ERC721Token {
Expand All @@ -12,9 +12,9 @@ mod mainnet {
],
token_id_to_slot: |token_id| {
let BORED_APE_YACHT_CLUB_MAX_TOKEN_ID: u64 = 9999;
let BORED_APE_YACHT_CLUB_TOKEN_OWNERS_INNER_ENTRIES_SLOT = [
let BORED_APE_YACHT_CLUB_TOKEN_OWNERS_INNER_ENTRIES_SLOT = U256::from([
0x40, 0x57, 0x87, 0xfa, 0x12, 0xa8, 0x23, 0xe0, 0xf2, 0xb7, 0x63, 0x1c, 0xc4, 0x1b, 0x3b, 0xa8, 0x82, 0x8b, 0x33, 0x21, 0xca, 0x81, 0x11, 0x11, 0xfa, 0x75, 0xcd, 0x3a, 0xa3, 0xbb, 0x5a, 0xce
];
]);
let VALUE_INDEX = 1;

assert(bytes32_to_field(token_id) as u64 <= BORED_APE_YACHT_CLUB_MAX_TOKEN_ID, "Token ID is too high");
Expand All @@ -32,7 +32,7 @@ mod mainnet {
],
token_id_to_slot: |token_id| {
let CRYPTO_PUNK_MAX_TOKEN_ID: u64 = 9999;
let CRYPTO_PUNK_TOKEN_OWNERS_INNER_ENTRIES_SLOT = field_to_bytes32(10);
let CRYPTO_PUNK_TOKEN_OWNERS_INNER_ENTRIES_SLOT = U256::from_field(10);

assert(bytes32_to_field(token_id) as u64 <= CRYPTO_PUNK_MAX_TOKEN_ID, "Token ID is too high");
mapping(CRYPTO_PUNK_TOKEN_OWNERS_INNER_ENTRIES_SLOT, token_id)
Expand All @@ -47,7 +47,7 @@ mod sepolia {
use crate::chain_id;
use crate::slot::{struct_slot, dynamic_array_with_precalculated_slot};
use dep::std::field::bytes32_to_field;
use dep::ethereum::misc::{types::Bytes32, bytes32::field_to_bytes32};
use dep::ethereum::{misc::{types::Bytes32, bytes32::field_to_bytes32}, uint256::U256};

// free mint: https://sepolia.etherscan.io/address/0x80d97726548fedae6ad7cf8df4f2b514fd24afba#readContract
fn FAKE_BORED_APE_YACHT_CLUB() -> ERC721Token {
Expand All @@ -57,9 +57,9 @@ mod sepolia {
],
token_id_to_slot: |token_id| {
let BORED_APE_YACHT_CLUB_MAX_TOKEN_ID: u64 = 9999;
let BORED_APE_YACHT_CLUB_TOKEN_OWNERS_INNER_ENTRIES_SLOT = [
let BORED_APE_YACHT_CLUB_TOKEN_OWNERS_INNER_ENTRIES_SLOT = U256::from([
0x40, 0x57, 0x87, 0xfa, 0x12, 0xa8, 0x23, 0xe0, 0xf2, 0xb7, 0x63, 0x1c, 0xc4, 0x1b, 0x3b, 0xa8, 0x82, 0x8b, 0x33, 0x21, 0xca, 0x81, 0x11, 0x11, 0xfa, 0x75, 0xcd, 0x3a, 0xa3, 0xbb, 0x5a, 0xce
];
]);
let VALUE_INDEX = 1;

assert(bytes32_to_field(token_id) as u64 <= BORED_APE_YACHT_CLUB_MAX_TOKEN_ID, "Token ID is too high");
Expand Down
24 changes: 11 additions & 13 deletions vlayer/ethereum/circuits/lib/src/slot.nr
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
use dep::ethereum::misc::{
types::{Address, Bytes32, ADDRESS_LENGTH, BYTES32_LENGTH}, bytes32::field_to_bytes32,
bytes::add_bigint
};
use dep::ethereum::misc::types::Bytes32;
use dep::std::hash::keccak256;
use dep::ethereum::uint256::U256;

global STORAGE_KEY_HASH_INPUT_LENGTH = 64;

pub(crate) fn mapping(slot: Bytes32, key: Bytes32) -> Bytes32 {
pub(crate) fn mapping(slot: U256, key: Bytes32) -> U256 {
let mut vector: BoundedVec<u8, STORAGE_KEY_HASH_INPUT_LENGTH> = BoundedVec::new();
vector.extend_from_array(key);
vector.extend_from_array(slot);
keccak256(vector.storage(), STORAGE_KEY_HASH_INPUT_LENGTH)
vector.extend_from_array(U256::into(slot));
U256::from(keccak256(vector.storage(), STORAGE_KEY_HASH_INPUT_LENGTH))
}

pub(crate) fn dynamic_array(slot: Bytes32, size: Field, index: Field) -> Bytes32 {
let start = keccak256(slot, 32);
pub(crate) fn dynamic_array(slot: U256, size: Field, index: Field) -> U256 {
let start: U256 = U256::from(keccak256(U256::into(slot), 32));
dynamic_array_with_precalculated_slot(start, size, index)
}

pub(crate) fn dynamic_array_with_precalculated_slot(slot: Bytes32, size: Field, index: Field) -> Bytes32 {
add_bigint(slot, field_to_bytes32(size * index))
pub(crate) fn dynamic_array_with_precalculated_slot(slot: U256, size: Field, index: Field) -> U256 {
slot + U256::from_field(size * index)
}

pub(crate) fn struct_slot(slot: Bytes32, offset: Field) -> Bytes32 {
add_bigint(slot, field_to_bytes32(offset))
pub(crate) fn struct_slot(slot: U256, offset: Field) -> U256 {
slot + U256::from_field(offset)
}
Loading

0 comments on commit a84b535

Please sign in to comment.