Separate finalizedHash from baseHash

This commit is contained in:
jangko 2024-06-25 21:34:22 +07:00
parent d40f007889
commit 0471f059c4
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
2 changed files with 80 additions and 27 deletions

View File

@ -30,6 +30,10 @@ type
hash: Hash256
header: BlockHeader
CanonicalDesc = object
cursorHash: Hash256
header: BlockHeader
ForkedChain* = object
stagingTx: CoreDbTxRef
db: CoreDbRef
@ -168,7 +172,9 @@ proc writeBaggage(c: var ForkedChain, target: Hash256) =
prevHash = blk.blk.header.parentHash
func updateBase(c: var ForkedChain,
newBaseHash: Hash256, newBaseHeader: BlockHeader) =
newBaseHash: Hash256,
newBaseHeader: BlockHeader,
canonicalCursorHash: Hash256) =
var cursorHeadsLen = c.cursorHeads.len
# Remove obsolete chains, example:
# -- A1 - A2 - A3 -- D5 - D6
@ -180,7 +186,8 @@ func updateBase(c: var ForkedChain,
# but not D
for i in 0..<cursorHeadsLen:
if c.cursorHeads[i].forkJunction <= newBaseHeader.number:
if c.cursorHeads[i].forkJunction <= newBaseHeader.number and
c.cursorHeads[i].hash != canonicalCursorHash:
var prevHash = c.cursorHeads[i].hash
while prevHash != c.baseHash:
c.blocks.withValue(prevHash, val) do:
@ -196,21 +203,37 @@ func updateBase(c: var ForkedChain,
# the sequence length will not updated
dec cursorHeadsLen
# Cleanup in-memory blocks starting from newBase backward
# while blocks from newBase+1 to canonicalCursor not deleted
# e.g. B4 onward
var prevHash = newBaseHash
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.baseHeader = newBaseHeader
c.baseHash = newBaseHash
func findCanonicalHead(c: ForkedChain,
hash: Hash256): Result[BlockHeader, string] =
hash: Hash256): Result[CanonicalDesc, string] =
if hash == c.baseHash:
return ok(c.baseHeader)
# The cursorHash here should not be used for next step
# because it not point to any active chain
return ok(CanonicalDesc(cursorHash: c.baseHash, header: c.baseHeader))
# Find hash belong to which chain
for x in c.cursorHeads:
let header = c.blocks[x.hash].blk.header
var prevHash = x.hash
for cursor in c.cursorHeads:
let header = c.blocks[cursor.hash].blk.header
var prevHash = cursor.hash
while prevHash != c.baseHash:
if prevHash == hash:
return ok(header)
return ok(CanonicalDesc(cursorHash: cursor.hash, header: header))
prevHash = c.blocks[prevHash].blk.header.parentHash
err("Block hash is not part of any active chain")
@ -253,6 +276,17 @@ func calculateNewBase(c: ForkedChain,
doAssert(false, "Unreachable code")
func trimCanonicalChain(c: var ForkedChain, head: CanonicalDesc) =
# Maybe the current active chain is longer than canonical chain
var prevHash = head.cursorHash
while prevHash != c.baseHash:
let header = c.blocks[prevHash].blk.header
if header.number > head.header.number:
c.blocks.del(prevHash)
else:
break
prevHash = header.parentHash
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
@ -302,30 +336,43 @@ proc forkChoice*(c: var ForkedChain,
finalizedHash: Hash256): Result[void, string] =
# If there are multiple heads, find which chain headHash belongs to
let headHeader = ?c.findCanonicalHead(headHash)
let head = ?c.findCanonicalHead(headHash)
# Finalized block must be part of canonical chain
let finalizedHeader = ?c.canonicalChain(finalizedHash, headHash)
if finalizedHash == c.baseHash:
# The base is not updated
let newBase = c.calculateNewBase(
finalizedHeader, headHash, head.header)
if newBase.hash == c.baseHash:
# The base is not updated but the cursor maybe need update
if c.cursorHash != head.cursorHash:
if not c.stagingTx.isNil:
c.stagingTx.rollback()
c.stagingTx = c.db.newTransaction()
c.replaySegment(headHash)
c.trimCanonicalChain(head)
if c.cursorHash != headHash:
c.cursorHeader = head.header
c.cursorHash = headHash
return ok()
# At this point cursorHeader.number > baseHeader.number
if finalizedHash == c.cursorHash:
if newBase.hash == c.cursorHash:
# Paranoid check, guaranteed by findCanonicalHead
doAssert(c.cursorHash == headHash)
# Current segment is canonical chain
c.writeBaggage(finalizedHash)
c.writeBaggage(newBase.hash)
# Paranoid check, guaranteed by `finalizedHash == c.cursorHash`
# Paranoid check, guaranteed by `newBase.hash == c.cursorHash`
doAssert(not c.stagingTx.isNil)
c.stagingTx.commit()
c.stagingTx = nil
# Move base to finalized
c.updateBase(finalizedHash, c.cursorHeader)
# Move base to newBase
c.updateBase(newBase.hash, c.cursorHeader, head.cursorHash)
# Save and record the block number before the last saved block state.
c.db.persistent(c.cursorHeader.number).isOkOr:
@ -335,10 +382,7 @@ proc forkChoice*(c: var ForkedChain,
# At this point finalizedHeader.number is <= headHeader.number
# and possibly switched to other chain beside the one with cursor
doAssert(finalizedHeader.number <= headHeader.number)
let newBase = c.calculateNewBase(
finalizedHeader, headHash, headHeader)
doAssert(finalizedHeader.number <= head.header.number)
# Write segment from base+1 to newBase into database
c.stagingTx.rollback()
@ -347,18 +391,22 @@ proc forkChoice*(c: var ForkedChain,
c.replaySegment(newBase.hash)
c.writeBaggage(newBase.hash)
c.stagingTx.commit()
c.stagingTx = nil
# Update base forward to newBase
c.updateBase(newBase.hash, newBase.header)
c.updateBase(newBase.hash, newBase.header, head.cursorHash)
c.db.persistent(newBase.header.number).isOkOr:
return err("Failed to save state: " & $$error)
# Move chain state forward to current head
if newBase.header.number < headHeader.number:
c.stagingTx = c.db.newTransaction()
if newBase.header.number < head.header.number:
if c.stagingTx.isNil:
c.stagingTx = c.db.newTransaction()
c.replaySegment(headHash)
# Move cursor forward to current head
c.cursorHeader = headHeader
c.cursorHash = headHash
c.trimCanonicalChain(head)
if c.cursorHash != headHash:
c.cursorHeader = head.header
c.cursorHash = headHash
ok()

View File

@ -21,6 +21,9 @@ import
../nimbus/common/common,
../nimbus/core/eip4844
const
debugMode = false
type
BlockDesc = object
blk: EthBlock
@ -118,6 +121,8 @@ proc executeCase(node: JsonNode): bool =
proc executeFile(node: JsonNode, testStatusIMPL: var TestStatus) =
for name, bctCase in node:
when debugMode:
debugEcho "TEST NAME: ", name
check executeCase(bctCase)
proc blockchainJsonMain*() =
@ -138,12 +143,12 @@ proc blockchainJsonMain*() =
jsonTest(newFolder, "newBlockchainTests", executeFile, skipNewBCTests)
when isMainModule:
when false:
when debugMode:
proc executeFile(name: string) =
var testStatusIMPL: TestStatus
let node = json.parseFile(name)
executeFile(node, testStatusIMPL)
executeFile("tests/fixtures/eth_tests/BlockchainTests/TransitionTests/bcFrontierToHomestead/HomesteadOverrideFrontier.json")
executeFile("tests/fixtures/eth_tests/BlockchainTests/ValidBlocks/bcTotalDifficultyTest/sideChainWithMoreTransactions.json")
else:
blockchainJsonMain()