mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-26 10:55:41 +00:00
Merge pull request #516 from status-im/validate_block_witness
syncing with PoA chain
This commit is contained in:
commit
6ea488e037
@ -33,6 +33,7 @@ type
|
||||
trie: SecureHexaryTrie
|
||||
savePoint: SavePoint
|
||||
witnessCache: Table[EthAddress, WitnessData]
|
||||
isDirty: bool
|
||||
|
||||
ReadOnlyStateDB* = distinct AccountsCache
|
||||
|
||||
@ -46,7 +47,17 @@ type
|
||||
cache: Table[EthAddress, RefAccount]
|
||||
state: TransactionState
|
||||
|
||||
const emptyAcc = newAccount()
|
||||
const
|
||||
emptyAcc = newAccount()
|
||||
|
||||
resetFlags = {
|
||||
IsDirty,
|
||||
IsNew,
|
||||
IsTouched,
|
||||
IsClone,
|
||||
CodeChanged,
|
||||
StorageChanged
|
||||
}
|
||||
|
||||
proc beginSavepoint*(ac: var AccountsCache): SavePoint {.gcsafe.}
|
||||
|
||||
@ -66,7 +77,7 @@ proc rootHash*(ac: AccountsCache): KeccakHash =
|
||||
# make sure all savepoint already committed
|
||||
doAssert(ac.savePoint.parentSavePoint.isNil)
|
||||
# make sure all cache already committed
|
||||
doAssert(ac.savePoint.cache.len == 0)
|
||||
doAssert(ac.isDirty == false)
|
||||
ac.trie.rootHash
|
||||
|
||||
proc beginSavepoint*(ac: var AccountsCache): SavePoint =
|
||||
@ -222,14 +233,20 @@ proc persistMode(acc: RefAccount): PersistMode =
|
||||
|
||||
proc persistCode(acc: RefAccount, db: TrieDatabaseRef) =
|
||||
if acc.code.len != 0:
|
||||
db.put(contractHashKey(acc.account.codeHash).toOpenArray, acc.code)
|
||||
when defined(geth):
|
||||
db.put(acc.account.codeHash.data, acc.code)
|
||||
else:
|
||||
db.put(contractHashKey(acc.account.codeHash).toOpenArray, acc.code)
|
||||
|
||||
proc persistStorage(acc: RefAccount, db: TrieDatabaseRef) =
|
||||
proc persistStorage(acc: RefAccount, db: TrieDatabaseRef, clearCache: bool) =
|
||||
if acc.overlayStorage.len == 0:
|
||||
# TODO: remove the storage too if we figure out
|
||||
# how to create 'virtual' storage room for each account
|
||||
return
|
||||
|
||||
if not clearCache and acc.originalStorage.isNil:
|
||||
acc.originalStorage = newTable[UInt256, UInt256]()
|
||||
|
||||
var accountTrie = getAccountTrie(db, acc)
|
||||
|
||||
for slot, value in acc.overlayStorage:
|
||||
@ -246,9 +263,21 @@ proc persistStorage(acc: RefAccount, db: TrieDatabaseRef) =
|
||||
# slotHash can be obtained from accountTrie.put?
|
||||
let slotHash = keccakHash(slotAsKey)
|
||||
db.put(slotHashToSlotKey(slotHash.data).toOpenArray, rlp.encode(slot))
|
||||
|
||||
if not clearCache:
|
||||
# if we preserve cache, move the overlayStorage
|
||||
# to originalStorage, related to EIP2200, EIP1283
|
||||
for slot, value in acc.overlayStorage:
|
||||
if value > 0:
|
||||
acc.originalStorage[slot] = value
|
||||
else:
|
||||
acc.originalStorage.del(slot)
|
||||
acc.overlayStorage.clear()
|
||||
|
||||
acc.account.storageRoot = accountTrie.rootHash
|
||||
|
||||
proc makeDirty(ac: AccountsCache, address: EthAddress, cloneStorage = true): RefAccount =
|
||||
ac.isDirty = true
|
||||
result = ac.getAccount(address)
|
||||
if address in ac.savePoint.cache:
|
||||
# it's already in latest savepoint
|
||||
@ -283,7 +312,11 @@ proc getCode*(ac: AccountsCache, address: EthAddress): seq[byte] =
|
||||
if CodeLoaded in acc.flags or CodeChanged in acc.flags:
|
||||
result = acc.code
|
||||
else:
|
||||
let data = ac.db.get(contractHashKey(acc.account.codeHash).toOpenArray)
|
||||
when defined(geth):
|
||||
let data = ac.db.get(acc.account.codeHash.data)
|
||||
else:
|
||||
let data = ac.db.get(contractHashKey(acc.account.codeHash).toOpenArray)
|
||||
|
||||
acc.code = data
|
||||
acc.flags.incl CodeLoaded
|
||||
result = acc.code
|
||||
@ -384,9 +417,11 @@ proc deleteAccount*(ac: var AccountsCache, address: EthAddress) =
|
||||
let acc = ac.getAccount(address)
|
||||
acc.kill()
|
||||
|
||||
proc persist*(ac: var AccountsCache) =
|
||||
proc persist*(ac: var AccountsCache, clearCache: bool = true) =
|
||||
# make sure all savepoint already committed
|
||||
doAssert(ac.savePoint.parentSavePoint.isNil)
|
||||
var cleanAccounts = initHashSet[EthAddress]()
|
||||
|
||||
for address, acc in ac.savePoint.cache:
|
||||
case acc.persistMode()
|
||||
of Update:
|
||||
@ -395,13 +430,24 @@ proc persist*(ac: var AccountsCache) =
|
||||
if StorageChanged in acc.flags:
|
||||
# storageRoot must be updated first
|
||||
# before persisting account into merkle trie
|
||||
acc.persistStorage(ac.db)
|
||||
acc.persistStorage(ac.db, clearCache)
|
||||
ac.trie.put address, rlp.encode(acc.account)
|
||||
of Remove:
|
||||
ac.trie.del address
|
||||
if not clearCache:
|
||||
#
|
||||
cleanAccounts.incl address
|
||||
of DoNothing:
|
||||
discard
|
||||
ac.savePoint.cache.clear()
|
||||
|
||||
acc.flags = acc.flags - resetFlags
|
||||
|
||||
if clearCache:
|
||||
ac.savePoint.cache.clear()
|
||||
else:
|
||||
for x in cleanAccounts:
|
||||
ac.savePoint.cache.del x
|
||||
ac.isDirty = false
|
||||
|
||||
iterator storage*(ac: AccountsCache, address: EthAddress): (UInt256, UInt256) =
|
||||
# beware that if the account not persisted,
|
||||
@ -411,10 +457,11 @@ iterator storage*(ac: AccountsCache, address: EthAddress): (UInt256, UInt256) =
|
||||
let storageRoot = acc.account.storageRoot
|
||||
var trie = initHexaryTrie(ac.db, storageRoot)
|
||||
|
||||
for slot, value in trie:
|
||||
if slot.len != 0:
|
||||
var keyData = ac.db.get(slotHashToSlotKey(slot).toOpenArray)
|
||||
yield (rlp.decode(keyData, UInt256), rlp.decode(value, UInt256))
|
||||
for slotHash, value in trie:
|
||||
if slotHash.len == 0: continue
|
||||
let keyData = ac.db.get(slotHashToSlotKey(slotHash).toOpenArray)
|
||||
if keyData.len == 0: continue
|
||||
yield (rlp.decode(keyData, UInt256), rlp.decode(value, UInt256))
|
||||
|
||||
proc getStorageRoot*(ac: AccountsCache, address: EthAddress): Hash256 =
|
||||
# beware that if the account not persisted,
|
||||
|
35
nimbus/db/geth_db.nim
Normal file
35
nimbus/db/geth_db.nim
Normal file
@ -0,0 +1,35 @@
|
||||
import eth/[rlp, common], db_chain, eth/trie/db
|
||||
|
||||
const
|
||||
headerPrefix = 'h'.byte # headerPrefix + num (uint64 big endian) + hash -> header
|
||||
headerHashSuffix = 'n'.byte # headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash
|
||||
blockBodyPrefix = 'b'.byte # blockBodyPrefix + num (uint64 big endian) + hash -> block body
|
||||
|
||||
proc headerHash*(db: BaseChainDB, number: uint64): Hash256 =
|
||||
var key: array[10, byte]
|
||||
key[0] = headerPrefix
|
||||
key[1..8] = toBytesBE(number)[0..^1]
|
||||
key[^1] = headerHashSuffix
|
||||
let res = db.db.get(key)
|
||||
doAssert(res.len == 32)
|
||||
result.data[0..31] = res[0..31]
|
||||
|
||||
proc blockHeader*(db: BaseChainDB, hash: Hash256, number: uint64): BlockHeader =
|
||||
var key: array[41, byte]
|
||||
key[0] = headerPrefix
|
||||
key[1..8] = toBytesBE(number)[0..^1]
|
||||
key[9..40] = hash.data[0..^1]
|
||||
let res = db.db.get(key)
|
||||
result = rlp.decode(res, BlockHeader)
|
||||
|
||||
proc blockHeader*(db: BaseChainDB, number: uint64): BlockHeader =
|
||||
let hash = db.headerHash(number)
|
||||
db.blockHeader(hash, number)
|
||||
|
||||
proc blockBody*(db: BaseChainDB, hash: Hash256, number: uint64): BlockBody =
|
||||
var key: array[41, byte]
|
||||
key[0] = blockBodyPrefix
|
||||
key[1..8] = toBytesBE(number)[0..^1]
|
||||
key[9..40] = hash.data[0..^1]
|
||||
let res = db.db.get(key)
|
||||
result = rlp.decode(res, BlockBody)
|
@ -36,7 +36,7 @@ method getAncestorHeader*(c: Chain, h: BlockHeader, output: var BlockHeader, ski
|
||||
method getBlockBody*(c: Chain, blockHash: KeccakHash): BlockBodyRef =
|
||||
result = nil
|
||||
|
||||
method persistBlocks*(c: Chain, headers: openarray[BlockHeader], bodies: openarray[BlockBody]): ValidationResult =
|
||||
method persistBlocks*(c: Chain, headers: openarray[BlockHeader], bodies: openarray[BlockBody]): ValidationResult {.gcsafe.} =
|
||||
# Run the VM here
|
||||
if headers.len != bodies.len:
|
||||
debug "Number of headers not matching number of bodies"
|
||||
|
@ -26,16 +26,18 @@ proc processTransaction*(tx: Transaction, sender: EthAddress, vmState: BaseVMSta
|
||||
|
||||
vmState.cumulativeGasUsed += result
|
||||
|
||||
let miner = vmState.coinbase()
|
||||
|
||||
vmState.mutateStateDB:
|
||||
# miner fee
|
||||
let txFee = result.u256 * tx.gasPrice.u256
|
||||
db.addBalance(vmState.blockHeader.coinbase, txFee)
|
||||
db.addBalance(miner, txFee)
|
||||
|
||||
for deletedAccount in vmState.suicides:
|
||||
db.deleteAccount deletedAccount
|
||||
|
||||
if fork >= FkSpurious:
|
||||
vmState.touchedAccounts.incl(vmState.blockHeader.coinbase)
|
||||
vmState.touchedAccounts.incl(miner)
|
||||
# EIP158/161 state clearing
|
||||
for account in vmState.touchedAccounts:
|
||||
if db.accountExists(account) and db.isEmptyAccount(account):
|
||||
@ -44,7 +46,7 @@ proc processTransaction*(tx: Transaction, sender: EthAddress, vmState: BaseVMSta
|
||||
|
||||
if vmState.generateWitness:
|
||||
vmState.accountDb.collectWitnessData()
|
||||
vmState.accountDb.persist()
|
||||
vmState.accountDb.persist(clearCache = false)
|
||||
|
||||
type
|
||||
# TODO: these types need to be removed
|
||||
@ -93,6 +95,25 @@ const
|
||||
eth2 # FkIstanbul
|
||||
]
|
||||
|
||||
proc calculateReward(fork: Fork, header: BlockHeader, body: BlockBody, vmState: BaseVMState) =
|
||||
# PoA consensus engine have no reward for miner
|
||||
if vmState.consensusEnginePoA: return
|
||||
|
||||
let blockReward = blockRewards[fork]
|
||||
var mainReward = blockReward
|
||||
|
||||
for uncle in body.uncles:
|
||||
var uncleReward = uncle.blockNumber.u256 + 8.u256
|
||||
uncleReward -= header.blockNumber.u256
|
||||
uncleReward = uncleReward * blockReward
|
||||
uncleReward = uncleReward div 8.u256
|
||||
vmState.mutateStateDB:
|
||||
db.addBalance(uncle.coinbase, uncleReward)
|
||||
mainReward += blockReward div 32.u256
|
||||
|
||||
vmState.mutateStateDB:
|
||||
db.addBalance(header.coinbase, mainReward)
|
||||
|
||||
proc processBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, vmState: BaseVMState): ValidationResult =
|
||||
var dbTx = chainDB.db.beginTransaction()
|
||||
defer: dbTx.dispose()
|
||||
@ -125,32 +146,26 @@ proc processBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, v
|
||||
return ValidationResult.Error
|
||||
vmState.receipts[txIndex] = makeReceipt(vmState, fork)
|
||||
|
||||
let blockReward = blockRewards[fork]
|
||||
var mainReward = blockReward
|
||||
if header.ommersHash != EMPTY_UNCLE_HASH:
|
||||
let h = chainDB.persistUncles(body.uncles)
|
||||
if h != header.ommersHash:
|
||||
debug "Uncle hash mismatch"
|
||||
return ValidationResult.Error
|
||||
for uncle in body.uncles:
|
||||
var uncleReward = uncle.blockNumber.u256 + 8.u256
|
||||
uncleReward -= header.blockNumber.u256
|
||||
uncleReward = uncleReward * blockReward
|
||||
uncleReward = uncleReward div 8.u256
|
||||
vmState.mutateStateDB:
|
||||
db.addBalance(uncle.coinbase, uncleReward)
|
||||
mainReward += blockReward div 32.u256
|
||||
|
||||
calculateReward(fork, header, body, vmState)
|
||||
|
||||
# Reward beneficiary
|
||||
vmState.mutateStateDB:
|
||||
db.addBalance(header.coinbase, mainReward)
|
||||
if vmState.generateWitness:
|
||||
db.collectWitnessData()
|
||||
db.persist()
|
||||
db.persist(ClearCache in vmState.flags)
|
||||
|
||||
let stateDb = vmState.accountDb
|
||||
if header.stateRoot != stateDb.rootHash:
|
||||
error "Wrong state root in block", blockNumber=header.blockNumber, expected=header.stateRoot, actual=stateDb.rootHash, arrivedFrom=chainDB.getCanonicalHead().stateRoot
|
||||
when defined(geth):
|
||||
error "Wrong state root in block", blockNumber=header.blockNumber, expected=header.stateRoot, actual=stateDb.rootHash
|
||||
else:
|
||||
error "Wrong state root in block", blockNumber=header.blockNumber, expected=header.stateRoot, actual=stateDb.rootHash, arrivedFrom=chainDB.getCanonicalHead().stateRoot
|
||||
# this one is a show stopper until we are confident in our VM's
|
||||
# compatibility with the main chain
|
||||
return ValidationResult.Error
|
||||
|
@ -86,11 +86,10 @@ proc binarySearchGas(vmState: var BaseVMState, transaction: Transaction, sender:
|
||||
|
||||
proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
|
||||
func getAccountDb(header: BlockHeader): ReadOnlyStateDB =
|
||||
proc getAccountDb(header: BlockHeader): ReadOnlyStateDB =
|
||||
## Retrieves the account db from canonical head
|
||||
# TODO: header.stateRoot to prevStateRoot
|
||||
let vmState = newBaseVMState(header.stateRoot, header, chain)
|
||||
result = vmState.readOnlyStateDB()
|
||||
let ac = AccountsCache.init(chain.db, header.stateRoot, chain.pruneTrie)
|
||||
result = ReadOnlyStateDB(ac)
|
||||
|
||||
proc accountDbFromTag(tag: string, readOnly = true): ReadOnlyStateDB =
|
||||
result = getAccountDb(chain.headerFromTag(tag))
|
||||
|
@ -5,8 +5,15 @@ import
|
||||
chronicles, rpc/hexstrings, launcher,
|
||||
vm/interpreter/vm_forks, ./config
|
||||
|
||||
proc getParentHeader(self: BaseChainDB, header: BlockHeader): BlockHeader =
|
||||
self.getBlockHeader(header.parentHash)
|
||||
when defined(geth):
|
||||
import db/geth_db
|
||||
|
||||
proc getParentHeader(db: BaseChainDB, header: BlockHeader): BlockHeader =
|
||||
db.blockHeader(header.blockNumber.truncate(uint64) - 1)
|
||||
|
||||
else:
|
||||
proc getParentHeader(self: BaseChainDB, header: BlockHeader): BlockHeader =
|
||||
self.getBlockHeader(header.parentHash)
|
||||
|
||||
proc `%`(x: openArray[byte]): JsonNode =
|
||||
result = %toHex(x, false)
|
||||
@ -80,7 +87,7 @@ proc traceTransaction*(chainDB: BaseChainDB, header: BlockHeader,
|
||||
memoryDB = newMemoryDB()
|
||||
captureDB = newCaptureDB(chainDB.db, memoryDB)
|
||||
captureTrieDB = trieDB captureDB
|
||||
captureChainDB = newBaseChainDB(captureTrieDB, false) # prune or not prune?
|
||||
captureChainDB = newBaseChainDB(captureTrieDB, false, PublicNetWork(chainDB.config.chainId)) # prune or not prune?
|
||||
vmState = newBaseVMState(parent.stateRoot, header, captureChainDB, tracerFlags + {EnableAccount})
|
||||
|
||||
var stateDb = vmState.accountDb
|
||||
@ -96,7 +103,9 @@ proc traceTransaction*(chainDB: BaseChainDB, header: BlockHeader,
|
||||
stateDiff = %{"before": before, "after": after}
|
||||
beforeRoot: Hash256
|
||||
|
||||
let fork = chainDB.config.toFork(header.blockNumber)
|
||||
let
|
||||
fork = chainDB.config.toFork(header.blockNumber)
|
||||
miner = vmState.coinbase()
|
||||
|
||||
for idx, tx in body.transactions:
|
||||
let sender = tx.getSender
|
||||
@ -106,7 +115,7 @@ proc traceTransaction*(chainDB: BaseChainDB, header: BlockHeader,
|
||||
vmState.enableTracing()
|
||||
before.captureAccount(stateDb, sender, senderName)
|
||||
before.captureAccount(stateDb, recipient, recipientName)
|
||||
before.captureAccount(stateDb, header.coinbase, minerName)
|
||||
before.captureAccount(stateDb, miner, minerName)
|
||||
stateDb.persist()
|
||||
stateDiff["beforeRoot"] = %($stateDb.rootHash)
|
||||
beforeRoot = stateDb.rootHash
|
||||
@ -116,8 +125,8 @@ proc traceTransaction*(chainDB: BaseChainDB, header: BlockHeader,
|
||||
if idx == txIndex:
|
||||
after.captureAccount(stateDb, sender, senderName)
|
||||
after.captureAccount(stateDb, recipient, recipientName)
|
||||
after.captureAccount(stateDb, header.coinbase, minerName)
|
||||
vmState.removeTracedAccounts(sender, recipient, header.coinbase)
|
||||
after.captureAccount(stateDb, miner, minerName)
|
||||
vmState.removeTracedAccounts(sender, recipient, miner)
|
||||
stateDb.persist()
|
||||
stateDiff["afterRoot"] = %($stateDb.rootHash)
|
||||
break
|
||||
@ -146,9 +155,10 @@ proc dumpBlockState*(db: BaseChainDB, header: BlockHeader, body: BlockBody, dump
|
||||
memoryDB = newMemoryDB()
|
||||
captureDB = newCaptureDB(db.db, memoryDB)
|
||||
captureTrieDB = trieDB captureDB
|
||||
captureChainDB = newBaseChainDB(captureTrieDB, false)
|
||||
captureChainDB = newBaseChainDB(captureTrieDB, false, PublicNetWork(db.config.chainId))
|
||||
# we only need stack dump if we want to scan for internal transaction address
|
||||
vmState = newBaseVMState(parent.stateRoot, header, captureChainDB, {EnableTracing, DisableMemory, DisableStorage, EnableAccount})
|
||||
miner = vmState.coinbase()
|
||||
|
||||
var
|
||||
before = newJArray()
|
||||
@ -161,7 +171,7 @@ proc dumpBlockState*(db: BaseChainDB, header: BlockHeader, body: BlockBody, dump
|
||||
before.captureAccount(stateBefore, sender, senderName & $idx)
|
||||
before.captureAccount(stateBefore, recipient, recipientName & $idx)
|
||||
|
||||
before.captureAccount(stateBefore, header.coinbase, minerName)
|
||||
before.captureAccount(stateBefore, miner, minerName)
|
||||
|
||||
for idx, uncle in body.uncles:
|
||||
before.captureAccount(stateBefore, uncle.coinbase, uncleName & $idx)
|
||||
@ -177,8 +187,8 @@ proc dumpBlockState*(db: BaseChainDB, header: BlockHeader, body: BlockBody, dump
|
||||
after.captureAccount(stateAfter, recipient, recipientName & $idx)
|
||||
vmState.removeTracedAccounts(sender, recipient)
|
||||
|
||||
after.captureAccount(stateAfter, header.coinbase, minerName)
|
||||
vmState.removeTracedAccounts(header.coinbase)
|
||||
after.captureAccount(stateAfter, miner, minerName)
|
||||
vmState.removeTracedAccounts(miner)
|
||||
|
||||
for idx, uncle in body.uncles:
|
||||
after.captureAccount(stateAfter, uncle.coinbase, uncleName & $idx)
|
||||
@ -202,7 +212,7 @@ proc traceBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, tra
|
||||
memoryDB = newMemoryDB()
|
||||
captureDB = newCaptureDB(chainDB.db, memoryDB)
|
||||
captureTrieDB = trieDB captureDB
|
||||
captureChainDB = newBaseChainDB(captureTrieDB, false)
|
||||
captureChainDB = newBaseChainDB(captureTrieDB, false, PublicNetWork(chainDB.config.chainId))
|
||||
vmState = newBaseVMState(parent.stateRoot, header, captureChainDB, tracerFlags + {EnableTracing})
|
||||
|
||||
if header.txRoot == BLANK_ROOT_HASH: return newJNull()
|
||||
@ -236,7 +246,7 @@ proc dumpDebuggingMetaData*(chainDB: BaseChainDB, header: BlockHeader,
|
||||
memoryDB = newMemoryDB()
|
||||
captureDB = newCaptureDB(chainDB.db, memoryDB)
|
||||
captureTrieDB = trieDB captureDB
|
||||
captureChainDB = newBaseChainDB(captureTrieDB, false)
|
||||
captureChainDB = newBaseChainDB(captureTrieDB, false, PublicNetWork(chainDB.config.chainId))
|
||||
bloom = createBloom(vmState.receipts)
|
||||
|
||||
let blockSummary = %{
|
||||
|
@ -7,11 +7,11 @@
|
||||
|
||||
import
|
||||
macros, strformat, tables, sets, options,
|
||||
eth/common,
|
||||
vm/interpreter/[vm_forks, gas_costs],
|
||||
eth/[common, keys, rlp], nimcrypto/keccak,
|
||||
vm/interpreter/[vm_forks, gas_costs], ./errors,
|
||||
./constants, ./db/[db_chain, accounts_cache],
|
||||
./utils, json, vm_types, vm/transaction_tracer,
|
||||
./config
|
||||
./config, ../stateless/[multi_keys, witness_from_tree, witness_types]
|
||||
|
||||
proc newAccessLogs*: AccessLogs =
|
||||
AccessLogs(reads: initTable[string, string](), writes: initTable[string, string]())
|
||||
@ -26,6 +26,8 @@ proc `$`*(vmState: BaseVMState): string =
|
||||
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 = @[]
|
||||
@ -37,12 +39,20 @@ proc init*(self: BaseVMState, prevStateRoot: Hash256, header: BlockHeader,
|
||||
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
|
||||
@ -55,11 +65,63 @@ proc setupTxContext*(vmState: BaseVMState, origin: EthAddress, gasPrice: GasInt,
|
||||
vmState.chainDB.config.toFork(vmState.blockHeader.blockNumber)
|
||||
vmState.gasCosts = vmState.fork.forkToSchedule
|
||||
|
||||
proc consensusEnginePoA*(vmState: BaseVMState): bool =
|
||||
let chainId = PublicNetwork(vmState.chainDB.config.chainId)
|
||||
# PoA consensus engine have no reward for miner
|
||||
result = chainId in {GoerliNet, RinkebyNet, KovanNet}
|
||||
|
||||
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, headerHash)
|
||||
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.suicides.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.blockHeader.coinbase
|
||||
vmState.minerAddress
|
||||
|
||||
method timestamp*(vmState: BaseVMState): EthTime {.base, gcsafe.} =
|
||||
vmState.blockHeader.timestamp
|
||||
@ -75,6 +137,9 @@ method difficulty*(vmState: BaseVMState): UInt256 {.base, gcsafe.} =
|
||||
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:
|
||||
@ -82,7 +147,10 @@ method getAncestorHash*(vmState: BaseVMState, blockNumber: BlockNumber): Hash256
|
||||
if blockNumber >= vmState.blockHeader.blockNumber:
|
||||
return
|
||||
|
||||
result = vmState.chainDB.getBlockHash(blockNumber)
|
||||
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
|
||||
@ -143,3 +211,12 @@ proc generateWitness*(vmState: BaseVMState): bool {.inline.} =
|
||||
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)
|
||||
|
@ -19,6 +19,7 @@ type
|
||||
VMFlag* = enum
|
||||
ExecutionOK
|
||||
GenerateWitness
|
||||
ClearCache
|
||||
|
||||
BaseVMState* = ref object of RootObj
|
||||
prevHeaders* : seq[BlockHeader]
|
||||
@ -38,6 +39,7 @@ type
|
||||
txGasPrice* : GasInt
|
||||
gasCosts* : GasCosts
|
||||
fork* : Fork
|
||||
minerAddress* : EthAddress
|
||||
|
||||
AccessLogs* = ref object
|
||||
reads*: Table[string, string]
|
||||
|
@ -1,5 +1,5 @@
|
||||
import stint, os, parseopt, strutils
|
||||
from ../nimbus/config import getDefaultDataDir, ConfigStatus, processInteger
|
||||
from ../nimbus/config import getDefaultDataDir, ConfigStatus, processInteger, PublicNetwork
|
||||
|
||||
export ConfigStatus
|
||||
|
||||
@ -9,6 +9,7 @@ type
|
||||
head*: Uint256
|
||||
maxBlocks*: int
|
||||
numCommits*: int
|
||||
netId*: PublicNetwork
|
||||
|
||||
var premixConfig {.threadvar.}: PremixConfiguration
|
||||
|
||||
@ -23,6 +24,7 @@ proc initConfiguration(): PremixConfiguration =
|
||||
result.head = 0.u256
|
||||
result.maxBlocks = 0
|
||||
result.numCommits = 128
|
||||
result.netId = MainNet
|
||||
|
||||
proc getConfiguration*(): PremixConfiguration =
|
||||
if isNil(premixConfig):
|
||||
@ -36,6 +38,15 @@ proc processU256(val: string, o: var Uint256): ConfigStatus =
|
||||
o = parse(val, Uint256)
|
||||
result = Success
|
||||
|
||||
proc processNetId(val: string, o: var PublicNetwork): ConfigStatus =
|
||||
case val.toLowerAscii()
|
||||
of "main": o = MainNet
|
||||
of "morden": o = MordenNet
|
||||
of "ropsten": o = RopstenNet
|
||||
of "rinkeby": o = RinkebyNet
|
||||
of "goerli": o = GoerliNet
|
||||
of "kovan": o = KovanNet
|
||||
|
||||
template checkArgument(fun, o: untyped) =
|
||||
## Checks if arguments got processed successfully
|
||||
var res = (fun)(value, o)
|
||||
@ -72,6 +83,8 @@ proc processArguments*(msg: var string): ConfigStatus =
|
||||
of "numcommits":
|
||||
checkArgument processInteger, config.numCommits
|
||||
config.numCommits = max(config.numCommits, 512)
|
||||
of "netid":
|
||||
checkArgument processNetId, config.netId
|
||||
else:
|
||||
msg = "Unknown option " & key
|
||||
if value.len > 0: msg = msg & " : " & value
|
||||
|
@ -3,7 +3,7 @@
|
||||
import
|
||||
eth/[common, rlp], stint,
|
||||
chronicles, downloader, configuration,
|
||||
../nimbus/errors
|
||||
../nimbus/[errors, config]
|
||||
|
||||
import
|
||||
eth/trie/[hexary, db],
|
||||
@ -34,10 +34,10 @@ proc main() =
|
||||
# 52029 first block with receipts logs
|
||||
# 66407 failed transaction
|
||||
|
||||
let conf = getConfiguration()
|
||||
let conf = configuration.getConfiguration()
|
||||
let db = newChainDb(conf.dataDir)
|
||||
let trieDB = trieDB db
|
||||
let chainDB = newBaseChainDB(trieDB, false)
|
||||
let chainDB = newBaseChainDB(trieDB, false, conf.netId)
|
||||
|
||||
# move head to block number ...
|
||||
if conf.head != 0.u256:
|
||||
@ -93,7 +93,7 @@ when isMainModule:
|
||||
var message: string
|
||||
|
||||
## Processing command line arguments
|
||||
if processArguments(message) != Success:
|
||||
if configuration.processArguments(message) != Success:
|
||||
echo message
|
||||
quit(QuitFailure)
|
||||
else:
|
||||
|
@ -18,7 +18,7 @@ import
|
||||
../nimbus/utils/header,
|
||||
../nimbus/p2p/[executor, dao],
|
||||
../nimbus/config,
|
||||
../stateless/[multi_keys, tree_from_witness, witness_from_tree, witness_types]
|
||||
../stateless/[tree_from_witness, witness_types]
|
||||
|
||||
type
|
||||
SealEngine = enum
|
||||
@ -283,13 +283,9 @@ proc parseTester(fixture: JsonNode, testStatusIMPL: var TestStatus): Tester =
|
||||
|
||||
proc blockWitness(vmState: BaseVMState, fork: Fork, chainDB: BaseChainDB) =
|
||||
let rootHash = vmState.accountDb.rootHash
|
||||
let mkeys = vmState.accountDb.makeMultiKeys()
|
||||
let witness = vmState.buildWitness()
|
||||
let flags = if fork >= FKSpurious: {wfEIP170} else: {}
|
||||
|
||||
# build witness from tree
|
||||
var wb = initWitnessBuilder(chainDB.db, rootHash, flags)
|
||||
let witness = wb.buildWitness(mkeys)
|
||||
|
||||
# build tree from witness
|
||||
var db = newMemoryDB()
|
||||
when defined(useInputStream):
|
||||
|
Loading…
x
Reference in New Issue
Block a user