mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-12 13:24:21 +00:00
775231eef1
The rationale in EIP-6[1] for changing names to `selfDestruct` applies to code as much as it does to specs. Also, Ethereum uses the new names consistently, so it's useful for our code to match the terms used in later EIP specs and testsuite entries. This change is straightforward, and is a prerequisite for patches to come that do things with the `selfDestruct` fields. [1] https://eips.ethereum.org/EIPS/eip-6 Hudson Jameson, "EIP-6: Renaming SUICIDE opcode," Ethereum Improvement Proposals, no. 6, November 2015. Signed-off-by: Jamie Lokier <jamie@shareable.org>
227 lines
7.6 KiB
Nim
227 lines
7.6 KiB
Nim
# Nimbus
|
|
# Copyright (c) 2018 Status Research & Development GmbH
|
|
# Licensed under either of
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
|
# http://opensource.org/licenses/MIT)
|
|
# at your option. This file may not be copied, modified, or distributed except
|
|
# according to those terms.
|
|
|
|
import
|
|
macros, strformat, tables, sets, options,
|
|
eth/[common, keys, rlp], nimcrypto/keccak,
|
|
./interpreter/[vm_forks, gas_costs], ../errors,
|
|
../constants, ../db/[db_chain, accounts_cache],
|
|
../utils, json, ./transaction_tracer, ./types,
|
|
../config, ../../stateless/[witness_from_tree, witness_types]
|
|
|
|
proc newAccessLogs*: AccessLogs =
|
|
AccessLogs(reads: initTable[string, string](), writes: initTable[string, string]())
|
|
|
|
proc update*[K, V](t: var Table[K, V], elements: Table[K, V]) =
|
|
for k, v in elements:
|
|
t[k] = v
|
|
|
|
proc `$`*(vmState: BaseVMState): string =
|
|
if vmState.isNil:
|
|
result = "nil"
|
|
else:
|
|
result = &"VMState {vmState.name}:\n header: {vmState.blockHeader}\n chaindb: {vmState.chaindb}"
|
|
|
|
proc getMinerAddress(vmState: BaseVMState): EthAddress
|
|
|
|
proc init*(self: BaseVMState, prevStateRoot: Hash256, header: BlockHeader,
|
|
chainDB: BaseChainDB, tracerFlags: set[TracerFlags] = {}) =
|
|
self.prevHeaders = @[]
|
|
self.name = "BaseVM"
|
|
self.accessLogs = newAccessLogs()
|
|
self.blockHeader = header
|
|
self.chaindb = chainDB
|
|
self.tracer.initTracer(tracerFlags)
|
|
self.logEntries = @[]
|
|
self.accountDb = AccountsCache.init(chainDB.db, prevStateRoot, chainDB.pruneTrie)
|
|
self.touchedAccounts = initHashSet[EthAddress]()
|
|
{.gcsafe.}:
|
|
self.minerAddress = self.getMinerAddress()
|
|
|
|
proc newBaseVMState*(prevStateRoot: Hash256, header: BlockHeader,
|
|
chainDB: BaseChainDB, tracerFlags: set[TracerFlags] = {}): BaseVMState =
|
|
new result
|
|
result.init(prevStateRoot, header, chainDB, tracerFlags)
|
|
|
|
proc newBaseVMState*(prevStateRoot: Hash256,
|
|
chainDB: BaseChainDB, tracerFlags: set[TracerFlags] = {}): BaseVMState =
|
|
new result
|
|
var header: BlockHeader
|
|
result.init(prevStateRoot, header, chainDB, tracerFlags)
|
|
|
|
proc setupTxContext*(vmState: BaseVMState, origin: EthAddress, gasPrice: GasInt, forkOverride=none(Fork)) =
|
|
## this proc will be called each time a new transaction
|
|
## is going to be executed
|
|
vmState.txOrigin = origin
|
|
vmState.txGasPrice = gasPrice
|
|
vmState.fork =
|
|
if forkOverride.isSome:
|
|
forkOverride.get
|
|
else:
|
|
vmState.chainDB.config.toFork(vmState.blockHeader.blockNumber)
|
|
vmState.gasCosts = vmState.fork.forkToSchedule
|
|
|
|
proc consensusEnginePoA*(vmState: BaseVMState): bool =
|
|
# PoA consensus engine have no reward for miner
|
|
# TODO: this need to be fixed somehow
|
|
# using `real` engine configuration
|
|
vmState.chainDB.config.poaEngine
|
|
|
|
proc getSignature(bytes: openArray[byte], output: var Signature): bool =
|
|
let sig = Signature.fromRaw(bytes)
|
|
if sig.isOk:
|
|
output = sig[]
|
|
return true
|
|
return false
|
|
|
|
proc headerHashOriExtraData(vmState: BaseVMState): Hash256 =
|
|
var tmp = vmState.blockHeader
|
|
tmp.extraData.setLen(tmp.extraData.len-65)
|
|
result = keccak256.digest(rlp.encode(tmp))
|
|
|
|
proc calcMinerAddress(sigRaw: openArray[byte], vmState: BaseVMState, output: var EthAddress): bool =
|
|
var sig: Signature
|
|
if sigRaw.getSignature(sig):
|
|
let headerHash = headerHashOriExtraData(vmState)
|
|
let pubkey = recover(sig, SKMessage(headerHash.data))
|
|
if pubkey.isOk:
|
|
output = pubkey[].toCanonicalAddress()
|
|
result = true
|
|
|
|
proc getMinerAddress(vmState: BaseVMState): EthAddress =
|
|
if not vmState.consensusEnginePoA:
|
|
return vmState.blockHeader.coinbase
|
|
|
|
template data: untyped =
|
|
vmState.blockHeader.extraData
|
|
|
|
let len = data.len
|
|
doAssert(len >= 65)
|
|
|
|
var miner: EthAddress
|
|
if calcMinerAddress(data.toOpenArray(len - 65, len-1), vmState, miner):
|
|
result = miner
|
|
else:
|
|
raise newException(ValidationError, "Could not derive miner address from header extradata")
|
|
|
|
proc updateBlockHeader*(vmState: BaseVMState, header: BlockHeader) =
|
|
vmState.blockHeader = header
|
|
vmState.touchedAccounts.clear()
|
|
vmState.selfDestructs.clear()
|
|
if EnableTracing in vmState.tracer.flags:
|
|
vmState.tracer.initTracer(vmState.tracer.flags)
|
|
vmState.logEntries = @[]
|
|
vmState.receipts = @[]
|
|
vmState.minerAddress = vmState.getMinerAddress()
|
|
|
|
method blockhash*(vmState: BaseVMState): Hash256 {.base, gcsafe.} =
|
|
vmState.blockHeader.hash
|
|
|
|
method coinbase*(vmState: BaseVMState): EthAddress {.base, gcsafe.} =
|
|
vmState.minerAddress
|
|
|
|
method timestamp*(vmState: BaseVMState): EthTime {.base, gcsafe.} =
|
|
vmState.blockHeader.timestamp
|
|
|
|
method blockNumber*(vmState: BaseVMState): BlockNumber {.base, gcsafe.} =
|
|
# it should return current block number
|
|
# and not head.blockNumber
|
|
vmState.blockHeader.blockNumber
|
|
|
|
method difficulty*(vmState: BaseVMState): UInt256 {.base, gcsafe.} =
|
|
vmState.blockHeader.difficulty
|
|
|
|
method gasLimit*(vmState: BaseVMState): GasInt {.base, gcsafe.} =
|
|
vmState.blockHeader.gasLimit
|
|
|
|
when defined(geth):
|
|
import db/geth_db
|
|
|
|
method getAncestorHash*(vmState: BaseVMState, blockNumber: BlockNumber): Hash256 {.base, gcsafe.} =
|
|
var ancestorDepth = vmState.blockHeader.blockNumber - blockNumber - 1
|
|
if ancestorDepth >= constants.MAX_PREV_HEADER_DEPTH:
|
|
return
|
|
if blockNumber >= vmState.blockHeader.blockNumber:
|
|
return
|
|
|
|
when defined(geth):
|
|
result = vmState.chainDB.headerHash(blockNumber.truncate(uint64))
|
|
else:
|
|
result = vmState.chainDB.getBlockHash(blockNumber)
|
|
#TODO: should we use deque here?
|
|
# someday we may revive this code when
|
|
# we already have working miner
|
|
when false:
|
|
let idx = ancestorDepth.toInt
|
|
if idx >= vmState.prevHeaders.len:
|
|
return
|
|
|
|
var header = vmState.prevHeaders[idx]
|
|
result = header.hash
|
|
|
|
proc readOnlyStateDB*(vmState: BaseVMState): ReadOnlyStateDB {.inline.} =
|
|
ReadOnlyStateDB(vmState.accountDb)
|
|
|
|
template mutateStateDB*(vmState: BaseVMState, body: untyped) =
|
|
block:
|
|
var db {.inject.} = vmState.accountDb
|
|
body
|
|
|
|
proc getTracingResult*(vmState: BaseVMState): JsonNode {.inline.} =
|
|
doAssert(EnableTracing in vmState.tracer.flags)
|
|
vmState.tracer.trace
|
|
|
|
proc getAndClearLogEntries*(vmState: BaseVMState): seq[Log] =
|
|
shallowCopy(result, vmState.logEntries)
|
|
vmState.logEntries = @[]
|
|
|
|
proc enableTracing*(vmState: BaseVMState) {.inline.} =
|
|
vmState.tracer.flags.incl EnableTracing
|
|
|
|
proc disableTracing*(vmState: BaseVMState) {.inline.} =
|
|
vmState.tracer.flags.excl EnableTracing
|
|
|
|
iterator tracedAccounts*(vmState: BaseVMState): EthAddress =
|
|
for acc in vmState.tracer.accounts:
|
|
yield acc
|
|
|
|
iterator tracedAccountsPairs*(vmState: BaseVMState): (int, EthAddress) =
|
|
var idx = 0
|
|
for acc in vmState.tracer.accounts:
|
|
yield (idx, acc)
|
|
inc idx
|
|
|
|
proc removeTracedAccounts*(vmState: BaseVMState, accounts: varargs[EthAddress]) =
|
|
for acc in accounts:
|
|
vmState.tracer.accounts.excl acc
|
|
|
|
proc status*(vmState: BaseVMState): bool {.inline.} =
|
|
ExecutionOK in vmState.flags
|
|
|
|
proc `status=`*(vmState: BaseVMState, status: bool) =
|
|
if status: vmState.flags.incl ExecutionOK
|
|
else: vmState.flags.excl ExecutionOK
|
|
|
|
proc generateWitness*(vmState: BaseVMState): bool {.inline.} =
|
|
GenerateWitness in vmState.flags
|
|
|
|
proc `generateWitness=`*(vmState: BaseVMState, status: bool) =
|
|
if status: vmState.flags.incl GenerateWitness
|
|
else: vmState.flags.excl GenerateWitness
|
|
|
|
proc buildWitness*(vmState: BaseVMState): seq[byte] =
|
|
let rootHash = vmState.accountDb.rootHash
|
|
let mkeys = vmState.accountDb.makeMultiKeys()
|
|
let flags = if vmState.fork >= FKSpurious: {wfEIP170} else: {}
|
|
|
|
# build witness from tree
|
|
var wb = initWitnessBuilder(vmState.chainDB.db, rootHash, flags)
|
|
result = wb.buildWitness(mkeys)
|