fix EIP-4399 'random' opcode

- fix previous implementation of EIP-4399
- now `random` opcode can be used with evmc_enabled
This commit is contained in:
jangko 2022-02-08 16:17:59 +07:00
parent 20068de838
commit 28cdfcaf6b
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
19 changed files with 102 additions and 109 deletions

View File

@ -19,6 +19,7 @@ type
networkId*: NetworkId networkId*: NetworkId
config* : ChainConfig config* : ChainConfig
genesis* : Genesis genesis* : Genesis
totalDifficulty*: DifficultyInt
# startingBlock, currentBlock, and highestBlock # startingBlock, currentBlock, and highestBlock
# are progress indicator # are progress indicator
@ -30,6 +31,16 @@ type
blockNumber: BlockNumber blockNumber: BlockNumber
index: int index: int
proc getTotalDifficulty*(self: BaseChainDB): UInt256 =
# this is actually a combination of `getHash` and `getScore`
const key = canonicalHeadHashKey()
let data = self.db.get(key.toOpenArray)
if data.len == 0:
return 0.u256
let blockHash = rlp.decode(data, Hash256)
rlp.decode(self.db.get(blockHashToScoreKey(blockHash).toOpenArray), Uint256)
proc newBaseChainDB*( proc newBaseChainDB*(
db: TrieDatabaseRef, db: TrieDatabaseRef,
pruneTrie: bool = true, pruneTrie: bool = true,
@ -42,6 +53,7 @@ proc newBaseChainDB*(
result.networkId = id result.networkId = id
result.config = params.config result.config = params.config
result.genesis = params.genesis result.genesis = params.genesis
result.totalDifficulty = result.getTotalDifficulty()
proc `$`*(db: BaseChainDB): string = proc `$`*(db: BaseChainDB): string =
result = "BaseChainDB" result = "BaseChainDB"
@ -335,6 +347,7 @@ proc persistHeaderToDb*(self: BaseChainDB; header: BlockHeader): seq[BlockHeader
return self.setAsCanonicalChainHead(headerHash) return self.setAsCanonicalChainHead(headerHash)
if score > headScore: if score > headScore:
self.totalDifficulty = score
result = self.setAsCanonicalChainHead(headerHash) result = self.setAsCanonicalChainHead(headerHash)
proc persistUncles*(self: BaseChainDB, uncles: openarray[BlockHeader]): Hash256 = proc persistUncles*(self: BaseChainDB, uncles: openarray[BlockHeader]): Hash256 =

View File

@ -18,6 +18,3 @@ type
FkIstanbul = "Istanbul" FkIstanbul = "Istanbul"
FkBerlin = "Berlin" FkBerlin = "Berlin"
FkLondon = "London" FkLondon = "London"
# TODO: PostMerge is a temporary name
# until we have an official name
FkPostMerge = "PostMerge"

View File

@ -38,8 +38,7 @@ const
eth2, # FkPetersburg eth2, # FkPetersburg
eth2, # FkIstanbul eth2, # FkIstanbul
eth2, # FkBerlin eth2, # FkBerlin
eth2, # FkLondon eth2 # FkLondon
eth0 # FkPostMerge
] ]
{.push raises: [Defect].} {.push raises: [Defect].}

View File

@ -73,10 +73,6 @@ proc setupTxContext(host: TransactionHost) =
# EIP-4399 # EIP-4399
# Transfer block randomness to difficulty OPCODE # Transfer block randomness to difficulty OPCODE
let difficulty = vmState.difficulty.toEvmc 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.txContext.block_difficulty = flip256(difficulty)
host.cachedTxContext = true host.cachedTxContext = true

View File

@ -66,6 +66,10 @@ type
limits: TxChainGasLimits ## Gas limits for packer and next header limits: TxChainGasLimits ## Gas limits for packer and next header
txEnv: TxChainPackerEnv ## Assorted parameters, tx packer environment txEnv: TxChainPackerEnv ## Assorted parameters, tx packer environment
# EIP-4399 and EIP-3675
ttdReached: bool ## Total Terminal Difficulty reached
random: Hash256 ## POS block randomness
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Private functions # Private functions
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -79,12 +83,10 @@ proc resetTxEnv(dh: TxChainRef; parent: BlockHeader; fee: Option[UInt256])
timestamp = getTime().utc.toTime, timestamp = getTime().utc.toTime,
gasLimit = (if dh.maxMode: dh.limits.maxLimit else: dh.limits.trgLimit), gasLimit = (if dh.maxMode: dh.limits.maxLimit else: dh.limits.trgLimit),
fee = fee, fee = fee,
# EIP-4399 extra complexity random = dh.random,
# TODO: make sure from where or what value
# this `random` param should be
random = Hash256(),
miner = dh.miner, miner = dh.miner,
chainDB = dh.db) chainDB = dh.db,
ttdReached= dh.ttdReached)
dh.txEnv.txRoot = BLANK_ROOT_HASH dh.txEnv.txRoot = BLANK_ROOT_HASH
dh.txEnv.stateRoot = dh.txEnv.vmState.parent.stateRoot dh.txEnv.stateRoot = dh.txEnv.vmState.parent.stateRoot
@ -307,6 +309,14 @@ proc `txRoot=`*(dh: TxChainRef; val: Hash256) =
## Setter ## Setter
dh.txEnv.txRoot = val dh.txEnv.txRoot = val
proc `ttdReached=`*(dh: TxChainRef; val: bool) =
## Setter
dh.ttdReached = val
proc `random=`*(dh: TxChainRef; val: Hash256) =
## Setter
dh.random = val
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# End # End
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -51,15 +51,6 @@ template getDifficulty*(c: Computation): DifficultyInt =
else: else:
c.vmState.difficulty 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 = template getGasLimit*(c: Computation): GasInt =
when evmc_enabled: when evmc_enabled:
c.host.getTxContext().block_gas_limit.GasInt c.host.getTxContext().block_gas_limit.GasInt

View File

@ -20,9 +20,6 @@ proc hostGetTxContextImpl(ctx: Computation): nimbus_tx_context {.cdecl.} =
# EIP-4399 # EIP-4399
# Transfer block randomness to difficulty OPCODE # Transfer block randomness to difficulty OPCODE
let difficulty = toEvmc(vmstate.difficulty) 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.block_difficulty = difficulty
result.chain_id = toEvmc(vmstate.chaindb.config.chainId.uint.u256) result.chain_id = toEvmc(vmstate.chaindb.config.chainId.uint.u256)

View File

@ -761,8 +761,7 @@ const
FkPetersburg: SpuriousGasFees, FkPetersburg: SpuriousGasFees,
FkIstanbul: IstanbulGasFees, FkIstanbul: IstanbulGasFees,
FkBerlin: BerlinGasFees, FkBerlin: BerlinGasFees,
FkLondon: LondonGasFees, FkLondon: LondonGasFees
FkPostMerge: LondonGasFees
] ]
@ -774,7 +773,6 @@ gasCosts(FkConstantinople, constantinople, ConstantinopleGasCosts)
gasCosts(FkIstanbul, istanbul, IstanbulGasCosts) gasCosts(FkIstanbul, istanbul, IstanbulGasCosts)
gasCosts(FkBerlin, berlin, BerlinGasCosts) gasCosts(FkBerlin, berlin, BerlinGasCosts)
gasCosts(FkLondon, london, LondonGasCosts) gasCosts(FkLondon, london, LondonGasCosts)
gasCosts(FkPostMerge, postMerge, PostMergeGasCosts)
proc forkToSchedule*(fork: Fork): GasCosts = proc forkToSchedule*(fork: Fork): GasCosts =
if fork < FkHomestead: if fork < FkHomestead:
@ -791,10 +789,8 @@ proc forkToSchedule*(fork: Fork): GasCosts =
IstanbulGasCosts IstanbulGasCosts
elif fork < FkLondon: elif fork < FkLondon:
BerlinGasCosts BerlinGasCosts
elif fork < FkPostMerge:
LondonGasCosts
else: else:
PostMergeGasCosts LondonGasCosts
const const
## Precompile costs ## Precompile costs

View File

@ -412,11 +412,6 @@ op difficulty, inline = true:
## 0x44, Get the block's difficulty ## 0x44, Get the block's difficulty
push: c.getDifficulty() 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: op gasLimit, inline = true:
## 0x45, Get the block's gas limit ## 0x45, Get the block's gas limit
push: c.getGasLimit() push: c.getGasLimit()

View File

@ -254,13 +254,6 @@ proc genLondonJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compileTi
let LondonOpDispatch {.compileTime.}: array[Op, NimNode] = genLondonJumpTable(BerlinOpDispatch) 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 = proc opTableToCaseStmt(opTable: array[Op, NimNode], c: NimNode): NimNode =
let instr = quote do: `c`.instr let instr = quote do: `c`.instr
@ -352,9 +345,6 @@ macro genBerlinDispatch(c: Computation): untyped =
macro genLondonDispatch(c: Computation): untyped = macro genLondonDispatch(c: Computation): untyped =
result = opTableToCaseStmt(LondonOpDispatch, c) result = opTableToCaseStmt(LondonOpDispatch, c)
macro genPostMergeDispatch(c: Computation): untyped =
result = opTableToCaseStmt(PostMergeOpDispatch, c)
proc frontierVM(c: Computation) = proc frontierVM(c: Computation) =
genFrontierDispatch(c) genFrontierDispatch(c)
@ -385,9 +375,6 @@ proc berlinVM(c: Computation) {.gcsafe.} =
proc londonVM(c: Computation) {.gcsafe.} = proc londonVM(c: Computation) {.gcsafe.} =
genLondonDispatch(c) genLondonDispatch(c)
proc postMergeVM(c: Computation) {.gcsafe.} =
genPostMergeDispatch(c)
proc selectVM(c: Computation, fork: Fork) {.gcsafe.} = proc selectVM(c: Computation, fork: Fork) {.gcsafe.} =
# TODO: Optimise getting fork and updating opCodeExec only when necessary # TODO: Optimise getting fork and updating opCodeExec only when necessary
case fork case fork
@ -409,10 +396,8 @@ proc selectVM(c: Computation, fork: Fork) {.gcsafe.} =
c.istanbulVM() c.istanbulVM()
of FkBerlin: of FkBerlin:
c.berlinVM() c.berlinVM()
of FkLondon:
c.londonVM()
else: else:
c.postMergeVM() c.londonVM()
proc executeOpcodes(c: Computation) = proc executeOpcodes(c: Computation) =
let fork = c.fork let fork = c.fork

View File

@ -62,6 +62,7 @@ proc init(
random: Hash256; random: Hash256;
miner: EthAddress; miner: EthAddress;
chainDB: BaseChainDB; chainDB: BaseChainDB;
ttdReached: bool;
tracer: TransactionTracer) tracer: TransactionTracer)
{.gcsafe, raises: [Defect,CatchableError].} = {.gcsafe, raises: [Defect,CatchableError].} =
## Initialisation helper ## Initialisation helper
@ -73,6 +74,7 @@ proc init(
self.fee = fee self.fee = fee
self.random = random self.random = random
self.chaindb = chainDB self.chaindb = chainDB
self.ttdReached = ttdReached
self.tracer = tracer self.tracer = tracer
self.logEntries = @[] self.logEntries = @[]
self.stateDB = ac self.stateDB = ac
@ -89,6 +91,7 @@ proc init(
random: Hash256; random: Hash256;
miner: EthAddress; miner: EthAddress;
chainDB: BaseChainDB; chainDB: BaseChainDB;
ttdReached: bool;
tracerFlags: set[TracerFlags]) tracerFlags: set[TracerFlags])
{.gcsafe, raises: [Defect,CatchableError].} = {.gcsafe, raises: [Defect,CatchableError].} =
var tracer: TransactionTracer var tracer: TransactionTracer
@ -102,6 +105,7 @@ proc init(
random = random, random = random,
miner = miner, miner = miner,
chainDB = chainDB, chainDB = chainDB,
ttdReached= ttdReached,
tracer = tracer) tracer = tracer)
# -------------- # --------------
@ -124,6 +128,7 @@ proc new*(
random: Hash256; ## tx env: POS block randomness random: Hash256; ## tx env: POS block randomness
miner: EthAddress; ## tx env: coinbase(PoW) or signer(PoA) miner: EthAddress; ## tx env: coinbase(PoW) or signer(PoA)
chainDB: BaseChainDB; ## block chain database chainDB: BaseChainDB; ## block chain database
ttdReached: bool; ## total terminal difficulty reached
tracerFlags: set[TracerFlags] = {}; tracerFlags: set[TracerFlags] = {};
pruneTrie: bool = true): T pruneTrie: bool = true): T
{.gcsafe, raises: [Defect,CatchableError].} = {.gcsafe, raises: [Defect,CatchableError].} =
@ -144,6 +149,7 @@ proc new*(
random = random, random = random,
miner = miner, miner = miner,
chainDB = chainDB, chainDB = chainDB,
ttdReached = ttdReached,
tracerFlags = tracerFlags) tracerFlags = tracerFlags)
proc reinit*(self: BaseVMState; ## Object descriptor proc reinit*(self: BaseVMState; ## Object descriptor
@ -153,6 +159,7 @@ proc reinit*(self: BaseVMState; ## Object descriptor
fee: Option[Uint256]; ## tx env: optional base fee fee: Option[Uint256]; ## tx env: optional base fee
random: Hash256; ## tx env: POS block randomness random: Hash256; ## tx env: POS block randomness
miner: EthAddress; ## tx env: coinbase(PoW) or signer(PoA) miner: EthAddress; ## tx env: coinbase(PoW) or signer(PoA)
ttdReached:bool; ## total terminal difficulty reached
pruneTrie: bool = true): bool pruneTrie: bool = true): bool
{.gcsafe, raises: [Defect,CatchableError].} = {.gcsafe, raises: [Defect,CatchableError].} =
## Re-initialise state descriptor. The `AccountsCache` database is ## Re-initialise state descriptor. The `AccountsCache` database is
@ -179,10 +186,17 @@ proc reinit*(self: BaseVMState; ## Object descriptor
random = random, random = random,
miner = miner, miner = miner,
chainDB = db, chainDB = db,
ttdReached = ttdReached,
tracer = tracer) tracer = tracer)
return true return true
# else: false # else: false
proc ttd(chainDB: BaseChainDB): DifficultyInt =
if chainDB.config.terminalTotalDifficulty.isSome:
chainDB.config.terminalTotalDifficulty.get()
else:
high(DifficultyInt)
proc reinit*(self: BaseVMState; ## Object descriptor proc reinit*(self: BaseVMState; ## Object descriptor
parent: BlockHeader; ## parent header, account sync pos. parent: BlockHeader; ## parent header, account sync pos.
header: BlockHeader; ## header with tx environment data fields header: BlockHeader; ## header with tx environment data fields
@ -194,6 +208,7 @@ proc reinit*(self: BaseVMState; ## Object descriptor
## ##
## It requires the `header` argument properly initalised so that for PoA ## It requires the `header` argument properly initalised so that for PoA
## networks, the miner address is retrievable via `ecRecover()`. ## networks, the miner address is retrievable via `ecRecover()`.
let ttdReached = self.chainDB.totalDifficulty + header.difficulty > self.chainDB.ttd
self.reinit( self.reinit(
parent = parent, parent = parent,
timestamp = header.timestamp, timestamp = header.timestamp,
@ -201,6 +216,7 @@ proc reinit*(self: BaseVMState; ## Object descriptor
fee = header.fee, fee = header.fee,
random = header.random, random = header.random,
miner = self.chainDB.getMinerAddress(header), miner = self.chainDB.getMinerAddress(header),
ttdReached= ttdReached,
pruneTrie = pruneTrie) pruneTrie = pruneTrie)
proc reinit*(self: BaseVMState; ## Object descriptor proc reinit*(self: BaseVMState; ## Object descriptor
@ -231,6 +247,7 @@ proc init*(
## ##
## It requires the `header` argument properly initalised so that for PoA ## It requires the `header` argument properly initalised so that for PoA
## networks, the miner address is retrievable via `ecRecover()`. ## networks, the miner address is retrievable via `ecRecover()`.
let ttdReached = chainDB.totalDifficulty + header.difficulty > chainDB.ttd
self.init(AccountsCache.init(chainDB.db, parent.stateRoot, pruneTrie), self.init(AccountsCache.init(chainDB.db, parent.stateRoot, pruneTrie),
parent, parent,
header.timestamp, header.timestamp,
@ -239,6 +256,7 @@ proc init*(
header.random, header.random,
chainDB.getMinerAddress(header), chainDB.getMinerAddress(header),
chainDB, chainDB,
ttdReached,
tracerFlags) tracerFlags)
proc new*( proc new*(
@ -308,9 +326,9 @@ method blockNumber*(vmState: BaseVMState): BlockNumber {.base, gcsafe.} =
vmState.parent.blockNumber + 1 vmState.parent.blockNumber + 1
method difficulty*(vmState: BaseVMState): UInt256 {.base, gcsafe.} = method difficulty*(vmState: BaseVMState): UInt256 {.base, gcsafe.} =
if vmState.fork >= FkPostMerge: if vmState.ttdReached:
# EIP-4399/EIP-3675 # EIP-4399/EIP-3675
0.u256 UInt256.fromBytesBE(vmState.random.data, allowPadding = false)
else: else:
vmState.chainDB.config.calcDifficulty(vmState.timestamp, vmState.parent) vmState.chainDB.config.calcDifficulty(vmState.timestamp, vmState.parent)

View File

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

View File

@ -58,9 +58,6 @@ template getBlockNumber*(c: Computation): Uint256 =
template getDifficulty*(c: Computation): DifficultyInt = template getDifficulty*(c: Computation): DifficultyInt =
c.vmState.difficulty c.vmState.difficulty
template getRandom*(c: Computation): Hash256 =
c.vmState.random
template getGasLimit*(c: Computation): GasInt = template getGasLimit*(c: Computation): GasInt =
c.vmState.gasLimit c.vmState.gasLimit

View File

@ -730,11 +730,9 @@ const
FkPetersburg: SpuriousGasFees, FkPetersburg: SpuriousGasFees,
FkIstanbul: IstanbulGasFees, FkIstanbul: IstanbulGasFees,
FkBerlin: BerlinGasFees, FkBerlin: BerlinGasFees,
FkLondon: LondonGasFees, FkLondon: LondonGasFees
FkPostMerge: LondonGasFees
] ]
gasCosts(FkFrontier, base, BaseGasCosts) gasCosts(FkFrontier, base, BaseGasCosts)
gasCosts(FkHomestead, homestead, HomesteadGasCosts) gasCosts(FkHomestead, homestead, HomesteadGasCosts)
gasCosts(FkTangerine, tangerine, TangerineGasCosts) gasCosts(FkTangerine, tangerine, TangerineGasCosts)
@ -743,7 +741,6 @@ gasCosts(FkConstantinople, constantinople, ConstantinopleGasCosts)
gasCosts(FkIstanbul, istanbul, IstanbulGasCosts) gasCosts(FkIstanbul, istanbul, IstanbulGasCosts)
gasCosts(FkBerlin, berlin, BerlinGasCosts) gasCosts(FkBerlin, berlin, BerlinGasCosts)
gasCosts(FkLondon, london, LondonGasCosts) gasCosts(FkLondon, london, LondonGasCosts)
gasCosts(FkPostMerge, postMerge, PostMergeGasCosts)
proc forkToSchedule*(fork: Fork): GasCosts = proc forkToSchedule*(fork: Fork): GasCosts =
if fork < FkHomestead: if fork < FkHomestead:
@ -760,10 +757,8 @@ proc forkToSchedule*(fork: Fork): GasCosts =
IstanbulGasCosts IstanbulGasCosts
elif fork < FkLondon: elif fork < FkLondon:
BerlinGasCosts BerlinGasCosts
elif fork < FkPostMerge:
LondonGasCosts
else: else:
PostMergeGasCosts LondonGasCosts
const const
## Precompile costs ## Precompile costs

View File

@ -53,11 +53,6 @@ const
k.cpt.stack.push: k.cpt.stack.push:
k.cpt.getDifficulty k.cpt.getDifficulty
randomOp: Vm2OpFn = proc (k: var Vm2Ctx) =
## 0x44, Get the block's randomness
k.cpt.stack.push:
k.cpt.getRandom
gasLimitOp: Vm2OpFn = proc (k: var Vm2Ctx) = gasLimitOp: Vm2OpFn = proc (k: var Vm2Ctx) =
## 0x45, Get the block's gas limit ## 0x45, Get the block's gas limit
k.cpt.stack.push: k.cpt.stack.push:
@ -118,7 +113,7 @@ const
post: vm2OpIgnore)), post: vm2OpIgnore)),
(opCode: Difficulty, ## 0x44, Block difficulty (opCode: Difficulty, ## 0x44, Block difficulty
forks: Vm2OpAllForks - Vm2OpPostMergeAndLater, forks: Vm2OpAllForks,
name: "difficulty", name: "difficulty",
info: "Get the block's difficulty", info: "Get the block's difficulty",
exec: (prep: vm2OpIgnore, exec: (prep: vm2OpIgnore,
@ -155,14 +150,6 @@ const
info: "Get current block's EIP-1559 base fee", info: "Get current block's EIP-1559 base fee",
exec: (prep: vm2OpIgnore, exec: (prep: vm2OpIgnore,
run: baseFeeOp, run: baseFeeOp,
post: vm2OpIgnore)),
(opCode: Random, ## 0x44, EIP-4399 Block randomness.
forks: Vm2OpPostMergeAndLater,
name: "random",
info: "Get current block's EIP-4399 randomness",
exec: (prep: vm2OpIgnore,
run: randomOp,
post: vm2OpIgnore))] post: vm2OpIgnore))]
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -81,9 +81,6 @@ const
Vm2OpLondonAndLater* = Vm2OpLondonAndLater* =
Vm2OpBerlinAndLater - {FkBerlin} Vm2OpBerlinAndLater - {FkBerlin}
Vm2OpPostMergeAndLater* =
Vm2OpLondonAndLater - {FkLondon}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# End # End
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -61,6 +61,7 @@ proc init(
random: Hash256; random: Hash256;
miner: EthAddress; miner: EthAddress;
chainDB: BaseChainDB; chainDB: BaseChainDB;
ttdReached: bool;
tracer: TransactionTracer) tracer: TransactionTracer)
{.gcsafe, raises: [Defect,CatchableError].} = {.gcsafe, raises: [Defect,CatchableError].} =
## Initialisation helper ## Initialisation helper
@ -72,6 +73,7 @@ proc init(
self.fee = fee self.fee = fee
self.random = random self.random = random
self.chaindb = chainDB self.chaindb = chainDB
self.ttdReached = ttdReached
self.tracer = tracer self.tracer = tracer
self.logEntries = @[] self.logEntries = @[]
self.stateDB = ac self.stateDB = ac
@ -88,6 +90,7 @@ proc init(
random: Hash256; random: Hash256;
miner: EthAddress; miner: EthAddress;
chainDB: BaseChainDB; chainDB: BaseChainDB;
ttdReached: bool;
tracerFlags: set[TracerFlags]) tracerFlags: set[TracerFlags])
{.gcsafe, raises: [Defect,CatchableError].} = {.gcsafe, raises: [Defect,CatchableError].} =
var tracer: TransactionTracer var tracer: TransactionTracer
@ -101,6 +104,7 @@ proc init(
random = random, random = random,
miner = miner, miner = miner,
chainDB = chainDB, chainDB = chainDB,
ttdReached= ttdReached,
tracer = tracer) tracer = tracer)
# -------------- # --------------
@ -123,6 +127,7 @@ proc new*(
random: Hash256; ## tx env: POS block randomness random: Hash256; ## tx env: POS block randomness
miner: EthAddress; ## tx env: coinbase(PoW) or signer(PoA) miner: EthAddress; ## tx env: coinbase(PoW) or signer(PoA)
chainDB: BaseChainDB; ## block chain database chainDB: BaseChainDB; ## block chain database
ttdReached: bool; ## total terminal difficulty reached
tracerFlags: set[TracerFlags] = {}; tracerFlags: set[TracerFlags] = {};
pruneTrie: bool = true): T pruneTrie: bool = true): T
{.gcsafe, raises: [Defect,CatchableError].} = {.gcsafe, raises: [Defect,CatchableError].} =
@ -143,6 +148,7 @@ proc new*(
random = random, random = random,
miner = miner, miner = miner,
chainDB = chainDB, chainDB = chainDB,
ttdReached = ttdReached,
tracerFlags = tracerFlags) tracerFlags = tracerFlags)
proc reinit*(self: BaseVMState; ## Object descriptor proc reinit*(self: BaseVMState; ## Object descriptor
@ -152,6 +158,7 @@ proc reinit*(self: BaseVMState; ## Object descriptor
fee: Option[Uint256]; ## tx env: optional base fee fee: Option[Uint256]; ## tx env: optional base fee
random: Hash256; ## tx env: POS block randomness random: Hash256; ## tx env: POS block randomness
miner: EthAddress; ## tx env: coinbase(PoW) or signer(PoA) miner: EthAddress; ## tx env: coinbase(PoW) or signer(PoA)
ttdReached:bool; ## total terminal difficulty reached
pruneTrie: bool = true): bool pruneTrie: bool = true): bool
{.gcsafe, raises: [Defect,CatchableError].} = {.gcsafe, raises: [Defect,CatchableError].} =
## Re-initialise state descriptor. The `AccountsCache` database is ## Re-initialise state descriptor. The `AccountsCache` database is
@ -178,10 +185,17 @@ proc reinit*(self: BaseVMState; ## Object descriptor
random = random, random = random,
miner = miner, miner = miner,
chainDB = db, chainDB = db,
ttdReached = ttdReached,
tracer = tracer) tracer = tracer)
return true return true
# else: false # else: false
proc ttd(chainDB: BaseChainDB): DifficultyInt =
if chainDB.config.terminalTotalDifficulty.isSome:
chainDB.config.terminalTotalDifficulty.get()
else:
high(DifficultyInt)
proc reinit*(self: BaseVMState; ## Object descriptor proc reinit*(self: BaseVMState; ## Object descriptor
parent: BlockHeader; ## parent header, account sync pos. parent: BlockHeader; ## parent header, account sync pos.
header: BlockHeader; ## header with tx environment data fields header: BlockHeader; ## header with tx environment data fields
@ -193,6 +207,7 @@ proc reinit*(self: BaseVMState; ## Object descriptor
## ##
## It requires the `header` argument properly initalised so that for PoA ## It requires the `header` argument properly initalised so that for PoA
## networks, the miner address is retrievable via `ecRecover()`. ## networks, the miner address is retrievable via `ecRecover()`.
let ttdReached = self.chainDB.totalDifficulty + header.difficulty > self.chainDB.ttd
self.reinit( self.reinit(
parent = parent, parent = parent,
timestamp = header.timestamp, timestamp = header.timestamp,
@ -200,6 +215,7 @@ proc reinit*(self: BaseVMState; ## Object descriptor
fee = header.fee, fee = header.fee,
random = header.random, random = header.random,
miner = self.chainDB.getMinerAddress(header), miner = self.chainDB.getMinerAddress(header),
ttdReached= ttdReached,
pruneTrie = pruneTrie) pruneTrie = pruneTrie)
proc reinit*(self: BaseVMState; ## Object descriptor proc reinit*(self: BaseVMState; ## Object descriptor
@ -230,6 +246,7 @@ proc init*(
## ##
## It requires the `header` argument properly initalised so that for PoA ## It requires the `header` argument properly initalised so that for PoA
## networks, the miner address is retrievable via `ecRecover()`. ## networks, the miner address is retrievable via `ecRecover()`.
let ttdReached = chainDB.totalDifficulty + header.difficulty > chainDB.ttd
self.init(AccountsCache.init(chainDB.db, parent.stateRoot, pruneTrie), self.init(AccountsCache.init(chainDB.db, parent.stateRoot, pruneTrie),
parent, parent,
header.timestamp, header.timestamp,
@ -238,6 +255,7 @@ proc init*(
header.random, header.random,
chainDB.getMinerAddress(header), chainDB.getMinerAddress(header),
chainDB, chainDB,
ttdReached,
tracerFlags) tracerFlags)
proc new*( proc new*(
@ -295,9 +313,9 @@ method blockNumber*(vmState: BaseVMState): BlockNumber {.base, gcsafe.} =
vmState.parent.blockNumber + 1 vmState.parent.blockNumber + 1
method difficulty*(vmState: BaseVMState): UInt256 {.base, gcsafe.} = method difficulty*(vmState: BaseVMState): UInt256 {.base, gcsafe.} =
if vmState.fork >= FkPostMerge: if vmState.ttdReached:
# EIP-4399/EIP-3675 # EIP-4399/EIP-3675
0.u256 UInt256.fromBytesBE(vmState.random.data, allowPadding = false)
else: else:
vmState.chainDB.config.calcDifficulty(vmState.timestamp, vmState.parent) vmState.chainDB.config.calcDifficulty(vmState.timestamp, vmState.parent)

View File

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

View File

@ -324,9 +324,7 @@ proc opEnvMain*() =
fork: berlin fork: berlin
gasused: 2603 gasused: 2603
when not defined(evmc_enabled): vmState.ttdReached = true
# TODO: enable this test for EVMC when EVMC
# support this EVM version
assembler: assembler:
title: "EIP-4399 RANDOM 0" title: "EIP-4399 RANDOM 0"
code: code:
@ -334,7 +332,7 @@ proc opEnvMain*() =
STOP STOP
stack: stack:
"0x0000000000000000000000000000000000000000000000000000000000000000" "0x0000000000000000000000000000000000000000000000000000000000000000"
fork: postmerge fork: london
vmState.random = EMPTY_UNCLE_HASH vmState.random = EMPTY_UNCLE_HASH
assembler: assembler:
@ -344,7 +342,9 @@ proc opEnvMain*() =
STOP STOP
stack: stack:
"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
fork: postmerge fork: london
vmState.ttdReached = false
when isMainModule: when isMainModule:
opEnvMain() opEnvMain()