EIP-4399 implementation of nim-vm

what's new:
- `RANDOM` OPCODE
- `random` field of BlockHeader(previously `mixDigest`)
- `PostMerge` temporary name
This commit is contained in:
jangko 2022-02-01 15:35:30 +07:00 committed by zah
parent 77c9b8c2f0
commit 71aa7e4b5c
13 changed files with 84 additions and 9 deletions

View File

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

View File

@ -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].}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -38,6 +38,7 @@ type
timestamp* : EthTime
gasLimit* : GasInt
fee* : Option[Uint256]
random* : Hash256
name* : string
flags* : set[VMFlag]
tracer* : TransactionTracer