diff --git a/.github/workflows/circuits_e2e.yaml b/.github/workflows/circuits_e2e.yaml index e2b4650f..2fae275a 100644 --- a/.github/workflows/circuits_e2e.yaml +++ b/.github/workflows/circuits_e2e.yaml @@ -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 @@ -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 diff --git a/Nargo.toml b/Nargo.toml index d9c4dc10..7df2926a 100644 --- a/Nargo.toml +++ b/Nargo.toml @@ -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", ] diff --git a/ethereum/circuits/lib/src/account_with_storage_recursive.nr b/ethereum/circuits/lib/src/account_with_storage_recursive.nr index 11585de0..83333b10 100644 --- a/ethereum/circuits/lib/src/account_with_storage_recursive.nr +++ b/ethereum/circuits/lib/src/account_with_storage_recursive.nr @@ -1,8 +1,7 @@ +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; -use crate::account_with_storage::StorageWithinBlock; -use dep::std::unsafe::zeroed; +use dep::std::{verify_proof, unsafe::zeroed}; struct RecursiveProof { key_hash: Field, @@ -26,19 +25,18 @@ pub fn get_account_with_storage_recursive( public_inputs.extend_back(address.serialize()); public_inputs.extend_back(storage_key.serialize()); public_inputs.extend_back(storage_within_block.serialize()); - let public_inputs_array: [Field; NUM_PUBLIC_INPUTS] = public_inputs.to_array(); verify_proof( verification_key.as_slice(), proof.as_slice(), - public_inputs_array.as_slice(), + public_inputs.to_array::().as_slice(), key_hash ); storage_within_block } -#[oracle(get_account_with_storage_recursive)] +#[oracle(get_storage_recursive)] unconstrained fn get_account_with_storage_recursive_oracle( chain_id: Field, block_number: u64, diff --git a/ethereum/circuits/lib/src/account_with_storage_recursive_int_test.nr b/ethereum/circuits/lib/src/account_with_storage_recursive_int_test.nr index fd7a5231..667707ec 100644 --- a/ethereum/circuits/lib/src/account_with_storage_recursive_int_test.nr +++ b/ethereum/circuits/lib/src/account_with_storage_recursive_int_test.nr @@ -1,16 +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::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 }; - let recursive_proof = RecursiveProof { key_hash: 1, verification_key: [0; 114], proof: [0; 93] }; + // 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_account_with_storage_recursive").returns((result.serialize(), recursive_proof)); + let _ = OracleMock::mock("get_storage_recursive").returns((result.serialize(), recursive_proof)); - get_account_with_storage_recursive(ETHEREUM_MAINNET_ID, number, address, keys[0]); + let _ = get_account_with_storage_recursive(ETHEREUM_MAINNET_ID, number, address, keys[0]); } diff --git a/ethereum/oracles/src/noir/oracles/server/app.ts b/ethereum/oracles/src/noir/oracles/server/app.ts index 23273cc4..10b7d289 100644 --- a/ethereum/oracles/src/noir/oracles/server/app.ts +++ b/ethereum/oracles/src/noir/oracles/server/app.ts @@ -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 = {}, diff --git a/ethereum/oracles/src/noir/oracles/server/handlers.ts b/ethereum/oracles/src/noir/oracles/server/handlers.ts index 923ec179..da250ebc 100644 --- a/ethereum/oracles/src/noir/oracles/server/handlers.ts +++ b/ethereum/oracles/src/noir/oracles/server/handlers.ts @@ -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 { diff --git a/ethereum/oracles/src/script/generateGetStorageVK.ts b/ethereum/oracles/src/script/generateGetStorageVK.ts index 55d3286f..a58d26b3 100644 --- a/ethereum/oracles/src/script/generateGetStorageVK.ts +++ b/ethereum/oracles/src/script/generateGetStorageVK.ts @@ -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 diff --git a/vlayer/ethereum/circuits/lib/src/token.nr b/vlayer/ethereum/circuits/lib/src/token.nr index 47df18c3..206cea8e 100644 --- a/vlayer/ethereum/circuits/lib/src/token.nr +++ b/vlayer/ethereum/circuits/lib/src/token.nr @@ -1,4 +1,5 @@ use dep::ethereum::{ + account_with_storage::get_account_with_storage, account_with_storage_recursive::get_account_with_storage_recursive, misc::types::Address, misc::bytes32::address_to_bytes32 }; @@ -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_recursive(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)) diff --git a/vlayer/ethereum/circuits/lib/src/token_int_test.nr b/vlayer/ethereum/circuits/lib/src/token_int_test.nr index 402fc12b..5e2ed903 100644 --- a/vlayer/ethereum/circuits/lib/src/token_int_test.nr +++ b/vlayer/ethereum/circuits/lib/src/token_int_test.nr @@ -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])); @@ -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); } diff --git a/vlayer/examples/circuits/is_dao_worthy/Verifier.toml b/vlayer/examples/circuits/is_dao_worthy/Verifier.toml index 6b5c37d3..d7521d88 100644 --- a/vlayer/examples/circuits/is_dao_worthy/Verifier.toml +++ b/vlayer/examples/circuits/is_dao_worthy/Verifier.toml @@ -1 +1 @@ -block_number = "0x000000000000000000000000000000000000000000000000000000000121eac0" +block_number = 19000000 diff --git a/vlayer/examples/circuits/is_dao_worthy/src/main.nr b/vlayer/examples/circuits/is_dao_worthy/src/main.nr index 250bd896..07d2235e 100644 --- a/vlayer/examples/circuits/is_dao_worthy/src/main.nr +++ b/vlayer/examples/circuits/is_dao_worthy/src/main.nr @@ -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"); diff --git a/vlayer/examples/circuits/is_dao_worthy_recursive/Nargo.toml b/vlayer/examples/circuits/is_dao_worthy_recursive/Nargo.toml new file mode 100644 index 00000000..502e862b --- /dev/null +++ b/vlayer/examples/circuits/is_dao_worthy_recursive/Nargo.toml @@ -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" } diff --git a/vlayer/examples/circuits/is_dao_worthy_recursive/Prover.toml b/vlayer/examples/circuits/is_dao_worthy_recursive/Prover.toml new file mode 100644 index 00000000..c77cac23 --- /dev/null +++ b/vlayer/examples/circuits/is_dao_worthy_recursive/Prover.toml @@ -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 ] diff --git a/vlayer/examples/circuits/is_dao_worthy_recursive/Verifier.toml b/vlayer/examples/circuits/is_dao_worthy_recursive/Verifier.toml new file mode 100644 index 00000000..d7521d88 --- /dev/null +++ b/vlayer/examples/circuits/is_dao_worthy_recursive/Verifier.toml @@ -0,0 +1 @@ +block_number = 19000000 diff --git a/vlayer/examples/circuits/is_dao_worthy_recursive/src/main.nr b/vlayer/examples/circuits/is_dao_worthy_recursive/src/main.nr new file mode 100644 index 00000000..7ce7056d --- /dev/null +++ b/vlayer/examples/circuits/is_dao_worthy_recursive/src/main.nr @@ -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"); +} diff --git a/vlayer/examples/circuits/is_dao_worthy_recursive/src/main_test.nr b/vlayer/examples/circuits/is_dao_worthy_recursive/src/main_test.nr new file mode 100644 index 00000000..15353417 --- /dev/null +++ b/vlayer/examples/circuits/is_dao_worthy_recursive/src/main_test.nr @@ -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); + } +}