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

get storage recursive proof #330

Merged
merged 2 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion .github/workflows/circuits_e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,20 @@ jobs:
toolchain: nightly-2024-05-22

- name: Compile Circuit
run: nargo compile --workspace --deny-warnings
run: |
nargo compile --package get_header --deny-warnings
nargo compile --package get_account --deny-warnings
nargo compile --package get_storage --deny-warnings
nargo compile --package get_storage_recursive --deny-warnings
nargo compile --package get_receipt --deny-warnings
nargo compile --package get_transaction --deny-warnings
nargo compile --package get_log --deny-warnings
nargo compile --package is_dao_worthy --deny-warnings
nargo compile --package is_ape_owner --deny-warnings

# We cannot use the `--deny-warnings` option in `is_dao_worthy_recursive` because the `verify_proof` method generates warnings
# that are actually informational messages from the compiler and cannot be ignored.
nargo compile --package is_dao_worthy_recursive

- name: Start Oracle Server
working-directory: ethereum/oracles
Expand All @@ -54,6 +67,18 @@ jobs:
nargo prove --package is_dao_worthy --oracle-resolver=http://localhost:5555
nargo prove --package is_ape_owner --oracle-resolver=http://localhost:5555

- name: Generate verification key for recursive proof
working-directory: ethereum/oracles
run: |
# Verification key generation uses Barretenberg backend located at ~/.nargo/backends/acvm-backend-barretenberg/backend_binary.
# It is automatically installed during the execution of the `nargo prove` command in previous step.
yarn generate-get-storage-vk

- name: Generate Recursive Proof
run: |
export NARGO_FOREIGN_CALL_TIMEOUT=100000 # miliseconds
nargo prove --package is_dao_worthy_recursive --oracle-resolver=http://localhost:5555

- name: Verify Proof
run: |
nargo verify --package get_header
Expand Down
1 change: 1 addition & 0 deletions Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ members = [
"ethereum/circuits/get_log",
"vlayer/ethereum/circuits/lib",
"vlayer/examples/circuits/is_dao_worthy",
"vlayer/examples/circuits/is_dao_worthy_recursive",
"vlayer/examples/circuits/is_ape_owner",
"vlayer/examples/circuits/is_crypto_punk_owner",
]
Expand Down
56 changes: 56 additions & 0 deletions ethereum/circuits/lib/src/account_with_storage_recursive.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use crate::account_with_storage::StorageWithinBlock;
use crate::misc::{fragment::Fragment, types::{Address, Bytes32, ADDRESS_LENGTH, BYTES32_LENGTH}};
use crate::serde::STORAGE_BLOCK_LEN;
use dep::std::{verify_proof, unsafe::zeroed};

struct RecursiveProof {
key_hash: Field,
verification_key: [Field; 114],
proof: [Field; 93],
}

global NUM_PUBLIC_INPUTS = 1 + 1 + ADDRESS_LENGTH + BYTES32_LENGTH + STORAGE_BLOCK_LEN; // chain_id + block_number + address + storage_key + public_return_inputs

pub fn get_account_with_storage_recursive(
chain_id: Field,
block_number: u64,
address: Address,
storage_key: Bytes32
) -> StorageWithinBlock<1> {
let (storage_within_block, RecursiveProof {key_hash, verification_key, proof}) = get_account_with_storage_recursive_unconstrained(chain_id, block_number, address, storage_key);

let mut public_inputs: Fragment<NUM_PUBLIC_INPUTS, Field> = Fragment::empty();
public_inputs.push_back(chain_id);
public_inputs.push_back(block_number as Field);
public_inputs.extend_back(address.serialize());
public_inputs.extend_back(storage_key.serialize());
public_inputs.extend_back(storage_within_block.serialize());

verify_proof(
verification_key.as_slice(),
proof.as_slice(),
public_inputs.to_array::<NUM_PUBLIC_INPUTS>().as_slice(),
key_hash
);

storage_within_block
}

#[oracle(get_storage_recursive)]
unconstrained fn get_account_with_storage_recursive_oracle(
chain_id: Field,
block_number: u64,
address: Address,
storage_key: Bytes32
) -> ([Field; STORAGE_BLOCK_LEN], RecursiveProof) {}

unconstrained fn get_account_with_storage_recursive_unconstrained(
chain_id: Field,
block_number: u64,
address: Address,
storage_key: Bytes32
) -> (StorageWithinBlock<1>, RecursiveProof) {
let (return_value_serialized, proof) = get_account_with_storage_recursive_oracle(chain_id, block_number, address, storage_key);
let return_value = StorageWithinBlock::deserialize(return_value_serialized);
(return_value, proof)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use crate::account_with_storage_recursive::{get_account_with_storage_recursive, RecursiveProof};
use crate::account_with_storage::StorageWithinBlock;
use crate::fixtures::mainnet::paris::usdc_circle::{header::{number, hash}, account::{address, account}, storage::{keys, values}};
use crate::chain::ETHEREUM_MAINNET_ID;
use crate::misc::types::{Bytes32, Address};
use dep::std::test::OracleMock;
use dep::std::unsafe::zeroed;

#[test]
fn success() {
let result = StorageWithinBlock { block_hash: hash, account, values };
// This is not a correct proof but it passes because nargo does not verify it. Nargo thinks that it's bb's job, but doesn't call bb.
let recursive_proof: RecursiveProof = zeroed();

let _ = OracleMock::mock("get_storage_recursive").returns((result.serialize(), recursive_proof));

let _ = get_account_with_storage_recursive(ETHEREUM_MAINNET_ID, number, address, keys[0]);
}
2 changes: 2 additions & 0 deletions ethereum/circuits/lib/src/lib.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ mod header;
mod header_int_test;
mod account_with_storage;
mod account_with_storage_int_test;
mod account_with_storage_recursive;
mod account_with_storage_recursive_int_test;
mod receipt;
mod receipt_int_test;
mod log;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { VerificationKey } from '../../circuit/vk.js';

export const getStorageOracle = async (args: NoirArguments): Promise<ForeignCallOutput[]> => {
const { blockNumber, address, storageKey, chainId } = decodeGetProofArguments(args);
const circuit = await MonorepoCircuit.create('../../', 'get_storage');
const circuit = await MonorepoCircuit.create('../../', 'get_storage_recursive');
const vk = await VerificationKey.create(circuit.vkAsFieldsPath());
const { proofAsFields, verifierData } = await new GetStorageProver(circuit).prove(
chainId,
Expand Down
2 changes: 1 addition & 1 deletion ethereum/oracles/src/noir/oracles/server/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jsonRPCServer.addMethod('get_account', getRpcOracleHandler.bind(this, getAccount
jsonRPCServer.addMethod('get_proof', getRpcOracleHandler.bind(this, getProofOracle));
jsonRPCServer.addMethod('get_receipt', getRpcOracleHandler.bind(this, getReceiptOracle));
jsonRPCServer.addMethod('get_transaction', getRpcOracleHandler.bind(this, getTransactionOracle));
jsonRPCServer.addMethod('recursive_get_storage', getOracleHandler.bind(this, getStorageOracle));
jsonRPCServer.addMethod('get_storage_recursive', getOracleHandler.bind(this, getStorageOracle));

export function buildOracleServer(
opts: Fastify.FastifyHttpOptions<http.Server> = {},
Expand Down
2 changes: 1 addition & 1 deletion ethereum/oracles/src/noir/oracles/server/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export type JSONRPCServerMethods = {
get_proof(params: ForeignCallParams): ForeignCallResult;
get_receipt(params: ForeignCallParams): ForeignCallResult;
get_transaction(params: ForeignCallParams): ForeignCallResult;
recursive_get_storage(params: ForeignCallParams): ForeignCallResult;
get_storage_recursive(params: ForeignCallParams): ForeignCallResult;
};

export interface ServerParams {
Expand Down
2 changes: 1 addition & 1 deletion ethereum/oracles/src/script/generateGetStorageVK.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { MonorepoCircuit } from '../noir/circuit/circuit.js';
import { VerificationKey, generateVk } from '../noir/circuit/vk.js';

const circuit = await MonorepoCircuit.create('../../', 'get_storage');
const circuit = await MonorepoCircuit.create('../../', 'get_storage_recursive');
await generateVk(circuit.artefact.bytecode, circuit.vkPath(), circuit.vkAsFieldsPath());
const vk = await VerificationKey.create(circuit.vkAsFieldsPath());
// eslint-disable-next-line no-console
Expand Down
13 changes: 9 additions & 4 deletions vlayer/ethereum/circuits/lib/src/token.nr
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use dep::ethereum::{
account_with_storage::get_account_with_storage, misc::types::Address,
account_with_storage::get_account_with_storage,
account_with_storage_recursive::get_account_with_storage_recursive, misc::types::Address,
misc::bytes32::address_to_bytes32
};
use dep::std::field::bytes32_to_field;
Expand All @@ -22,13 +23,17 @@ impl ERC20Token {
}

trait ERC20 {
fn get_balance(self, wallet_address: Address, block_number: u64) -> U128 ;
fn get_balance(self, wallet_address: Address, block_number: u64, recursive: bool) -> U128;
}

impl ERC20 for ERC20Token {
fn get_balance(self, wallet_address: Address, block_number: u64) -> U128 {
fn get_balance(self, wallet_address: Address, block_number: u64, recursive: bool) -> U128 {
let storage_key = self.calculate_balance_storage_key(wallet_address);
let account = get_account_with_storage(self.chain_id, block_number, self.address, U256::into(storage_key));
let account = if recursive {
get_account_with_storage_recursive(self.chain_id, block_number, self.address, U256::into(storage_key))
} else {
get_account_with_storage(self.chain_id, block_number, self.address, U256::into(storage_key))
};
let balance = account.values[TOKEN_BALANCE_INDEX];

U128::from_integer(bytes32_to_field(balance))
Expand Down
10 changes: 8 additions & 2 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,21 @@ 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::account::account, paris::usdc_circle::storage::values,
paris::usdc_circle::state_proof::proof_input_serialized as state_proof_input_serialized,
paris::usdc_circle::storage_proof::proofs_serialized
};
use dep::ethereum::uint256::U256;
use crate::chain_id::MAINNET;
use dep::ethereum::account_with_storage_recursive::RecursiveProof;
use dep::ethereum::account_with_storage::StorageWithinBlock;

#[test]
fn success() {
let result = StorageWithinBlock { block_hash: hash, account, values };
let recursive_proof = RecursiveProof { key_hash: 1, verification_key: [0; 114], proof: [0; 93] };

let _ = OracleMock::mock("get_storage_recursive").returns((result.serialize(), recursive_proof));
let _ = OracleMock::mock("get_header").returns((paris_block_header_partial, paris_block_header_rlp));
let _ = OracleMock::mock("get_proof").returns((account, state_proof_input_serialized, proofs_serialized[0]));

Expand All @@ -29,7 +35,7 @@ mod test_ERC20Token {
0x55, 0xFE, 0x00, 0x2a, 0xef, 0xF0, 0x2F, 0x77, 0x36, 0x4d, 0xe3, 0x39, 0xa1, 0x29, 0x29, 0x23, 0xA1, 0x58, 0x44, 0xB8
];
let block_number = 19000000;
let balance = usdc_token.get_balance(circle_address, block_number);
let balance = usdc_token.get_balance(circle_address, block_number, false);
let expected_usdc_balnce_of_circle = U128::from_integer(125761774888720);
assert_eq(expected_usdc_balnce_of_circle, balance);
}
Expand Down
2 changes: 1 addition & 1 deletion vlayer/examples/circuits/is_dao_worthy/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ mod main_test;
global MIN_BALANCE = U128::from_integer(100_000_000_000); // $100k

fn main(wallet_address: Address, block_number: pub u64) {
let wallet_balance = USDC.get_balance(wallet_address, block_number);
let wallet_balance = USDC.get_balance(wallet_address, block_number, false);
let is_balance_greater_or_equal = MIN_BALANCE <= wallet_balance;

assert(is_balance_greater_or_equal, "Insufficient USDC balance");
Expand Down
8 changes: 8 additions & 0 deletions vlayer/examples/circuits/is_dao_worthy_recursive/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "is_dao_worthy_recursive"
type = "bin"
compiler_version = ">=0.30.0"

[dependencies]
ethereum = { path = "../../../../ethereum/circuits/lib" }
token = { path = "../../../ethereum/circuits/lib" }
4 changes: 4 additions & 0 deletions vlayer/examples/circuits/is_dao_worthy_recursive/Prover.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
block_number = 19000000

# Circle address
wallet_address = [ 0x55, 0xfe, 0x00, 0x2a, 0xef, 0xf0, 0x2f, 0x77, 0x36, 0x4d, 0xe3, 0x39, 0xa1, 0x29, 0x29, 0x23, 0xa1, 0x58, 0x44, 0xb8 ]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
block_number = 19000000
12 changes: 12 additions & 0 deletions vlayer/examples/circuits/is_dao_worthy_recursive/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use dep::ethereum::misc::types::Address;
use dep::token::token_list::mainnet::USDC;

mod main_test;

global MIN_BALANCE = U128::from_integer(100_000_000_000); // $100k

fn main(wallet_address: Address, block_number: pub u64) {
let wallet_balance = USDC.get_balance(wallet_address, block_number, true);

assert(MIN_BALANCE <= wallet_balance, "Insufficient USDC balance");
}
27 changes: 27 additions & 0 deletions vlayer/examples/circuits/is_dao_worthy_recursive/src/main_test.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
mod is_dao_worthy_main {
use dep::std::test::OracleMock;
use dep::ethereum::fixtures::mainnet::{
paris::usdc_circle::header::{hash, block_header_partial as paris_block_header_partial, block_header_rlp as paris_block_header_rlp},
paris::usdc_circle::account::account, paris::usdc_circle::storage::values,
paris::usdc_circle::state_proof::proof_input_serialized as state_proof_input_serialized,
paris::usdc_circle::storage_proof::proofs_serialized
};
use dep::ethereum::{account_with_storage_recursive::RecursiveProof, account_with_storage::StorageWithinBlock};
use crate::main;

#[test]
fn success_greater_then() {
let result = StorageWithinBlock { block_hash: hash, account, values };
let recursive_proof = RecursiveProof { key_hash: 1, verification_key: [0; 114], proof: [0; 93] };

let _ = OracleMock::mock("get_storage_recursive").returns((result.serialize(), recursive_proof));
let _ = OracleMock::mock("get_header").returns((paris_block_header_partial, paris_block_header_rlp));
let _ = OracleMock::mock("get_proof").returns((account, state_proof_input_serialized, proofs_serialized[0]));

let block_number = 19_000_000;
let circle_address = [
0x55, 0xfe, 0x00, 0x2a, 0xef, 0xf0, 0x2f, 0x77, 0x36, 0x4d, 0xe3, 0x39, 0xa1, 0x29, 0x29, 0x23, 0xa1, 0x58, 0x44, 0xb8
];
main(circle_address, block_number);
}
}
Loading