diff --git a/plonky2x/src/frontend/eth/storage/builder.rs b/plonky2x/src/frontend/eth/storage/builder.rs index 3410eda02..20741ad81 100644 --- a/plonky2x/src/frontend/eth/storage/builder.rs +++ b/plonky2x/src/frontend/eth/storage/builder.rs @@ -1,9 +1,9 @@ -use ethers::types::{Address, U256}; +use ethers::types::{U256}; use plonky2::field::extension::Extendable; use plonky2::hash::hash_types::RichField; use super::generators::block::EthBlockGenerator; -use super::generators::storage::EthStorageProofGenerator; +use super::generators::storage::{EthStorageProofGenerator, EthAccountProofGenerator}; use super::vars::{EthAccountVariable, EthHeaderVariable, EthLogVariable}; use crate::frontend::builder::CircuitBuilder; use crate::frontend::eth::vars::AddressVariable; @@ -40,10 +40,12 @@ impl, const D: usize> CircuitBuilder { #[allow(non_snake_case)] pub fn eth_get_account( &mut self, - _address: Address, - _block_hash: Bytes32Variable, + address: AddressVariable, + block_hash: Bytes32Variable, ) -> EthAccountVariable { - todo!() + let generator = EthAccountProofGenerator::new(self, address, block_hash); + self.add_simple_generator(&generator); + generator.value } #[allow(non_snake_case)] @@ -65,7 +67,7 @@ mod tests { use plonky2::plonk::config::PoseidonGoldilocksConfig; use super::*; - use crate::frontend::eth::storage::vars::EthHeader; + use crate::frontend::eth::storage::vars::{EthHeader, EthAccount}; use crate::prelude::CircuitBuilderX; use crate::utils::{address, bytes32}; @@ -117,6 +119,56 @@ mod tests { let _ = circuit.serialize().unwrap(); } + #[test] + #[cfg_attr(feature = "ci", ignore)] + #[allow(non_snake_case)] + fn test_eth_get_account() { + dotenv::dotenv().ok(); + let rpc_url = env::var("RPC_1").unwrap(); + let provider = Provider::::try_from(rpc_url).unwrap(); + + // This is the circuit definition + let mut builder = CircuitBuilderX::new(); + builder.set_execution_client(provider); + let block_hash = builder.read::(); + let address = builder.read::(); + + let value = builder.eth_get_account(address, block_hash); + builder.write(value); + + // Build your circuit. + let circuit = builder.build::(); + + // Write to the circuit input. + // These values are taken from Ethereum block https://etherscan.io/block/17880427 + let mut input = circuit.input(); + input.write::(bytes32!( + "0x281dc31bb78779a1ede7bf0f4d2bc5f07ddebc9f9d1155e413d8804384604bbe" + )); + input.write::(address!("0x55032650b14df07b85bF18A3a3eC8E0Af2e028d5")); + + // Generate a proof. + let (proof, output) = circuit.prove(&input); + + // Verify proof. + circuit.verify(&proof, &input, &output); + + // Read output. + let circuit_value = output.read::(); + println!("{:?}", circuit_value); + assert_eq!( + circuit_value, + EthAccount { + nonce: U256::from(1), + balance: U256::from(0), + code_hash: bytes32!("0xb9c1c929064cd21734c102a698e68bf617feefcfa5a9f62407c45401546736bf"), + storage_hash: bytes32!("0x073d71569b4b986bc20b6921dbbc1b74145588f765627dd5e566d65a6b7b33cc"), + }, + ); + + let _ = circuit.serialize().unwrap(); + } + #[test] #[cfg_attr(feature = "ci", ignore)] #[allow(non_snake_case)] diff --git a/plonky2x/src/frontend/eth/storage/generators/storage.rs b/plonky2x/src/frontend/eth/storage/generators/storage.rs index 076984eb0..21b8d5de9 100644 --- a/plonky2x/src/frontend/eth/storage/generators/storage.rs +++ b/plonky2x/src/frontend/eth/storage/generators/storage.rs @@ -13,6 +13,7 @@ use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; use tokio::runtime::Runtime; use crate::frontend::builder::CircuitBuilder; +use crate::frontend::eth::storage::vars::{EthAccountVariable, EthAccount}; use crate::frontend::eth::utils::u256_to_h256_be; use crate::frontend::eth::vars::AddressVariable; use crate::frontend::vars::{Bytes32Variable, CircuitVariable}; @@ -118,3 +119,112 @@ impl, const D: usize> SimpleGenerator }) } } + +#[derive(Debug, Clone)] +pub struct EthAccountProofGenerator, const D: usize> { + address: AddressVariable, + block_hash: Bytes32Variable, + pub value: EthAccountVariable, + chain_id: u64, + _phantom: PhantomData, +} + +impl, const D: usize> EthAccountProofGenerator { + pub fn new( + builder: &mut CircuitBuilder, + address: AddressVariable, + block_hash: Bytes32Variable, + ) -> EthAccountProofGenerator { + let chain_id = builder.get_chain_id(); + let value = builder.init::(); + EthAccountProofGenerator { + address, + block_hash, + value, + chain_id, + _phantom: PhantomData::, + } + } +} + +impl, const D: usize> SimpleGenerator + for EthAccountProofGenerator +{ + fn id(&self) -> String { + "EthStorageProofGenerator".to_string() + } + + fn dependencies(&self) -> Vec { + let mut targets = Vec::new(); + targets.extend(self.address.targets()); + targets.extend(self.block_hash.targets()); + targets + } + + fn run_once(&self, witness: &PartitionWitness, buffer: &mut GeneratedValues) { + let address = self.address.get(witness); + let block_hash = self.block_hash.get(witness); + let provider = get_provider(self.chain_id); + let rt = Runtime::new().expect("failed to create tokio runtime"); + let result: EIP1186ProofResponse = rt.block_on(async { + provider + .get_proof(address, vec![], Some(block_hash.into())) + .await + .expect("Failed to get proof") + }); + // Copy u64 into U256 + let mut bytes = [0u8; 8]; + result + .nonce + .to_big_endian(&mut bytes); + // Append 24 zero bytes to the beginning of the number + let mut total_bytes = [0u8; 32]; + total_bytes[24..].copy_from_slice(&bytes); + + let nonce = ethers::types::U256::from_big_endian(&total_bytes); + + self.value.set(buffer, EthAccount { + balance: result.balance, + code_hash: result.code_hash, + nonce, + storage_hash: result.storage_hash, + }); + } + + #[allow(unused_variables)] + fn serialize(&self, dst: &mut Vec, common_data: &CommonCircuitData) -> IoResult<()> { + let chain_id_bytes = self.chain_id.to_be_bytes(); + dst.write_all(&chain_id_bytes)?; + + dst.write_target_vec(&self.block_hash.targets())?; + dst.write_target_vec(&self.address.targets())?; + dst.write_target_vec(&self.value.targets()) + } + + #[allow(unused_variables)] + fn deserialize(src: &mut Buffer, common_data: &CommonCircuitData) -> IoResult { + let mut chain_id_bytes = [0u8; 8]; + src.read_exact(&mut chain_id_bytes)?; + let chain_id = u64::from_be_bytes(chain_id_bytes); + + let block_hash_targets = src.read_target_vec()?; + let block_hash = Bytes32Variable::from_targets(&block_hash_targets); + + let address_targets = src.read_target_vec()?; + let address = AddressVariable::from_targets(&address_targets); + + let storage_key_targets = src.read_target_vec()?; + let storage_key = Bytes32Variable::from_targets(&storage_key_targets); + + let value_targets = src.read_target_vec()?; + let value = EthAccountVariable::from_targets(&value_targets); + + Ok(Self { + address, + block_hash, + value, + chain_id, + _phantom: PhantomData::, + }) + } +} diff --git a/plonky2x/src/frontend/eth/storage/vars/storage.rs b/plonky2x/src/frontend/eth/storage/vars/storage.rs index 5b375f3c5..b87722e9a 100644 --- a/plonky2x/src/frontend/eth/storage/vars/storage.rs +++ b/plonky2x/src/frontend/eth/storage/vars/storage.rs @@ -62,10 +62,11 @@ impl CircuitVariable for EthProofVariable { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct EthAccount { pub balance: U256, pub code_hash: H256, + // Update to U64 once we have U64 variables pub nonce: U256, pub storage_hash: H256, } @@ -74,6 +75,7 @@ pub struct EthAccount { pub struct EthAccountVariable { pub balance: U256Variable, pub code_hash: Bytes32Variable, + // Update to U64 once we have U64 variables pub nonce: U256Variable, pub storage_hash: Bytes32Variable, } @@ -115,11 +117,13 @@ impl CircuitVariable for EthAccountVariable { fn from_variables(variables: &[Variable]) -> Self { let balance = U256Variable::from_variables(&variables[0..4]); - let code_hash = Bytes32Variable::from_variables(&variables[4..4 + 32 * 8]); - let offset = 4 + 32 * 8; + let mut offset = 4; + let code_hash = Bytes32Variable::from_variables(&variables[offset..offset + 32 * 8]); + offset += 32 * 8; let nonce = U256Variable::from_variables(&variables[offset..offset + 4]); + offset += 4; let storage_hash = - Bytes32Variable::from_variables(&variables[offset + 4..offset + 4 + 32 * 8]); + Bytes32Variable::from_variables(&variables[offset..offset + 32 * 8]); Self { balance, code_hash,