diff --git a/EVM.md b/EVM.md index dc3ac85632..0e631987d5 100644 --- a/EVM.md +++ b/EVM.md @@ -61,9 +61,9 @@ Implemented in this workspace: creating or funding the corresponding EVM-native purse identity. - [EIP-7702][eip-7702] type `0x04` set-code transactions, with authorization lists passed through to `revm` for Prague execution. -- [EIP-4788][eip-4788] beacon roots predeploy, native pre-block state update, - and native direct-call lookup. Casper stores the parent Casper block hash as - the root value. +- [EIP-4788][eip-4788] beacon roots predeploy with a native direct-call + shortcut that returns a zero root. Casper does not support beacon-root + history or pre-block state updates. - [EIP-2935][eip-2935] block-hash history predeploy and native direct-call lookup backed by indexed Casper block headers. @@ -80,7 +80,8 @@ Implemented in the sidecar workspace for validation: - `eth_getTransactionReceipt` projects logs stored in `ExecutionResult::Evm` as Ethereum receipt log entries. - `eth_getBlockByNumber` projects `parentBeaconBlockRoot` as the parent Casper - block hash, matching Casper's [EIP-4788][eip-4788] system-contract value. + block hash. This sidecar projection is independent of the zero-valued + [EIP-4788][eip-4788] contract shortcut. - Development-only Cargo patches pointing sidecar at this node workspace for unreleased `casper-types` and `casper-binary-port` changes. @@ -107,7 +108,7 @@ Ethereum JSON-RPC method names below refer to the Ethereum | `EvmSpec::Prague` / `revm::SpecId::PRAGUE` | Implemented. | Execution behavior is delegated to `revm`; Casper does not maintain its own EVM interpreter. | | [EIP-2537][eip-2537] BLS12-381 precompiles | Delegated to `revm`. | Expected at `0x0b` through `0x11`, but Casper-owned conformance tests are still needed for gas costs, malformed input, subgroup checks, and failure behavior. | | [EIP-2935][eip-2935] block-hash history contract | Implemented with Casper storage semantics. | The standard address, bytecode, and 8191-block interface are present, but direct calls read indexed LMDB block headers instead of Merkleized contract storage. There is no pre-block system call or gradual ring-buffer fill. | -| [EIP-4788][eip-4788] beacon roots contract | Implemented. | The standard address, bytecode, and interface are present. Casper writes and reads the parent Casper block hash natively through block-global state rather than executing the predeploy bytecode. | +| [EIP-4788][eip-4788] beacon roots contract | Intentionally unsupported (non-goal). | The standard address and bytecode are present, but direct `CALL` and `STATICCALL` lookups are intercepted and always return an all-zero root. Casper does not write beacon-root history. Do not treat this product decision as an open compatibility gap. | | [EIP-6110][eip-6110] validator deposit requests | Missing / decision needed. | Ethereum-specific deposit-log-to-request flow. Full support requires [EIP-7685][eip-7685] request construction and commitment. | | [EIP-7002][eip-7002] withdrawal request predeploy | Missing / decision needed. | Contract-visible predeploy at `0x00000961Ef480Eb55e80D19ad83579A64c007002` is absent. Full support requires queue/fee state, post-block extraction, and [EIP-7685][eip-7685] request output. | | [EIP-7251][eip-7251] consolidation request predeploy | Missing / decision needed. | Contract-visible predeploy at `0x0000BBdDc7CE488642fb579F8B00f3a590007251` is absent. Full support has the same request-output dependency as EIP-7002. | @@ -136,7 +137,7 @@ Ethereum JSON-RPC method names below refer to the Ethereum | `NUMBER`, `TIMESTAMP`, `GASLIMIT`, `BASEFEE` | Implemented. | Timestamp is Casper block time in seconds. Base fee is chainspec-configured and wei-denominated through `wei_per_mote`, not Ethereum's dynamic base-fee adjustment. | | `COINBASE` | Implemented with Casper semantics. | The address is derived from the Casper block proposer public key. | | `CHAINID` | Implemented. | Transaction chain ID is enforced against chainspec `[evm].chain_id`. | -| `PREVRANDAO` | Weak / missing semantic mapping. | Casper does not plumb a randomness field into the EVM block context, so contracts should not treat it as Ethereum beacon randomness. | +| `PREVRANDAO` | Implemented with Casper semantics. | The value is derived from the parent Casper block's accumulated seed. It provides Casper randomness, not Ethereum beacon-chain RANDAO semantics. | | `BLOBHASH` and blob-related block context | Missing / misleading. | No accepted blob transactions means no meaningful blob versioned hashes, blob gas, or blob fee context. | | Fee charging | Casper-specific. | `revm` fee charging is disabled; Casper hold, refund, and fee accounting owns balance effects according to the chainspec. | @@ -181,17 +182,19 @@ executor. previous Cancun/Dencun upgrade. It is included in this review because a Prague Ethereum-compatible environment has this contract. In this branch, Casper installs the exact EIP-4788 runtime bytecode at -`0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02`, writes the parent hash natively -before user transactions, and serves direct calls through the Casper -precompile provider. It uses: +`0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02`. Direct `CALL` and `STATICCALL` +lookups are intercepted by the Casper precompile provider and currently +return: ```text -parentBeaconBlockRoot := parent Casper block hash +parentBeaconBlockRoot := 0x0000000000000000000000000000000000000000000000000000000000000000 ``` -This gives contracts a deterministic consensus-root oracle for Casper. It is -not strict Ethereum beacon-chain semantics. The detailed, audited compatibility -matrix is in [Current Status](#current-status). +Casper does not support Ethereum beacon-chain roots, run the EIP-4788 pre-block +system call, or persist its ring buffer. The zero-valued response is not strict +EIP-4788 semantics. This is an intentional product non-goal, not an open gap; +revisit it only if the product decision changes. The detailed, audited +compatibility matrix is in [Current Status](#current-status). Casper installs the exact EIP-2935 runtime bytecode at `0x0000F90827F1C53a10cb7A02335B175320002935`. Direct `CALL` and `STATICCALL` @@ -199,8 +202,8 @@ lookups are intercepted natively and read indexed Casper block headers through the data access layer. Casper does not execute an EIP-2935 block-boundary system call or populate the standard contract-storage ring. -The highest-priority smart-contract-visible gaps after EIP-4788 and EIP-2935 -are request predeploy decisions for [EIP-7002][eip-7002] and +The highest-priority smart-contract-visible gaps are request predeploy +decisions for [EIP-7002][eip-7002] and [EIP-7251][eip-7251], and explicit Prague conformance coverage for [EIP-2537][eip-2537], [EIP-7623][eip-7623], and [EIP-7702][eip-7702]. diff --git a/execution_engine_testing/tests/src/test/explorer/faucet.rs b/execution_engine_testing/tests/src/test/explorer/faucet.rs index 77a77ae8c5..fac9d27488 100644 --- a/execution_engine_testing/tests/src/test/explorer/faucet.rs +++ b/execution_engine_testing/tests/src/test/explorer/faucet.rs @@ -663,14 +663,14 @@ fn faucet_costs() { // This test will fail if execution costs vary. The expected costs should not be updated // without understanding why the cost has changed. If the costs do change, it should be // reflected in the "Costs by Entry Point" section of the faucet crate's README.md. - const EXPECTED_FAUCET_INSTALL_COST: u64 = 119_810_320_929; + const EXPECTED_FAUCET_INSTALL_COST: u64 = 118_807_845_390; const EXPECTED_FAUCET_INSTALL_COST_ALT: u64 = 149_230_872_143; - const EXPECTED_FAUCET_SET_VARIABLES_COST: u64 = 79_749_265; + const EXPECTED_FAUCET_SET_VARIABLES_COST: u64 = 79_790_440; - const EXPECTED_FAUCET_CALL_BY_INSTALLER_COST: u64 = 2_652_913_398; + const EXPECTED_FAUCET_CALL_BY_INSTALLER_COST: u64 = 2_652_954_573; - const EXPECTED_FAUCET_CALL_BY_USER_COST: u64 = 2_558_746_881; + const EXPECTED_FAUCET_CALL_BY_USER_COST: u64 = 2_558_820_996; let installer_account = AccountHash::new([1u8; 32]); let user_account: AccountHash = AccountHash::new([2u8; 32]); diff --git a/executor/evm/src/db.rs b/executor/evm/src/db.rs index 40a15a5ef1..fa8d0ef3e3 100644 --- a/executor/evm/src/db.rs +++ b/executor/evm/src/db.rs @@ -4,7 +4,6 @@ use casper_storage::{ data_access_layer::DataAccessLayer, eip2935, global_state::{error::Error as GlobalStateError, state::StateReader}, - tracking_copy::TrackingCopyExt, TrackingCopy, }; use casper_types::{evm, CLValue, EvmAddr, Key, StoredValue, U512}; @@ -92,38 +91,6 @@ where }; self.block_hash_at_height(requested_height).map(Some) } - - /// Executes the native EIP-4788 `get` operation. - /// - /// The lookup is backed only by the current tracking copy; EVM execution must not depend on - /// locally retained block history. - pub(crate) fn eip4788_get(&mut self, input: &[u8]) -> Result, DbError> { - // EIP-4788 accepts one uint256 timestamp. Values wider than u64 must revert rather - // than truncate to a colliding ring-buffer timestamp. - if input.len() != 32 || input[..24].iter().any(|byte| *byte != 0) { - return Ok(None); - } - - let timestamp = u64::from_be_bytes( - input[24..] - .try_into() - .expect("the final 8 bytes of a 32-byte input have fixed length"), - ); - if timestamp == 0 { - return Ok(None); - } - - let Some((stored_timestamp, block_hash)) = - self.tracking_copy.get_eip4788_parent_hash(timestamp)? - else { - return Ok(None); - }; - if stored_timestamp != timestamp { - return Ok(None); - } - - Ok(Some(tx::to_revm_block_hash(block_hash))) - } } impl Database for CasperDb<'_, R, S> diff --git a/executor/evm/src/precompiles.rs b/executor/evm/src/precompiles.rs index 61ec952123..f64ffdd7e7 100644 --- a/executor/evm/src/precompiles.rs +++ b/executor/evm/src/precompiles.rs @@ -82,12 +82,14 @@ where && inputs.bytecode_address == beacon_roots_address && matches!(inputs.scheme, CallScheme::Call | CallScheme::StaticCall) { - // Copy the input before borrowing the database mutably. The input may be backed by - // revm's shared memory buffer. - let input = inputs.input.bytes(context); - let lookup_result = context.db_mut().eip4788_get(&input); - let result = - native_get_result(context, lookup_result, inputs.gas_limit, inputs.reservoir); + // Casper does not support Ethereum beacon-chain roots. Keep the predeploy callable + // without maintaining placeholder state. + let result = native_get_result( + context, + Ok(Some(B256::ZERO)), + inputs.gas_limit, + inputs.reservoir, + ); return Ok(Some(result)); } diff --git a/executor/evm/src/request.rs b/executor/evm/src/request.rs index c04daf803d..74c6e3e380 100644 --- a/executor/evm/src/request.rs +++ b/executor/evm/src/request.rs @@ -88,6 +88,7 @@ pub struct BlockContext { /// /// Defaults to chainspec `[evm].base_fee * [evm].wei_per_mote`. pub base_fee: Option, + /// Casper parent-block accumulated seed exposed through the `PREVRANDAO` opcode. pub prevrandao: evm::Hash, } diff --git a/executor/evm/tests/executor.rs b/executor/evm/tests/executor.rs index 48af34f411..906efc77af 100644 --- a/executor/evm/tests/executor.rs +++ b/executor/evm/tests/executor.rs @@ -24,15 +24,14 @@ use casper_storage::{ CommitProvider, StateProvider, StateReader, }, }, - tracking_copy::TrackingCopyExt, TrackingCopy, }; use casper_types::{ - contracts::NamedKeys, evm, AccessRights, Account, BlockGlobalAddr, BlockHash, BlockHeader, - BlockHeaderV2, ByteCode, ByteCodeKind, CLValue, ChainspecRegistry, Digest, EraId, EvmAddr, - EvmConfig, EvmSpec, EvmTransaction, GenesisAccount, GenesisConfig, HoldBalanceHandling, Key, - Motes, ProtocolVersion, PublicKey, SecretKey, StorageCosts, StoredValue, SystemConfig, - Timestamp, URef, WasmConfig, DEFAULT_WEI_PER_MOTE, U256 as CasperU256, U512, + contracts::NamedKeys, evm, AccessRights, Account, BlockHash, BlockHeader, BlockHeaderV2, + ByteCode, ByteCodeKind, CLValue, ChainspecRegistry, Digest, EraId, EvmAddr, EvmConfig, EvmSpec, + EvmTransaction, GenesisAccount, GenesisConfig, HoldBalanceHandling, Key, Motes, + ProtocolVersion, PublicKey, SecretKey, StorageCosts, StoredValue, SystemConfig, Timestamp, + URef, WasmConfig, DEFAULT_WEI_PER_MOTE, U256 as CasperU256, U512, }; use once_cell::sync::OnceCell; use revm::bytecode::opcode; @@ -842,16 +841,6 @@ fn seed_evm_code>( ); } -fn seed_eip4788_parent_hash>( - tracking_copy: &mut TrackingCopy, - timestamp: u64, - parent_hash: BlockHash, -) { - tracking_copy - .set_eip4788_parent_hash(timestamp, parent_hash) - .expect("EIP-4788 tuple should encode"); -} - fn read_evm_nonce>( tracking_copy: &mut TrackingCopy, address: evm::Address, @@ -920,206 +909,30 @@ fn prague_bls12_g1_add_precompile_delegates_to_revm() { } #[test] -fn eip4788_native_lookup_bypasses_predeploy_bytecode() { - let executor = executor(EvmSpec::Prague); - let (mut tracking_copy, data_access_layer, _tempdir) = tracking_copy(); - let timestamp = block().timestamp; - let root = [0xab; evm::HASH_LENGTH]; - - seed_eip4788_parent_hash( - &mut tracking_copy, - timestamp, - BlockHash::new(Digest::from_raw(root)), - ); - // A direct call must use the native lookup even when the installed code - // would revert, proving that the bytecode is not executed. - seed_evm_code( - &mut tracking_copy, - eip4788::BEACON_ROOTS_ADDRESS, - reverting_runtime(), - ); - - let query = execute_call( - &executor, - &data_access_layer, - &mut tracking_copy, - evm::Address::ZERO, - Some(eip4788::BEACON_ROOTS_ADDRESS), - word(timestamp).to_vec(), - ); - assert_eq!(query.output, root); -} - -#[test] -fn eip4788_returns_a_matching_zero_parent_hash() { +fn eip4788_native_shortcut_always_returns_zero() { let executor = executor(EvmSpec::Prague); let (mut tracking_copy, data_access_layer, _tempdir) = tracking_copy(); - let timestamp = block().timestamp; - let zero_hash = [0; evm::HASH_LENGTH]; - seed_eip4788_parent_hash( - &mut tracking_copy, - timestamp, - BlockHash::new(Digest::from_raw(zero_hash)), - ); + // The shortcut must bypass the installed bytecode and avoid all state reads. seed_evm_code( &mut tracking_copy, eip4788::BEACON_ROOTS_ADDRESS, reverting_runtime(), ); - let query = execute_call( - &executor, - &data_access_layer, - &mut tracking_copy, - evm::Address::ZERO, - Some(eip4788::BEACON_ROOTS_ADDRESS), - word(timestamp).to_vec(), - ); - - assert_eq!(query.output, zero_hash); -} - -#[test] -fn eip4788_unknown_and_overwritten_timestamps_revert() { - let executor = executor(EvmSpec::Prague); - let (mut tracking_copy, data_access_layer, _tempdir) = tracking_copy(); - let timestamp = block().timestamp; - let replacement_timestamp = timestamp + eip4788::HISTORY_BUFFER_LENGTH; - let replacement_root = [0xcd; evm::HASH_LENGTH]; - - seed_eip4788_parent_hash( - &mut tracking_copy, - timestamp, - BlockHash::new(Digest::from_raw([0xab; evm::HASH_LENGTH])), - ); - seed_evm_code( - &mut tracking_copy, - eip4788::BEACON_ROOTS_ADDRESS, - eip4788::BEACON_ROOTS_CODE.to_vec(), - ); - - let unknown = executor - .execute( + for input in [vec![], word(block().timestamp).to_vec(), vec![0xff; 33]] { + let query = execute_call( + &executor, &data_access_layer, &mut tracking_copy, - call_request( - evm::Address::ZERO, - Some(eip4788::BEACON_ROOTS_ADDRESS), - word(timestamp + 1).to_vec(), - CasperU256::zero(), - ), - ) - .expect("EVM call should execute"); - assert_eq!(unknown.status, ExecutionStatus::Revert); - - // Writing a timestamp one full ring ahead overwrites the same Global - // State key. The old timestamp must now revert rather than return the - // new root. - seed_eip4788_parent_hash( - &mut tracking_copy, - replacement_timestamp, - BlockHash::new(Digest::from_raw(replacement_root)), - ); - - let stale = executor - .execute( - &data_access_layer, - &mut tracking_copy, - call_request( - evm::Address::ZERO, - Some(eip4788::BEACON_ROOTS_ADDRESS), - word(timestamp).to_vec(), - CasperU256::zero(), - ), - ) - .expect("EVM call should execute"); - assert_eq!(stale.status, ExecutionStatus::Revert); - - let query = execute_call( - &executor, - &data_access_layer, - &mut tracking_copy, - evm::Address::ZERO, - Some(eip4788::BEACON_ROOTS_ADDRESS), - word(replacement_timestamp).to_vec(), - ); - assert_eq!(query.output, replacement_root); -} - -#[test] -fn eip4788_rejects_invalid_calldata() { - let executor = executor(EvmSpec::Prague); - let (mut tracking_copy, data_access_layer, _tempdir) = tracking_copy(); - let timestamp = block().timestamp; - - seed_eip4788_parent_hash( - &mut tracking_copy, - timestamp, - BlockHash::new(Digest::from_raw([0xab; evm::HASH_LENGTH])), - ); - seed_evm_code( - &mut tracking_copy, - eip4788::BEACON_ROOTS_ADDRESS, - eip4788::BEACON_ROOTS_CODE.to_vec(), - ); - - let mut oversized_timestamp = word(timestamp); - oversized_timestamp[0] = 1; - for input in [ - vec![], - vec![0; 31], - vec![0; 32], - oversized_timestamp.to_vec(), - ] { - let outcome = executor - .execute( - &data_access_layer, - &mut tracking_copy, - call_request( - evm::Address::ZERO, - Some(eip4788::BEACON_ROOTS_ADDRESS), - input, - CasperU256::zero(), - ), - ) - .expect("EVM call should execute"); - assert_eq!(outcome.status, ExecutionStatus::Revert); + evm::Address::ZERO, + Some(eip4788::BEACON_ROOTS_ADDRESS), + input, + ); + assert_eq!(query.output, vec![0; evm::HASH_LENGTH]); } } -#[test] -fn eip4788_preserves_tracking_copy_errors() { - let executor = executor(EvmSpec::Prague); - let (mut tracking_copy, data_access_layer, _tempdir) = tracking_copy(); - let timestamp = block().timestamp; - seed_evm_code( - &mut tracking_copy, - eip4788::BEACON_ROOTS_ADDRESS, - eip4788::BEACON_ROOTS_CODE.to_vec(), - ); - tracking_copy.write( - Key::BlockGlobal(BlockGlobalAddr::BlockParentHash { - slot: timestamp % eip4788::HISTORY_BUFFER_LENGTH, - }), - StoredValue::CLValue(CLValue::from_t(1u64).expect("value should encode")), - ); - - let error = executor - .execute( - &data_access_layer, - &mut tracking_copy, - call_request( - evm::Address::ZERO, - Some(eip4788::BEACON_ROOTS_ADDRESS), - word(timestamp).to_vec(), - CasperU256::zero(), - ), - ) - .expect_err("malformed EIP-4788 state should fail execution"); - assert!(matches!(error, Error::Database(DbError::TrackingCopy(_)))); -} - #[test] fn eip2935_native_lookup_reads_indexed_header_and_bypasses_bytecode() { let executor = executor(EvmSpec::Prague); diff --git a/node/src/components/contract_runtime/operations.rs b/node/src/components/contract_runtime/operations.rs index 42be4c9c81..491a53251c 100644 --- a/node/src/components/contract_runtime/operations.rs +++ b/node/src/components/contract_runtime/operations.rs @@ -47,9 +47,9 @@ use casper_types::{ execution::{Effects, ExecutionResult, TransformKindV2, TransformV2}, system::handle_payment::ARG_AMOUNT, BlockHash, BlockHeader, BlockTime, BlockV2, CLValue, Chainspec, ChecksumRegistry, Digest, - EntityAddr, EraEndV2, EraId, EvmSpec, FeeHandling, Gas, InvalidTransaction, - InvalidTransactionV1, Key, ProtocolVersion, PublicKey, RefundHandling, StoredValue, TimeDiff, - Transaction, TransactionEntryPoint, AUCTION_LANE_ID, MINT_LANE_ID, U512, + EntityAddr, EraEndV2, EraId, FeeHandling, Gas, InvalidTransaction, InvalidTransactionV1, Key, + ProtocolVersion, PublicKey, RefundHandling, StoredValue, TimeDiff, Transaction, + TransactionEntryPoint, AUCTION_LANE_ID, MINT_LANE_ID, U512, }; use super::{ @@ -110,38 +110,6 @@ fn speculative_evm_prevrandao( Ok(evm_prevrandao(*parent_header.accumulated_seed())) } -fn write_eip4788_beacon_roots( - scratch_state: &ScratchGlobalState, - state_root_hash: Digest, - chainspec: &Chainspec, - protocol_version: ProtocolVersion, - block_context: EvmBlockContext, - parent_hash: BlockHash, -) -> Result { - if !chainspec.evm_config.enabled || chainspec.evm_config.spec < EvmSpec::Prague { - return Ok(state_root_hash); - } - - if block_context.number == 0 { - return Ok(state_root_hash); - } - - match scratch_state.block_global(BlockGlobalRequest::set_eip4788_parent_hash( - state_root_hash, - protocol_version, - block_context.timestamp, - parent_hash, - )) { - BlockGlobalResult::RootNotFound => Err(BlockExecutionError::RootNotFound(state_root_hash)), - BlockGlobalResult::Failure(err) => { - Err(BlockExecutionError::BlockGlobal(format!("{err:?}"))) - } - BlockGlobalResult::Success { - post_state_hash, .. - } => Ok(post_state_hash), - } -} - fn evm_precondition_receipt(effective_gas_price: u128) -> EvmReceipt { EvmReceipt { status: EvmReceiptStatus::Halt(EvmHaltReason::Unknown), @@ -709,16 +677,6 @@ pub fn execute_finalized_block( } } - let prevrandao = evm_prevrandao(parent_seed); - state_root_hash = write_eip4788_beacon_roots( - &scratch_state, - state_root_hash, - chainspec, - protocol_version, - evm_block_context(chainspec, block_height, block_time, &proposer, prevrandao), - parent_hash, - )?; - let transaction_config = &chainspec.transaction_config; for stored_transaction in executable_block.transactions { @@ -1212,6 +1170,7 @@ pub fn execute_finalized_block( _ if is_evm => { let evm_transaction = evm_transaction.expect("EVM transaction should exist"); let base_fee_wei = chainspec.evm_config.base_fee_wei(); + let prevrandao = evm_prevrandao(parent_seed); let block_context = evm_block_context( chainspec, block_height, @@ -2406,10 +2365,8 @@ pub(crate) fn compute_execution_results_checksum<'a>( #[cfg(test)] mod tests { use super::*; - use casper_storage::{ - block_store::BlockStoreTransaction, global_state::state, tracking_copy::TrackingCopyExt, - }; - use casper_types::{BlockHeaderV2, EvmConfig, Timestamp, DEFAULT_WEI_PER_MOTE}; + use casper_storage::block_store::BlockStoreTransaction; + use casper_types::{BlockHeaderV2, EvmConfig, EvmSpec, Timestamp, DEFAULT_WEI_PER_MOTE}; #[test] fn should_not_raise_evm_min_cost_above_converted_fee() { @@ -2537,95 +2494,4 @@ mod tests { evm_prevrandao(selected_seed) ); } - - #[test] - fn eip4788_hook_writes_beacon_roots_without_transactions() { - let chainspec = Chainspec { - evm_config: EvmConfig { - enabled: true, - chain_id: 7, - spec: EvmSpec::Prague, - block_gas_limit: 30_000_000, - base_fee: 0, - wei_per_mote: DEFAULT_WEI_PER_MOTE, - }, - ..Default::default() - }; - let (global_state, state_root_hash, _tempdir) = - state::lmdb::make_temporary_global_state([]); - let scratch_state = global_state.create_scratch(); - let block_time = BlockTime::new(2_000); - let block_context = - evm_block_context(&chainspec, 1, block_time, &PublicKey::System, EvmHash::ZERO); - let parent_hash = BlockHash::new(Digest::from_raw([0x44; 32])); - - let updated_state_root_hash = write_eip4788_beacon_roots( - &scratch_state, - state_root_hash, - &chainspec, - ProtocolVersion::V1_0_0, - block_context.clone(), - parent_hash, - ) - .expect("EIP-4788 hook should succeed"); - let tracking_copy = scratch_state - .tracking_copy(updated_state_root_hash) - .expect("tracking copy should not fail") - .expect("state root should exist"); - let entry = tracking_copy - .get_eip4788_parent_hash(block_context.timestamp) - .expect("EIP-4788 beacon root should be readable"); - - assert_eq!(entry, Some((block_context.timestamp, parent_hash))); - } - - #[test] - fn evm_prevrandao_uses_parent_block_accumulated_seed() { - let parent_hash = BlockHash::new(Digest::from_raw([0x11; Digest::LENGTH])); - let parent_seed = Digest::from_raw([0x22; Digest::LENGTH]); - let state_root_hash = Digest::from_raw([0x33; Digest::LENGTH]); - let block_with_zero_bit = BlockV2::new( - parent_hash, - parent_seed, - state_root_hash, - false, - None, - Timestamp::zero(), - EraId::new(1), - 1, - ProtocolVersion::V2_0_0, - PublicKey::System, - BTreeMap::new(), - Default::default(), - 1, - None, - ); - let block_with_one_bit = BlockV2::new( - parent_hash, - parent_seed, - state_root_hash, - true, - None, - Timestamp::zero(), - EraId::new(1), - 1, - ProtocolVersion::V2_0_0, - PublicKey::System, - BTreeMap::new(), - Default::default(), - 1, - None, - ); - - let prevrandao = evm_prevrandao(parent_seed); - assert_eq!(prevrandao.as_ref(), parent_seed.as_ref()); - assert_ne!( - prevrandao.as_ref(), - block_with_zero_bit.accumulated_seed().as_ref() - ); - assert_ne!( - prevrandao.as_ref(), - block_with_one_bit.accumulated_seed().as_ref() - ); - } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index d72668b05a..f706767681 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,3 @@ [toolchain] channel = "1.91.0" +components = ["rust-analyzer"] diff --git a/smart_contracts/contracts/explorer/faucet/README.md b/smart_contracts/contracts/explorer/faucet/README.md index 4ffc74d253..053d5fd367 100644 --- a/smart_contracts/contracts/explorer/faucet/README.md +++ b/smart_contracts/contracts/explorer/faucet/README.md @@ -35,7 +35,7 @@ If you try to invoke the contract before these variables are set, then you'll ge | feature | cost | | ------------------------ | ----------------- | -| faucet install | `119_810_320_929` | -| faucet set variables | `79_749_265` | -| faucet call by installer | `2_652_913_398` | -| faucet call by user | `2_558_746_881` | +| faucet install | `118_807_845_390` | +| faucet set variables | `79_790_440` | +| faucet call by installer | `2_652_954_573` | +| faucet call by user | `2_558_820_996` | diff --git a/storage/src/data_access_layer/block_global.rs b/storage/src/data_access_layer/block_global.rs index 9c7e293605..a8dc1c7298 100644 --- a/storage/src/data_access_layer/block_global.rs +++ b/storage/src/data_access_layer/block_global.rs @@ -1,5 +1,5 @@ use crate::tracking_copy::TrackingCopyError; -use casper_types::{execution::Effects, BlockHash, BlockTime, Digest, ProtocolVersion}; +use casper_types::{execution::Effects, BlockTime, Digest, ProtocolVersion}; use std::fmt::{Display, Formatter}; use thiserror::Error; @@ -14,13 +14,6 @@ pub enum BlockGlobalKind { ProtocolVersion(ProtocolVersion), /// Addressable entity flag. AddressableEntity(bool), - /// EIP-4788 parent block hash record. - Eip4788ParentHash { - /// EVM block timestamp in seconds. - timestamp_secs: u64, - /// Parent block hash associated with the timestamp. - parent_hash: BlockHash, - }, } impl Default for BlockGlobalKind { @@ -76,24 +69,6 @@ impl BlockGlobalRequest { } } - /// Returns an EIP-4788 parent block hash setting request. - pub fn set_eip4788_parent_hash( - state_hash: Digest, - protocol_version: ProtocolVersion, - timestamp_secs: u64, - parent_hash: BlockHash, - ) -> Self { - let block_global_kind = BlockGlobalKind::Eip4788ParentHash { - timestamp_secs, - parent_hash, - }; - BlockGlobalRequest { - state_hash, - protocol_version, - block_global_kind, - } - } - /// Returns state hash. pub fn state_hash(&self) -> Digest { self.state_hash diff --git a/storage/src/eip4788.rs b/storage/src/eip4788.rs index aabce9568d..7b84e22e87 100644 --- a/storage/src/eip4788.rs +++ b/storage/src/eip4788.rs @@ -1,7 +1,7 @@ -//! Storage support for the EIP-4788 beacon block roots predeploy. +//! EIP-4788 beacon block roots predeploy constants. use alloy_primitives::keccak256; -use casper_types::{evm, BlockGlobalAddr, BlockHash, CLValue, CLValueError, Digest, Key}; +use casper_types::evm; /// EIP-4788 beacon roots contract address. pub const BEACON_ROOTS_ADDRESS: evm::Address = evm::Address::new([ @@ -9,9 +9,6 @@ pub const BEACON_ROOTS_ADDRESS: evm::Address = evm::Address::new([ 0xd0, 0xbe, 0xac, 0x02, ]); -/// Number of timestamp and block-root slots maintained by EIP-4788. -pub const HISTORY_BUFFER_LENGTH: u64 = 8_191; - /// Prague EIP-4788 beacon roots runtime bytecode. pub const BEACON_ROOTS_CODE: &[u8] = &[ 0x33, 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, @@ -23,26 +20,6 @@ pub const BEACON_ROOTS_CODE: &[u8] = &[ 0x00, ]; -/// Returns the EIP-4788 ring-buffer slot for `timestamp_secs`. -pub const fn slot_for_timestamp(timestamp_secs: u64) -> u64 { - timestamp_secs % HISTORY_BUFFER_LENGTH -} - -/// Returns the Global State key containing the EIP-4788 record for `timestamp_secs`. -pub(crate) fn parent_hash_key(timestamp_secs: u64) -> Key { - Key::BlockGlobal(BlockGlobalAddr::BlockParentHash { - slot: slot_for_timestamp(timestamp_secs), - }) -} - -/// Returns the CLValue used to persist an EIP-4788 timestamp and parent block hash. -pub(crate) fn parent_hash_value( - timestamp_secs: u64, - parent_hash: BlockHash, -) -> Result { - CLValue::from_t((timestamp_secs, Digest::from(parent_hash))) -} - /// Returns the Keccak-256 code hash for [`BEACON_ROOTS_CODE`]. pub fn beacon_roots_code_hash() -> evm::Hash { let digest = keccak256(BEACON_ROOTS_CODE); @@ -53,25 +30,7 @@ pub fn beacon_roots_code_hash() -> evm::Hash { #[cfg(test)] mod tests { - use casper_types::{ByteCode, ByteCodeKind, Digest, StoredValue}; - use super::*; - use crate::{ - global_state::state::{self, lmdb::LmdbGlobalStateView, StateProvider as _}, - tracking_copy::{TrackingCopy, TrackingCopyError, TrackingCopyExt}, - }; - - fn tracking_copy( - initial_data: impl IntoIterator, - ) -> (TrackingCopy, impl Send) { - let (global_state, root_hash, tempdir) = - state::lmdb::make_temporary_global_state(initial_data); - let reader = global_state - .checkout(root_hash) - .expect("checkout should not fail") - .expect("root should exist"); - (TrackingCopy::new(reader, 5, false), tempdir) - } #[test] fn constants_match_eip4788() { @@ -79,107 +38,7 @@ mod tests { BEACON_ROOTS_ADDRESS.to_hex_string(), "000f3df6d732807ef1319fb7b8bb8522d0beac02" ); - assert_eq!(HISTORY_BUFFER_LENGTH, 8_191); assert_eq!(BEACON_ROOTS_CODE.len(), 97); assert!(!beacon_roots_code_hash().is_zero()); } - - #[test] - fn slot_is_derived_from_timestamp() { - assert_eq!(slot_for_timestamp(0), 0); - assert_eq!(slot_for_timestamp(HISTORY_BUFFER_LENGTH - 1), 8_190); - assert_eq!(slot_for_timestamp(HISTORY_BUFFER_LENGTH), 0); - } - - #[test] - fn tracking_copy_ext_reads_parent_hash_tuple() { - let timestamp = 42; - let parent_hash = BlockHash::new(Digest::from([7; Digest::LENGTH])); - let value = parent_hash_value(timestamp, parent_hash).expect("tuple should encode"); - let (tracking_copy, _tempdir) = - tracking_copy([(parent_hash_key(timestamp), StoredValue::CLValue(value))]); - - assert_eq!( - tracking_copy - .get_eip4788_parent_hash(timestamp) - .expect("read should succeed"), - Some((timestamp, parent_hash)) - ); - } - - #[test] - fn tracking_copy_ext_sets_parent_hash_tuple() { - let timestamp = 42; - let parent_hash = BlockHash::new(Digest::from([7; Digest::LENGTH])); - let (mut tracking_copy, _tempdir) = tracking_copy([]); - - tracking_copy - .set_eip4788_parent_hash(timestamp, parent_hash) - .expect("tuple should encode"); - - let stored_value = tracking_copy - .read(&parent_hash_key(timestamp)) - .expect("read should succeed") - .expect("tuple should exist"); - let StoredValue::CLValue(cl_value) = stored_value else { - panic!("EIP-4788 tuple should be stored as a CLValue"); - }; - assert_eq!( - cl_value - .into_t::<(u64, Digest)>() - .expect("tuple should decode"), - (timestamp, Digest::from(parent_hash)) - ); - } - - #[test] - fn latest_entry_replaces_the_same_slot_after_a_full_ring() { - let (mut tracking_copy, _tempdir) = tracking_copy([]); - let replacement_timestamp = HISTORY_BUFFER_LENGTH + 1; - let mut replacement_raw_hash = [0; Digest::LENGTH]; - replacement_raw_hash[..8].copy_from_slice(&replacement_timestamp.to_le_bytes()); - let replacement_hash = BlockHash::new(Digest::from(replacement_raw_hash)); - - for timestamp in 1..=replacement_timestamp { - let mut raw_hash = [0; Digest::LENGTH]; - raw_hash[..8].copy_from_slice(×tamp.to_le_bytes()); - let parent_hash = BlockHash::new(Digest::from(raw_hash)); - tracking_copy - .set_eip4788_parent_hash(timestamp, parent_hash) - .expect("tuple should encode"); - } - - assert_eq!( - tracking_copy - .get_eip4788_parent_hash(1) - .expect("read should succeed"), - Some((replacement_timestamp, replacement_hash)) - ); - } - - #[test] - fn returns_none_when_slot_is_absent() { - let (tracking_copy, _tempdir) = tracking_copy([]); - - assert_eq!( - tracking_copy - .get_eip4788_parent_hash(42) - .expect("read should succeed"), - None - ); - } - - #[test] - fn rejects_unexpected_value_type() { - let timestamp = 42; - let (tracking_copy, _tempdir) = tracking_copy([( - parent_hash_key(timestamp), - StoredValue::ByteCode(ByteCode::new(ByteCodeKind::V1CasperWasm, vec![])), - )]); - - assert!(matches!( - tracking_copy.get_eip4788_parent_hash(timestamp), - Err(TrackingCopyError::UnexpectedStoredValueVariant) - )); - } } diff --git a/storage/src/global_state/state/lmdb.rs b/storage/src/global_state/state/lmdb.rs index 3cc4a070a7..bec6ee4366 100644 --- a/storage/src/global_state/state/lmdb.rs +++ b/storage/src/global_state/state/lmdb.rs @@ -539,16 +539,10 @@ pub fn make_temporary_global_state( #[cfg(test)] mod tests { - use casper_types::{ - account::AccountHash, execution::TransformKindV2, BlockHash, CLValue, Digest, - ProtocolVersion, - }; + use casper_types::{account::AccountHash, execution::TransformKindV2, CLValue, Digest}; - use crate::{ - data_access_layer::{BlockGlobalRequest, BlockGlobalResult}, - eip4788, - global_state::state::{scratch::tests::TestPair, CommitProvider as _, StateProvider as _}, - tracking_copy::TrackingCopyExt, + use crate::global_state::state::{ + scratch::tests::TestPair, CommitProvider as _, StateProvider as _, }; use super::*; @@ -656,48 +650,4 @@ mod tests { original_checkout.read(&test_pairs_updated[2].key).unwrap() ); } - - #[test] - fn block_global_writes_and_overwrites_eip4788_parent_hash() { - let timestamp = 42; - let replacement_timestamp = timestamp + eip4788::HISTORY_BUFFER_LENGTH; - let initial_parent_hash = BlockHash::new(Digest::from([1; Digest::LENGTH])); - let replacement_parent_hash = BlockHash::new(Digest::from([2; Digest::LENGTH])); - let (state, root_hash, _tempdir) = make_temporary_global_state([]); - - let post_state_hash = match state.block_global(BlockGlobalRequest::set_eip4788_parent_hash( - root_hash, - ProtocolVersion::V1_0_0, - timestamp, - initial_parent_hash, - )) { - BlockGlobalResult::Success { - post_state_hash, .. - } => post_state_hash, - result => panic!("unexpected block-global result: {:?}", result), - }; - - let post_state_hash = match state.block_global(BlockGlobalRequest::set_eip4788_parent_hash( - post_state_hash, - ProtocolVersion::V1_0_0, - replacement_timestamp, - replacement_parent_hash, - )) { - BlockGlobalResult::Success { - post_state_hash, .. - } => post_state_hash, - result => panic!("unexpected block-global result: {:?}", result), - }; - - let tracking_copy = state - .tracking_copy(post_state_hash) - .expect("tracking copy should be available") - .expect("post-state root should exist"); - assert_eq!( - tracking_copy - .get_eip4788_parent_hash(timestamp) - .expect("read should succeed"), - Some((replacement_timestamp, replacement_parent_hash)) - ); - } } diff --git a/storage/src/global_state/state/mod.rs b/storage/src/global_state/state/mod.rs index 666e622842..924fb22120 100644 --- a/storage/src/global_state/state/mod.rs +++ b/storage/src/global_state/state/mod.rs @@ -633,17 +633,6 @@ pub trait CommitProvider: StateProvider { StoredValue::CLValue(cl_value), ); } - BlockGlobalKind::Eip4788ParentHash { - timestamp_secs, - parent_hash, - } => { - let mut tracking_copy = tc.borrow_mut(); - if let Err(error) = - tracking_copy.set_eip4788_parent_hash(timestamp_secs, parent_hash) - { - return BlockGlobalResult::Failure(error); - } - } } let effects = tc.borrow_mut().effects(); diff --git a/storage/src/lib.rs b/storage/src/lib.rs index d15e80eff7..4faaefb7f5 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -16,7 +16,7 @@ pub mod block_store; pub mod data_access_layer; /// EIP-2935 block hash history predeploy support. pub mod eip2935; -/// EIP-4788 beacon roots storage support. +/// EIP-4788 beacon roots predeploy support. pub mod eip4788; /// Global state logic. pub mod global_state; diff --git a/storage/src/tracking_copy/ext.rs b/storage/src/tracking_copy/ext.rs index 9f80ab0826..41ee058ad2 100644 --- a/storage/src/tracking_copy/ext.rs +++ b/storage/src/tracking_copy/ext.rs @@ -8,7 +8,6 @@ use crate::{ data_access_layer::balance::{ AvailableBalanceChecker, BalanceHolds, BalanceHoldsWithProof, ProcessingHoldBalanceHandling, }, - eip4788, global_state::{error::Error as GlobalStateError, state::StateReader}, tracking_copy::{TrackingCopy, TrackingCopyEntityExt, TrackingCopyError}, KeyPrefix, @@ -27,10 +26,9 @@ use casper_types::{ }, MINT, }, - BlockGlobalAddr, BlockHash, BlockTime, ByteCode, ByteCodeAddr, ByteCodeHash, CLValue, - ChecksumRegistry, Contract, Digest, EntityAddr, EntryPoints, HashAddr, HoldBalanceHandling, - HoldsEpoch, Key, Motes, Package, StoredValue, StoredValueTypeMismatch, SystemHashRegistry, - URef, URefAddr, U512, + BlockGlobalAddr, BlockTime, ByteCode, ByteCodeAddr, ByteCodeHash, CLValue, ChecksumRegistry, + Contract, EntityAddr, EntryPoints, HashAddr, HoldBalanceHandling, HoldsEpoch, Key, Motes, + Package, StoredValue, StoredValueTypeMismatch, SystemHashRegistry, URef, URefAddr, U512, }; /// Higher-level operations on the state via a `TrackingCopy`. @@ -44,19 +42,6 @@ pub trait TrackingCopyExt { /// Returns block time associated with checked out root hash. fn get_block_time(&self) -> Result, Self::Error>; - /// Returns the EIP-4788 timestamp and parent hash for the timestamp's ring-buffer slot. - fn get_eip4788_parent_hash( - &self, - timestamp_secs: u64, - ) -> Result, Self::Error>; - - /// Stores an EIP-4788 timestamp and parent hash in the timestamp's ring-buffer slot. - fn set_eip4788_parent_hash( - &mut self, - timestamp_secs: u64, - parent_hash: BlockHash, - ) -> Result<(), Self::Error>; - /// Returns balance hold configuration settings for imputed kind of balance hold. fn get_balance_hold_config( &self, @@ -167,41 +152,6 @@ where } } - fn get_eip4788_parent_hash( - &self, - timestamp_secs: u64, - ) -> Result, Self::Error> { - match self.read(&eip4788::parent_hash_key(timestamp_secs))? { - None => Ok(None), - Some(StoredValue::CLValue(cl_value)) => { - let (timestamp, parent_hash): (u64, Digest) = - cl_value.into_t().map_err(Self::Error::CLValue)?; - Ok(Some((timestamp, BlockHash::from(parent_hash)))) - } - Some(unexpected) => { - warn!( - ?unexpected, - "EIP-4788 parent hash stored as unexpected value type" - ); - Err(Self::Error::UnexpectedStoredValueVariant) - } - } - } - - fn set_eip4788_parent_hash( - &mut self, - timestamp_secs: u64, - parent_hash: BlockHash, - ) -> Result<(), Self::Error> { - let value = eip4788::parent_hash_value(timestamp_secs, parent_hash) - .map_err(Self::Error::CLValue)?; - self.write( - eip4788::parent_hash_key(timestamp_secs), - StoredValue::CLValue(value), - ); - Ok(()) - } - fn get_balance_hold_config( &self, hold_kind: BalanceHoldAddrTag, diff --git a/types/src/block/block_global.rs b/types/src/block/block_global.rs index e9ecf135bc..13a8c7bd25 100644 --- a/types/src/block/block_global.rs +++ b/types/src/block/block_global.rs @@ -29,7 +29,6 @@ const BLOCK_TIME_TAG: u8 = 0; const MESSAGE_COUNT_TAG: u8 = 1; const PROTOCOL_VERSION_TAG: u8 = 2; const ADDRESSABLE_ENTITY_TAG: u8 = 3; -const BLOCK_PARENT_HASH_TAG: u8 = 4; /// Serialization tag for BlockGlobalAddr variants. #[derive( @@ -48,8 +47,6 @@ pub enum BlockGlobalAddrTag { ProtocolVersion = PROTOCOL_VERSION_TAG, /// Tag for addressable entity variant. AddressableEntity = ADDRESSABLE_ENTITY_TAG, - /// Tag for parent block hash variant. - BlockParentHash = BLOCK_PARENT_HASH_TAG, } impl BlockGlobalAddrTag { @@ -71,9 +68,6 @@ impl BlockGlobalAddrTag { if value == ADDRESSABLE_ENTITY_TAG { return Some(BlockGlobalAddrTag::AddressableEntity); } - if value == BLOCK_PARENT_HASH_TAG { - return Some(BlockGlobalAddrTag::BlockParentHash); - } None } } @@ -85,7 +79,6 @@ impl Display for BlockGlobalAddrTag { BlockGlobalAddrTag::MessageCount => MESSAGE_COUNT_TAG, BlockGlobalAddrTag::ProtocolVersion => PROTOCOL_VERSION_TAG, BlockGlobalAddrTag::AddressableEntity => ADDRESSABLE_ENTITY_TAG, - BlockGlobalAddrTag::BlockParentHash => BLOCK_PARENT_HASH_TAG, }; write!(f, "{}", base16::encode_lower(&[tag])) } @@ -137,20 +130,12 @@ pub enum BlockGlobalAddr { ProtocolVersion, /// Addressable entity. AddressableEntity, - /// Parent block hash at a slot in a block-global ring buffer. - BlockParentHash { - /// Slot in the ring buffer. - slot: u64, - }, } impl BlockGlobalAddr { /// The serialized length of a tag-only [`BlockGlobalAddr`]. pub const BLOCK_GLOBAL_ADDR_LENGTH: usize = BlockGlobalAddrTag::BLOCK_GLOBAL_ADDR_TAG_LENGTH; - /// The serialized length of a [`BlockGlobalAddr::BlockParentHash`]. - pub const BLOCK_PARENT_HASH_ADDR_LENGTH: usize = 32; - /// Returns the tag of this instance. pub fn tag(&self) -> BlockGlobalAddrTag { match self { @@ -158,7 +143,6 @@ impl BlockGlobalAddr { BlockGlobalAddr::BlockTime => BlockGlobalAddrTag::BlockTime, BlockGlobalAddr::ProtocolVersion => BlockGlobalAddrTag::ProtocolVersion, BlockGlobalAddr::AddressableEntity => BlockGlobalAddrTag::AddressableEntity, - BlockGlobalAddr::BlockParentHash { .. } => BlockGlobalAddrTag::BlockParentHash, } } @@ -173,11 +157,6 @@ impl BlockGlobalAddr { BlockGlobalAddr::AddressableEntity => { base16::encode_lower(&ADDRESSABLE_ENTITY_TAG.to_le_bytes()) } - BlockGlobalAddr::BlockParentHash { slot } => { - let mut formatted = base16::encode_lower(&BLOCK_PARENT_HASH_TAG.to_le_bytes()); - formatted.push_str(&base16::encode_lower(&slot.to_be_bytes())); - formatted - } } } @@ -204,13 +183,6 @@ impl BlockGlobalAddr { BlockGlobalAddrTag::MessageCount => Ok(BlockGlobalAddr::MessageCount), BlockGlobalAddrTag::ProtocolVersion => Ok(BlockGlobalAddr::ProtocolVersion), BlockGlobalAddrTag::AddressableEntity => Ok(BlockGlobalAddr::AddressableEntity), - BlockGlobalAddrTag::BlockParentHash => { - let slot_bytes = <[u8; core::mem::size_of::()]>::try_from(&bytes[1..]) - .map_err(|error| FromStrError::BlockGlobal(error.to_string()))?; - Ok(BlockGlobalAddr::BlockParentHash { - slot: u64::from_be_bytes(slot_bytes), - }) - } } } } @@ -223,29 +195,11 @@ impl ToBytes for BlockGlobalAddr { } fn serialized_length(&self) -> usize { - match self { - BlockGlobalAddr::BlockParentHash { .. } => Self::BLOCK_PARENT_HASH_ADDR_LENGTH, - BlockGlobalAddr::BlockTime - | BlockGlobalAddr::MessageCount - | BlockGlobalAddr::ProtocolVersion - | BlockGlobalAddr::AddressableEntity => Self::BLOCK_GLOBAL_ADDR_LENGTH, - } + Self::BLOCK_GLOBAL_ADDR_LENGTH } fn write_bytes(&self, writer: &mut Vec) -> Result<(), bytesrepr::Error> { - match self { - BlockGlobalAddr::BlockParentHash { slot } => { - let mut bytes = [0u8; Self::BLOCK_PARENT_HASH_ADDR_LENGTH]; - bytes[0] = self.tag() as u8; - bytes[Self::BLOCK_PARENT_HASH_ADDR_LENGTH - core::mem::size_of::()..] - .copy_from_slice(&slot.to_be_bytes()); - writer.extend_from_slice(&bytes); - } - BlockGlobalAddr::BlockTime - | BlockGlobalAddr::MessageCount - | BlockGlobalAddr::ProtocolVersion - | BlockGlobalAddr::AddressableEntity => writer.push(self.tag() as u8), - } + writer.push(self.tag() as u8); Ok(()) } } @@ -266,25 +220,6 @@ impl FromBytes for BlockGlobalAddr { tag if tag == BlockGlobalAddrTag::AddressableEntity as u8 => { Ok((BlockGlobalAddr::AddressableEntity, remainder)) } - tag if tag == BlockGlobalAddrTag::BlockParentHash as u8 => { - if bytes.len() < Self::BLOCK_PARENT_HASH_ADDR_LENGTH { - return Err(bytesrepr::Error::EarlyEndOfStream); - } - let (serialized_addr, remainder) = - bytes.split_at(Self::BLOCK_PARENT_HASH_ADDR_LENGTH); - let slot_offset = Self::BLOCK_PARENT_HASH_ADDR_LENGTH - core::mem::size_of::(); - if serialized_addr[BlockGlobalAddrTag::BLOCK_GLOBAL_ADDR_TAG_LENGTH..slot_offset] - .iter() - .any(|byte| *byte != 0) - { - return Err(bytesrepr::Error::Formatting); - } - let slot_bytes = - <[u8; core::mem::size_of::()]>::try_from(&serialized_addr[slot_offset..]) - .map_err(|_| bytesrepr::Error::Formatting)?; - let slot = u64::from_be_bytes(slot_bytes); - Ok((BlockGlobalAddr::BlockParentHash { slot }, remainder)) - } _ => Err(bytesrepr::Error::Formatting), } } @@ -311,25 +246,18 @@ impl TryFrom for BlockGlobalAddr { impl Display for BlockGlobalAddr { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - BlockGlobalAddr::BlockParentHash { slot } => write!(f, "{}-{}", self.tag(), slot), - BlockGlobalAddr::BlockTime => write!(f, "{}", self.tag()), - BlockGlobalAddr::MessageCount => write!(f, "{}", self.tag()), - BlockGlobalAddr::ProtocolVersion => write!(f, "{}", self.tag()), - BlockGlobalAddr::AddressableEntity => write!(f, "{}", self.tag()), - } + write!(f, "{}", self.tag()) } } #[cfg(any(feature = "testing", test))] impl Distribution for Standard { fn sample(&self, rng: &mut R) -> BlockGlobalAddr { - match rng.gen_range(BLOCK_TIME_TAG..=BLOCK_PARENT_HASH_TAG) { + match rng.gen_range(BLOCK_TIME_TAG..=ADDRESSABLE_ENTITY_TAG) { BLOCK_TIME_TAG => BlockGlobalAddr::BlockTime, MESSAGE_COUNT_TAG => BlockGlobalAddr::MessageCount, PROTOCOL_VERSION_TAG => BlockGlobalAddr::ProtocolVersion, ADDRESSABLE_ENTITY_TAG => BlockGlobalAddr::AddressableEntity, - BLOCK_PARENT_HASH_TAG => BlockGlobalAddr::BlockParentHash { slot: rng.gen() }, _ => unreachable!(), } } @@ -352,14 +280,10 @@ mod tests { bytesrepr::test_serialization_roundtrip(&addr); let addr = BlockGlobalAddr::AddressableEntity; bytesrepr::test_serialization_roundtrip(&addr); - let addr = BlockGlobalAddr::BlockParentHash { - slot: 0x0102_0304_0506_0708, - }; - bytesrepr::test_serialization_roundtrip(&addr); } #[test] - fn legacy_variants_keep_tag_only_serialization() { + fn variants_keep_tag_only_serialization() { let variants = [ (BlockGlobalAddr::BlockTime, 0), (BlockGlobalAddr::MessageCount, 1), @@ -377,47 +301,6 @@ mod tests { assert_eq!(remainder, &[0xaa, 0xbb]); } } - - #[test] - fn block_parent_hash_has_canonical_bytes_and_formatted_string() { - let addr = BlockGlobalAddr::BlockParentHash { - slot: 0x0102_0304_0506_0708, - }; - - assert_eq!(addr.to_bytes().unwrap(), { - let mut expected = vec![0x04]; - expected.extend_from_slice(&[0u8; 23]); - expected.extend_from_slice(&0x0102_0304_0506_0708u64.to_be_bytes()); - expected - }); - assert_eq!(addr.to_formatted_string(), "040102030405060708"); - assert_eq!( - BlockGlobalAddr::from_formatted_string(&addr.to_formatted_string()).unwrap(), - addr - ); - } - - #[test] - fn block_parent_hash_rejects_invalid_fixed_width_payloads() { - let addr = BlockGlobalAddr::BlockParentHash { slot: u64::MAX }; - let mut bytes = addr.to_bytes().unwrap(); - - assert_eq!(bytes.len(), BlockGlobalAddr::BLOCK_PARENT_HASH_ADDR_LENGTH); - assert_eq!(&bytes[24..], &u64::MAX.to_be_bytes()); - - bytes[1] = 1; - assert_eq!( - BlockGlobalAddr::from_bytes(&bytes).unwrap_err(), - bytesrepr::Error::Formatting - ); - - let encoded = addr.to_bytes().unwrap(); - let truncated = &encoded[..31]; - assert_eq!( - BlockGlobalAddr::from_bytes(truncated).unwrap_err(), - bytesrepr::Error::EarlyEndOfStream - ); - } } #[cfg(test)] diff --git a/types/src/gens.rs b/types/src/gens.rs index 82a3480518..d590c2b014 100644 --- a/types/src/gens.rs +++ b/types/src/gens.rs @@ -293,7 +293,6 @@ pub fn block_global_addr_arb() -> impl Strategy { Just(BlockGlobalAddr::MessageCount), Just(BlockGlobalAddr::ProtocolVersion), Just(BlockGlobalAddr::AddressableEntity), - any::().prop_map(|slot| BlockGlobalAddr::BlockParentHash { slot }), ] } diff --git a/types/src/key.rs b/types/src/key.rs index 6ea8d4605e..e11ded098d 100644 --- a/types/src/key.rs +++ b/types/src/key.rs @@ -82,7 +82,6 @@ const BLOCK_GLOBAL_TIME_PREFIX: &str = "block-time-"; const BLOCK_GLOBAL_MESSAGE_COUNT_PREFIX: &str = "block-message-count-"; const BLOCK_GLOBAL_PROTOCOL_VERSION_PREFIX: &str = "block-protocol-version-"; const BLOCK_GLOBAL_ADDRESSABLE_ENTITY_PREFIX: &str = "block-addressable-entity-"; -const BLOCK_GLOBAL_PARENT_HASH_PREFIX: &str = "block-parent-hash-"; const STATE_PREFIX: &str = "state-"; const REWARDS_HANDLING_PREFIX: &str = "rewards-handling-"; const EVM_ACCOUNT_PREFIX: &str = "evm-account-"; @@ -705,16 +704,12 @@ impl Key { Key::NamedKey(named_key) => { format!("{}", named_key) } - Key::BlockGlobal(BlockGlobalAddr::BlockParentHash { slot }) => { - format!("{BLOCK_GLOBAL_PARENT_HASH_PREFIX}{slot}") - } Key::BlockGlobal(addr) => { let prefix = match addr { BlockGlobalAddr::BlockTime => BLOCK_GLOBAL_TIME_PREFIX, BlockGlobalAddr::MessageCount => BLOCK_GLOBAL_MESSAGE_COUNT_PREFIX, BlockGlobalAddr::ProtocolVersion => BLOCK_GLOBAL_PROTOCOL_VERSION_PREFIX, BlockGlobalAddr::AddressableEntity => BLOCK_GLOBAL_ADDRESSABLE_ENTITY_PREFIX, - BlockGlobalAddr::BlockParentHash { .. } => unreachable!(), }; format!( "{}{}", @@ -1061,13 +1056,6 @@ impl Key { return Ok(BlockGlobalAddr::AddressableEntity.into()); } - if let Some(parent_hash) = input.strip_prefix(BLOCK_GLOBAL_PARENT_HASH_PREFIX) { - let slot = parent_hash - .parse::() - .map_err(|error| FromStrError::BlockGlobal(error.to_string()))?; - return Ok(BlockGlobalAddr::BlockParentHash { slot }.into()); - } - match EntryPointAddr::from_formatted_str(input) { Ok(entry_point_addr) => return Ok(Key::EntryPoint(entry_point_addr)), Err(addressable_entity::FromStrError::InvalidPrefix) => {} @@ -1618,9 +1606,6 @@ impl Display for Key { Key::NamedKey(named_key_addr) => { write!(f, "Key::NamedKey({})", named_key_addr) } - Key::BlockGlobal(BlockGlobalAddr::BlockParentHash { slot }) => { - write!(f, "Key::BlockGlobal({})", slot) - } Key::BlockGlobal(addr) => { write!( f, @@ -1831,15 +1816,7 @@ impl ToBytes for Key { | Key::RewardsHandling => PADDING_BYTES.write_bytes(writer), Key::BlockGlobal(addr) => { addr.write_bytes(writer)?; - match addr { - BlockGlobalAddr::BlockParentHash { .. } => Ok(()), - BlockGlobalAddr::BlockTime - | BlockGlobalAddr::MessageCount - | BlockGlobalAddr::ProtocolVersion - | BlockGlobalAddr::AddressableEntity => { - BLOCK_GLOBAL_PADDING_BYTES.write_bytes(writer) - } - } + BLOCK_GLOBAL_PADDING_BYTES.write_bytes(writer) } Key::BidAddr(bid_addr) => bid_addr.write_bytes(writer), Key::SmartContract(package_addr) => package_addr.write_bytes(writer), @@ -2283,9 +2260,6 @@ mod tests { )); const BLOCK_TIME_KEY: Key = Key::BlockGlobal(BlockGlobalAddr::BlockTime); const BLOCK_MESSAGE_COUNT_KEY: Key = Key::BlockGlobal(BlockGlobalAddr::MessageCount); - const BLOCK_PARENT_HASH_KEY: Key = Key::BlockGlobal(BlockGlobalAddr::BlockParentHash { - slot: 0x0102_0304_0506_0708, - }); // const STATE_KEY: Key = Key::State(EntityAddr::new_contract_entity_addr([42; 32])); const BALANCE_HOLD: Key = Key::BalanceHold(BalanceHoldAddr::new_gas([42; 32], BlockTime::new(100))); @@ -2320,7 +2294,6 @@ mod tests { NAMED_KEY, BLOCK_TIME_KEY, BLOCK_MESSAGE_COUNT_KEY, - BLOCK_PARENT_HASH_KEY, BALANCE_HOLD, STATE_KEY, ]; @@ -2525,59 +2498,6 @@ mod tests { base16::encode_lower(&[0u8; 31]) ) ); - assert_eq!( - format!("{}", BLOCK_PARENT_HASH_KEY), - "Key::BlockGlobal(72623859790382856)" - ); - } - - #[test] - fn block_parent_hash_key_uses_fixed_width_canonical_bytes() { - let key = BLOCK_PARENT_HASH_KEY; - let mut expected = vec![KeyTag::BlockGlobal as u8, 0x04]; - expected.extend_from_slice(&[0u8; 23]); - expected.extend_from_slice(&0x0102_0304_0506_0708u64.to_be_bytes()); - - assert_eq!(key.to_bytes().unwrap(), expected); - assert_eq!(key.serialized_length(), 33); - assert_eq!( - key.to_formatted_string(), - "block-parent-hash-72623859790382856" - ); - assert_eq!( - Key::from_formatted_str(&key.to_formatted_string()).unwrap(), - key - ); - - let legacy_keys = [ - (BlockGlobalAddr::BlockTime, 0), - (BlockGlobalAddr::MessageCount, 1), - (BlockGlobalAddr::ProtocolVersion, 2), - (BlockGlobalAddr::AddressableEntity, 3), - ]; - for (addr, tag) in legacy_keys { - let key = Key::BlockGlobal(addr); - let mut dev_bytes = vec![KeyTag::BlockGlobal as u8, tag]; - dev_bytes.extend_from_slice(&[0u8; 31]); - - assert_eq!(key.to_bytes().unwrap(), dev_bytes); - assert_eq!(key.serialized_length(), 33); - - dev_bytes.extend_from_slice(&[0xaa, 0xbb]); - let (decoded, remainder) = Key::from_bytes(&dev_bytes).unwrap(); - assert_eq!(decoded, key); - assert_eq!(remainder, &[0xaa, 0xbb]); - } - } - - #[test] - fn block_parent_hash_key_rejects_non_decimal_formatted_slot() { - let formatted = format!("{}not-a-slot", BLOCK_GLOBAL_PARENT_HASH_PREFIX); - - assert!(matches!( - Key::from_formatted_str(&formatted), - Err(FromStrError::BlockGlobal(_)) - )); } #[test] @@ -2970,7 +2890,6 @@ mod tests { bytesrepr::test_serialization_roundtrip(&NAMED_KEY); bytesrepr::test_serialization_roundtrip(&BLOCK_TIME_KEY); bytesrepr::test_serialization_roundtrip(&BLOCK_MESSAGE_COUNT_KEY); - bytesrepr::test_serialization_roundtrip(&BLOCK_PARENT_HASH_KEY); bytesrepr::test_serialization_roundtrip(&STATE_KEY); } @@ -3021,9 +2940,6 @@ mod tests { round_trip(&Key::BlockGlobal(BlockGlobalAddr::MessageCount)); round_trip(&Key::BlockGlobal(BlockGlobalAddr::ProtocolVersion)); round_trip(&Key::BlockGlobal(BlockGlobalAddr::AddressableEntity)); - round_trip(&Key::BlockGlobal(BlockGlobalAddr::BlockParentHash { - slot: 0, - })); round_trip(&Key::BalanceHold(BalanceHoldAddr::default())); round_trip(&Key::State(EntityAddr::new_system(zeros))); }