From 71aa7e4b5c9f9748570bb98c9d7c4b624a7b5711 Mon Sep 17 00:00:00 2001 From: jangko Date: Tue, 1 Feb 2022 15:35:30 +0700 Subject: [PATCH] EIP-4399 implementation of nim-vm what's new: - `RANDOM` OPCODE - `random` field of BlockHeader(previously `mixDigest`) - `PostMerge` temporary name --- nimbus/forks.nim | 3 +++ nimbus/p2p/executor/calculate_reward.nim | 4 +++- nimbus/transaction/host_services.nim | 11 +++++++++-- nimbus/transaction/host_types.nim | 3 +++ nimbus/utils/tx_pool/tx_chain.nim | 6 +++++- nimbus/vm/computation.nim | 9 +++++++++ nimbus/vm/evmc_host.nim | 10 +++++++++- nimbus/vm/interpreter/gas_costs.nim | 8 ++++++-- nimbus/vm/interpreter/opcode_values.nim | 4 ++++ nimbus/vm/interpreter/opcodes_impl.nim | 5 +++++ nimbus/vm/interpreter_dispatch.nim | 19 +++++++++++++++++-- nimbus/vm/state.nim | 10 ++++++++++ nimbus/vm/types.nim | 1 + 13 files changed, 84 insertions(+), 9 deletions(-) diff --git a/nimbus/forks.nim b/nimbus/forks.nim index 12ac040f2..b0a033e72 100644 --- a/nimbus/forks.nim +++ b/nimbus/forks.nim @@ -18,3 +18,6 @@ type FkIstanbul = "Istanbul" FkBerlin = "Berlin" FkLondon = "London" + # TODO: PostMerge is a temporary name + # until we have an official name + FkPostMerge = "PostMerge" diff --git a/nimbus/p2p/executor/calculate_reward.nim b/nimbus/p2p/executor/calculate_reward.nim index 60c67f032..54c5f9afb 100644 --- a/nimbus/p2p/executor/calculate_reward.nim +++ b/nimbus/p2p/executor/calculate_reward.nim @@ -24,6 +24,7 @@ const eth5 = 5.eth eth3 = 3.eth eth2 = 2.eth + eth0 = 0.u256 # Note than the `blockRewards` were previously exported but nowhere # used otherwise. @@ -37,7 +38,8 @@ const eth2, # FkPetersburg eth2, # FkIstanbul eth2, # FkBerlin - eth2 # FkLondon + eth2, # FkLondon + eth0 # FkPostMerge ] {.push raises: [Defect].} diff --git a/nimbus/transaction/host_services.nim b/nimbus/transaction/host_services.nim index 543c571fc..4575292bf 100644 --- a/nimbus/transaction/host_services.nim +++ b/nimbus/transaction/host_services.nim @@ -61,17 +61,24 @@ proc setupTxContext(host: TransactionHost) = # vmState.gasLimit now unused host.txContext.block_gas_limit = vmState.gasLimit # vmState.difficulty now unused - host.txContext.block_difficulty = vmState.difficulty.toEvmc host.txContext.chain_id = vmState.chaindb.config.chainId.uint.u256.toEvmc host.txContext.block_base_fee = vmState.baseFee.toEvmc # Most host functions do `flip256` in `evmc_host_glue`, but due to this # result being cached, it's better to do `flip256` when filling the cache. host.txContext.tx_gas_price = flip256(host.txContext.tx_gas_price) - host.txContext.block_difficulty = flip256(host.txContext.block_difficulty) host.txContext.chain_id = flip256(host.txContext.chain_id) host.txContext.block_base_fee = flip256(host.txContext.block_base_fee) + # EIP-4399 + # Transfer block randomness to difficulty OPCODE + let difficulty = vmState.difficulty.toEvmc + if difficulty.isZero: + # no flipping, because hash is a 32 bytes array + host.txContext.block_difficulty = vmState.random.toEvmc + else: + host.txContext.block_difficulty = flip256(difficulty) + host.cachedTxContext = true const use_evmc_glue = defined(evmc_enabled) diff --git a/nimbus/transaction/host_types.nim b/nimbus/transaction/host_types.nim index ac898ea28..a51211eb8 100644 --- a/nimbus/transaction/host_types.nim +++ b/nimbus/transaction/host_types.nim @@ -94,6 +94,9 @@ template isCreate*(kind: EvmcCallKind): bool = template isStatic*(msg: EvmcMessage): bool = EVMC_STATIC in msg.flags +template isZero*(n: evmc_bytes32): bool = + n == default(evmc_bytes32) + # Nim quirks: Exporting `evmc_status_code` (etc) are needed to access the enum # values, even though alias `EnumStatusCode` is already exported. Exporting # `evmc_flags` won't export the flags, `evmc_flag_bit_shifts` must be used. diff --git a/nimbus/utils/tx_pool/tx_chain.nim b/nimbus/utils/tx_pool/tx_chain.nim index 40c270794..033e3b80d 100644 --- a/nimbus/utils/tx_pool/tx_chain.nim +++ b/nimbus/utils/tx_pool/tx_chain.nim @@ -78,7 +78,11 @@ proc resetTxEnv(dh: TxChainRef; parent: BlockHeader; fee: Option[UInt256]) parent = parent, timestamp = getTime().utc.toTime, gasLimit = (if dh.maxMode: dh.limits.maxLimit else: dh.limits.trgLimit), - fee = fee, + fee = fee, + # EIP-4399 extra complexity + # TODO: make sure from where or what value + # this `random` param should be + random = Hash256(), miner = dh.miner, chainDB = dh.db) diff --git a/nimbus/vm/computation.nim b/nimbus/vm/computation.nim index 4010721f8..12ebd0883 100644 --- a/nimbus/vm/computation.nim +++ b/nimbus/vm/computation.nim @@ -51,6 +51,15 @@ template getDifficulty*(c: Computation): DifficultyInt = else: c.vmState.difficulty +template getRandom*(c: Computation): Hash256 = + when evmc_enabled: + # EIP-4399 + # no flipping because `block_difficulty` in this context + # is a 32 bytes array + Hash256.fromEvmc c.host.getTxContext().block_difficulty + else: + c.vmState.random + template getGasLimit*(c: Computation): GasInt = when evmc_enabled: c.host.getTxContext().block_gas_limit.GasInt diff --git a/nimbus/vm/evmc_host.nim b/nimbus/vm/evmc_host.nim index 0dee3dd45..8e9de99ac 100644 --- a/nimbus/vm/evmc_host.nim +++ b/nimbus/vm/evmc_host.nim @@ -16,7 +16,15 @@ proc hostGetTxContextImpl(ctx: Computation): nimbus_tx_context {.cdecl.} = result.block_number = vmstate.blockNumber.truncate(int64) result.block_timestamp = vmstate.timestamp.toUnix() result.block_gas_limit = int64(vmstate.gasLimit) - result.block_difficulty = toEvmc(vmstate.difficulty) + + # EIP-4399 + # Transfer block randomness to difficulty OPCODE + let difficulty = toEvmc(vmstate.difficulty) + if difficulty == default(evmc_bytes32): # or difficulty.isZero + result.block_difficulty = vmState.random.toEvmc + else: + result.block_difficulty = difficulty + result.chain_id = toEvmc(vmstate.chaindb.config.chainId.uint.u256) result.block_base_fee = toEvmc(vmstate.baseFee) diff --git a/nimbus/vm/interpreter/gas_costs.nim b/nimbus/vm/interpreter/gas_costs.nim index 0ad1cbc8b..99c861fec 100644 --- a/nimbus/vm/interpreter/gas_costs.nim +++ b/nimbus/vm/interpreter/gas_costs.nim @@ -761,7 +761,8 @@ const FkPetersburg: SpuriousGasFees, FkIstanbul: IstanbulGasFees, FkBerlin: BerlinGasFees, - FkLondon: LondonGasFees + FkLondon: LondonGasFees, + FkPostMerge: LondonGasFees ] @@ -773,6 +774,7 @@ gasCosts(FkConstantinople, constantinople, ConstantinopleGasCosts) gasCosts(FkIstanbul, istanbul, IstanbulGasCosts) gasCosts(FkBerlin, berlin, BerlinGasCosts) gasCosts(FkLondon, london, LondonGasCosts) +gasCosts(FkPostMerge, postMerge, PostMergeGasCosts) proc forkToSchedule*(fork: Fork): GasCosts = if fork < FkHomestead: @@ -789,8 +791,10 @@ proc forkToSchedule*(fork: Fork): GasCosts = IstanbulGasCosts elif fork < FkLondon: BerlinGasCosts - else: + elif fork < FkPostMerge: LondonGasCosts + else: + PostMergeGasCosts const ## Precompile costs diff --git a/nimbus/vm/interpreter/opcode_values.nim b/nimbus/vm/interpreter/opcode_values.nim index 643b5fce3..8df40cc19 100644 --- a/nimbus/vm/interpreter/opcode_values.nim +++ b/nimbus/vm/interpreter/opcode_values.nim @@ -188,3 +188,7 @@ fill_enum_holes: Revert = 0xfd, # Halt execution reverting state changes but returning data and remaining gas. Invalid = 0xfe, # Designated invalid instruction. SelfDestruct = 0xff # Halt execution and register account for later deletion. + +const + # EIP-4399 new opcode + Random* = Difficulty diff --git a/nimbus/vm/interpreter/opcodes_impl.nim b/nimbus/vm/interpreter/opcodes_impl.nim index 9b4a8622d..8f583a93d 100644 --- a/nimbus/vm/interpreter/opcodes_impl.nim +++ b/nimbus/vm/interpreter/opcodes_impl.nim @@ -412,6 +412,11 @@ op difficulty, inline = true: ## 0x44, Get the block's difficulty push: c.getDifficulty() +op randomEIP4399, inline = true: + ## since EIP-4399 0x44 renamed from `DIFFICULTY` to `RANDOM` + ## 0x44, Get the block's randomness + push: c.getRandom() + op gasLimit, inline = true: ## 0x45, Get the block's gas limit push: c.getGasLimit() diff --git a/nimbus/vm/interpreter_dispatch.nim b/nimbus/vm/interpreter_dispatch.nim index edf680d55..fcad86fd3 100644 --- a/nimbus/vm/interpreter_dispatch.nim +++ b/nimbus/vm/interpreter_dispatch.nim @@ -249,11 +249,18 @@ let BerlinOpDispatch {.compileTime.}: array[Op, NimNode] = genBerlinJumpTable(Is proc genLondonJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compileTime.} = result = ops - # incoming EIP-3198 and EIP-3529 + # EIP-3198 and EIP-3529 result[BaseFee] = newIdentNode "baseFee" let LondonOpDispatch {.compileTime.}: array[Op, NimNode] = genLondonJumpTable(BerlinOpDispatch) +proc genPostMergeJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compileTime.} = + result = ops + # EIP-4399 + result[Random] = newIdentNode "randomEIP4399" + +let PostMergeOpDispatch {.compileTime.}: array[Op, NimNode] = genPostMergeJumpTable(LondonOpDispatch) + proc opTableToCaseStmt(opTable: array[Op, NimNode], c: NimNode): NimNode = let instr = quote do: `c`.instr @@ -345,6 +352,9 @@ macro genBerlinDispatch(c: Computation): untyped = macro genLondonDispatch(c: Computation): untyped = result = opTableToCaseStmt(LondonOpDispatch, c) +macro genPostMergeDispatch(c: Computation): untyped = + result = opTableToCaseStmt(PostMergeOpDispatch, c) + proc frontierVM(c: Computation) = genFrontierDispatch(c) @@ -375,6 +385,9 @@ proc berlinVM(c: Computation) {.gcsafe.} = proc londonVM(c: Computation) {.gcsafe.} = genLondonDispatch(c) +proc postMergeVM(c: Computation) {.gcsafe.} = + genPostMergeDispatch(c) + proc selectVM(c: Computation, fork: Fork) {.gcsafe.} = # TODO: Optimise getting fork and updating opCodeExec only when necessary case fork @@ -396,8 +409,10 @@ proc selectVM(c: Computation, fork: Fork) {.gcsafe.} = c.istanbulVM() of FkBerlin: c.berlinVM() - else: + of FkLondon: c.londonVM() + else: + c.postMergeVM() proc executeOpcodes(c: Computation) = let fork = c.fork diff --git a/nimbus/vm/state.nim b/nimbus/vm/state.nim index e8bbf6a03..2a0190623 100644 --- a/nimbus/vm/state.nim +++ b/nimbus/vm/state.nim @@ -59,6 +59,7 @@ proc init( timestamp: EthTime; gasLimit: GasInt; fee: Option[Uint256]; + random: Hash256; miner: EthAddress; chainDB: BaseChainDB; tracer: TransactionTracer) @@ -70,6 +71,7 @@ proc init( self.timestamp = timestamp self.gasLimit = gasLimit self.fee = fee + self.random = random self.chaindb = chainDB self.tracer = tracer self.logEntries = @[] @@ -84,6 +86,7 @@ proc init( timestamp: EthTime; gasLimit: GasInt; fee: Option[Uint256]; + random: Hash256; miner: EthAddress; chainDB: BaseChainDB; tracerFlags: set[TracerFlags]) @@ -96,6 +99,7 @@ proc init( timestamp = timestamp, gasLimit = gasLimit, fee = fee, + random = random, miner = miner, chainDB = chainDB, tracer = tracer) @@ -117,6 +121,7 @@ proc new*( timestamp: EthTime; ## tx env: time stamp gasLimit: GasInt; ## tx env: gas limit fee: Option[Uint256]; ## tx env: optional base fee + random: Hash256; ## tx env: POS block randomness miner: EthAddress; ## tx env: coinbase(PoW) or signer(PoA) chainDB: BaseChainDB; ## block chain database tracerFlags: set[TracerFlags] = {}; @@ -136,6 +141,7 @@ proc new*( timestamp = timestamp, gasLimit = gasLimit, fee = fee, + random = random, miner = miner, chainDB = chainDB, tracerFlags = tracerFlags) @@ -145,6 +151,7 @@ proc reinit*(self: BaseVMState; ## Object descriptor timestamp: EthTime; ## tx env: time stamp gasLimit: GasInt; ## tx env: gas limit fee: Option[Uint256]; ## tx env: optional base fee + random: Hash256; ## tx env: POS block randomness miner: EthAddress; ## tx env: coinbase(PoW) or signer(PoA) pruneTrie: bool = true): bool {.gcsafe, raises: [Defect,CatchableError].} = @@ -169,6 +176,7 @@ proc reinit*(self: BaseVMState; ## Object descriptor timestamp = timestamp, gasLimit = gasLimit, fee = fee, + random = random, miner = miner, chainDB = db, tracer = tracer) @@ -191,6 +199,7 @@ proc reinit*(self: BaseVMState; ## Object descriptor timestamp = header.timestamp, gasLimit = header.gasLimit, fee = header.fee, + random = header.random, miner = self.chainDB.getMinerAddress(header), pruneTrie = pruneTrie) @@ -227,6 +236,7 @@ proc init*( header.timestamp, header.gasLimit, header.fee, + header.random, chainDB.getMinerAddress(header), chainDB, tracerFlags) diff --git a/nimbus/vm/types.nim b/nimbus/vm/types.nim index 0e6414aab..1f422550d 100644 --- a/nimbus/vm/types.nim +++ b/nimbus/vm/types.nim @@ -38,6 +38,7 @@ type timestamp* : EthTime gasLimit* : GasInt fee* : Option[Uint256] + random* : Hash256 name* : string flags* : set[VMFlag] tracer* : TransactionTracer