Skip to content

Commit

Permalink
feat: eth_getTransactionLog (#99)
Browse files Browse the repository at this point in the history
* wip

* feat: test transaction log

* refactor: update offset explicitly

* lint

* fix order

* refactor: update order of blockhash

* lint

* lint

* add generator to macro, dummy serialzie in circuit

* add storage key gen from main

* chore: cargo fmt
  • Loading branch information
ratankaliani authored Aug 30, 2023
1 parent 0921dcc commit 4018306
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 24 deletions.
5 changes: 3 additions & 2 deletions plonky2x/src/backend/circuit/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use plonky2::util::serialization::{Buffer, IoResult, WitnessGeneratorSerializer}

use crate::frontend::eth::storage::generators::block::EthBlockGenerator;
use crate::frontend::eth::storage::generators::storage::{
EthStorageKeyGenerator, EthStorageProofGenerator,
EthLogGenerator, EthStorageKeyGenerator, EthStorageProofGenerator,
};
use crate::frontend::hash::keccak::keccak256::Keccack256Generator;

Expand Down Expand Up @@ -144,8 +144,9 @@ where
SplitGenerator, "SplitGenerator",
WireSplitGenerator, "WireSplitGenerator",
EthStorageProofGenerator<F, D>, "EthStorageProofGenerator",
EthStorageKeyGenerator<F, D>, "EthStorageKeyGenerator",
EthLogGenerator<F, D>, "EthLogGenerator",
EthBlockGenerator<F, D>, "EthBlockGenerator",
EthStorageKeyGenerator<F, D>, "EthStorageKeyGenerator",
Keccack256Generator<F, D>, "Keccak256Generator"
}
}
87 changes: 77 additions & 10 deletions plonky2x/src/frontend/eth/storage/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use plonky2::field::extension::Extendable;
use plonky2::hash::hash_types::RichField;

use super::generators::block::EthBlockGenerator;
use super::generators::storage::{EthStorageKeyGenerator, EthStorageProofGenerator};
use super::generators::storage::{
EthLogGenerator, EthStorageKeyGenerator, EthStorageProofGenerator,
};
use super::vars::{EthAccountVariable, EthHeaderVariable, EthLogVariable};
use crate::frontend::builder::CircuitBuilder;
use crate::frontend::eth::vars::AddressVariable;
Expand All @@ -24,11 +26,11 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
#[allow(non_snake_case)]
pub fn eth_get_storage_at(
&mut self,
block_hash: Bytes32Variable,
address: AddressVariable,
storage_key: Bytes32Variable,
block_hash: Bytes32Variable,
) -> Bytes32Variable {
let generator = EthStorageProofGenerator::new(self, address, storage_key, block_hash);
let generator = EthStorageProofGenerator::new(self, block_hash, address, storage_key);
self.add_simple_generator(&generator);
generator.value
}
Expand All @@ -50,13 +52,15 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}

#[allow(non_snake_case)]
pub fn eth_get_transaction_receipt(
pub fn eth_get_transaction_log(
&mut self,
_transaction_hash: Bytes32Variable,
_block_hash: Bytes32Variable,
_log_index: usize,
transaction_hash: Bytes32Variable,
block_hash: Bytes32Variable,
log_index: u64,
) -> EthLogVariable {
todo!()
let generator = EthLogGenerator::new(self, transaction_hash, block_hash, log_index);
self.add_simple_generator(&generator);
generator.value
}
}

Expand All @@ -70,7 +74,7 @@ mod tests {

use super::*;
use crate::frontend::eth::storage::utils::get_map_storage_location;
use crate::frontend::eth::storage::vars::EthHeader;
use crate::frontend::eth::storage::vars::{EthHeader, EthLog};
use crate::prelude::CircuitBuilderX;
use crate::utils::{address, bytes32};

Expand All @@ -88,7 +92,7 @@ mod tests {
let block_hash = builder.evm_read::<Bytes32Variable>();
let address = builder.evm_read::<AddressVariable>();
let location = builder.evm_read::<Bytes32Variable>();
let value = builder.eth_get_storage_at(address, location, block_hash);
let value = builder.eth_get_storage_at(block_hash, address, location);
builder.evm_write(value);

// Build your circuit.
Expand All @@ -97,10 +101,13 @@ mod tests {
// Write to the circuit input.
// These values are taken from Ethereum block https://etherscan.io/block/17880427
let mut input = circuit.input();
// block hash
input.evm_write::<Bytes32Variable>(bytes32!(
"0x281dc31bb78779a1ede7bf0f4d2bc5f07ddebc9f9d1155e413d8804384604bbe"
));
// address
input.evm_write::<AddressVariable>(address!("0x55032650b14df07b85bF18A3a3eC8E0Af2e028d5"));
// location
input.evm_write::<Bytes32Variable>(bytes32!(
"0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"
));
Expand Down Expand Up @@ -236,4 +243,64 @@ mod tests {

let _ = circuit.serialize().unwrap();
}

#[test]
#[cfg_attr(feature = "ci", ignore)]
#[allow(non_snake_case)]
fn test_eth_get_transaction_log() {
dotenv::dotenv().ok();
let rpc_url = env::var("RPC_1").unwrap();
let provider = Provider::<Http>::try_from(rpc_url).unwrap();

// This is the circuit definition
let mut builder = CircuitBuilderX::new();
builder.set_execution_client(provider);
let transaction_hash = builder.read::<Bytes32Variable>();
let block_hash = builder.read::<Bytes32Variable>();
let log_index = 0u64;

let value = builder.eth_get_transaction_log(transaction_hash, block_hash, log_index);
builder.write(value);

// Build your circuit.
let circuit = builder.build::<PoseidonGoldilocksConfig>();

// Write to the circuit input.
// These values are taken from Ethereum block https://etherscan.io/block/17880427
let mut input = circuit.input();
// transaction hash
input.write::<Bytes32Variable>(bytes32!(
"0xead2251970404128e6f9bdff0133badb7338c5fa7ea4eec24e88af85a6d03cf2"
));
// block hash
input.write::<Bytes32Variable>(bytes32!(
"0x281dc31bb78779a1ede7bf0f4d2bc5f07ddebc9f9d1155e413d8804384604bbe"
));

// Generate a proof.
let (proof, output) = circuit.prove(&input);

// Verify proof.
circuit.verify(&proof, &input, &output);

// Read output.
let circuit_value = output.read::<EthLogVariable>();
println!("{:?}", circuit_value);
assert_eq!(
circuit_value,
EthLog {
address: address!("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"),
topics: [
bytes32!("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
bytes32!("0x00000000000000000000000059b4bb1f5d943cf71a10df63f6b743ee4a4489ee"),
bytes32!("0x000000000000000000000000def1c0ded9bec7f1a1670819833240f027b25eff")
],
data_hash: bytes32!(
"0x5cdda96947975d4afbc971c9aa8bb2cc684e158d10a0d878b3a5b8b0f895262c"
)
}
);

let _ = circuit.serialize().unwrap();
}
}
121 changes: 117 additions & 4 deletions plonky2x/src/frontend/eth/storage/generators/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@ use core::fmt::Debug;
use core::marker::PhantomData;

use ethers::providers::Middleware;
use ethers::types::EIP1186ProofResponse;
use ethers::types::{EIP1186ProofResponse, TransactionReceipt};
use plonky2::field::extension::Extendable;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
use plonky2::iop::target::Target;
use plonky2::iop::witness::PartitionWitness;
use plonky2::plonk::circuit_data::CommonCircuitData;
use plonky2::util::serialization::{Buffer, IoResult, Read, Write};
use sha2::Digest;
use tokio::runtime::Runtime;

use crate::frontend::builder::CircuitBuilder;
use crate::frontend::eth::storage::utils::get_map_storage_location;
use crate::frontend::eth::storage::vars::{EthLog, EthLogVariable};
use crate::frontend::eth::utils::u256_to_h256_be;
use crate::frontend::eth::vars::AddressVariable;
use crate::frontend::uint::uint256::U256Variable;
Expand All @@ -22,9 +24,9 @@ use crate::utils::eth::get_provider;

#[derive(Debug, Clone)]
pub struct EthStorageProofGenerator<F: RichField + Extendable<D>, const D: usize> {
block_hash: Bytes32Variable,
address: AddressVariable,
storage_key: Bytes32Variable,
block_hash: Bytes32Variable,
pub value: Bytes32Variable,
chain_id: u64,
_phantom: PhantomData<F>,
Expand All @@ -33,16 +35,16 @@ pub struct EthStorageProofGenerator<F: RichField + Extendable<D>, const D: usize
impl<F: RichField + Extendable<D>, const D: usize> EthStorageProofGenerator<F, D> {
pub fn new(
builder: &mut CircuitBuilder<F, D>,
block_hash: Bytes32Variable,
address: AddressVariable,
storage_key: Bytes32Variable,
block_hash: Bytes32Variable,
) -> EthStorageProofGenerator<F, D> {
let chain_id = builder.get_chain_id();
let value = builder.init::<Bytes32Variable>();
EthStorageProofGenerator {
block_hash,
address,
storage_key,
block_hash,
value,
chain_id,
_phantom: PhantomData::<F>,
Expand Down Expand Up @@ -193,3 +195,114 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F, D>
})
}
}

#[derive(Debug, Clone)]
pub struct EthLogGenerator<F: RichField + Extendable<D>, const D: usize> {
transaction_hash: Bytes32Variable,
block_hash: Bytes32Variable,
log_index: u64,
pub value: EthLogVariable,
chain_id: u64,
_phantom: PhantomData<F>,
}

impl<F: RichField + Extendable<D>, const D: usize> EthLogGenerator<F, D> {
pub fn new(
builder: &mut CircuitBuilder<F, D>,
transaction_hash: Bytes32Variable,
block_hash: Bytes32Variable,
log_index: u64,
) -> EthLogGenerator<F, D> {
let chain_id = builder.get_chain_id();
let value = builder.init::<EthLogVariable>();
EthLogGenerator {
transaction_hash,
block_hash,
log_index,
value,
chain_id,
_phantom: PhantomData::<F>,
}
}
}

impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F, D> for EthLogGenerator<F, D> {
fn id(&self) -> String {
"EthLogGenerator".to_string()
}

fn dependencies(&self) -> Vec<Target> {
let mut targets = Vec::new();
targets.extend(self.transaction_hash.targets());
targets.extend(self.block_hash.targets());
targets
}

fn run_once(&self, witness: &PartitionWitness<F>, buffer: &mut GeneratedValues<F>) {
let transaction_hash = self.transaction_hash.get(witness);
// block_hash is unused
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: TransactionReceipt = rt
.block_on(async {
provider
.get_transaction_receipt(transaction_hash)
.await
.expect("Failed to call get_transaction_receipt")
})
.expect("No transaction receipt found");

let log = &result.logs[self.log_index as usize];
let value = EthLog {
address: log.address,
topics: [log.topics[0], log.topics[1], log.topics[2]],
data_hash: ethers::types::H256::from_slice(sha2::Sha256::digest(&log.data).as_ref()),
};
self.value.set(buffer, value);
}

#[allow(unused_variables)]
fn serialize(&self, dst: &mut Vec<u8>, common_data: &CommonCircuitData<F, D>) -> IoResult<()> {
let chain_id_bytes = self.chain_id.to_be_bytes();
dst.write_all(&chain_id_bytes)?;

dst.write_target_vec(&self.transaction_hash.targets())?;
dst.write_target_vec(&self.block_hash.targets())?;

let log_index_bytes = self.log_index.to_be_bytes();
dst.write_all(&log_index_bytes)?;

dst.write_target_vec(&self.value.targets())
}

#[allow(unused_variables)]
fn deserialize(src: &mut Buffer, common_data: &CommonCircuitData<F, D>) -> IoResult<Self> {
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 transaction_hash_targets = src.read_target_vec()?;
let transaction_hash = Bytes32Variable::from_targets(&transaction_hash_targets);

let block_hash_targets = src.read_target_vec()?;
let block_hash = Bytes32Variable::from_targets(&block_hash_targets);

let mut log_index_bytes = [0u8; 8];
src.read_exact(&mut log_index_bytes)?;
let log_index = u64::from_be_bytes(log_index_bytes);

let value_targets = src.read_target_vec()?;
let value = EthLogVariable::from_targets(&value_targets);

Ok(Self {
block_hash,
transaction_hash,
log_index,
value,
chain_id,
_phantom: PhantomData::<F>,
})
}
}
8 changes: 5 additions & 3 deletions plonky2x/src/frontend/eth/storage/vars/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,11 @@ impl CircuitVariable for EthHeaderVariable {
#[allow(unused_variables)]
fn from_variables(variables: &[Variable]) -> Self {
let parent_hash = Bytes32Variable::from_variables(&variables[0..32 * 8]);
let uncle_hash = Bytes32Variable::from_variables(&variables[32 * 8..64 * 8]);
let coinbase = AddressVariable::from_variables(&variables[64 * 8..64 * 8 + 8 * 20]);
let mut offset = 64 * 8 + 8 * 20;
let mut offset = 32 * 8;
let uncle_hash = Bytes32Variable::from_variables(&variables[offset..offset + 32 * 8]);
offset += 32 * 8;
let coinbase = AddressVariable::from_variables(&variables[offset..offset + 8 * 20]);
offset += 8 * 20;
let root = Bytes32Variable::from_variables(&variables[offset..offset + 32 * 8]);
offset += 32 * 8;

Expand Down
11 changes: 6 additions & 5 deletions plonky2x/src/frontend/eth/storage/vars/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,12 @@ 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]);
let storage_hash =
Bytes32Variable::from_variables(&variables[offset + 4..offset + 4 + 32 * 8]);
offset += 4;
let storage_hash = Bytes32Variable::from_variables(&variables[offset..offset + 32 * 8]);
Self {
balance,
code_hash,
Expand All @@ -145,7 +146,7 @@ impl CircuitVariable for EthAccountVariable {
}
}

#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct EthLog {
pub address: Address,
pub topics: [H256; 3],
Expand Down

0 comments on commit 4018306

Please sign in to comment.