From 8e1a97e364d0179cb85ee6e9b93604a55c370733 Mon Sep 17 00:00:00 2001 From: Andreas Bigger Date: Tue, 29 Aug 2023 16:10:20 -0700 Subject: [PATCH] Implement the deposit transaction --- lib/src/host/mod.rs | 4 + primitives/Cargo.toml | 1 + primitives/src/lib.rs | 3 + primitives/src/optimism.rs | 162 ++++++++++++++++++++++++++++++++++ primitives/src/transaction.rs | 24 +++++ 5 files changed, 194 insertions(+) create mode 100644 primitives/src/optimism.rs diff --git a/lib/src/host/mod.rs b/lib/src/host/mod.rs index 49479a1d..3ac1a880 100644 --- a/lib/src/host/mod.rs +++ b/lib/src/host/mod.rs @@ -96,6 +96,8 @@ pub fn get_initial_data( let provider_db = crate::host::provider_db::ProviderDb::new(provider, init_block.number.unwrap().as_u64()); + info!("Created provider db ..."); + // Create input let input = Input { beneficiary: fini_block.author.map(from_ethers_h160).unwrap_or_default(), @@ -120,6 +122,8 @@ pub fn get_initial_data( ..Default::default() }; + info!("Created input: {:?}", input); + // Create the block builder, run the transactions and extract the DB let mut builder = BlockBuilder::new(Ð_MAINNET_CHAIN_SPEC, input) .with_db(provider_db) diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 8774b9cb..32d2297b 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -26,3 +26,4 @@ serde_json = "1.0" [features] ethers = ["dep:ethers-core"] revm = ["dep:revm-primitives"] +optimism = ["dep:revm-primitives"] diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 683285b0..1a2ddbeb 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -29,6 +29,9 @@ pub mod ethers; #[cfg(feature = "revm")] pub mod revm; +#[cfg(feature = "optimism")] +pub mod optimism; + pub use alloy_primitives::*; pub use alloy_rlp as rlp; diff --git a/primitives/src/optimism.rs b/primitives/src/optimism.rs new file mode 100644 index 00000000..df364df2 --- /dev/null +++ b/primitives/src/optimism.rs @@ -0,0 +1,162 @@ +// Copyright 2023 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use alloy_primitives::{Address, Bytes, TxHash}; +use alloy_rlp::{Encodable, EMPTY_STRING_CODE}; +use serde::{Deserialize, Serialize}; + +use crate::transaction::TransactionKind; + +// use reth_codecs::{main_codec, Compact}; +// use reth_rlp::{length_of_length, Decodable, DecodeError, Encodable, Header, +// EMPTY_STRING_CODE}; + +/// Deposit transactions, also known as deposits, are initiated on L1, and executed on L2. +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] +pub struct TxDeposit { + /// Hash that uniquely identifies the source of the deposit. + pub source_hash: TxHash, + /// The address of the sender account. + pub from: Address, + /// The address of the recipient account, or the null (zero-length) address if the + /// deposited transaction is a contract creation. + pub to: TransactionKind, + /// The ETH value to mint on L2. + pub mint: Option, + /// The ETH value to send to the recipient account. + pub value: u128, + /// The gas limit for the L2 transaction. + pub gas_limit: u64, + /// Field indicating if this transaction is exempt from the L2 gas limit. + pub is_system_transaction: bool, + /// Input has two uses depending if transaction is Create or Call (if `to` field is + /// None or Some). + pub input: Bytes, +} + +impl TxDeposit { + /// Computes the length of the RLP-encoded payload in bytes. + /// + /// This method calculates the combined length of all the individual fields + /// of the transaction when they are RLP-encoded. + pub(crate) fn payload_length(&self) -> usize { + let mut len = 0; + len += self.source_hash.length(); + len += self.from.length(); + len += self.to.length(); + len += self.mint.map_or(1, |mint| mint.length()); + len += self.value.length(); + len += self.gas_limit.length(); + len += self.is_system_transaction.length(); + len += self.input.0.length(); + len + } + + /// Encodes the transaction into the provided `out` buffer for the purpose of signing. + pub(crate) fn signing_encode(&self, out: &mut dyn alloy_rlp::BufMut) { + self.encode(out); + } + + /// Computes the length of the RLP-encoded transaction essence in bytes for signing. + /// + /// This method calculates the total length of the transaction when it is RLP-encoded, + /// including any additional bytes required for the encoding format. + pub(crate) fn signing_length(&self) -> usize { + let payload_length = self.payload_length(); + // 'tx type' + 'header length' + 'payload length' + let len = 1 + alloy_rlp::length_of_length(payload_length) + payload_length; + alloy_rlp::length_of_length(len) + len + } + + /// Encodes only the transaction's fields into the desired buffer, without a RLP + /// header. + pub(crate) fn encode_fields(&self, out: &mut dyn bytes::BufMut) { + self.source_hash.encode(out); + self.from.encode(out); + self.to.encode(out); + if let Some(mint) = self.mint { + mint.encode(out); + } else { + out.put_u8(EMPTY_STRING_CODE); + } + self.value.encode(out); + self.gas_limit.encode(out); + self.is_system_transaction.encode(out); + self.input.encode(out); + } + + /// Get the transaction type + pub(crate) fn tx_type(&self) -> u8 { + 0x7e + } +} + +// Implement the Encodable trait for `TxDeposit`. +impl Encodable for TxDeposit { + /// Encodes the [TxDeposit] instance into the provided `out` buffer. + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + let payload_length = self.payload_length(); + // if with_header { + // alloy_rlp::Header { + // list: false, + // payload_length: 1 + alloy_rlp::length_of_length(payload_length) + + // payload_length, } + // .encode(out); + // } + out.put_u8(self.tx_type()); + let header = alloy_rlp::Header { + list: true, + payload_length, + }; + header.encode(out); + self.encode_fields(out); + } + + /// Computes the length of the RLP-encoded [TxDeposit] instance in bytes. + /// + /// This method calculates the total length of the transaction when it is RLP-encoded. + fn length(&self) -> usize { + let payload_length = self.payload_length(); + // 'tx type' + 'header length' + 'payload length' + let len = 1 + alloy_rlp::length_of_length(payload_length) + payload_length; + alloy_rlp::length_of_length(len) + len + } +} + +// #[cfg(test)] +// mod tests { +// use crate::{Bytes, TransactionSigned}; +// use bytes::BytesMut; +// use reth_rlp::Decodable; +// use revm_primitives::hex_literal::hex; +// +// #[test] +// fn test_rlp_roundtrip() { +// let bytes = +// hex!("7ef9015aa044bae9d41b8380d781187b426c6fe43df5fb2fb57bd4466ef6a701e1f01e015694deaddeaddeaddeaddeaddeaddeaddeaddead000194420000000000000000000000000000000000001580808408f0d18001b90104015d8eb900000000000000000000000000000000000000000000000000000000008057650000000000000000000000000000000000000000000000000000000063d96d10000000000000000000000000000000000000000000000000000000000009f35273d89754a1e0387b89520d989d3be9c37c1f32495a88faf1ea05c61121ab0d1900000000000000000000000000000000000000000000000000000000000000010000000000000000000000002d679b567db6187c0c8323fa982cfb88b74dbcc7000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240" +// ); +// +// let tx_a = +// TransactionSigned::decode_enveloped(Bytes::from(&bytes[..])).unwrap(); let tx_b +// = TransactionSigned::decode(&mut &bytes[..]).unwrap(); +// +// let mut buf_a = BytesMut::default(); +// tx_a.encode_enveloped(&mut buf_a); +// assert_eq!(&buf_a[..], &bytes[..]); +// +// let mut buf_b = BytesMut::default(); +// tx_b.encode_enveloped(&mut buf_b); +// assert_eq!(&buf_b[..], &bytes[..]); +// } +// } diff --git a/primitives/src/transaction.rs b/primitives/src/transaction.rs index 04f41563..d4163dce 100644 --- a/primitives/src/transaction.rs +++ b/primitives/src/transaction.rs @@ -17,6 +17,8 @@ use alloy_rlp::{Encodable, EMPTY_STRING_CODE}; use alloy_rlp_derive::RlpEncodable; use serde::{Deserialize, Serialize}; +#[cfg(feature = "optimism")] +use crate::optimism::TxDeposit; use crate::{access_list::AccessList, keccak::keccak, signature::TxSignature}; /// Represents a legacy Ethereum transaction as detailed in [EIP-155](https://eips.ethereum.org/EIPS/eip-155). @@ -218,6 +220,9 @@ pub enum TxEssence { /// This mechanism aims to improve the predictability of gas fees and enhances the /// overall user experience. Eip1559(TxEssenceEip1559), + /// Optimism deposit transaction. + #[cfg(feature = "optimism")] + Deposit(TxDeposit), } // Implement the Encodable trait for the TxEssence enum. @@ -232,6 +237,8 @@ impl Encodable for TxEssence { TxEssence::Legacy(tx) => tx.encode(out), TxEssence::Eip2930(tx) => tx.encode(out), TxEssence::Eip1559(tx) => tx.encode(out), + #[cfg(feature = "optimism")] + TxEssence::Deposit(tx) => tx.encode(out), } } @@ -244,6 +251,8 @@ impl Encodable for TxEssence { TxEssence::Legacy(tx) => tx.length(), TxEssence::Eip2930(tx) => tx.length(), TxEssence::Eip1559(tx) => tx.length(), + #[cfg(feature = "optimism")] + TxEssence::Deposit(tx) => tx.length(), } } } @@ -281,6 +290,13 @@ impl TxEssence { tx.encode(&mut buf); buf } + #[cfg(feature = "optimism")] + TxEssence::Deposit(tx) => { + let mut buf = Vec::with_capacity(tx.length() + 1); + buf.push(0x7e); + tx.encode(&mut buf); + buf + } } } @@ -294,6 +310,8 @@ impl TxEssence { TxEssence::Legacy(tx) => tx.payload_length(), TxEssence::Eip2930(tx) => tx._alloy_rlp_payload_length(), TxEssence::Eip1559(tx) => tx._alloy_rlp_payload_length(), + #[cfg(feature = "optimism")] + TxEssence::Deposit(tx) => tx.payload_length(), } } } @@ -442,6 +460,8 @@ impl Transaction { TxEssence::Legacy(_) => 0x00, TxEssence::Eip2930(_) => 0x01, TxEssence::Eip1559(_) => 0x02, + #[cfg(feature = "optimism")] + TxEssence::Deposit(_) => 0x7e, } } @@ -454,6 +474,8 @@ impl Transaction { TxEssence::Legacy(tx) => tx.gas_limit, TxEssence::Eip2930(tx) => tx.gas_limit, TxEssence::Eip1559(tx) => tx.gas_limit, + #[cfg(feature = "optimism")] + TxEssence::Deposit(tx) => U256::from(tx.gas_limit), } } @@ -466,6 +488,8 @@ impl Transaction { TxEssence::Legacy(tx) => tx.to.into(), TxEssence::Eip2930(tx) => tx.to.into(), TxEssence::Eip1559(tx) => tx.to.into(), + #[cfg(feature = "optimism")] + TxEssence::Deposit(tx) => tx.to.into(), } } }