EIP-1559: Fee market change for ETH 1.0 chain

Transaction and BlockHeader already updated in nim-eth repo
to support EIP-1559

EIP-1559 header validation and gasLimit validation
already implemented in previous commit

This commit deals with block validation:
- Effective gasPrice per EIP-1559
- new miner reward based on priorityFee
This commit is contained in:
jangko 2021-06-30 20:30:39 +07:00
parent 7600046a11
commit db8988fe64
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
7 changed files with 71 additions and 9 deletions

View File

@ -68,6 +68,7 @@ type
mixHash* : Hash256 mixHash* : Hash256
coinbase* : EthAddress coinbase* : EthAddress
alloc* : GenesisAlloc alloc* : GenesisAlloc
baseFeePerGas*: Option[UInt256]
GenesisAlloc* = Table[EthAddress, GenesisAccount] GenesisAlloc* = Table[EthAddress, GenesisAccount]
GenesisAccount* = object GenesisAccount* = object

View File

@ -4,7 +4,7 @@ import
chronicles, eth/trie/db, chronicles, eth/trie/db,
./db/[db_chain, state_db], ./db/[db_chain, state_db],
./genesis_alloc, ./config, ./constants, ./genesis_alloc, ./config, ./constants,
./chain_config ./chain_config, ./forks, ./p2p/gaslimit
proc defaultGenesisBlockForNetwork*(id: NetworkId): Genesis = proc defaultGenesisBlockForNetwork*(id: NetworkId): Genesis =
result = case id result = case id
@ -74,6 +74,11 @@ proc toBlock*(g: Genesis, db: BaseChainDB = nil): BlockHeader =
ommersHash: EMPTY_UNCLE_HASH ommersHash: EMPTY_UNCLE_HASH
) )
if g.baseFeePerGas.isSome:
result.baseFee = g.baseFeePerGas.get()
elif db.isNil.not and db.config.toFork(0.toBlockNumber) >= FkLondon:
result.baseFee = EIP1559_INITIAL_BASE_FEE.u256
if g.gasLimit == 0: if g.gasLimit == 0:
result.gasLimit = GENESIS_GAS_LIMIT result.gasLimit = GENESIS_GAS_LIMIT

View File

@ -6,6 +6,11 @@ import options, sets,
./dao, ./validate, ../config, ../forks, ./dao, ./validate, ../config, ../forks,
../transaction/call_evm ../transaction/call_evm
proc eip1559TxNormalization(tx: Transaction): Transaction =
result = tx
if tx.txType < TxEip1559:
result.maxPriorityFee = tx.gasPrice
result.maxFee = tx.gasPrice
proc processTransaction*(tx: Transaction, sender: EthAddress, vmState: BaseVMState, fork: Fork): GasInt = proc processTransaction*(tx: Transaction, sender: EthAddress, vmState: BaseVMState, fork: Fork): GasInt =
## Process the transaction, write the results to db. ## Process the transaction, write the results to db.
@ -13,18 +18,33 @@ proc processTransaction*(tx: Transaction, sender: EthAddress, vmState: BaseVMSta
trace "Sender", sender trace "Sender", sender
trace "txHash", rlpHash = tx.rlpHash trace "txHash", rlpHash = tx.rlpHash
var tx = eip1559TxNormalization(tx)
var priorityFee: GasInt
if fork >= FkLondon:
# priority fee is capped because the base fee is filled first
let baseFee = vmState.blockHeader.baseFee.truncate(GasInt)
priorityFee = min(tx.maxPriorityFee, tx.maxFee - baseFee)
# signer pays both the priority fee and the base fee
# tx.gasPrice now is the effective gasPrice
tx.gasPrice = priorityFee + baseFee
let miner = vmState.coinbase()
if validateTransaction(vmState, tx, sender, fork): if validateTransaction(vmState, tx, sender, fork):
result = txCallEvm(tx, sender, vmState, fork) result = txCallEvm(tx, sender, vmState, fork)
# miner fee
if fork >= FkLondon:
# miner only receives the priority fee;
# note that the base fee is not given to anyone (it is burned)
let txFee = result.u256 * priorityFee.u256
vmState.accountDb.addBalance(miner, txFee)
else:
let txFee = result.u256 * tx.gasPrice.u256
vmState.accountDb.addBalance(miner, txFee)
vmState.cumulativeGasUsed += result vmState.cumulativeGasUsed += result
let miner = vmState.coinbase()
vmState.mutateStateDB: vmState.mutateStateDB:
# miner fee
let txFee = result.u256 * tx.gasPrice.u256
db.addBalance(miner, txFee)
for deletedAccount in vmState.selfDestructs: for deletedAccount in vmState.selfDestructs:
db.deleteAccount deletedAccount db.deleteAccount deletedAccount
@ -145,6 +165,12 @@ proc processBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, v
return ValidationResult.Error return ValidationResult.Error
vmState.receipts[txIndex] = makeReceipt(vmState, fork, tx.txType) vmState.receipts[txIndex] = makeReceipt(vmState, fork, tx.txType)
if vmState.cumulativeGasUsed != header.gasUsed:
debug "gasUsed neq cumulativeGasUsed",
gasUsed=header.gasUsed,
cumulativeGasUsed=vmState.cumulativeGasUsed
return ValidationResult.Error
if header.ommersHash != EMPTY_UNCLE_HASH: if header.ommersHash != EMPTY_UNCLE_HASH:
let h = chainDB.persistUncles(body.uncles) let h = chainDB.persistUncles(body.uncles)
if h != header.ommersHash: if h != header.ommersHash:

View File

@ -259,6 +259,10 @@ proc validateUncles(chainDB: BaseChainDB; header: BlockHeader;
if result.isErr: if result.isErr:
return return
result = chainDB.validateGasLimitOrBaseFee(uncle, uncleParent)
if result.isErr:
return
result = ok() result = ok()
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -270,6 +274,14 @@ proc validateTransaction*(vmState: BaseVMState, tx: Transaction,
let balance = vmState.readOnlyStateDB.getBalance(sender) let balance = vmState.readOnlyStateDB.getBalance(sender)
let nonce = vmState.readOnlyStateDB.getNonce(sender) let nonce = vmState.readOnlyStateDB.getNonce(sender)
if tx.txType == TxEip2930 and fork < FkBerlin:
debug "invalid tx: Eip2930 Tx type detected before Berlin"
return
if tx.txType == TxEip1559 and fork < FkLondon:
debug "invalid tx: Eip1559 Tx type detected before London"
return
if vmState.cumulativeGasUsed + tx.gasLimit > vmState.blockHeader.gasLimit: if vmState.cumulativeGasUsed + tx.gasLimit > vmState.blockHeader.gasLimit:
debug "invalid tx: block header gasLimit reached", debug "invalid tx: block header gasLimit reached",
maxLimit=vmState.blockHeader.gasLimit, maxLimit=vmState.blockHeader.gasLimit,
@ -277,6 +289,21 @@ proc validateTransaction*(vmState: BaseVMState, tx: Transaction,
addition=tx.gasLimit addition=tx.gasLimit
return return
# ensure that the user was willing to at least pay the base fee
let baseFee = vmState.blockHeader.baseFee.truncate(GasInt)
if tx.maxFee < baseFee:
debug "invalid tx: maxFee is smaller than baseFee",
maxFee=tx.maxFee,
baseFee=baseFee
return
# The total must be the larger of the two
if tx.maxFee < tx.maxPriorityFee:
debug "invalid tx: maxFee is smaller than maPriorityFee",
maxFee=tx.maxFee,
maxPriorityFee=tx.maxPriorityFee
return
let gasCost = tx.gasLimit.u256 * tx.gasPrice.u256 let gasCost = tx.gasLimit.u256 * tx.gasPrice.u256
if gasCost > balance: if gasCost > balance:
debug "invalid tx: not enough cash for gas", debug "invalid tx: not enough cash for gas",

View File

@ -59,7 +59,7 @@ proc computeGasLimit*(parent: BlockHeader, gasLimitFloor: GasInt): GasInt =
proc generateHeaderFromParentHeader*(config: ChainConfig, parent: BlockHeader, proc generateHeaderFromParentHeader*(config: ChainConfig, parent: BlockHeader,
coinbase: EthAddress, timestamp: Option[EthTime], coinbase: EthAddress, timestamp: Option[EthTime],
gasLimit: Option[GasInt], extraData: Blob): BlockHeader = gasLimit: Option[GasInt], extraData: Blob, baseFee: Option[Uint256]): BlockHeader =
var lcTimestamp: EthTime var lcTimestamp: EthTime
if timestamp.isNone: if timestamp.isNone:
@ -78,4 +78,5 @@ proc generateHeaderFromParentHeader*(config: ChainConfig, parent: BlockHeader,
stateRoot: parent.stateRoot, stateRoot: parent.stateRoot,
coinbase: coinbase, coinbase: coinbase,
extraData: extraData, extraData: extraData,
fee: baseFee
) )

View File

@ -18,6 +18,7 @@ proc hostGetTxContextImpl(ctx: Computation): nimbus_tx_context {.cdecl.} =
result.block_gas_limit = int64(vmstate.blockHeader.gasLimit) result.block_gas_limit = int64(vmstate.blockHeader.gasLimit)
result.block_difficulty = toEvmc(vmstate.difficulty) result.block_difficulty = toEvmc(vmstate.difficulty)
result.chain_id = toEvmc(vmstate.chaindb.config.chainId.uint.u256) result.chain_id = toEvmc(vmstate.chaindb.config.chainId.uint.u256)
result.block_base_fee = toEvmc(vmstate.blockHeader.baseFee)
proc hostGetBlockHashImpl(ctx: Computation, number: int64): Hash256 {.cdecl.} = proc hostGetBlockHashImpl(ctx: Computation, number: int64): Hash256 {.cdecl.} =
ctx.vmState.getAncestorHash(number.u256) ctx.vmState.getAncestorHash(number.u256)

View File

@ -32,6 +32,7 @@ export
vms.disableTracing, vms.disableTracing,
vms.enableTracing, vms.enableTracing,
vms.gasLimit, vms.gasLimit,
vms.baseFee,
vms.generateWitness, vms.generateWitness,
vms.`generateWitness=`, vms.`generateWitness=`,
vms.getAncestorHash, vms.getAncestorHash,