From db8988fe6400eb4350c46c78b1af2fdf1263edae Mon Sep 17 00:00:00 2001 From: jangko Date: Wed, 30 Jun 2021 20:30:39 +0700 Subject: [PATCH] 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 --- nimbus/chain_config.nim | 3 ++- nimbus/genesis.nim | 7 ++++++- nimbus/p2p/executor.nim | 38 ++++++++++++++++++++++++++++++++------ nimbus/p2p/validate.nim | 27 +++++++++++++++++++++++++++ nimbus/utils/header.nim | 3 ++- nimbus/vm/evmc_host.nim | 1 + nimbus/vm_state.nim | 1 + 7 files changed, 71 insertions(+), 9 deletions(-) diff --git a/nimbus/chain_config.nim b/nimbus/chain_config.nim index e7ddca78c..e48e62880 100644 --- a/nimbus/chain_config.nim +++ b/nimbus/chain_config.nim @@ -68,7 +68,8 @@ type mixHash* : Hash256 coinbase* : EthAddress alloc* : GenesisAlloc - + baseFeePerGas*: Option[UInt256] + GenesisAlloc* = Table[EthAddress, GenesisAccount] GenesisAccount* = object code* : seq[byte] diff --git a/nimbus/genesis.nim b/nimbus/genesis.nim index 2bebfb602..a91712620 100644 --- a/nimbus/genesis.nim +++ b/nimbus/genesis.nim @@ -4,7 +4,7 @@ import chronicles, eth/trie/db, ./db/[db_chain, state_db], ./genesis_alloc, ./config, ./constants, - ./chain_config + ./chain_config, ./forks, ./p2p/gaslimit proc defaultGenesisBlockForNetwork*(id: NetworkId): Genesis = result = case id @@ -74,6 +74,11 @@ proc toBlock*(g: Genesis, db: BaseChainDB = nil): BlockHeader = 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: result.gasLimit = GENESIS_GAS_LIMIT diff --git a/nimbus/p2p/executor.nim b/nimbus/p2p/executor.nim index 0d4880ee4..3e02d0d1c 100644 --- a/nimbus/p2p/executor.nim +++ b/nimbus/p2p/executor.nim @@ -6,6 +6,11 @@ import options, sets, ./dao, ./validate, ../config, ../forks, ../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 = ## Process the transaction, write the results to db. @@ -13,18 +18,33 @@ proc processTransaction*(tx: Transaction, sender: EthAddress, vmState: BaseVMSta trace "Sender", sender 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): 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 - let miner = vmState.coinbase() - vmState.mutateStateDB: - # miner fee - let txFee = result.u256 * tx.gasPrice.u256 - db.addBalance(miner, txFee) - for deletedAccount in vmState.selfDestructs: db.deleteAccount deletedAccount @@ -145,6 +165,12 @@ proc processBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, v return ValidationResult.Error 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: let h = chainDB.persistUncles(body.uncles) if h != header.ommersHash: diff --git a/nimbus/p2p/validate.nim b/nimbus/p2p/validate.nim index e1b0e4a6a..a1f612f81 100644 --- a/nimbus/p2p/validate.nim +++ b/nimbus/p2p/validate.nim @@ -259,6 +259,10 @@ proc validateUncles(chainDB: BaseChainDB; header: BlockHeader; if result.isErr: return + result = chainDB.validateGasLimitOrBaseFee(uncle, uncleParent) + if result.isErr: + return + result = ok() # ------------------------------------------------------------------------------ @@ -270,6 +274,14 @@ proc validateTransaction*(vmState: BaseVMState, tx: Transaction, let balance = vmState.readOnlyStateDB.getBalance(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: debug "invalid tx: block header gasLimit reached", maxLimit=vmState.blockHeader.gasLimit, @@ -277,6 +289,21 @@ proc validateTransaction*(vmState: BaseVMState, tx: Transaction, addition=tx.gasLimit 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 if gasCost > balance: debug "invalid tx: not enough cash for gas", diff --git a/nimbus/utils/header.nim b/nimbus/utils/header.nim index c2773eee2..61f380ba8 100644 --- a/nimbus/utils/header.nim +++ b/nimbus/utils/header.nim @@ -59,7 +59,7 @@ proc computeGasLimit*(parent: BlockHeader, gasLimitFloor: GasInt): GasInt = proc generateHeaderFromParentHeader*(config: ChainConfig, parent: BlockHeader, coinbase: EthAddress, timestamp: Option[EthTime], - gasLimit: Option[GasInt], extraData: Blob): BlockHeader = + gasLimit: Option[GasInt], extraData: Blob, baseFee: Option[Uint256]): BlockHeader = var lcTimestamp: EthTime if timestamp.isNone: @@ -78,4 +78,5 @@ proc generateHeaderFromParentHeader*(config: ChainConfig, parent: BlockHeader, stateRoot: parent.stateRoot, coinbase: coinbase, extraData: extraData, + fee: baseFee ) diff --git a/nimbus/vm/evmc_host.nim b/nimbus/vm/evmc_host.nim index 65a1f5938..067fe752f 100644 --- a/nimbus/vm/evmc_host.nim +++ b/nimbus/vm/evmc_host.nim @@ -18,6 +18,7 @@ proc hostGetTxContextImpl(ctx: Computation): nimbus_tx_context {.cdecl.} = result.block_gas_limit = int64(vmstate.blockHeader.gasLimit) result.block_difficulty = toEvmc(vmstate.difficulty) 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.} = ctx.vmState.getAncestorHash(number.u256) diff --git a/nimbus/vm_state.nim b/nimbus/vm_state.nim index 643ddf4a8..053f96814 100644 --- a/nimbus/vm_state.nim +++ b/nimbus/vm_state.nim @@ -32,6 +32,7 @@ export vms.disableTracing, vms.enableTracing, vms.gasLimit, + vms.baseFee, vms.generateWitness, vms.`generateWitness=`, vms.getAncestorHash,