Various fixes towards state reconstruction

This commit is contained in:
Yuriy Glukhov 2018-09-06 20:05:22 +03:00 committed by zah
parent f9034e95b5
commit 3a1ec035b3
13 changed files with 65 additions and 54 deletions

View File

@ -21,8 +21,12 @@ proc newChainDB*(basePath: string): ChainDB =
proc get*(db: ChainDB, key: openarray[byte]): seq[byte] = proc get*(db: ChainDB, key: openarray[byte]): seq[byte] =
let s = db.store.getBytes(key) let s = db.store.getBytes(key)
if not s.ok: raiseKeyReadError(key) if s.ok:
return s.value return s.value
elif s.error.len == 0:
discard
else:
raiseKeyReadError(key)
proc put*(db: ChainDB, key, value: openarray[byte]) = proc put*(db: ChainDB, key, value: openarray[byte]) =
let s = db.store.put(key, value) let s = db.store.put(key, value)

View File

@ -69,10 +69,12 @@ proc get*(db: ChainDB, key: openarray[byte]): seq[byte] =
var var
resStart = columnBlob(db.selectStmt, 0) resStart = columnBlob(db.selectStmt, 0)
resLen = columnBytes(db.selectStmt, 0) resLen = columnBytes(db.selectStmt, 0)
resSeq = newSeq[byte](resLen) result = newSeq[byte](resLen)
copyMem(resSeq.baseAddr, resStart, resLen) copyMem(result.baseAddr, resStart, resLen)
return resSeq of SQLITE_DONE:
else: raiseKeySearchError(key) discard
else:
raiseKeyReadError(key)
proc put*(db: ChainDB, key, value: openarray[byte]) = proc put*(db: ChainDB, key, value: openarray[byte]) =
template check(op) = template check(op) =

View File

@ -31,13 +31,10 @@ proc `$`*(db: BaseChainDB): string =
result = "BaseChainDB" result = "BaseChainDB"
proc getBlockHeader*(self: BaseChainDB; blockHash: Hash256, output: var BlockHeader): bool = proc getBlockHeader*(self: BaseChainDB; blockHash: Hash256, output: var BlockHeader): bool =
try: let data = self.db.get(genericHashKey(blockHash).toOpenArray).toRange
let blk = self.db.get(genericHashKey(blockHash).toOpenArray).toRange if data.len != 0:
if blk.len != 0: output = rlp.decode(data, BlockHeader)
output = rlp.decode(blk, BlockHeader)
result = true result = true
except KeyError:
discard
proc getBlockHeader*(self: BaseChainDB, blockHash: Hash256): BlockHeader = proc getBlockHeader*(self: BaseChainDB, blockHash: Hash256): BlockHeader =
## Returns the requested block header as specified by block hash. ## Returns the requested block header as specified by block hash.
@ -47,11 +44,10 @@ proc getBlockHeader*(self: BaseChainDB, blockHash: Hash256): BlockHeader =
raise newException(BlockNotFound, "No block with hash " & blockHash.data.toHex) raise newException(BlockNotFound, "No block with hash " & blockHash.data.toHex)
proc getHash(self: BaseChainDB, key: DbKey, output: var Hash256): bool {.inline.} = proc getHash(self: BaseChainDB, key: DbKey, output: var Hash256): bool {.inline.} =
try: let data = self.db.get(key.toOpenArray).toRange
output = rlp.decode(self.db.get(key.toOpenArray).toRange, Hash256) if data.len != 0:
output = rlp.decode(data, Hash256)
result = true result = true
except KeyError:
discard
proc getCanonicalHead*(self: BaseChainDB): BlockHeader = proc getCanonicalHead*(self: BaseChainDB): BlockHeader =
var headHash: Hash256 var headHash: Hash256
@ -145,13 +141,13 @@ proc setAsCanonicalChainHead(self: BaseChainDB; headerHash: Hash256): seq[BlockH
for h in newCanonicalHeaders: for h in newCanonicalHeaders:
self.addBlockNumberToHashLookup(h) self.addBlockNumberToHashLookup(h)
self.db.put(canonicalHeadHashKey().toOpenArray, rlp.encode(header.hash).toOpenArray) self.db.put(canonicalHeadHashKey().toOpenArray, rlp.encode(headerHash).toOpenArray)
return newCanonicalHeaders return newCanonicalHeaders
proc headerExists*(self: BaseChainDB; blockHash: Hash256): bool = proc headerExists*(self: BaseChainDB; blockHash: Hash256): bool =
## Returns True if the header with the given block hash is in our DB. ## Returns True if the header with the given block hash is in our DB.
self.db.contains(blockHash.data) self.db.contains(genericHashKey(blockHash).toOpenArray)
iterator getBlockTransactionData(self: BaseChainDB, transactionRoot: Hash256): BytesRange = iterator getBlockTransactionData(self: BaseChainDB, transactionRoot: Hash256): BytesRange =
var transactionDb = initHexaryTrie(self.db, transactionRoot) var transactionDb = initHexaryTrie(self.db, transactionRoot)
@ -183,14 +179,15 @@ iterator getBlockTransactions(self: BaseChainDB; transactionRoot: Hash256;
proc persistHeaderToDb*(self: BaseChainDB; header: BlockHeader): seq[BlockHeader] = proc persistHeaderToDb*(self: BaseChainDB; header: BlockHeader): seq[BlockHeader] =
let isGenesis = header.parentHash == GENESIS_PARENT_HASH let isGenesis = header.parentHash == GENESIS_PARENT_HASH
let headerHash = header.blockHash
if not isGenesis and not self.headerExists(header.parentHash): if not isGenesis and not self.headerExists(header.parentHash):
raise newException(ParentNotFound, "Cannot persist block header " & raise newException(ParentNotFound, "Cannot persist block header " &
$header.hash & " with unknown parent " & $header.parentHash) $headerHash & " with unknown parent " & $header.parentHash)
self.db.put(genericHashKey(header.hash).toOpenArray, rlp.encode(header).toOpenArray) self.db.put(genericHashKey(headerHash).toOpenArray, rlp.encode(header).toOpenArray)
let score = if isGenesis: header.difficulty let score = if isGenesis: header.difficulty
else: self.getScore(header.parentHash).u256 + header.difficulty else: self.getScore(header.parentHash).u256 + header.difficulty
self.db.put(blockHashToScoreKey(header.hash).toOpenArray, rlp.encode(score).toOpenArray) self.db.put(blockHashToScoreKey(headerHash).toOpenArray, rlp.encode(score).toOpenArray)
self.addBlockNumberToHashLookup(header) self.addBlockNumberToHashLookup(header)
@ -198,10 +195,10 @@ proc persistHeaderToDb*(self: BaseChainDB; header: BlockHeader): seq[BlockHeader
try: try:
headScore = self.getScore(self.getCanonicalHead().hash) headScore = self.getScore(self.getCanonicalHead().hash)
except CanonicalHeadNotFound: except CanonicalHeadNotFound:
return self.setAsCanonicalChainHead(header.hash) return self.setAsCanonicalChainHead(headerHash)
if score > headScore.u256: if score > headScore.u256:
result = self.setAsCanonicalChainHead(header.hash) result = self.setAsCanonicalChainHead(headerHash)
proc addTransactionToCanonicalChain(self: BaseChainDB, txHash: Hash256, proc addTransactionToCanonicalChain(self: BaseChainDB, txHash: Hash256,
blockHeader: BlockHeader, index: int) = blockHeader: BlockHeader, index: int) =

View File

@ -73,7 +73,7 @@ template raiseKeyWriteError*(key: auto) =
raise newException(StorageError, "failed to write key " & $key) raise newException(StorageError, "failed to write key " & $key)
template raiseKeySearchError*(key: auto) = template raiseKeySearchError*(key: auto) =
raise newException(KeyError, "failure during search for key " & $key) raise newException(StorageError, "failure during search for key " & $key)
template raiseKeyDeletionError*(key: auto) = template raiseKeyDeletionError*(key: auto) =
raise newException(StorageError, "failure to delete key " & $key) raise newException(StorageError, "failure to delete key " & $key)

View File

@ -14,7 +14,7 @@ import
config, genesis, rpc/[common, p2p], p2p/chain, config, genesis, rpc/[common, p2p], p2p/chain,
eth_trie eth_trie
const UseSqlite = true const UseSqlite = false
when UseSqlite: when UseSqlite:
import db/backends/sqlite_backend import db/backends/sqlite_backend

View File

@ -1,5 +1,5 @@
import ../db/db_chain, eth_common, chronicles, ../vm_state, ../vm_types, ../transaction, import ../db/[db_chain, state_db], eth_common, chronicles, ../vm_state, ../vm_types, ../transaction, ranges,
../vm/[computation, interpreter_dispatch, message] ../vm/[computation, interpreter_dispatch, message], ../constants
type type
@ -35,17 +35,28 @@ method persistBlocks*(c: Chain, headers: openarray[BlockHeader], bodies: openarr
assert(headers.len == bodies.len) assert(headers.len == bodies.len)
for i in 0 ..< headers.len: for i in 0 ..< headers.len:
echo "Persisting block: ", headers[i].blockNumber
if headers[i].txRoot != BLANK_ROOT_HASH:
let head = c.db.getCanonicalHead() let head = c.db.getCanonicalHead()
# assert(head.blockNumber == headers[i].blockNumber - 1) # assert(head.blockNumber == headers[i].blockNumber - 1)
let vmState = newBaseVMState(head, c.db) let vmState = newBaseVMState(head, c.db)
let stateDb = newAccountStateDB(c.db.db, head.stateRoot)
if bodies[i].transactions.len != 0: if bodies[i].transactions.len != 0:
# echo "block: ", headers[i].blockNumber # echo "block: ", headers[i].blockNumber
for t in bodies[i].transactions: for t in bodies[i].transactions:
var msg: Message var sender: EthAddress
# echo "trns: ", t if t.getSender(sender):
echo "Sender: ", sender
let code = stateDb.getCode(sender)
debug "Transaction", sender, to = t.to, value = t.value, hasCode = code.len != 0
let msg = newMessage(t.gasLimit, t.gasPrice, t.to, sender, t.value, t.payload, code.toSeq)
assert(false, "Dont know how to persist transactions")
# let msg = newMessage(t.gasLimit, t.gasPrice, t.to, t.getSender, if headers[i].ommersHash != EMPTY_UNCLE_HASH:
debug "Ignoring ommers", blockNumber = headers[i].blockNumber
discard c.db.persistHeaderToDb(headers[i])
assert(c.db.getCanonicalHead().blockHash == headers[i].blockHash)
# let c = newBaseComputation(vmState,
discard discard

View File

@ -49,7 +49,7 @@ proc toSignature*(transaction: Transaction): Signature =
bytes[0..31] = transaction.R.toByteArrayBE() bytes[0..31] = transaction.R.toByteArrayBE()
bytes[32..63] = transaction.S.toByteArrayBE() bytes[32..63] = transaction.S.toByteArrayBE()
# TODO: V will become a byte or range soon. # TODO: V will become a byte or range soon.
bytes[64] = transaction.V bytes[64] = transaction.V - 27 # TODO: 27 should come from somewhere
initSignature(bytes) initSignature(bytes)
proc getSender*(transaction: Transaction, output: var EthAddress): bool = proc getSender*(transaction: Transaction, output: var EthAddress): bool =

View File

@ -25,7 +25,7 @@ proc newBaseComputation*(vmState: BaseVMState, blockNumber: UInt256, message: Me
result.children = @[] result.children = @[]
result.accountsToDelete = initTable[EthAddress, EthAddress]() result.accountsToDelete = initTable[EthAddress, EthAddress]()
result.logEntries = @[] result.logEntries = @[]
result.code = newCodeStreamFromUnescaped(message.code) # TODO: what is the best repr result.code = newCodeStream(message.code)
# result.rawOutput = "0x" # result.rawOutput = "0x"
result.gasCosts = blockNumber.toFork.forkToSchedule result.gasCosts = blockNumber.toFork.forkToSchedule
@ -67,7 +67,7 @@ proc prepareChildMessage*(
to, to,
value, value,
data, data,
code.bytesToHex, # TODO: use seq[byte] for Message as well code,
childOptions) childOptions)
func output*(c: BaseComputation): seq[byte] = func output*(c: BaseComputation): seq[byte] =

View File

@ -39,7 +39,7 @@ proc newMessage*(
sender: EthAddress, sender: EthAddress,
value: UInt256, value: UInt256,
data: seq[byte], data: seq[byte],
code: string, code: seq[byte],
options: MessageOptions = newMessageOptions()): Message = options: MessageOptions = newMessageOptions()): Message =
validateGte(options.depth, minimum=0, title="Message.depth") validateGte(options.depth, minimum=0, title="Message.depth")

View File

@ -90,7 +90,7 @@ type
# Not in EVMC API # Not in EVMC API
# TODO: Done via callback function (v)table in EVMC # TODO: Done via callback function (v)table in EVMC
code*: string # TODO: seq[byte] is probably a better representation code*: seq[byte]
internalOrigin*: EthAddress internalOrigin*: EthAddress
internalCodeAddress*: EthAddress internalCodeAddress*: EthAddress

View File

@ -6,7 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
import import
unittest, tables, parseutils, unittest, tables, parseutils, byteutils,
eth_trie/[types, memdb], eth_common/eth_types, eth_trie/[types, memdb], eth_common/eth_types,
../nimbus/[constants, vm_types, vm_state], ../nimbus/[constants, vm_types, vm_state],
../nimbus/vm/interpreter, ../nimbus/vm/interpreter,
@ -32,7 +32,7 @@ proc testCode(code: string, initialGas: GasInt, blockNum: UInt256): BaseComputat
sender=ZERO_ADDRESS, #fixture{"exec"}{"caller"}.getStr, sender=ZERO_ADDRESS, #fixture{"exec"}{"caller"}.getStr,
value=0.u256, value=0.u256,
data = @[], data = @[],
code=code, code=code.hexToSeqByte,
gas=initial_gas, gas=initial_gas,
gasPrice=1) # What is this used for? gasPrice=1) # What is this used for?
# gasPrice=fixture{"exec"}{"gasPrice"}.getHexadecimalInt.u256, # gasPrice=fixture{"exec"}{"gasPrice"}.getHexadecimalInt.u256,

View File

@ -49,6 +49,8 @@ template backendTests(DB) =
keyA notin db keyA notin db
keyB in db keyB in db
check db.get(keyA) == @[]
check db.get(keyB) == value1 check db.get(keyB) == value1
db.del(keyA) db.del(keyA)

View File

@ -22,11 +22,6 @@ suite "vm json tests":
jsonTest("VMTests", testFixture) jsonTest("VMTests", testFixture)
proc stringFromBytes(x: ByteRange): string =
result = newString(x.len)
for i in 0 ..< x.len:
result[i] = char(x[i])
proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) = proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
var fixture: JsonNode var fixture: JsonNode
for label, child in fixtures: for label, child in fixtures:
@ -47,13 +42,13 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
var memDb = newMemDB() var memDb = newMemDB()
var vmState = newBaseVMState(header, newBaseChainDB(trieDB memDb)) var vmState = newBaseVMState(header, newBaseChainDB(trieDB memDb))
let fexec = fixture["exec"] let fexec = fixture["exec"]
var code = "" var code: seq[byte]
vmState.mutateStateDB: vmState.mutateStateDB:
setupStateDB(fixture{"pre"}, db) setupStateDB(fixture{"pre"}, db)
let address = fexec{"address"}.getStr.parseAddress let address = fexec{"address"}.getStr.parseAddress
code = stringFromBytes db.getCode(address) code = db.getCode(address).toSeq
code = fexec{"code"}.getStr code = fexec{"code"}.getStr.hexToSeqByte
let toAddress = fexec{"address"}.getStr.parseAddress let toAddress = fexec{"address"}.getStr.parseAddress
let message = newMessage( let message = newMessage(
to = toAddress, to = toAddress,
@ -67,7 +62,7 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
createAddress = toAddress)) createAddress = toAddress))
#echo fixture{"exec"} #echo fixture{"exec"}
var c = newCodeStreamFromUnescaped(code) var c = newCodeStream(code)
when defined(nimbusdebug): when defined(nimbusdebug):
c.displayDecompiled() c.displayDecompiled()