mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-05 21:56:50 +00:00
[NOD-1210] Add integration test for IBD and fix bug where requestSelectedTipsIfRequired ran in handshake's goroutine (#834)
* [NOD-1162] Separate kaspad to it's own package, so that I can use it out of integration test * [NOD-1162] Begin integration tests * [NOD-1162] [FIX] Assign cfg to RPCServer * [NOD-1162] Basic integration test ready * [NOD-1162] Wait for connection for real * [NOD-1162] [FIX] Connection manager should run the moment it adds a request * [NOD-1162] Make connect something that can be invoked in middle of test * [NOD-1162] Complete first integration test * [NOD-1162] Undo refactor error * [NOD-1162] Rename Kaspad to App * [NOD-1162] Convert checking connection to polling * [NOD-1162] [FIX] Set peerID on handshake * [NOD-1162] [FIX] Broadcast should send to outgoing route, not incoming * [NOD-1162] [FIX] Add CmdInvRelayBlock to MakeEmptyMessage * [NOD-1162] [FIX] Initialize Hash before decoding MsgInvRelayBlock * [NOD-1162] [FIX] Invert condition * [NOD-1162] [FIX] Fixes to encoding of MsgGetRelayBlocks * [NOD-1162] [FIX] Add MsgGetRelayBlocks to MakeEmptyMessage * [NOD-1162] [FIX] Connection manager should run the moment it adds a request * [NOD-1162] [FIX] Set peerID on handshake * [NOD-1162] [FIX] Broadcast should send to outgoing route, not incoming * [NOD-1162] [FIX] Add CmdInvRelayBlock to MakeEmptyMessage * [NOD-1162] [FIX] Initialize Hash before decoding MsgInvRelayBlock * [NOD-1162] [FIX] Invert condition * [NOD-1162] [FIX] Fixes to encoding of MsgGetRelayBlocks * [NOD-1162] [FIX] Add MsgGetRelayBlocks to MakeEmptyMessage * [NOD-1162] Add comment * [NOD-1162] Added support for 3 nodes and clients in integration tests * [NOD-1162] Add third node to integration test * [NOD-1192] Use lock-less functions in TxPool.HandleNewBlock * [NOD-1192] Broadcast transactions only if there's more then 0 * [NOD-1162] Removed double waitTillNextIteration * [NOD-1192] Rename: broadcastTransactions -> broadcastTransactionsAfterBlockAdded * [NOD-1162] Call NotifyBlocks on client3 as well * [NOD-1162] ErrTimeout and ErrRouteClosed should be ProtocolErrors * [NOD-1162] Added comment and removed redundant type PeerAddedCallback * [NOD-1162] Revert overly eager rename * [NOD-1162] Move DisalbeTLS to common config + minimize call for ioutil.TempDir() * [NOD-1162] Add some clarifications in code * [NOD-1193] Skip closed connections in NetAdapter.Broadcast * [NOD-1193] Make sure to protect connectionsToRouters from concurrent access * [NOD-1162] Add _test to all files in integration package * [NOD-1162] Introduced appHarness to better encapsulate a single node * [NOD-1162] Removed onChainChanged handler * [NOD-1162] Remove redundant closure * [NOD-1162] Correctly mark integration_test config as Simnet * [NOD-1162] Rename app.ID -> app.P2PNodeID * [NOD-1162] Move TestIntegrationBasicSync to basic_sync_test.go * [NOD-1210] Made it possible to setup any number of harnesses needed * [NOD-1210] Rename appHarness1/2 to incoming/outgoing in connect function * [NOD-1210] Add the 117-incoming-connections integration test * [NOD-1210] Delete 117-incoming-connections test because it opens too much files * [NOD-1210] Added function to notify of blocks conveniently * [NOD-1210] Added function to mine a block from-A-to-Z * [NOD-1210] Added IBD integration test * [NOD-1210] Finish test for IBD and fix bug where requestSelectedTipsIfRequired ran in handshake's goroutine * [NOD-1210] Set log level to debug * [NOD-1210] A bunch of renamings
This commit is contained in:
parent
42e50e6dc2
commit
16a658a5be
@ -5,12 +5,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/wire"
|
||||
|
||||
clientpkg "github.com/kaspanet/kaspad/rpc/client"
|
||||
)
|
||||
|
||||
func TestIntegrationBasicSync(t *testing.T) {
|
||||
appHarness1, appHarness2, appHarness3, teardown := setup(t)
|
||||
appHarness1, appHarness2, appHarness3, teardown := standardSetup(t)
|
||||
defer teardown()
|
||||
|
||||
// Connect nodes in chain: 1 <--> 2 <--> 3
|
||||
@ -18,40 +16,17 @@ func TestIntegrationBasicSync(t *testing.T) {
|
||||
connect(t, appHarness1, appHarness2)
|
||||
connect(t, appHarness2, appHarness3)
|
||||
|
||||
blockTemplate, err := appHarness1.rpcClient.GetBlockTemplate(testAddress1, "")
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting block template: %+v", err)
|
||||
}
|
||||
|
||||
block, err := clientpkg.ConvertGetBlockTemplateResultToBlock(blockTemplate)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing blockTemplate: %s", err)
|
||||
}
|
||||
|
||||
solveBlock(t, block)
|
||||
|
||||
err = appHarness2.rpcClient.NotifyBlocks()
|
||||
if err != nil {
|
||||
t.Fatalf("Error from NotifyBlocks: %+v", err)
|
||||
}
|
||||
app2OnBlockAddedChan := make(chan *wire.BlockHeader)
|
||||
appHarness2.rpcClient.onBlockAdded = func(header *wire.BlockHeader) {
|
||||
setOnBlockAddedHandler(t, appHarness2, func(header *wire.BlockHeader) {
|
||||
app2OnBlockAddedChan <- header
|
||||
}
|
||||
})
|
||||
|
||||
err = appHarness3.rpcClient.NotifyBlocks()
|
||||
if err != nil {
|
||||
t.Fatalf("Error from NotifyBlocks: %+v", err)
|
||||
}
|
||||
app3OnBlockAddedChan := make(chan *wire.BlockHeader)
|
||||
appHarness3.rpcClient.onBlockAdded = func(header *wire.BlockHeader) {
|
||||
setOnBlockAddedHandler(t, appHarness3, func(header *wire.BlockHeader) {
|
||||
app3OnBlockAddedChan <- header
|
||||
}
|
||||
})
|
||||
|
||||
err = appHarness1.rpcClient.SubmitBlock(block, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error submitting block: %s", err)
|
||||
}
|
||||
block := mineNextBlock(t, appHarness1)
|
||||
|
||||
var header *wire.BlockHeader
|
||||
select {
|
@ -21,14 +21,14 @@ const (
|
||||
rpcUser = "user"
|
||||
rpcPass = "pass"
|
||||
|
||||
testAddress1 = "kaspasim:qz3tm5pew9lrdpnn8kytgtm6a0mx772j4uw02snetn"
|
||||
testAddress1PK = "69f470ff9cd4010de7f4a95161867c49834435423526d9bab83781821cdf95bf"
|
||||
miningAddress1 = "kaspasim:qz3tm5pew9lrdpnn8kytgtm6a0mx772j4uw02snetn"
|
||||
miningAddress1PK = "69f470ff9cd4010de7f4a95161867c49834435423526d9bab83781821cdf95bf"
|
||||
|
||||
testAddress2 = "kaspasim:qqdf0vrh3u576eqzkp0s8qagc04tuj2xnu4sfskhx0"
|
||||
testAddress2PK = "aed46ef760223032d2641e086dd48d0b0a4d581811e68ccf15bed2b8fe87348e"
|
||||
miningAddress2 = "kaspasim:qqdf0vrh3u576eqzkp0s8qagc04tuj2xnu4sfskhx0"
|
||||
miningAddress2PK = "aed46ef760223032d2641e086dd48d0b0a4d581811e68ccf15bed2b8fe87348e"
|
||||
|
||||
testAddress3 = "kaspasim:qq2wz0hl73a0qcl8872wr3djplwmyulurscsqxehu2"
|
||||
testAddress3PK = "cc94a79bbccca30b0e3edff1895cbdf8d4ddcc119eacfd692970151dcc2881c2"
|
||||
miningAddress3 = "kaspasim:qq2wz0hl73a0qcl8872wr3djplwmyulurscsqxehu2"
|
||||
miningAddress3PK = "cc94a79bbccca30b0e3edff1895cbdf8d4ddcc119eacfd692970151dcc2881c2"
|
||||
|
||||
defaultTimeout = 10 * time.Second
|
||||
)
|
||||
|
@ -5,8 +5,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func connect(t *testing.T, appHarness1, appHarness2 *appHarness) {
|
||||
err := appHarness2.rpcClient.ConnectNode(appHarness1.p2pAddress)
|
||||
func connect(t *testing.T, incoming, outgoing *appHarness) {
|
||||
err := outgoing.rpcClient.ConnectNode(incoming.p2pAddress)
|
||||
if err != nil {
|
||||
t.Fatalf("Error connecting the nodes")
|
||||
}
|
||||
@ -17,7 +17,7 @@ func connect(t *testing.T, appHarness1, appHarness2 *appHarness) {
|
||||
|
||||
spawn("integration.connect-Wait for connection", func() {
|
||||
for range time.Tick(10 * time.Millisecond) {
|
||||
if isConnected(t, appHarness1, appHarness2) {
|
||||
if isConnected(t, incoming, outgoing) {
|
||||
close(onConnectedChan)
|
||||
return
|
||||
}
|
||||
|
47
integration/ibd_test.go
Normal file
47
integration/ibd_test.go
Normal file
@ -0,0 +1,47 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/locks"
|
||||
|
||||
"github.com/kaspanet/kaspad/wire"
|
||||
)
|
||||
|
||||
func TestIBD(t *testing.T) {
|
||||
const numBlocks = 100
|
||||
|
||||
syncer, syncee, _, teardown := standardSetup(t)
|
||||
defer teardown()
|
||||
|
||||
for i := 0; i < numBlocks; i++ {
|
||||
mineNextBlock(t, syncer)
|
||||
}
|
||||
|
||||
blockAddedWG := sync.WaitGroup{}
|
||||
blockAddedWG.Add(numBlocks)
|
||||
setOnBlockAddedHandler(t, syncee, func(header *wire.BlockHeader) { blockAddedWG.Done() })
|
||||
|
||||
connect(t, syncer, syncee)
|
||||
|
||||
select {
|
||||
case <-time.After(defaultTimeout):
|
||||
t.Fatalf("Timeout waiting for IBD to finish")
|
||||
case <-locks.ReceiveFromChanWhenDone(func() { blockAddedWG.Wait() }):
|
||||
}
|
||||
|
||||
tip1, err := syncer.rpcClient.GetSelectedTip()
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting tip for syncer")
|
||||
}
|
||||
tip2, err := syncee.rpcClient.GetSelectedTip()
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting tip for syncee")
|
||||
}
|
||||
|
||||
if tip1.Hash != tip2.Hash {
|
||||
t.Errorf("Tips of syncer: '%s' and syncee '%s' are not equal", tip1.Hash, tip2.Hash)
|
||||
}
|
||||
}
|
14
integration/main_test.go
Normal file
14
integration/main_test.go
Normal file
@ -0,0 +1,14 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/logger"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetLogLevels("debug")
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
@ -4,12 +4,14 @@ import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
clientpkg "github.com/kaspanet/kaspad/rpc/client"
|
||||
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"github.com/kaspanet/kaspad/util/daghash"
|
||||
"github.com/kaspanet/kaspad/wire"
|
||||
)
|
||||
|
||||
func solveBlock(t *testing.T, block *util.Block) *wire.MsgBlock {
|
||||
func solveBlock(block *util.Block) *wire.MsgBlock {
|
||||
msgBlock := block.MsgBlock()
|
||||
targetDifficulty := util.CompactToBig(msgBlock.Header.Bits)
|
||||
initialNonce := rand.Uint64()
|
||||
@ -23,3 +25,24 @@ func solveBlock(t *testing.T, block *util.Block) *wire.MsgBlock {
|
||||
|
||||
panic("Failed to solve block! This should never happen")
|
||||
}
|
||||
|
||||
func mineNextBlock(t *testing.T, harness *appHarness) *util.Block {
|
||||
blockTemplate, err := harness.rpcClient.GetBlockTemplate(harness.miningAddress, "")
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting block template: %+v", err)
|
||||
}
|
||||
|
||||
block, err := clientpkg.ConvertGetBlockTemplateResultToBlock(blockTemplate)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing blockTemplate: %s", err)
|
||||
}
|
||||
|
||||
solveBlock(block)
|
||||
|
||||
err = harness.rpcClient.SubmitBlock(block, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error submitting block: %s", err)
|
||||
}
|
||||
|
||||
return block
|
||||
}
|
||||
|
15
integration/notifications_test.go
Normal file
15
integration/notifications_test.go
Normal file
@ -0,0 +1,15 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/wire"
|
||||
)
|
||||
|
||||
func setOnBlockAddedHandler(t *testing.T, harness *appHarness, handler func(header *wire.BlockHeader)) {
|
||||
err := harness.rpcClient.NotifyBlocks()
|
||||
if err != nil {
|
||||
t.Fatalf("Error from NotifyBlocks: %s", err)
|
||||
}
|
||||
harness.rpcClient.onBlockAdded = handler
|
||||
}
|
@ -14,41 +14,64 @@ type appHarness struct {
|
||||
rpcClient *rpcClient
|
||||
p2pAddress string
|
||||
rpcAddress string
|
||||
miningAddress string
|
||||
miningAddressPK string
|
||||
config *config.Config
|
||||
databaseContext *dbaccess.DatabaseContext
|
||||
}
|
||||
|
||||
func setup(t *testing.T) (appHarness1, appHarness2, appHarness3 *appHarness, teardownFunc func()) {
|
||||
appHarness1 = &appHarness{p2pAddress: p2pAddress1, rpcAddress: rpcAddress1}
|
||||
appHarness2 = &appHarness{p2pAddress: p2pAddress2, rpcAddress: rpcAddress2}
|
||||
appHarness3 = &appHarness{p2pAddress: p2pAddress3, rpcAddress: rpcAddress3}
|
||||
type harnessParams struct {
|
||||
p2pAddress string
|
||||
rpcAddress string
|
||||
miningAddress string
|
||||
miningAddressPK string
|
||||
}
|
||||
|
||||
setConfig(t, appHarness1)
|
||||
setConfig(t, appHarness2)
|
||||
setConfig(t, appHarness3)
|
||||
// setupHarness creates a single appHarness with given parameters
|
||||
func setupHarness(t *testing.T, params *harnessParams) (harness *appHarness, teardownFunc func()) {
|
||||
harness = &appHarness{
|
||||
p2pAddress: params.p2pAddress,
|
||||
rpcAddress: params.rpcAddress,
|
||||
miningAddress: params.miningAddress,
|
||||
miningAddressPK: params.miningAddressPK,
|
||||
}
|
||||
|
||||
setDatabaseContext(t, appHarness1)
|
||||
setDatabaseContext(t, appHarness2)
|
||||
setDatabaseContext(t, appHarness3)
|
||||
setConfig(t, harness)
|
||||
setDatabaseContext(t, harness)
|
||||
setApp(t, harness)
|
||||
harness.app.Start()
|
||||
setRPCClient(t, harness)
|
||||
|
||||
setApp(t, appHarness1)
|
||||
setApp(t, appHarness2)
|
||||
setApp(t, appHarness3)
|
||||
return harness, func() {
|
||||
teardown(t, harness)
|
||||
}
|
||||
}
|
||||
|
||||
appHarness1.app.Start()
|
||||
appHarness2.app.Start()
|
||||
appHarness3.app.Start()
|
||||
// setupHarnesses creates multiple appHarnesses, according to number of parameters passed
|
||||
func setupHarnesses(t *testing.T, harnessesParams []*harnessParams) (harnesses []*appHarness, teardownFunc func()) {
|
||||
var teardowns []func()
|
||||
for _, params := range harnessesParams {
|
||||
harness, teardownFunc := setupHarness(t, params)
|
||||
harnesses = append(harnesses, harness)
|
||||
teardowns = append(teardowns, teardownFunc)
|
||||
}
|
||||
|
||||
setRPCClient(t, appHarness1)
|
||||
setRPCClient(t, appHarness2)
|
||||
setRPCClient(t, appHarness3)
|
||||
|
||||
return appHarness1, appHarness2, appHarness3,
|
||||
func() {
|
||||
teardown(t, appHarness1)
|
||||
teardown(t, appHarness2)
|
||||
teardown(t, appHarness3)
|
||||
return harnesses, func() {
|
||||
for _, teardownFunc := range teardowns {
|
||||
teardownFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// standardSetup creates a standard setup of 3 appHarnesses that should work for most tests
|
||||
func standardSetup(t *testing.T) (appHarness1, appHarness2, appHarness3 *appHarness, teardownFunc func()) {
|
||||
harnesses, teardown := setupHarnesses(t, []*harnessParams{
|
||||
{p2pAddress: p2pAddress1, rpcAddress: rpcAddress1, miningAddress: miningAddress1},
|
||||
{p2pAddress: p2pAddress2, rpcAddress: rpcAddress2, miningAddress: miningAddress2},
|
||||
{p2pAddress: p2pAddress3, rpcAddress: rpcAddress3, miningAddress: miningAddress3},
|
||||
})
|
||||
|
||||
return harnesses[0], harnesses[1], harnesses[2], teardown
|
||||
}
|
||||
|
||||
func setRPCClient(t *testing.T, harness *appHarness) {
|
||||
|
@ -1,10 +1,11 @@
|
||||
package flowcontext
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/blockdag"
|
||||
peerpkg "github.com/kaspanet/kaspad/protocol/peer"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/blockdag"
|
||||
peerpkg "github.com/kaspanet/kaspad/protocol/peer"
|
||||
)
|
||||
|
||||
// StartIBDIfRequired selects a peer and starts IBD against it
|
||||
@ -19,7 +20,7 @@ func (f *FlowContext) StartIBDIfRequired() {
|
||||
|
||||
peer := f.selectPeerForIBD(f.dag)
|
||||
if peer == nil {
|
||||
f.requestSelectedTipsIfRequired()
|
||||
spawn("StartIBDIfRequired-requestSelectedTipsIfRequired", f.requestSelectedTipsIfRequired)
|
||||
return
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user