From a94271a6db48ddbe08c2d98b894b5a4ee3b83f61 Mon Sep 17 00:00:00 2001 From: ivebeenherebefore <148863352+ivebeenherebefore@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:27:11 -0600 Subject: [PATCH 1/5] WIP --- contracts/genesis.json | 20 +++--- primitives/src/lib.rs | 1 + primitives/src/receipt.rs | 25 +++++++ script/bin/operator.rs | 134 +++++++++++++++++++++++++++++++++++--- 4 files changed, 161 insertions(+), 19 deletions(-) create mode 100644 primitives/src/receipt.rs diff --git a/contracts/genesis.json b/contracts/genesis.json index 59e6d15..bee933f 100644 --- a/contracts/genesis.json +++ b/contracts/genesis.json @@ -1,15 +1,15 @@ { - "executionStateRoot": "0xefd1e79b7ea23418b36c0d402940e82c2fa7c0eefb7ab6eba7d1bfed6ed3583b", - "genesisTime": 1655733600, - "genesisValidatorsRoot": "0xd8ea171f3c94aea21ebc42a1ed61052acf3f9209c00e4efbaaddac09ed9b8078", - "guardian": "0xded0000e32f8f40414d3ab3a830f735a3553e18e", - "head": 6258496, - "header": "0xc30412bbc474103121140c10705d608b72dd060794090e3a9fff2f0cc2204b5d", - "heliosProgramVkey": "0x0045a4708ac27a6e55aa8ac0269955904326abce5a5828d2ad91f5f3c0d89cdd", + "executionStateRoot": "0xd30d19d84c3c01bb7cd60393e227cd586d4996cdb5bd1a3fbe3c9845ad6f05e1", + "genesisTime": 1606824023, + "genesisValidatorsRoot": "0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95", + "guardian": "0xe6709a64cfa1d7d2d177a3b544b4fe3fc000bfa8", + "head": 10599264, + "header": "0xadcd83b153af90dcb6ce979b7bf3c486fcba79e5daf4dcf23c426bd96d27d84b", + "heliosProgramVkey": "0x008e014e12f07ad1ee7069351ac85c7f586ba89bb4792c20666c9f7a00af97aa", "secondsPerSlot": 12, "slotsPerEpoch": 32, "slotsPerPeriod": 8192, - "sourceChainId": 11155111, - "syncCommitteeHash": "0xb997e17dda358926c21e29985b199afb5500fd4828a6c1a0455101cb1c514062", - "verifier": "0x3b6041173b80e77f038f3f2c0f9744f04837185e" + "sourceChainId": 1, + "syncCommitteeHash": "0xbe9b25fd743f094b6d302f4c1676d701ee22acc8cd979cf7fe0b577e8ba6dcec", + "verifier": "0x0000000000000000000000000000000000000000" } \ No newline at end of file diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index cd40856..e9fefab 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -1 +1,2 @@ pub mod types; +// pub mod receipt; \ No newline at end of file diff --git a/primitives/src/receipt.rs b/primitives/src/receipt.rs new file mode 100644 index 0000000..b9fc714 --- /dev/null +++ b/primitives/src/receipt.rs @@ -0,0 +1,25 @@ +use alloy::rpc::types::TransactionReceipt; +use alloy_primitives::{bytes::BufMut, Bloom}; + +// pub trait TransactionReceiptRlp { +// fn rlp_encoded_fields_length(&self, bloom: &Bloom) -> usize; +// fn rlp_encode_fields(&self, bloom: &Bloom, out: &mut dyn BufMut); +// } + +// impl TransactionReceiptRlp for TransactionReceipt { +// /// Returns length of RLP-encoded receipt fields with the given [`Bloom`] without an RLP header. +// fn rlp_encoded_fields_length(&self, bloom: &Bloom) -> usize { +// self.inner.status().length() + +// self.inner.cumulative_gas_used().length() + +// self.inner.logs_bloom().length() + +// self.inner.logs().length() +// } + +// /// RLP-encodes receipt fields with the given [`Bloom`] without an RLP header. +// fn rlp_encode_fields(&self, bloom: &Bloom, out: &mut dyn BufMut) { +// // self.success.encode(out); +// // self.cumulative_gas_used.encode(out); +// // bloom.encode(out); +// // self.logs.encode(out); +// } +// } diff --git a/script/bin/operator.rs b/script/bin/operator.rs index f73c097..5f30937 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -1,18 +1,14 @@ /// Continuously generate proofs & keep light client updated with chain use alloy::{ - network::{Ethereum, EthereumWallet}, - primitives::Address, - providers::{ + consensus::Receipt, eips::BlockNumberOrTag, network::{Ethereum, EthereumWallet}, primitives::Address, providers::{ fillers::{ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller}, Identity, Provider, ProviderBuilder, RootProvider, - }, - signers::local::PrivateKeySigner, - sol, - transports::http::{Client, Http}, + }, rpc::{alloy_rpc_types::Log, types::TransactionReceipt}, signers::{k256::pkcs8::der::Encode, local::PrivateKeySigner}, sol, transports::http::{Client, Http} }; -use alloy_primitives::{B256, U256}; +use alloy_merkle_tree::tree::MerkleTree; +use alloy_primitives::{Bloom, B256, U256}; use anyhow::Result; -use helios_consensus_core::consensus_spec::MainnetConsensusSpec; +use helios_consensus_core::{consensus_spec::MainnetConsensusSpec, types::ExecutionPayload}; use helios_ethereum::consensus::Inner; use helios_ethereum::rpc::http_rpc::HttpRpc; use helios_ethereum::rpc::ConsensusRpc; @@ -25,6 +21,7 @@ use std::env; use std::sync::Arc; use std::time::Duration; use tree_hash::TreeHash; +use alloy_rlp::{bytes::BufMut, BytesMut}; const ELF: &[u8] = include_bytes!("../../elf/riscv32im-succinct-zkvm-elf"); @@ -127,6 +124,9 @@ impl SP1LightClientOperator { async fn request_update( &self, mut client: Inner, + // target_block: u64, + // contract_address, + // event ) -> Result> { // Fetch required values. let contract = SP1LightClient::new(self.contract_address, self.wallet_filler.clone()); @@ -159,13 +159,129 @@ impl SP1LightClientOperator { let mut sync_committee_updates = get_updates(&client).await; let finality_update = client.rpc.get_finality_update().await.unwrap(); + // Check if contract is up to date let latest_block = finality_update.finalized_header.beacon().slot; + if latest_block <= head { info!("Contract is up to date. Nothing to update."); return Ok(None); } + // Introspect target block + if latest_block < target_block { + info!("Target block not reached, yet."); + return Ok(None); + } + + let consensus_block = client.rpc.get_block(target_block).await.unwrap(); + let execution_payload = consensus_block.body.execution_payload(); + + let block = BlockNumberOrTag::from(*execution_payload.block_number()); + let receipts_root = execution_payload.receipts_root(); + let receipts = self.wallet_filler.get_block_receipts(block).await.unwrap().unwrap(); + + let mut tree = MerkleTree::new(); + for receipt in receipts { + + let r: TransactionReceipt = receipt; + + let rr:Receipt = Receipt::from(r.inner.as_receipt_with_bloom()); + + // mut dyn BufMut + // let mut out = Vec::with_capacity(10000); + + // receipt.inner.status().encode(&mut out); + // receipt.inner.cumulative_gas_used().encode(&mut out); + // receipt.inner.logs_bloom().encode(&mut out); + // receipt.inner.logs().encode(&mut out); + + // let mut buf = vec![]; + // buf.put(receipt.inner.as_receipt_with_bloom().unwrap()); + + // let r = RlpReceipt { + // status: receipt.status(), + // cumulative_gas_used: receipt.inner.cumulative_gas_used(), + // logs_bloom: receipt.inner.logs_bloom().clone(), + // logs: receipt.inner.logs().clone(), + // }; + + // let mut stream = RlpStream::new(); + // stream.begin_list(4); + // stream.append(receipt.status()); + // stream.append(receipt.inner.cumulative_gas_used()); + // stream.append(receipt.inner.logs_bloom()); + // stream.append_list(receipt.inner.logs()); + // stream.out().copy_to_bytes(out.remaining_mut()); + + tree.insert(r.encode()); + } + + + + + // // Should be 2 ^ N leaves + // let num_leaves = 16; + // for i in 0..num_leaves { + // tree.insert(B256::from(U256::from(i))); + // } + // tree.finish(); + + // for i in 0..num_leaves { + // let proof = tree.create_proof(&B256::from(U256::from(i))).unwrap(); + // assert!(MerkleTree::verify_proof(&proof)); + // } + + // RLP encode receipts + // let encoded_receipts: Vec> = receipts + // .iter() + // .map(|receipt| { + // let mut stream = RlpStream::new(); + // receipt.rlp_append(&mut stream); + // stream.out() + // }) + // .collect(); + + // // Construct the Patricia Trie + // let mut backend = MemoryBackend::, H256>::default(); + // let mut trie = Trie::new(&mut backend); + + // for (index, encoded_receipt) in encoded_receipts.iter().enumerate() { + // let key = index.to_be_bytes().to_vec(); // Transaction index as key + // trie.insert(&key, encoded_receipt).unwrap(); + // } + + // Compute the receiptsRoot + // let receipts_root = trie.commit().unwrap(); + + + + let parent_hash = execution_payload.parent_hash(); + let block_hash = execution_payload.block_hash(); + + + let body_root = beacon.body_root; + // TOOD: randao reveal, eth1_data, graffiti, proposer_slashings, attester_slashings..., execution_payload + + + let execution = header.execution().unwrap(); + let execution_block_hash = execution.block_hash(); + let receipt_root = execution.receipts_root(); + // TODO: Exeuction Payload: parentHash, blockHash, stateRoot, logsBloom, receiptsRoot, transactions, baseFeePerGas, gasUsed, etc... + // Receipt root: Transaction status, Cumulative gas used, Logs (address, topics, data), Bloom filter for logs. + + // TODO: show that block hash is part of execution payload, which is part of beacon body root + + + // Use alloy to fetch data + // curl -X POST -H "Content-Type: application/json" \ + // --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x1b4", true],"id":1}' \ + // http://127.0.0.1:8545 + + + + + // Optimization: // Skip processing update inside program if next_sync_committee is already stored in contract. // We must still apply the update locally to "sync" the helios client, this is due to From 24b10f615d3f86c5db59a18e847a7d0756721ae1 Mon Sep 17 00:00:00 2001 From: ivebeenherebefore <148863352+ivebeenherebefore@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:54:41 -0600 Subject: [PATCH 2/5] WIP --- script/bin/operator.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/script/bin/operator.rs b/script/bin/operator.rs index 5f30937..ea94d82 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -3,9 +3,9 @@ use alloy::{ consensus::Receipt, eips::BlockNumberOrTag, network::{Ethereum, EthereumWallet}, primitives::Address, providers::{ fillers::{ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller}, Identity, Provider, ProviderBuilder, RootProvider, - }, rpc::{alloy_rpc_types::Log, types::TransactionReceipt}, signers::{k256::pkcs8::der::Encode, local::PrivateKeySigner}, sol, transports::http::{Client, Http} + }, rpc::{types::TransactionReceipt}, signers::{k256::pkcs8::der::Encode, local::PrivateKeySigner}, sol, transports::http::{Client, Http} }; -use alloy_merkle_tree::tree::MerkleTree; +// use alloy_merkle_tree::tree::MerkleTree; use alloy_primitives::{Bloom, B256, U256}; use anyhow::Result; use helios_consensus_core::{consensus_spec::MainnetConsensusSpec, types::ExecutionPayload}; @@ -21,7 +21,7 @@ use std::env; use std::sync::Arc; use std::time::Duration; use tree_hash::TreeHash; -use alloy_rlp::{bytes::BufMut, BytesMut}; +// use alloy_rlp::{bytes::BufMut, BytesMut}; const ELF: &[u8] = include_bytes!("../../elf/riscv32im-succinct-zkvm-elf"); @@ -124,7 +124,7 @@ impl SP1LightClientOperator { async fn request_update( &self, mut client: Inner, - // target_block: u64, + target_block: u64, // contract_address, // event ) -> Result> { @@ -181,12 +181,25 @@ impl SP1LightClientOperator { let receipts_root = execution_payload.receipts_root(); let receipts = self.wallet_filler.get_block_receipts(block).await.unwrap().unwrap(); - let mut tree = MerkleTree::new(); + // let mut tree = MerkleTree::new(); for receipt in receipts { let r: TransactionReceipt = receipt; - let rr:Receipt = Receipt::from(r.inner.as_receipt_with_bloom()); + let _r = r.inner.as_receipt_with_bloom().unwrap(); + // let rr = Receipt::from(*_r); + // rr.en + + // ordered_trie_root_with_encoder(); + + // let rrr = calculate_receipt_root(); + + // pub fn calculate_receipt_root(receipts: &[T]) -> B256 + // where + // T: Encodable2718, + // { + // ordered_trie_root_with_encoder(receipts, |r, buf| r.encode_2718(buf)) + // } // mut dyn BufMut // let mut out = Vec::with_capacity(10000); From 78b8fc859ef6054d73055e4608a44b855fd78394 Mon Sep 17 00:00:00 2001 From: ivebeenherebefore <148863352+ivebeenherebefore@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:07:19 -0600 Subject: [PATCH 3/5] WIP --- Cargo.lock | 1 + Cargo.toml | 1 + elf/riscv32im-succinct-zkvm-elf | Bin 1265588 -> 1265588 bytes primitives/Cargo.toml | 1 + primitives/src/lib.rs | 2 +- primitives/src/receipt.rs | 191 +++++++++++++++++++++++++++----- script/bin/operator.rs | 86 +++++--------- 7 files changed, 198 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2c9d9f5..12b4572 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6814,6 +6814,7 @@ dependencies = [ name = "sp1-helios-primitives" version = "0.1.0" dependencies = [ + "alloy 0.1.4", "alloy-primitives 0.7.7", "alloy-sol-types", "helios-consensus-core", diff --git a/Cargo.toml b/Cargo.toml index 4bdd2e1..38fd741 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ log = "0.4.22" env_logger = "0.11.3" alloy-primitives = "0.7.7" alloy = { version = "0.1.1", features = ["full"] } +alloy-rlp = "0.3.4" anyhow = "1.0.86" reqwest = "0.12.5" tree_hash = "0.7.0" diff --git a/elf/riscv32im-succinct-zkvm-elf b/elf/riscv32im-succinct-zkvm-elf index 4bcc6f559e2e2ee0f791ba9ff06873932a9e1222..498abf00cf07418fba806cbfa410b9e9ac9e412f 100755 GIT binary patch delta 201 zcmdlo-*?M=--Z^(7N!>F7M2#)7Pc1l7LFFq7OocV7M>Q~7QPn#Edr|=g^WzhjEpRd zEX*wp%`A*f3`|U??`afxCWUZ8=1tgDlneDuth+#{bH-Y_KU58 k-**dRQ8oSlHX)tqm5l<1*k#uW%Cx6D32jez687Z<0MX|=z5oCK delta 201 zcmdlo-*?M=--Z^(7N!>F7M2#)7Pc1l7LFFq7OocV7M>Q~7QPn#Edr|=h0F~t3@yz~ z%nS`I4a_Vp42&(O?`afxCWUZ8=1tgDlneDuth+#{bH-Y_KU58 k-**dRQ8oSlHX)tqm5l<1*k#uW%Cx6D32jez687Z<0Nx)v;Q#;t diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 3d184f4..2afb02e 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -9,5 +9,6 @@ authors.workspace = true ssz-rs = { workspace = true } serde = { workspace = true } helios-consensus-core = { workspace = true } +alloy = { workspace = true } alloy-sol-types = { workspace = true } alloy-primitives = { workspace = true } diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index e9fefab..13ad6b5 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -1,2 +1,2 @@ pub mod types; -// pub mod receipt; \ No newline at end of file +pub mod receipt; \ No newline at end of file diff --git a/primitives/src/receipt.rs b/primitives/src/receipt.rs index b9fc714..a60d1e9 100644 --- a/primitives/src/receipt.rs +++ b/primitives/src/receipt.rs @@ -1,25 +1,166 @@ -use alloy::rpc::types::TransactionReceipt; -use alloy_primitives::{bytes::BufMut, Bloom}; - -// pub trait TransactionReceiptRlp { -// fn rlp_encoded_fields_length(&self, bloom: &Bloom) -> usize; -// fn rlp_encode_fields(&self, bloom: &Bloom, out: &mut dyn BufMut); -// } - -// impl TransactionReceiptRlp for TransactionReceipt { -// /// Returns length of RLP-encoded receipt fields with the given [`Bloom`] without an RLP header. -// fn rlp_encoded_fields_length(&self, bloom: &Bloom) -> usize { -// self.inner.status().length() + -// self.inner.cumulative_gas_used().length() + -// self.inner.logs_bloom().length() + -// self.inner.logs().length() -// } - -// /// RLP-encodes receipt fields with the given [`Bloom`] without an RLP header. -// fn rlp_encode_fields(&self, bloom: &Bloom, out: &mut dyn BufMut) { -// // self.success.encode(out); -// // self.cumulative_gas_used.encode(out); -// // bloom.encode(out); -// // self.logs.encode(out); -// } -// } +/// Adapted from reth: https://github.com/paradigmxyz/reth/blob/v1.0.1/crates/primitives/src/receipt.rs +use alloy::{ + consensus::{ReceiptEnvelope, TxType}, + core::rlp::{bytes::BufMut, length_of_length, Encodable, Header}, + primitives::{Bytes, Log}, + rpc::types::{Log as RpcLog, TransactionReceipt}, +}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ReceiptWithBloomEncoder<'a> { + // bloom: &'a Bloom, + receipt: &'a TransactionReceipt>, +} + +impl<'a> ReceiptWithBloomEncoder<'a> { + + /// Create new [`ReceiptWithBloom`] + pub const fn new(receipt: &'a TransactionReceipt>) -> Self { + Self { receipt } + } + + /// Returns the enveloped encoded receipt. + /// + /// See also [`ReceiptWithBloom::encode_enveloped`] + pub fn envelope_encoded(&self) -> Bytes { + let mut buf = Vec::new(); + self.encode_enveloped(&mut buf); + buf.into() + } + + /// Encodes the receipt into its "raw" format. + /// This format is also referred to as "binary" encoding. + /// + /// For legacy receipts, it encodes the RLP of the receipt into the buffer: + /// `rlp([status, cumulativeGasUsed, logsBloom, logs])` as per EIP-2718. + /// For EIP-2718 typed transactions, it encodes the type of the transaction followed by the rlp + /// of the receipt: + /// - EIP-1559, 2930 and 4844 transactions: `tx-type || rlp([status, cumulativeGasUsed, + /// logsBloom, logs])` + pub fn encode_enveloped(&self, out: &mut dyn BufMut) { + self.encode_inner(out, false) + } + + /// Encode receipt with or without the header data. + pub fn encode_inner(&self, out: &mut dyn BufMut, with_header: bool) { + self._encode_inner(out, with_header) + } + + fn raw_receipt(&self) -> &ReceiptEnvelope { + &self.receipt.inner + } + + fn raw_tx_type(&self) -> TxType { + self.receipt.transaction_type() + } + + fn raw_logs(&self) -> Vec<&Log> { + self.raw_receipt().logs().iter().map(|log| &log.inner).collect() + } + + /// Returns the rlp header for the receipt payload. + fn receipt_rlp_header(&self) -> Header { + let mut rlp_head = Header { list: true, payload_length: 0 }; + + // rlp_head.payload_length += self.receipt.success.length(); + rlp_head.payload_length += self.raw_receipt().cumulative_gas_used().length(); + // rlp_head.payload_length += self.bloom.length(); + rlp_head.payload_length += self.raw_receipt().logs_bloom().length(); + rlp_head.payload_length += self.raw_logs().length(); + + #[cfg(feature = "optimism")] + if self.raw_tx_type() == TxType::Deposit { + if let Some(deposit_nonce) = self.receipt.deposit_nonce { + rlp_head.payload_length += deposit_nonce.length(); + } + if let Some(deposit_receipt_version) = self.receipt.deposit_receipt_version { + rlp_head.payload_length += deposit_receipt_version.length(); + } + } + + rlp_head + } + + /// Encodes the receipt data. + fn encode_fields(&self, out: &mut dyn BufMut) { + self.receipt_rlp_header().encode(out); + + // self.receipt.success.encode(out); + self.raw_receipt().status().encode(out); + self.raw_receipt().cumulative_gas_used().encode(out); + self.raw_receipt().logs_bloom().encode(out); + self.raw_logs().encode(out); + + #[cfg(feature = "optimism")] + if self.raw_tx_type() == TxType::Deposit { + if let Some(deposit_nonce) = self.receipt.deposit_nonce { + deposit_nonce.encode(out) + } + if let Some(deposit_receipt_version) = self.receipt.deposit_receipt_version { + deposit_receipt_version.encode(out) + } + } + } + + /// Encode receipt with or without the header data. + fn _encode_inner(&self, out: &mut dyn BufMut, with_header: bool) { + + if matches!(self.raw_tx_type(), TxType::Legacy) { + self.encode_fields(out); + return + } + + let mut payload = Vec::new(); + self.encode_fields(&mut payload); + + if with_header { + let payload_length = payload.len() + 1; + let header = Header { list: false, payload_length }; + header.encode(out); + } + + match self.raw_tx_type() { + TxType::Legacy => unreachable!("legacy already handled"), + + TxType::Eip2930 => { + out.put_u8(0x01); + } + TxType::Eip1559 => { + out.put_u8(0x02); + } + TxType::Eip4844 => { + out.put_u8(0x03); + } + #[cfg(feature = "optimism")] + TxType::Deposit => { + out.put_u8(0x7E); + } + } + out.put_slice(payload.as_ref()); + } + + /// Returns the length of the receipt data. + fn receipt_length(&self) -> usize { + let rlp_head = self.receipt_rlp_header(); + length_of_length(rlp_head.payload_length) + rlp_head.payload_length + } +} + +impl<'a> Encodable for ReceiptWithBloomEncoder<'a> { + + fn encode(&self, out: &mut dyn BufMut) { + self.encode_inner(out, true) + } + + fn length(&self) -> usize { + let mut payload_len = self.receipt_length(); + // account for eip-2718 type prefix and set the list + if !matches!(self.raw_tx_type(), TxType::Legacy) { + payload_len += 1; + // we include a string header for typed receipts, so include the length here + payload_len += length_of_length(payload_len); + } + + payload_len + } +} diff --git a/script/bin/operator.rs b/script/bin/operator.rs index ea94d82..2503b1e 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -1,27 +1,34 @@ /// Continuously generate proofs & keep light client updated with chain use alloy::{ - consensus::Receipt, eips::BlockNumberOrTag, network::{Ethereum, EthereumWallet}, primitives::Address, providers::{ + core::rlp::Encodable, + consensus::{Receipt, TxReceipt}, + eips::BlockNumberOrTag, + network::{Ethereum, EthereumWallet}, + primitives::{Address, Bloom, B256, Log, U256}, + providers::{ fillers::{ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller}, Identity, Provider, ProviderBuilder, RootProvider, - }, rpc::{types::TransactionReceipt}, signers::{k256::pkcs8::der::Encode, local::PrivateKeySigner}, sol, transports::http::{Client, Http} + }, + rpc::types::TransactionReceipt, + signers::local::PrivateKeySigner, + sol, + transports::http::{Client, Http} }; // use alloy_merkle_tree::tree::MerkleTree; -use alloy_primitives::{Bloom, B256, U256}; use anyhow::Result; -use helios_consensus_core::{consensus_spec::MainnetConsensusSpec, types::ExecutionPayload}; +use helios_consensus_core::{consensus_spec::MainnetConsensusSpec, types::{BeaconBlock, ExecutionPayload}}; use helios_ethereum::consensus::Inner; use helios_ethereum::rpc::http_rpc::HttpRpc; use helios_ethereum::rpc::ConsensusRpc; use log::{error, info}; -use sp1_helios_primitives::types::ProofInputs; +use sp1_helios_primitives::{receipt::ReceiptWithBloomEncoder, types::ProofInputs}; use sp1_helios_script::*; -use sp1_sdk::{ProverClient, SP1ProofWithPublicValues, SP1ProvingKey, SP1Stdin}; +use sp1_sdk::{network::proto::network::twirp::axum::extract::FromRef, ProverClient, SP1ProofWithPublicValues, SP1ProvingKey, SP1Stdin}; use ssz_rs::prelude::*; use std::env; use std::sync::Arc; use std::time::Duration; use tree_hash::TreeHash; -// use alloy_rlp::{bytes::BufMut, BytesMut}; const ELF: &[u8] = include_bytes!("../../elf/riscv32im-succinct-zkvm-elf"); @@ -124,7 +131,7 @@ impl SP1LightClientOperator { async fn request_update( &self, mut client: Inner, - target_block: u64, + // target_block: u64, // contract_address, // event ) -> Result> { @@ -159,7 +166,6 @@ impl SP1LightClientOperator { let mut sync_committee_updates = get_updates(&client).await; let finality_update = client.rpc.get_finality_update().await.unwrap(); - // Check if contract is up to date let latest_block = finality_update.finalized_header.beacon().slot; @@ -168,66 +174,30 @@ impl SP1LightClientOperator { return Ok(None); } + + // TODO: Hardcoded values for now + let target_block: u64 = 6000; // TODO move to argument + // Introspect target block if latest_block < target_block { info!("Target block not reached, yet."); return Ok(None); } - let consensus_block = client.rpc.get_block(target_block).await.unwrap(); + let consensus_block: BeaconBlock = client.rpc.get_block(target_block).await.unwrap(); let execution_payload = consensus_block.body.execution_payload(); let block = BlockNumberOrTag::from(*execution_payload.block_number()); let receipts_root = execution_payload.receipts_root(); let receipts = self.wallet_filler.get_block_receipts(block).await.unwrap().unwrap(); + let computed_receipts_root = ordered_trie_root_with_encoder(receipts, |r, buf| ReceiptWithBloomEncoder::new(&r).encode_inner(buf, false)) + // let mut tree = MerkleTree::new(); for receipt in receipts { - let r: TransactionReceipt = receipt; - - let _r = r.inner.as_receipt_with_bloom().unwrap(); - // let rr = Receipt::from(*_r); - // rr.en - - // ordered_trie_root_with_encoder(); - - // let rrr = calculate_receipt_root(); - - // pub fn calculate_receipt_root(receipts: &[T]) -> B256 - // where - // T: Encodable2718, - // { - // ordered_trie_root_with_encoder(receipts, |r, buf| r.encode_2718(buf)) - // } - - // mut dyn BufMut - // let mut out = Vec::with_capacity(10000); - - // receipt.inner.status().encode(&mut out); - // receipt.inner.cumulative_gas_used().encode(&mut out); - // receipt.inner.logs_bloom().encode(&mut out); - // receipt.inner.logs().encode(&mut out); - - // let mut buf = vec![]; - // buf.put(receipt.inner.as_receipt_with_bloom().unwrap()); - - // let r = RlpReceipt { - // status: receipt.status(), - // cumulative_gas_used: receipt.inner.cumulative_gas_used(), - // logs_bloom: receipt.inner.logs_bloom().clone(), - // logs: receipt.inner.logs().clone(), - // }; - - // let mut stream = RlpStream::new(); - // stream.begin_list(4); - // stream.append(receipt.status()); - // stream.append(receipt.inner.cumulative_gas_used()); - // stream.append(receipt.inner.logs_bloom()); - // stream.append_list(receipt.inner.logs()); - // stream.out().copy_to_bytes(out.remaining_mut()); - - tree.insert(r.encode()); + // let encoder = ReceiptWithBloomEncoder::new(&receipt); + // encoder.encode(out); } @@ -273,13 +243,13 @@ impl SP1LightClientOperator { let block_hash = execution_payload.block_hash(); - let body_root = beacon.body_root; + // let body_root = beacon.body_root; // TOOD: randao reveal, eth1_data, graffiti, proposer_slashings, attester_slashings..., execution_payload - let execution = header.execution().unwrap(); - let execution_block_hash = execution.block_hash(); - let receipt_root = execution.receipts_root(); + // let execution = header.execution().unwrap(); + // let execution_block_hash = execution.block_hash(); + // let receipt_root = execution.receipts_root(); // TODO: Exeuction Payload: parentHash, blockHash, stateRoot, logsBloom, receiptsRoot, transactions, baseFeePerGas, gasUsed, etc... // Receipt root: Transaction status, Cumulative gas used, Logs (address, topics, data), Bloom filter for logs. From ee78ba3f8de8feba8f49e1a79bfa1eaca5230012 Mon Sep 17 00:00:00 2001 From: ivebeenherebefore <148863352+ivebeenherebefore@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:45:00 -0600 Subject: [PATCH 4/5] Fix compilation --- Cargo.lock | 1 - Cargo.toml | 1 - primitives/Cargo.toml | 1 - primitives/src/lib.rs | 1 - script/bin/operator.rs | 75 ++------------------------- script/src/lib.rs | 1 + {primitives => script}/src/receipt.rs | 46 ++++++++-------- 7 files changed, 29 insertions(+), 97 deletions(-) rename {primitives => script}/src/receipt.rs (82%) diff --git a/Cargo.lock b/Cargo.lock index 12b4572..2c9d9f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6814,7 +6814,6 @@ dependencies = [ name = "sp1-helios-primitives" version = "0.1.0" dependencies = [ - "alloy 0.1.4", "alloy-primitives 0.7.7", "alloy-sol-types", "helios-consensus-core", diff --git a/Cargo.toml b/Cargo.toml index 38fd741..4bdd2e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,6 @@ log = "0.4.22" env_logger = "0.11.3" alloy-primitives = "0.7.7" alloy = { version = "0.1.1", features = ["full"] } -alloy-rlp = "0.3.4" anyhow = "1.0.86" reqwest = "0.12.5" tree_hash = "0.7.0" diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 2afb02e..3d184f4 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -9,6 +9,5 @@ authors.workspace = true ssz-rs = { workspace = true } serde = { workspace = true } helios-consensus-core = { workspace = true } -alloy = { workspace = true } alloy-sol-types = { workspace = true } alloy-primitives = { workspace = true } diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 13ad6b5..cd40856 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -1,2 +1 @@ pub mod types; -pub mod receipt; \ No newline at end of file diff --git a/script/bin/operator.rs b/script/bin/operator.rs index 2503b1e..95bfea4 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -21,7 +21,7 @@ use helios_ethereum::consensus::Inner; use helios_ethereum::rpc::http_rpc::HttpRpc; use helios_ethereum::rpc::ConsensusRpc; use log::{error, info}; -use sp1_helios_primitives::{receipt::ReceiptWithBloomEncoder, types::ProofInputs}; +use sp1_helios_primitives::types::ProofInputs; use sp1_helios_script::*; use sp1_sdk::{network::proto::network::twirp::axum::extract::FromRef, ProverClient, SP1ProofWithPublicValues, SP1ProvingKey, SP1Stdin}; use ssz_rs::prelude::*; @@ -191,78 +191,13 @@ impl SP1LightClientOperator { let receipts_root = execution_payload.receipts_root(); let receipts = self.wallet_filler.get_block_receipts(block).await.unwrap().unwrap(); - let computed_receipts_root = ordered_trie_root_with_encoder(receipts, |r, buf| ReceiptWithBloomEncoder::new(&r).encode_inner(buf, false)) + // let computed_receipts_root = ordered_trie_root_with_encoder(receipts, |r, buf| ReceiptWithBloomEncoder::new(&r).encode_inner(buf, false)) - // let mut tree = MerkleTree::new(); - for receipt in receipts { + // for receipt in receipts { - // let encoder = ReceiptWithBloomEncoder::new(&receipt); - // encoder.encode(out); - } - - - - - // // Should be 2 ^ N leaves - // let num_leaves = 16; - // for i in 0..num_leaves { - // tree.insert(B256::from(U256::from(i))); + // let encoder = ReceiptWithBloomEncoder::new(&receipt); + // encoder.encode(out); // } - // tree.finish(); - - // for i in 0..num_leaves { - // let proof = tree.create_proof(&B256::from(U256::from(i))).unwrap(); - // assert!(MerkleTree::verify_proof(&proof)); - // } - - // RLP encode receipts - // let encoded_receipts: Vec> = receipts - // .iter() - // .map(|receipt| { - // let mut stream = RlpStream::new(); - // receipt.rlp_append(&mut stream); - // stream.out() - // }) - // .collect(); - - // // Construct the Patricia Trie - // let mut backend = MemoryBackend::, H256>::default(); - // let mut trie = Trie::new(&mut backend); - - // for (index, encoded_receipt) in encoded_receipts.iter().enumerate() { - // let key = index.to_be_bytes().to_vec(); // Transaction index as key - // trie.insert(&key, encoded_receipt).unwrap(); - // } - - // Compute the receiptsRoot - // let receipts_root = trie.commit().unwrap(); - - - - let parent_hash = execution_payload.parent_hash(); - let block_hash = execution_payload.block_hash(); - - - // let body_root = beacon.body_root; - // TOOD: randao reveal, eth1_data, graffiti, proposer_slashings, attester_slashings..., execution_payload - - - // let execution = header.execution().unwrap(); - // let execution_block_hash = execution.block_hash(); - // let receipt_root = execution.receipts_root(); - // TODO: Exeuction Payload: parentHash, blockHash, stateRoot, logsBloom, receiptsRoot, transactions, baseFeePerGas, gasUsed, etc... - // Receipt root: Transaction status, Cumulative gas used, Logs (address, topics, data), Bloom filter for logs. - - // TODO: show that block hash is part of execution payload, which is part of beacon body root - - - // Use alloy to fetch data - // curl -X POST -H "Content-Type: application/json" \ - // --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x1b4", true],"id":1}' \ - // http://127.0.0.1:8545 - - - // Optimization: diff --git a/script/src/lib.rs b/script/src/lib.rs index 4e5ab63..e646b3a 100644 --- a/script/src/lib.rs +++ b/script/src/lib.rs @@ -16,6 +16,7 @@ use std::sync::Arc; use tokio::sync::{mpsc::channel, watch}; use tree_hash::TreeHash; pub mod relay; +pub mod receipt; pub const MAX_REQUEST_LIGHT_CLIENT_UPDATES: u8 = 128; diff --git a/primitives/src/receipt.rs b/script/src/receipt.rs similarity index 82% rename from primitives/src/receipt.rs rename to script/src/receipt.rs index a60d1e9..19339b9 100644 --- a/primitives/src/receipt.rs +++ b/script/src/receipt.rs @@ -68,15 +68,15 @@ impl<'a> ReceiptWithBloomEncoder<'a> { rlp_head.payload_length += self.raw_receipt().logs_bloom().length(); rlp_head.payload_length += self.raw_logs().length(); - #[cfg(feature = "optimism")] - if self.raw_tx_type() == TxType::Deposit { - if let Some(deposit_nonce) = self.receipt.deposit_nonce { - rlp_head.payload_length += deposit_nonce.length(); - } - if let Some(deposit_receipt_version) = self.receipt.deposit_receipt_version { - rlp_head.payload_length += deposit_receipt_version.length(); - } - } + // #[cfg(feature = "optimism")] + // if self.raw_tx_type() == TxType::Deposit { + // if let Some(deposit_nonce) = self.receipt.deposit_nonce { + // rlp_head.payload_length += deposit_nonce.length(); + // } + // if let Some(deposit_receipt_version) = self.receipt.deposit_receipt_version { + // rlp_head.payload_length += deposit_receipt_version.length(); + // } + // } rlp_head } @@ -91,15 +91,15 @@ impl<'a> ReceiptWithBloomEncoder<'a> { self.raw_receipt().logs_bloom().encode(out); self.raw_logs().encode(out); - #[cfg(feature = "optimism")] - if self.raw_tx_type() == TxType::Deposit { - if let Some(deposit_nonce) = self.receipt.deposit_nonce { - deposit_nonce.encode(out) - } - if let Some(deposit_receipt_version) = self.receipt.deposit_receipt_version { - deposit_receipt_version.encode(out) - } - } + // #[cfg(feature = "optimism")] + // if self.raw_tx_type() == TxType::Deposit { + // if let Some(deposit_nonce) = self.receipt.deposit_nonce { + // deposit_nonce.encode(out) + // } + // if let Some(deposit_receipt_version) = self.receipt.deposit_receipt_version { + // deposit_receipt_version.encode(out) + // } + // } } /// Encode receipt with or without the header data. @@ -131,10 +131,10 @@ impl<'a> ReceiptWithBloomEncoder<'a> { TxType::Eip4844 => { out.put_u8(0x03); } - #[cfg(feature = "optimism")] - TxType::Deposit => { - out.put_u8(0x7E); - } + // #[cfg(feature = "optimism")] + // TxType::Deposit => { + // out.put_u8(0x7E); + // } } out.put_slice(payload.as_ref()); } @@ -147,7 +147,7 @@ impl<'a> ReceiptWithBloomEncoder<'a> { } impl<'a> Encodable for ReceiptWithBloomEncoder<'a> { - + fn encode(&self, out: &mut dyn BufMut) { self.encode_inner(out, true) } From 690a3f314114c615831084c785bd3af3e53f4dbe Mon Sep 17 00:00:00 2001 From: ivebeenherebefore <148863352+ivebeenherebefore@users.noreply.github.com> Date: Fri, 20 Dec 2024 12:03:42 -0600 Subject: [PATCH 5/5] Trie root --- Cargo.lock | 31 ++++++++++++++++++++++++++++ Cargo.toml | 4 ++++ script/Cargo.toml | 2 ++ script/bin/operator.rs | 4 ++-- script/src/lib.rs | 4 +++- script/src/trie.rs | 47 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 script/src/trie.rs diff --git a/Cargo.lock b/Cargo.lock index 2c9d9f5..89807a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -912,6 +912,22 @@ dependencies = [ "ws_stream_wasm", ] +[[package]] +name = "alloy-trie" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03704f265cbbb943b117ecb5055fd46e8f41e7dc8a58b1aed20bcd40ace38c15" +dependencies = [ + "alloy-primitives 0.7.7", + "alloy-rlp", + "derive_more 0.99.18", + "hashbrown 0.14.5", + "nybbles", + "serde", + "smallvec", + "tracing", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -4664,6 +4680,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "nybbles" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95f06be0417d97f81fe4e5c86d7d01b392655a9cac9c19a848aa033e18937b23" +dependencies = [ + "alloy-rlp", + "const-hex", + "proptest", + "serde", + "smallvec", +] + [[package]] name = "object" version = "0.36.5" @@ -6841,6 +6870,7 @@ version = "0.1.0" dependencies = [ "alloy 0.1.4", "alloy-primitives 0.7.7", + "alloy-trie", "anyhow", "cargo_metadata", "clap", @@ -6849,6 +6879,7 @@ dependencies = [ "helios-consensus-core", "helios-ethereum", "log", + "nybbles", "reqwest 0.12.9", "serde", "serde_cbor", diff --git a/Cargo.toml b/Cargo.toml index 4bdd2e1..3250ff3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,10 @@ tree_hash = "0.7.0" serde_with = { version = "3.4.0", features = ["hex"] } cargo_metadata = "0.18" +# receipt +alloy-trie = "0.4" +nybbles = "0.2.1" + [patch.crates-io] sha2-v0-9-9 = { git = "https://github.com/sp1-patches/RustCrypto-hashes", package = "sha2", branch = "patch-sha2-v0.9.9" } sha2-v0-10-8 = { git = "https://github.com/sp1-patches/RustCrypto-hashes", package = "sha2", branch = "patch-sha2-v0.10.8" } diff --git a/script/Cargo.toml b/script/Cargo.toml index 792c8b7..66d4b54 100644 --- a/script/Cargo.toml +++ b/script/Cargo.toml @@ -37,6 +37,8 @@ cargo_metadata = { workspace = true } reqwest = { workspace = true } tree_hash = { workspace = true } serde_json = { workspace = true } +alloy-trie = { workspace = true } +nybbles = { workspace = true } [build-dependencies] sp1-build = { workspace = true } diff --git a/script/bin/operator.rs b/script/bin/operator.rs index 95bfea4..7b1b0b1 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -22,7 +22,7 @@ use helios_ethereum::rpc::http_rpc::HttpRpc; use helios_ethereum::rpc::ConsensusRpc; use log::{error, info}; use sp1_helios_primitives::types::ProofInputs; -use sp1_helios_script::*; +use sp1_helios_script::{*, receipt::*, trie::*}; use sp1_sdk::{network::proto::network::twirp::axum::extract::FromRef, ProverClient, SP1ProofWithPublicValues, SP1ProvingKey, SP1Stdin}; use ssz_rs::prelude::*; use std::env; @@ -191,7 +191,7 @@ impl SP1LightClientOperator { let receipts_root = execution_payload.receipts_root(); let receipts = self.wallet_filler.get_block_receipts(block).await.unwrap().unwrap(); - // let computed_receipts_root = ordered_trie_root_with_encoder(receipts, |r, buf| ReceiptWithBloomEncoder::new(&r).encode_inner(buf, false)) + let computed_receipts_root = ordered_trie_root_with_encoder(receipts, |r, buf| ReceiptWithBloomEncoder::new(&r).encode_inner(buf, false)); // for receipt in receipts { diff --git a/script/src/lib.rs b/script/src/lib.rs index e646b3a..6fd3aa7 100644 --- a/script/src/lib.rs +++ b/script/src/lib.rs @@ -15,8 +15,10 @@ use ssz_rs::prelude::*; use std::sync::Arc; use tokio::sync::{mpsc::channel, watch}; use tree_hash::TreeHash; -pub mod relay; + pub mod receipt; +pub mod relay; +pub mod trie; pub const MAX_REQUEST_LIGHT_CLIENT_UPDATES: u8 = 128; diff --git a/script/src/trie.rs b/script/src/trie.rs new file mode 100644 index 0000000..d423e67 --- /dev/null +++ b/script/src/trie.rs @@ -0,0 +1,47 @@ +/// Adapted from reth: https://github.com/paradigmxyz/reth/blob/v1.0.1/crates/trie/common/src/root.rs + +use alloy::{ + core::rlp::{Encodable, encode_fixed_size}, + primitives::{Address, Bloom, B256, Log, U256}, +}; +use alloy_trie::HashBuilder; +use nybbles::Nibbles; + +/// Adjust the index of an item for rlp encoding. +pub const fn adjust_index_for_rlp(i: usize, len: usize) -> usize { + if i > 0x7f { + i + } else if i == 0x7f || i + 1 == len { + 0 + } else { + i + 1 + } +} + +/// Compute a trie root of the collection of rlp encodable items. +pub fn ordered_trie_root(items: &[T]) -> B256 { + ordered_trie_root_with_encoder(items, |item, buf| item.encode(buf)) +} + +/// Compute a trie root of the collection of items with a custom encoder. +pub fn ordered_trie_root_with_encoder(items: &[T], mut encode: F) -> B256 +where + F: FnMut(&T, &mut Vec), +{ + let mut value_buffer = Vec::new(); + + let mut hb = HashBuilder::default(); + let items_len = items.len(); + for i in 0..items_len { + let index = adjust_index_for_rlp(i, items_len); + + let index_buffer = encode_fixed_size(&index); + + value_buffer.clear(); + encode(&items[index], &mut value_buffer); + + hb.add_leaf(Nibbles::unpack(&index_buffer), &value_buffer); + } + + hb.root() +}