mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-03 15:55:47 +00:00
multi root state trie implementation
This commit is contained in:
parent
5c96cf8e87
commit
1ffb992674
@ -17,20 +17,25 @@ type
|
|||||||
AccountStateDB* = ref object
|
AccountStateDB* = ref object
|
||||||
trie: SecureHexaryTrie
|
trie: SecureHexaryTrie
|
||||||
originalRoot: KeccakHash # will be updated for every transaction
|
originalRoot: KeccakHash # will be updated for every transaction
|
||||||
|
transactionID: TransactionID
|
||||||
|
|
||||||
ReadOnlyStateDB* = distinct AccountStateDB
|
ReadOnlyStateDB* = distinct AccountStateDB
|
||||||
|
|
||||||
|
template trieDB(stateDB: AccountStateDB): TrieDatabaseRef =
|
||||||
|
HexaryTrie(stateDB.trie).db
|
||||||
|
|
||||||
proc rootHash*(db: AccountStateDB): KeccakHash =
|
proc rootHash*(db: AccountStateDB): KeccakHash =
|
||||||
db.trie.rootHash
|
db.trie.rootHash
|
||||||
|
|
||||||
proc `rootHash=`*(db: AccountStateDB, root: KeccakHash) =
|
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,
|
proc newAccountStateDB*(backingStore: TrieDatabaseRef,
|
||||||
root: KeccakHash, pruneTrie: bool): AccountStateDB =
|
root: KeccakHash, pruneTrie: bool): AccountStateDB =
|
||||||
result.new()
|
result.new()
|
||||||
result.trie = initSecureHexaryTrie(backingStore, root, pruneTrie)
|
result.trie = initSecureHexaryTrie(backingStore, root, pruneTrie)
|
||||||
result.originalRoot = root
|
result.originalRoot = root
|
||||||
|
result.transactionID = backingStore.getTransactionID()
|
||||||
|
|
||||||
template createRangeFromAddress(address: EthAddress): ByteRange =
|
template createRangeFromAddress(address: EthAddress): ByteRange =
|
||||||
## XXX: The name of this proc is intentionally long, because it
|
## 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))
|
# pad32(int_to_big_endian(slot))
|
||||||
# morally equivalent to toByteRange_Unnecessary but with different types
|
# 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.
|
# TODO: implement `prefix-db` to solve issue #228 permanently.
|
||||||
# the `prefix-db` will automatically insert account address to the
|
# the `prefix-db` will automatically insert account address to the
|
||||||
# underlying-db key without disturb how the trie works.
|
# underlying-db key without disturb how the trie works.
|
||||||
# it will create virtual container for each account.
|
# it will create virtual container for each account.
|
||||||
# see nim-eth#9
|
# 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
|
# XXX: https://github.com/status-im/nimbus/issues/142#issuecomment-420583181
|
||||||
proc setStorageRoot*(db: var AccountStateDB, address: EthAddress, storageRoot: Hash256) =
|
proc setStorageRoot*(db: var AccountStateDB, address: EthAddress, storageRoot: Hash256) =
|
||||||
@ -114,7 +119,7 @@ proc setStorage*(db: var AccountStateDB,
|
|||||||
# map slothash back to slot value
|
# map slothash back to slot value
|
||||||
# see iterator storage below
|
# see iterator storage below
|
||||||
var
|
var
|
||||||
triedb = HexaryTrie(db.trie).db
|
triedb = trieDB(db)
|
||||||
# slotHash can be obtained from accountTrie.put?
|
# slotHash can be obtained from accountTrie.put?
|
||||||
slotHash = keccakHash(slot.toByteArrayBE)
|
slotHash = keccakHash(slot.toByteArrayBE)
|
||||||
triedb.put(slotHashToSlotKey(slotHash.data).toOpenArray, rlp.encode(slot))
|
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) =
|
iterator storage*(db: AccountStateDB, address: EthAddress): (UInt256, UInt256) =
|
||||||
let
|
let
|
||||||
storageRoot = db.getStorageRoot(address)
|
storageRoot = db.getStorageRoot(address)
|
||||||
triedb = HexaryTrie(db.trie).db
|
triedb = trieDB(db)
|
||||||
var trie = initHexaryTrie(triedb, storageRoot)
|
var trie = initHexaryTrie(triedb, storageRoot)
|
||||||
|
|
||||||
for key, value in trie:
|
for key, value in trie:
|
||||||
@ -167,7 +172,7 @@ proc setCode*(db: AccountStateDB, address: EthAddress, code: ByteRange) =
|
|||||||
|
|
||||||
let
|
let
|
||||||
newCodeHash = keccakHash(code.toOpenArray)
|
newCodeHash = keccakHash(code.toOpenArray)
|
||||||
triedb = HexaryTrie(db.trie).db
|
triedb = trieDB(db)
|
||||||
|
|
||||||
if code.len != 0:
|
if code.len != 0:
|
||||||
triedb.put(contractHashKey(newCodeHash).toOpenArray, code.toOpenArray)
|
triedb.put(contractHashKey(newCodeHash).toOpenArray, code.toOpenArray)
|
||||||
@ -176,7 +181,7 @@ proc setCode*(db: AccountStateDB, address: EthAddress, code: ByteRange) =
|
|||||||
db.setAccount(address, account)
|
db.setAccount(address, account)
|
||||||
|
|
||||||
proc getCode*(db: AccountStateDB, address: EthAddress): ByteRange =
|
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)
|
let data = triedb.get(contractHashKey(db.getCodeHash(address)).toOpenArray)
|
||||||
data.toRange
|
data.toRange
|
||||||
|
|
||||||
@ -213,12 +218,16 @@ proc getCommittedStorage*(db: AccountStateDB, address: EthAddress, slot: UInt256
|
|||||||
let tmpHash = db.rootHash
|
let tmpHash = db.rootHash
|
||||||
db.rootHash = db.originalRoot
|
db.rootHash = db.originalRoot
|
||||||
var exists: bool
|
var exists: bool
|
||||||
|
shortTimeReadOnly(trieDB(db), db.transactionID):
|
||||||
(result, exists) = db.getStorage(address, slot)
|
(result, exists) = db.getStorage(address, slot)
|
||||||
db.rootHash = tmpHash
|
db.rootHash = tmpHash
|
||||||
|
|
||||||
proc updateOriginalRoot*(db: AccountStateDB) =
|
proc updateOriginalRoot*(db: AccountStateDB) =
|
||||||
## this proc will be called for every transaction
|
## this proc will be called for every transaction
|
||||||
db.originalRoot = db.rootHash
|
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 rootHash*(db: ReadOnlyStateDB): KeccakHash {.borrow.}
|
||||||
proc getAccount*(db: ReadOnlyStateDB, address: EthAddress): Account {.borrow.}
|
proc getAccount*(db: ReadOnlyStateDB, address: EthAddress): Account {.borrow.}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import options, sets,
|
import options, sets,
|
||||||
eth/[common, bloom], stew/ranges, chronicles, nimcrypto,
|
eth/[common, bloom, trie/db], stew/ranges, chronicles, nimcrypto,
|
||||||
../db/[db_chain, state_db],
|
../db/[db_chain, state_db],
|
||||||
../utils, ../constants, ../transaction,
|
../utils, ../constants, ../transaction,
|
||||||
../vm_state, ../vm_types, ../vm_state_transactions,
|
../vm_state, ../vm_types, ../vm_state_transactions,
|
||||||
@ -119,6 +119,9 @@ const
|
|||||||
]
|
]
|
||||||
|
|
||||||
proc processBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, vmState: BaseVMState): ValidationResult =
|
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:
|
if chainDB.config.daoForkSupport and header.blockNumber == chainDB.config.daoForkBlock:
|
||||||
vmState.mutateStateDB:
|
vmState.mutateStateDB:
|
||||||
db.applyDAOHardFork()
|
db.applyDAOHardFork()
|
||||||
@ -183,3 +186,8 @@ proc processBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, v
|
|||||||
if header.receiptRoot != receiptRoot:
|
if header.receiptRoot != receiptRoot:
|
||||||
debug "wrong receiptRoot in block", blockNumber=header.blockNumber, actual=receiptRoot, expected=header.receiptRoot
|
debug "wrong receiptRoot in block", blockNumber=header.blockNumber, actual=receiptRoot, expected=header.receiptRoot
|
||||||
return ValidationResult.Error
|
return ValidationResult.Error
|
||||||
|
|
||||||
|
# `applyDeletes = false`
|
||||||
|
# preserve previous block stateRoot
|
||||||
|
# while still benefits from trie pruning
|
||||||
|
dbTx.commit(applyDeletes = false)
|
||||||
|
@ -271,7 +271,10 @@ proc assignBlockRewards(minedBlock: PlainBlock, vmState: BaseVMState, fork: Fork
|
|||||||
if minedBlock.header.txRoot != txRoot:
|
if minedBlock.header.txRoot != txRoot:
|
||||||
raise newException(ValidationError, "wrong 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.receipts = newSeq[Receipt](minedBlock.transactions.len)
|
||||||
vmState.cumulativeGasUsed = 0
|
vmState.cumulativeGasUsed = 0
|
||||||
|
|
||||||
@ -288,6 +291,11 @@ proc processBlock(vmState: BaseVMState, minedBlock: PlainBlock, fork: Fork) =
|
|||||||
|
|
||||||
assignBlockRewards(minedBlock, vmState, fork, vmState.chainDB)
|
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 =
|
func validateBlockUnchanged(a, b: PlainBlock): bool =
|
||||||
result = rlp.encode(a) == rlp.encode(b)
|
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 : {}
|
let tracerFlags: set[TracerFlags] = if tester.trace: {TracerFlags.EnableTracing} else : {}
|
||||||
tester.vmState = newBaseVMState(parentHeader.stateRoot, baseHeaderForImport, chainDB, tracerFlags)
|
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.stateRoot = tester.vmState.blockHeader.stateRoot
|
||||||
result.header.parentHash = parentHeader.hash
|
result.header.parentHash = parentHeader.hash
|
||||||
result.header.difficulty = baseHeaderForImport.difficulty
|
result.header.difficulty = baseHeaderForImport.difficulty
|
||||||
@ -626,9 +635,8 @@ proc testFixture(node: JsonNode, testStatusIMPL: var TestStatus, debugMode = fal
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
var tester = parseTester(fixture, testStatusIMPL)
|
var tester = parseTester(fixture, testStatusIMPL)
|
||||||
# TODO: implement journalDB in AccountStateDB
|
# TODO: do we need another test with pruneTrie = false?
|
||||||
# then turn on state trie pruning
|
var chainDB = newBaseChainDB(newMemoryDb(), pruneTrie = false)
|
||||||
var chainDB = newBaseChainDB(newMemoryDb(), false)
|
|
||||||
|
|
||||||
echo "TESTING: ", fixtureName
|
echo "TESTING: ", fixtureName
|
||||||
if not tester.good: continue
|
if not tester.good: continue
|
||||||
|
@ -87,9 +87,10 @@ proc dumpDebugData(tester: Tester, vmState: BaseVMState, sender: EthAddress, gas
|
|||||||
|
|
||||||
proc testFixtureIndexes(tester: Tester, testStatusIMPL: var TestStatus) =
|
proc testFixtureIndexes(tester: Tester, testStatusIMPL: var TestStatus) =
|
||||||
var tracerFlags: set[TracerFlags] = if tester.trace: {TracerFlags.EnableTracing} else : {}
|
var tracerFlags: set[TracerFlags] = if tester.trace: {TracerFlags.EnableTracing} else : {}
|
||||||
# TODO: implement journalDB in AccountStateDB
|
|
||||||
# then turn on state trie pruning
|
# TODO: do we need another test with pruneTrie = false?
|
||||||
var vmState = newGST_VMState(emptyRlpHash, tester.header, newBaseChainDB(newMemoryDb(), false), tracerFlags)
|
var chainDB = newBaseChainDB(newMemoryDb(), pruneTrie = true)
|
||||||
|
var vmState = newGST_VMState(emptyRlpHash, tester.header, chainDB, tracerFlags)
|
||||||
var gasUsed: GasInt
|
var gasUsed: GasInt
|
||||||
let sender = tester.tx.getSender()
|
let sender = tester.tx.getSender()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user