# Nimbus # Copyright (c) 2023 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at # https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at # https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed # except according to those terms. import unittest2, ./setup_env, ../../nimbus/sync/beacon/skeleton_main, ../../nimbus/sync/beacon/skeleton_utils, ../../nimbus/sync/beacon/skeleton_db type TestCase = object name : string 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 reorg : bool let testCases = [ # The sync is expected to create a single subchain with the requested head. TestCase( name: "Completely empty database with only the genesis set.", head: block50, newState: @[subchain(50, 50)], reorg: true ), # This is a synthetic case, just for the sake of covering things. TestCase( name: "Empty database with only the genesis set with a leftover empty sync progress", head: block50, newState: @[subchain(50, 50)], reorg: true ), # The old subchain should be left as is and a new one appended to the sync status. TestCase( name: "A single leftover subchain is present, older than the new head.", oldState: @[subchain(10, 5)], head: block50, newState: @[ subchain(10, 5), subchain(50, 50), ], reorg: true ), # The old subchains should be left as is and a new one appended to the sync status. TestCase( name: "Multiple leftover subchains are present, older than the new head.", oldState: @[ subchain(10, 5), subchain(20, 15), ], head: block50, newState: @[ subchain(10, 5), subchain(20, 15), subchain(50, 50), ], reorg: true ), # The newer subchain should be deleted and a fresh one created for the head. TestCase( name: "A single leftover subchain is present, newer than the new head.", oldState: @[subchain(65, 60)], head: block50, newState: @[subchain(50, 50)], reorg: true ), # The newer subchains should be deleted and a fresh one created for the head. TestCase( name: "Multiple leftover subchain is present, newer than the new head.", oldState: @[ subchain(65, 60), subchain(75, 70), ], head: block50, newState: @[subchain(50, 50)], reorg: true ), # than the announced head. The head should delete the newer one, # keeping the older one. TestCase( name: "Two leftover subchains are present, one fully older and one fully newer", oldState: @[ subchain(10, 5), subchain(65, 60), ], head: block50, newState: @[ subchain(10, 5), subchain(50, 50), ], reorg: true ), # than the announced head. The head should delete the newer # ones, keeping the older ones. TestCase( name: "Multiple leftover subchains are present, some fully older and some fully newer", oldState: @[ subchain(10, 5), subchain(20, 15), subchain(65, 60), subchain(75, 70), ], head: block50, newState: @[ subchain(10, 5), subchain(20, 15), subchain(50, 50), ], reorg: true ), # it with one more header. We expect the subchain head to be pushed forward. TestCase( name: "A single leftover subchain is present and the new head is extending", blocks: @[block49], oldState: @[subchain(49, 5)], head: block50, newState: @[subchain(50, 5)], reorg: false ), # 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( name: "Duplicate announcement should not modify subchain", blocks: @[block49, block50], oldState: @[subchain(100, 5)], head: block50, newState: @[subchain(100, 5)], reorg: false ), # 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( name: "A new alternate head is announced in the middle should truncate subchain", blocks: @[block49, block50], oldState: @[subchain(100, 5)], head: block50B, newState: @[subchain(50, 5)], reorg: true ), # 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( name: "The old subchain to be truncated and a new chain be created for the dangling head", blocks: @[block49B], oldState: @[subchain(100, 5)], head: block50, newState: @[ subchain(49, 5), subchain(50, 50), ], reorg: true ), ] proc test1*() = suite "Tests various sync initializations": # based on previous leftovers in the database # and announced heads. for z in testCases: test z.name: let env = setupEnv() let skel = SkeletonRef.new(env.chain) let res = skel.open() check res.isOk if res.isErr: debugEcho res.error break for header in z.blocks: skel.putHeader(header) for x in z.oldState: skel.push(x.head, x.tail, Hash256()) let r = skel.initSync(z.head).valueOr: debugEcho "initSync: ", error check false break check r.status.card == 0 check r.reorg == z.reorg check skel.len == z.newState.len for i, sc in skel: check sc.head == z.newState[i].head check sc.tail == z.newState[i].tail