Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Account and Storage to merkle_patricia_proofs #316

Merged
merged 11 commits into from
May 28, 2024
46 changes: 35 additions & 11 deletions ethereum/circuits/lib/src/account.nr
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
use crate::account_with_storage::{Account, StateProof};
use crate::account_with_storage::Account;
use crate::header::get_header;
use crate::misc::types::{Address, Bytes32, BYTES32_LENGTH};
use crate::verifiers::account::verify_account;
use crate::serde::Serde;
use crate::merkle_patricia_proofs::proof::ProofInput;

global MAX_KEY_LEN = 32;
global MAX_PREFIXED_KEY_NIBBLE_LEN = 66; // (MAX_KEY_LEN + 1) * 2
global MAX_ACCOUNT_DEPTH_NO_LEAF_M = 10; // Emperically correct values to be determined after we scan ethereum state trie.

global MAX_ACCOUNT_STATE_LEN = 110; // Values taken from accountProofConfig in account.ts.
global MAX_ACCOUNT_LEAF_LEN = 148;

global LEGACY_MAX_ACCOUNT_STATE_LEN = 134; // Legacy, incorrect (too big) limit.
global LEGACY_STATE_PROOF_LEN = 5852; // = 11 (MAX_STATE_PROOF_LEVELS) * 532 (MAX_TRIE_NODE_LEN)

struct AccountWithinBlock {
account: Account,
Expand All @@ -14,22 +26,34 @@ impl Eq for AccountWithinBlock {
}
}

type AccountWithStateProof = (Account, StateProof);
struct LegacyStateProof {
key: Address,
value: [u8; MAX_ACCOUNT_STATE_LEN],
proof: [u8; LEGACY_STATE_PROOF_LEN],
depth: u64,
}

type AccountWithStateProofM = (Account, ProofInput<MAX_PREFIXED_KEY_NIBBLE_LEN, MAX_ACCOUNT_STATE_LEN, MAX_ACCOUNT_DEPTH_NO_LEAF_M, MAX_ACCOUNT_LEAF_LEN>);

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

pub fn get_account(chain_id: Field, block_no: u64, address: Address) -> AccountWithinBlock {
let (account, state_proof) = get_account_unconstrained(chain_id, block_no, address);
let (account, state_proof) = get_account_unconstrained_M(chain_id, block_no, address);
let header = get_header(chain_id, block_no);
verify_account(address, account, state_proof, header.state_root);
AccountWithinBlock { account, block_hash: header.hash }
}

#[oracle(get_account)]
unconstrained fn get_account_oracle(_chain_id: Field, _block_no: u64, _address: [u8; 20]) -> AccountWithStateProof {}

unconstrained fn get_account_unconstrained(
chain_id: Field,
block_no: u64,
address: Address
) -> AccountWithStateProof {
get_account_oracle(chain_id, block_no, address)
unconstrained fn get_account_oracle<PROOF_INPUT_LEN>(
_chain_id: Field,
_block_no: u64,
_address: [u8; 20]
) -> (Account, ProofInputSerialized<PROOF_INPUT_LEN>) {}

unconstrained fn get_account_unconstrained_M(chain_id: Field, block_no: u64, address: Address) -> AccountWithStateProofM {
let (account, proof_input) = get_account_oracle(chain_id, block_no, address);
let proof_input = Serde::deserialize(proof_input);

(account, proof_input)
}
20 changes: 8 additions & 12 deletions ethereum/circuits/lib/src/account_int_test.nr
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@ use crate::chain::ETHEREUM_MAINNET_ID;
use crate::fixtures::mainnet::{
paris::{
usdc_circle::{
header::{number, state_root, block_header_partial, block_header_rlp}, account::account,
state_proof::state_proof
header::{number, state_root, block_header_partial, block_header_rlp}, account::{account, address},
state_proof_new::proof_input_serialized as state_proof_input_serialized
}
},
london::vitalik_balance::{
account::account as account_from_different_header,
account::{account as account_from_different_header, address as address_from_different_header},
header::state_root as state_root_from_different_header,
state_proof::state_proof as state_proof_from_different_header
state_proof_new::proof_input_serialized as state_proof_input_from_different_header_serialized
}
};
use dep::std::test::OracleMock;

#[test]
fn test_get_account_success() {
let _ = OracleMock::mock("get_header").returns((block_header_partial, block_header_rlp));
let _ = OracleMock::mock("get_account").returns((account, state_proof));
let _ = OracleMock::mock("get_account").returns((account, state_proof_input_serialized));

let account_within_block = get_account(ETHEREUM_MAINNET_ID, number, state_proof.key);
let account_within_block = get_account(ETHEREUM_MAINNET_ID, number, address);

assert_eq(account.nonce, account_within_block.account.nonce);
assert_eq(account.balance, account_within_block.account.balance);
Expand All @@ -33,11 +33,7 @@ fn test_get_account_success() {
#[test(should_fail)]
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_from_different_header));
let _ = OracleMock::mock("get_account").returns((account_from_different_header, state_proof_input_from_different_header_serialized));

let _ = get_account(
ETHEREUM_MAINNET_ID,
number,
state_proof_from_different_header.key
);
let _ = get_account(ETHEREUM_MAINNET_ID, number, address_from_different_header);
}
97 changes: 71 additions & 26 deletions ethereum/circuits/lib/src/account_with_storage.nr
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
use dep::proof::const::MAX_ACCOUNT_STATE_LENGTH;

use crate::header::{get_header, BlockHeaderPartial};
use crate::misc::types::{Address, Bytes32};
use crate::account::{MAX_ACCOUNT_STATE_LEN, MAX_ACCOUNT_DEPTH_NO_LEAF_M, MAX_ACCOUNT_LEAF_LEN};
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
global MAX_STORAGE_DEPTH_NO_LEAF_M = 6; // Emperically correct values to be determined after we scan ethereum state trie.

global STATE_PROOF_LEN = 5852; // = 11 (MAX_STATE_PROOF_LEVELS) * 532 (MAX_TRIE_NODE_LEN)
global STORAGE_PROOF_LEN = 3724; // = 7 (MAX_STORAGE_PROOF_LEVELS) * 532 (MAX_TRIE_NODE_LEN)
global MAX_STORAGE_VALUE_LEN = 32; // Values taken from storageProofConfig in storage.ts.
global MAX_STORAGE_LEAF_LEN = 69;

Pasifaee marked this conversation as resolved.
Show resolved Hide resolved
global LEGACY_STORAGE_PROOF_LEN = 3724; // = 7 (MAX_STORAGE_PROOF_LEVELS) * 532 (MAX_TRIE_NODE_LEN)

struct Account {
nonce: u64,
Expand All @@ -21,26 +31,19 @@ impl Eq for Account {
}
}

struct StateProof {
key: Address,
value: [u8; MAX_ACCOUNT_STATE_LENGTH],
proof: [u8; STATE_PROOF_LEN],
depth: u64,
}

struct StorageProof {
struct LegacyStorageProof {
key: Bytes32,
value: Bytes32,
proof: [u8; STORAGE_PROOF_LEN],
proof: [u8; LEGACY_STORAGE_PROOF_LEN],
depth: u64
}

// For now oracle does not support returning array of arrays so at the moment we support only one storage proof.
// When https://github.com/noir-lang/noir/issues/4498 is resolved we can change to `StateAndStorageProof<N>` and `storage_proof: [StorageProof; N]`.
struct StateAndStorageProof {
struct StateAndStorageProofInput {
account: Account,
state_proof: StateProof,
storage_proof: StorageProof
state_proof_input: ProofInput<MAX_PREFIXED_KEY_NIBBLE_LEN, MAX_ACCOUNT_STATE_LEN, MAX_ACCOUNT_DEPTH_NO_LEAF_M, MAX_ACCOUNT_LEAF_LEN>,
storage_proof_input: ProofInput<MAX_PREFIXED_KEY_NIBBLE_LEN, MAX_STORAGE_VALUE_LEN, MAX_STORAGE_DEPTH_NO_LEAF_M, MAX_STORAGE_LEAF_LEN>
}

struct StorageWithinBlock<N> {
Expand All @@ -49,42 +52,84 @@ 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_fragment<N>(left_padded_value: [u8; N]) -> Fragment<N, u8> {
let value_len = right_pad(left_padded_value).len();
let value_offset = N - value_len;
Fragment::new(value_offset, value_len, left_padded_value)
}

fn get_storage_value(rlp_encoded_value: [u8; MAX_STORAGE_VALUE_LEN]) -> [u8; MAX_STORAGE_VALUE_LEN] {
let mut storage_value = get_fragment(rlp_encoded_value);
let rlp_fragment = decode_string(storage_value);
let rlp_header_len = rlp_fragment.offset;

// Storage value is maximum 32 bytes long, so its RLP-encoding's header is maximum 1 byte long.
assert(rlp_header_len <= 1, "Expected RLP header to be maximum 1 byte long");
if rlp_fragment.offset == 1 {
let rlp_header_position = storage_value.offset;
storage_value.data[rlp_header_position] = 0;
}

storage_value.data
}

pub fn get_account_with_storage(
chain_id: Field,
block_number: u64,
address: Address,
storage_key: Bytes32
) -> StorageWithinBlock<1> {
let BlockHeaderPartial { number: _, hash, state_root, transactions_root: _, receipts_root: _ } = get_header(chain_id, block_number);
let StateAndStorageProof { account, state_proof, storage_proof } = get_proof_unconstrained(chain_id, block_number, address, storage_key);
let StateAndStorageProofInput { account, state_proof_input, storage_proof_input } = get_proof_unconstrained(chain_id, block_number, address, storage_key);

verify_account(address, account, state_proof, state_root);
verify_storage_values(account.storage_root, [storage_proof]);
verify_account(address, account, state_proof_input, state_root);
verify_storage_values([storage_proof_input], account.storage_root);

assert(storage_key == storage_proof.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.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
) -> StateAndStorageProof {}
) -> (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
) -> StateAndStorageProof {
get_proof_oracle(chain_id, block_no, address, storage_key)
) -> StateAndStorageProofInput {
let (account, state_proof_input, storage_proof_input) = get_proof_oracle(chain_id, block_no, address, storage_key);
let state_proof_input = Serde::deserialize(state_proof_input);
let storage_proof_input = Serde::deserialize(storage_proof_input);

StateAndStorageProofInput { account, state_proof_input, storage_proof_input }
}
19 changes: 10 additions & 9 deletions ethereum/circuits/lib/src/account_with_storage_int_test.nr
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use crate::misc::types::Bytes32;
use crate::chain::ETHEREUM_MAINNET_ID;
use crate::account_with_storage::{StorageProof, get_account_with_storage};
use crate::account_with_storage::{LegacyStorageProof, get_account_with_storage};
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ global address = [
];

global rlp_encoded_left_padded_account = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x4e, 0x80, 0x8a, 0x15, 0x2d, 0x02, 0xc7, 0xe1, 0x4a, 0xf6, 0x80, 0x00, 0x00, 0xa0, 0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21, 0xa0, 0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x4e, 0x80, 0x8a, 0x15, 0x2d, 0x02, 0xc7, 0xe1, 0x4a, 0xf6, 0x80, 0x00, 0x00, 0xa0, 0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21, 0xa0, 0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70
];

global nonce = 0;
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,5 @@ global proof_input = ProofInput {
depth: 5
}
};

global proof_input_serialized = proof_input.serialize();
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ global address = [
];

global rlp_encoded_left_padded_account = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x4d, 0x80, 0x89, 0x0a, 0xd7, 0x8e, 0xbc, 0x5a, 0xc6, 0x20, 0x00, 0x00, 0xa0, 0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21, 0xa0, 0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x4d, 0x80, 0x89, 0x0a, 0xd7, 0x8e, 0xbc, 0x5a, 0xc6, 0x20, 0x00, 0x00, 0xa0, 0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21, 0xa0, 0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70
];

global nonce = 0;
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,5 @@ global proof_input = ProofInput {
depth: 5
}
};

global proof_input_serialized = proof_input.serialize();
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ global address = [
];

global rlp_encoded_left_padded_account = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x4e, 0x01, 0x8a, 0x03, 0x13, 0x57, 0x0a, 0x84, 0xbf, 0x37, 0x8e, 0xfd, 0x25, 0xa0, 0xae, 0x27, 0x92, 0x24, 0x44, 0x17, 0xbc, 0x17, 0x49, 0xb9, 0xcd, 0x9a, 0x0b, 0xdc, 0x1c, 0x4a, 0x6c, 0xf3, 0x2f, 0x14, 0x7b, 0x37, 0x20, 0x2c, 0x8c, 0xb3, 0x59, 0x07, 0x77, 0x65, 0x9a, 0xec, 0xa0, 0xe2, 0xe7, 0xa7, 0x52, 0x4a, 0x98, 0xce, 0x62, 0x9e, 0xe4, 0x06, 0xc1, 0x5c, 0x51, 0xa6, 0x83, 0xe4, 0x16, 0x7f, 0x0b, 0x74, 0xea, 0x23, 0x05, 0x66, 0xdd, 0xec, 0xe7, 0xae, 0x9d, 0x6f, 0x0b
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x4e, 0x01, 0x8a, 0x03, 0x13, 0x57, 0x0a, 0x84, 0xbf, 0x37, 0x8e, 0xfd, 0x25, 0xa0, 0xae, 0x27, 0x92, 0x24, 0x44, 0x17, 0xbc, 0x17, 0x49, 0xb9, 0xcd, 0x9a, 0x0b, 0xdc, 0x1c, 0x4a, 0x6c, 0xf3, 0x2f, 0x14, 0x7b, 0x37, 0x20, 0x2c, 0x8c, 0xb3, 0x59, 0x07, 0x77, 0x65, 0x9a, 0xec, 0xa0, 0xe2, 0xe7, 0xa7, 0x52, 0x4a, 0x98, 0xce, 0x62, 0x9e, 0xe4, 0x06, 0xc1, 0x5c, 0x51, 0xa6, 0x83, 0xe4, 0x16, 0x7f, 0x0b, 0x74, 0xea, 0x23, 0x05, 0x66, 0xdd, 0xec, 0xe7, 0xae, 0x9d, 0x6f, 0x0b
];

global nonce = 1;
Expand Down
Loading
Loading