diff --git a/side_loading_test.go b/side_loading_test.go new file mode 100644 index 00000000..7dce6d84 --- /dev/null +++ b/side_loading_test.go @@ -0,0 +1,960 @@ +// NOTE: This is the integration test file for sideloading. All implementation of the chainDataLoader.Reader +// are to write their custom config and test here. + +package neutrino + +import ( + "os" + "testing" + "time" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcwallet/walletdb" + "github.com/lightninglabs/neutrino/headerfs" + "github.com/lightninglabs/neutrino/headerlist" +) + +const ( + checkChainTipTimeout = 5 * time.Second + syncUpdate = time.Second +) + +// TestSideLoadingBinaryImp is the integration test of the binary file reader. It tests that the +// side loader along with other components it interacts with, operates as it should. +func TestSideLoadingBinaryImp(t *testing.T) { + SLAtGenesisTip := func(c *Config) { + c.SideLoad = SideLoadOpt{ + Path: "chaindataloader/testdata/start_0_end_10_encoded_10_valid_testnet_headers.bin", + Enabled: true, + SkipVerify: true, + SourceType: "binary", + } + } + + SLAtStartHeight2 := func(c *Config) { + c.SideLoad = SideLoadOpt{ + Path: "chaindataloader/testdata/start_2_end_10_encoded_8_valid_regtest_headers.bin", + Enabled: true, + SkipVerify: true, + SourceType: "binary", + } + + c.ChainParams.Net = wire.TestNet + } + + SLAtEndHeight2 := func(c *Config) { + c.SideLoad = SideLoadOpt{ + Path: "chaindataloader/testdata/start_0_end_2_encoded_2_valid_testnet_headers.bin", + Enabled: true, + SkipVerify: true, + SourceType: "binary", + } + } + + SLInvalidHeaderAtHeight5 := func(c *Config) { + c.SideLoad = SideLoadOpt{ + Path: "chaindataloader/testdata/start_0_end_10_encoded_10_invalidHeadersAt5.bin", + Enabled: true, + SkipVerify: true, + SourceType: "binary", + } + } + + SlAtStartHeight0EndHeight8 := func(c *Config) { + c.SideLoad = SideLoadOpt{ + Path: "chaindataloader/testdata/start_0_end_8_encoded_8_valid_testnet_headers.bin", + Enabled: true, + SkipVerify: true, + SourceType: "binary", + } + } + + testNeutrinoTipAndSLAtGenesis(t, SLAtGenesisTip) + + testNeutrinoTipAheadSLStartHeight(t, SLAtGenesisTip) + + testNeutrinoTipBelowSLStartHeight(t, SLAtStartHeight2) + + testNeutrinoTipGreaterSLEndHeight(t, SLAtEndHeight2) + + testNeutrinoAndSLDiffChain(t, SlAtStartHeight0EndHeight8) + + testNeutrinoSLSkipVerifyFalse(t, SLInvalidHeaderAtHeight5) + + testNeutrinoSLSkipVerifyTrue(t, SLInvalidHeaderAtHeight5) +} + +// testNeutrinoTipAndSLAtGenesis tests that headers are sideloaded when the sideload source and neutrino's tip both start +// at the genesis header. +// Note: The sideload source here should have its start Height at the genesis header height. +func testNeutrinoTipAndSLAtGenesis(t *testing.T, setUpSideLoad func(config *Config)) { + tempDir, err := os.MkdirTemp("", "test1") + defer func() { + err := os.RemoveAll(tempDir) + if err != nil { + t.Errorf("error removing tempDir: %v", err) + } + }() + + if err != nil { + t.Errorf("error making temp directory: %v", err) + + t.FailNow() + } + + db, err := walletdb.Create( + "bdb", tempDir+"/weks.db", true, 40*time.Second, + ) + if err != nil { + t.Errorf("error creating wallet: %v", err) + + t.FailNow() + } + + c := &Config{ + DataDir: tempDir, + Database: db, + ChainParams: chaincfg.TestNet3Params, + } + + setUpSideLoad(c) + + if c.SideLoad.Path == "" { + t.Errorf("Side load not set up") + + t.FailNow() + } + + svc, err := NewChainService(*c) + defer func() { + err = c.Database.Close() + + if err != nil { + t.Errorf("Error closing database: %v", err) + + t.FailNow() + } + }() + + if err != nil { + t.Errorf("Error during chain service creation: %v", err) + t.FailNow() + } + + _, initialTipHeight, err := svc.BlockHeaders.ChainTip() + + if err != nil { + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + + if int64(initialTipHeight) != 0 { + t.Errorf("Expected chain tip to be at genesis initialTipHeight but of initialTipHeight %v", initialTipHeight) + t.FailNow() + } + + if svc.blockManager.sideLoadBlkHdrReader == nil { + t.Errorf("Error initializing side load reader in block manager") + t.FailNow() + } + + if svc.blockManager.sideLoadBlkHdrReader.StartHeight() != 0 { + t.Errorf("Expected side load source to have headers starting at zero") + t.FailNow() + } + + err = svc.Start() + + if err != nil { + t.Errorf("Error while starting chainservice: %v", err) + t.FailNow() + } + defer func() { + err = svc.Stop() + + if err != nil { + t.Errorf("Error while stopping chainservice: %v", err) + t.FailNow() + } + }() + + var total time.Duration + for { + + _, finalTipHeight, err := svc.BlockHeaders.ChainTip() + if err != nil { + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + if int64(finalTipHeight) == svc.blockManager.sideLoadBlkHdrReader.EndHeight() { + break + } + + if total > checkChainTipTimeout { + t.Errorf("Expected chain tip to be of finalTipHeight %v but of finalTipHeight %v", + svc.blockManager.sideLoadBlkHdrReader.EndHeight(), finalTipHeight) + t.FailNow() + } + time.Sleep(syncUpdate) + total += syncUpdate + } + + if svc.chainParams.Net != svc.blockManager.sideLoadBlkHdrReader.HeadersChain() { + t.Errorf("Expected side load source and chain params to be on same network chain") + t.FailNow() + } + + if int64(initialTipHeight) >= svc.blockManager.sideLoadBlkHdrReader.EndHeight() { + t.Errorf("Expected chain tip to have headers behind side load source's end height") + t.FailNow() + } +} + +// testNeutrinoTipAheadSLStartHeight tests sideloading when Neutrino's tip is ahead of the sideload source's startHeight. +// NOTE: The sideload source to be used here should have its startHeight below 2. +func testNeutrinoTipAheadSLStartHeight(t *testing.T, setUpSideLoad func(config *Config)) { + tempDir, err := os.MkdirTemp("", "test2") + defer func() { + err := os.RemoveAll(tempDir) + if err != nil { + t.Errorf("error removing tempDir: %v", err) + } + }() + + if err != nil { + t.Errorf("error making temp directory: %v", err) + + t.FailNow() + } + db, err := walletdb.Create( + "bdb", tempDir+"/weks.db", true, dbOpenTimeout, + ) + if err != nil { + t.Errorf("error creating wallet: %v", err) + + t.FailNow() + } + + c := &Config{ + DataDir: tempDir, + Database: db, + ChainParams: chaincfg.TestNet3Params, + } + + setUpSideLoad(c) + + svc, err := NewChainService(*c) + defer func() { + err = c.Database.Close() + + if err != nil { + t.Errorf("Error closing database: %v", err) + + t.FailNow() + } + }() + + if err != nil { + t.Errorf("Error during chain service creation: %v", err) + t.FailNow() + } + + err = svc.BlockHeaders.WriteHeaders([]headerfs.BlockHeader{ + { + Height: 1, + BlockHeader: &wire.BlockHeader{}, + }, + + { + Height: 2, + BlockHeader: &wire.BlockHeader{}, + }, + }...) + + if err != nil { + t.Errorf("Error writing headers for test: %v", err) + + t.FailNow() + } + + _, initialTipHeight, err := svc.BlockHeaders.ChainTip() + + if err != nil { + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + + if int64(initialTipHeight) != 2 { + t.Errorf("Expected chain tip to be at tipHeight 2 but of tipHeight %v", initialTipHeight) + t.FailNow() + } + + svc.blockManager.headerTip = initialTipHeight + + svc.blockManager.headerList.ResetHeaderState(headerlist.Node{ + Height: int32(initialTipHeight), + }) + + sideLoadStartHeight := svc.blockManager.sideLoadBlkHdrReader.StartHeight() + + if int64(initialTipHeight) <= sideLoadStartHeight { + t.Errorf("Expected chain tip to have headers ahead of chain tip source") + t.FailNow() + } + + err = svc.Start() + + if err != nil { + t.Errorf("Error while starting chainservice: %v", err) + t.FailNow() + } + defer func() { + err = svc.Stop() + + if err != nil { + t.Errorf("Error while stopping chainservice: %v", err) + t.FailNow() + } + }() + + var total time.Duration + for { + + _, finalTipHeight, err := svc.BlockHeaders.ChainTip() + if err != nil { + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + if int64(finalTipHeight) == svc.blockManager.sideLoadBlkHdrReader.EndHeight() { + break + } + + if total > checkChainTipTimeout { + t.Errorf("Expected chain tip to be of finalTipHeight %v but of finalTipHeight %v", + svc.blockManager.sideLoadBlkHdrReader.EndHeight(), finalTipHeight) + t.FailNow() + } + time.Sleep(syncUpdate) + total += syncUpdate + } + + if svc.chainParams.Net != svc.blockManager.sideLoadBlkHdrReader.HeadersChain() { + t.Errorf("Expected side load source and chain params to be on same network chain") + t.FailNow() + } + + if int64(initialTipHeight) >= svc.blockManager.sideLoadBlkHdrReader.EndHeight() { + t.Errorf("Expected chain tip to have headers behind side load source's end height") + t.FailNow() + } +} + +// testNeutrinoTipBelowSLStartHeight tests sideloading when Neutrino's tip is below the side load source's startHeight. +// NOTE: The sideload source to be used here should have its startHeight higher than the genesis header height. +func testNeutrinoTipBelowSLStartHeight(t *testing.T, setUpSideLoad func(config *Config)) { + tempDir, err := os.MkdirTemp("", "test3") + defer func() { + err := os.RemoveAll(tempDir) + if err != nil { + t.Errorf("error removing tempDir: %v", err) + } + }() + + if err != nil { + t.Errorf("error making temp directory: %v", err) + + t.FailNow() + } + db, err := walletdb.Create( + "bdb", tempDir+"/weks.db", true, dbOpenTimeout, + ) + if err != nil { + t.Errorf("error creating wallet: %v", err) + + t.FailNow() + } + + c := &Config{ + DataDir: tempDir, + Database: db, + ChainParams: chaincfg.TestNet3Params, + } + + setUpSideLoad(c) + + svc, err := NewChainService(*c) + defer func() { + err = c.Database.Close() + + if err != nil { + t.Errorf("Error closing database: %v", err) + + t.FailNow() + } + }() + + if err != nil { + t.Errorf("Error during chain service creation: %v", err) + t.FailNow() + } + + _, tipHeight, err := svc.BlockHeaders.ChainTip() + + if err != nil { + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + + sideLoadStartHeight := svc.blockManager.sideLoadBlkHdrReader.StartHeight() + + if int64(tipHeight) >= sideLoadStartHeight { + t.Errorf("Expected chain tip to have headers behind side load source") + } + + err = svc.Start() + + if err != nil { + t.Errorf("Error while starting chainservice: %v", err) + t.FailNow() + } + defer func() { + err = svc.Stop() + + if err != nil { + t.Errorf("Error while stopping chainservice: %v", err) + t.FailNow() + } + }() + + var total time.Duration + for { + + _, finalTipHeight, err := svc.BlockHeaders.ChainTip() + if err != nil { + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + if finalTipHeight != tipHeight { + t.Errorf("Expected no headers be written to the DB from side load") + t.FailNow() + } + + if total > checkChainTipTimeout { + break + } + time.Sleep(syncUpdate) + total += syncUpdate + } + +} + +// testNeutrinoTipGreaterSLEndHeight tests sideloading when Neutrino's tip is greater than the side load source's end +// Height. +// NOTE: The sideload source to be used here should have its EndHeight below 4. +func testNeutrinoTipGreaterSLEndHeight(t *testing.T, setUpSideLoad func(config *Config)) { + tempDir, err := os.MkdirTemp("", "test4") + defer func() { + err := os.RemoveAll(tempDir) + if err != nil { + t.Errorf("error removing tempDir: %v", err) + } + }() + + if err != nil { + t.Errorf("error making temp directory: %v", err) + + t.FailNow() + } + db, err := walletdb.Create( + "bdb", tempDir+"/weks.db", true, dbOpenTimeout, + ) + if err != nil { + t.Errorf("error creating wallet: %v", err) + + t.FailNow() + } + + c := &Config{ + DataDir: tempDir, + Database: db, + ChainParams: chaincfg.TestNet3Params, + } + + setUpSideLoad(c) + + if c.SideLoad.Path == "" { + t.Errorf("Side load not set up") + } + + svc, err := NewChainService(*c) + if err != nil { + t.Errorf("Error during chain service creation: %v", err) + t.FailNow() + } + + defer func() { + err = c.Database.Close() + + if err != nil { + t.Errorf("Error closing database: %v", err) + + t.FailNow() + } + }() + + err = svc.BlockHeaders.WriteHeaders([]headerfs.BlockHeader{ + { + Height: 1, + BlockHeader: &wire.BlockHeader{}, + }, + + { + Height: 2, + BlockHeader: &wire.BlockHeader{}, + }, + { + Height: 3, + BlockHeader: &wire.BlockHeader{}, + }, + { + Height: 4, + BlockHeader: &wire.BlockHeader{}, + }, + }...) + + if err != nil { + t.Errorf("Error writing headers to DB for test") + + t.FailNow() + } + + _, initialTipHeight, err := svc.BlockHeaders.ChainTip() + + if err != nil { + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + + if int64(initialTipHeight) != 4 { + t.Errorf("Expected chain tip to be at tipHeight 2 but of tipHeight %v", initialTipHeight) + t.FailNow() + } + + svc.blockManager.headerTip = initialTipHeight + + svc.blockManager.headerList.ResetHeaderState(headerlist.Node{ + Height: int32(initialTipHeight), + }) + + if svc.blockManager.sideLoadBlkHdrReader == nil { + t.Errorf("Error initializing side load reader in block manager") + t.FailNow() + } + + sideLoadEndHeight := svc.blockManager.sideLoadBlkHdrReader.EndHeight() + + if int64(initialTipHeight) <= sideLoadEndHeight { + t.Errorf("Expected chain tip to have headers ahead of side load source's end height"+ + "Tip height: %v while sideLoadEndHeight: %v", initialTipHeight, sideLoadEndHeight) + t.FailNow() + } + + sideLoadStartHeight := svc.blockManager.sideLoadBlkHdrReader.StartHeight() + + if int64(initialTipHeight) < sideLoadStartHeight { + t.Errorf("Expected chain tip to be greater than or equal to source file") + t.FailNow() + } + + err = svc.Start() + + if err != nil { + t.Errorf("Error while starting chainservice: %v", err) + t.FailNow() + } + defer func() { + err = svc.Stop() + + if err != nil { + t.Errorf("Error while stopping chainservice: %v", err) + t.FailNow() + } + }() + + var total time.Duration + for { + _, finalTipHeight, err := svc.BlockHeaders.ChainTip() + if err != nil { + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + if finalTipHeight != initialTipHeight { + t.Errorf("Expected no headers be written to the DB from side load") + t.FailNow() + } + + if total > checkChainTipTimeout { + break + } + time.Sleep(syncUpdate) + total += syncUpdate + } +} + +// testNeutrinoAndSLDiffChain tests sideloading when the sideload source has headers on a different chain from +// the chain's. +// NOTE: The sideload source header to be used here should be on a bitcoin network other than wire.TestNet3. +func testNeutrinoAndSLDiffChain(t *testing.T, setUpSideLoad func(config *Config)) { + tempDir, err := os.MkdirTemp("", "test5") + defer func() { + err := os.RemoveAll(tempDir) + if err != nil { + t.Errorf("error removing tempDir: %v", err) + } + }() + + if err != nil { + t.Errorf("error making temp directory: %v", err) + + t.FailNow() + } + db, err := walletdb.Create( + "bdb", tempDir+"/weks.db", true, dbOpenTimeout, + ) + if err != nil { + t.Errorf("error creating wallet: %v", err) + + t.FailNow() + } + + c := &Config{ + DataDir: tempDir, + Database: db, + ChainParams: chaincfg.TestNet3Params, + } + + setUpSideLoad(c) + + c.ChainParams.Net = wire.SimNet + + svc, err := NewChainService(*c) + defer func() { + err = c.Database.Close() + + if err != nil { + t.Errorf("Error closing database: %v", err) + + t.FailNow() + } + }() + + if err != nil { + t.Errorf("Error during chain service creation: %v", err) + t.FailNow() + } + + if svc.chainParams.Net == svc.blockManager.sideLoadBlkHdrReader.HeadersChain() { + t.Errorf("Expected side load source and chain params to have different network chain") + t.FailNow() + } + + _, initialTipHeight, err := svc.BlockHeaders.ChainTip() + + if err != nil { + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + + if int64(initialTipHeight) >= svc.blockManager.sideLoadBlkHdrReader.EndHeight() { + t.Errorf("Expected chain tip to have headers behind side load source's end height") + t.FailNow() + } + + sideLoadStartHeight := svc.blockManager.sideLoadBlkHdrReader.StartHeight() + if int64(initialTipHeight) < sideLoadStartHeight { + t.Errorf("Expected chain tip to have headers ahead of chain tip source"+ + "TipHeight: %v, SideLoad Start Height: %v", initialTipHeight, sideLoadStartHeight) + t.FailNow() + } + + err = svc.Start() + + if err != nil { + t.Errorf("Error while starting chainservice: %v", err) + t.FailNow() + } + defer func() { + err = svc.Stop() + + if err != nil { + t.Errorf("Error while stopping chainservice: %v", err) + t.FailNow() + } + }() + + var total time.Duration + for { + _, finalTipHeight, err := svc.BlockHeaders.ChainTip() + if err != nil { + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + if finalTipHeight != initialTipHeight { + t.Errorf("Expected no headers be written to the DB from side load") + t.FailNow() + } + + if total > checkChainTipTimeout { + break + } + time.Sleep(syncUpdate) + total += syncUpdate + } +} + +// testNeutrinoSLSkipVerifyFalse tests sideloading when verifying headers is enabled. +// NOTE: The sideload source to be used here should have invalid headers at some point (not at the first header though). +// The sideload config's SkipVerify field should be set to false and the same sideload source used here should be used +// in the testNeutrinoSLSkipVerifyTrue for contrast. +func testNeutrinoSLSkipVerifyFalse(t *testing.T, setUpInValidSideLoad func(config *Config)) { + tempDir, err := os.MkdirTemp("", "test6") + defer func() { + err := os.RemoveAll(tempDir) + if err != nil { + t.Errorf("error removing tempDir: %v", err) + } + }() + + if err != nil { + t.Errorf("error making temp directory: %v", err) + + t.FailNow() + } + db, err := walletdb.Create( + "bdb", tempDir+"/weks.db", true, dbOpenTimeout, + ) + if err != nil { + t.Errorf("error creating wallet: %v", err) + + t.FailNow() + } + + c := &Config{ + DataDir: tempDir, + Database: db, + ChainParams: chaincfg.TestNet3Params, + } + + setUpInValidSideLoad(c) + + c.SideLoad.SkipVerify = false + + if c.SideLoad.SkipVerify { + t.Errorf("Expected skip verification false") + t.FailNow() + } + + svc, err := NewChainService(*c) + if err != nil { + t.Errorf("Error during chain service creation: %v", err) + t.FailNow() + } + + defer func() { + err = c.Database.Close() + + if err != nil { + t.Errorf("Error closing database: %v", err) + + t.FailNow() + } + }() + + if svc.chainParams.Net != svc.blockManager.sideLoadBlkHdrReader.HeadersChain() { + t.Errorf("Expected side load source and chain params to have same network chain") + t.FailNow() + } + + _, initialTipHeight, err := svc.BlockHeaders.ChainTip() + + if err != nil { + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + + if int64(initialTipHeight) > svc.blockManager.sideLoadBlkHdrReader.EndHeight() { + t.Errorf("Expected chain tip to have headers behind side load source's end height") + t.FailNow() + } + + sideLoadStartHeight := svc.blockManager.sideLoadBlkHdrReader.StartHeight() + if int64(initialTipHeight) < sideLoadStartHeight { + t.Errorf("Expected chain tip to have headers ahead or equal to side load source") + t.FailNow() + } + + err = svc.Start() + + if err != nil { + t.Errorf("Error starting chainservice") + + t.FailNow() + } + + defer func() { + err = svc.Stop() + if err != nil { + t.Errorf("error stopping chain service: %v", err) + + t.FailNow() + } + }() + + var ( + total time.Duration + finalTipHeight uint32 + ) + for { + _, finalTipHeight, err = svc.BlockHeaders.ChainTip() + if err != nil { + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + if int64(finalTipHeight) == svc.blockManager.sideLoadBlkHdrReader.EndHeight() { + t.Errorf("Expected no headers be written to the DB from side load") + t.FailNow() + } + + // Increased time so as to allow time for rollback (check comment on rollback) + if total > 2*checkChainTipTimeout { + break + } + time.Sleep(syncUpdate) + total += syncUpdate + } + + if int64(finalTipHeight) == svc.blockManager.sideLoadBlkHdrReader.EndHeight() { + t.Errorf("Expected final height not to equate side loader's end height.") + t.FailNow() + } +} + +// testNeutrinoSLSkipVerifyTrue tests sideloading when verifying headers is disabled in contrast to the +// testNeutrinoSLVerifyTrue testcase. +// NOTE: The sideload source to be used here should have invalid headers at some point (not at the first header though). +// The sideload config's Verify field should be set to false and the same sideload source used here should be used in +// the testNeutrinoSLSkipVerifyFalse for contrast. +func testNeutrinoSLSkipVerifyTrue(t *testing.T, setUpInValidSideLoad func(config *Config)) { + tempDir, err := os.MkdirTemp("", "test7") + defer func() { + err := os.RemoveAll(tempDir) + if err != nil { + t.Errorf("error removing tempDir: %v", err) + } + }() + + if err != nil { + t.Errorf("error making temp directory: %v", err) + + t.FailNow() + } + db, err := walletdb.Create( + "bdb", tempDir+"/weks.db", true, dbOpenTimeout, + ) + if err != nil { + t.Errorf("error creating wallet: %v", err) + + t.FailNow() + } + + c := &Config{ + DataDir: tempDir, + Database: db, + ChainParams: chaincfg.TestNet3Params, + } + + // SkipVerify is already set to true. + setUpInValidSideLoad(c) + + svc, err := NewChainService(*c) + + if err != nil { + t.Errorf("Error during start up: %v", err) + t.FailNow() + } + + defer func() { + err = c.Database.Close() + + if err != nil { + t.Errorf("Error closing DB") + + t.FailNow() + } + }() + + if err != nil { + t.Errorf("Error during chain service creation: %v", err) + t.FailNow() + } + + if svc.chainParams.Net != svc.blockManager.sideLoadBlkHdrReader.HeadersChain() { + t.Errorf("Expected side load source and chain params to have same network chain") + t.FailNow() + } + + _, initialTipHeight, err := svc.BlockHeaders.ChainTip() + + if err != nil { + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + + if int64(initialTipHeight) >= svc.blockManager.sideLoadBlkHdrReader.EndHeight() { + t.Errorf("Expected chain tip to have headers behind side load source's end height") + t.FailNow() + } + + sideLoadStartHeight := svc.blockManager.sideLoadBlkHdrReader.StartHeight() + if int64(initialTipHeight) < sideLoadStartHeight { + t.Errorf("Expected chain tip to have headers ahead or equal to side load source") + t.FailNow() + } + + err = svc.Start() + + if err != nil { + t.Errorf("Error while starting chainservice: %v", err) + t.FailNow() + } + defer func() { + err = svc.Stop() + + if err != nil { + t.Errorf("Error while stopping chainservice: %v", err) + t.FailNow() + } + }() + + var total time.Duration + for { + + _, finalTipHeight, err := svc.BlockHeaders.ChainTip() + if err != nil { + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + if int64(finalTipHeight) == svc.blockManager.sideLoadBlkHdrReader.EndHeight() { + break + } + + if total > checkChainTipTimeout { + t.Errorf("Expected chain tip to be of finalTipHeight %v but of finalTipHeight %v", + svc.blockManager.sideLoadBlkHdrReader.EndHeight(), finalTipHeight) + t.FailNow() + } + time.Sleep(syncUpdate) + total += syncUpdate + } +}