diff --git a/side_loading_test.go b/side_loading_test.go new file mode 100644 index 00000000..24738c75 --- /dev/null +++ b/side_loading_test.go @@ -0,0 +1,971 @@ +package neutrino + +import ( + "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" + "os" + "testing" + "time" +) + +// This is the integration test of the binary file reader. It tests that the +// side laoder along with other components it interacts with, operate as they should. +func TestSideLoadingBinaryImp(t *testing.T) { + + SLAtGenesisTip := func(c *Config) { + + c.SideLoad = SideLoadOpt{ + Path: "sideload/testdata/start_0_end_10_encoded_10_valid_headers.bin", + Enabled: true, + Verify: false, + SourceType: "binary", + } + + } + + SLAtStartHeight2 := func(c *Config) { + + c.SideLoad = SideLoadOpt{ + Path: "sideload/testdata/enc_start_2_end_10_R_valid_headers.bin", + Enabled: true, + Verify: false, + SourceType: "binary", + } + + c.ChainParams.Net = wire.TestNet + + } + + SLAtEndHeight2 := func(c *Config) { + + c.SideLoad = SideLoadOpt{ + Path: "sideload/testdata/enc_2_valid_headers.bin", + Enabled: true, + Verify: false, + SourceType: "binary", + } + + } + + SLInvalidHeaderAtHeight5 := func(c *Config) { + + c.SideLoad = SideLoadOpt{ + Path: "sideload/testdata/enc_INvalid_at_5_headers.bin", + Enabled: true, + Verify: false, + SourceType: "binary", + } + + } + + SlAtStartHeight0EndHeight8 := func(c *Config) { + + c.SideLoad = SideLoadOpt{ + Path: "sideload/testdata/start_0_end_8_encoded_8_valid_headers.bin", + Enabled: true, + Verify: false, + SourceType: "binary", + } + + } + MaxHeaderWrite = 10 + + testNeutrinoTipAndSLAtGenesis(t, SLAtGenesisTip) + + testNeutrinoTipAheadSLStartHeight(t, SLAtGenesisTip) + + testNeutrinoTipBelowSLStartHeight(t, SLAtStartHeight2) + + testNeutrinoTipGreaterSLEndHeight(t, SLAtEndHeight2) + + testNeutrinoAndSLDiffChain(t, SlAtStartHeight0EndHeight8) + + testNeutrinoSLVerifyTrue(t, SLInvalidHeaderAtHeight5) + testNeutrinoSLVerifyFalse(t, SLInvalidHeaderAtHeight5) + +} + +func testNeutrinoTipAndSLAtGenesis(t *testing.T, setUpSideLoad func(config *Config)) { + + tempDir, err := os.MkdirTemp("", "test1") + 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, + } + + if err != nil { + t.Errorf("Error during start up: %v", err) + t.FailNow() + } + + 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.sideLoadReader == nil { + + t.Errorf("Error initializing side load reader in block manager") + t.FailNow() + } + + if svc.blockManager.sideLoadReader.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() + } + }() + + time.Sleep(30) + + _, finalTipHeight, err := svc.BlockHeaders.ChainTip() + + if err != nil { + + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + + if int64(finalTipHeight) != svc.blockManager.sideLoadReader.EndHeight() { + + t.Errorf("Expected chain tip to be of finalTipHeight %v but of finalTipHeight %v", + svc.blockManager.sideLoadReader.EndHeight(), finalTipHeight) + t.FailNow() + + } + + if svc.chainParams.Net != svc.blockManager.sideLoadReader.HeadersChain() { + + t.Errorf("Expected side load source and chain params to be on same network chain") + t.FailNow() + } + + if int64(initialTipHeight) >= svc.blockManager.sideLoadReader.EndHeight() { + + t.Errorf("Expected chain tip to have headers behind side load source's end height") + t.FailNow() + } + +} + +func testNeutrinoTipAheadSLStartHeight(t *testing.T, setUpSideLoad func(config *Config)) { + + tempDir, err := os.MkdirTemp("", "test2") + 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, + } + + if err != nil { + t.Errorf("Error during start up: %v", err) + t.FailNow() + } + + 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.sideLoadReader.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() + } + }() + + time.Sleep(30) + + _, finalTipHeight, err := svc.BlockHeaders.ChainTip() + + if err != nil { + + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + + sideLoadEndHeight := svc.blockManager.sideLoadReader.EndHeight() + + if int64(finalTipHeight) != sideLoadEndHeight { + + t.Errorf("Expected chain tip to be of height, %v but got %v", sideLoadEndHeight, finalTipHeight) + t.FailNow() + } + + if svc.chainParams.Net != svc.blockManager.sideLoadReader.HeadersChain() { + + t.Errorf("Expected side load source and chain params to be on same network chain") + t.FailNow() + } + + if int64(initialTipHeight) >= svc.blockManager.sideLoadReader.EndHeight() { + + t.Errorf("Expected chain tip to have headers behind side load source's end height") + t.FailNow() + } + +} + +func testNeutrinoTipBelowSLStartHeight(t *testing.T, setUpSideLoad func(config *Config)) { + + tempDir, err := os.MkdirTemp("", "test3") + 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, + } + + if err != nil { + t.Errorf("Error during start up: %v", err) + t.FailNow() + } + + 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.sideLoadReader.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() + } + }() + + time.Sleep(30) + + _, 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() + } + +} + +func testNeutrinoTipGreaterSLEndHeight(t *testing.T, setUpSideLoad func(config *Config)) { + + tempDir, err := os.MkdirTemp("", "test4") + 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, + } + + if err != nil { + t.Errorf("Error during start up: %v", err) + t.FailNow() + } + + 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.sideLoadReader == nil { + + t.Errorf("Error initializing side load reader in block manager") + t.FailNow() + } + + sideLoadEndHeight := svc.blockManager.sideLoadReader.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.sideLoadReader.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() + } + }() + + time.Sleep(30) + + _, 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"+ + "initialTipHeight: %v and finalTipHeight: %v", initialTipHeight, finalTipHeight) + t.FailNow() + } + +} + +func testNeutrinoAndSLDiffChain(t *testing.T, setUpSideLoad func(config *Config)) { + + tempDir, err := os.MkdirTemp("", "test5") + 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, + } + + if err != nil { + t.Errorf("Error during start up: %v", err) + t.FailNow() + } + + 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.sideLoadReader.HeadersChain() { + + t.Errorf("Expected side load source and chain params to have differnt 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.sideLoadReader.EndHeight() { + + t.Errorf("Expected chain tip to have headers behind side load source's end height") + t.FailNow() + } + + sideLoadStartHeight := svc.blockManager.sideLoadReader.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() + } + }() + + time.Sleep(30) + + _, 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."+ + "initialTipHeight: %v, finalTipHeight: %v", initialTipHeight, finalTipHeight) + t.FailNow() + } + +} + +func testNeutrinoSLVerifyTrue(t *testing.T, setUpInValidSideLoad func(config *Config)) { + tempDir, err := os.MkdirTemp("", "test6") + 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, + } + + if err != nil { + t.Errorf("Error during start up: %v", err) + t.FailNow() + } + + setUpInValidSideLoad(c) + + c.SideLoad.Verify = true + + if !c.SideLoad.Verify { + + t.Errorf("Expected verification true") + 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.sideLoadReader.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.sideLoadReader.EndHeight() { + + t.Errorf("Expected chain tip to have headers behind side load source's end height") + t.FailNow() + } + + sideLoadStartHeight := svc.blockManager.sideLoadReader.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() + } + }() + + time.Sleep(30) + + _, 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 header written to db"+ + "initialTipHeight: %v, finalTipHeight: %v", initialTipHeight, finalTipHeight) + t.FailNow() + } + + if int64(finalTipHeight) == svc.blockManager.sideLoadReader.EndHeight() { + + t.Errorf("Expected final height not to equate side loader's end height.") + t.FailNow() + } + +} + +func testNeutrinoSLVerifyFalse(t *testing.T, setUpInValidSideLoad func(config *Config)) { + + // No verification enabled + + tempDir, err := os.MkdirTemp("", "test7") + 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, + } + + if err != nil { + t.Errorf("Error during start up: %v", err) + t.FailNow() + } + + if c.SideLoad.Verify { + + t.Errorf("Expected verification false") + t.FailNow() + } + + setUpInValidSideLoad(c) + + if err != nil { + t.Errorf("Error during start up: %v", err) + t.FailNow() + } + + svc, err := NewChainService(*c) + + 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.sideLoadReader.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.sideLoadReader.EndHeight() { + + t.Errorf("Expected chain tip to have headers behind side load source's end height") + t.FailNow() + } + + sideLoadStartHeight := svc.blockManager.sideLoadReader.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() + } + }() + + time.Sleep(30) + + _, finalTipHeight, err := svc.BlockHeaders.ChainTip() + + if err != nil { + + t.Errorf("Error while fetching chain tip: %v", err) + t.FailNow() + } + + if int64(finalTipHeight) != svc.blockManager.sideLoadReader.EndHeight() { + + t.Errorf("Expected header height to be same as side load") + t.FailNow() + } + +}