multi root state trie implementation

This commit is contained in:
andri lim 2019-11-28 17:02:11 +07:00 committed by zah
parent 5c96cf8e87
commit 1ffb992674
4 changed files with 43 additions and 17 deletions

View File

@ -17,20 +17,25 @@ type
AccountStateDB* = ref object
trie: SecureHexaryTrie
originalRoot: KeccakHash # will be updated for every transaction
transactionID: TransactionID
ReadOnlyStateDB* = distinct AccountStateDB
template trieDB(stateDB: AccountStateDB): TrieDatabaseRef =
HexaryTrie(stateDB.trie).db
proc rootHash*(db: AccountStateDB): KeccakHash =
db.trie.rootHash
proc `rootHash=`*(db: AccountStateDB, root: KeccakHash) =
db.trie = initSecureHexaryTrie(HexaryTrie(db.trie).db, root, db.trie.isPruning)
db.trie = initSecureHexaryTrie(trieDB(db), root, db.trie.isPruning)
proc newAccountStateDB*(backingStore: TrieDatabaseRef,
root: KeccakHash, pruneTrie: bool): AccountStateDB =
result.new()
result.trie = initSecureHexaryTrie(backingStore, root, pruneTrie)
result.originalRoot = root
result.transactionID = backingStore.getTransactionID()
template createRangeFromAddress(address: EthAddress): ByteRange =
## XXX: The name of this proc is intentionally long, because it
@ -80,13 +85,13 @@ template createTrieKeyFromSlot(slot: UInt256): ByteRange =
# pad32(int_to_big_endian(slot))
# morally equivalent to toByteRange_Unnecessary but with different types
template getAccountTrie(stateDb: AccountStateDB, account: Account): auto =
template getAccountTrie(db: AccountStateDB, account: Account): auto =
# TODO: implement `prefix-db` to solve issue #228 permanently.
# the `prefix-db` will automatically insert account address to the
# underlying-db key without disturb how the trie works.
# it will create virtual container for each account.
# see nim-eth#9
initSecureHexaryTrie(HexaryTrie(stateDb.trie).db, account.storageRoot, false)
initSecureHexaryTrie(trieDB(db), account.storageRoot, false)
# XXX: https://github.com/status-im/nimbus/issues/142#issuecomment-420583181
proc setStorageRoot*(db: var AccountStateDB, address: EthAddress, storageRoot: Hash256) =
@ -114,7 +119,7 @@ proc setStorage*(db: var AccountStateDB,
# map slothash back to slot value
# see iterator storage below
var
triedb = HexaryTrie(db.trie).db
triedb = trieDB(db)
# slotHash can be obtained from accountTrie.put?
slotHash = keccakHash(slot.toByteArrayBE)
triedb.put(slotHashToSlotKey(slotHash.data).toOpenArray, rlp.encode(slot))
@ -125,7 +130,7 @@ proc setStorage*(db: var AccountStateDB,
iterator storage*(db: AccountStateDB, address: EthAddress): (UInt256, UInt256) =
let
storageRoot = db.getStorageRoot(address)
triedb = HexaryTrie(db.trie).db
triedb = trieDB(db)
var trie = initHexaryTrie(triedb, storageRoot)
for key, value in trie:
@ -167,7 +172,7 @@ proc setCode*(db: AccountStateDB, address: EthAddress, code: ByteRange) =
let
newCodeHash = keccakHash(code.toOpenArray)
triedb = HexaryTrie(db.trie).db
triedb = trieDB(db)
if code.len != 0:
triedb.put(contractHashKey(newCodeHash).toOpenArray, code.toOpenArray)
@ -176,7 +181,7 @@ proc setCode*(db: AccountStateDB, address: EthAddress, code: ByteRange) =
db.setAccount(address, account)
proc getCode*(db: AccountStateDB, address: EthAddress): ByteRange =
let triedb = HexaryTrie(db.trie).db
let triedb = trieDB(db)
let data = triedb.get(contractHashKey(db.getCodeHash(address)).toOpenArray)
data.toRange
@ -213,12 +218,16 @@ proc getCommittedStorage*(db: AccountStateDB, address: EthAddress, slot: UInt256
let tmpHash = db.rootHash
db.rootHash = db.originalRoot
var exists: bool
(result, exists) = db.getStorage(address, slot)
shortTimeReadOnly(trieDB(db), db.transactionID):
(result, exists) = db.getStorage(address, slot)
db.rootHash = tmpHash
proc updateOriginalRoot*(db: AccountStateDB) =
## this proc will be called for every transaction
db.originalRoot = db.rootHash
# no need to rollback or dispose
# transactionID, it will be handled elsewhere
db.transactionID = trieDB(db).getTransactionID()
proc rootHash*(db: ReadOnlyStateDB): KeccakHash {.borrow.}
proc getAccount*(db: ReadOnlyStateDB, address: EthAddress): Account {.borrow.}

View File

@ -1,5 +1,5 @@
import options, sets,
eth/[common, bloom], stew/ranges, chronicles, nimcrypto,
eth/[common, bloom, trie/db], stew/ranges, chronicles, nimcrypto,
../db/[db_chain, state_db],
../utils, ../constants, ../transaction,
../vm_state, ../vm_types, ../vm_state_transactions,
@ -119,6 +119,9 @@ const
]
proc processBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, vmState: BaseVMState): ValidationResult =
var dbTx = chainDB.db.beginTransaction()
defer: dbTx.dispose()
if chainDB.config.daoForkSupport and header.blockNumber == chainDB.config.daoForkBlock:
vmState.mutateStateDB:
db.applyDAOHardFork()
@ -183,3 +186,8 @@ proc processBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, v
if header.receiptRoot != receiptRoot:
debug "wrong receiptRoot in block", blockNumber=header.blockNumber, actual=receiptRoot, expected=header.receiptRoot
return ValidationResult.Error
# `applyDeletes = false`
# preserve previous block stateRoot
# while still benefits from trie pruning
dbTx.commit(applyDeletes = false)

View File

@ -271,7 +271,10 @@ proc assignBlockRewards(minedBlock: PlainBlock, vmState: BaseVMState, fork: Fork
if minedBlock.header.txRoot != txRoot:
raise newException(ValidationError, "wrong txRoot")
proc processBlock(vmState: BaseVMState, minedBlock: PlainBlock, fork: Fork) =
proc processBlock(chainDB: BaseChainDB, vmState: BaseVMState, minedBlock: PlainBlock, fork: Fork) =
var dbTx = chainDB.db.beginTransaction()
defer: dbTx.dispose()
vmState.receipts = newSeq[Receipt](minedBlock.transactions.len)
vmState.cumulativeGasUsed = 0
@ -288,6 +291,11 @@ proc processBlock(vmState: BaseVMState, minedBlock: PlainBlock, fork: Fork) =
assignBlockRewards(minedBlock, vmState, fork, vmState.chainDB)
# `applyDeletes = false`
# preserve previous block stateRoot
# while still benefits from trie pruning
dbTx.commit(applyDeletes = false)
func validateBlockUnchanged(a, b: PlainBlock): bool =
result = rlp.encode(a) == rlp.encode(b)
@ -513,7 +521,8 @@ proc importBlock(tester: var Tester, chainDB: BaseChainDB,
let tracerFlags: set[TracerFlags] = if tester.trace: {TracerFlags.EnableTracing} else : {}
tester.vmState = newBaseVMState(parentHeader.stateRoot, baseHeaderForImport, chainDB, tracerFlags)
processBlock(tester.vmState, result, fork)
processBlock(chainDB, tester.vmState, result, fork)
result.header.stateRoot = tester.vmState.blockHeader.stateRoot
result.header.parentHash = parentHeader.hash
result.header.difficulty = baseHeaderForImport.difficulty
@ -626,9 +635,8 @@ proc testFixture(node: JsonNode, testStatusIMPL: var TestStatus, debugMode = fal
continue
var tester = parseTester(fixture, testStatusIMPL)
# TODO: implement journalDB in AccountStateDB
# then turn on state trie pruning
var chainDB = newBaseChainDB(newMemoryDb(), false)
# TODO: do we need another test with pruneTrie = false?
var chainDB = newBaseChainDB(newMemoryDb(), pruneTrie = false)
echo "TESTING: ", fixtureName
if not tester.good: continue

View File

@ -87,9 +87,10 @@ proc dumpDebugData(tester: Tester, vmState: BaseVMState, sender: EthAddress, gas
proc testFixtureIndexes(tester: Tester, testStatusIMPL: var TestStatus) =
var tracerFlags: set[TracerFlags] = if tester.trace: {TracerFlags.EnableTracing} else : {}
# TODO: implement journalDB in AccountStateDB
# then turn on state trie pruning
var vmState = newGST_VMState(emptyRlpHash, tester.header, newBaseChainDB(newMemoryDb(), false), tracerFlags)
# TODO: do we need another test with pruneTrie = false?
var chainDB = newBaseChainDB(newMemoryDb(), pruneTrie = true)
var vmState = newGST_VMState(emptyRlpHash, tester.header, chainDB, tracerFlags)
var gasUsed: GasInt
let sender = tester.tx.getSender()