diff --git a/src/kernel_evm/kernel/src/block.rs b/src/kernel_evm/kernel/src/block.rs index b8c4c3df714bbf7cbfe31da01e13ca843244e0ad..3181bd26ddedee6a1dc97fd544cfa781b6fac73e 100644 --- a/src/kernel_evm/kernel/src/block.rs +++ b/src/kernel_evm/kernel/src/block.rs @@ -3,133 +3,32 @@ // // SPDX-License-Identifier: MIT -use crate::apply::{apply_transaction, TransactionObjectInfo, TransactionReceiptInfo}; +use crate::apply::apply_transaction; +use crate::block_in_progress; use crate::blueprint::{Blueprint, Queue}; use crate::current_timestamp; use crate::error::Error; use crate::error::StorageError::AccountInitialisation; -use crate::error::TransferError::CumulativeGasUsedOverflow; use crate::indexable_storage::IndexableStorage; use crate::storage::{self, init_account_index}; -use evm_execution::account_storage::init_account_storage; -use evm_execution::account_storage::EthereumAccountStorage; +use block_in_progress::BlockInProgress; +use evm_execution::account_storage::{init_account_storage, EthereumAccountStorage}; use evm_execution::precompiles; use evm_execution::precompiles::PrecompileBTreeMap; -use tezos_ethereum::transaction::TransactionObject; +use primitive_types::U256; use tezos_smart_rollup_host::runtime::Runtime; -use primitive_types::{H256, U256}; -use tezos_ethereum::block::{BlockConstants, L2Block}; -use tezos_ethereum::transaction::{ - TransactionReceipt, TransactionStatus, TransactionType, -}; - -fn make_receipt( - block: &L2Block, - receipt_info: TransactionReceiptInfo, - cumulative_gas_used: &mut U256, -) -> Result { - let hash = receipt_info.tx_hash; - let index = receipt_info.index; - let block_hash = block.hash; - let block_number = block.number; - let from = receipt_info.caller; - let to = receipt_info.to; - let effective_gas_price = block.constants().gas_price; - - let tx_receipt = match receipt_info.execution_outcome { - Some(outcome) => { - *cumulative_gas_used = cumulative_gas_used - .checked_add(U256::from(outcome.gas_used)) - .ok_or(Error::Transfer(CumulativeGasUsedOverflow))?; - TransactionReceipt { - hash, - index, - block_hash, - block_number, - from, - to, - cumulative_gas_used: *cumulative_gas_used, - effective_gas_price, - gas_used: U256::from(outcome.gas_used), - contract_address: outcome.new_address, - type_: TransactionType::Legacy, - status: if outcome.is_success { - TransactionStatus::Success - } else { - TransactionStatus::Failure - }, - } - } - None => TransactionReceipt { - hash, - index, - block_hash, - block_number, - from, - to, - cumulative_gas_used: *cumulative_gas_used, - effective_gas_price, - gas_used: U256::zero(), - contract_address: None, - type_: TransactionType::Legacy, - status: TransactionStatus::Failure, - }, - }; - - Ok(tx_receipt) -} - -fn make_receipts( - block: &L2Block, - receipt_infos: Vec, -) -> Result, Error> { - let mut cumulative_gas_used = U256::zero(); - receipt_infos - .into_iter() - .map(|receipt_info| make_receipt(block, receipt_info, &mut cumulative_gas_used)) - .collect() -} - -fn make_objects( - object_infos: Vec, - block_hash: &H256, - block_number: &U256, -) -> Vec { - object_infos - .into_iter() - .map(|object_info| TransactionObject { - block_hash: *block_hash, - block_number: *block_number, - from: object_info.from, - gas_used: object_info.gas_used, - gas_price: object_info.gas_price, - hash: object_info.hash, - input: object_info.input, - nonce: object_info.nonce, - to: object_info.to, - index: object_info.index, - value: object_info.value, - v: object_info.v, - r: object_info.r, - s: object_info.s, - }) - .collect() -} +use tezos_ethereum::block::BlockConstants; fn compute( host: &mut Host, proposal: Blueprint, + block_in_progress: &mut BlockInProgress, block_constants: &BlockConstants, - next_level: U256, precompiles: &PrecompileBTreeMap, evm_account_storage: &mut EthereumAccountStorage, accounts_index: &mut IndexableStorage, -) -> Result { - let mut valid_txs = Vec::new(); - let mut receipts_infos = Vec::new(); - let mut object_infos = Vec::new(); - let mut index = 0; +) -> Result<(), Error> { let transactions = proposal.transactions; for transaction in transactions.into_iter() { @@ -142,33 +41,18 @@ fn compute( block_constants, precompiles, transaction, - index, + block_in_progress.index, evm_account_storage, accounts_index, )? { - valid_txs.push(tx_hash); - receipts_infos.push(receipt_info); - object_infos.push(object_info); - index += 1 + block_in_progress.register_transaction(tx_hash, object_info.gas_used)?; + let receipt = block_in_progress.make_receipt(receipt_info)?; + storage::store_transaction_receipt(host, &receipt)?; + let object = block_in_progress.make_object(object_info); + storage::store_transaction_object(host, &object)?; } } - - let timestamp = current_timestamp(host); - let new_block = L2Block::new(next_level, valid_txs, timestamp); - storage::store_current_block(host, &new_block)?; - storage::store_transaction_receipts( - host, - &make_receipts(&new_block, receipts_infos)?, - )?; - // Note that this is not efficient nor "properly" implemented. This - // is a temporary hack to answer to third-party tools that asks - // for transaction objects. - storage::store_transaction_objects( - host, - &make_objects(object_infos, &new_block.hash, &new_block.number), - )?; - - Ok(new_block) + Ok(()) } pub fn produce(host: &mut Host, queue: Queue) -> Result<(), Error> { @@ -187,19 +71,22 @@ pub fn produce(host: &mut Host, queue: Queue) -> Result<(), Error let precompiles = precompiles::precompile_set::(); for proposal in queue.proposals { + // TODO: https://gitlab.com/tezos/tezos/-/issues/5873 + // add proposal to the container + let mut block_in_progress = + BlockInProgress::new(current_block_number, current_constants.gas_price)?; compute( host, proposal, + &mut block_in_progress, ¤t_constants, - current_block_number, &precompiles, &mut evm_account_storage, &mut accounts_index, - ) - .map(|new_block| { - current_block_number = new_block.number + 1; - current_constants = new_block.constants() - })?; + )?; + let new_block = block_in_progress.finalize_and_store(host)?; + current_block_number = new_block.number + 1; + current_constants = new_block.constants(); } Ok(()) } @@ -213,11 +100,11 @@ mod tests { use crate::storage::internal_for_tests::{ read_transaction_receipt, read_transaction_receipt_status, }; - use crate::storage::{ - init_account_index, init_blocks_index, init_transaction_hashes_index, + use crate::storage::{init_blocks_index, init_transaction_hashes_index}; + use evm_execution::account_storage::{ + account_path, init_account_storage, EthereumAccountStorage, }; - use evm_execution::account_storage::{account_path, EthereumAccountStorage}; - use primitive_types::{H160, H256}; + use primitive_types::{H160, H256, U256}; use std::str::FromStr; use tezos_ethereum::signatures::EthereumTransactionCommon; use tezos_ethereum::transaction::{TransactionStatus, TRANSACTION_HASH_SIZE}; diff --git a/src/kernel_evm/kernel/src/block_in_progress.rs b/src/kernel_evm/kernel/src/block_in_progress.rs new file mode 100644 index 0000000000000000000000000000000000000000..1040e639de4fe214416a678f33546fed9848c2b4 --- /dev/null +++ b/src/kernel_evm/kernel/src/block_in_progress.rs @@ -0,0 +1,152 @@ +// SPDX-FileCopyrightText: 2023 Marigold +// SPDX-FileCopyrightText: 2023 Nomadic Labs +// SPDX-FileCopyrightText: 2023 Functori +// +// SPDX-License-Identifier: MIT + +use crate::apply::{TransactionObjectInfo, TransactionReceiptInfo}; +use crate::current_timestamp; +use crate::error::Error; +use crate::error::TransferError::CumulativeGasUsedOverflow; +use crate::storage; +use primitive_types::{H256, U256}; +use tezos_ethereum::block::L2Block; +use tezos_ethereum::transaction::*; +use tezos_smart_rollup_debug::Runtime; + +/// Container for all data needed during block computation +pub struct BlockInProgress { + /// block number + pub number: U256, + /// list of transactions executed without issue + valid_txs: Vec<[u8; 32]>, + /// gas accumulator + pub cumulative_gas: U256, + /// index for next transaction + pub index: u32, + /// gas price for transactions in the block being created + pub gas_price: U256, + /// hash to use for receipt + /// (computed from number, not the correct way to do it) + pub hash: H256, +} + +impl BlockInProgress { + pub fn new(number: U256, gas_price: U256) -> Result { + let block_in_progress = BlockInProgress { + number, + valid_txs: Vec::new(), + cumulative_gas: U256::zero(), + index: 0, + gas_price, + // hash is not the true value of the hashed block + // it should be computed at the end, and not included in the receipt + // the block is referenced in the storage by the block number anyway + hash: H256(number.into()), + }; + + Ok(block_in_progress) + } + + pub fn register_transaction( + &mut self, + tx_hash: [u8; 32], + gas_used: U256, + ) -> Result<(), Error> { + self.cumulative_gas = self + .cumulative_gas + .checked_add(gas_used) + .ok_or(Error::Transfer(CumulativeGasUsedOverflow))?; + self.valid_txs.push(tx_hash); + self.index += 1; + Ok(()) + } + + pub fn finalize_and_store( + self, + host: &mut Host, + ) -> Result { + let timestamp = current_timestamp(host); + let new_block = L2Block::new(self.number, self.valid_txs, timestamp); + storage::store_current_block(host, &new_block)?; + Ok(new_block) + } + + pub fn make_receipt( + &self, + receipt_info: TransactionReceiptInfo, + ) -> Result { + let TransactionReceiptInfo { + tx_hash: hash, + index, + caller: from, + to, + execution_outcome, + .. + } = receipt_info; + + let &Self { + hash: block_hash, + number: block_number, + gas_price: effective_gas_price, + cumulative_gas, + .. + } = self; + + let tx_receipt = match execution_outcome { + Some(outcome) => TransactionReceipt { + hash, + index, + block_hash, + block_number, + from, + to, + cumulative_gas_used: cumulative_gas, + effective_gas_price, + gas_used: U256::from(outcome.gas_used), + contract_address: outcome.new_address, + type_: TransactionType::Legacy, + status: if outcome.is_success { + TransactionStatus::Success + } else { + TransactionStatus::Failure + }, + }, + None => TransactionReceipt { + hash, + index, + block_hash, + block_number, + from, + to, + cumulative_gas_used: cumulative_gas, + effective_gas_price, + gas_used: U256::zero(), + contract_address: None, + type_: TransactionType::Legacy, + status: TransactionStatus::Failure, + }, + }; + + Ok(tx_receipt) + } + + pub fn make_object(&self, object_info: TransactionObjectInfo) -> TransactionObject { + TransactionObject { + block_hash: self.hash, + block_number: self.number, + from: object_info.from, + gas_used: object_info.gas_used, + gas_price: object_info.gas_price, + hash: object_info.hash, + input: object_info.input, + nonce: object_info.nonce, + to: object_info.to, + index: object_info.index, + value: object_info.value, + v: object_info.v, + r: object_info.r, + s: object_info.s, + } + } +} diff --git a/src/kernel_evm/kernel/src/lib.rs b/src/kernel_evm/kernel/src/lib.rs index da1bd7f06fd29108f64b232cd071dbfe1faba609..c5306b85f5039f78d7a577906c5d5d3a39e3db4f 100644 --- a/src/kernel_evm/kernel/src/lib.rs +++ b/src/kernel_evm/kernel/src/lib.rs @@ -28,6 +28,7 @@ use crate::upgrade::upgrade_kernel; mod apply; mod block; +mod block_in_progress; mod blueprint; mod error; mod inbox; diff --git a/src/kernel_evm/kernel/src/storage.rs b/src/kernel_evm/kernel/src/storage.rs index 385104b128ac2e2c8f8a0f6987d31e50ec56878e..03e90077fe99491567406a23f6977c0bbb8f8820 100644 --- a/src/kernel_evm/kernel/src/storage.rs +++ b/src/kernel_evm/kernel/src/storage.rs @@ -348,50 +348,26 @@ pub fn store_simulation_status( } pub fn store_transaction_receipt( - receipt_path: &OwnedPath, host: &mut Host, receipt: &TransactionReceipt, ) -> Result<(), Error> { - let bytes = receipt.rlp_bytes(); - host.store_write_all(receipt_path, &bytes)?; + // For each transaction hash there exists a receipt and an object. As + // such the indexing must be done either with the objects or the + // receipts otherwise they would be indexed twice. We chose to index + // hashes using the receipts. + let mut transaction_hashes_index = init_transaction_hashes_index()?; + index_transaction_hash(host, &receipt.hash, &mut transaction_hashes_index)?; + let receipt_path = receipt_path(&receipt.hash)?; + host.store_write_all(&receipt_path, &receipt.rlp_bytes())?; Ok(()) } pub fn store_transaction_object( - object_path: &OwnedPath, host: &mut Host, object: &TransactionObject, ) -> Result<(), Error> { - let bytes = object.rlp_bytes(); - host.store_write_all(object_path, &bytes)?; - Ok(()) -} - -pub fn store_transaction_objects( - host: &mut Host, - objects: &[TransactionObject], -) -> Result<(), Error> { - for object in objects { - let object_path = object_path(&object.hash)?; - store_transaction_object(&object_path, host, object)?; - } - Ok(()) -} - -pub fn store_transaction_receipts( - host: &mut Host, - receipts: &[TransactionReceipt], -) -> Result<(), Error> { - let mut transaction_hashes_index = init_transaction_hashes_index()?; - for receipt in receipts { - // For each transaction hash there exists a receipt and an object. As - // such the indexing must be done either with the objects or the - // receipts otherwise they would be indexed twice. We chose to index - // hashes using the receipts. - index_transaction_hash(host, &receipt.hash, &mut transaction_hashes_index)?; - let receipt_path = receipt_path(&receipt.hash)?; - store_transaction_receipt(&receipt_path, host, receipt)? - } + let object_path = object_path(&object.hash)?; + host.store_write_all(&object_path, &object.rlp_bytes())?; Ok(()) }