Compare commits

...

3 Commits

Author SHA1 Message Date
jangko 73a0f7caeb
Write baggage to database 2024-06-24 12:04:59 +07:00
jangko 7c679a8ea9
Do not force base update 2024-06-24 11:12:37 +07:00
jangko d40529ebef
Avoid reloading parent header 2024-06-24 09:53:35 +07:00
1 changed files with 126 additions and 48 deletions

View File

@ -18,31 +18,37 @@ import
../executor/process_block ../executor/process_block
type type
HeadDesc = object
number: BlockNumber
hash: Hash256
BlockDesc = object
blk: EthBlock
receipts: seq[Receipt]
ForkedChain* = object ForkedChain* = object
stagingTx: CoreDbTxRef stagingTx: CoreDbTxRef
db: CoreDbRef db: CoreDbRef
com: CommonRef com: CommonRef
blocks: Table[Hash256, EthBlock] blocks: Table[Hash256, BlockDesc]
head: Hash256 headHash: Hash256
base: Hash256 baseHash: Hash256
headBlockNumber: BlockNumber baseHeader: BlockHeader
headHeader: BlockHeader
heads: seq[HeadDesc]
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Private # Private
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
proc getVmState(c: ForkedChain, proc processBlock(c: ForkedChain,
header: BlockHeader): Result[BaseVMState, string] = parent: BlockHeader,
let vmState = BaseVMState() blk: EthBlock): Result[seq[Receipt], string] =
if not vmState.init(header, c.com):
return err("Could not initialise VMState")
ok(vmState)
proc processBlock(c: ForkedChain, blk: EthBlock): Result[void, string] =
template header(): BlockHeader = template header(): BlockHeader =
blk.header blk.header
let vmState = ?c.getVmState(header) let vmState = BaseVMState()
vmState.init(parent, header, c.com)
c.com.hardForkTransition(header) c.com.hardForkTransition(header)
?c.com.validateHeaderAndKinship(blk, vmState.parent, checkSealOK = false) ?c.com.validateHeaderAndKinship(blk, vmState.parent, checkSealOK = false)
@ -54,6 +60,8 @@ proc processBlock(c: ForkedChain, blk: EthBlock): Result[void, string] =
skipUncles = true, skipUncles = true,
) )
# We still need to write header to database
# because validateUncles still need it
let blockHash = header.blockHash() let blockHash = header.blockHash()
if not c.db.persistHeader( if not c.db.persistHeader(
blockHash, blockHash,
@ -61,55 +69,102 @@ proc processBlock(c: ForkedChain, blk: EthBlock): Result[void, string] =
c.com.startOfHistory): c.com.startOfHistory):
return err("Could not persist header") return err("Could not persist header")
ok() ok(move(vmState.receipts))
proc updateHead(c: var ForkedChain, blk: EthBlock) = proc updateHeads(c: var ForkedChain,
hash: Hash256,
header: BlockHeader) =
for i in 0..<c.heads.len:
if c.heads[i].hash == header.parentHash:
c.heads[i] = HeadDesc(
hash: hash,
number: header.number,
)
return
c.heads.add HeadDesc(
hash: hash,
number: header.number,
)
proc updateHead(c: var ForkedChain,
blk: EthBlock,
receipts: sink seq[Receipt]) =
template header(): BlockHeader = template header(): BlockHeader =
blk.header blk.header
c.head = header.blockHash c.headHeader = header
c.headBlockNumber = header.number c.headHash = header.blockHash
c.blocks[c.head] = blk c.blocks[c.headHash] = BlockDesc(
blk: blk,
receipts: move(receipts)
)
c.updateHeads(c.headHash, header)
proc validatePotentialHead(c: var ForkedChain, proc validatePotentialHead(c: var ForkedChain,
parent: BlockHeader,
blk: EthBlock, blk: EthBlock,
updateHead: bool = true) = updateHead: bool = true) =
let dbTx = c.db.newTransaction() let dbTx = c.db.newTransaction()
defer: defer:
dbTx.dispose() dbTx.dispose()
let res = c.processBlock(blk) var res = c.processBlock(parent, blk)
if res.isErr: if res.isErr:
dbTx.rollback() dbTx.rollback()
return return
dbTx.commit() dbTx.commit()
if updateHead: if updateHead:
c.updateHead(blk) c.updateHead(blk, move(res.value))
proc replaySegment(c: var ForkedChain, proc replaySegment(c: var ForkedChain,
head: Hash256): BlockNumber = head: Hash256) =
var var
prevHash = head prevHash = head
chain = newSeq[EthBlock]() chain = newSeq[EthBlock]()
while prevHash != c.base: while prevHash != c.baseHash:
chain.add c.blocks[prevHash] chain.add c.blocks[prevHash].blk
prevHash = chain[^1].header.parentHash prevHash = chain[^1].header.parentHash
c.stagingTx.rollback() c.stagingTx.rollback()
c.stagingTx = c.db.newTransaction() c.stagingTx = c.db.newTransaction()
c.headHeader = c.baseHeader
for i in countdown(chain.high, chain.low): for i in countdown(chain.high, chain.low):
c.validatePotentialHead(chain[i], updateHead = false) c.validatePotentialHead(c.headHeader, chain[i], updateHead = false)
c.headHeader = chain[i].header
chain[^1].header.number proc writeBaggage(c: var ForkedChain, blockHash: Hash256) =
var prevHash = blockHash
while prevHash != c.baseHash:
let blk = c.blocks[prevHash]
c.db.persistTransactions(blk.blk.header.number, blk.blk.transactions)
c.db.persistReceipts(blk.receipts)
discard c.db.persistUncles(blk.blk.uncles)
if blk.blk.withdrawals.isSome:
c.db.persistWithdrawals(blk.blk.withdrawals.get)
prevHash = blk.blk.header.parentHash
proc updateBase(c: var ForkedChain, proc updateBase(c: var ForkedChain,
head: Hash256, headBlockNumber: BlockNumber) = newBaseHash: Hash256, newBaseHeader: BlockHeader) =
c.base = head # remove obsolete chains
c.head = head for i in 0..<c.heads.len:
c.headBlockNumber = headBlockNumber if c.heads[i].number <= c.baseHeader.number:
c.blocks.clear() var prevHash = c.heads[i].hash
while prevHash != c.baseHash:
c.blocks.withValue(prevHash, val) do:
let rmHash = prevHash
prevHash = val.blk.header.parentHash
c.blocks.del(rmHash)
do:
# older chain segment have been deleted
# by previous head
break
c.heads.del(i)
c.baseHeader = newBaseHeader
c.baseHash = newBaseHash
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Public functions # Public functions
@ -119,23 +174,24 @@ proc initForkedChain*(com: CommonRef): ForkedChain =
result.com = com result.com = com
result.db = com.db result.db = com.db
result.stagingTx = com.db.newTransaction() result.stagingTx = com.db.newTransaction()
let head = com.db.getCanonicalHead() result.baseHeader = com.db.getCanonicalHead()
let headHash = head.blockHash let headHash = result.baseHeader.blockHash
result.head = headHash result.headHash = headHash
result.base = headHash result.baseHash = headHash
result.headHeader = result.baseHeader
proc addBlock*(c: var ForkedChain, blk: EthBlock) = proc addBlock*(c: var ForkedChain, blk: EthBlock) =
template header(): BlockHeader = template header(): BlockHeader =
blk.header blk.header
if header.parentHash == c.head: if header.parentHash == c.headHash:
c.validatePotentialHead(blk) c.validatePotentialHead(c.headHeader, blk)
return return
if header.parentHash == c.base: if header.parentHash == c.baseHash:
c.stagingTx.rollback() c.stagingTx.rollback()
c.stagingTx = c.db.newTransaction() c.stagingTx = c.db.newTransaction()
c.validatePotentialHead(blk) c.validatePotentialHead(c.baseHeader, blk)
return return
if header.parentHash notin c.blocks: if header.parentHash notin c.blocks:
@ -143,35 +199,57 @@ proc addBlock*(c: var ForkedChain, blk: EthBlock) =
# there is no hope the descendant is valid # there is no hope the descendant is valid
return return
discard c.replaySegment(header.parentHash) c.replaySegment(header.parentHash)
c.validatePotentialHead(blk) c.validatePotentialHead(c.headHeader, blk)
proc finalizeSegment*(c: var ForkedChain, proc finalizeSegment*(c: var ForkedChain,
finalized: Hash256): Result[void, string] = finalizedHash: Hash256): Result[void, string] =
if finalized == c.head: if finalizedHash == c.headHash:
c.writeBaggage(finalizedHash)
# the current segment is canonical chain # the current segment is canonical chain
c.stagingTx.commit() c.stagingTx.commit()
# Save and record the block number before the last saved block state. # Save and record the block number before the last saved block state.
c.db.persistent(c.headBlockNumber).isOkOr: c.db.persistent(c.headHeader.number).isOkOr:
return err("Failed to save state: " & $$error) return err("Failed to save state: " & $$error)
c.updateBase(finalized, c.headBlockNumber)
c.stagingTx = c.db.newTransaction() c.stagingTx = c.db.newTransaction()
c.updateBase(finalizedHash, c.headHeader)
return ok() return ok()
if finalized notin c.blocks: var
newBaseHash: Hash256
newBaseHeader: BlockHeader
c.blocks.withValue(finalizedHash, val) do:
if c.headHeader.number <= 128:
if val.blk.header.number < c.headHeader.number:
newBaseHash = finalizedHash
newBaseHeader = val.blk.header
else:
newBaseHash = c.headHash
newBaseHeader = c.headHeader
elif val.blk.header.number < c.headHeader.number - 128:
newBaseHash = finalizedHash
newBaseHeader = val.blk.header
else:
newBaseHash = c.headHash
newBaseHeader = c.headHeader
do:
return err("Finalized head not in segments list") return err("Finalized head not in segments list")
c.stagingTx.rollback() c.stagingTx.rollback()
c.stagingTx = c.db.newTransaction() c.stagingTx = c.db.newTransaction()
let headBlockNumber = c.replaySegment(finalized) c.replaySegment(newBaseHash)
c.writeBaggage(newBaseHash)
c.stagingTx.commit() c.stagingTx.commit()
c.db.persistent(headBlockNumber).isOkOr: c.db.persistent(newBaseHeader.number).isOkOr:
return err("Failed to save state: " & $$error) return err("Failed to save state: " & $$error)
c.updateBase(finalized, headBlockNumber)
c.stagingTx = c.db.newTransaction() c.stagingTx = c.db.newTransaction()
c.updateBase(newBaseHash, newBaseHeader)
ok() ok()