kaspad/domain/blockdag/tips_test.go
Svarog 1e08bfca9c
[NOD-1032] Consensus updates pre-pruning (#917)
* [NOD-1249] Add pruning related constants (#869)

* [NOD-1249] Add pruning related constants

* [NOD-1249] Change status suspect to UTXONotVerified

* [NOD-1249] Add TestPruningDepth

* [NOD-1249] Add comment to pruningDepth

* [NOD-1249] Add pruning helper functions (#875)

* [NOD-1249] Added node.blockAtDepth

* [NOD-1249] Added node.finalityPoint()

* [NOD-1249] Add hasFinalityPointInOthersSelectedChain

* [NOD-1249] Add nonFinalityViolatingBlues

* [NOD-1249] Added isInPastOfAny

* [NOD-1249] Updated all calls to blockNode functions that require dag

* [NOD-1249] Add blockNode.reds field and persist it

* [NOD-1249] Add checkObjectiveFinality

* [NOD-1249] Add isViolatingSubjectiveFinality

* [NOD-1249] Added to TestGHOSTDAG check that reds are as expected

* [NOD-1249] Add checkMergeLimit and checkDAGRelations

* [NOD-1249] Invert condition in blockInDepth

* [NOD-1249] Make isInPastOfAny resemble isInPast

* [NOD-1249] Added comments to isInPast and isInPastOfAny

* [NOD-1252] Remove any references to legacy finality (#876)

* [NOD-1032] validateParents: check number of parents and that no parents were manually rejected (#877)

* [NOD-1254] Block verification changes (#882)

* [NOD-1254] Call checkDAGRelations and move it to correct place

* [NOD-1254] Use blockStatuses properly

* [NOD-1254] Add support for setting node's verification flag and set it to UTXONotVerified once block passes basic verification

* [NOD-1254] Check for subjctiveFinality, and for node not being in the selectedParentChain

* [NOD-1254] Make blockStatus an ordinary value - not bit flags

* [NOD-1254] Isolate all utxo-requiring validation into a single separate if branches

* [NOD-1254] Re-arrange connectBloc so that things that happen in UTXO-validated blocks only are all grouped together

* [NOD-1254] Resolve and check selectedParent's status before validatingUTXO

* [NOD-1254] Separate virtualUTXODiff from utxoVerificationOutput

* [NOD-1254] Stylistic fixes

* [NOD-1254] Use dag.index.(Set)BlockNodeStatus instead of accessing node.status

* [NOD-1288] Sub-routinize checkConnectToPastUTXO

* [NOD-1288] Re-write checkConnectToPastUTXO in a way that allows to filter-out invalid transactions

* [NOD-1288] Make checkTxSequenceLock use already calculated utxo outputs

* [NOD-1288] Make checkTxMass use already calculated utxo outputs

* [NOD-1288] Use dag.sigCache for ValidateTransactionScripts

* [NOD-1288] Use checkConnectTransactionToPastUTXO in applyBlueBlocks

* [NOD-1288] Clean-up old code-path from no longer used functions

* [NOD-1288] Skip any irrelevant parts of txo verification if block is genesis

* [NOD-1288] Set  where it should have been

* [NOD-1288] Fix reachability checks to never use the new node + make isInSelectedParentChainOf return true if node == other

* [NOD-1288] invert the condition for isNewSelectedTip

* [NOD-1288] Separate checkIsAccepted to own function, and properly handle coinbase

* [NOD-1288] Don't update utxo-diff for UTXONotVerified parents/tips + Make PrepareBlockForTest resolve the selectedParent's UTXOSet if needed

* [NOD-1288] Include mass off coinbase transactions

* [NOD-1288] Move comment to correct position

* [NOD-1288] If blockAtDepth should return genesis - do it immidiately

* [NOD-1288] Comment-out instead of removeing scriptval_test.go

* [NOD-1288] Rename: entry -> utxoEntry

* [NOD-1288] Remove special function for calcCoinbaseTxMass

* [NOD-1288] Remove redundant check from checkEntryAmounts

* [NOD-1288] Rename: MaxMassPerBlock -> MaxMassAcceptedByBlock

* [NOD-1255] Implement boundedMergeBreakingParents

* [NOD-1255] Implement selectAllowedTips

* [NOD-1255] Integrate virtual parent selection into block verification process

* [NOD-1255] Add node to tips all the time, remove it from candidates and add it's parents if it's disqualified

* [NOD-1255] remove tips from virtaulBlock

* [NOD-1255] Rename: didVirtualParentsChanged -> didVirtualParentsChange

* [NOD-1255] Remove redundant sanity check

* [NOD-1255] Handle a forgotten error

* [NOD-1255] Prettify selectVirtualParents

* [NOD-1255] UpdateTipsUTXO should run over all UTXO-Verified tips, even if they are not parents of virtual

* [NOD-1311] Make isInPast inclusive

* [NOD-1032] Handle finality conflicts (#904)

* [NOD-1312] AddTip should not include finalityViolating and manuallyRejected blocks

* [NOD-1312] Implement resolveFinalityConflict

* [NOD-1312] Implement dag notifications for finalityChanges + updateing DAG state

* [NOD-1312] Added finality conflict rpc boilerplate

* [NOD-1312] Implement handling of getFinalityConflicts + resolveFinalityConflict RPCs

* [NOD-1312] Implement finality conflict related notifications

* [NOD-1312] Move all time to millisecond time

* [NOD-1312] Add comments + unexport some methods

* [NOD-1312] Add clarification in comments

* [NOD-1312] Move updateFinalityConflictResolution to finality_conflicts.go

* [NOD-1312] Rename: currentSelectedTip -> selectedTip

* [NOD-1312] Add long comment to ResolveFinalityConflict

* [NOD-1312] Convert areAllInSelectedParentChainOf into isInSelectedParentChainOfAll

* [NOD-1312] Rename chainUpdates (type) -> selectedParentChainUpdates, to distinguish from the variable chainUpdates

* [NOD-1032] Make all blockdag tests compile

* [NOD-1278] Fix finality-related tests (#910)

* [NOD-1032] Don't return node.dag.genesis from blockAtDepth because it might still not exist

* [NOD-1032] Actually add a tip in dag.addTip

* [NOD-1278] Add transaction to utxo set if it's coinbase

* [NOD-1278] Use VirtualParentHashes instead of TipHashes where appropriate

* [NOD-1278] If no valid virtual parent candidates - return error, don't wait for panic

* [NOD-1278] Transition TestCalcSequenceLock from newTestDAG to DAGSetup

* [NOD-1278] Fix .bluest() tie-breaker

* [NOD-1278] Remove feeData structure, as it no longer works, store feeData in acceptanceData

* [NOD-1278] Remove dag parameter from blockNode methods

* [NOD-1278] Fix TestBlueBlockWindow

* [NOD-1278] Don't subject selectedParent to MaxMergeSet

* [NOD-1278] se PrepareAndProcessBlockForTest instead of .addTip in TestSelectedPath

* [NOD-1278] Fixed TestDAGStateSerialization

* [NOD-1278] Fix TestAcceptanceIndexRecover

* [NOD-1278] Fix TestCheckConnectBlockTemplate

* [NOD-1278] Fix TestChainUpdates

* [NOD-1278] Fix and rename TestVirtualBlock -> TestTips

* [NOD-1278] Rename checkIsAccepted -> maybeAcceptTx

* [NOD-1278] Re-activate TestDoubleSpends

* Revert "[NOD-1278] Fixed TestDAGStateSerialization"

This reverts commit 845095d6de7207b07cf819d05f3f38ad94da9cf6.

* [NOD-1278] Remove dag parameter from expectedCoinbaseTransaction

* [NOD-1348] Implemented simplified Finality Conflict Resolution scheme (#911)

* [NOD-1348] Rename functions according to Research spec

* [NOD-1348] Added blockSet.areAllIn

* [NOD-1348] Implemented simplified finality conflict resolution scheme

* [NOD-1348] Refactorings and fixes in selectVirtualParents

* [NOD-1278] Fix bugs discovered by unit-tests + Fix unit-tests (#916)

* Updated to version v0.3.1

* [NOD-858] Don't switch sync peer if the syncing process hasn't yet started with the current sync peer (#700)

* [NOD-858] Don't switch sync peer if the syncing process hasn't yet started with the current sync peer

* [NOD-858] SetShouldSendBlockLocator(false) on OnBlockLocator

* [NOD-858] Rename shouldSendBlockLocator->wasBlockLocatorRequested

* [NOD-858] Move panic to shouldReplaceSyncPeer

* [NOD-869] Add a print after os.Exit(1) to see if it is ever called (#701)

* [NOD-1238] Fix acceptance index never being initialized. (#859)

* [NOD-1278] Genesis never violates finality

* [NOD-1348] Refactorings and fixes in selectVirtualParents

* [NOD-1278] Don't call dag.selectVirtualParents for genesis

* [NOD-1278] Properly organize errors in maybeAcceptBlock

* [NOD-1278] updateTipsUTXO should only run on tips whose status is

* [NOD-1278] updateTipsUTXO should only run on tips whose status is `valid`

* [NOD-1278] Fix TestDoubleSpends

* [NOD-1278] Fix TestDAGIndexFailedStatus

* [NOD-1278] IsFinalizedTransaction should use uint64 everywhere

* [NOD-1278] If tx is coinbase and not selectedParent - don't update pastUTXO

* [NOD-1278] Store tips and validTips separately

* [NOD-1278] Store ValidTips and VirtualParents in dagState

* [NOD-1278] Fix TestProcessOrphans

* [NOD-1278] Fix TestProcessOrphans

* [NOD-1278] Fix TestOrderInDiffFromAcceptanceData

* [NOD-1278] Fix TestHelp

* [NOD-1278] Remove mining.PrepareBlockForTest; use blockdag.PrepareBlockForTest instead

* [NOD-1278] Explicitly disallow chained transactions

* [NOD-1278]

* [NOD-1278] Fix some comments

Co-authored-by: Ori Newman <orinewman1@gmail.com>
Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
Co-authored-by: Yuval Shaul <yuval.shaul@gmail.com>

* [NOD-1355] Add unit-test for finality + When resolving finalityConflict - make sure the block that will come out selectedTip is statusValid (#919)

* [NOD-1355] Added test for finality

* [NOD-1355] When resolving finalityConflict - make sure the block that will come out selectedTip is statusValid

* [NOD-1032] Renames: anything about inputsWithReferencedUTXOEntries -> remove 'Referenced'

* [NOD-1032] Don't ignore non-rule errors

* [NOD-1032] Fix comment

* [NOD-1032] Enhanced comments on TestChainUpdates

* [NOD-1032] Remove scriptval_test.go

* [NOD-1032] Extracted isNewSelectedTip to a method

* [NOD-1032] Use dag.Now() instead of mstime.Now()

* [NOD-1032] Print block status when accepting block

* [NOD-1032] Add comment explaining boundedMergeBreakingParents

* [NOD-1032] Enhanced test and imporved comments in TestFinality

* [NOD-1032] Rename: Objective finality -> bounded merge depth

* [NOD-1032] No need to check that validTips are valid

* [NOD-1032] Remove dag from arguments of updateDiffAndDiffChild

* [NOD-1032] Correct variable names in LookupNodes

[NOD-1032] Correct variable names in LookupNodes

* [NOD-1032] Fix some comments

* [NOD-1032] Some style changes

* [NOD-1032] Refactor isAnyInPastOf

* [NOD-1032] Enhance comment in updateVirtualParents

* [NOD-1032] Flip condition in updateVirtualParents

* [NOD-1032] Stylistic and grammatic fixes in dag.go and dag_test.go

* [NOD-1032] Explain why updateSelectedParentSet receives geneses on init

* [NOD-1032] Remove ErrParentManuallyRejected

* [NOD-1032] Added wrapper for resolveNodeStatus that creates a special transaction for it

* [NOD-1032] Rename: statusUTXONotVerified -> statusUTXOPendingVerification

* [NOD-1032] Use virtual parents in CurrentBits()

* [NOD-1032] rename: isViolatingSubjectiveFinality -> isViolatingFinality

* [NOD-1032] Move updateVirtualAndTips to applyDAGChanges

* [NOD-1032] Invert condition for isFinalityPointInPast

* [NOD-1032] Fix antiPastBetween isInPast became inclusive

* [NOD-1032] Remove redundant call for addTip

* [NOD-1032] Use calcCoinbaseTxMass where appropriate

* [NOD-1032] Remove time fields from conflict notifications

* [NOD-1032] Assign the correct thing to i

* [NOD-1032] unify checkOutputsAmounts and checkTxOutputAmounts

* [NOD-1032] Cleanup in CheckTransactionInputsAndCalulateFee

* [NOD-1032] Fixed some style and comments

* [NOD-1032] If selectedParent is disqualifiedFromChain - validateAndApplyUTXOSet should return this as a ruleError

* [NOD-1032] Set the status in resolveNodeStatus

* [NOD-1032] Correct comment on boundedMergeBreakingParents

* [NOD-1032] Fix a typo.

* [NOD-1032] Update a variable name.

* [NOD-1032] Fix a comment.

* [NOD-1032] Fix merge errors.

* [NOD-1032] Add VirtualParentHashes to getBlockDagInfo.

* [NOD-1032] Update handleGetBlockTemplate.

* [NOD-1032] Comment out all the old RPC stuff.

* [NOD-1032] Remove irrelevant type.

* [NOD-1032] Implement ResolveFinalityConflict.

* [NOD-1032] Remove irrelevant comments.

* [NOD-1032] Implement NotifyFinalityConflicts.

* [NOD-1032] Add FinalityConflictNotification and FinalityConflictResolvedNotification.

* [NOD-1032] Finish implementing finality conflict notifications.

* [NOD-1032] Remove old RPC stuff.

* [NOD-1032] Fix grammar in a comment.

Co-authored-by: Ori Newman <orinewman1@gmail.com>
Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
Co-authored-by: Yuval Shaul <yuval.shaul@gmail.com>
Co-authored-by: stasatdaglabs <stas@daglabs.com>
2020-09-13 17:49:59 +03:00

296 lines
10 KiB
Go

// Copyright (c) 2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"reflect"
"testing"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/util"
)
func buildNode(t *testing.T, dag *BlockDAG, parents blockSet) *blockNode {
block, err := PrepareBlockForTest(dag, parents.hashes(), nil)
if err != nil {
t.Fatalf("error in PrepareBlockForTest: %s", err)
}
utilBlock := util.NewBlock(block)
isOrphan, isDelayed, err := dag.ProcessBlock(utilBlock, BFNoPoWCheck)
if err != nil {
t.Fatalf("unexpected error in ProcessBlock: %s", err)
}
if isDelayed {
t.Fatalf("block is too far in the future")
}
if isOrphan {
t.Fatalf("block was unexpectedly orphan")
}
return nodeByMsgBlock(t, dag, block)
}
// TestTips ensures that tips are updated as expected.
func TestTips(t *testing.T) {
// Create a new database and DAG instance to run tests against.
params := dagconfig.SimnetParams
params.K = 1
dag, teardownFunc, err := DAGSetup("TestTips", true, Config{
DAGParams: &params,
})
if err != nil {
t.Fatalf("TestTips: Failed to setup DAG instance: %s", err)
}
defer teardownFunc()
resetExtraNonceForTest()
// Create a DAG as follows:
// 0 <- 1 <- 2
// \
// <- 3 <- 5
// \ X
// <- 4 <- 6
node0 := dag.genesis
node1 := buildNode(t, dag, blockSetFromSlice(node0))
node2 := buildNode(t, dag, blockSetFromSlice(node1))
node3 := buildNode(t, dag, blockSetFromSlice(node0))
node4 := buildNode(t, dag, blockSetFromSlice(node0))
node5 := buildNode(t, dag, blockSetFromSlice(node3, node4))
node6 := buildNode(t, dag, blockSetFromSlice(node3, node4))
// Given an empty VirtualBlock, each of the following test cases will:
// Set its tips to tipsToSet
// Add to it all the tips in tipsToAdd, one after the other
// Call .Tips() on it and compare the result to expectedTips
// Call .selectedTip() on it and compare the result to expectedSelectedParent
tests := []struct {
name string
tipsToSet []*blockNode
tipsToAdd []*blockNode
expectedTips blockSet
expectedSelectedParent *blockNode
}{
{
name: "virtual with genesis tip",
tipsToSet: []*blockNode{node0},
tipsToAdd: []*blockNode{},
expectedTips: blockSetFromSlice(node0),
expectedSelectedParent: node0,
},
{
name: "virtual with genesis tip, add child of genesis",
tipsToSet: []*blockNode{node0},
tipsToAdd: []*blockNode{node1},
expectedTips: blockSetFromSlice(node1),
expectedSelectedParent: node1,
},
{
name: "virtual with genesis, add a full DAG",
tipsToSet: []*blockNode{node0},
tipsToAdd: []*blockNode{node1, node2, node3, node4, node5, node6},
expectedTips: blockSetFromSlice(node2, node5, node6),
expectedSelectedParent: node6,
},
}
for _, test := range tests {
// Set the tips. This will be the initial state
_, _, err := dag.setTips(blockSetFromSlice(test.tipsToSet...))
if err != nil {
t.Fatalf("%s: Error setting tips: %+v", test.name, err)
}
// Add all blockNodes in tipsToAdd in order
for _, tipToAdd := range test.tipsToAdd {
addNodeAsChildToParents(tipToAdd)
_, _, err := dag.addTip(tipToAdd)
if err != nil {
t.Fatalf("%s: Error adding tip: %+v", test.name, err)
}
}
// Ensure that the dag's tips are now equal to expectedTips
resultTips := dag.tips
if !reflect.DeepEqual(resultTips, test.expectedTips) {
t.Errorf("%s: unexpected tips. "+
"Expected: %v, got: %v.", test.name, test.expectedTips, resultTips)
}
// Ensure that the virtual block's selectedParent is now equal to expectedSelectedParent
resultSelectedTip := dag.virtual.selectedParent
if !reflect.DeepEqual(resultSelectedTip, test.expectedSelectedParent) {
t.Errorf("%s: unexpected selected tip. "+
"Expected: %v, got: %v.", test.name, test.expectedSelectedParent, resultSelectedTip)
}
}
}
func TestSelectedPath(t *testing.T) {
// Create a new database and DAG instance to run tests against.
params := dagconfig.SimnetParams
params.K = 1
dag, teardownFunc, err := DAGSetup("TestSelectedPath", true, Config{
DAGParams: &params,
})
if err != nil {
t.Fatalf("TestSelectedPath: Failed to setup DAG instance: %s", err)
}
defer teardownFunc()
initialPath := blockSetFromSlice(dag.genesis)
tip := dag.genesis
for i := 0; i < 5; i++ {
tipBlock := PrepareAndProcessBlockForTest(t, dag, []*daghash.Hash{tip.hash}, nil)
var ok bool
tip, ok = dag.index.LookupNode(tipBlock.BlockHash())
if !ok {
t.Fatalf("Couldn't lookup node that was just added")
}
initialPath.add(tip)
}
initialTip := tip
firstPath := initialPath.clone()
for i := 0; i < 5; i++ {
tip = buildNode(t, dag, blockSetFromSlice(tip))
firstPath.add(tip)
}
// For now we don't have any DAG, just chain, the selected path should include all the blocks on the chain.
if !reflect.DeepEqual(dag.virtual.selectedParentChainSet, firstPath) {
t.Fatalf("TestSelectedPath: selectedPathSet doesn't include the expected values. got %v, want %v",
dag.virtual.selectedParent, firstPath)
}
// We expect that selectedParentChainSlice should have all the blocks we've added so far
wantLen := 11
gotLen := len(dag.virtual.selectedParentChainSlice)
if wantLen != gotLen {
t.Fatalf("TestSelectedPath: selectedParentChainSlice doesn't have the expected length. got %d, want %d",
gotLen, wantLen)
}
secondPath := initialPath.clone()
tip = initialTip
for i := 0; i < 100; i++ {
tip = buildNode(t, dag, blockSetFromSlice(tip))
secondPath.add(tip)
}
// Because we added a chain that is much longer than the previous chain, the selected path should be re-organized.
if !reflect.DeepEqual(dag.virtual.selectedParentChainSet, secondPath) {
t.Fatalf("TestSelectedPath: selectedPathSet didn't handle the re-org as expected. got %v, want %v",
dag.virtual.selectedParent, firstPath)
}
// We expect that selectedParentChainSlice should have all the blocks we've added so far except the old chain
wantLen = 106
gotLen = len(dag.virtual.selectedParentChainSlice)
if wantLen != gotLen {
t.Fatalf("TestSelectedPath: selectedParentChainSlice doesn't have"+
"the expected length, possibly because it didn't handle the re-org as expected. got %d, want %d", gotLen, wantLen)
}
tip = initialTip
for i := 0; i < 3; i++ {
tip = buildNode(t, dag, blockSetFromSlice(tip))
}
// Because we added a very short chain, the selected path should not be affected.
if !reflect.DeepEqual(dag.virtual.selectedParentChainSet, secondPath) {
t.Fatalf("TestSelectedPath: selectedPathSet did an unexpected re-org. got %v, want %v",
dag.virtual.selectedParent, firstPath)
}
// We expect that selectedParentChainSlice not to change
wantLen = 106
gotLen = len(dag.virtual.selectedParentChainSlice)
if wantLen != gotLen {
t.Fatalf("TestSelectedPath: selectedParentChainSlice doesn't"+
"have the expected length, possibly due to unexpected did an unexpected re-org. got %d, want %d", gotLen, wantLen)
}
// We call updateSelectedParentSet manually without updating the tips, to check if it panics
virtual2 := newVirtualBlock(dag, nil)
defer func() {
if r := recover(); r == nil {
t.Fatalf("updateSelectedParentSet didn't panic")
}
}()
virtual2.updateSelectedParentSet(buildNode(t, dag, blockSetFromSlice()))
}
// TestChainUpdates makes sure the chainUpdates from setTips are correct:
// It creates two chains: a main-chain to be removed and a side-chain to be added
// The main-chain has to be longer than the side-chain, so that the natural selected tip of the DAG is the one
// from the main chain.
// Then dag.setTip is called with the tip of the side-chain to artificially re-org the DAG, and verify
// the chainUpdates return value is correct.
func TestChainUpdates(t *testing.T) {
// Create a new database and DAG instance to run tests against.
params := dagconfig.SimnetParams
params.K = 1
dag, teardownFunc, err := DAGSetup("TestChainUpdates", true, Config{
DAGParams: &params,
})
if err != nil {
t.Fatalf("TestChainUpdates: Failed to setup DAG instance: %s", err)
}
defer teardownFunc()
genesis := dag.genesis
// Create the main-chain to be removed
var toBeRemovedNodes []*blockNode
toBeRemovedTip := genesis
for i := 0; i < 9; i++ {
toBeRemovedTip = buildNode(t, dag, blockSetFromSlice(toBeRemovedTip))
toBeRemovedNodes = append(toBeRemovedNodes, toBeRemovedTip)
}
// Create the side-chain to be added
var toBeAddedNodes []*blockNode
toBeAddedTip := genesis
for i := 0; i < 8; i++ {
toBeAddedTip = buildNode(t, dag, blockSetFromSlice(toBeAddedTip))
toBeAddedNodes = append(toBeAddedNodes, toBeAddedTip)
}
err = resolveNodeStatusForTest(toBeAddedTip)
if err != nil {
t.Fatalf("Error resolving status of toBeAddedTip: %+v", err)
}
// Set the virtual tip to be the tip of the toBeAdded side-chain
_, chainUpdates, err := dag.setTips(blockSetFromSlice(toBeAddedTip))
if err != nil {
t.Fatalf("Error setting tips: %+v", err)
}
// Make sure that the removed blocks are as expected (in reverse order)
if len(chainUpdates.removedChainBlockHashes) != len(toBeRemovedNodes) {
t.Fatalf("TestChainUpdates: wrong removed amount. "+
"Got: %d, want: %d", len(chainUpdates.removedChainBlockHashes), len(toBeRemovedNodes))
}
for i, removedHash := range chainUpdates.removedChainBlockHashes {
correspondingRemovedNode := toBeRemovedNodes[len(toBeRemovedNodes)-1-i]
if !removedHash.IsEqual(correspondingRemovedNode.hash) {
t.Fatalf("TestChainUpdates: wrong removed hash. "+
"Got: %s, want: %s", removedHash, correspondingRemovedNode.hash)
}
}
// Make sure that the added blocks are as expected (in forward order)
if len(chainUpdates.addedChainBlockHashes) != len(toBeAddedNodes) {
t.Fatalf("TestChainUpdates: wrong added amount. "+
"Got: %d, want: %d", len(chainUpdates.addedChainBlockHashes), len(toBeAddedNodes))
}
for i, addedHash := range chainUpdates.addedChainBlockHashes {
correspondingAddedNode := toBeAddedNodes[i]
if !addedHash.IsEqual(correspondingAddedNode.hash) {
t.Fatalf("TestChainUpdates: wrong added hash. "+
"Got: %s, want: %s", addedHash, correspondingAddedNode.hash)
}
}
}