Add ForkedChainRef tests (#2430)
ForkedChainRef have become quite complex. test_blockchain_json is not sufficient cover for edge cases or synthetic cases.
This commit is contained in:
parent
403b93104b
commit
401537ad38
|
@ -19,6 +19,10 @@ import
|
|||
../validate,
|
||||
../executor/process_block
|
||||
|
||||
export
|
||||
common,
|
||||
core_db
|
||||
|
||||
type
|
||||
CursorDesc = object
|
||||
forkJunction: BlockNumber
|
||||
|
@ -46,6 +50,8 @@ type
|
|||
cursorHash: Hash256
|
||||
cursorHeader: BlockHeader
|
||||
cursorHeads: seq[CursorDesc]
|
||||
extraValidation: bool
|
||||
baseDistance: uint64
|
||||
|
||||
const
|
||||
BaseDistance = 128
|
||||
|
@ -69,7 +75,8 @@ proc processBlock(c: ForkedChainRef,
|
|||
vmState.init(parent, header, c.com)
|
||||
c.com.hardForkTransition(header)
|
||||
|
||||
?c.com.validateHeaderAndKinship(blk, vmState.parent, checkSealOK = false)
|
||||
if c.extraValidation:
|
||||
?c.com.validateHeaderAndKinship(blk, vmState.parent, checkSealOK = false)
|
||||
|
||||
?vmState.processBlock(
|
||||
blk,
|
||||
|
@ -83,10 +90,15 @@ proc processBlock(c: ForkedChainRef,
|
|||
let blockHash = header.blockHash()
|
||||
if not c.db.persistHeader(
|
||||
blockHash,
|
||||
header, c.com.consensus == ConsensusType.POS,
|
||||
header,
|
||||
c.com.startOfHistory):
|
||||
return err("Could not persist header")
|
||||
|
||||
# update currentBlock *after* we persist it
|
||||
# so the rpc return consistent result
|
||||
# between eth_blockNumber and eth_syncing
|
||||
c.com.syncCurrent = header.number
|
||||
|
||||
ok(move(vmState.receipts))
|
||||
|
||||
func updateCursorHeads(c: ForkedChainRef,
|
||||
|
@ -166,6 +178,29 @@ proc replaySegment(c: ForkedChainRef, target: Hash256) =
|
|||
c.validateBlock(c.cursorHeader, chain[i],
|
||||
updateCursor = false).expect("have been validated before")
|
||||
c.cursorHeader = chain[i].header
|
||||
c.cursorHash = target
|
||||
|
||||
proc replaySegment(c: ForkedChainRef,
|
||||
target: Hash256,
|
||||
parent: BlockHeader,
|
||||
parentHash: Hash256) =
|
||||
# Replay from parent+1 to target block
|
||||
# with assumption last state is at parent
|
||||
var
|
||||
prevHash = target
|
||||
chain = newSeq[EthBlock]()
|
||||
|
||||
shouldNotKeyError:
|
||||
while prevHash != parentHash:
|
||||
chain.add c.blocks[prevHash].blk
|
||||
prevHash = chain[^1].header.parentHash
|
||||
|
||||
c.cursorHeader = parent
|
||||
for i in countdown(chain.high, chain.low):
|
||||
c.validateBlock(c.cursorHeader, chain[i],
|
||||
updateCursor = false).expect("have been validated before")
|
||||
c.cursorHeader = chain[i].header
|
||||
c.cursorHash = target
|
||||
|
||||
proc writeBaggage(c: ForkedChainRef, target: Hash256) =
|
||||
# Write baggage from base+1 to target block
|
||||
|
@ -244,12 +279,12 @@ func findCanonicalHead(c: ForkedChainRef,
|
|||
shouldNotKeyError:
|
||||
# Find hash belong to which chain
|
||||
for cursor in c.cursorHeads:
|
||||
let header = c.blocks[cursor.hash].blk.header
|
||||
var prevHash = cursor.hash
|
||||
while prevHash != c.baseHash:
|
||||
let header = c.blocks[prevHash].blk.header
|
||||
if prevHash == hash:
|
||||
return ok(CanonicalDesc(cursorHash: cursor.hash, header: header))
|
||||
prevHash = c.blocks[prevHash].blk.header.parentHash
|
||||
prevHash = header.parentHash
|
||||
|
||||
err("Block hash is not part of any active chain")
|
||||
|
||||
|
@ -273,14 +308,14 @@ func calculateNewBase(c: ForkedChainRef,
|
|||
finalizedHeader: BlockHeader,
|
||||
headHash: Hash256,
|
||||
headHeader: BlockHeader): BaseDesc =
|
||||
# It's important to have base at least `BaseDistance` behind head
|
||||
# It's important to have base at least `baseDistance` behind head
|
||||
# so we can answer state queries about history that deep.
|
||||
|
||||
let targetNumber = min(finalizedHeader.number,
|
||||
max(headHeader.number, BaseDistance) - BaseDistance)
|
||||
max(headHeader.number, c.baseDistance) - c.baseDistance)
|
||||
|
||||
# The distance is less than `BaseDistance`, don't move the base
|
||||
if targetNumber - c.baseHeader.number <= BaseDistance:
|
||||
# The distance is less than `baseDistance`, don't move the base
|
||||
if targetNumber - c.baseHeader.number <= c.baseDistance:
|
||||
return BaseDesc(hash: c.baseHash, header: c.baseHeader)
|
||||
|
||||
shouldNotKeyError:
|
||||
|
@ -293,7 +328,9 @@ func calculateNewBase(c: ForkedChainRef,
|
|||
|
||||
doAssert(false, "Unreachable code")
|
||||
|
||||
func trimCanonicalChain(c: ForkedChainRef, head: CanonicalDesc) =
|
||||
func trimCanonicalChain(c: ForkedChainRef,
|
||||
head: CanonicalDesc,
|
||||
headHash: Hash256) =
|
||||
# Maybe the current active chain is longer than canonical chain
|
||||
shouldNotKeyError:
|
||||
var prevHash = head.cursorHash
|
||||
|
@ -305,18 +342,56 @@ func trimCanonicalChain(c: ForkedChainRef, head: CanonicalDesc) =
|
|||
break
|
||||
prevHash = header.parentHash
|
||||
|
||||
if c.cursorHeads.len == 0:
|
||||
return
|
||||
|
||||
# Update cursorHeads if indeed we trim
|
||||
for i in 0..<c.cursorHeads.len:
|
||||
if c.cursorHeads[i].hash == head.cursorHash:
|
||||
c.cursorHeads[i].hash = headHash
|
||||
return
|
||||
|
||||
doAssert(false, "Unreachable code")
|
||||
|
||||
proc setHead(c: ForkedChainRef,
|
||||
headHash: Hash256,
|
||||
number: BlockNumber) =
|
||||
# TODO: db.setHead should not read from db anymore
|
||||
# and raise RlpError, all canonical chain marking
|
||||
# should be done from here.
|
||||
try:
|
||||
discard c.db.setHead(headHash)
|
||||
except RlpError as exc:
|
||||
raiseAssert(exc.msg)
|
||||
|
||||
# update global syncHighest
|
||||
c.com.syncHighest = number
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc newForkedChain*(com: CommonRef, baseHeader: BlockHeader): ForkedChainRef =
|
||||
new(result)
|
||||
result.com = com
|
||||
result.db = com.db
|
||||
result.baseHeader = baseHeader
|
||||
result.cursorHash = baseHeader.blockHash
|
||||
result.baseHash = result.cursorHash
|
||||
result.cursorHeader = result.baseHeader
|
||||
proc newForkedChain*(com: CommonRef,
|
||||
baseHeader: BlockHeader,
|
||||
baseDistance: uint64 = BaseDistance,
|
||||
extraValidation: bool = true): ForkedChainRef =
|
||||
|
||||
let baseHash = baseHeader.blockHash
|
||||
|
||||
var chain = ForkedChainRef(
|
||||
com: com,
|
||||
db : com.db,
|
||||
baseHeader : baseHeader,
|
||||
cursorHash : baseHash,
|
||||
baseHash : baseHash,
|
||||
cursorHeader: baseHeader,
|
||||
extraValidation: extraValidation,
|
||||
baseDistance: baseDistance,
|
||||
)
|
||||
|
||||
# update global syncStart
|
||||
com.syncStart = baseHeader.number
|
||||
chain
|
||||
|
||||
proc importBlock*(c: ForkedChainRef, blk: EthBlock): Result[void, string] =
|
||||
# Try to import block to canonical or side chain.
|
||||
|
@ -370,22 +445,31 @@ proc forkChoice*(c: ForkedChainRef,
|
|||
c.stagingTx = c.db.newTransaction()
|
||||
c.replaySegment(headHash)
|
||||
|
||||
c.trimCanonicalChain(head)
|
||||
c.trimCanonicalChain(head, headHash)
|
||||
if c.cursorHash != headHash:
|
||||
c.cursorHeader = head.header
|
||||
c.cursorHash = headHash
|
||||
|
||||
if c.stagingTx.isNil:
|
||||
# setHead below don't go straight to db
|
||||
c.stagingTx = c.db.newTransaction()
|
||||
|
||||
c.setHead(headHash, head.header.number)
|
||||
return ok()
|
||||
|
||||
# At this point cursorHeader.number > baseHeader.number
|
||||
if newBase.hash == c.cursorHash:
|
||||
# Paranoid check, guaranteed by findCanonicalHead
|
||||
doAssert(c.cursorHash == head.cursorHash)
|
||||
# Paranoid check, guaranteed by `newBase.hash == c.cursorHash`
|
||||
doAssert(not c.stagingTx.isNil)
|
||||
|
||||
# CL decide to move backward and then forward?
|
||||
if c.cursorHeader.number < head.header.number:
|
||||
c.replaySegment(headHash, c.cursorHeader, c.cursorHash)
|
||||
|
||||
# Current segment is canonical chain
|
||||
c.writeBaggage(newBase.hash)
|
||||
c.setHead(headHash, head.header.number)
|
||||
|
||||
# Paranoid check, guaranteed by `newBase.hash == c.cursorHash`
|
||||
doAssert(not c.stagingTx.isNil)
|
||||
c.stagingTx.commit()
|
||||
c.stagingTx = nil
|
||||
|
||||
|
@ -393,7 +477,7 @@ proc forkChoice*(c: ForkedChainRef,
|
|||
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:
|
||||
c.db.persistent(newBase.header.number).isOkOr:
|
||||
return err("Failed to save state: " & $$error)
|
||||
|
||||
return ok()
|
||||
|
@ -406,6 +490,7 @@ proc forkChoice*(c: ForkedChainRef,
|
|||
# Write segment from base+1 to newBase into database
|
||||
c.stagingTx.rollback()
|
||||
c.stagingTx = c.db.newTransaction()
|
||||
|
||||
if newBase.header.number > c.baseHeader.number:
|
||||
c.replaySegment(newBase.hash)
|
||||
c.writeBaggage(newBase.hash)
|
||||
|
@ -416,16 +501,71 @@ proc forkChoice*(c: ForkedChainRef,
|
|||
c.db.persistent(newBase.header.number).isOkOr:
|
||||
return err("Failed to save state: " & $$error)
|
||||
|
||||
if c.stagingTx.isNil:
|
||||
# replaySegment or setHead below don't
|
||||
# go straight to db
|
||||
c.stagingTx = c.db.newTransaction()
|
||||
|
||||
# Move chain state forward to current head
|
||||
if newBase.header.number < head.header.number:
|
||||
if c.stagingTx.isNil:
|
||||
c.stagingTx = c.db.newTransaction()
|
||||
c.replaySegment(headHash)
|
||||
|
||||
c.setHead(headHash, head.header.number)
|
||||
|
||||
# Move cursor to current head
|
||||
c.trimCanonicalChain(head)
|
||||
c.trimCanonicalChain(head, headHash)
|
||||
if c.cursorHash != headHash:
|
||||
c.cursorHeader = head.header
|
||||
c.cursorHash = headHash
|
||||
|
||||
ok()
|
||||
|
||||
func haveBlockAndState*(c: ForkedChainRef, hash: Hash256): bool =
|
||||
if c.blocks.hasKey(hash):
|
||||
return true
|
||||
if c.baseHash == hash:
|
||||
return true
|
||||
false
|
||||
|
||||
func stateReady*(c: ForkedChainRef, header: BlockHeader): bool =
|
||||
let blockHash = header.blockHash
|
||||
blockHash == c.cursorHash
|
||||
|
||||
func com*(c: ForkedChainRef): CommonRef =
|
||||
c.com
|
||||
|
||||
func db*(c: ForkedChainRef): CoreDbRef =
|
||||
c.db
|
||||
|
||||
func latestHeader*(c: ForkedChainRef): BlockHeader =
|
||||
c.cursorHeader
|
||||
|
||||
func latestHash*(c: ForkedChainRef): Hash256 =
|
||||
c.cursorHash
|
||||
|
||||
proc headerByNumber*(c: ForkedChainRef, number: BlockNumber): Result[BlockHeader, string] =
|
||||
if number > c.cursorHeader.number:
|
||||
return err("Requested block number not exists: " & $number)
|
||||
|
||||
if number == c.cursorHeader.number:
|
||||
return ok(c.cursorHeader)
|
||||
|
||||
if number == c.baseHeader.number:
|
||||
return ok(c.baseHeader)
|
||||
|
||||
if number < c.baseHeader.number:
|
||||
var header: BlockHeader
|
||||
if c.db.getBlockHeader(number, header):
|
||||
return ok(header)
|
||||
else:
|
||||
return err("Failed to get block with number: " & $number)
|
||||
|
||||
shouldNotKeyError:
|
||||
var prevHash = c.cursorHeader.parentHash
|
||||
while prevHash != c.baseHash:
|
||||
let header = c.blocks[prevHash].blk.header
|
||||
if header.number == number:
|
||||
return ok(header)
|
||||
prevHash = header.parentHash
|
||||
|
||||
doAssert(false, "headerByNumber: Unreachable code")
|
||||
|
|
|
@ -0,0 +1,428 @@
|
|||
# Nimbus
|
||||
# Copyright (c) 2018-2024 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * 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
|
||||
stew/byteutils,
|
||||
../nimbus/common,
|
||||
../nimbus/config,
|
||||
../nimbus/utils/utils,
|
||||
../nimbus/core/chain/forked_chain,
|
||||
../nimbus/db/ledger,
|
||||
unittest2
|
||||
|
||||
const
|
||||
genesisFile = "tests/customgenesis/cancun123.json"
|
||||
senderAddr = hexToByteArray[20]("73cf19657412508833f618a15e8251306b3e6ee5")
|
||||
|
||||
type
|
||||
TestEnv = object
|
||||
conf: NimbusConf
|
||||
|
||||
proc setupEnv(): TestEnv =
|
||||
let
|
||||
conf = makeConfig(@[
|
||||
"--custom-network:" & genesisFile
|
||||
])
|
||||
|
||||
TestEnv(conf: conf)
|
||||
|
||||
proc newCom(env: TestEnv): CommonRef =
|
||||
let
|
||||
com = CommonRef.new(
|
||||
newCoreDbRef DefaultDbMemory,
|
||||
env.conf.networkId,
|
||||
env.conf.networkParams
|
||||
)
|
||||
|
||||
com.initializeEmptyDb()
|
||||
com
|
||||
|
||||
proc makeBlk(com: CommonRef, number: BlockNumber, parentBlk: EthBlock): EthBlock =
|
||||
template parent(): BlockHeader =
|
||||
parentBlk.header
|
||||
|
||||
var wds = newSeqOfCap[Withdrawal](number.int)
|
||||
for i in 0..<number:
|
||||
wds.add Withdrawal(
|
||||
index: i,
|
||||
validatorIndex: 1,
|
||||
address: senderAddr,
|
||||
amount: 1,
|
||||
)
|
||||
|
||||
let ledger = LedgerRef.init(com.db, parent.stateRoot)
|
||||
for wd in wds:
|
||||
ledger.addBalance(wd.address, wd.weiAmount)
|
||||
|
||||
ledger.persist()
|
||||
|
||||
let wdRoot = calcWithdrawalsRoot(wds)
|
||||
var body = BlockBody(
|
||||
withdrawals: Opt.some(move(wds))
|
||||
)
|
||||
|
||||
let header = BlockHeader(
|
||||
number : number,
|
||||
parentHash : parent.blockHash,
|
||||
difficulty : 0.u256,
|
||||
timestamp : parent.timestamp + 1,
|
||||
gasLimit : parent.gasLimit,
|
||||
stateRoot : ledger.state,
|
||||
txRoot : parent.txRoot,
|
||||
baseFeePerGas : parent.baseFeePerGas,
|
||||
receiptsRoot : parent.receiptsRoot,
|
||||
ommersHash : parent.ommersHash,
|
||||
withdrawalsRoot: Opt.some(wdRoot),
|
||||
blobGasUsed : parent.blobGasUsed,
|
||||
excessBlobGas : parent.excessBlobGas,
|
||||
parentBeaconBlockRoot: parent.parentBeaconBlockRoot,
|
||||
)
|
||||
|
||||
EthBlock.init(header, body)
|
||||
|
||||
proc makeBlk(com: CommonRef, number: BlockNumber, parentBlk: EthBlock, extraData: byte): EthBlock =
|
||||
var blk = com.makeBlk(number, parentBlk)
|
||||
blk.header.extraData = @[extraData]
|
||||
blk
|
||||
|
||||
proc headHash(c: CommonRef): Hash256 =
|
||||
c.db.getCanonicalHead().blockHash
|
||||
|
||||
func blockHash(x: EthBlock): Hash256 =
|
||||
x.header.blockHash
|
||||
|
||||
proc wdWritten(com: CommonRef, blk: EthBlock): int =
|
||||
if blk.header.withdrawalsRoot.isSome:
|
||||
com.db.getWithdrawals(blk.header.withdrawalsRoot.get).len
|
||||
else:
|
||||
0
|
||||
|
||||
proc forkedChainMain*() =
|
||||
suite "ForkedChainRef tests":
|
||||
var env = setupEnv()
|
||||
let
|
||||
cc = env.newCom
|
||||
genesisHash = cc.genesisHeader.blockHash
|
||||
genesis = EthBlock.init(cc.genesisHeader, BlockBody())
|
||||
|
||||
let
|
||||
blk1 = cc.makeBlk(1, genesis)
|
||||
blk2 = cc.makeBlk(2, blk1)
|
||||
blk3 = cc.makeBlk(3, blk2)
|
||||
|
||||
dbTx = cc.db.newTransaction()
|
||||
blk4 = cc.makeBlk(4, blk3)
|
||||
blk5 = cc.makeBlk(5, blk4)
|
||||
blk6 = cc.makeBlk(6, blk5)
|
||||
blk7 = cc.makeBlk(7, blk6)
|
||||
|
||||
dbTx.dispose()
|
||||
|
||||
let
|
||||
B4 = cc.makeBlk(4, blk3, 1.byte)
|
||||
B5 = cc.makeBlk(5, B4)
|
||||
B6 = cc.makeBlk(6, B5)
|
||||
B7 = cc.makeBlk(7, B6)
|
||||
|
||||
test "newBase == oldBase":
|
||||
let com = env.newCom()
|
||||
|
||||
var chain = newForkedChain(com, com.genesisHeader)
|
||||
check chain.importBlock(blk1).isOk
|
||||
|
||||
# same header twice
|
||||
check chain.importBlock(blk1).isOk
|
||||
|
||||
check chain.importBlock(blk2).isOk
|
||||
|
||||
check chain.importBlock(blk3).isOk
|
||||
|
||||
# no parent
|
||||
check chain.importBlock(blk5).isErr
|
||||
|
||||
check com.headHash == genesisHash
|
||||
check chain.latestHash == blk3.blockHash
|
||||
|
||||
# finalized > head -> error
|
||||
check chain.forkChoice(blk1.blockHash, blk3.blockHash).isErr
|
||||
|
||||
# blk4 is not part of chain
|
||||
check chain.forkChoice(blk4.blockHash, blk2.blockHash).isErr
|
||||
|
||||
# finalized > head -> error
|
||||
check chain.forkChoice(blk1.blockHash, blk2.blockHash).isErr
|
||||
|
||||
# blk4 is not part of chain
|
||||
check chain.forkChoice(blk2.blockHash, blk4.blockHash).isErr
|
||||
|
||||
# finalized < head -> ok
|
||||
check chain.forkChoice(blk2.blockHash, blk1.blockHash).isOk
|
||||
check com.headHash == blk2.blockHash
|
||||
check chain.latestHash == blk2.blockHash
|
||||
|
||||
# finalized == head -> ok
|
||||
check chain.forkChoice(blk2.blockHash, blk2.blockHash).isOk
|
||||
check com.headHash == blk2.blockHash
|
||||
check chain.latestHash == blk2.blockHash
|
||||
|
||||
# no baggage written
|
||||
check com.wdWritten(blk1) == 0
|
||||
check com.wdWritten(blk2) == 0
|
||||
|
||||
test "newBase == cursor":
|
||||
let com = env.newCom()
|
||||
|
||||
var chain = newForkedChain(com, com.genesisHeader, baseDistance = 3)
|
||||
check chain.importBlock(blk1).isOk
|
||||
check chain.importBlock(blk2).isOk
|
||||
check chain.importBlock(blk3).isOk
|
||||
check chain.importBlock(blk4).isOk
|
||||
check chain.importBlock(blk5).isOk
|
||||
check chain.importBlock(blk6).isOk
|
||||
check chain.importBlock(blk7).isOk
|
||||
|
||||
check chain.importBlock(blk4).isOk
|
||||
|
||||
# newbase == cursor
|
||||
check chain.forkChoice(blk7.blockHash, blk6.blockHash).isOk
|
||||
|
||||
check com.headHash == blk7.blockHash
|
||||
check chain.latestHash == blk7.blockHash
|
||||
|
||||
check com.wdWritten(blk7) == 0
|
||||
|
||||
# head - baseDistance must been finalized
|
||||
check com.wdWritten(blk4) == 4
|
||||
# make sure aristo not wiped out baggage
|
||||
check com.wdWritten(blk3) == 3
|
||||
|
||||
test "newBase between oldBase and cursor":
|
||||
let com = env.newCom()
|
||||
|
||||
var chain = newForkedChain(com, com.genesisHeader, baseDistance = 3)
|
||||
check chain.importBlock(blk1).isOk
|
||||
check chain.importBlock(blk2).isOk
|
||||
check chain.importBlock(blk3).isOk
|
||||
check chain.importBlock(blk4).isOk
|
||||
check chain.importBlock(blk5).isOk
|
||||
check chain.importBlock(blk6).isOk
|
||||
check chain.importBlock(blk7).isOk
|
||||
|
||||
check chain.forkChoice(blk7.blockHash, blk6.blockHash).isOk
|
||||
|
||||
check com.headHash == blk7.blockHash
|
||||
check chain.latestHash == blk7.blockHash
|
||||
|
||||
check com.wdWritten(blk6) == 0
|
||||
check com.wdWritten(blk7) == 0
|
||||
|
||||
# head - baseDistance must been finalized
|
||||
check com.wdWritten(blk4) == 4
|
||||
# make sure aristo not wiped out baggage
|
||||
check com.wdWritten(blk3) == 3
|
||||
|
||||
test "newBase == oldBase, fork and keep on that fork":
|
||||
let com = env.newCom()
|
||||
|
||||
var chain = newForkedChain(com, com.genesisHeader)
|
||||
check chain.importBlock(blk1).isOk
|
||||
check chain.importBlock(blk2).isOk
|
||||
check chain.importBlock(blk3).isOk
|
||||
check chain.importBlock(blk4).isOk
|
||||
check chain.importBlock(blk5).isOk
|
||||
check chain.importBlock(blk6).isOk
|
||||
check chain.importBlock(blk7).isOk
|
||||
|
||||
check chain.importBlock(B4).isOk
|
||||
check chain.importBlock(B5).isOk
|
||||
check chain.importBlock(B6).isOk
|
||||
check chain.importBlock(B7).isOk
|
||||
|
||||
check chain.forkChoice(B7.blockHash, B5.blockHash).isOk
|
||||
|
||||
check com.headHash == B7.blockHash
|
||||
check chain.latestHash == B7.blockHash
|
||||
|
||||
test "newBase == cursor, fork and keep on that fork":
|
||||
let com = env.newCom()
|
||||
|
||||
var chain = newForkedChain(com, com.genesisHeader, baseDistance = 3)
|
||||
check chain.importBlock(blk1).isOk
|
||||
check chain.importBlock(blk2).isOk
|
||||
check chain.importBlock(blk3).isOk
|
||||
check chain.importBlock(blk4).isOk
|
||||
check chain.importBlock(blk5).isOk
|
||||
check chain.importBlock(blk6).isOk
|
||||
check chain.importBlock(blk7).isOk
|
||||
|
||||
check chain.importBlock(B4).isOk
|
||||
check chain.importBlock(B5).isOk
|
||||
check chain.importBlock(B6).isOk
|
||||
check chain.importBlock(B7).isOk
|
||||
|
||||
check chain.importBlock(B4).isOk
|
||||
|
||||
check chain.forkChoice(B7.blockHash, B6.blockHash).isOk
|
||||
|
||||
check com.headHash == B7.blockHash
|
||||
check chain.latestHash == B7.blockHash
|
||||
|
||||
test "newBase between oldBase and cursor, fork and keep on that fork":
|
||||
let com = env.newCom()
|
||||
|
||||
var chain = newForkedChain(com, com.genesisHeader, baseDistance = 3)
|
||||
check chain.importBlock(blk1).isOk
|
||||
check chain.importBlock(blk2).isOk
|
||||
check chain.importBlock(blk3).isOk
|
||||
check chain.importBlock(blk4).isOk
|
||||
check chain.importBlock(blk5).isOk
|
||||
check chain.importBlock(blk6).isOk
|
||||
check chain.importBlock(blk7).isOk
|
||||
|
||||
check chain.importBlock(B4).isOk
|
||||
check chain.importBlock(B5).isOk
|
||||
check chain.importBlock(B6).isOk
|
||||
check chain.importBlock(B7).isOk
|
||||
|
||||
check chain.forkChoice(B7.blockHash, B5.blockHash).isOk
|
||||
|
||||
check com.headHash == B7.blockHash
|
||||
check chain.latestHash == B7.blockHash
|
||||
|
||||
test "newBase == oldBase, fork and return to old chain":
|
||||
let com = env.newCom()
|
||||
|
||||
var chain = newForkedChain(com, com.genesisHeader)
|
||||
check chain.importBlock(blk1).isOk
|
||||
check chain.importBlock(blk2).isOk
|
||||
check chain.importBlock(blk3).isOk
|
||||
check chain.importBlock(blk4).isOk
|
||||
check chain.importBlock(blk5).isOk
|
||||
check chain.importBlock(blk6).isOk
|
||||
check chain.importBlock(blk7).isOk
|
||||
|
||||
check chain.importBlock(B4).isOk
|
||||
check chain.importBlock(B5).isOk
|
||||
check chain.importBlock(B6).isOk
|
||||
check chain.importBlock(B7).isOk
|
||||
|
||||
check chain.forkChoice(blk7.blockHash, blk5.blockHash).isOk
|
||||
|
||||
check com.headHash == blk7.blockHash
|
||||
check chain.latestHash == blk7.blockHash
|
||||
|
||||
test "newBase == cursor, fork and return to old chain":
|
||||
let com = env.newCom()
|
||||
|
||||
var chain = newForkedChain(com, com.genesisHeader, baseDistance = 3)
|
||||
check chain.importBlock(blk1).isOk
|
||||
check chain.importBlock(blk2).isOk
|
||||
check chain.importBlock(blk3).isOk
|
||||
check chain.importBlock(blk4).isOk
|
||||
check chain.importBlock(blk5).isOk
|
||||
check chain.importBlock(blk6).isOk
|
||||
check chain.importBlock(blk7).isOk
|
||||
|
||||
check chain.importBlock(B4).isOk
|
||||
check chain.importBlock(B5).isOk
|
||||
check chain.importBlock(B6).isOk
|
||||
check chain.importBlock(B7).isOk
|
||||
|
||||
check chain.importBlock(blk4).isOk
|
||||
|
||||
check chain.forkChoice(blk7.blockHash, blk5.blockHash).isOk
|
||||
|
||||
check com.headHash == blk7.blockHash
|
||||
check chain.latestHash == blk7.blockHash
|
||||
|
||||
test "newBase between oldBase and cursor, fork and return to old chain, switch to new chain":
|
||||
let com = env.newCom()
|
||||
|
||||
var chain = newForkedChain(com, com.genesisHeader, baseDistance = 3)
|
||||
check chain.importBlock(blk1).isOk
|
||||
check chain.importBlock(blk2).isOk
|
||||
check chain.importBlock(blk3).isOk
|
||||
check chain.importBlock(blk4).isOk
|
||||
check chain.importBlock(blk5).isOk
|
||||
check chain.importBlock(blk6).isOk
|
||||
check chain.importBlock(blk7).isOk
|
||||
|
||||
check chain.importBlock(B4).isOk
|
||||
check chain.importBlock(B5).isOk
|
||||
check chain.importBlock(B6).isOk
|
||||
check chain.importBlock(B7).isOk
|
||||
|
||||
check chain.importBlock(blk4).isOk
|
||||
|
||||
check chain.forkChoice(B7.blockHash, B5.blockHash).isOk
|
||||
|
||||
check com.headHash == B7.blockHash
|
||||
check chain.latestHash == B7.blockHash
|
||||
|
||||
test "newBase between oldBase and cursor, fork and return to old chain":
|
||||
let com = env.newCom()
|
||||
|
||||
var chain = newForkedChain(com, com.genesisHeader, baseDistance = 3)
|
||||
check chain.importBlock(blk1).isOk
|
||||
check chain.importBlock(blk2).isOk
|
||||
check chain.importBlock(blk3).isOk
|
||||
check chain.importBlock(blk4).isOk
|
||||
check chain.importBlock(blk5).isOk
|
||||
check chain.importBlock(blk6).isOk
|
||||
check chain.importBlock(blk7).isOk
|
||||
|
||||
check chain.importBlock(B4).isOk
|
||||
check chain.importBlock(B5).isOk
|
||||
check chain.importBlock(B6).isOk
|
||||
check chain.importBlock(B7).isOk
|
||||
|
||||
check chain.forkChoice(blk7.blockHash, blk5.blockHash).isOk
|
||||
|
||||
check com.headHash == blk7.blockHash
|
||||
check chain.latestHash == blk7.blockHash
|
||||
|
||||
test "headerByNumber":
|
||||
let com = env.newCom()
|
||||
|
||||
var chain = newForkedChain(com, com.genesisHeader, baseDistance = 3)
|
||||
check chain.importBlock(blk1).isOk
|
||||
check chain.importBlock(blk2).isOk
|
||||
check chain.importBlock(blk3).isOk
|
||||
check chain.importBlock(blk4).isOk
|
||||
check chain.importBlock(blk5).isOk
|
||||
check chain.importBlock(blk6).isOk
|
||||
check chain.importBlock(blk7).isOk
|
||||
|
||||
check chain.importBlock(B4).isOk
|
||||
check chain.importBlock(B5).isOk
|
||||
check chain.importBlock(B6).isOk
|
||||
check chain.importBlock(B7).isOk
|
||||
|
||||
check chain.forkChoice(blk7.blockHash, blk5.blockHash).isOk
|
||||
|
||||
# cursor
|
||||
check chain.headerByNumber(8).isErr
|
||||
check chain.headerByNumber(7).expect("OK").number == 7
|
||||
check chain.headerByNumber(7).expect("OK").blockHash == blk7.blockHash
|
||||
|
||||
# from db
|
||||
check chain.headerByNumber(3).expect("OK").number == 3
|
||||
check chain.headerByNumber(3).expect("OK").blockHash == blk3.blockHash
|
||||
|
||||
# base
|
||||
check chain.headerByNumber(4).expect("OK").number == 4
|
||||
check chain.headerByNumber(4).expect("OK").blockHash == blk4.blockHash
|
||||
|
||||
# from cache
|
||||
check chain.headerByNumber(5).expect("OK").number == 5
|
||||
check chain.headerByNumber(5).expect("OK").blockHash == blk5.blockHash
|
||||
|
||||
when isMainModule:
|
||||
forkedChainMain()
|
Loading…
Reference in New Issue