Gas fees go to coinbase

This commit is contained in:
Daniel Lubarov 2023-03-12 21:35:04 -07:00
parent 6dd99e4372
commit d5003b7cf2
8 changed files with 162 additions and 37 deletions

View File

@ -11,6 +11,8 @@ pub static KERNEL: Lazy<Kernel> = 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"),

View File

@ -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

View File

@ -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

View File

@ -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",
}
}
}

View File

@ -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"
}
}
}
}

View File

@ -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<F: RichField + Extendable<D>, const D: usize>(
state: &mut GenerationState<F>,
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<F: RichField + Extendable<D>, const D: usize>(
all_stark: &AllStark<F, D>,
inputs: GenerationInputs,
@ -75,6 +107,8 @@ pub(crate) fn generate_traces<F: RichField + Extendable<D>, const D: usize>(
) -> anyhow::Result<([Vec<PolynomialValues<F>>; NUM_TABLES], PublicValues)> {
let mut state = GenerationState::<F>::new(inputs.clone(), &KERNEL.code);
apply_metadata_memops(&mut state, &inputs.block_metadata);
generate_bootstrap_kernel::<F>(&mut state);
timed!(timing, "simulate CPU", simulate_cpu(&mut state)?);

View File

@ -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::<F, D>::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![],

View File

@ -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::<F, D>::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(),