Persist* functions
This commit is contained in:
parent
14a1b51981
commit
1c79d1ab3d
|
@ -6,15 +6,12 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
stint,
|
||||
./logging, ./constants,
|
||||
./utils/header
|
||||
stint, eth_common,
|
||||
./logging, ./constants
|
||||
|
||||
type
|
||||
CountableList*[T] = ref object
|
||||
elements: seq[T] # TODO
|
||||
|
||||
Block* = ref object of RootObj
|
||||
header*: BlockHeader
|
||||
uncles*: CountableList[BlockHeader]
|
||||
blockNumber*: UInt256
|
||||
uncles*: seq[BlockHeader]
|
||||
|
||||
proc blockNumber*(b: Block): BlockNumber {.inline.} = b.header.blockNumber
|
||||
|
|
|
@ -12,28 +12,36 @@ type
|
|||
genericHash
|
||||
blockNumberToHash
|
||||
blockHashToScore
|
||||
transactionHashToBlock
|
||||
canonicalHeadHash
|
||||
|
||||
DbKey* = object
|
||||
case kind: DBKeyKind
|
||||
of genericHash, blockHashToScore:
|
||||
of genericHash, blockHashToScore, transactionHashToBlock:
|
||||
h: Hash256
|
||||
of blockNumberToHash:
|
||||
u: BlockNumber
|
||||
of canonicalHeadHash:
|
||||
discard
|
||||
|
||||
MemoryDB* = ref object
|
||||
kvStore*: Table[DbKey, seq[byte]]
|
||||
|
||||
proc genericHashKey*(h: Hash256): DbKey {.inline.} = DbKey(kind: genericHash, h: h)
|
||||
proc blockHashToScoreKey*(h: Hash256): DbKey {.inline.} = DbKey(kind: blockHashToScore, h: h)
|
||||
proc transactionHashToBlockKey*(h: Hash256): DbKey {.inline.} = DbKey(kind: transactionHashToBlock, h: h)
|
||||
proc blockNumberToHashKey*(u: BlockNumber): DbKey {.inline.} = DbKey(kind: blockNumberToHash, u: u)
|
||||
proc canonicalHeadHashKey*(): DbKey {.inline.} = DbKey(kind: canonicalHeadHash)
|
||||
|
||||
proc hash(k: DbKey): Hash =
|
||||
result = result !& hash(k.kind)
|
||||
case k.kind
|
||||
of genericHash, blockHashToScore:
|
||||
of genericHash, blockHashToScore, transactionHashToBlock:
|
||||
result = result !& hash(k.h)
|
||||
of blockNumberToHash:
|
||||
result = result !& hashData(unsafeAddr k.u, sizeof(k.u))
|
||||
of canonicalHeadHash:
|
||||
discard
|
||||
result = result
|
||||
|
||||
proc `==`(a, b: DbKey): bool {.inline.} =
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
# * 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 stint, tables, rlp, ranges, state_db, backends / memory_backend,
|
||||
../errors, ../utils/header, ../constants, eth_common, byteutils
|
||||
import stint, tables, sequtils, algorithm, rlp, ranges, state_db, nimcrypto,
|
||||
backends / memory_backend,
|
||||
../errors, ../block_types, ../utils/header, ../constants, eth_common, byteutils
|
||||
|
||||
type
|
||||
BaseChainDB* = ref object
|
||||
|
@ -17,6 +18,10 @@ type
|
|||
blockNumberToHash
|
||||
blockHashToScore
|
||||
|
||||
TransactionKey = tuple
|
||||
blockNumber: BlockNumber
|
||||
index: int
|
||||
|
||||
proc newBaseChainDB*(db: MemoryDB): BaseChainDB =
|
||||
new(result)
|
||||
result.db = db
|
||||
|
@ -39,16 +44,19 @@ proc getBlockHeaderByHash*(self: BaseChainDB; blockHash: Hash256): BlockHeader =
|
|||
let rng = blk.toRange
|
||||
return decode(rng, BlockHeader)
|
||||
|
||||
# proc getCanonicalHead*(self: BaseChainDB): BlockHeader =
|
||||
# if notself.exists(CANONICALHEADHASHDBKEY):
|
||||
# raise newException(CanonicalHeadNotFound,
|
||||
# "No canonical head set for this chain")
|
||||
# return self.getBlockHeaderByHash(self.db.get(CANONICALHEADHASHDBKEY))
|
||||
proc getHash(self: BaseChainDB, key: DbKey): Hash256 {.inline.} =
|
||||
rlp.decode(self.db.get(key).toRange, Hash256)
|
||||
|
||||
proc getCanonicalHead*(self: BaseChainDB): BlockHeader =
|
||||
let k = canonicalHeadHashKey()
|
||||
if k notin self.db:
|
||||
raise newException(CanonicalHeadNotFound,
|
||||
"No canonical head set for this chain")
|
||||
return self.getBlockHeaderByHash(self.getHash(k))
|
||||
|
||||
proc lookupBlockHash*(self: BaseChainDB; n: BlockNumber): Hash256 {.inline.} =
|
||||
## Return the block hash for the given block number.
|
||||
let numberToHashKey = blockNumberToHashKey(n)
|
||||
result = rlp.decode(self.db.get(numberToHashKey).toRange, Hash256)
|
||||
self.getHash(blockNumberToHashKey(n))
|
||||
|
||||
proc getCanonicalBlockHeaderByNumber*(self: BaseChainDB; n: BlockNumber): BlockHeader =
|
||||
## Returns the block header with the given number in the canonical chain.
|
||||
|
@ -60,35 +68,86 @@ proc getCanonicalBlockHeaderByNumber*(self: BaseChainDB; n: BlockNumber): BlockH
|
|||
proc getScore*(self: BaseChainDB; blockHash: Hash256): int =
|
||||
rlp.decode(self.db.get(blockHashToScoreKey(blockHash)).toRange, int)
|
||||
|
||||
# proc setAsCanonicalChainHead*(self: BaseChainDB; header: BlockHeader): void =
|
||||
# ## Sets the header as the canonical chain HEAD.
|
||||
# for h in reversed(self.findCommonAncestor(header)):
|
||||
# self.addBlockNumberToHashLookup(h)
|
||||
# try:
|
||||
# self.getBlockHeaderByHash(header.hash)
|
||||
# except BlockNotFound:
|
||||
# raise newException(ValueError, "Cannot use unknown block hash as canonical head: {}".format(
|
||||
# header.hash))
|
||||
# self.db.set(CANONICALHEADHASHDBKEY, header.hash)
|
||||
|
||||
iterator findCommonAncestor*(self: BaseChainDB; header: BlockHeader): BlockHeader =
|
||||
iterator findNewAncestors(self: BaseChainDB; header: BlockHeader): BlockHeader =
|
||||
## Returns the chain leading up from the given header until the first ancestor it has in
|
||||
## common with our canonical chain.
|
||||
var h = header
|
||||
while true:
|
||||
try:
|
||||
let orig = self.getCanonicalBlockHeaderByNumber(h.blockNumber)
|
||||
if orig.hash == h.hash:
|
||||
break
|
||||
except BlockNotFound:
|
||||
discard
|
||||
|
||||
yield h
|
||||
|
||||
if h.parentHash == GENESIS_PARENT_HASH:
|
||||
break
|
||||
else:
|
||||
h = self.getBlockHeaderByHash(h.parentHash)
|
||||
|
||||
proc addBlockNumberToHashLookup(self: BaseChainDB; header: BlockHeader) =
|
||||
self.db.set(blockNumberToHashKey(header.blockNumber), rlp.encode(header.hash).toSeq())
|
||||
|
||||
iterator getBlockTransactionHashes(self: BaseChainDB, blockHeader: BlockHeader): Hash256 =
|
||||
## Returns an iterable of the transaction hashes from th block specified
|
||||
## by the given block header.
|
||||
doAssert(false, "TODO: Implement me")
|
||||
# let all_encoded_transactions = self._get_block_transaction_data(
|
||||
# blockHeader.transactionRoot,
|
||||
# )
|
||||
# for encoded_transaction in all_encoded_transactions:
|
||||
# yield keccak(encoded_transaction)
|
||||
|
||||
|
||||
proc removeTransactionFromCanonicalChain(self: BaseChainDB, transactionHash: Hash256) {.inline.} =
|
||||
## Removes the transaction specified by the given hash from the canonical chain.
|
||||
self.db.delete(transactionHashToBlockKey(transactionHash))
|
||||
|
||||
proc setAsCanonicalChainHead(self: BaseChainDB; headerHash: Hash256): seq[BlockHeader] =
|
||||
## Sets the header as the canonical chain HEAD.
|
||||
|
||||
let header = self.getBlockHeaderByHash(headerHash)
|
||||
|
||||
var newCanonicalHeaders = sequtils.toSeq(findNewAncestors(self, header))
|
||||
reverse(newCanonicalHeaders)
|
||||
for h in newCanonicalHeaders:
|
||||
var oldHash: Hash256
|
||||
try:
|
||||
var orig = self.getCanonicalBlockHeaderByNumber(h.blockNumber)
|
||||
except KeyError:
|
||||
discard # TODO: break??
|
||||
h = self.getBlockHeaderByHash(h.parentHash)
|
||||
oldHash = self.lookupBlockHash(h.blockNumber)
|
||||
except BlockNotFound:
|
||||
break
|
||||
|
||||
let oldHeader = self.getBlockHeaderByHash(oldHash)
|
||||
for txHash in self.getBlockTransactionHashes(oldHeader):
|
||||
self.removeTransactionFromCanonicalChain(txHash)
|
||||
# TODO re-add txn to internal pending pool (only if local sender)
|
||||
|
||||
for h in newCanonicalHeaders:
|
||||
self.addBlockNumberToHashLookup(h)
|
||||
|
||||
self.db.set(canonicalHeadHashKey(), rlp.encode(header.hash).toSeq())
|
||||
return newCanonicalHeaders
|
||||
|
||||
proc headerExists*(self: BaseChainDB; blockHash: Hash256): bool =
|
||||
## Returns True if the header with the given block hash is in our DB.
|
||||
self.contains(blockHash)
|
||||
|
||||
iterator getBlockTransactionData(self: BaseChainDB, transactionRoot: Hash256): BytesRange =
|
||||
doAssert(false, "TODO: Implement me")
|
||||
# var transactionDb = HexaryTrie(self.db, transactionRoot)
|
||||
# var transactionIdx = 0
|
||||
# while true:
|
||||
# var transactionKey = rlp.encode(transactionIdx)
|
||||
# if transactionKey in transactionDb:
|
||||
# var transactionData = transactionDb[transactionKey]
|
||||
# yield transactionDb[transactionKey]
|
||||
# else:
|
||||
# break
|
||||
# inc transactionIdx
|
||||
|
||||
|
||||
# iterator getReceipts*(self: BaseChainDB; header: BlockHeader; receiptClass: typedesc): Receipt =
|
||||
# var receiptDb = HexaryTrie()
|
||||
# for receiptIdx in itertools.count():
|
||||
|
@ -99,45 +158,55 @@ proc headerExists*(self: BaseChainDB; blockHash: Hash256): bool =
|
|||
# else:
|
||||
# break
|
||||
|
||||
# iterator getBlockTransactions*(self: BaseChainDB; blockHeader: BlockHeader;
|
||||
# transactionClass: typedesc): FrontierTransaction =
|
||||
# var transactionDb = HexaryTrie(self.db)
|
||||
# for transactionIdx in itertools.count():
|
||||
# var transactionKey = rlp.encode(transactionIdx)
|
||||
# if transactionKey in transactionDb:
|
||||
# var transactionData = transactionDb[transactionKey]
|
||||
# yield rlp.decode(transactionData)
|
||||
# else:
|
||||
# break
|
||||
iterator getBlockTransactions(self: BaseChainDB; transactionRoot: Hash256;
|
||||
transactionClass: typedesc): transactionClass =
|
||||
for encodedTransaction in self.getBlockTransactionData(transactionRoot):
|
||||
yield rlp.decode(encodedTransaction, transactionClass)
|
||||
|
||||
# proc addBlockNumberToHashLookup*(self: BaseChainDB; header: BlockHeader): void =
|
||||
# var blockNumberToHashKey = makeBlockNumberToHashLookupKey(header.blockNumber)
|
||||
# self.db.set(blockNumberToHashKey, rlp.encode(header.hash))
|
||||
proc persistHeaderToDb*(self: BaseChainDB; header: BlockHeader): seq[BlockHeader] =
|
||||
let isGenesis = header.parentHash == GENESIS_PARENT_HASH
|
||||
if not isGenesis and not self.headerExists(header.parentHash):
|
||||
raise newException(ParentNotFound, "Cannot persist block header " &
|
||||
$header.hash & " with unknown parent " & $header.parentHash)
|
||||
self.db.set(genericHashKey(header.hash), rlp.encode(header).toSeq())
|
||||
let score = if isGenesis: header.difficulty
|
||||
else: self.getScore(header.parentHash).u256 + header.difficulty
|
||||
self.db.set(blockHashToScoreKey(header.hash), rlp.encode(score).toSeq())
|
||||
var headScore: int
|
||||
try:
|
||||
headScore = self.getScore(self.getCanonicalHead().hash)
|
||||
except CanonicalHeadNotFound:
|
||||
return self.setAsCanonicalChainHead(header.hash)
|
||||
|
||||
# proc persistHeaderToDb*(self: BaseChainDB; header: BlockHeader): void =
|
||||
# if header.parentHash != GENESISPARENTHASH and
|
||||
# notself.headerExists(header.parentHash):
|
||||
# raise newException(ParentNotFound, "Cannot persist block header ({}) with unknown parent ({})".format(
|
||||
# encodeHex(header.hash), encodeHex(header.parentHash)))
|
||||
# self.db.set(header.hash, rlp.encode(header))
|
||||
# if header.parentHash == GENESISPARENTHASH:
|
||||
# var score = header.difficulty
|
||||
# else:
|
||||
# score = self.getScore(header.parentHash) + header.difficulty
|
||||
# self.db.set(makeBlockHashToScoreLookupKey(header.hash), rlp.encode(score))
|
||||
# try:
|
||||
# var headScore = self.getScore(self.getCanonicalHead().hash)
|
||||
# except CanonicalHeadNotFound:
|
||||
# self.setAsCanonicalChainHead(header)
|
||||
if score > headScore.u256:
|
||||
result = self.setAsCanonicalChainHead(header.hash)
|
||||
|
||||
# proc persistBlockToDb*(self: BaseChainDB; block: FrontierBlock): void =
|
||||
# self.persistHeaderToDb(block.header)
|
||||
# var transactionDb = HexaryTrie(self.db)
|
||||
# for i in 0 ..< len(block.transactions):
|
||||
# var indexKey = rlp.encode(i)
|
||||
# transactionDb[indexKey] = rlp.encode(block.transactions[i])
|
||||
# nil
|
||||
# self.db.set(block.header.unclesHash, rlp.encode(block.uncles))
|
||||
|
||||
proc addTransactionToCanonicalChain(self: BaseChainDB, txHash: Hash256,
|
||||
blockHeader: BlockHeader, index: int) =
|
||||
let k: TransactionKey = (blockHeader.blockNumber, index)
|
||||
self.db.set(transactionHashToBlockKey(txHash), rlp.encode(k).toSeq())
|
||||
|
||||
proc persistUncles*(self: BaseChainDB, uncles: openarray[BlockHeader]): Hash256 =
|
||||
## Persists the list of uncles to the database.
|
||||
## Returns the uncles hash.
|
||||
let enc = rlp.encode(uncles)
|
||||
result = keccak256.digest(enc.toOpenArray())
|
||||
self.db.set(genericHashKey(result), enc.toSeq())
|
||||
|
||||
proc persistBlockToDb*(self: BaseChainDB; blk: Block) =
|
||||
## Persist the given block's header and uncles.
|
||||
## Assumes all block transactions have been persisted already.
|
||||
let newCanonicalHeaders = self.persistHeaderToDb(blk.header)
|
||||
for header in newCanonicalHeaders:
|
||||
var index = 0
|
||||
for txHash in self.getBlockTransactionHashes(header):
|
||||
self.addTransactionToCanonicalChain(txHash, header, index)
|
||||
inc index
|
||||
|
||||
if blk.uncles.len != 0:
|
||||
let ommersHash = self.persistUncles(blk.uncles)
|
||||
assert ommersHash == blk.header.ommersHash
|
||||
|
||||
# proc addTransaction*(self: BaseChainDB; blockHeader: BlockHeader; indexKey: cstring;
|
||||
# transaction: FrontierTransaction): cstring =
|
||||
|
@ -163,5 +232,3 @@ proc headerExists*(self: BaseChainDB; blockHash: Hash256): bool =
|
|||
method getStateDb*(self: BaseChainDB; stateRoot: Hash256; readOnly: bool = false): AccountStateDB =
|
||||
# TODO
|
||||
result = newAccountStateDB(initTable[string, string]())
|
||||
|
||||
# var CANONICALHEADHASHDBKEY = cstring"v1:canonical_head_hash"
|
||||
|
|
Loading…
Reference in New Issue