From b3e93e91eb2f6197aaeeb7064ba120283aae2795 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Thu, 9 Mar 2023 17:36:43 -0500 Subject: [PATCH 1/3] Fix plonky2 compilation with wasm32-unknown-unknown target --- plonky2/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plonky2/Cargo.toml b/plonky2/Cargo.toml index db81fab0..6df46060 100644 --- a/plonky2/Cargo.toml +++ b/plonky2/Cargo.toml @@ -34,6 +34,9 @@ serde = { version = "1.0", default-features = false, features = ["derive"] } static_assertions = { version = "1.1.0", default-features = false } unroll = { version = "0.1.5", default-features = false } +[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] +getrandom = { version = "0.2", default-features = false, features = ["js"] } + [dev-dependencies] criterion = { version = "0.4.0", default-features = false } env_logger = { version = "0.9.0", default-features = false } From 84fbbbf410f9a02dade27e6528fd27e3807f6b05 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Fri, 10 Mar 2023 08:54:26 -0800 Subject: [PATCH 2/3] Couple minor fixes --- evm/src/cpu/kernel/asm/core/call.asm | 2 +- evm/src/cpu/kernel/asm/core/intrinsic_gas.asm | 9 +++ evm/src/cpu/kernel/asm/core/process_txn.asm | 44 +++++++++++--- evm/src/cpu/kernel/asm/mpt/hash/hash.asm | 4 +- .../cpu/kernel/constants/global_metadata.rs | 59 ++++++++++++------- evm/src/cpu/kernel/constants/txn_fields.rs | 19 +++--- evm/tests/basic_smart_contract.rs | 6 +- evm/tests/simple_transfer.rs | 5 +- 8 files changed, 102 insertions(+), 46 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index 198a6cbb..cb66b7fa 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -144,7 +144,7 @@ global after_call_instruction: %macro set_new_ctx_gas_limit // stack: gas_limit, new_ctx %stack (gas_limit, new_ctx) - -> (new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CODE_SIZE, gas_limit, new_ctx) + -> (new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_GAS_LIMIT, gas_limit, new_ctx) MSTORE_GENERAL // stack: new_ctx %endmacro diff --git a/evm/src/cpu/kernel/asm/core/intrinsic_gas.asm b/evm/src/cpu/kernel/asm/core/intrinsic_gas.asm index 5891807c..9e8a7f80 100644 --- a/evm/src/cpu/kernel/asm/core/intrinsic_gas.asm +++ b/evm/src/cpu/kernel/asm/core/intrinsic_gas.asm @@ -60,3 +60,12 @@ count_zeros_finish: SWAP1 JUMP + +// Convenience macro to call intrinsic_gas and return where we left off. +%macro intrinsic_gas + // stack: (empty) + PUSH %%after + %jump(intrinsic_gas) +%%after: + // stack: (empty) +%endmacro diff --git a/evm/src/cpu/kernel/asm/core/process_txn.asm b/evm/src/cpu/kernel/asm/core/process_txn.asm index 17770ba4..8a6c3afb 100644 --- a/evm/src/cpu/kernel/asm/core/process_txn.asm +++ b/evm/src/cpu/kernel/asm/core/process_txn.asm @@ -16,15 +16,18 @@ global process_normalized_txn: %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 - POP // TODO: Assert gas_limit >= intrinsic_gas. + // Compute this transaction's intrinsic gas and store it. + %intrinsic_gas + %mstore_txn_field(@TXN_FIELD_INTRINSIC_GAS) // stack: retdest + + // Assert gas_limit >= intrinsic_gas. + %mload_txn_field(@TXN_FIELD_INTRINSIC_GAS) + %mload_txn_field(@TXN_FIELD_GAS_LIMIT) + %assert_ge + // TODO: Check that txn nonce matches account nonce. // TODO: Assert nonce is correct. // TODO: Assert sender has no code. @@ -110,7 +113,14 @@ global process_message_txn_insufficient_balance: PANIC // TODO global process_message_txn_return: - // TODO: Since there was no code to execute, do we still return leftover gas? + // Refund leftover gas. + // stack: retdest + %mload_txn_field(@TXN_FIELD_INTRINSIC_GAS) + %mload_txn_field(@TXN_FIELD_GAS_LIMIT) + SUB + // stack: leftover_gas, retdest + %refund_leftover_gas_cost + // stack: retdest JUMP global process_message_txn_code_loaded: @@ -124,7 +134,13 @@ global process_message_txn_code_loaded: %mload_txn_field(@TXN_FIELD_VALUE) %set_new_ctx_value %set_new_ctx_parent_ctx %set_new_ctx_parent_pc(process_message_txn_after_call) - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) %set_new_ctx_gas_limit + // stack: new_ctx, retdest + + // The gas provided to the callee is gas_limit - intrinsic_gas. + %mload_txn_field(@TXN_FIELD_INTRINSIC_GAS) + %mload_txn_field(@TXN_FIELD_GAS_LIMIT) + SUB + %set_new_ctx_gas_limit // stack: new_ctx, retdest // TODO: Copy TXN_DATA to CALLDATA @@ -135,7 +151,17 @@ global process_message_txn_after_call: // stack: success, leftover_gas, new_ctx, retdest POP // TODO: Success will go into the receipt when we support that. // stack: leftover_gas, new_ctx, retdest - POP // TODO: Refund leftover gas. + %refund_leftover_gas_cost // stack: new_ctx, retdest POP JUMP + +%macro refund_leftover_gas_cost + // stack: leftover_gas + %mload_txn_field(@TXN_FIELD_COMPUTED_FEE_PER_GAS) + MUL + // stack: leftover_gas_cost + %mload_txn_field(@TXN_FIELD_ORIGIN) + // stack: origin, leftover_gas_cost + %add_eth +%endmacro diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm index 9fe0edef..0c8beae7 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm @@ -135,9 +135,9 @@ encode_node_branch: // Get the next unused offset within the encoded child buffers. // Then immediately increment the next unused offset by 16, so any // recursive calls will use nonoverlapping offsets. - %mload_global_metadata(@TRIE_ENCODED_CHILD_SIZE) + %mload_global_metadata(@GLOBAL_METADATA_TRIE_ENCODED_CHILD_SIZE) DUP1 %add_const(16) - %mstore_global_metadata(@TRIE_ENCODED_CHILD_SIZE) + %mstore_global_metadata(@GLOBAL_METADATA_TRIE_ENCODED_CHILD_SIZE) // stack: base_offset, node_payload_ptr, encode_value, retdest // We will call encode_or_hash_node on each child. For the i'th child, we diff --git a/evm/src/cpu/kernel/constants/global_metadata.rs b/evm/src/cpu/kernel/constants/global_metadata.rs index f68a91f5..cd6c0d7e 100644 --- a/evm/src/cpu/kernel/constants/global_metadata.rs +++ b/evm/src/cpu/kernel/constants/global_metadata.rs @@ -30,10 +30,19 @@ pub(crate) enum GlobalMetadata { /// The sizes of the `TrieEncodedChild` and `TrieEncodedChildLen` buffers. In other words, the /// next available offset in these buffers. TrieEncodedChildSize = 14, + + // Block metadata. + BlockBeneficiary = 15, + BlockTimestamp = 16, + BlockNumber = 17, + BlockDifficulty = 18, + BlockGasLimit = 19, + BlockChainId = 20, + BlockBaseFee = 21, } impl GlobalMetadata { - pub(crate) const COUNT: usize = 13; + pub(crate) const COUNT: usize = 20; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -50,33 +59,39 @@ impl GlobalMetadata { Self::TransactionTrieRootDigestAfter, Self::ReceiptTrieRootDigestAfter, Self::TrieEncodedChildSize, + Self::BlockBeneficiary, + Self::BlockTimestamp, + Self::BlockNumber, + Self::BlockDifficulty, + Self::BlockGasLimit, + Self::BlockChainId, + Self::BlockBaseFee, ] } /// The variable name that gets passed into kernel assembly code. pub(crate) fn var_name(&self) -> &'static str { match self { - GlobalMetadata::LargestContext => "GLOBAL_METADATA_LARGEST_CONTEXT", - GlobalMetadata::MemorySize => "GLOBAL_METADATA_MEMORY_SIZE", - GlobalMetadata::TrieDataSize => "GLOBAL_METADATA_TRIE_DATA_SIZE", - GlobalMetadata::StateTrieRoot => "GLOBAL_METADATA_STATE_TRIE_ROOT", - GlobalMetadata::TransactionTrieRoot => "GLOBAL_METADATA_TXN_TRIE_ROOT", - GlobalMetadata::ReceiptTrieRoot => "GLOBAL_METADATA_RECEIPT_TRIE_ROOT", - GlobalMetadata::StateTrieRootDigestBefore => "GLOBAL_METADATA_STATE_TRIE_DIGEST_BEFORE", - GlobalMetadata::TransactionTrieRootDigestBefore => { - "GLOBAL_METADATA_TXN_TRIE_DIGEST_BEFORE" - } - GlobalMetadata::ReceiptTrieRootDigestBefore => { - "GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_BEFORE" - } - GlobalMetadata::StateTrieRootDigestAfter => "GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER", - GlobalMetadata::TransactionTrieRootDigestAfter => { - "GLOBAL_METADATA_TXN_TRIE_DIGEST_AFTER" - } - GlobalMetadata::ReceiptTrieRootDigestAfter => { - "GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_AFTER" - } - GlobalMetadata::TrieEncodedChildSize => "TRIE_ENCODED_CHILD_SIZE", + Self::LargestContext => "GLOBAL_METADATA_LARGEST_CONTEXT", + Self::MemorySize => "GLOBAL_METADATA_MEMORY_SIZE", + Self::TrieDataSize => "GLOBAL_METADATA_TRIE_DATA_SIZE", + Self::StateTrieRoot => "GLOBAL_METADATA_STATE_TRIE_ROOT", + Self::TransactionTrieRoot => "GLOBAL_METADATA_TXN_TRIE_ROOT", + Self::ReceiptTrieRoot => "GLOBAL_METADATA_RECEIPT_TRIE_ROOT", + Self::StateTrieRootDigestBefore => "GLOBAL_METADATA_STATE_TRIE_DIGEST_BEFORE", + Self::TransactionTrieRootDigestBefore => "GLOBAL_METADATA_TXN_TRIE_DIGEST_BEFORE", + Self::ReceiptTrieRootDigestBefore => "GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_BEFORE", + Self::StateTrieRootDigestAfter => "GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER", + Self::TransactionTrieRootDigestAfter => "GLOBAL_METADATA_TXN_TRIE_DIGEST_AFTER", + Self::ReceiptTrieRootDigestAfter => "GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_AFTER", + Self::TrieEncodedChildSize => "GLOBAL_METADATA_TRIE_ENCODED_CHILD_SIZE", + Self::BlockBeneficiary => "GLOBAL_METADATA_BLOCK_BENEFICIARY", + Self::BlockTimestamp => "GLOBAL_METADATA_BLOCK_TIMESTAMP", + Self::BlockNumber => "GLOBAL_METADATA_BLOCK_NUMBER", + Self::BlockDifficulty => "GLOBAL_METADATA_BLOCK_DIFFICULTY", + Self::BlockGasLimit => "GLOBAL_METADATA_BLOCK_GAS_LIMIT", + Self::BlockChainId => "GLOBAL_METADATA_BLOCK_CHAIN_ID", + Self::BlockBaseFee => "GLOBAL_METADATA_BLOCK_BASE_FEE", } } } diff --git a/evm/src/cpu/kernel/constants/txn_fields.rs b/evm/src/cpu/kernel/constants/txn_fields.rs index cb434788..c2931f05 100644 --- a/evm/src/cpu/kernel/constants/txn_fields.rs +++ b/evm/src/cpu/kernel/constants/txn_fields.rs @@ -13,18 +13,19 @@ pub(crate) enum NormalizedTxnField { /// This is not technically a transaction field, as it depends on the block's base fee. ComputedFeePerGas = 5, GasLimit = 6, - To = 7, - Value = 8, + IntrinsicGas = 7, + To = 8, + Value = 9, /// The length of the data field. The data itself is stored in another segment. - DataLen = 9, - YParity = 10, - R = 11, - S = 12, - Origin = 13, + DataLen = 10, + YParity = 11, + R = 12, + S = 13, + Origin = 14, } impl NormalizedTxnField { - pub(crate) const COUNT: usize = 14; + pub(crate) const COUNT: usize = 15; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -35,6 +36,7 @@ impl NormalizedTxnField { Self::MaxFeePerGas, Self::ComputedFeePerGas, Self::GasLimit, + Self::IntrinsicGas, Self::To, Self::Value, Self::DataLen, @@ -55,6 +57,7 @@ impl NormalizedTxnField { NormalizedTxnField::MaxFeePerGas => "TXN_FIELD_MAX_FEE_PER_GAS", NormalizedTxnField::ComputedFeePerGas => "TXN_FIELD_COMPUTED_FEE_PER_GAS", NormalizedTxnField::GasLimit => "TXN_FIELD_GAS_LIMIT", + NormalizedTxnField::IntrinsicGas => "TXN_FIELD_INTRINSIC_GAS", NormalizedTxnField::To => "TXN_FIELD_TO", NormalizedTxnField::Value => "TXN_FIELD_VALUE", NormalizedTxnField::DataLen => "TXN_FIELD_DATA_LEN", diff --git a/evm/tests/basic_smart_contract.rs b/evm/tests/basic_smart_contract.rs index e52b7a76..c127ae33 100644 --- a/evm/tests/basic_smart_contract.rs +++ b/evm/tests/basic_smart_contract.rs @@ -42,6 +42,7 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { let add = get_opcode("ADD"); let stop = get_opcode("STOP"); let code = [push1, 3, push1, 4, add, stop]; + let code_gas = 3 + 3 + 3; let code_hash = keccak(code); let sender_account_before = AccountRlp { @@ -99,9 +100,10 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { timing.filter(Duration::from_millis(100)).print(); let expected_state_trie_after = { + let txdata_gas = 2 * 16; + let gas_used = 21_000 + code_gas + txdata_gas; let sender_account_after = AccountRlp { - // TODO: Should be 21k; 1k gas should be refunded. - balance: sender_account_before.balance - value - 22_000 * 10, + balance: sender_account_before.balance - value - gas_used * 10, nonce: sender_account_before.nonce + 1, ..sender_account_before }; diff --git a/evm/tests/simple_transfer.rs b/evm/tests/simple_transfer.rs index 2d27abfe..a04e5af3 100644 --- a/evm/tests/simple_transfer.rs +++ b/evm/tests/simple_transfer.rs @@ -72,9 +72,10 @@ fn test_simple_transfer() -> anyhow::Result<()> { timing.filter(Duration::from_millis(100)).print(); let expected_state_trie_after = { + let txdata_gas = 2 * 16; + let gas_used = 21_000 + txdata_gas; let sender_account_after = AccountRlp { - // TODO: Should be 21k; 1k gas should be refunded. - balance: sender_account_before.balance - value - 22_000 * 10, + balance: sender_account_before.balance - value - gas_used * 10, nonce: sender_account_before.nonce + 1, ..sender_account_before }; From d5003b7cf2c44cefd12a1178a1df5580ea5d25d5 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Sun, 12 Mar 2023 21:35:04 -0700 Subject: [PATCH 3/3] Gas fees go to coinbase --- evm/src/cpu/kernel/aggregator.rs | 2 + evm/src/cpu/kernel/asm/core/process_txn.asm | 71 +++++++++++++++---- evm/src/cpu/kernel/asm/util/basic_macros.asm | 8 +-- .../cpu/kernel/constants/global_metadata.rs | 7 +- evm/src/cpu/kernel/constants/txn_fields.rs | 18 +++-- evm/src/generation/mod.rs | 38 +++++++++- evm/tests/basic_smart_contract.rs | 27 +++++-- evm/tests/simple_transfer.rs | 28 ++++++-- 8 files changed, 162 insertions(+), 37 deletions(-) diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs index 1d8f11a0..e204f26f 100644 --- a/evm/src/cpu/kernel/aggregator.rs +++ b/evm/src/cpu/kernel/aggregator.rs @@ -11,6 +11,8 @@ pub static KERNEL: Lazy = Lazy::new(combined_kernel); pub(crate) fn combined_kernel() -> Kernel { let files = vec![ + "global jumped_to_0: PANIC", + "global jumped_to_1: PANIC", include_str!("asm/core/bootloader.asm"), include_str!("asm/core/call.asm"), include_str!("asm/core/create.asm"), diff --git a/evm/src/cpu/kernel/asm/core/process_txn.asm b/evm/src/cpu/kernel/asm/core/process_txn.asm index 8a6c3afb..9629d647 100644 --- a/evm/src/cpu/kernel/asm/core/process_txn.asm +++ b/evm/src/cpu/kernel/asm/core/process_txn.asm @@ -7,15 +7,7 @@ // 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) + %compute_fees // stack: retdest // Compute this transaction's intrinsic gas and store it. @@ -42,9 +34,8 @@ global buy_gas: // stack: gas_cost, retdest %mload_txn_field(@TXN_FIELD_ORIGIN) // stack: sender_addr, gas_cost, retdest - %deduct_eth // TODO: It should be transferred to coinbase instead? + %deduct_eth // stack: deduct_eth_status, retdest -global txn_failure_insufficient_balance: %jumpi(panic) // stack: retdest @@ -113,13 +104,12 @@ global process_message_txn_insufficient_balance: PANIC // TODO global process_message_txn_return: - // Refund leftover gas. // stack: retdest %mload_txn_field(@TXN_FIELD_INTRINSIC_GAS) %mload_txn_field(@TXN_FIELD_GAS_LIMIT) SUB // stack: leftover_gas, retdest - %refund_leftover_gas_cost + %pay_coinbase_and_refund_sender // stack: retdest JUMP @@ -151,17 +141,68 @@ global process_message_txn_after_call: // stack: success, leftover_gas, new_ctx, retdest POP // TODO: Success will go into the receipt when we support that. // stack: leftover_gas, new_ctx, retdest - %refund_leftover_gas_cost + %pay_coinbase_and_refund_sender // stack: new_ctx, retdest POP JUMP -%macro refund_leftover_gas_cost +%macro pay_coinbase_and_refund_sender // stack: leftover_gas + DUP1 + // stack: leftover_gas, leftover_gas + %mload_txn_field(@TXN_FIELD_GAS_LIMIT) + SUB + // stack: used_gas, leftover_gas + %mload_global_metadata(@GLOBAL_METADATA_REFUND_COUNTER) + // stack: refund, used_gas, leftover_gas + DUP2 %div_const(2) // max_refund = used_gas/2 + // stack: max_refund, refund, used_gas, leftover_gas + %min + %stack (refund, used_gas, leftover_gas) -> (leftover_gas, refund, refund, used_gas) + ADD + // stack: leftover_gas', refund, used_gas + SWAP2 + // stack: used_gas, refund, leftover_gas' + SUB + // stack: used_gas', leftover_gas' + + // Pay the coinbase. + %mload_txn_field(@TXN_FIELD_COMPUTED_PRIORITY_FEE_PER_GAS) + MUL + // stack: used_gas_tip, leftover_gas' + %mload_global_metadata(@GLOBAL_METADATA_BLOCK_BENEFICIARY) + // stack: coinbase, used_gas_tip, leftover_gas' + %add_eth + // stack: leftover_gas' + + // Refund gas to the origin. %mload_txn_field(@TXN_FIELD_COMPUTED_FEE_PER_GAS) MUL // stack: leftover_gas_cost %mload_txn_field(@TXN_FIELD_ORIGIN) // stack: origin, leftover_gas_cost %add_eth + // stack: (empty) +%endmacro + +// Sets @TXN_FIELD_MAX_FEE_PER_GAS and @TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS. +%macro compute_fees + // stack: (empty) + %mload_global_metadata(@GLOBAL_METADATA_BLOCK_BASE_FEE) + %mload_txn_field(@TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS) + %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) + // stack: max_fee, max_priority_fee, base_fee + DUP3 DUP2 %assert_ge // Assert max_fee >= base_fee + // stack: max_fee, max_priority_fee, base_fee + %stack (max_fee, max_priority_fee, base_fee) -> (max_fee, base_fee, max_priority_fee, base_fee) + SUB + // stack: max_fee - base_fee, max_priority_fee, base_fee + %min + // stack: computed_priority_fee, base_fee + %stack (computed_priority_fee, base_fee) -> (computed_priority_fee, base_fee, computed_priority_fee) + ADD + // stack: computed_fee, computed_priority_fee + %mstore_txn_field(@TXN_FIELD_COMPUTED_FEE_PER_GAS) + %mstore_txn_field(@TXN_FIELD_COMPUTED_PRIORITY_FEE_PER_GAS) + // stack: (empty) %endmacro diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm index a68d832d..5ee4a1e7 100644 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/util/basic_macros.asm @@ -219,8 +219,8 @@ DUP2 DUP2 // stack: x, y, x, y - LT - // stack: x < y, x, y + GT + // stack: x > y, x, y %select_bool // stack: min %endmacro @@ -230,8 +230,8 @@ DUP2 DUP2 // stack: x, y, x, y - GT - // stack: x > y, x, y + LT + // stack: x < y, x, y %select_bool // stack: max %endmacro diff --git a/evm/src/cpu/kernel/constants/global_metadata.rs b/evm/src/cpu/kernel/constants/global_metadata.rs index cd6c0d7e..cee02e86 100644 --- a/evm/src/cpu/kernel/constants/global_metadata.rs +++ b/evm/src/cpu/kernel/constants/global_metadata.rs @@ -39,10 +39,13 @@ pub(crate) enum GlobalMetadata { BlockGasLimit = 19, BlockChainId = 20, BlockBaseFee = 21, + + /// Gas to refund at the end of the transaction. + RefundCounter = 22, } impl GlobalMetadata { - pub(crate) const COUNT: usize = 20; + pub(crate) const COUNT: usize = 21; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -66,6 +69,7 @@ impl GlobalMetadata { Self::BlockGasLimit, Self::BlockChainId, Self::BlockBaseFee, + Self::RefundCounter, ] } @@ -92,6 +96,7 @@ impl GlobalMetadata { Self::BlockGasLimit => "GLOBAL_METADATA_BLOCK_GAS_LIMIT", Self::BlockChainId => "GLOBAL_METADATA_BLOCK_CHAIN_ID", Self::BlockBaseFee => "GLOBAL_METADATA_BLOCK_BASE_FEE", + Self::RefundCounter => "GLOBAL_METADATA_REFUND_COUNTER", } } } diff --git a/evm/src/cpu/kernel/constants/txn_fields.rs b/evm/src/cpu/kernel/constants/txn_fields.rs index c2931f05..f4364c6f 100644 --- a/evm/src/cpu/kernel/constants/txn_fields.rs +++ b/evm/src/cpu/kernel/constants/txn_fields.rs @@ -9,9 +9,6 @@ pub(crate) enum NormalizedTxnField { Nonce = 2, MaxPriorityFeePerGas = 3, MaxFeePerGas = 4, - /// 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, IntrinsicGas = 7, To = 8, @@ -22,10 +19,15 @@ pub(crate) enum NormalizedTxnField { R = 12, S = 13, Origin = 14, + + /// 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 = 15, + ComputedPriorityFeePerGas = 16, } impl NormalizedTxnField { - pub(crate) const COUNT: usize = 15; + pub(crate) const COUNT: usize = 16; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -34,7 +36,6 @@ impl NormalizedTxnField { Self::Nonce, Self::MaxPriorityFeePerGas, Self::MaxFeePerGas, - Self::ComputedFeePerGas, Self::GasLimit, Self::IntrinsicGas, Self::To, @@ -44,6 +45,8 @@ impl NormalizedTxnField { Self::R, Self::S, Self::Origin, + Self::ComputedFeePerGas, + Self::ComputedPriorityFeePerGas, ] } @@ -55,7 +58,6 @@ 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::IntrinsicGas => "TXN_FIELD_INTRINSIC_GAS", NormalizedTxnField::To => "TXN_FIELD_TO", @@ -65,6 +67,10 @@ impl NormalizedTxnField { NormalizedTxnField::R => "TXN_FIELD_R", NormalizedTxnField::S => "TXN_FIELD_S", NormalizedTxnField::Origin => "TXN_FIELD_ORIGIN", + NormalizedTxnField::ComputedFeePerGas => "TXN_FIELD_COMPUTED_FEE_PER_GAS", + NormalizedTxnField::ComputedPriorityFeePerGas => { + "TXN_FIELD_COMPUTED_PRIORITY_FEE_PER_GAS" + } } } } diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 9bde0106..c01e8af1 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use eth_trie_utils::partial_trie::PartialTrie; -use ethereum_types::{Address, BigEndianHash, H256}; +use ethereum_types::{Address, BigEndianHash, H256, U256}; use plonky2::field::extension::Extendable; use plonky2::field::polynomial::PolynomialValues; use plonky2::hash::hash_types::RichField; @@ -24,7 +24,7 @@ use crate::generation::state::GenerationState; use crate::generation::trie_extractor::read_state_trie_value; use crate::memory::segments::Segment; use crate::proof::{BlockMetadata, PublicValues, TrieRoots}; -use crate::witness::memory::MemoryAddress; +use crate::witness::memory::{MemoryAddress, MemoryChannel}; use crate::witness::transition::transition; pub mod mpt; @@ -33,6 +33,7 @@ pub(crate) mod rlp; pub(crate) mod state; mod trie_extractor; use crate::generation::trie_extractor::read_trie; +use crate::witness::util::mem_write_log; #[derive(Clone, Debug, Deserialize, Serialize, Default)] /// Inputs needed for trace generation. @@ -67,6 +68,37 @@ pub struct TrieInputs { pub storage_tries: Vec<(Address, PartialTrie)>, } +fn apply_metadata_memops, const D: usize>( + state: &mut GenerationState, + metadata: &BlockMetadata, +) { + let fields = [ + ( + GlobalMetadata::BlockBeneficiary, + U256::from_big_endian(&metadata.block_beneficiary.0), + ), + (GlobalMetadata::BlockTimestamp, metadata.block_timestamp), + (GlobalMetadata::BlockNumber, metadata.block_number), + (GlobalMetadata::BlockDifficulty, metadata.block_difficulty), + (GlobalMetadata::BlockGasLimit, metadata.block_gaslimit), + (GlobalMetadata::BlockChainId, metadata.block_chain_id), + (GlobalMetadata::BlockBaseFee, metadata.block_base_fee), + ]; + + let channel = MemoryChannel::GeneralPurpose(0); + let ops = fields.map(|(field, val)| { + mem_write_log( + channel, + MemoryAddress::new(0, Segment::GlobalMetadata, field as usize), + state, + val, + ) + }); + + state.memory.apply_ops(&ops); + state.traces.memory_ops.extend(ops); +} + pub(crate) fn generate_traces, const D: usize>( all_stark: &AllStark, inputs: GenerationInputs, @@ -75,6 +107,8 @@ pub(crate) fn generate_traces, const D: usize>( ) -> anyhow::Result<([Vec>; NUM_TABLES], PublicValues)> { let mut state = GenerationState::::new(inputs.clone(), &KERNEL.code); + apply_metadata_memops(&mut state, &inputs.block_metadata); + generate_bootstrap_kernel::(&mut state); timed!(timing, "simulate CPU", simulate_cpu(&mut state)?); diff --git a/evm/tests/basic_smart_contract.rs b/evm/tests/basic_smart_contract.rs index c127ae33..71d11933 100644 --- a/evm/tests/basic_smart_contract.rs +++ b/evm/tests/basic_smart_contract.rs @@ -3,7 +3,7 @@ use std::time::Duration; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use eth_trie_utils::partial_trie::{Nibbles, PartialTrie}; -use ethereum_types::U256; +use ethereum_types::{Address, U256}; use hex_literal::hex; use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; @@ -30,13 +30,17 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { let all_stark = AllStark::::default(); let config = StarkConfig::standard_fast_config(); + let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23"); let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"); + + let beneficiary_state_key = keccak(beneficiary); let sender_state_key = keccak(sender); let to_state_key = keccak(to); + + let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap(); let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); - let value = U256::from(100u32); let push1 = get_push_opcode(1); let add = get_opcode("ADD"); @@ -45,12 +49,12 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { let code_gas = 3 + 3 + 3; let code_hash = keccak(code); + let beneficiary_account_before = AccountRlp::default(); let sender_account_before = AccountRlp { nonce: 5.into(), balance: eth_to_wei(100_000.into()), ..AccountRlp::default() }; - let to_account_before = AccountRlp { code_hash, ..AccountRlp::default() @@ -83,8 +87,12 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { // Generated using a little py-evm script. let txn = hex!("f861050a8255f094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0648242421ba02c89eb757d9deeb1f5b3859a9d4d679951ef610ac47ad4608dc142beb1b7e313a05af7e9fbab825455d36c36c7f4cfcafbeafa9a77bdff936b52afb36d4fe4bcdd"); + let value = U256::from(100u32); - let block_metadata = BlockMetadata::default(); + let block_metadata = BlockMetadata { + block_beneficiary: Address::from(beneficiary), + ..BlockMetadata::default() + }; let mut contract_code = HashMap::new(); contract_code.insert(code_hash, code.to_vec()); @@ -102,6 +110,11 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { let expected_state_trie_after = { let txdata_gas = 2 * 16; let gas_used = 21_000 + code_gas + txdata_gas; + + let beneficiary_account_after = AccountRlp { + balance: beneficiary_account_before.balance + gas_used * 10, + ..beneficiary_account_before + }; let sender_account_after = AccountRlp { balance: sender_account_before.balance - value - gas_used * 10, nonce: sender_account_before.nonce + 1, @@ -113,6 +126,11 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { }; let mut children = core::array::from_fn(|_| PartialTrie::Empty.into()); + children[beneficiary_nibbles.get_nibble(0) as usize] = PartialTrie::Leaf { + nibbles: beneficiary_nibbles.truncate_n_nibbles_front(1), + value: rlp::encode(&beneficiary_account_after).to_vec(), + } + .into(); children[sender_nibbles.get_nibble(0) as usize] = PartialTrie::Leaf { nibbles: sender_nibbles.truncate_n_nibbles_front(1), value: rlp::encode(&sender_account_after).to_vec(), @@ -123,7 +141,6 @@ 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/simple_transfer.rs b/evm/tests/simple_transfer.rs index a04e5af3..094bc070 100644 --- a/evm/tests/simple_transfer.rs +++ b/evm/tests/simple_transfer.rs @@ -3,7 +3,7 @@ use std::time::Duration; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use eth_trie_utils::partial_trie::{Nibbles, PartialTrie}; -use ethereum_types::U256; +use ethereum_types::{Address, U256}; use hex_literal::hex; use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; @@ -29,20 +29,26 @@ fn test_simple_transfer() -> anyhow::Result<()> { let all_stark = AllStark::::default(); let config = StarkConfig::standard_fast_config(); + let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23"); let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"); + + let beneficiary_state_key = keccak(beneficiary); let sender_state_key = keccak(sender); let to_state_key = keccak(to); + + let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap(); let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); - let value = U256::from(100u32); + let beneficiary_account_before = AccountRlp::default(); let sender_account_before = AccountRlp { nonce: 5.into(), balance: eth_to_wei(100_000.into()), storage_root: PartialTrie::Empty.calc_hash(), code_hash: keccak([]), }; + let to_account_before = AccountRlp::default(); let state_trie_before = PartialTrie::Leaf { nibbles: sender_nibbles, @@ -57,8 +63,12 @@ fn test_simple_transfer() -> anyhow::Result<()> { // Generated using a little py-evm script. let txn = hex!("f861050a8255f094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0648242421ba02c89eb757d9deeb1f5b3859a9d4d679951ef610ac47ad4608dc142beb1b7e313a05af7e9fbab825455d36c36c7f4cfcafbeafa9a77bdff936b52afb36d4fe4bcdd"); + let value = U256::from(100u32); - let block_metadata = BlockMetadata::default(); + let block_metadata = BlockMetadata { + block_beneficiary: Address::from(beneficiary), + ..BlockMetadata::default() + }; let inputs = GenerationInputs { signed_txns: vec![txn.to_vec()], @@ -74,6 +84,11 @@ fn test_simple_transfer() -> anyhow::Result<()> { let expected_state_trie_after = { let txdata_gas = 2 * 16; let gas_used = 21_000 + txdata_gas; + + let beneficiary_account_after = AccountRlp { + balance: beneficiary_account_before.balance + gas_used * 10, + ..beneficiary_account_before + }; let sender_account_after = AccountRlp { balance: sender_account_before.balance - value - gas_used * 10, nonce: sender_account_before.nonce + 1, @@ -81,10 +96,15 @@ fn test_simple_transfer() -> anyhow::Result<()> { }; let to_account_after = AccountRlp { balance: value, - ..AccountRlp::default() + ..to_account_before }; let mut children = core::array::from_fn(|_| PartialTrie::Empty.into()); + children[beneficiary_nibbles.get_nibble(0) as usize] = PartialTrie::Leaf { + nibbles: beneficiary_nibbles.truncate_n_nibbles_front(1), + value: rlp::encode(&beneficiary_account_after).to_vec(), + } + .into(); children[sender_nibbles.get_nibble(0) as usize] = PartialTrie::Leaf { nibbles: sender_nibbles.truncate_n_nibbles_front(1), value: rlp::encode(&sender_account_after).to_vec(),