mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-23 01:08:26 +00:00
add beacon sync skeleton test
This commit is contained in:
parent
86f6d284aa
commit
06249abc01
@ -52,6 +52,10 @@ type
|
||||
|
||||
blockZeroStateRoot: KeccakHash
|
||||
|
||||
validateBlock: bool ##\
|
||||
## If turn off, `persistBlocks` will always return
|
||||
## ValidationResult.OK and disable extraValidation too.
|
||||
|
||||
extraValidation: bool ##\
|
||||
## Trigger extra validation, currently within `persistBlocks()`
|
||||
## function only.
|
||||
@ -167,6 +171,7 @@ proc initChain(c: Chain; db: BaseChainDB; poa: Clique; extraValidation: bool)
|
||||
|
||||
if not db.config.daoForkSupport:
|
||||
db.config.daoForkBlock = db.config.homesteadBlock
|
||||
c.validateBlock = true
|
||||
c.extraValidation = extraValidation
|
||||
c.setForkId()
|
||||
|
||||
@ -246,6 +251,10 @@ proc db*(c: Chain): BaseChainDB =
|
||||
## Getter
|
||||
c.db
|
||||
|
||||
proc validateBlock*(c: Chain): bool =
|
||||
## Getter
|
||||
c.validateBlock
|
||||
|
||||
proc extraValidation*(c: Chain): bool =
|
||||
## Getter
|
||||
c.extraValidation
|
||||
@ -268,6 +277,10 @@ proc currentBlock*(c: Chain): BlockHeader
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public `Chain` setters
|
||||
# ------------------------------------------------------------------------------
|
||||
proc `validateBlock=`*(c: Chain; validateBlock: bool) =
|
||||
## Setter. If set `true`, the assignment value `validateBlock` enables
|
||||
## block execution, else it will always return ValidationResult.OK
|
||||
c.validateBlock = validateBlock
|
||||
|
||||
proc `extraValidation=`*(c: Chain; extraValidation: bool) =
|
||||
## Setter. If set `true`, the assignment value `extraValidation` enables
|
||||
|
@ -76,7 +76,10 @@ proc persistBlocksImpl(c: Chain; headers: openArray[BlockHeader];
|
||||
return ValidationResult.Error
|
||||
|
||||
let
|
||||
validationResult = vmState.processBlock(c.clique, header, body)
|
||||
validationResult = if c.validateBlock:
|
||||
vmState.processBlock(c.clique, header, body)
|
||||
else:
|
||||
ValidationResult.OK
|
||||
when not defined(release):
|
||||
if validationResult == ValidationResult.Error and
|
||||
body.transactions.calcTxRoot == header.txRoot:
|
||||
@ -86,7 +89,8 @@ proc persistBlocksImpl(c: Chain; headers: openArray[BlockHeader];
|
||||
if validationResult != ValidationResult.OK:
|
||||
return validationResult
|
||||
|
||||
if c.extraValidation and c.verifyFrom <= header.blockNumber:
|
||||
if c.validateBlock and c.extraValidation and
|
||||
c.verifyFrom <= header.blockNumber:
|
||||
let isBlockAfterTtd = c.isBlockAfterTtd(header)
|
||||
if c.db.config.poaEngine and not isBlockAfterTtd:
|
||||
var parent = if 0 < i: @[headers[i-1]] else: @[]
|
||||
|
33
tests/customgenesis/post-merge.json
Normal file
33
tests/customgenesis/post-merge.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"config": {
|
||||
"chainId": 1,
|
||||
"homesteadBlock": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"byzantiumBlock": 0,
|
||||
"constantinopleBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"terminalTotalDifficulty": 0
|
||||
},
|
||||
"genesis": {
|
||||
"nonce": "0x42",
|
||||
"timestamp": "0x0",
|
||||
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"gasLimit": "0x1C9C380",
|
||||
"difficulty": "0x400000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"alloc": {
|
||||
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { "balance": "0x6d6172697573766477000000" }
|
||||
},
|
||||
"number": "0x0",
|
||||
"gasUsed": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"baseFeePerGas": "0x7"
|
||||
}
|
||||
}
|
648
tests/test_skeleton.nim
Normal file
648
tests/test_skeleton.nim
Normal file
@ -0,0 +1,648 @@
|
||||
import
|
||||
unittest2,
|
||||
chronicles,
|
||||
stew/[results, byteutils],
|
||||
eth/[common, trie/db],
|
||||
../nimbus/sync/skeleton,
|
||||
../nimbus/db/db_chain,
|
||||
../nimbus/p2p/chain,
|
||||
../nimbus/[chain_config, config, genesis, constants],
|
||||
./test_helpers,
|
||||
./test_txpool/helpers
|
||||
|
||||
const
|
||||
baseDir = [".", "tests"]
|
||||
repoDir = [".", "customgenesis"]
|
||||
genesisFile = "post-merge.json"
|
||||
|
||||
type
|
||||
Subchain = object
|
||||
head: int
|
||||
tail: int
|
||||
|
||||
TestEnv = object
|
||||
conf : NimbusConf
|
||||
chainDB : BaseChainDB
|
||||
chain : Chain
|
||||
|
||||
CCModify = proc(cc: NetworkParams)
|
||||
|
||||
# TODO: too bad that blockHash
|
||||
# cannot be executed at compile time
|
||||
let
|
||||
block49 = BlockHeader(
|
||||
blockNumber: 49.toBlockNumber
|
||||
)
|
||||
block49B = BlockHeader(
|
||||
blockNumber: 49.toBlockNumber,
|
||||
extraData: @['B'.byte]
|
||||
)
|
||||
block50 = BlockHeader(
|
||||
blockNumber: 50.toBlockNumber,
|
||||
parentHash: block49.blockHash
|
||||
)
|
||||
block51 = BlockHeader(
|
||||
blockNumber: 51.toBlockNumber,
|
||||
parentHash: block50.blockHash
|
||||
)
|
||||
|
||||
proc initEnv(ccm: CCModify = nil): TestEnv =
|
||||
let
|
||||
conf = makeConfig(@[
|
||||
"--custom-network:" & genesisFile.findFilePath(baseDir,repoDir).value
|
||||
])
|
||||
|
||||
if ccm.isNil.not:
|
||||
ccm(conf.networkParams)
|
||||
|
||||
let
|
||||
chainDB = newBaseChainDB(
|
||||
newMemoryDb(),
|
||||
conf.pruneMode == PruneMode.Full,
|
||||
conf.networkId,
|
||||
conf.networkParams
|
||||
)
|
||||
chain = newChain(chainDB)
|
||||
|
||||
initializeEmptyDb(chainDB)
|
||||
|
||||
result = TestEnv(
|
||||
conf: conf,
|
||||
chainDB: chainDB,
|
||||
chain: chain
|
||||
)
|
||||
|
||||
proc `subchains=`(sk: SkeletonRef, subchains: openArray[Subchain]) =
|
||||
var sc = newSeqOfCap[SkeletonSubchain](subchains.len)
|
||||
for i in 0..<subchains.len:
|
||||
let x = subchains[i]
|
||||
sc.add(SkeletonSubchain(
|
||||
head: x.head.toBlockNumber,
|
||||
tail: x.tail.toBlockNumber
|
||||
))
|
||||
sk.subchains = sc
|
||||
|
||||
suite "syncInit":
|
||||
# Tests various sync initializations based on previous leftovers in the database
|
||||
# and announced heads.
|
||||
|
||||
type
|
||||
TestCase = object
|
||||
blocks : seq[BlockHeader] # Database content (besides the genesis)
|
||||
oldState: seq[Subchain] # Old sync state with various interrupted subchains
|
||||
head : BlockHeader # New head header to announce to reorg to
|
||||
newState: seq[Subchain] # Expected sync state after the reorg
|
||||
|
||||
let testCases = [
|
||||
# Completely empty database with only the genesis set. The sync is expected
|
||||
# to create a single subchain with the requested head.
|
||||
TestCase(
|
||||
head: block50,
|
||||
newState: @[SubChain(head: 50, tail: 50)]
|
||||
),
|
||||
# Empty database with only the genesis set with a leftover empty sync
|
||||
# progress. This is a synthetic case, just for the sake of covering things.
|
||||
TestCase(
|
||||
head: block50,
|
||||
newState: @[SubChain(head: 50, tail: 50)]
|
||||
),
|
||||
# A single leftover subchain is present, older than the new head. The
|
||||
# old subchain should be left as is and a new one appended to the sync
|
||||
# status.
|
||||
TestCase(
|
||||
oldState: @[SubChain(head: 10, tail: 5)],
|
||||
head: block50,
|
||||
newState: @[
|
||||
SubChain(head: 50, tail: 50),
|
||||
SubChain(head: 10, tail: 5)
|
||||
]
|
||||
),
|
||||
# Multiple leftover subchains are present, older than the new head. The
|
||||
# old subchains should be left as is and a new one appended to the sync
|
||||
# status.
|
||||
TestCase(
|
||||
oldState: @[
|
||||
SubChain(head: 20, tail: 15),
|
||||
SubChain(head: 10, tail: 5)
|
||||
],
|
||||
head: block50,
|
||||
newState: @[
|
||||
SubChain(head: 50, tail: 50),
|
||||
SubChain(head: 20, tail: 15),
|
||||
SubChain(head: 10, tail: 5)
|
||||
]
|
||||
),
|
||||
# A single leftover subchain is present, newer than the new head. The
|
||||
# newer subchain should be deleted and a fresh one created for the head.
|
||||
TestCase(
|
||||
oldState: @[SubChain(head: 65, tail: 60)],
|
||||
head: block50,
|
||||
newState: @[SubChain(head: 50, tail: 50)]
|
||||
),
|
||||
# Multiple leftover subchain is present, newer than the new head. The
|
||||
# newer subchains should be deleted and a fresh one created for the head.
|
||||
TestCase(
|
||||
oldState: @[
|
||||
SubChain(head: 75, tail: 70),
|
||||
SubChain(head: 65, tail: 60)
|
||||
],
|
||||
head: block50,
|
||||
newState: @[SubChain(head: 50, tail: 50)]
|
||||
),
|
||||
# Two leftover subchains are present, one fully older and one fully
|
||||
# newer than the announced head. The head should delete the newer one,
|
||||
# keeping the older one.
|
||||
TestCase(
|
||||
oldState: @[
|
||||
SubChain(head: 65, tail: 60),
|
||||
SubChain(head: 10, tail: 5),
|
||||
],
|
||||
head: block50,
|
||||
newState: @[
|
||||
SubChain(head: 50, tail: 50),
|
||||
SubChain(head: 10, tail: 5),
|
||||
],
|
||||
),
|
||||
# Multiple leftover subchains are present, some fully older and some
|
||||
# fully newer than the announced head. The head should delete the newer
|
||||
# ones, keeping the older ones.
|
||||
TestCase(
|
||||
oldState: @[
|
||||
SubChain(head: 75, tail: 70),
|
||||
SubChain(head: 65, tail: 60),
|
||||
SubChain(head: 20, tail: 15),
|
||||
SubChain(head: 10, tail: 5),
|
||||
],
|
||||
head: block50,
|
||||
newState: @[
|
||||
SubChain(head: 50, tail: 50),
|
||||
SubChain(head: 20, tail: 15),
|
||||
SubChain(head: 10, tail: 5),
|
||||
],
|
||||
),
|
||||
# A single leftover subchain is present and the new head is extending
|
||||
# it with one more header. We expect the subchain head to be pushed
|
||||
# forward.
|
||||
TestCase(
|
||||
blocks: @[block49],
|
||||
oldState: @[SubChain(head: 49, tail: 5)],
|
||||
head: block50,
|
||||
newState: @[SubChain(head: 50, tail: 5)]
|
||||
),
|
||||
# A single leftover subchain is present. A new head is announced that
|
||||
# links into the middle of it, correctly anchoring into an existing
|
||||
# header. We expect the old subchain to be truncated and extended with
|
||||
# the new head.
|
||||
TestCase(
|
||||
blocks: @[block49],
|
||||
oldState: @[SubChain(head: 100, tail: 5)],
|
||||
head: block50,
|
||||
newState: @[SubChain(head: 50, tail: 5)]
|
||||
),
|
||||
# A single leftover subchain is present. A new head is announced that
|
||||
# links into the middle of it, but does not anchor into an existing
|
||||
# header. We expect the old subchain to be truncated and a new chain
|
||||
# be created for the dangling head.
|
||||
TestCase(
|
||||
blocks: @[block49B],
|
||||
oldState: @[SubChain(head: 100, tail: 5)],
|
||||
head: block50,
|
||||
newState: @[
|
||||
SubChain(head: 50, tail: 50),
|
||||
SubChain(head: 49, tail: 5),
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
for z, testCase in testCases:
|
||||
test "test case #" & $z:
|
||||
let env = initEnv()
|
||||
let skeleton = SkeletonRef.new(env.chain)
|
||||
skeleton.open()
|
||||
|
||||
for header in testCase.blocks:
|
||||
skeleton.putHeader(header)
|
||||
|
||||
if testCase.oldState.len > 0:
|
||||
skeleton.subchains = testCase.oldState
|
||||
|
||||
skeleton.initSync(testCase.head)
|
||||
|
||||
check skeleton.len == testCase.newState.len
|
||||
for i, sc in skeleton:
|
||||
check sc.head == testCase.newState[i].head.toBlockNumber
|
||||
check sc.tail == testCase.newState[i].tail.toBlockNumber
|
||||
|
||||
suite "sync extend":
|
||||
type
|
||||
TestCase = object
|
||||
head : BlockHeader # New head header to announce to reorg to
|
||||
extend : BlockHeader # New head header to announce to extend with
|
||||
newState: seq[Subchain] # Expected sync state after the reorg
|
||||
err : string # Whether extension succeeds or not
|
||||
|
||||
let testCases = [
|
||||
# Initialize a sync and try to extend it with a subsequent block.
|
||||
TestCase(
|
||||
head: block49,
|
||||
extend: block50,
|
||||
newState: @[Subchain(head: 50, tail: 49)],
|
||||
),
|
||||
# Initialize a sync and try to extend it with the existing head block.
|
||||
TestCase(
|
||||
head: block49,
|
||||
extend: block49,
|
||||
newState: @[Subchain(head: 49, tail: 49)],
|
||||
),
|
||||
# Initialize a sync and try to extend it with a sibling block.
|
||||
TestCase(
|
||||
head: block49,
|
||||
extend: block49B,
|
||||
newState: @[Subchain(head: 49, tail: 49)],
|
||||
err: "ErrReorgDenied",
|
||||
),
|
||||
# Initialize a sync and try to extend it with a number-wise sequential
|
||||
# header, but a hash wise non-linking one.
|
||||
TestCase(
|
||||
head: block49B,
|
||||
extend: block50,
|
||||
newState: @[Subchain(head: 49, tail: 49)],
|
||||
err: "ErrReorgDenied",
|
||||
),
|
||||
# Initialize a sync and try to extend it with a non-linking future block.
|
||||
TestCase(
|
||||
head: block49,
|
||||
extend: block51,
|
||||
newState: @[Subchain(head: 49, tail: 49)],
|
||||
err: "ErrReorgDenied",
|
||||
),
|
||||
# Initialize a sync and try to extend it with a past canonical block.
|
||||
TestCase(
|
||||
head: block50,
|
||||
extend: block49,
|
||||
newState: @[Subchain(head: 50, tail: 50)],
|
||||
err: "ErrReorgDenied",
|
||||
),
|
||||
# Initialize a sync and try to extend it with a past sidechain block.
|
||||
TestCase(
|
||||
head: block50,
|
||||
extend: block49B,
|
||||
newState: @[Subchain(head: 50, tail: 50)],
|
||||
err: "ErrReorgDenied",
|
||||
)
|
||||
]
|
||||
|
||||
for z, testCase in testCases:
|
||||
test "test case #" & $z:
|
||||
let env = initEnv()
|
||||
let skeleton = SkeletonRef.new(env.chain)
|
||||
skeleton.open()
|
||||
|
||||
skeleton.initSync(testCase.head)
|
||||
|
||||
try:
|
||||
skeleton.setHead(testCase.extend)
|
||||
check testCase.err.len == 0
|
||||
except Exception as e:
|
||||
check testCase.err.len > 0
|
||||
check testCase.err == e.name
|
||||
|
||||
check skeleton.len == testCase.newState.len
|
||||
for i, sc in skeleton:
|
||||
check sc.head == testCase.newState[i].head.toBlockNumber
|
||||
check sc.tail == testCase.newState[i].tail.toBlockNumber
|
||||
|
||||
|
||||
template testCond(expr: untyped) =
|
||||
if not (expr):
|
||||
return TestStatus.Failed
|
||||
|
||||
template testCond(expr, body: untyped) =
|
||||
if not (expr):
|
||||
body
|
||||
return TestStatus.Failed
|
||||
|
||||
proc linkedToGenesis(env: TestEnv): TestStatus =
|
||||
result = TestStatus.OK
|
||||
env.chain.validateBlock = false
|
||||
let skeleton = SkeletonRef.new(env.chain)
|
||||
|
||||
let
|
||||
genesis = env.chainDB.getCanonicalHead()
|
||||
block1 = BlockHeader(
|
||||
blockNumber: 1.toBlockNumber, parentHash: genesis.blockHash, difficulty: 100.u256
|
||||
)
|
||||
block2 = BlockHeader(
|
||||
blockNumber: 2.toBlockNumber, parentHash: block1.blockHash, difficulty: 100.u256
|
||||
)
|
||||
block3 = BlockHeader(
|
||||
blockNumber: 3.toBlockNumber, parentHash: block2.blockHash, difficulty: 100.u256
|
||||
)
|
||||
block4 = BlockHeader(
|
||||
blockNumber: 4.toBlockNumber, parentHash: block3.blockHash, difficulty: 100.u256
|
||||
)
|
||||
block5 = BlockHeader(
|
||||
blockNumber: 5.toBlockNumber, parentHash: block4.blockHash, difficulty: 100.u256
|
||||
)
|
||||
|
||||
skeleton.open()
|
||||
skeleton.initSync(block4)
|
||||
|
||||
skeleton.ignoreTxs = true
|
||||
discard skeleton.putBlocks([block3, block2])
|
||||
testCond env.chainDB.currentBlock == 0.toBlockNumber:
|
||||
error "canonical height should be at genesis"
|
||||
|
||||
discard skeleton.putBlocks([block1])
|
||||
testCond env.chainDB.currentBlock == 4.toBlockNumber:
|
||||
error "canonical height should update after being linked"
|
||||
|
||||
skeleton.setHead(block5, false)
|
||||
testCond env.chainDB.currentBlock == 4.toBlockNumber:
|
||||
error "canonical height should not change when setHead is set with force=false"
|
||||
|
||||
skeleton.setHead(block5, true)
|
||||
testCond env.chainDB.currentBlock == 5.toBlockNumber:
|
||||
error "canonical height should change when setHead is set with force=true"
|
||||
|
||||
var h: BlockHeader
|
||||
for header in [block1, block2, block3, block4, block5]:
|
||||
var res = skeleton.getHeader(header.blockNumber, h, true)
|
||||
testCond res == false:
|
||||
error "skeleton block should be cleaned up after filling canonical chain",
|
||||
number=header.blockNumber
|
||||
|
||||
res = skeleton.getHeaderByHash(header.blockHash, h)
|
||||
testCond res == false:
|
||||
error "skeleton block should be cleaned up after filling canonical chain",
|
||||
number=header.blockNumber
|
||||
|
||||
proc linkedPastGenesis(env: TestEnv): TestStatus =
|
||||
result = TestStatus.OK
|
||||
env.chain.validateBlock = false
|
||||
let skeleton = SkeletonRef.new(env.chain)
|
||||
|
||||
skeleton.open()
|
||||
let
|
||||
genesis = env.chainDB.getCanonicalHead()
|
||||
block1 = BlockHeader(
|
||||
blockNumber: 1.toBlockNumber, parentHash: genesis.blockHash, difficulty: 100.u256
|
||||
)
|
||||
block2 = BlockHeader(
|
||||
blockNumber: 2.toBlockNumber, parentHash: block1.blockHash, difficulty: 100.u256
|
||||
)
|
||||
block3 = BlockHeader(
|
||||
blockNumber: 3.toBlockNumber, parentHash: block2.blockHash, difficulty: 100.u256
|
||||
)
|
||||
block4 = BlockHeader(
|
||||
blockNumber: 4.toBlockNumber, parentHash: block3.blockHash, difficulty: 100.u256
|
||||
)
|
||||
block5 = BlockHeader(
|
||||
blockNumber: 5.toBlockNumber, parentHash: block4.blockHash, difficulty: 100.u256
|
||||
)
|
||||
|
||||
var body: BlockBody
|
||||
let vr = env.chain.persistBlocks([block1, block2], [body, body])
|
||||
testCond vr == ValidationResult.OK
|
||||
|
||||
skeleton.initSync(block4)
|
||||
testCond env.chainDB.currentBlock == 2.toBlockNumber:
|
||||
error "canonical height should be at block 2"
|
||||
|
||||
skeleton.ignoreTxs = true
|
||||
discard skeleton.putBlocks([block3])
|
||||
testCond env.chainDB.currentBlock == 4.toBlockNumber:
|
||||
error "canonical height should update after being linked"
|
||||
|
||||
skeleton.setHead(block5, false)
|
||||
testCond env.chainDB.currentBlock == 4.toBlockNumber:
|
||||
error "canonical height should not change when setHead with force=false"
|
||||
|
||||
skeleton.setHead(block5, true)
|
||||
testCond env.chainDB.currentBlock == 5.toBlockNumber:
|
||||
error "canonical height should change when setHead with force=true"
|
||||
|
||||
var h: BlockHeader
|
||||
for header in [block3, block4, block5]:
|
||||
var res = skeleton.getHeader(header.blockNumber, h, true)
|
||||
testCond res == false:
|
||||
error "skeleton block should be cleaned up after filling canonical chain",
|
||||
number=header.blockNumber
|
||||
|
||||
res = skeleton.getHeaderByHash(header.blockHash, h)
|
||||
testCond res == false:
|
||||
error "skeleton block should be cleaned up after filling canonical chain",
|
||||
number=header.blockNumber
|
||||
|
||||
proc ccmAbortTerminalInvalid(cc: NetworkParams) =
|
||||
cc.config.terminalTotalDifficulty = some(200.u256)
|
||||
cc.genesis.extraData = hexToSeqByte("0x000000000000000000")
|
||||
cc.genesis.difficulty = UInt256.fromHex("0x01")
|
||||
|
||||
proc abortTerminalInvalid(env: TestEnv): TestStatus =
|
||||
result = TestStatus.OK
|
||||
env.chain.validateBlock = false
|
||||
let skeleton = SkeletonRef.new(env.chain)
|
||||
|
||||
let
|
||||
genesisBlock = env.chainDB.getCanonicalHead()
|
||||
block1 = BlockHeader(
|
||||
blockNumber: 1.toBlockNumber, parentHash: genesisBlock.blockHash, difficulty: 100.u256
|
||||
)
|
||||
block2 = BlockHeader(
|
||||
blockNumber: 2.toBlockNumber, parentHash: block1.blockHash, difficulty: 100.u256
|
||||
)
|
||||
block3PoW = BlockHeader(
|
||||
blockNumber: 3.toBlockNumber, parentHash: block2.blockHash, difficulty: 100.u256
|
||||
)
|
||||
block3PoS = BlockHeader(
|
||||
blockNumber: 3.toBlockNumber, parentHash: block2.blockHash, difficulty: 0.u256
|
||||
#{ common, hardforkByTTD: BigInt(200) }
|
||||
)
|
||||
block4InvalidPoS = BlockHeader(
|
||||
blockNumber: 4.toBlockNumber, parentHash: block3PoW.blockHash, difficulty: 0.u256
|
||||
#{ common, hardforkByTTD: BigInt(200) }
|
||||
)
|
||||
block4PoS = BlockHeader(
|
||||
blockNumber: 4.toBlockNumber, parentHash: block3PoS.blockHash, difficulty: 0.u256
|
||||
#{ common, hardforkByTTD: BigInt(200) }
|
||||
)
|
||||
block5 = BlockHeader(
|
||||
blockNumber: 5.toBlockNumber, parentHash: block4PoS.blockHash, difficulty: 0.u256
|
||||
#{ common, hardforkByTTD: BigInt(200) }
|
||||
)
|
||||
|
||||
skeleton.ignoreTxs = true
|
||||
skeleton.open()
|
||||
skeleton.initSync(block4InvalidPoS)
|
||||
|
||||
discard skeleton.putBlocks([block3PoW, block2])
|
||||
testCond env.chainDB.currentBlock == 0.toBlockNumber:
|
||||
error "canonical height should be at genesis"
|
||||
|
||||
discard skeleton.putBlocks([block1])
|
||||
testCond env.chainDB.currentBlock == 2.toBlockNumber:
|
||||
error "canonical height should stop at block 2 (valid terminal block), since block 3 is invalid (past ttd)"
|
||||
|
||||
try:
|
||||
skeleton.setHead(block5, false)
|
||||
except ErrReorgDenied:
|
||||
testCond true
|
||||
except:
|
||||
testCond false
|
||||
|
||||
testCond env.chainDB.currentBlock == 2.toBlockNumber:
|
||||
error "canonical height should not change when setHead is set with force=false"
|
||||
|
||||
# Put correct chain
|
||||
skeleton.initSync(block4PoS)
|
||||
try:
|
||||
discard skeleton.putBlocks([block3PoS])
|
||||
except ErrSyncMerged:
|
||||
testCond true
|
||||
except:
|
||||
testCond false
|
||||
|
||||
testCond env.chainDB.currentBlock == 4.toBlockNumber:
|
||||
error "canonical height should now be at head with correct chain"
|
||||
|
||||
var header: BlockHeader
|
||||
testCond env.chainDB.getBlockHeader(env.chainDB.highestBlock, header):
|
||||
error "cannot get block header", number = env.chainDB.highestBlock
|
||||
|
||||
testCond header.blockHash == block4PoS.blockHash:
|
||||
error "canonical height should now be at head with correct chain"
|
||||
|
||||
skeleton.setHead(block5, false)
|
||||
testCond skeleton.bounds().head == 5.toBlockNumber:
|
||||
error "should update to new height"
|
||||
|
||||
proc ccmAbortAndBackstep(cc: NetworkParams) =
|
||||
cc.config.terminalTotalDifficulty = some(200.u256)
|
||||
cc.genesis.extraData = hexToSeqByte("0x000000000000000000")
|
||||
cc.genesis.difficulty = UInt256.fromHex("0x01")
|
||||
|
||||
proc abortAndBackstep(env: TestEnv): TestStatus =
|
||||
result = TestStatus.OK
|
||||
env.chain.validateBlock = false
|
||||
let skeleton = SkeletonRef.new(env.chain)
|
||||
|
||||
let
|
||||
genesisBlock = env.chainDB.getCanonicalHead()
|
||||
block1 = BlockHeader(
|
||||
blockNumber: 1.toBlockNumber, parentHash: genesisBlock.blockHash, difficulty: 100.u256
|
||||
)
|
||||
block2 = BlockHeader(
|
||||
blockNumber: 2.toBlockNumber, parentHash: block1.blockHash, difficulty: 100.u256
|
||||
)
|
||||
block3PoW = BlockHeader(
|
||||
blockNumber: 3.toBlockNumber, parentHash: block2.blockHash, difficulty: 100.u256
|
||||
)
|
||||
block4InvalidPoS = BlockHeader(
|
||||
blockNumber: 4.toBlockNumber, parentHash: block3PoW.blockHash, difficulty: 0.u256
|
||||
#{ common, hardforkByTTD: 200 }
|
||||
)
|
||||
|
||||
skeleton.open()
|
||||
skeleton.ignoreTxs = true
|
||||
skeleton.initSync(block4InvalidPoS)
|
||||
discard skeleton.putBlocks([block3PoW, block2])
|
||||
|
||||
testCond env.chainDB.currentBlock == 0.toBlockNumber:
|
||||
error "canonical height should be at genesis"
|
||||
|
||||
discard skeleton.putBlocks([block1])
|
||||
testCond env.chainDB.currentBlock == 2.toBlockNumber:
|
||||
error "canonical height should stop at block 2 (valid terminal block), since block 3 is invalid (past ttd)"
|
||||
|
||||
testCond skeleton.bounds().tail == 4.toBlockNumber:
|
||||
error "Subchain should have been backstepped to 4"
|
||||
|
||||
proc ccmAbortPOSTooEarly(cc: NetworkParams) =
|
||||
cc.config.terminalTotalDifficulty = some(200.u256)
|
||||
#skeletonFillCanonicalBackStep: 0,
|
||||
cc.genesis.difficulty = UInt256.fromHex("0x01")
|
||||
|
||||
proc abortPOSTooEarly(env: TestEnv): TestStatus =
|
||||
result = TestStatus.OK
|
||||
env.chain.validateBlock = false
|
||||
let skeleton = SkeletonRef.new(env.chain)
|
||||
|
||||
let
|
||||
genesisBlock = env.chainDB.getCanonicalHead()
|
||||
block1 = BlockHeader(
|
||||
blockNumber: 1.toBlockNumber, parentHash: genesisBlock.blockHash, difficulty: 100.u256
|
||||
)
|
||||
block2 = BlockHeader(
|
||||
blockNumber: 2.toBlockNumber, parentHash: block1.blockHash, difficulty: 100.u256
|
||||
)
|
||||
block2PoS = BlockHeader(
|
||||
blockNumber: 2.toBlockNumber, parentHash: block1.blockHash, difficulty: 0.u256
|
||||
)
|
||||
block3 = BlockHeader(
|
||||
blockNumber: 3.toBlockNumber, parentHash: block2.blockHash, difficulty: 0.u256
|
||||
)
|
||||
|
||||
skeleton.ignoreTxs = true
|
||||
skeleton.open()
|
||||
skeleton.initSync(block2PoS)
|
||||
discard skeleton.putBlocks([block1])
|
||||
|
||||
testCond env.chainDB.currentBlock == 1.toBlockNumber:
|
||||
error "canonical height should stop at block 1 (valid PoW block), since block 2 is invalid (invalid PoS, not past ttd)"
|
||||
|
||||
# Put correct chain
|
||||
skeleton.initSync(block3)
|
||||
try:
|
||||
discard skeleton.putBlocks([block2])
|
||||
except ErrSyncMerged:
|
||||
testCond true
|
||||
except:
|
||||
testCond false
|
||||
|
||||
testCond env.chainDB.currentBlock == 3.toBlockNumber:
|
||||
error "canonical height should now be at head with correct chain"
|
||||
|
||||
var header: BlockHeader
|
||||
testCond env.chainDB.getBlockHeader(env.chainDB.highestBlock, header):
|
||||
error "cannot get block header", number = env.chainDB.highestBlock
|
||||
|
||||
testCond header.blockHash == block3.blockHash:
|
||||
error "canonical height should now be at head with correct chain"
|
||||
|
||||
suite "fillCanonicalChain tests":
|
||||
type
|
||||
TestCase = object
|
||||
name: string
|
||||
ccm : CCModify
|
||||
run : proc(env: TestEnv): TestStatus
|
||||
|
||||
const testCases = [
|
||||
TestCase(
|
||||
name: "should fill the canonical chain after being linked to genesis",
|
||||
run : linkedToGenesis
|
||||
),
|
||||
TestCase(
|
||||
name: "should fill the canonical chain after being linked to a canonical block past genesis",
|
||||
run : linkedPastGenesis
|
||||
),
|
||||
TestCase(
|
||||
name: "should abort filling the canonical chain if the terminal block is invalid",
|
||||
ccm : ccmAbortTerminalInvalid,
|
||||
run : abortTerminalInvalid
|
||||
),
|
||||
TestCase(
|
||||
name: "should abort filling the canonical chain and backstep if the terminal block is invalid",
|
||||
ccm : ccmAbortAndBackstep,
|
||||
run : abortAndBackstep
|
||||
),
|
||||
TestCase(
|
||||
name: "should abort filling the canonical chain if a PoS block comes too early without hitting ttd",
|
||||
ccm : ccmAbortPOSTooEarly,
|
||||
run : abortPOSTooEarly
|
||||
)
|
||||
]
|
||||
for testCase in testCases:
|
||||
test testCase.name:
|
||||
let env = initEnv(testCase.ccm)
|
||||
check testCase.run(env) == TestStatus.OK
|
Loading…
x
Reference in New Issue
Block a user