From f2e40541d90eb77f716ca11c2e8a2851d45633be Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Sat, 14 Jan 2023 22:08:07 -0800 Subject: [PATCH] Increment sender nonce + buy gas --- evm/src/cpu/kernel/asm/core/nonce.asm | 14 ++++++- evm/src/cpu/kernel/asm/core/process_txn.asm | 37 ++++++++++++++++--- evm/src/cpu/kernel/asm/core/transfer.asm | 6 ++- .../cpu/kernel/constants/global_metadata.rs | 6 +-- evm/src/cpu/kernel/constants/txn_fields.rs | 23 +++++++----- evm/tests/basic_smart_contract.rs | 6 ++- ...sfer_to_new_addr.rs => simple_transfer.rs} | 5 ++- 7 files changed, 69 insertions(+), 28 deletions(-) rename evm/tests/{transfer_to_new_addr.rs => simple_transfer.rs} (95%) diff --git a/evm/src/cpu/kernel/asm/core/nonce.asm b/evm/src/cpu/kernel/asm/core/nonce.asm index 204620fb..042d81f3 100644 --- a/evm/src/cpu/kernel/asm/core/nonce.asm +++ b/evm/src/cpu/kernel/asm/core/nonce.asm @@ -15,10 +15,20 @@ global get_nonce: %%after: %endmacro +// Increment the given account's nonce. Assumes the account already exists; panics otherwise. global increment_nonce: // stack: address, retdest - // TODO: Replace with actual implementation. - POP + %mpt_read_state_trie + // stack: account_ptr, retdest + DUP1 ISZERO %jumpi(panic) + // stack: nonce_ptr, retdest + DUP1 %mload_trie_data + // stack: nonce, nonce_ptr, retdest + %increment + SWAP1 + // stack: nonce_ptr, nonce', retdest + %mstore_trie_data + // stack: retdest JUMP // Convenience macro to call increment_nonce and return where we left off. diff --git a/evm/src/cpu/kernel/asm/core/process_txn.asm b/evm/src/cpu/kernel/asm/core/process_txn.asm index 1ea9a6d3..8671fb1c 100644 --- a/evm/src/cpu/kernel/asm/core/process_txn.asm +++ b/evm/src/cpu/kernel/asm/core/process_txn.asm @@ -6,22 +6,45 @@ // Pre stack: retdest // Post stack: (empty) global process_normalized_txn: + // stack: retdest + PUSH 0 // TODO: Load block's base fee + %mload_txn_field(@TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS) + ADD + // stack: priority_fee + base_fee, retdest + %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) + // stack: max_fee, priority_fee + base_fee, retdest + %min + // stack: computed_fee, retdest + %mstore_txn_field(@TXN_FIELD_COMPUTED_FEE_PER_GAS) + // stack: retdest PUSH validate %jump(intrinsic_gas) global validate: // stack: intrinsic_gas, retdest - // TODO: Check signature? (Or might happen in type_0.asm etc.) - // TODO: Assert nonce is correct. - // TODO: Assert sender has no code. POP // TODO: Assert gas_limit >= intrinsic_gas. // stack: retdest + // TODO: Check that txn nonce matches account nonce. + // TODO: Assert nonce is correct. + // TODO: Assert sender has no code. + // TODO: Assert sender balance >= gas_limit * gas_price + value. + // stack: retdest -global charge_gas: - // TODO: Deduct gas limit from sender (some gas may be refunded later). +global buy_gas: + %mload_txn_field(@TXN_FIELD_COMPUTED_FEE_PER_GAS) + %mload_txn_field(@TXN_FIELD_GAS_LIMIT) + MUL + // stack: gas_cost, retdest + %mload_txn_field(@TXN_FIELD_ORIGIN) + // stack: sender_addr, gas_cost, retdest + %deduct_eth + // stack: deduct_eth_status, retdest + %jumpi(panic) + // stack: retdest - PUSH 0 // TODO: Push sender. +global increment_sender_nonce: + %mload_txn_field(@TXN_FIELD_ORIGIN) %increment_nonce global process_based_on_type: @@ -53,6 +76,8 @@ global process_message_txn: %jumpi(process_message_txn_insufficient_balance) // stack: retdest + // TODO: Handle precompiles. + // If to's code is empty, return. %mload_txn_field(@TXN_FIELD_TO) %ext_code_empty // stack: code_empty, retdest diff --git a/evm/src/cpu/kernel/asm/core/transfer.asm b/evm/src/cpu/kernel/asm/core/transfer.asm index e7e63495..0ba99fd8 100644 --- a/evm/src/cpu/kernel/asm/core/transfer.asm +++ b/evm/src/cpu/kernel/asm/core/transfer.asm @@ -10,7 +10,6 @@ global transfer_eth: %jumpi(transfer_eth_failure) // stack: to, amount, retdest %add_eth -global transfer_eth_3: %stack (retdest) -> (retdest, 0) JUMP global transfer_eth_failure: @@ -43,7 +42,7 @@ global deduct_eth: // stack: addr, amount, retdest %mpt_read_state_trie // stack: account_ptr, amount, retdest - DUP1 ISZERO %jumpi(panic) // If the account pointer is null, return 0. + DUP1 ISZERO %jumpi(deduct_eth_no_such_account) // If the account pointer is null, return 1. %add_const(1) // stack: balance_ptr, amount, retdest DUP1 %mload_trie_data @@ -58,6 +57,9 @@ global deduct_eth: %mstore_trie_data // stack: retdest, 0 JUMP +global deduct_eth_no_such_account: + %stack (account_ptr, amount, retdest) -> (retdest, 1) + JUMP global deduct_eth_insufficient_balance: %stack (balance, balance_ptr, amount, retdest) -> (retdest, 1) JUMP diff --git a/evm/src/cpu/kernel/constants/global_metadata.rs b/evm/src/cpu/kernel/constants/global_metadata.rs index 1fa62efe..f68a91f5 100644 --- a/evm/src/cpu/kernel/constants/global_metadata.rs +++ b/evm/src/cpu/kernel/constants/global_metadata.rs @@ -5,8 +5,6 @@ pub(crate) enum GlobalMetadata { /// The largest context ID that has been used so far in this execution. Tracking this allows us /// give each new context a unique ID, so that its memory will be zero-initialized. LargestContext = 0, - /// The address of the sender of the transaction. - Origin = 1, /// The size of active memory, in bytes. MemorySize = 2, /// The size of the `TrieData` segment, in bytes. In other words, the next address available for @@ -35,12 +33,11 @@ pub(crate) enum GlobalMetadata { } impl GlobalMetadata { - pub(crate) const COUNT: usize = 14; + pub(crate) const COUNT: usize = 13; pub(crate) fn all() -> [Self; Self::COUNT] { [ Self::LargestContext, - Self::Origin, Self::MemorySize, Self::TrieDataSize, Self::StateTrieRoot, @@ -60,7 +57,6 @@ impl GlobalMetadata { pub(crate) fn var_name(&self) -> &'static str { match self { GlobalMetadata::LargestContext => "GLOBAL_METADATA_LARGEST_CONTEXT", - GlobalMetadata::Origin => "GLOBAL_METADATA_ORIGIN", GlobalMetadata::MemorySize => "GLOBAL_METADATA_MEMORY_SIZE", GlobalMetadata::TrieDataSize => "GLOBAL_METADATA_TRIE_DATA_SIZE", GlobalMetadata::StateTrieRoot => "GLOBAL_METADATA_STATE_TRIE_ROOT", diff --git a/evm/src/cpu/kernel/constants/txn_fields.rs b/evm/src/cpu/kernel/constants/txn_fields.rs index 5b175e3b..cb434788 100644 --- a/evm/src/cpu/kernel/constants/txn_fields.rs +++ b/evm/src/cpu/kernel/constants/txn_fields.rs @@ -9,19 +9,22 @@ pub(crate) enum NormalizedTxnField { Nonce = 2, MaxPriorityFeePerGas = 3, MaxFeePerGas = 4, - GasLimit = 5, - To = 6, - Value = 7, + /// The actual computed gas price for this transaction in the block. + /// This is not technically a transaction field, as it depends on the block's base fee. + ComputedFeePerGas = 5, + GasLimit = 6, + To = 7, + Value = 8, /// The length of the data field. The data itself is stored in another segment. - DataLen = 8, - YParity = 9, - R = 10, - S = 11, - Origin = 12, + DataLen = 9, + YParity = 10, + R = 11, + S = 12, + Origin = 13, } impl NormalizedTxnField { - pub(crate) const COUNT: usize = 13; + pub(crate) const COUNT: usize = 14; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -30,6 +33,7 @@ impl NormalizedTxnField { Self::Nonce, Self::MaxPriorityFeePerGas, Self::MaxFeePerGas, + Self::ComputedFeePerGas, Self::GasLimit, Self::To, Self::Value, @@ -49,6 +53,7 @@ impl NormalizedTxnField { NormalizedTxnField::Nonce => "TXN_FIELD_NONCE", NormalizedTxnField::MaxPriorityFeePerGas => "TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS", NormalizedTxnField::MaxFeePerGas => "TXN_FIELD_MAX_FEE_PER_GAS", + NormalizedTxnField::ComputedFeePerGas => "TXN_FIELD_COMPUTED_FEE_PER_GAS", NormalizedTxnField::GasLimit => "TXN_FIELD_GAS_LIMIT", NormalizedTxnField::To => "TXN_FIELD_TO", NormalizedTxnField::Value => "TXN_FIELD_VALUE", diff --git a/evm/tests/basic_smart_contract.rs b/evm/tests/basic_smart_contract.rs index a9172b89..44b7fb4d 100644 --- a/evm/tests/basic_smart_contract.rs +++ b/evm/tests/basic_smart_contract.rs @@ -100,8 +100,9 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { let expected_state_trie_after = { let sender_account_after = AccountRlp { - balance: sender_account_before.balance - value, // TODO: Also subtract gas_used * price. - // nonce: sender_account_before.nonce + 1, // TODO + // TODO: Should be 21k; 1k gas should be refunded. + balance: sender_account_before.balance - value - 22_000 * 10, + nonce: sender_account_before.nonce + 1, ..sender_account_before }; let to_account_after = AccountRlp { @@ -120,6 +121,7 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { value: rlp::encode(&to_account_after).to_vec(), } .into(); + // TODO: Beneficiary should receive gas... PartialTrie::Branch { children, value: vec![], diff --git a/evm/tests/transfer_to_new_addr.rs b/evm/tests/simple_transfer.rs similarity index 95% rename from evm/tests/transfer_to_new_addr.rs rename to evm/tests/simple_transfer.rs index 4a5e69e3..72801388 100644 --- a/evm/tests/transfer_to_new_addr.rs +++ b/evm/tests/simple_transfer.rs @@ -73,8 +73,9 @@ fn test_simple_transfer() -> anyhow::Result<()> { let expected_state_trie_after = { let sender_account_after = AccountRlp { - balance: sender_account_before.balance - value, // TODO: Also subtract gas_used * price. - // nonce: sender_account_before.nonce + 1, // TODO + // TODO: Should be 21k; 1k gas should be refunded. + balance: sender_account_before.balance - value - 22_000 * 10, + nonce: sender_account_before.nonce + 1, ..sender_account_before }; let to_account_after = AccountRlp {