From 609a72a6cc674aae04e427ac2cd91ba21e690f5f Mon Sep 17 00:00:00 2001 From: Arkadiusz Osowski Date: Wed, 18 Dec 2024 18:00:14 +0100 Subject: [PATCH 1/3] feat: add wirereader tests --- internal/p2p/tests/wire_reader_test.go | 119 +++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 internal/p2p/tests/wire_reader_test.go diff --git a/internal/p2p/tests/wire_reader_test.go b/internal/p2p/tests/wire_reader_test.go new file mode 100644 index 000000000..8b397a1f5 --- /dev/null +++ b/internal/p2p/tests/wire_reader_test.go @@ -0,0 +1,119 @@ +package p2p_tests + +import ( + "bytes" + "context" + "io" + "testing" + "time" + + "github.com/bitcoin-sv/arc/internal/p2p" + "github.com/libsv/go-p2p/wire" + "github.com/stretchr/testify/require" +) + +func TestWireReader_ReadNextMsg(t *testing.T) { + t.Run("Success", func(t *testing.T) { + // given + expectedMsg := wire.NewMsgGetBlocks(blockHash) + + var buff bytes.Buffer + err := wire.WriteMessage(&buff, expectedMsg, wire.ProtocolVersion, bitcoinNet) + require.NoError(t, err) + + sut := p2p.NewWireReader(&buff, 4096) + + // when + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + res, err := sut.ReadNextMsg(ctx, wire.ProtocolVersion, bitcoinNet) + + // then + require.NoError(t, err) + require.Equal(t, expectedMsg, res) + }) + + t.Run("Unknown msg", func(t *testing.T) { + // given + unknownMsg := unknownMsg{} + + expectedMsg := wire.NewMsgGetBlocks(blockHash) + + var buff bytes.Buffer + // first write unknown msg + err := wire.WriteMessage(&buff, &unknownMsg, wire.ProtocolVersion, bitcoinNet) + require.NoError(t, err) + + // next write regular msg + err = wire.WriteMessage(&buff, expectedMsg, wire.ProtocolVersion, bitcoinNet) + require.NoError(t, err) + + sut := p2p.NewWireReader(&buff, 4096) + + // when + ctx, cancel := context.WithTimeout(context.Background(), 35*time.Second) + defer cancel() + + res, err := sut.ReadNextMsg(ctx, wire.ProtocolVersion, bitcoinNet) + + // then + require.NoError(t, err) + require.Equal(t, expectedMsg, res) + }) + + t.Run("Context cancelled", func(t *testing.T) { + // given + expectedMsg := wire.NewMsgGetBlocks(blockHash) + + var buff bytes.Buffer + err := wire.WriteMessage(&buff, expectedMsg, wire.ProtocolVersion, bitcoinNet) + require.NoError(t, err) + + sut := p2p.NewWireReader(&buff, 4096) + + // when + ctx, cancel := context.WithCancel(context.Background()) + cancel() // Cancel the context immediately + + res, err := sut.ReadNextMsg(ctx, wire.ProtocolVersion, bitcoinNet) + + // then + require.ErrorIs(t, err, context.Canceled) + require.Nil(t, res) + }) + + t.Run("Read error", func(t *testing.T) { + var buff bytes.Buffer + sut := p2p.NewWireReader(&buff, 4096) + + // when + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + res, err := sut.ReadNextMsg(ctx, wire.ProtocolVersion, bitcoinNet) + + // then + require.Error(t, err) + require.Nil(t, res) + }) +} + +type unknownMsg struct { +} + +func (m *unknownMsg) Bsvdecode(_ io.Reader, _ uint32, _ wire.MessageEncoding) error { + return nil +} + +func (m *unknownMsg) BsvEncode(_ io.Writer, _ uint32, _ wire.MessageEncoding) error { + return nil +} + +func (m *unknownMsg) Command() string { + return "test-cmd" +} + +func (m *unknownMsg) MaxPayloadLength(_ uint32) uint64 { + return 0 +} From dc3d060682ddcb6cf14f151ff3f268868798faea Mon Sep 17 00:00:00 2001 From: Arkadiusz Osowski Date: Wed, 18 Dec 2024 18:55:31 +0100 Subject: [PATCH 2/3] feat: use internal p2p in blocktx --- cmd/arc/services/blocktx.go | 24 +- go.mod | 2 +- go.sum | 2 - internal/blocktx/background_workers.go | 9 +- internal/blocktx/background_workers_test.go | 13 +- internal/blocktx/bcnet/block_message.go | 30 + .../bcnet/blocktx_p2p/message_handler.go | 78 +++ .../{peer_handler.go => bcnet/init.go} | 69 +-- internal/blocktx/bcnet/init_test.go | 33 ++ internal/blocktx/health_check.go | 16 +- internal/blocktx/health_check_test.go | 2 +- internal/blocktx/integration_test/helpers.go | 9 +- .../reorg_integration_test.go | 37 +- internal/blocktx/interface.go | 41 -- internal/blocktx/mocks/peer_manager_mock.go | 339 ------------ internal/blocktx/mocks/peer_mock.go | 512 ------------------ internal/blocktx/processor.go | 33 +- internal/blocktx/processor_test.go | 38 +- internal/blocktx/server.go | 7 +- internal/blocktx/server_test.go | 2 +- 20 files changed, 238 insertions(+), 1058 deletions(-) create mode 100644 internal/blocktx/bcnet/block_message.go create mode 100644 internal/blocktx/bcnet/blocktx_p2p/message_handler.go rename internal/blocktx/{peer_handler.go => bcnet/init.go} (56%) create mode 100644 internal/blocktx/bcnet/init_test.go delete mode 100644 internal/blocktx/interface.go delete mode 100644 internal/blocktx/mocks/peer_manager_mock.go delete mode 100644 internal/blocktx/mocks/peer_mock.go diff --git a/cmd/arc/services/blocktx.go b/cmd/arc/services/blocktx.go index cf6c44389..90a860f55 100644 --- a/cmd/arc/services/blocktx.go +++ b/cmd/arc/services/blocktx.go @@ -13,12 +13,14 @@ import ( "github.com/bitcoin-sv/arc/internal/message_queue/nats/client/nats_jetstream" "github.com/bitcoin-sv/arc/internal/message_queue/nats/nats_connection" "github.com/bitcoin-sv/arc/internal/tracing" - "github.com/libsv/go-p2p" "github.com/bitcoin-sv/arc/config" "github.com/bitcoin-sv/arc/internal/blocktx" + "github.com/bitcoin-sv/arc/internal/blocktx/bcnet" + "github.com/bitcoin-sv/arc/internal/blocktx/bcnet/blocktx_p2p" "github.com/bitcoin-sv/arc/internal/blocktx/store" "github.com/bitcoin-sv/arc/internal/blocktx/store/postgresql" + "github.com/bitcoin-sv/arc/internal/p2p" "github.com/bitcoin-sv/arc/internal/version" ) @@ -38,7 +40,7 @@ func StartBlockTx(logger *slog.Logger, arcConfig *config.ArcConfig) (func(), err blockStore store.BlocktxStore mqClient blocktx.MessageQueueClient processor *blocktx.Processor - pm p2p.PeerManagerI + pm *p2p.PeerManager server *blocktx.Server healthServer *grpc_opts.GrpcServer workers *blocktx.BackgroundWorkers @@ -129,8 +131,8 @@ func StartBlockTx(logger *slog.Logger, arcConfig *config.ArcConfig) (func(), err blocktx.WithMaxBlockProcessingDuration(btxConfig.MaxBlockProcessingDuration), ) - blockRequestCh := make(chan blocktx.BlockRequest, blockProcessingBuffer) - blockProcessCh := make(chan *p2p.BlockMessage, blockProcessingBuffer) + blockRequestCh := make(chan blocktx_p2p.BlockRequest, blockProcessingBuffer) + blockProcessCh := make(chan *bcnet.BlockMessage, blockProcessingBuffer) processor, err = blocktx.NewProcessor(logger, blockStore, blockRequestCh, blockProcessCh, processorOpts...) if err != nil { @@ -144,7 +146,10 @@ func StartBlockTx(logger *slog.Logger, arcConfig *config.ArcConfig) (func(), err return nil, fmt.Errorf("failed to start peer handler: %v", err) } - pmOpts := []p2p.PeerManagerOptions{p2p.WithExcessiveBlockSize(maximumBlockSize)} + // p2p global setting + p2p.SetExcessiveBlockSize(maximumBlockSize) + + pmOpts := []p2p.PeerManagerOptions{} if arcConfig.Blocktx.MonitorPeers { pmOpts = append(pmOpts, p2p.WithRestartUnhealthyPeers()) } @@ -152,7 +157,7 @@ func StartBlockTx(logger *slog.Logger, arcConfig *config.ArcConfig) (func(), err pm = p2p.NewPeerManager(logger.With(slog.String("module", "peer-mng")), network, pmOpts...) peers := make([]p2p.PeerI, len(arcConfig.Broadcasting.Unicast.Peers)) - peerHandler := blocktx.NewPeerHandler(logger, blockRequestCh, blockProcessCh) + peerHandler := blocktx_p2p.NewMsgHandler(logger, blockRequestCh, blockProcessCh) peerOpts := []p2p.PeerOptions{ p2p.WithMaximumMessageSize(maximumBlockSize), @@ -171,8 +176,9 @@ func StartBlockTx(logger *slog.Logger, arcConfig *config.ArcConfig) (func(), err return nil, fmt.Errorf("error getting peer url: %v", err) } - peer, err := p2p.NewPeer(logger.With(slog.String("module", "peer")), peerURL, peerHandler, network, peerOpts...) - if err != nil { + peer := p2p.NewPeer(logger.With(slog.String("module", "peer")), peerHandler, peerURL, network, peerOpts...) + ok := peer.Connect() + if !ok { stopFn() return nil, fmt.Errorf("error creating peer %s: %v", peerURL, err) } @@ -245,7 +251,7 @@ func NewBlocktxStore(logger *slog.Logger, dbConfig *config.DbConfig, tracingConf } func disposeBlockTx(l *slog.Logger, server *blocktx.Server, processor *blocktx.Processor, - pm p2p.PeerManagerI, mqClient blocktx.MessageQueueClient, + pm *p2p.PeerManager, mqClient blocktx.MessageQueueClient, store store.BlocktxStore, healthServer *grpc_opts.GrpcServer, workers *blocktx.BackgroundWorkers, shutdownFns []func(), ) { diff --git a/go.mod b/go.mod index 8e8d332eb..77d7d3ad9 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,6 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 go.opentelemetry.io/otel/sdk v1.28.0 go.opentelemetry.io/otel/trace v1.31.0 - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 google.golang.org/genproto/googleapis/rpc v0.0.0-20240805194559-2c9e96a0b5d4 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 @@ -157,6 +156,7 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.28.0 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/net v0.30.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/sync v0.8.0 // indirect diff --git a/go.sum b/go.sum index ee2756a92..58d92b8fc 100644 --- a/go.sum +++ b/go.sum @@ -213,8 +213,6 @@ github.com/libsv/go-bt v1.0.4 h1:2Css5lfomk/J97tM5Gk56Lp+tTK6xWYnmHNc/fGO6lE= github.com/libsv/go-bt v1.0.4/go.mod h1:AfXoLFYEbY/TvCq/84xTce2xGjPUuC5imokHmcykF2k= github.com/libsv/go-bt/v2 v2.2.5 h1:VoggBLMRW9NYoFujqe5bSYKqnw5y+fYfufgERSoubog= github.com/libsv/go-bt/v2 v2.2.5/go.mod h1:cV45+jDlPOLfhJLfpLmpQoWzrIvVth9Ao2ZO1f6CcqU= -github.com/libsv/go-p2p v0.3.2 h1:O32CzkqM+jhSuleRHJln6JjL2pKH8aaRTx8lAfhIiic= -github.com/libsv/go-p2p v0.3.2/go.mod h1:TENFxbTT/bfSfuiirjU6l+PfAWxwZgF8GYUxs5tzc/M= github.com/libsv/go-p2p v0.3.3 h1:5h+69MsGgFwQWyD8MEqyPeqbqKGRpKLzzOcI5cSLfgY= github.com/libsv/go-p2p v0.3.3/go.mod h1:TENFxbTT/bfSfuiirjU6l+PfAWxwZgF8GYUxs5tzc/M= github.com/lmittmann/tint v1.0.5 h1:NQclAutOfYsqs2F1Lenue6OoWCajs5wJcP3DfWVpePw= diff --git a/internal/blocktx/background_workers.go b/internal/blocktx/background_workers.go index 7ef9cf22b..a66c567dd 100644 --- a/internal/blocktx/background_workers.go +++ b/internal/blocktx/background_workers.go @@ -6,8 +6,9 @@ import ( "sync" "time" + "github.com/bitcoin-sv/arc/internal/blocktx/bcnet/blocktx_p2p" "github.com/bitcoin-sv/arc/internal/blocktx/store" - "github.com/libsv/go-p2p" + "github.com/bitcoin-sv/arc/internal/p2p" ) type BackgroundWorkers struct { @@ -40,7 +41,7 @@ func (w *BackgroundWorkers) GracefulStop() { w.l.Info("Shutdown complete") } -func (w *BackgroundWorkers) StartFillGaps(peers []p2p.PeerI, interval time.Duration, retentionDays int, blockRequestingCh chan<- BlockRequest) { +func (w *BackgroundWorkers) StartFillGaps(peers []p2p.PeerI, interval time.Duration, retentionDays int, blockRequestingCh chan<- blocktx_p2p.BlockRequest) { w.workersWg.Add(1) go func() { @@ -68,7 +69,7 @@ func (w *BackgroundWorkers) StartFillGaps(peers []p2p.PeerI, interval time.Durat }() } -func (w *BackgroundWorkers) fillGaps(peer p2p.PeerI, retentionDays int, blockRequestingCh chan<- BlockRequest) error { +func (w *BackgroundWorkers) fillGaps(peer p2p.PeerI, retentionDays int, blockRequestingCh chan<- blocktx_p2p.BlockRequest) error { const ( hoursPerDay = 24 blocksPerHour = 6 @@ -91,7 +92,7 @@ func (w *BackgroundWorkers) fillGaps(peer p2p.PeerI, retentionDays int, blockReq slog.String("peer", peer.String()), ) - blockRequestingCh <- BlockRequest{ + blockRequestingCh <- blocktx_p2p.BlockRequest{ Hash: block.Hash, Peer: peer, } diff --git a/internal/blocktx/background_workers_test.go b/internal/blocktx/background_workers_test.go index edcdd5c90..f94a6b13a 100644 --- a/internal/blocktx/background_workers_test.go +++ b/internal/blocktx/background_workers_test.go @@ -8,16 +8,15 @@ import ( "testing" "time" - "github.com/libsv/go-p2p" - "github.com/bitcoin-sv/arc/internal/blocktx" - "github.com/bitcoin-sv/arc/internal/blocktx/mocks" + "github.com/bitcoin-sv/arc/internal/blocktx/bcnet/blocktx_p2p" "github.com/bitcoin-sv/arc/internal/blocktx/store" storeMocks "github.com/bitcoin-sv/arc/internal/blocktx/store/mocks" - "github.com/stretchr/testify/require" - + "github.com/bitcoin-sv/arc/internal/p2p" + p2pMocks "github.com/bitcoin-sv/arc/internal/p2p/mocks" "github.com/bitcoin-sv/arc/internal/testdata" + "github.com/stretchr/testify/require" ) func TestStartFillGaps(t *testing.T) { @@ -69,7 +68,7 @@ func TestStartFillGaps(t *testing.T) { // given const fillGapsInterval = 50 * time.Millisecond - blockRequestingCh := make(chan blocktx.BlockRequest, 10) + blockRequestingCh := make(chan blocktx_p2p.BlockRequest, 10) getBlockErrCh := make(chan error) getBlockGapTestErr := tc.getBlockGapsErr @@ -84,7 +83,7 @@ func TestStartFillGaps(t *testing.T) { }, } - peerMock := &mocks.PeerMock{ + peerMock := &p2pMocks.PeerIMock{ StringFunc: func() string { return "" }, diff --git a/internal/blocktx/bcnet/block_message.go b/internal/blocktx/bcnet/block_message.go new file mode 100644 index 000000000..2114465c5 --- /dev/null +++ b/internal/blocktx/bcnet/block_message.go @@ -0,0 +1,30 @@ +package bcnet + +import ( + "io" + + "github.com/libsv/go-p2p/chaincfg/chainhash" + "github.com/libsv/go-p2p/wire" +) + +// BlockMessage only stores the transaction IDs of the block, not the full transactions +type BlockMessage struct { + Hash *chainhash.Hash + Header *wire.BlockHeader + Height uint64 + TransactionHashes []*chainhash.Hash + Size uint64 +} + +func (bm *BlockMessage) Bsvdecode(io.Reader, uint32, wire.MessageEncoding) error { + return nil +} +func (bm *BlockMessage) BsvEncode(io.Writer, uint32, wire.MessageEncoding) error { + return nil +} +func (bm *BlockMessage) Command() string { + return "block" +} +func (bm *BlockMessage) MaxPayloadLength(uint32) uint64 { + return wire.MaxExtMsgPayload +} diff --git a/internal/blocktx/bcnet/blocktx_p2p/message_handler.go b/internal/blocktx/bcnet/blocktx_p2p/message_handler.go new file mode 100644 index 000000000..a4aafbe3d --- /dev/null +++ b/internal/blocktx/bcnet/blocktx_p2p/message_handler.go @@ -0,0 +1,78 @@ +package blocktx_p2p + +import ( + "errors" + "log/slog" + + "github.com/bitcoin-sv/arc/internal/blocktx/bcnet" + "github.com/bitcoin-sv/arc/internal/p2p" + "github.com/libsv/go-p2p/chaincfg/chainhash" + "github.com/libsv/go-p2p/wire" +) + +var ErrUnableToCastWireMessage = errors.New("unable to cast wire.Message to blockchain.BlockMessage") + +type BlockRequest struct { + Hash *chainhash.Hash + Peer p2p.PeerI +} + +var _ p2p.MessageHandlerI = (*MsgHandler)(nil) + +type MsgHandler struct { + logger *slog.Logger + blockRequestingCh chan<- BlockRequest + blockProcessingCh chan<- *bcnet.BlockMessage +} + +func NewMsgHandler(logger *slog.Logger, blockRequestCh chan<- BlockRequest, blockProcessCh chan<- *bcnet.BlockMessage) *MsgHandler { + return &MsgHandler{ + logger: logger.With(slog.String("module", "peer-msg-handler")), + blockRequestingCh: blockRequestCh, + blockProcessingCh: blockProcessCh, + } +} + +// OnReceive handles incoming messages depending on command type +func (h *MsgHandler) OnReceive(msg wire.Message, peer p2p.PeerI) { + cmd := msg.Command() + + switch cmd { + case wire.CmdInv: + invMsg, ok := msg.(*wire.MsgInv) + if !ok { + return + } + + go func() { + for _, iv := range invMsg.InvList { + if iv.Type == wire.InvTypeBlock { + req := BlockRequest{ + Hash: &iv.Hash, + Peer: peer, + } + + h.blockRequestingCh <- req + } + // ignore INV with transaction or error + } + }() + + case wire.CmdBlock: + blockMsg, ok := msg.(*bcnet.BlockMessage) + if !ok { + h.logger.Error("Block msg receive", slog.Any("err", ErrUnableToCastWireMessage)) + return + } + + h.blockProcessingCh <- blockMsg + + default: + // ignore other messages + } +} + +// OnSend handles outgoing messages depending on command type +func (h *MsgHandler) OnSend(_ wire.Message, _ p2p.PeerI) { + // ignore +} diff --git a/internal/blocktx/peer_handler.go b/internal/blocktx/bcnet/init.go similarity index 56% rename from internal/blocktx/peer_handler.go rename to internal/blocktx/bcnet/init.go index 179adeb24..88dc6255e 100644 --- a/internal/blocktx/peer_handler.go +++ b/internal/blocktx/bcnet/init.go @@ -1,25 +1,20 @@ -package blocktx +package bcnet import ( "encoding/binary" - "errors" "io" - "log/slog" "github.com/bitcoin-sv/go-sdk/script" sdkTx "github.com/bitcoin-sv/go-sdk/transaction" "github.com/bitcoin-sv/go-sdk/util" - "github.com/libsv/go-p2p" "github.com/libsv/go-p2p/chaincfg/chainhash" "github.com/libsv/go-p2p/wire" ) -var ErrUnableToCastWireMessage = errors.New("unable to cast wire.Message to p2p.BlockMessage") - func init() { // override the default wire block handler with our own that streams and stores only the transaction ids wire.SetExternalHandler(wire.CmdBlock, func(reader io.Reader, _ uint64, bytesRead int) (int, wire.Message, []byte, error) { - blockMessage := &p2p.BlockMessage{ + blockMessage := &BlockMessage{ Header: &wire.BlockHeader{}, } @@ -42,7 +37,7 @@ func init() { var tx *sdkTx.Transaction var hash *chainhash.Hash var txBytes []byte - for i := 0; i < int(txCount); i++ { + for i := uint64(0); i < uint64(txCount); i++ { tx = sdkTx.NewTransaction() read, err = tx.ReadFrom(reader) if err != nil { @@ -62,66 +57,14 @@ func init() { } } - blockMessage.Size = uint64(bytesRead) + blockMessage.Size = uint64(bytesRead) // #nosec G115 + blockHash := blockMessage.Header.BlockHash() + blockMessage.Hash = &blockHash return bytesRead, blockMessage, nil, nil }) } -type PeerHandler struct { - logger *slog.Logger - blockRequestCh chan BlockRequest - blockProcessCh chan *p2p.BlockMessage -} - -func NewPeerHandler(logger *slog.Logger, blockRequestCh chan BlockRequest, blockProcessCh chan *p2p.BlockMessage) *PeerHandler { - return &PeerHandler{ - logger: logger.With(slog.String("module", "peer-handler")), - blockRequestCh: blockRequestCh, - blockProcessCh: blockProcessCh, - } -} - -func (ph *PeerHandler) HandleTransactionsGet(_ []*wire.InvVect, _ p2p.PeerI) ([][]byte, error) { - return nil, nil -} - -func (ph *PeerHandler) HandleTransactionSent(_ *wire.MsgTx, _ p2p.PeerI) error { - return nil -} - -func (ph *PeerHandler) HandleTransactionAnnouncement(_ *wire.InvVect, _ p2p.PeerI) error { - return nil -} - -func (ph *PeerHandler) HandleTransactionRejection(_ *wire.MsgReject, _ p2p.PeerI) error { - return nil -} - -func (ph *PeerHandler) HandleTransaction(_ *wire.MsgTx, _ p2p.PeerI) error { - return nil -} - -func (ph *PeerHandler) HandleBlockAnnouncement(msg *wire.InvVect, peer p2p.PeerI) error { - req := BlockRequest{ - Hash: &msg.Hash, - Peer: peer, - } - - ph.blockRequestCh <- req - return nil -} - -func (ph *PeerHandler) HandleBlock(wireMsg wire.Message, _ p2p.PeerI) error { - msg, ok := wireMsg.(*p2p.BlockMessage) - if !ok { - return ErrUnableToCastWireMessage - } - - ph.blockProcessCh <- msg - return nil -} - func extractHeightFromCoinbaseTx(tx *sdkTx.Transaction) uint64 { // Coinbase tx has a special format, the height is encoded in the first 4 bytes of the scriptSig (BIP-34) diff --git a/internal/blocktx/bcnet/init_test.go b/internal/blocktx/bcnet/init_test.go new file mode 100644 index 000000000..095c94f8f --- /dev/null +++ b/internal/blocktx/bcnet/init_test.go @@ -0,0 +1,33 @@ +package bcnet + +import ( + "testing" + + sdkTx "github.com/bitcoin-sv/go-sdk/transaction" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestExtractHeight(t *testing.T) { + // given + tx, err := sdkTx.NewTransactionFromHex("01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff570350cc0b041547b5630cfabe6d6d0000000000000000000000000000000000000000000000000000000000000000010000000000000047ed20542096bd0000000000143362663865373833636662643732306431383436000000000140be4025000000001976a914c9b0abe09b7dd8e9d1e8c1e3502d32ab0d7119e488ac00000000") + require.NoError(t, err) + + // when + height := extractHeightFromCoinbaseTx(tx) + + // then + assert.Equalf(t, uint64(773200), height, "height should be 773200, got %d", height) +} + +func TestExtractHeightForRegtest(t *testing.T) { + // given + tx, err := sdkTx.NewTransactionFromHex("02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0502dc070101ffffffff012f500900000000002321032efe256e14fd77eea05d0453374f8920e0a7a4a573bb3937ef3f567f3937129cac00000000") + require.NoError(t, err) + + // when + height := extractHeightFromCoinbaseTx(tx) + + // then + assert.Equalf(t, uint64(2012), height, "height should be 2012, got %d", height) +} diff --git a/internal/blocktx/health_check.go b/internal/blocktx/health_check.go index 72180cab8..f6c556fe8 100644 --- a/internal/blocktx/health_check.go +++ b/internal/blocktx/health_check.go @@ -31,13 +31,7 @@ func (s *Server) Check(ctx context.Context, req *grpc_health_v1.HealthCheckReque } // verify we have at least 1 node connected to blocktx - healthy := false - for _, peer := range s.pm.GetPeers() { - if peer.IsHealthy() && peer.Connected() { - healthy = true - break - } - } + healthy := s.pm.CountConnectedPeers() > 0 if !healthy { s.logger.Error("healthy peer not found") @@ -66,13 +60,7 @@ func (s *Server) Watch(req *grpc_health_v1.HealthCheckRequest, server grpc_healt } // verify we have at least 1 node connected to blocktx - healthy := false - for _, peer := range s.pm.GetPeers() { - if peer.IsHealthy() && peer.Connected() { - healthy = true - break - } - } + healthy := s.pm.CountConnectedPeers() > 0 if !healthy { s.logger.Error("healthy peer not found") diff --git a/internal/blocktx/health_check_test.go b/internal/blocktx/health_check_test.go index faffb0513..bf8abc5fa 100644 --- a/internal/blocktx/health_check_test.go +++ b/internal/blocktx/health_check_test.go @@ -7,7 +7,7 @@ import ( "os" "testing" - "github.com/libsv/go-p2p" + "github.com/bitcoin-sv/arc/internal/p2p" "github.com/stretchr/testify/require" "google.golang.org/grpc/health/grpc_health_v1" diff --git a/internal/blocktx/integration_test/helpers.go b/internal/blocktx/integration_test/helpers.go index 5e1d8540c..9e498a0fe 100644 --- a/internal/blocktx/integration_test/helpers.go +++ b/internal/blocktx/integration_test/helpers.go @@ -7,11 +7,12 @@ import ( "os" "testing" - "github.com/libsv/go-p2p" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" "github.com/bitcoin-sv/arc/internal/blocktx" + "github.com/bitcoin-sv/arc/internal/blocktx/bcnet" + "github.com/bitcoin-sv/arc/internal/blocktx/bcnet/blocktx_p2p" "github.com/bitcoin-sv/arc/internal/blocktx/blocktx_api" "github.com/bitcoin-sv/arc/internal/blocktx/store/postgresql" "github.com/bitcoin-sv/arc/internal/message_queue/nats/client/nats_core" @@ -19,12 +20,12 @@ import ( testutils "github.com/bitcoin-sv/arc/internal/test_utils" ) -func setupSut(t *testing.T, dbInfo string) (*blocktx.Processor, *blocktx.PeerHandler, *postgresql.PostgreSQL, chan []byte, chan *blocktx_api.TransactionBlock) { +func setupSut(t *testing.T, dbInfo string) (*blocktx.Processor, *blocktx_p2p.MsgHandler, *postgresql.PostgreSQL, chan []byte, chan *blocktx_api.TransactionBlock) { t.Helper() logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})) - blockProcessCh := make(chan *p2p.BlockMessage, 10) + blockProcessCh := make(chan *bcnet.BlockMessage, 10) requestTxChannel := make(chan []byte, 10) publishedTxsCh := make(chan *blocktx_api.TransactionBlock, 10) @@ -44,7 +45,7 @@ func setupSut(t *testing.T, dbInfo string) (*blocktx.Processor, *blocktx.PeerHan } mqClient := nats_core.New(mockNatsConn, nats_core.WithLogger(logger)) - p2pMsgHandler := blocktx.NewPeerHandler(logger, nil, blockProcessCh) + p2pMsgHandler := blocktx_p2p.NewMsgHandler(logger, nil, blockProcessCh) processor, err := blocktx.NewProcessor( logger, store, diff --git a/internal/blocktx/integration_test/reorg_integration_test.go b/internal/blocktx/integration_test/reorg_integration_test.go index ed754d4b2..3077fd2de 100644 --- a/internal/blocktx/integration_test/reorg_integration_test.go +++ b/internal/blocktx/integration_test/reorg_integration_test.go @@ -32,12 +32,12 @@ import ( "testing" "time" + "github.com/bitcoin-sv/arc/internal/blocktx/bcnet" "github.com/bitcoin-sv/arc/internal/blocktx/blocktx_api" _ "github.com/golang-migrate/migrate/v4/source/file" _ "github.com/lib/pq" "github.com/libsv/go-bc" - "github.com/libsv/go-p2p" "github.com/libsv/go-p2p/chaincfg/chainhash" "github.com/libsv/go-p2p/wire" "github.com/stretchr/testify/require" @@ -68,8 +68,8 @@ func TestReorg(t *testing.T) { require.NoError(t, err) // should become LONGEST - blockMessage := &p2p.BlockMessage{ - // Hash: blockHash, + blockMessage := &bcnet.BlockMessage{ + //Hash: blockHash, Header: &wire.BlockHeader{ Version: 541065216, PrevBlock: *prevBlockHash, // NON-existent in the db @@ -81,8 +81,7 @@ func TestReorg(t *testing.T) { } processor.StartBlockProcessing() - err = p2pMsgHandler.HandleBlock(blockMessage, nil) - require.NoError(t, err) + p2pMsgHandler.OnReceive(blockMessage, nil) // Allow DB to process the block time.Sleep(200 * time.Millisecond) @@ -112,8 +111,8 @@ func TestReorg(t *testing.T) { merkleRoot := treeStore[len(treeStore)-1] // should become STALE - blockMessage := &p2p.BlockMessage{ - // Hash: blockHash, + blockMessage := &bcnet.BlockMessage{ + //Hash: blockHash, Header: &wire.BlockHeader{ Version: 541065216, PrevBlock: *prevBlockHash, // block with status LONGEST at height 822014 @@ -125,8 +124,7 @@ func TestReorg(t *testing.T) { } processor.StartBlockProcessing() - err := p2pMsgHandler.HandleBlock(blockMessage, nil) - require.NoError(t, err) + p2pMsgHandler.OnReceive(blockMessage, nil) // Allow DB to process the block time.Sleep(200 * time.Millisecond) @@ -171,8 +169,8 @@ func TestReorg(t *testing.T) { // should become LONGEST // reorg should happen - blockMessage := &p2p.BlockMessage{ - // Hash: blockHash, + blockMessage := &bcnet.BlockMessage{ + //Hash: blockHash, Header: &wire.BlockHeader{ Version: 541065216, PrevBlock: *prevhash, // block with status STALE at height 822015 @@ -184,8 +182,7 @@ func TestReorg(t *testing.T) { } processor.StartBlockProcessing() - err := p2pMsgHandler.HandleBlock(blockMessage, nil) - require.NoError(t, err) + p2pMsgHandler.OnReceive(blockMessage, nil) // Allow DB to process the block and perform reorg time.Sleep(1 * time.Second) @@ -258,8 +255,8 @@ func TestReorg(t *testing.T) { prevhash := testutils.RevChainhash(t, blockHash822020Orphan) // should become STALE - blockMessage := &p2p.BlockMessage{ - // Hash: blockHash, + blockMessage := &bcnet.BlockMessage{ + //Hash: blockHash, Header: &wire.BlockHeader{ Version: 541065216, PrevBlock: *prevhash, // block with status ORPHANED at height 822020 - connected to STALE chain @@ -271,8 +268,7 @@ func TestReorg(t *testing.T) { } processor.StartBlockProcessing() - err := p2pMsgHandler.HandleBlock(blockMessage, nil) - require.NoError(t, err) + p2pMsgHandler.OnReceive(blockMessage, nil) // Allow DB to process the block and find orphans time.Sleep(1 * time.Second) @@ -332,8 +328,8 @@ func TestReorg(t *testing.T) { // should become LONGEST // reorg should happen - blockMessage := &p2p.BlockMessage{ - // Hash: blockHash, + blockMessage := &bcnet.BlockMessage{ + //Hash: blockHash, Header: &wire.BlockHeader{ Version: 541065216, PrevBlock: *prevhash, // block with status ORPHANED at height 822020 - connected to STALE chain @@ -346,8 +342,7 @@ func TestReorg(t *testing.T) { } processor.StartBlockProcessing() - err := p2pMsgHandler.HandleBlock(blockMessage, nil) - require.NoError(t, err) + p2pMsgHandler.OnReceive(blockMessage, nil) // Allow DB to process the block, find orphans and perform reorg time.Sleep(3 * time.Second) diff --git a/internal/blocktx/interface.go b/internal/blocktx/interface.go deleted file mode 100644 index 9fa6d5264..000000000 --- a/internal/blocktx/interface.go +++ /dev/null @@ -1,41 +0,0 @@ -package blocktx - -import ( - "errors" - - "github.com/libsv/go-p2p" - "github.com/libsv/go-p2p/chaincfg/chainhash" - "github.com/libsv/go-p2p/wire" -) - -var ErrMerklePathNotFoundForTransaction = errors.New("merkle path not found for transaction") - -type BlockRequest struct { - Hash *chainhash.Hash - Peer p2p.PeerI -} - -type Peer interface { - Connected() bool - WriteMsg(msg wire.Message) error - String() string - AnnounceTransaction(txHash *chainhash.Hash) - RequestTransaction(txHash *chainhash.Hash) - AnnounceBlock(blockHash *chainhash.Hash) - RequestBlock(blockHash *chainhash.Hash) - Network() wire.BitcoinNet - IsHealthy() bool - IsUnhealthyCh() <-chan struct{} - Shutdown() - Restart() -} - -type PeerManager interface { - AnnounceTransaction(txHash *chainhash.Hash, peers []p2p.PeerI) []p2p.PeerI - RequestTransaction(txHash *chainhash.Hash) p2p.PeerI - AnnounceBlock(blockHash *chainhash.Hash, peers []p2p.PeerI) []p2p.PeerI - RequestBlock(blockHash *chainhash.Hash) p2p.PeerI - AddPeer(peer p2p.PeerI) error - GetPeers() []p2p.PeerI - Shutdown() -} diff --git a/internal/blocktx/mocks/peer_manager_mock.go b/internal/blocktx/mocks/peer_manager_mock.go deleted file mode 100644 index 646f80919..000000000 --- a/internal/blocktx/mocks/peer_manager_mock.go +++ /dev/null @@ -1,339 +0,0 @@ -// Code generated by moq; DO NOT EDIT. -// github.com/matryer/moq - -package mocks - -import ( - "github.com/bitcoin-sv/arc/internal/blocktx" - "github.com/libsv/go-p2p" - "github.com/libsv/go-p2p/chaincfg/chainhash" - "sync" -) - -// Ensure, that PeerManagerMock does implement blocktx.PeerManager. -// If this is not the case, regenerate this file with moq. -var _ blocktx.PeerManager = &PeerManagerMock{} - -// PeerManagerMock is a mock implementation of blocktx.PeerManager. -// -// func TestSomethingThatUsesPeerManager(t *testing.T) { -// -// // make and configure a mocked blocktx.PeerManager -// mockedPeerManager := &PeerManagerMock{ -// AddPeerFunc: func(peer p2p.PeerI) error { -// panic("mock out the AddPeer method") -// }, -// AnnounceBlockFunc: func(blockHash *chainhash.Hash, peers []p2p.PeerI) []p2p.PeerI { -// panic("mock out the AnnounceBlock method") -// }, -// AnnounceTransactionFunc: func(txHash *chainhash.Hash, peers []p2p.PeerI) []p2p.PeerI { -// panic("mock out the AnnounceTransaction method") -// }, -// GetPeersFunc: func() []p2p.PeerI { -// panic("mock out the GetPeers method") -// }, -// RequestBlockFunc: func(blockHash *chainhash.Hash) p2p.PeerI { -// panic("mock out the RequestBlock method") -// }, -// RequestTransactionFunc: func(txHash *chainhash.Hash) p2p.PeerI { -// panic("mock out the RequestTransaction method") -// }, -// ShutdownFunc: func() { -// panic("mock out the Shutdown method") -// }, -// } -// -// // use mockedPeerManager in code that requires blocktx.PeerManager -// // and then make assertions. -// -// } -type PeerManagerMock struct { - // AddPeerFunc mocks the AddPeer method. - AddPeerFunc func(peer p2p.PeerI) error - - // AnnounceBlockFunc mocks the AnnounceBlock method. - AnnounceBlockFunc func(blockHash *chainhash.Hash, peers []p2p.PeerI) []p2p.PeerI - - // AnnounceTransactionFunc mocks the AnnounceTransaction method. - AnnounceTransactionFunc func(txHash *chainhash.Hash, peers []p2p.PeerI) []p2p.PeerI - - // GetPeersFunc mocks the GetPeers method. - GetPeersFunc func() []p2p.PeerI - - // RequestBlockFunc mocks the RequestBlock method. - RequestBlockFunc func(blockHash *chainhash.Hash) p2p.PeerI - - // RequestTransactionFunc mocks the RequestTransaction method. - RequestTransactionFunc func(txHash *chainhash.Hash) p2p.PeerI - - // ShutdownFunc mocks the Shutdown method. - ShutdownFunc func() - - // calls tracks calls to the methods. - calls struct { - // AddPeer holds details about calls to the AddPeer method. - AddPeer []struct { - // Peer is the peer argument value. - Peer p2p.PeerI - } - // AnnounceBlock holds details about calls to the AnnounceBlock method. - AnnounceBlock []struct { - // BlockHash is the blockHash argument value. - BlockHash *chainhash.Hash - // Peers is the peers argument value. - Peers []p2p.PeerI - } - // AnnounceTransaction holds details about calls to the AnnounceTransaction method. - AnnounceTransaction []struct { - // TxHash is the txHash argument value. - TxHash *chainhash.Hash - // Peers is the peers argument value. - Peers []p2p.PeerI - } - // GetPeers holds details about calls to the GetPeers method. - GetPeers []struct { - } - // RequestBlock holds details about calls to the RequestBlock method. - RequestBlock []struct { - // BlockHash is the blockHash argument value. - BlockHash *chainhash.Hash - } - // RequestTransaction holds details about calls to the RequestTransaction method. - RequestTransaction []struct { - // TxHash is the txHash argument value. - TxHash *chainhash.Hash - } - // Shutdown holds details about calls to the Shutdown method. - Shutdown []struct { - } - } - lockAddPeer sync.RWMutex - lockAnnounceBlock sync.RWMutex - lockAnnounceTransaction sync.RWMutex - lockGetPeers sync.RWMutex - lockRequestBlock sync.RWMutex - lockRequestTransaction sync.RWMutex - lockShutdown sync.RWMutex -} - -// AddPeer calls AddPeerFunc. -func (mock *PeerManagerMock) AddPeer(peer p2p.PeerI) error { - if mock.AddPeerFunc == nil { - panic("PeerManagerMock.AddPeerFunc: method is nil but PeerManager.AddPeer was just called") - } - callInfo := struct { - Peer p2p.PeerI - }{ - Peer: peer, - } - mock.lockAddPeer.Lock() - mock.calls.AddPeer = append(mock.calls.AddPeer, callInfo) - mock.lockAddPeer.Unlock() - return mock.AddPeerFunc(peer) -} - -// AddPeerCalls gets all the calls that were made to AddPeer. -// Check the length with: -// -// len(mockedPeerManager.AddPeerCalls()) -func (mock *PeerManagerMock) AddPeerCalls() []struct { - Peer p2p.PeerI -} { - var calls []struct { - Peer p2p.PeerI - } - mock.lockAddPeer.RLock() - calls = mock.calls.AddPeer - mock.lockAddPeer.RUnlock() - return calls -} - -// AnnounceBlock calls AnnounceBlockFunc. -func (mock *PeerManagerMock) AnnounceBlock(blockHash *chainhash.Hash, peers []p2p.PeerI) []p2p.PeerI { - if mock.AnnounceBlockFunc == nil { - panic("PeerManagerMock.AnnounceBlockFunc: method is nil but PeerManager.AnnounceBlock was just called") - } - callInfo := struct { - BlockHash *chainhash.Hash - Peers []p2p.PeerI - }{ - BlockHash: blockHash, - Peers: peers, - } - mock.lockAnnounceBlock.Lock() - mock.calls.AnnounceBlock = append(mock.calls.AnnounceBlock, callInfo) - mock.lockAnnounceBlock.Unlock() - return mock.AnnounceBlockFunc(blockHash, peers) -} - -// AnnounceBlockCalls gets all the calls that were made to AnnounceBlock. -// Check the length with: -// -// len(mockedPeerManager.AnnounceBlockCalls()) -func (mock *PeerManagerMock) AnnounceBlockCalls() []struct { - BlockHash *chainhash.Hash - Peers []p2p.PeerI -} { - var calls []struct { - BlockHash *chainhash.Hash - Peers []p2p.PeerI - } - mock.lockAnnounceBlock.RLock() - calls = mock.calls.AnnounceBlock - mock.lockAnnounceBlock.RUnlock() - return calls -} - -// AnnounceTransaction calls AnnounceTransactionFunc. -func (mock *PeerManagerMock) AnnounceTransaction(txHash *chainhash.Hash, peers []p2p.PeerI) []p2p.PeerI { - if mock.AnnounceTransactionFunc == nil { - panic("PeerManagerMock.AnnounceTransactionFunc: method is nil but PeerManager.AnnounceTransaction was just called") - } - callInfo := struct { - TxHash *chainhash.Hash - Peers []p2p.PeerI - }{ - TxHash: txHash, - Peers: peers, - } - mock.lockAnnounceTransaction.Lock() - mock.calls.AnnounceTransaction = append(mock.calls.AnnounceTransaction, callInfo) - mock.lockAnnounceTransaction.Unlock() - return mock.AnnounceTransactionFunc(txHash, peers) -} - -// AnnounceTransactionCalls gets all the calls that were made to AnnounceTransaction. -// Check the length with: -// -// len(mockedPeerManager.AnnounceTransactionCalls()) -func (mock *PeerManagerMock) AnnounceTransactionCalls() []struct { - TxHash *chainhash.Hash - Peers []p2p.PeerI -} { - var calls []struct { - TxHash *chainhash.Hash - Peers []p2p.PeerI - } - mock.lockAnnounceTransaction.RLock() - calls = mock.calls.AnnounceTransaction - mock.lockAnnounceTransaction.RUnlock() - return calls -} - -// GetPeers calls GetPeersFunc. -func (mock *PeerManagerMock) GetPeers() []p2p.PeerI { - if mock.GetPeersFunc == nil { - panic("PeerManagerMock.GetPeersFunc: method is nil but PeerManager.GetPeers was just called") - } - callInfo := struct { - }{} - mock.lockGetPeers.Lock() - mock.calls.GetPeers = append(mock.calls.GetPeers, callInfo) - mock.lockGetPeers.Unlock() - return mock.GetPeersFunc() -} - -// GetPeersCalls gets all the calls that were made to GetPeers. -// Check the length with: -// -// len(mockedPeerManager.GetPeersCalls()) -func (mock *PeerManagerMock) GetPeersCalls() []struct { -} { - var calls []struct { - } - mock.lockGetPeers.RLock() - calls = mock.calls.GetPeers - mock.lockGetPeers.RUnlock() - return calls -} - -// RequestBlock calls RequestBlockFunc. -func (mock *PeerManagerMock) RequestBlock(blockHash *chainhash.Hash) p2p.PeerI { - if mock.RequestBlockFunc == nil { - panic("PeerManagerMock.RequestBlockFunc: method is nil but PeerManager.RequestBlock was just called") - } - callInfo := struct { - BlockHash *chainhash.Hash - }{ - BlockHash: blockHash, - } - mock.lockRequestBlock.Lock() - mock.calls.RequestBlock = append(mock.calls.RequestBlock, callInfo) - mock.lockRequestBlock.Unlock() - return mock.RequestBlockFunc(blockHash) -} - -// RequestBlockCalls gets all the calls that were made to RequestBlock. -// Check the length with: -// -// len(mockedPeerManager.RequestBlockCalls()) -func (mock *PeerManagerMock) RequestBlockCalls() []struct { - BlockHash *chainhash.Hash -} { - var calls []struct { - BlockHash *chainhash.Hash - } - mock.lockRequestBlock.RLock() - calls = mock.calls.RequestBlock - mock.lockRequestBlock.RUnlock() - return calls -} - -// RequestTransaction calls RequestTransactionFunc. -func (mock *PeerManagerMock) RequestTransaction(txHash *chainhash.Hash) p2p.PeerI { - if mock.RequestTransactionFunc == nil { - panic("PeerManagerMock.RequestTransactionFunc: method is nil but PeerManager.RequestTransaction was just called") - } - callInfo := struct { - TxHash *chainhash.Hash - }{ - TxHash: txHash, - } - mock.lockRequestTransaction.Lock() - mock.calls.RequestTransaction = append(mock.calls.RequestTransaction, callInfo) - mock.lockRequestTransaction.Unlock() - return mock.RequestTransactionFunc(txHash) -} - -// RequestTransactionCalls gets all the calls that were made to RequestTransaction. -// Check the length with: -// -// len(mockedPeerManager.RequestTransactionCalls()) -func (mock *PeerManagerMock) RequestTransactionCalls() []struct { - TxHash *chainhash.Hash -} { - var calls []struct { - TxHash *chainhash.Hash - } - mock.lockRequestTransaction.RLock() - calls = mock.calls.RequestTransaction - mock.lockRequestTransaction.RUnlock() - return calls -} - -// Shutdown calls ShutdownFunc. -func (mock *PeerManagerMock) Shutdown() { - if mock.ShutdownFunc == nil { - panic("PeerManagerMock.ShutdownFunc: method is nil but PeerManager.Shutdown was just called") - } - callInfo := struct { - }{} - mock.lockShutdown.Lock() - mock.calls.Shutdown = append(mock.calls.Shutdown, callInfo) - mock.lockShutdown.Unlock() - mock.ShutdownFunc() -} - -// ShutdownCalls gets all the calls that were made to Shutdown. -// Check the length with: -// -// len(mockedPeerManager.ShutdownCalls()) -func (mock *PeerManagerMock) ShutdownCalls() []struct { -} { - var calls []struct { - } - mock.lockShutdown.RLock() - calls = mock.calls.Shutdown - mock.lockShutdown.RUnlock() - return calls -} diff --git a/internal/blocktx/mocks/peer_mock.go b/internal/blocktx/mocks/peer_mock.go deleted file mode 100644 index 6b3f52020..000000000 --- a/internal/blocktx/mocks/peer_mock.go +++ /dev/null @@ -1,512 +0,0 @@ -// Code generated by moq; DO NOT EDIT. -// github.com/matryer/moq - -package mocks - -import ( - "github.com/bitcoin-sv/arc/internal/blocktx" - "github.com/libsv/go-p2p/chaincfg/chainhash" - "github.com/libsv/go-p2p/wire" - "sync" -) - -// Ensure, that PeerMock does implement blocktx.Peer. -// If this is not the case, regenerate this file with moq. -var _ blocktx.Peer = &PeerMock{} - -// PeerMock is a mock implementation of blocktx.Peer. -// -// func TestSomethingThatUsesPeer(t *testing.T) { -// -// // make and configure a mocked blocktx.Peer -// mockedPeer := &PeerMock{ -// AnnounceBlockFunc: func(blockHash *chainhash.Hash) { -// panic("mock out the AnnounceBlock method") -// }, -// AnnounceTransactionFunc: func(txHash *chainhash.Hash) { -// panic("mock out the AnnounceTransaction method") -// }, -// ConnectedFunc: func() bool { -// panic("mock out the Connected method") -// }, -// IsHealthyFunc: func() bool { -// panic("mock out the IsHealthy method") -// }, -// IsUnhealthyChFunc: func() <-chan struct{} { -// panic("mock out the IsUnhealthyCh method") -// }, -// NetworkFunc: func() wire.BitcoinNet { -// panic("mock out the Network method") -// }, -// RequestBlockFunc: func(blockHash *chainhash.Hash) { -// panic("mock out the RequestBlock method") -// }, -// RequestTransactionFunc: func(txHash *chainhash.Hash) { -// panic("mock out the RequestTransaction method") -// }, -// RestartFunc: func() { -// panic("mock out the Restart method") -// }, -// ShutdownFunc: func() { -// panic("mock out the Shutdown method") -// }, -// StringFunc: func() string { -// panic("mock out the String method") -// }, -// WriteMsgFunc: func(msg wire.Message) error { -// panic("mock out the WriteMsg method") -// }, -// } -// -// // use mockedPeer in code that requires blocktx.Peer -// // and then make assertions. -// -// } -type PeerMock struct { - // AnnounceBlockFunc mocks the AnnounceBlock method. - AnnounceBlockFunc func(blockHash *chainhash.Hash) - - // AnnounceTransactionFunc mocks the AnnounceTransaction method. - AnnounceTransactionFunc func(txHash *chainhash.Hash) - - // ConnectedFunc mocks the Connected method. - ConnectedFunc func() bool - - // IsHealthyFunc mocks the IsHealthy method. - IsHealthyFunc func() bool - - // IsUnhealthyChFunc mocks the IsUnhealthyCh method. - IsUnhealthyChFunc func() <-chan struct{} - - // NetworkFunc mocks the Network method. - NetworkFunc func() wire.BitcoinNet - - // RequestBlockFunc mocks the RequestBlock method. - RequestBlockFunc func(blockHash *chainhash.Hash) - - // RequestTransactionFunc mocks the RequestTransaction method. - RequestTransactionFunc func(txHash *chainhash.Hash) - - // RestartFunc mocks the Restart method. - RestartFunc func() - - // ShutdownFunc mocks the Shutdown method. - ShutdownFunc func() - - // StringFunc mocks the String method. - StringFunc func() string - - // WriteMsgFunc mocks the WriteMsg method. - WriteMsgFunc func(msg wire.Message) error - - // calls tracks calls to the methods. - calls struct { - // AnnounceBlock holds details about calls to the AnnounceBlock method. - AnnounceBlock []struct { - // BlockHash is the blockHash argument value. - BlockHash *chainhash.Hash - } - // AnnounceTransaction holds details about calls to the AnnounceTransaction method. - AnnounceTransaction []struct { - // TxHash is the txHash argument value. - TxHash *chainhash.Hash - } - // Connected holds details about calls to the Connected method. - Connected []struct { - } - // IsHealthy holds details about calls to the IsHealthy method. - IsHealthy []struct { - } - // IsUnhealthyCh holds details about calls to the IsUnhealthyCh method. - IsUnhealthyCh []struct { - } - // Network holds details about calls to the Network method. - Network []struct { - } - // RequestBlock holds details about calls to the RequestBlock method. - RequestBlock []struct { - // BlockHash is the blockHash argument value. - BlockHash *chainhash.Hash - } - // RequestTransaction holds details about calls to the RequestTransaction method. - RequestTransaction []struct { - // TxHash is the txHash argument value. - TxHash *chainhash.Hash - } - // Restart holds details about calls to the Restart method. - Restart []struct { - } - // Shutdown holds details about calls to the Shutdown method. - Shutdown []struct { - } - // String holds details about calls to the String method. - String []struct { - } - // WriteMsg holds details about calls to the WriteMsg method. - WriteMsg []struct { - // Msg is the msg argument value. - Msg wire.Message - } - } - lockAnnounceBlock sync.RWMutex - lockAnnounceTransaction sync.RWMutex - lockConnected sync.RWMutex - lockIsHealthy sync.RWMutex - lockIsUnhealthyCh sync.RWMutex - lockNetwork sync.RWMutex - lockRequestBlock sync.RWMutex - lockRequestTransaction sync.RWMutex - lockRestart sync.RWMutex - lockShutdown sync.RWMutex - lockString sync.RWMutex - lockWriteMsg sync.RWMutex -} - -// AnnounceBlock calls AnnounceBlockFunc. -func (mock *PeerMock) AnnounceBlock(blockHash *chainhash.Hash) { - if mock.AnnounceBlockFunc == nil { - panic("PeerMock.AnnounceBlockFunc: method is nil but Peer.AnnounceBlock was just called") - } - callInfo := struct { - BlockHash *chainhash.Hash - }{ - BlockHash: blockHash, - } - mock.lockAnnounceBlock.Lock() - mock.calls.AnnounceBlock = append(mock.calls.AnnounceBlock, callInfo) - mock.lockAnnounceBlock.Unlock() - mock.AnnounceBlockFunc(blockHash) -} - -// AnnounceBlockCalls gets all the calls that were made to AnnounceBlock. -// Check the length with: -// -// len(mockedPeer.AnnounceBlockCalls()) -func (mock *PeerMock) AnnounceBlockCalls() []struct { - BlockHash *chainhash.Hash -} { - var calls []struct { - BlockHash *chainhash.Hash - } - mock.lockAnnounceBlock.RLock() - calls = mock.calls.AnnounceBlock - mock.lockAnnounceBlock.RUnlock() - return calls -} - -// AnnounceTransaction calls AnnounceTransactionFunc. -func (mock *PeerMock) AnnounceTransaction(txHash *chainhash.Hash) { - if mock.AnnounceTransactionFunc == nil { - panic("PeerMock.AnnounceTransactionFunc: method is nil but Peer.AnnounceTransaction was just called") - } - callInfo := struct { - TxHash *chainhash.Hash - }{ - TxHash: txHash, - } - mock.lockAnnounceTransaction.Lock() - mock.calls.AnnounceTransaction = append(mock.calls.AnnounceTransaction, callInfo) - mock.lockAnnounceTransaction.Unlock() - mock.AnnounceTransactionFunc(txHash) -} - -// AnnounceTransactionCalls gets all the calls that were made to AnnounceTransaction. -// Check the length with: -// -// len(mockedPeer.AnnounceTransactionCalls()) -func (mock *PeerMock) AnnounceTransactionCalls() []struct { - TxHash *chainhash.Hash -} { - var calls []struct { - TxHash *chainhash.Hash - } - mock.lockAnnounceTransaction.RLock() - calls = mock.calls.AnnounceTransaction - mock.lockAnnounceTransaction.RUnlock() - return calls -} - -// Connected calls ConnectedFunc. -func (mock *PeerMock) Connected() bool { - if mock.ConnectedFunc == nil { - panic("PeerMock.ConnectedFunc: method is nil but Peer.Connected was just called") - } - callInfo := struct { - }{} - mock.lockConnected.Lock() - mock.calls.Connected = append(mock.calls.Connected, callInfo) - mock.lockConnected.Unlock() - return mock.ConnectedFunc() -} - -// ConnectedCalls gets all the calls that were made to Connected. -// Check the length with: -// -// len(mockedPeer.ConnectedCalls()) -func (mock *PeerMock) ConnectedCalls() []struct { -} { - var calls []struct { - } - mock.lockConnected.RLock() - calls = mock.calls.Connected - mock.lockConnected.RUnlock() - return calls -} - -// IsHealthy calls IsHealthyFunc. -func (mock *PeerMock) IsHealthy() bool { - if mock.IsHealthyFunc == nil { - panic("PeerMock.IsHealthyFunc: method is nil but Peer.IsHealthy was just called") - } - callInfo := struct { - }{} - mock.lockIsHealthy.Lock() - mock.calls.IsHealthy = append(mock.calls.IsHealthy, callInfo) - mock.lockIsHealthy.Unlock() - return mock.IsHealthyFunc() -} - -// IsHealthyCalls gets all the calls that were made to IsHealthy. -// Check the length with: -// -// len(mockedPeer.IsHealthyCalls()) -func (mock *PeerMock) IsHealthyCalls() []struct { -} { - var calls []struct { - } - mock.lockIsHealthy.RLock() - calls = mock.calls.IsHealthy - mock.lockIsHealthy.RUnlock() - return calls -} - -// IsUnhealthyCh calls IsUnhealthyChFunc. -func (mock *PeerMock) IsUnhealthyCh() <-chan struct{} { - if mock.IsUnhealthyChFunc == nil { - panic("PeerMock.IsUnhealthyChFunc: method is nil but Peer.IsUnhealthyCh was just called") - } - callInfo := struct { - }{} - mock.lockIsUnhealthyCh.Lock() - mock.calls.IsUnhealthyCh = append(mock.calls.IsUnhealthyCh, callInfo) - mock.lockIsUnhealthyCh.Unlock() - return mock.IsUnhealthyChFunc() -} - -// IsUnhealthyChCalls gets all the calls that were made to IsUnhealthyCh. -// Check the length with: -// -// len(mockedPeer.IsUnhealthyChCalls()) -func (mock *PeerMock) IsUnhealthyChCalls() []struct { -} { - var calls []struct { - } - mock.lockIsUnhealthyCh.RLock() - calls = mock.calls.IsUnhealthyCh - mock.lockIsUnhealthyCh.RUnlock() - return calls -} - -// Network calls NetworkFunc. -func (mock *PeerMock) Network() wire.BitcoinNet { - if mock.NetworkFunc == nil { - panic("PeerMock.NetworkFunc: method is nil but Peer.Network was just called") - } - callInfo := struct { - }{} - mock.lockNetwork.Lock() - mock.calls.Network = append(mock.calls.Network, callInfo) - mock.lockNetwork.Unlock() - return mock.NetworkFunc() -} - -// NetworkCalls gets all the calls that were made to Network. -// Check the length with: -// -// len(mockedPeer.NetworkCalls()) -func (mock *PeerMock) NetworkCalls() []struct { -} { - var calls []struct { - } - mock.lockNetwork.RLock() - calls = mock.calls.Network - mock.lockNetwork.RUnlock() - return calls -} - -// RequestBlock calls RequestBlockFunc. -func (mock *PeerMock) RequestBlock(blockHash *chainhash.Hash) { - if mock.RequestBlockFunc == nil { - panic("PeerMock.RequestBlockFunc: method is nil but Peer.RequestBlock was just called") - } - callInfo := struct { - BlockHash *chainhash.Hash - }{ - BlockHash: blockHash, - } - mock.lockRequestBlock.Lock() - mock.calls.RequestBlock = append(mock.calls.RequestBlock, callInfo) - mock.lockRequestBlock.Unlock() - mock.RequestBlockFunc(blockHash) -} - -// RequestBlockCalls gets all the calls that were made to RequestBlock. -// Check the length with: -// -// len(mockedPeer.RequestBlockCalls()) -func (mock *PeerMock) RequestBlockCalls() []struct { - BlockHash *chainhash.Hash -} { - var calls []struct { - BlockHash *chainhash.Hash - } - mock.lockRequestBlock.RLock() - calls = mock.calls.RequestBlock - mock.lockRequestBlock.RUnlock() - return calls -} - -// RequestTransaction calls RequestTransactionFunc. -func (mock *PeerMock) RequestTransaction(txHash *chainhash.Hash) { - if mock.RequestTransactionFunc == nil { - panic("PeerMock.RequestTransactionFunc: method is nil but Peer.RequestTransaction was just called") - } - callInfo := struct { - TxHash *chainhash.Hash - }{ - TxHash: txHash, - } - mock.lockRequestTransaction.Lock() - mock.calls.RequestTransaction = append(mock.calls.RequestTransaction, callInfo) - mock.lockRequestTransaction.Unlock() - mock.RequestTransactionFunc(txHash) -} - -// RequestTransactionCalls gets all the calls that were made to RequestTransaction. -// Check the length with: -// -// len(mockedPeer.RequestTransactionCalls()) -func (mock *PeerMock) RequestTransactionCalls() []struct { - TxHash *chainhash.Hash -} { - var calls []struct { - TxHash *chainhash.Hash - } - mock.lockRequestTransaction.RLock() - calls = mock.calls.RequestTransaction - mock.lockRequestTransaction.RUnlock() - return calls -} - -// Restart calls RestartFunc. -func (mock *PeerMock) Restart() { - if mock.RestartFunc == nil { - panic("PeerMock.RestartFunc: method is nil but Peer.Restart was just called") - } - callInfo := struct { - }{} - mock.lockRestart.Lock() - mock.calls.Restart = append(mock.calls.Restart, callInfo) - mock.lockRestart.Unlock() - mock.RestartFunc() -} - -// RestartCalls gets all the calls that were made to Restart. -// Check the length with: -// -// len(mockedPeer.RestartCalls()) -func (mock *PeerMock) RestartCalls() []struct { -} { - var calls []struct { - } - mock.lockRestart.RLock() - calls = mock.calls.Restart - mock.lockRestart.RUnlock() - return calls -} - -// Shutdown calls ShutdownFunc. -func (mock *PeerMock) Shutdown() { - if mock.ShutdownFunc == nil { - panic("PeerMock.ShutdownFunc: method is nil but Peer.Shutdown was just called") - } - callInfo := struct { - }{} - mock.lockShutdown.Lock() - mock.calls.Shutdown = append(mock.calls.Shutdown, callInfo) - mock.lockShutdown.Unlock() - mock.ShutdownFunc() -} - -// ShutdownCalls gets all the calls that were made to Shutdown. -// Check the length with: -// -// len(mockedPeer.ShutdownCalls()) -func (mock *PeerMock) ShutdownCalls() []struct { -} { - var calls []struct { - } - mock.lockShutdown.RLock() - calls = mock.calls.Shutdown - mock.lockShutdown.RUnlock() - return calls -} - -// String calls StringFunc. -func (mock *PeerMock) String() string { - if mock.StringFunc == nil { - panic("PeerMock.StringFunc: method is nil but Peer.String was just called") - } - callInfo := struct { - }{} - mock.lockString.Lock() - mock.calls.String = append(mock.calls.String, callInfo) - mock.lockString.Unlock() - return mock.StringFunc() -} - -// StringCalls gets all the calls that were made to String. -// Check the length with: -// -// len(mockedPeer.StringCalls()) -func (mock *PeerMock) StringCalls() []struct { -} { - var calls []struct { - } - mock.lockString.RLock() - calls = mock.calls.String - mock.lockString.RUnlock() - return calls -} - -// WriteMsg calls WriteMsgFunc. -func (mock *PeerMock) WriteMsg(msg wire.Message) error { - if mock.WriteMsgFunc == nil { - panic("PeerMock.WriteMsgFunc: method is nil but Peer.WriteMsg was just called") - } - callInfo := struct { - Msg wire.Message - }{ - Msg: msg, - } - mock.lockWriteMsg.Lock() - mock.calls.WriteMsg = append(mock.calls.WriteMsg, callInfo) - mock.lockWriteMsg.Unlock() - return mock.WriteMsgFunc(msg) -} - -// WriteMsgCalls gets all the calls that were made to WriteMsg. -// Check the length with: -// -// len(mockedPeer.WriteMsgCalls()) -func (mock *PeerMock) WriteMsgCalls() []struct { - Msg wire.Message -} { - var calls []struct { - Msg wire.Message - } - mock.lockWriteMsg.RLock() - calls = mock.calls.WriteMsg - mock.lockWriteMsg.RUnlock() - return calls -} diff --git a/internal/blocktx/processor.go b/internal/blocktx/processor.go index 2dfe49578..2600730a8 100644 --- a/internal/blocktx/processor.go +++ b/internal/blocktx/processor.go @@ -14,12 +14,13 @@ import ( "github.com/cenkalti/backoff/v4" "github.com/libsv/go-bc" - "github.com/libsv/go-p2p" "github.com/libsv/go-p2p/chaincfg/chainhash" "github.com/libsv/go-p2p/wire" "go.opentelemetry.io/otel/attribute" "golang.org/x/sync/errgroup" + "github.com/bitcoin-sv/arc/internal/blocktx/bcnet" + "github.com/bitcoin-sv/arc/internal/blocktx/bcnet/blocktx_p2p" "github.com/bitcoin-sv/arc/internal/blocktx/blocktx_api" "github.com/bitcoin-sv/arc/internal/blocktx/store" "github.com/bitcoin-sv/arc/internal/tracing" @@ -53,8 +54,8 @@ const ( type Processor struct { hostname string - blockRequestCh chan BlockRequest - blockProcessCh chan *p2p.BlockMessage + blockRequestCh chan blocktx_p2p.BlockRequest + blockProcessCh chan *bcnet.BlockMessage store store.BlocktxStore logger *slog.Logger transactionStorageBatchSize int @@ -83,8 +84,8 @@ type Processor struct { func NewProcessor( logger *slog.Logger, storeI store.BlocktxStore, - blockRequestCh chan BlockRequest, - blockProcessCh chan *p2p.BlockMessage, + blockRequestCh chan blocktx_p2p.BlockRequest, + blockProcessCh chan *bcnet.BlockMessage, opts ...func(*Processor), ) (*Processor, error) { hostname, err := os.Hostname() @@ -208,7 +209,7 @@ func (p *Processor) StartBlockRequesting() { p.logger.Info("Sending block request", slog.String("hash", hash.String())) msg := wire.NewMsgGetDataSizeHint(1) _ = msg.AddInvVect(wire.NewInvVect(wire.InvTypeBlock, hash)) // ignore error at this point - _ = peer.WriteMsg(msg) + peer.WriteMsg(msg) p.startBlockProcessGuard(p.ctx, hash) p.logger.Info("Block request message sent to peer", slog.String("hash", hash.String()), slog.String("peer", peer.String())) @@ -231,21 +232,21 @@ func (p *Processor) StartBlockProcessing() { var err error timeStart := time.Now() - hash := blockMsg.Header.BlockHash() + hash := blockMsg.Hash p.logger.Info("received block", slog.String("hash", hash.String())) err = p.processBlock(blockMsg) if err != nil { p.logger.Error("block processing failed", slog.String("hash", hash.String()), slog.String("err", err.Error())) - p.unlockBlock(p.ctx, &hash) + p.unlockBlock(p.ctx, hash) continue } - storeErr := p.store.MarkBlockAsDone(p.ctx, &hash, blockMsg.Size, uint64(len(blockMsg.TransactionHashes))) + storeErr := p.store.MarkBlockAsDone(p.ctx, hash, blockMsg.Size, uint64(len(blockMsg.TransactionHashes))) if storeErr != nil { p.logger.Error("unable to mark block as processed", slog.String("hash", hash.String()), slog.String("err", storeErr.Error())) - p.unlockBlock(p.ctx, &hash) + p.unlockBlock(p.ctx, hash) continue } @@ -463,14 +464,14 @@ func (p *Processor) registerTransactions(txHashes [][]byte) { } } -func (p *Processor) processBlock(blockMsg *p2p.BlockMessage) (err error) { +func (p *Processor) processBlock(blockMsg *bcnet.BlockMessage) (err error) { ctx := p.ctx var block *blocktx_api.Block - blockHash := blockMsg.Header.BlockHash() + blockHash := blockMsg.Hash // release guardian - defer p.stopBlockProcessGuard(&blockHash) + defer p.stopBlockProcessGuard(blockHash) ctx, span := tracing.StartTracing(ctx, "processBlock", p.tracingEnabled, p.tracingAttributes...) defer func() { @@ -487,7 +488,7 @@ func (p *Processor) processBlock(blockMsg *p2p.BlockMessage) (err error) { p.logger.Info("processing incoming block", slog.String("hash", blockHash.String()), slog.Uint64("height", blockMsg.Height)) // check if we've already processed that block - existingBlock, _ := p.store.GetBlock(ctx, &blockHash) + existingBlock, _ := p.store.GetBlock(ctx, blockHash) if existingBlock != nil { p.logger.Warn("ignoring already existing block", slog.String("hash", blockHash.String()), slog.Uint64("height", blockMsg.Height)) @@ -530,13 +531,13 @@ func (p *Processor) processBlock(blockMsg *p2p.BlockMessage) (err error) { return nil } -func (p *Processor) verifyAndInsertBlock(ctx context.Context, blockMsg *p2p.BlockMessage) (incomingBlock *blocktx_api.Block, err error) { +func (p *Processor) verifyAndInsertBlock(ctx context.Context, blockMsg *bcnet.BlockMessage) (incomingBlock *blocktx_api.Block, err error) { ctx, span := tracing.StartTracing(ctx, "verifyAndInsertBlock", p.tracingEnabled, p.tracingAttributes...) defer func() { tracing.EndTracing(span, err) }() - blockHash := blockMsg.Header.BlockHash() + blockHash := blockMsg.Hash previousBlockHash := blockMsg.Header.PrevBlock merkleRoot := blockMsg.Header.MerkleRoot diff --git a/internal/blocktx/processor_test.go b/internal/blocktx/processor_test.go index b293992db..ea8365411 100644 --- a/internal/blocktx/processor_test.go +++ b/internal/blocktx/processor_test.go @@ -9,17 +9,19 @@ import ( "testing" "time" - "github.com/libsv/go-p2p" "github.com/libsv/go-p2p/chaincfg/chainhash" "github.com/libsv/go-p2p/wire" "github.com/stretchr/testify/require" "google.golang.org/protobuf/reflect/protoreflect" "github.com/bitcoin-sv/arc/internal/blocktx" + "github.com/bitcoin-sv/arc/internal/blocktx/bcnet" + "github.com/bitcoin-sv/arc/internal/blocktx/bcnet/blocktx_p2p" "github.com/bitcoin-sv/arc/internal/blocktx/blocktx_api" "github.com/bitcoin-sv/arc/internal/blocktx/mocks" "github.com/bitcoin-sv/arc/internal/blocktx/store" storeMocks "github.com/bitcoin-sv/arc/internal/blocktx/store/mocks" + p2p_mocks "github.com/bitcoin-sv/arc/internal/p2p/mocks" "github.com/bitcoin-sv/arc/internal/testdata" ) @@ -199,13 +201,14 @@ func TestHandleBlock(t *testing.T) { } logger := slog.Default() - blockProcessCh := make(chan *p2p.BlockMessage, 1) - p2pMsgHandler := blocktx.NewPeerHandler(logger, nil, blockProcessCh) + blockProcessCh := make(chan *bcnet.BlockMessage, 1) + p2pMsgHandler := blocktx_p2p.NewMsgHandler(logger, nil, blockProcessCh) sut, err := blocktx.NewProcessor(logger, storeMock, nil, blockProcessCh, blocktx.WithTransactionBatchSize(batchSize), blocktx.WithMessageQueueClient(mq)) require.NoError(t, err) - blockMessage := &p2p.BlockMessage{ + blockMessage := &bcnet.BlockMessage{ + Hash: testdata.Block1Hash, Header: &wire.BlockHeader{ Version: 541065216, PrevBlock: tc.prevBlockHash, @@ -222,8 +225,7 @@ func TestHandleBlock(t *testing.T) { sut.StartBlockProcessing() // simulate receiving block from node - err = p2pMsgHandler.HandleBlock(blockMessage, &mocks.PeerMock{StringFunc: func() string { return "peer" }}) - require.NoError(t, err) + p2pMsgHandler.OnReceive(blockMessage, &p2p_mocks.PeerIMock{StringFunc: func() string { return "peer" }}) var actualInsertedBlockTransactions []string time.Sleep(20 * time.Millisecond) @@ -445,8 +447,8 @@ func TestHandleBlockReorgAndOrphans(t *testing.T) { // build peer manager and processor logger := slog.Default() - blockProcessCh := make(chan *p2p.BlockMessage, 10) - p2pMsgHandler := blocktx.NewPeerHandler(logger, nil, blockProcessCh) + blockProcessCh := make(chan *bcnet.BlockMessage, 10) + p2pMsgHandler := blocktx_p2p.NewMsgHandler(logger, nil, blockProcessCh) sut, err := blocktx.NewProcessor(logger, storeMock, nil, blockProcessCh) require.NoError(t, err) @@ -456,8 +458,8 @@ func TestHandleBlockReorgAndOrphans(t *testing.T) { merkleRoot, err := chainhash.NewHashFromStr("be181e91217d5f802f695e52144078f8dfbe51b8a815c3d6fb48c0d853ec683b") require.NoError(t, err) - blockMessage := &p2p.BlockMessage{ - // Hash: testdata.Block1Hash, + blockMessage := &bcnet.BlockMessage{ + Hash: testdata.Block1Hash, Header: &wire.BlockHeader{ Version: 541065216, MerkleRoot: *merkleRoot, @@ -471,8 +473,7 @@ func TestHandleBlockReorgAndOrphans(t *testing.T) { sut.StartBlockProcessing() // simulate receiving block from node - err = p2pMsgHandler.HandleBlock(blockMessage, nil) - require.NoError(t, err) + p2pMsgHandler.OnReceive(blockMessage, nil) // then time.Sleep(20 * time.Millisecond) @@ -629,18 +630,18 @@ func TestStartBlockRequesting(t *testing.T) { return 1, nil } - peerMock := &mocks.PeerMock{ - WriteMsgFunc: func(_ wire.Message) error { return nil }, + peerMock := &p2p_mocks.PeerIMock{ + WriteMsgFunc: func(_ wire.Message) {}, StringFunc: func() string { return "peer" }, } // build peer manager logger := slog.Default() - blockRequestCh := make(chan blocktx.BlockRequest, 10) - blockProcessCh := make(chan *p2p.BlockMessage, 10) + blockRequestCh := make(chan blocktx_p2p.BlockRequest, 10) + blockProcessCh := make(chan *bcnet.BlockMessage, 10) - peerHandler := blocktx.NewPeerHandler(logger, blockRequestCh, blockProcessCh) + peerHandler := blocktx_p2p.NewMsgHandler(logger, blockRequestCh, blockProcessCh) sut, err := blocktx.NewProcessor(logger, storeMock, blockRequestCh, blockProcessCh) require.NoError(t, err) @@ -652,8 +653,7 @@ func TestStartBlockRequesting(t *testing.T) { invMsg := wire.NewMsgInvSizeHint(1) err = invMsg.AddInvVect(wire.NewInvVect(wire.InvTypeBlock, blockHash)) require.NoError(t, err) - err = peerHandler.HandleBlockAnnouncement(invMsg.InvList[0], peerMock) - require.NoError(t, err) + peerHandler.OnReceive(invMsg, peerMock) time.Sleep(200 * time.Millisecond) diff --git a/internal/blocktx/server.go b/internal/blocktx/server.go index e1ae3b3d9..ff0d6a8f7 100644 --- a/internal/blocktx/server.go +++ b/internal/blocktx/server.go @@ -9,12 +9,11 @@ import ( "google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/libsv/go-p2p" - "github.com/bitcoin-sv/arc/config" "github.com/bitcoin-sv/arc/internal/blocktx/blocktx_api" "github.com/bitcoin-sv/arc/internal/blocktx/store" "github.com/bitcoin-sv/arc/internal/grpc_opts" + "github.com/bitcoin-sv/arc/internal/p2p" ) // Server type carries the logger within it. @@ -23,14 +22,14 @@ type Server struct { grpc_opts.GrpcServer logger *slog.Logger - pm p2p.PeerManagerI + pm *p2p.PeerManager store store.BlocktxStore maxAllowedBlockHeightMismatch int } // NewServer will return a server instance with the logger stored within it. func NewServer(prometheusEndpoint string, maxMsgSize int, logger *slog.Logger, - store store.BlocktxStore, pm p2p.PeerManagerI, maxAllowedBlockHeightMismatch int, tracingConfig *config.TracingConfig) (*Server, error) { + store store.BlocktxStore, pm *p2p.PeerManager, maxAllowedBlockHeightMismatch int, tracingConfig *config.TracingConfig) (*Server, error) { logger = logger.With(slog.String("module", "server")) grpcServer, err := grpc_opts.NewGrpcServer(logger, "blocktx", prometheusEndpoint, maxMsgSize, tracingConfig) diff --git a/internal/blocktx/server_test.go b/internal/blocktx/server_test.go index 18a7090bf..7fa61838f 100644 --- a/internal/blocktx/server_test.go +++ b/internal/blocktx/server_test.go @@ -8,13 +8,13 @@ import ( "testing" "time" - "github.com/libsv/go-p2p" "github.com/libsv/go-p2p/chaincfg/chainhash" "github.com/stretchr/testify/require" "github.com/bitcoin-sv/arc/internal/blocktx" "github.com/bitcoin-sv/arc/internal/blocktx/blocktx_api" storeMocks "github.com/bitcoin-sv/arc/internal/blocktx/store/mocks" + "github.com/bitcoin-sv/arc/internal/p2p" "github.com/bitcoin-sv/arc/internal/testdata" ) From b6a2548e2a81d20701b5e3caa1b4f4b3f4b0ea97 Mon Sep 17 00:00:00 2001 From: Arkadiusz Osowski Date: Wed, 18 Dec 2024 20:27:08 +0100 Subject: [PATCH 3/3] feat: use internal p2p in mtm --- cmd/arc/services/metamorph.go | 54 +- config/example_config.yaml | 2 +- go.mod | 5 - go.sum | 13 - .../reorg_integration_test.go | 18 +- .../bcnet/metamorph_p2p/message_handler.go | 182 +++++++ .../metamorph_p2p/message_handler_test.go | 196 +++++++ internal/metamorph/collector_processor.go | 2 +- .../double_spend_integration_test.go | 7 +- .../processor_integration_test.go | 39 +- internal/metamorph/metamorph_mocks.go | 6 - internal/metamorph/mocks/peer_manager_mock.go | 339 ------------ internal/metamorph/mocks/peer_mock.go | 512 ------------------ internal/metamorph/mocks/processor_mock.go | 2 +- internal/metamorph/peer.go | 21 - internal/metamorph/peer_handler.go | 125 ----- internal/metamorph/peer_handler_test.go | 184 ------- internal/metamorph/peer_manager.go | 16 - internal/metamorph/processor.go | 69 +-- internal/metamorph/processor_test.go | 257 +++++---- internal/metamorph/server.go | 11 +- internal/metamorph/server_test.go | 7 +- internal/metamorph/stats_collector_test.go | 7 +- internal/metamorph/types.go | 11 - internal/metamorph/zmq.go | 19 +- internal/metamorph/zmq_test.go | 9 +- internal/p2p/wire_reader.go | 2 +- test/config/config.yaml | 2 +- 28 files changed, 650 insertions(+), 1467 deletions(-) create mode 100644 internal/metamorph/bcnet/metamorph_p2p/message_handler.go create mode 100644 internal/metamorph/bcnet/metamorph_p2p/message_handler_test.go delete mode 100644 internal/metamorph/mocks/peer_manager_mock.go delete mode 100644 internal/metamorph/mocks/peer_mock.go delete mode 100644 internal/metamorph/peer.go delete mode 100644 internal/metamorph/peer_handler.go delete mode 100644 internal/metamorph/peer_handler_test.go delete mode 100644 internal/metamorph/peer_manager.go diff --git a/cmd/arc/services/metamorph.go b/cmd/arc/services/metamorph.go index 2d637fd51..05ed0eea6 100644 --- a/cmd/arc/services/metamorph.go +++ b/cmd/arc/services/metamorph.go @@ -10,7 +10,6 @@ import ( "go.opentelemetry.io/otel/attribute" - "github.com/libsv/go-p2p" "github.com/ordishs/go-bitcoin" "google.golang.org/grpc" @@ -26,9 +25,12 @@ import ( "github.com/bitcoin-sv/arc/internal/message_queue/nats/client/nats_jetstream" "github.com/bitcoin-sv/arc/internal/message_queue/nats/nats_connection" "github.com/bitcoin-sv/arc/internal/metamorph" + "github.com/bitcoin-sv/arc/internal/metamorph/bcnet/metamorph_p2p" "github.com/bitcoin-sv/arc/internal/metamorph/metamorph_api" "github.com/bitcoin-sv/arc/internal/metamorph/store" "github.com/bitcoin-sv/arc/internal/metamorph/store/postgresql" + + "github.com/bitcoin-sv/arc/internal/p2p" "github.com/bitcoin-sv/arc/internal/version" ) @@ -45,9 +47,8 @@ func StartMetamorph(logger *slog.Logger, arcConfig *config.ArcConfig, cacheStore var ( metamorphStore store.MetamorphStore - peerHandler *metamorph.PeerHandler - pm metamorph.PeerManager - statusMessageCh chan *metamorph.TxStatusMessage + pm *p2p.PeerManager + statusMessageCh chan *metamorph_p2p.TxStatusMessage mqClient metamorph.MessageQueue processor *metamorph.Processor server *metamorph.Server @@ -84,7 +85,7 @@ func StartMetamorph(logger *slog.Logger, arcConfig *config.ArcConfig, cacheStore stopFn := func() { logger.Info("Shutting down metamorph") - disposeMtm(logger, server, processor, peerHandler, mqClient, metamorphStore, healthServer, shutdownFns) + disposeMtm(logger, server, processor, pm, mqClient, metamorphStore, healthServer, shutdownFns) logger.Info("Shutdown complete") } @@ -93,7 +94,7 @@ func StartMetamorph(logger *slog.Logger, arcConfig *config.ArcConfig, cacheStore return nil, fmt.Errorf("failed to create metamorph store: %v", err) } - pm, peerHandler, statusMessageCh, err = initPeerManager(logger, metamorphStore, arcConfig) + pm, statusMessageCh, err = initPeerManager(logger, metamorphStore, arcConfig) if err != nil { stopFn() return nil, err @@ -165,7 +166,7 @@ func StartMetamorph(logger *slog.Logger, arcConfig *config.ArcConfig, cacheStore processor, err = metamorph.NewProcessor( metamorphStore, cacheStore, - pm, + p2p.NewNetworkMessenger(logger, pm), statusMessageCh, processorOpts..., ) @@ -276,50 +277,53 @@ func NewMetamorphStore(dbConfig *config.DbConfig, tracingConfig *config.TracingC return s, err } -func initPeerManager(logger *slog.Logger, s store.MetamorphStore, arcConfig *config.ArcConfig) (p2p.PeerManagerI, *metamorph.PeerHandler, chan *metamorph.TxStatusMessage, error) { +func initPeerManager(logger *slog.Logger, s store.MetamorphStore, arcConfig *config.ArcConfig) (*p2p.PeerManager, chan *metamorph_p2p.TxStatusMessage, error) { network, err := config.GetNetwork(arcConfig.Network) if err != nil { - return nil, nil, nil, fmt.Errorf("failed to get network: %v", err) + return nil, nil, fmt.Errorf("failed to get network: %v", err) } logger.Info("Assuming bitcoin network", "network", network) - messageCh := make(chan *metamorph.TxStatusMessage, 10000) + messageCh := make(chan *metamorph_p2p.TxStatusMessage, 10000) var pmOpts []p2p.PeerManagerOptions if arcConfig.Metamorph.MonitorPeers { pmOpts = append(pmOpts, p2p.WithRestartUnhealthyPeers()) } - pm := p2p.NewPeerManager(logger.With(slog.String("module", "peer-handler")), network, pmOpts...) + pm := p2p.NewPeerManager(logger.With(slog.String("module", "peer-mng")), network, pmOpts...) - peerHandler := metamorph.NewPeerHandler(s, messageCh) + msgHandler := metamorph_p2p.NewMsgHandler(logger.With(slog.String("module", "peer-msg-handler")), s, messageCh) peerOpts := []p2p.PeerOptions{ - p2p.WithRetryReadWriteMessageInterval(5 * time.Second), - p2p.WithPingInterval(30*time.Second, 1*time.Minute), + p2p.WithPingInterval(30*time.Second, 2*time.Minute), + p2p.WithNrOfWriteHandlers(8), + p2p.WithWriteChannelSize(4096), } + if version.Version != "" { peerOpts = append(peerOpts, p2p.WithUserAgent("ARC", version.Version)) } + l := logger.With(slog.String("module", "peer")) for _, peerSetting := range arcConfig.Broadcasting.Unicast.Peers { peerURL, err := peerSetting.GetP2PUrl() if err != nil { - return nil, nil, nil, fmt.Errorf("error getting peer url: %v", err) + return nil, nil, fmt.Errorf("error getting peer url: %v", err) } - var peer *p2p.Peer - peer, err = p2p.NewPeer(logger.With(slog.String("module", "peer")), peerURL, peerHandler, network, peerOpts...) - if err != nil { - return nil, nil, nil, fmt.Errorf("error creating peer %s: %v", peerURL, err) + peer := p2p.NewPeer(l, msgHandler, peerURL, network, peerOpts...) + ok := peer.Connect() + if !ok { + return nil, nil, fmt.Errorf("cannot connect to peer %s", peerURL) } if err = pm.AddPeer(peer); err != nil { - return nil, nil, nil, fmt.Errorf("error adding peer %s: %v", peerURL, err) + return nil, nil, fmt.Errorf("error adding peer %s: %v", peerURL, err) } } - return pm, peerHandler, messageCh, nil + return pm, messageCh, nil } func initGrpcCallbackerConn(address, prometheusEndpoint string, grpcMsgSize int, tracingConfig *config.TracingConfig) (callbacker_api.CallbackerAPIClient, error) { @@ -336,14 +340,14 @@ func initGrpcCallbackerConn(address, prometheusEndpoint string, grpcMsgSize int, } func disposeMtm(l *slog.Logger, server *metamorph.Server, processor *metamorph.Processor, - peerHandler *metamorph.PeerHandler, mqClient metamorph.MessageQueue, + peerManaager *p2p.PeerManager, mqClient metamorph.MessageQueueClient, metamorphStore store.MetamorphStore, healthServer *grpc_opts.GrpcServer, shutdownFns []func(), ) { // dispose the dependencies in the correct order: // 1. server - ensure no new request will be received // 2. processor - ensure all started job are complete - // 3. peerHandler + // 3. peerManaager // 4. mqClient // 5. store // 6. healthServer @@ -355,8 +359,8 @@ func disposeMtm(l *slog.Logger, server *metamorph.Server, processor *metamorph.P if processor != nil { processor.Shutdown() } - if peerHandler != nil { - peerHandler.Shutdown() + if peerManaager != nil { + peerManaager.Shutdown() } if mqClient != nil { mqClient.Shutdown() diff --git a/config/example_config.yaml b/config/example_config.yaml index 6f6f20c6f..4a23a08b4 100644 --- a/config/example_config.yaml +++ b/config/example_config.yaml @@ -1,6 +1,6 @@ --- -logLevel: DEBUG # mode of logging. Value can be one of TRACE | DEBUG | INFO | WARN | ERROR logFormat: text # format of logging. Value can be one of text | json | tint +logLevel: DEBUG # mode of logging. Value can be one of TRACE | DEBUG | INFO | WARN | ERROR profilerAddr: localhost:9999 # address to start profiler server on (optional) prometheus: enabled: false # if true, then prometheus metrics are enabled diff --git a/go.mod b/go.mod index 77d7d3ad9..f2c993c64 100644 --- a/go.mod +++ b/go.mod @@ -93,7 +93,6 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.21.0 // indirect - github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect @@ -123,8 +122,6 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opencontainers/runc v1.1.14 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect - github.com/ordishs/go-utils v1.0.51 // indirect github.com/paulmach/orb v0.9.0 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect @@ -144,8 +141,6 @@ require ( github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect - github.com/uber/jaeger-lib v2.4.1+incompatible // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect diff --git a/go.sum b/go.sum index 58d92b8fc..63acab19a 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,6 @@ github.com/ClickHouse/ch-go v0.55.0 h1:jw4Tpx887YXrkyL5DfgUome/po8MLz92nz2heOQ6R github.com/ClickHouse/ch-go v0.55.0/go.mod h1:kQT2f+yp2p+sagQA/7kS6G3ukym+GQ5KAu1kuFAFDiU= github.com/ClickHouse/clickhouse-go/v2 v2.9.1 h1:IeE2bwVvAba7Yw5ZKu98bKI4NpDmykEy6jUaQdJJCk8= github.com/ClickHouse/clickhouse-go/v2 v2.9.1/go.mod h1:teXfZNM90iQ99Jnuht+dxQXCuhDZ8nvvMoTJOFrcmcg= -github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= -github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= @@ -142,8 +140,6 @@ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwn github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= github.com/grpc-ecosystem/grpc-gateway/v2 v2.21.0 h1:CWyXh/jylQWp2dtiV33mY4iSSp6yf4lmn+c7/tN+ObI= github.com/grpc-ecosystem/grpc-gateway/v2 v2.21.0/go.mod h1:nCLIt0w3Ept2NwF8ThLmrppXsfT07oC8k0XNDxd8sVU= -github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= -github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -273,12 +269,8 @@ github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opencontainers/runc v1.1.14 h1:rgSuzbmgz5DUJjeSnw337TxDbRuqjs6iqQck/2weR6w= github.com/opencontainers/runc v1.1.14/go.mod h1:E4C2z+7BxR7GHXp0hAY53mek+x49X1LjPNeMTfRGvOA= -github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/ordishs/go-bitcoin v1.0.86 h1:OuLnaOfzCe/dHFlCredPFSJKQLOQIuUuuJj/faPtJnE= github.com/ordishs/go-bitcoin v1.0.86/go.mod h1:O3lqD8unDlwLXTmQTT4F5x/Gl3xgP4IgMQDFxTmi9V4= -github.com/ordishs/go-utils v1.0.51 h1:XgBphXkjoUxRdahzyNRpQ5NnB96ygkggIqqzX6ruaFo= -github.com/ordishs/go-utils v1.0.51/go.mod h1:AlHKaGdyFidMIzXcltV/dPtcfoHlhcJl42H4d482dh8= github.com/ory/dockertest/v3 v3.11.0 h1:OiHcxKAvSDUwsEVh2BjxQQc/5EHz9n0va9awCtNGuyA= github.com/ory/dockertest/v3 v3.11.0/go.mod h1:VIPxS1gwT9NpPOrfD3rACs8Y9Z7yhzO4SB194iUDnUI= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= @@ -337,7 +329,6 @@ github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKk github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -350,10 +341,6 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8 github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= -github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= -github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= diff --git a/internal/blocktx/integration_test/reorg_integration_test.go b/internal/blocktx/integration_test/reorg_integration_test.go index 3077fd2de..51a6c3c66 100644 --- a/internal/blocktx/integration_test/reorg_integration_test.go +++ b/internal/blocktx/integration_test/reorg_integration_test.go @@ -60,7 +60,7 @@ func TestReorg(t *testing.T) { const blockHash822011 = "bf9be09b345cc2d904b59951cc8a2ed452d8d143e2e25cde64058270fb3a667a" - // blockHash := testutils.RevChainhash(t, blockHash822011) + blockHash := testutils.RevChainhash(t, blockHash822011) prevBlockHash := testutils.RevChainhash(t, "00000000000000000a00c377b260a3219b0c314763f486bc363df7aa7e22ad72") txHash, err := chainhash.NewHashFromStr("be181e91217d5f802f695e52144078f8dfbe51b8a815c3d6fb48c0d853ec683b") require.NoError(t, err) @@ -69,7 +69,7 @@ func TestReorg(t *testing.T) { // should become LONGEST blockMessage := &bcnet.BlockMessage{ - //Hash: blockHash, + Hash: blockHash, Header: &wire.BlockHeader{ Version: 541065216, PrevBlock: *prevBlockHash, // NON-existent in the db @@ -103,7 +103,7 @@ func TestReorg(t *testing.T) { txhash822015Competing = "b16cea53fc823e146fbb9ae4ad3124f7c273f30562585ad6e4831495d609f430" ) - // blockHash := testutils.RevChainhash(t, blockHash822015Fork) + blockHash := testutils.RevChainhash(t, blockHash822015Fork) prevBlockHash := testutils.RevChainhash(t, blockHash822014StartOfChain) txHash := testutils.RevChainhash(t, txhash822015) txHash2 := testutils.RevChainhash(t, txhash822015Competing) // should not be published - is already in the longest chain @@ -112,7 +112,7 @@ func TestReorg(t *testing.T) { // should become STALE blockMessage := &bcnet.BlockMessage{ - //Hash: blockHash, + Hash: blockHash, Header: &wire.BlockHeader{ Version: 541065216, PrevBlock: *prevBlockHash, // block with status LONGEST at height 822014 @@ -170,7 +170,7 @@ func TestReorg(t *testing.T) { // should become LONGEST // reorg should happen blockMessage := &bcnet.BlockMessage{ - //Hash: blockHash, + Hash: blockHash, Header: &wire.BlockHeader{ Version: 541065216, PrevBlock: *prevhash, // block with status STALE at height 822015 @@ -249,14 +249,14 @@ func TestReorg(t *testing.T) { blockHash822023Orphan = "0000000000000000082131979a4e25a5101912a5f8461e18f306d23e158161cd" ) - // blockHash := testutils.RevChainhash(t, blockHash822021) + blockHash := testutils.RevChainhash(t, blockHash822021) txHash := testutils.RevChainhash(t, "de0753d9ce6f92e340843cbfdd11e58beff8c578956ecdec4c461b018a26b8a9") merkleRoot := testutils.RevChainhash(t, "de0753d9ce6f92e340843cbfdd11e58beff8c578956ecdec4c461b018a26b8a9") prevhash := testutils.RevChainhash(t, blockHash822020Orphan) // should become STALE blockMessage := &bcnet.BlockMessage{ - //Hash: blockHash, + Hash: blockHash, Header: &wire.BlockHeader{ Version: 541065216, PrevBlock: *prevhash, // block with status ORPHANED at height 822020 - connected to STALE chain @@ -321,7 +321,7 @@ func TestReorg(t *testing.T) { txhash822017 = "ece2b7e40d98749c03c551b783420d6e3fdc3c958244bbf275437839585829a6" ) - // blockHash := testutils.RevChainhash(t, blockHash822021) + blockHash := testutils.RevChainhash(t, blockHash822021) prevhash := testutils.RevChainhash(t, blockHash822020Orphan) txHash := testutils.RevChainhash(t, "3e15f823a7de25c26ce9001d4814a6f0ebc915a1ca4f1ba9cfac720bd941c39c") merkleRoot := testutils.RevChainhash(t, "3e15f823a7de25c26ce9001d4814a6f0ebc915a1ca4f1ba9cfac720bd941c39c") @@ -329,7 +329,7 @@ func TestReorg(t *testing.T) { // should become LONGEST // reorg should happen blockMessage := &bcnet.BlockMessage{ - //Hash: blockHash, + Hash: blockHash, Header: &wire.BlockHeader{ Version: 541065216, PrevBlock: *prevhash, // block with status ORPHANED at height 822020 - connected to STALE chain diff --git a/internal/metamorph/bcnet/metamorph_p2p/message_handler.go b/internal/metamorph/bcnet/metamorph_p2p/message_handler.go new file mode 100644 index 000000000..36e0164b9 --- /dev/null +++ b/internal/metamorph/bcnet/metamorph_p2p/message_handler.go @@ -0,0 +1,182 @@ +package metamorph_p2p + +import ( + "context" + "encoding/hex" + "errors" + "fmt" + "log/slog" + "time" + + "github.com/bitcoin-sv/arc/internal/metamorph/metamorph_api" + "github.com/bitcoin-sv/arc/internal/metamorph/store" + "github.com/bitcoin-sv/arc/internal/p2p" + "github.com/libsv/go-p2p/bsvutil" + "github.com/libsv/go-p2p/chaincfg/chainhash" + "github.com/libsv/go-p2p/wire" +) + +var ErrTxRejectedByPeer = errors.New("transaction rejected by peer") + +type TxStatusMessage struct { + Start time.Time + Hash *chainhash.Hash + Status metamorph_api.Status + Peer string + Err error + CompetingTxs []string +} + +var _ p2p.MessageHandlerI = (*MsgHandler)(nil) + +type MsgHandler struct { + logger *slog.Logger + store store.MetamorphStore + messageCh chan<- *TxStatusMessage +} + +func NewMsgHandler(l *slog.Logger, s store.MetamorphStore, messageCh chan<- *TxStatusMessage) *MsgHandler { + ph := &MsgHandler{ + logger: l, + store: s, + messageCh: messageCh, + } + + return ph +} + +// OnReceive handles incoming messages depending on command type +func (h *MsgHandler) OnReceive(msg wire.Message, peer p2p.PeerI) { + cmd := msg.Command() + switch cmd { + case wire.CmdInv: + h.handleReceivedInv(msg, peer) + + case wire.CmdTx: + h.handleReceivedTx(msg, peer) + + case wire.CmdReject: + h.handleReceivedReject(msg, peer) + + case wire.CmdGetData: + h.handleReceivedGetData(msg, peer) + + default: + // ignore other messages + } +} + +// OnSend handles outgoing messages depending on command type +func (h *MsgHandler) OnSend(msg wire.Message, peer p2p.PeerI) { + cmd := msg.Command() + switch cmd { + case wire.CmdTx: + txMsg, ok := msg.(*wire.MsgTx) + if !ok { + return + } + + hash := txMsg.TxHash() + h.messageCh <- &TxStatusMessage{ + Hash: &hash, + Status: metamorph_api.Status_SENT_TO_NETWORK, + Peer: peer.String(), + } + default: + // ignore other messages + } +} + +func (h *MsgHandler) handleReceivedInv(wireMsg wire.Message, peer p2p.PeerI) { + msg, ok := wireMsg.(*wire.MsgInv) + if !ok { + return + } + + go func() { + for _, iv := range msg.InvList { + if iv.Type == wire.InvTypeTx { + select { + case h.messageCh <- &TxStatusMessage{ + Hash: &iv.Hash, + Status: metamorph_api.Status_SEEN_ON_NETWORK, + Peer: peer.String(), + }: + default: // Ensure that writing to channel is non-blocking -- probably we should give up on this + } + } + // ignore INV with block or error + } + }() +} + +func (h *MsgHandler) handleReceivedTx(wireMsg wire.Message, peer p2p.PeerI) { + msg, ok := wireMsg.(*wire.MsgTx) + if !ok { + return + } + + hash := msg.TxHash() + h.messageCh <- &TxStatusMessage{ + Hash: &hash, + Status: metamorph_api.Status_SEEN_ON_NETWORK, + Peer: peer.String(), + } +} + +func (h *MsgHandler) handleReceivedReject(wireMsg wire.Message, peer p2p.PeerI) { + msg, ok := wireMsg.(*wire.MsgReject) + if !ok { + return + } + + h.messageCh <- &TxStatusMessage{ + Hash: &msg.Hash, + Status: metamorph_api.Status_REJECTED, + Peer: peer.String(), + Err: errors.Join(ErrTxRejectedByPeer, fmt.Errorf("peer: %s reason: %s", peer.String(), msg.Reason)), + } +} + +func (h *MsgHandler) handleReceivedGetData(wireMsg wire.Message, peer p2p.PeerI) { + msg, ok := wireMsg.(*wire.MsgGetData) + if !ok { + return + } + + // do not block main goroutine + go func(msg *wire.MsgGetData, peer p2p.PeerI) { + // handle tx INV + txRequests := make([][]byte, 0, len(msg.InvList)) + + for _, iv := range msg.InvList { + if iv.Type == wire.InvTypeTx { + txRequests = append(txRequests, iv.Hash[:]) + } + // ignore other INV types + } + + rtx, err := h.store.GetRawTxs(context.Background(), txRequests) + if err != nil { + h.logger.Error("Unable to fetch txs from store", slog.Int("count", len(txRequests)), slog.String("err", err.Error())) + return + } + + for _, txBytes := range rtx { + tx, err := bsvutil.NewTxFromBytes(txBytes) + if err != nil { + h.logger.Error("failed to parse tx", slog.String("rawHex", hex.EncodeToString(txBytes)), slog.String("err", err.Error())) + continue + } + + h.messageCh <- &TxStatusMessage{ + Hash: tx.Hash(), + Status: metamorph_api.Status_REQUESTED_BY_NETWORK, + Peer: peer.String(), + } + + wm := tx.MsgTx() + peer.WriteMsg(wm) + } + }(msg, peer) +} diff --git a/internal/metamorph/bcnet/metamorph_p2p/message_handler_test.go b/internal/metamorph/bcnet/metamorph_p2p/message_handler_test.go new file mode 100644 index 000000000..3f9a80334 --- /dev/null +++ b/internal/metamorph/bcnet/metamorph_p2p/message_handler_test.go @@ -0,0 +1,196 @@ +package metamorph_p2p + +import ( + "context" + "errors" + "fmt" + "log/slog" + "testing" + "time" + + "github.com/bitcoin-sv/arc/internal/metamorph/metamorph_api" + storeMocks "github.com/bitcoin-sv/arc/internal/metamorph/store/mocks" + p2pMocks "github.com/bitcoin-sv/arc/internal/p2p/mocks" + "github.com/bitcoin-sv/arc/internal/testdata" + "github.com/libsv/go-p2p/wire" + "github.com/stretchr/testify/assert" +) + +const ( + bitconnet = wire.TestNet + peerAddr = "peer" +) + +var ( + txHash = testdata.TX1Hash + blockHash = testdata.Block1Hash +) + +func Test_MessageHandlerOnReceive(t *testing.T) { + tt := []struct { + name string + wireMsg wire.Message + expectedOnChannelMsg *TxStatusMessage + ignore bool + }{ + { + name: wire.CmdTx, + wireMsg: wire.NewMsgTx(70001), + expectedOnChannelMsg: &TxStatusMessage{ + Hash: ptrTo(wire.NewMsgTx(70001).TxHash()), + Status: metamorph_api.Status_SEEN_ON_NETWORK, + Peer: peerAddr, + }, + }, + { + name: wire.CmdInv, + wireMsg: func() wire.Message { + msg := wire.NewMsgInv() + _ = msg.AddInvVect(wire.NewInvVect(wire.InvTypeTx, txHash)) + return msg + }(), + + expectedOnChannelMsg: &TxStatusMessage{ + Hash: testdata.TX1Hash, + Status: metamorph_api.Status_SEEN_ON_NETWORK, + Peer: peerAddr, + }, + }, + { + name: wire.CmdInv + " BLOCK should ignore", + wireMsg: func() wire.Message { + msg := wire.NewMsgInv() + _ = msg.AddInvVect(wire.NewInvVect(wire.InvTypeBlock, blockHash)) + return msg + }(), + ignore: true, + }, + { + name: wire.CmdReject, + wireMsg: wire.NewMsgReject("command", wire.RejectMalformed, "malformed"), + expectedOnChannelMsg: &TxStatusMessage{ + Hash: ptrTo(wire.NewMsgReject("command", wire.RejectMalformed, "malformed").Hash), + Status: metamorph_api.Status_REJECTED, + Peer: peerAddr, + Err: errors.Join(ErrTxRejectedByPeer, fmt.Errorf("peer: %s reason: %s", peerAddr, "malformed")), + }, + }, + { + name: wire.CmdGetData, + wireMsg: func() wire.Message { + msg := wire.NewMsgGetData() + // add block inv + _ = msg.AddInvVect(wire.NewInvVect(wire.InvTypeBlock, blockHash)) + // add tx inv + _ = msg.AddInvVect(wire.NewInvVect(wire.InvTypeTx, txHash)) + + return msg + }(), + expectedOnChannelMsg: &TxStatusMessage{ + Hash: txHash, + Status: metamorph_api.Status_REQUESTED_BY_NETWORK, + Peer: peerAddr, + }, + }, + } + + for _, tc := range tt { + t.Run(fmt.Sprintf("Received %s", tc.name), func(t *testing.T) { + // given + messageCh := make(chan *TxStatusMessage, 10) + store := &storeMocks.MetamorphStoreMock{ + GetRawTxsFunc: func(_ context.Context, _ [][]byte) ([][]byte, error) { + return [][]byte{ + testdata.TX1Raw.Bytes(), + }, nil + }, + } + peer := &p2pMocks.PeerIMock{ + StringFunc: func() string { return peerAddr }, + WriteMsgFunc: func(_ wire.Message) {}, + } + + sut := NewMsgHandler(slog.Default(), store, messageCh) + + // when + sut.OnReceive(tc.wireMsg, peer) + + // then + select { + case msg := <-messageCh: + if tc.ignore { + t.Fatal("MsgHandler react on message it should ignore") + } + + assert.Equal(t, tc.expectedOnChannelMsg, msg) + + case <-time.After(time.Second): + if !tc.ignore { + t.Fatal("test timed out or error while executing goroutine") + } + } + }) + } +} + +func Test_MessageHandlerOnSend(t *testing.T) { + tt := []struct { + name string + wireMsg wire.Message + expectedOnChannelMsg *TxStatusMessage + ignore bool + }{ + { + name: wire.CmdTx, + wireMsg: wire.NewMsgTx(70001), + expectedOnChannelMsg: &TxStatusMessage{ + Hash: ptrTo(wire.NewMsgTx(70001).TxHash()), + Status: metamorph_api.Status_SENT_TO_NETWORK, + Peer: peerAddr, + }, + }, + { + name: wire.CmdInv + " should ignore", + wireMsg: func() wire.Message { + msg := wire.NewMsgInv() + _ = msg.AddInvVect(wire.NewInvVect(wire.InvTypeTx, txHash)) + return msg + }(), + ignore: true, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + messageCh := make(chan *TxStatusMessage, 10) + store := &storeMocks.MetamorphStoreMock{} + peer := &p2pMocks.PeerIMock{ + StringFunc: func() string { return peerAddr }, + } + + sut := NewMsgHandler(slog.Default(), store, messageCh) + + // when + sut.OnSend(tc.wireMsg, peer) + + // then + select { + case msg := <-messageCh: + if tc.ignore { + t.Fatal("MsgHandler react on message it should ignore") + } + + assert.Equal(t, tc.expectedOnChannelMsg, msg) + + case <-time.After(time.Second): + if !tc.ignore { + t.Fatal("test timed out or error while executing goroutine") + } + } + }) + } +} + +func ptrTo[T any](v T) *T { + return &v +} diff --git a/internal/metamorph/collector_processor.go b/internal/metamorph/collector_processor.go index 9bf069f0b..93407d938 100644 --- a/internal/metamorph/collector_processor.go +++ b/internal/metamorph/collector_processor.go @@ -49,7 +49,7 @@ func (c *prometheusCollector) Collect(ch chan<- prometheus.Metric) { healthyConnections := 0 for _, peer := range c.processor.GetPeers() { - if peer.Connected() && peer.IsHealthy() { + if peer.Connected() { healthyConnections++ continue } diff --git a/internal/metamorph/integration_test/double_spend_integration_test.go b/internal/metamorph/integration_test/double_spend_integration_test.go index 5ab7f18a1..5a57e73e3 100644 --- a/internal/metamorph/integration_test/double_spend_integration_test.go +++ b/internal/metamorph/integration_test/double_spend_integration_test.go @@ -30,9 +30,11 @@ import ( "github.com/bitcoin-sv/arc/internal/blocktx/blocktx_api" "github.com/bitcoin-sv/arc/internal/cache" "github.com/bitcoin-sv/arc/internal/metamorph" + "github.com/bitcoin-sv/arc/internal/metamorph/bcnet/metamorph_p2p" "github.com/bitcoin-sv/arc/internal/metamorph/metamorph_api" "github.com/bitcoin-sv/arc/internal/metamorph/mocks" "github.com/bitcoin-sv/arc/internal/metamorph/store/postgresql" + "github.com/bitcoin-sv/arc/internal/p2p" testutils "github.com/bitcoin-sv/arc/internal/test_utils" ) @@ -54,7 +56,7 @@ func TestDoubleSpendDetection(t *testing.T) { logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})) - statusMessageChannel := make(chan *metamorph.TxStatusMessage, 10) + statusMessageChannel := make(chan *metamorph_p2p.TxStatusMessage, 10) minedTxChannel := make(chan *blocktx_api.TransactionBlock, 10) mockedZMQ := &mocks.ZMQIMock{ @@ -75,13 +77,14 @@ func TestDoubleSpendDetection(t *testing.T) { require.NoError(t, err) defer metamorphStore.Close(context.Background()) - pm := &mocks.PeerManagerMock{ShutdownFunc: func() {}} cStore := cache.NewMemoryStore() for _, h := range hashes { err := cStore.Set(h, []byte("1"), 10*time.Minute) // make sure txs are registered require.NoError(t, err) } + pm := &p2p.NetworkMessenger{} + processor, err := metamorph.NewProcessor(metamorphStore, cStore, pm, statusMessageChannel, metamorph.WithMinedTxsChan(minedTxChannel), metamorph.WithNow(func() time.Time { return time.Date(2023, 10, 1, 13, 0, 0, 0, time.UTC) }), diff --git a/internal/metamorph/integration_test/processor_integration_test.go b/internal/metamorph/integration_test/processor_integration_test.go index 4512e2592..1afd3c3e1 100644 --- a/internal/metamorph/integration_test/processor_integration_test.go +++ b/internal/metamorph/integration_test/processor_integration_test.go @@ -2,6 +2,7 @@ package integrationtest import ( "context" + "log/slog" "testing" "time" @@ -9,13 +10,15 @@ import ( "github.com/bitcoin-sv/arc/internal/message_queue/nats/client/nats_core" nats_mocks "github.com/bitcoin-sv/arc/internal/message_queue/nats/client/nats_core/mocks" "github.com/bitcoin-sv/arc/internal/metamorph" + "github.com/bitcoin-sv/arc/internal/metamorph/bcnet/metamorph_p2p" "github.com/bitcoin-sv/arc/internal/metamorph/metamorph_api" - "github.com/bitcoin-sv/arc/internal/metamorph/mocks" "github.com/bitcoin-sv/arc/internal/metamorph/store" "github.com/bitcoin-sv/arc/internal/metamorph/store/postgresql" + "github.com/bitcoin-sv/arc/internal/p2p" + p2p_mocks "github.com/bitcoin-sv/arc/internal/p2p/mocks" testutils "github.com/bitcoin-sv/arc/internal/test_utils" - "github.com/libsv/go-p2p" - "github.com/libsv/go-p2p/chaincfg/chainhash" + + "github.com/libsv/go-p2p/wire" "github.com/stretchr/testify/require" ) @@ -31,15 +34,19 @@ func TestProcessor(t *testing.T) { cacheStore := cache.NewRedisStore(context.Background(), redisClient) - pm := &mocks.PeerManagerMock{ - ShutdownFunc: func() {}, - RequestTransactionFunc: func(_ *chainhash.Hash) p2p.PeerI { - return nil - }, - AnnounceTransactionFunc: func(_ *chainhash.Hash, _ []p2p.PeerI) []p2p.PeerI { - return nil - }, + peer := &p2p_mocks.PeerIMock{ + WriteMsgFunc: func(_ wire.Message) {}, + NetworkFunc: func() wire.BitcoinNet { return wire.TestNet }, + StringFunc: func() string { return "peer" }, + ConnectedFunc: func() bool { return true }, } + + pm := p2p.NewPeerManager(slog.Default(), wire.TestNet) + err = pm.AddPeer(peer) + require.NoError(t, err) + + messenger := p2p.NewNetworkMessenger(slog.Default(), pm) + natsMock := &nats_mocks.NatsConnectionMock{ DrainFunc: func() error { return nil @@ -49,9 +56,9 @@ func TestProcessor(t *testing.T) { }, } natsQueue := nats_core.New(natsMock) - statusMessageChannel := make(chan *metamorph.TxStatusMessage, 10) + statusMessageChannel := make(chan *metamorph_p2p.TxStatusMessage, 10) - sut, err := metamorph.NewProcessor(mtmStore, cacheStore, pm, statusMessageChannel, + sut, err := metamorph.NewProcessor(mtmStore, cacheStore, messenger, statusMessageChannel, metamorph.WithProcessStatusUpdatesInterval(200*time.Millisecond), metamorph.WithMessageQueueClient(natsQueue), ) @@ -117,17 +124,17 @@ func TestProcessor(t *testing.T) { } // when - statusMessageChannel <- &metamorph.TxStatusMessage{ + statusMessageChannel <- &metamorph_p2p.TxStatusMessage{ Hash: tx1, Status: metamorph_api.Status_ACCEPTED_BY_NETWORK, Peer: "", } - statusMessageChannel <- &metamorph.TxStatusMessage{ + statusMessageChannel <- &metamorph_p2p.TxStatusMessage{ Hash: tx2, Status: metamorph_api.Status_REJECTED, // this tx should be removed from the cache - final status Peer: "", } - statusMessageChannel <- &metamorph.TxStatusMessage{ + statusMessageChannel <- &metamorph_p2p.TxStatusMessage{ Hash: txNotRegistered, // this tx should not be processed Status: metamorph_api.Status_ACCEPTED_BY_NETWORK, Peer: "", diff --git a/internal/metamorph/metamorph_mocks.go b/internal/metamorph/metamorph_mocks.go index c3f17ba82..0d7f4d344 100644 --- a/internal/metamorph/metamorph_mocks.go +++ b/internal/metamorph/metamorph_mocks.go @@ -12,12 +12,6 @@ package metamorph // from processor.go //go:generate moq -pkg mocks -out ./mocks/callback_sender_mock.go . CallbackSender -// from peer_manager.go -//go:generate moq -pkg mocks -out ./mocks/peer_manager_mock.go . PeerManager - -// from peer.go -//go:generate moq -pkg mocks -out ./mocks/peer_mock.go . PeerI - // from zmq.go //go:generate moq -pkg mocks -out ./mocks/zmq_mock.go . ZMQI diff --git a/internal/metamorph/mocks/peer_manager_mock.go b/internal/metamorph/mocks/peer_manager_mock.go deleted file mode 100644 index f60305fcc..000000000 --- a/internal/metamorph/mocks/peer_manager_mock.go +++ /dev/null @@ -1,339 +0,0 @@ -// Code generated by moq; DO NOT EDIT. -// github.com/matryer/moq - -package mocks - -import ( - "github.com/bitcoin-sv/arc/internal/metamorph" - "github.com/libsv/go-p2p" - "github.com/libsv/go-p2p/chaincfg/chainhash" - "sync" -) - -// Ensure, that PeerManagerMock does implement metamorph.PeerManager. -// If this is not the case, regenerate this file with moq. -var _ metamorph.PeerManager = &PeerManagerMock{} - -// PeerManagerMock is a mock implementation of metamorph.PeerManager. -// -// func TestSomethingThatUsesPeerManager(t *testing.T) { -// -// // make and configure a mocked metamorph.PeerManager -// mockedPeerManager := &PeerManagerMock{ -// AddPeerFunc: func(peer p2p.PeerI) error { -// panic("mock out the AddPeer method") -// }, -// AnnounceBlockFunc: func(blockHash *chainhash.Hash, peers []p2p.PeerI) []p2p.PeerI { -// panic("mock out the AnnounceBlock method") -// }, -// AnnounceTransactionFunc: func(txHash *chainhash.Hash, peers []p2p.PeerI) []p2p.PeerI { -// panic("mock out the AnnounceTransaction method") -// }, -// GetPeersFunc: func() []p2p.PeerI { -// panic("mock out the GetPeers method") -// }, -// RequestBlockFunc: func(blockHash *chainhash.Hash) p2p.PeerI { -// panic("mock out the RequestBlock method") -// }, -// RequestTransactionFunc: func(txHash *chainhash.Hash) p2p.PeerI { -// panic("mock out the RequestTransaction method") -// }, -// ShutdownFunc: func() { -// panic("mock out the Shutdown method") -// }, -// } -// -// // use mockedPeerManager in code that requires metamorph.PeerManager -// // and then make assertions. -// -// } -type PeerManagerMock struct { - // AddPeerFunc mocks the AddPeer method. - AddPeerFunc func(peer p2p.PeerI) error - - // AnnounceBlockFunc mocks the AnnounceBlock method. - AnnounceBlockFunc func(blockHash *chainhash.Hash, peers []p2p.PeerI) []p2p.PeerI - - // AnnounceTransactionFunc mocks the AnnounceTransaction method. - AnnounceTransactionFunc func(txHash *chainhash.Hash, peers []p2p.PeerI) []p2p.PeerI - - // GetPeersFunc mocks the GetPeers method. - GetPeersFunc func() []p2p.PeerI - - // RequestBlockFunc mocks the RequestBlock method. - RequestBlockFunc func(blockHash *chainhash.Hash) p2p.PeerI - - // RequestTransactionFunc mocks the RequestTransaction method. - RequestTransactionFunc func(txHash *chainhash.Hash) p2p.PeerI - - // ShutdownFunc mocks the Shutdown method. - ShutdownFunc func() - - // calls tracks calls to the methods. - calls struct { - // AddPeer holds details about calls to the AddPeer method. - AddPeer []struct { - // Peer is the peer argument value. - Peer p2p.PeerI - } - // AnnounceBlock holds details about calls to the AnnounceBlock method. - AnnounceBlock []struct { - // BlockHash is the blockHash argument value. - BlockHash *chainhash.Hash - // Peers is the peers argument value. - Peers []p2p.PeerI - } - // AnnounceTransaction holds details about calls to the AnnounceTransaction method. - AnnounceTransaction []struct { - // TxHash is the txHash argument value. - TxHash *chainhash.Hash - // Peers is the peers argument value. - Peers []p2p.PeerI - } - // GetPeers holds details about calls to the GetPeers method. - GetPeers []struct { - } - // RequestBlock holds details about calls to the RequestBlock method. - RequestBlock []struct { - // BlockHash is the blockHash argument value. - BlockHash *chainhash.Hash - } - // RequestTransaction holds details about calls to the RequestTransaction method. - RequestTransaction []struct { - // TxHash is the txHash argument value. - TxHash *chainhash.Hash - } - // Shutdown holds details about calls to the Shutdown method. - Shutdown []struct { - } - } - lockAddPeer sync.RWMutex - lockAnnounceBlock sync.RWMutex - lockAnnounceTransaction sync.RWMutex - lockGetPeers sync.RWMutex - lockRequestBlock sync.RWMutex - lockRequestTransaction sync.RWMutex - lockShutdown sync.RWMutex -} - -// AddPeer calls AddPeerFunc. -func (mock *PeerManagerMock) AddPeer(peer p2p.PeerI) error { - if mock.AddPeerFunc == nil { - panic("PeerManagerMock.AddPeerFunc: method is nil but PeerManager.AddPeer was just called") - } - callInfo := struct { - Peer p2p.PeerI - }{ - Peer: peer, - } - mock.lockAddPeer.Lock() - mock.calls.AddPeer = append(mock.calls.AddPeer, callInfo) - mock.lockAddPeer.Unlock() - return mock.AddPeerFunc(peer) -} - -// AddPeerCalls gets all the calls that were made to AddPeer. -// Check the length with: -// -// len(mockedPeerManager.AddPeerCalls()) -func (mock *PeerManagerMock) AddPeerCalls() []struct { - Peer p2p.PeerI -} { - var calls []struct { - Peer p2p.PeerI - } - mock.lockAddPeer.RLock() - calls = mock.calls.AddPeer - mock.lockAddPeer.RUnlock() - return calls -} - -// AnnounceBlock calls AnnounceBlockFunc. -func (mock *PeerManagerMock) AnnounceBlock(blockHash *chainhash.Hash, peers []p2p.PeerI) []p2p.PeerI { - if mock.AnnounceBlockFunc == nil { - panic("PeerManagerMock.AnnounceBlockFunc: method is nil but PeerManager.AnnounceBlock was just called") - } - callInfo := struct { - BlockHash *chainhash.Hash - Peers []p2p.PeerI - }{ - BlockHash: blockHash, - Peers: peers, - } - mock.lockAnnounceBlock.Lock() - mock.calls.AnnounceBlock = append(mock.calls.AnnounceBlock, callInfo) - mock.lockAnnounceBlock.Unlock() - return mock.AnnounceBlockFunc(blockHash, peers) -} - -// AnnounceBlockCalls gets all the calls that were made to AnnounceBlock. -// Check the length with: -// -// len(mockedPeerManager.AnnounceBlockCalls()) -func (mock *PeerManagerMock) AnnounceBlockCalls() []struct { - BlockHash *chainhash.Hash - Peers []p2p.PeerI -} { - var calls []struct { - BlockHash *chainhash.Hash - Peers []p2p.PeerI - } - mock.lockAnnounceBlock.RLock() - calls = mock.calls.AnnounceBlock - mock.lockAnnounceBlock.RUnlock() - return calls -} - -// AnnounceTransaction calls AnnounceTransactionFunc. -func (mock *PeerManagerMock) AnnounceTransaction(txHash *chainhash.Hash, peers []p2p.PeerI) []p2p.PeerI { - if mock.AnnounceTransactionFunc == nil { - panic("PeerManagerMock.AnnounceTransactionFunc: method is nil but PeerManager.AnnounceTransaction was just called") - } - callInfo := struct { - TxHash *chainhash.Hash - Peers []p2p.PeerI - }{ - TxHash: txHash, - Peers: peers, - } - mock.lockAnnounceTransaction.Lock() - mock.calls.AnnounceTransaction = append(mock.calls.AnnounceTransaction, callInfo) - mock.lockAnnounceTransaction.Unlock() - return mock.AnnounceTransactionFunc(txHash, peers) -} - -// AnnounceTransactionCalls gets all the calls that were made to AnnounceTransaction. -// Check the length with: -// -// len(mockedPeerManager.AnnounceTransactionCalls()) -func (mock *PeerManagerMock) AnnounceTransactionCalls() []struct { - TxHash *chainhash.Hash - Peers []p2p.PeerI -} { - var calls []struct { - TxHash *chainhash.Hash - Peers []p2p.PeerI - } - mock.lockAnnounceTransaction.RLock() - calls = mock.calls.AnnounceTransaction - mock.lockAnnounceTransaction.RUnlock() - return calls -} - -// GetPeers calls GetPeersFunc. -func (mock *PeerManagerMock) GetPeers() []p2p.PeerI { - if mock.GetPeersFunc == nil { - panic("PeerManagerMock.GetPeersFunc: method is nil but PeerManager.GetPeers was just called") - } - callInfo := struct { - }{} - mock.lockGetPeers.Lock() - mock.calls.GetPeers = append(mock.calls.GetPeers, callInfo) - mock.lockGetPeers.Unlock() - return mock.GetPeersFunc() -} - -// GetPeersCalls gets all the calls that were made to GetPeers. -// Check the length with: -// -// len(mockedPeerManager.GetPeersCalls()) -func (mock *PeerManagerMock) GetPeersCalls() []struct { -} { - var calls []struct { - } - mock.lockGetPeers.RLock() - calls = mock.calls.GetPeers - mock.lockGetPeers.RUnlock() - return calls -} - -// RequestBlock calls RequestBlockFunc. -func (mock *PeerManagerMock) RequestBlock(blockHash *chainhash.Hash) p2p.PeerI { - if mock.RequestBlockFunc == nil { - panic("PeerManagerMock.RequestBlockFunc: method is nil but PeerManager.RequestBlock was just called") - } - callInfo := struct { - BlockHash *chainhash.Hash - }{ - BlockHash: blockHash, - } - mock.lockRequestBlock.Lock() - mock.calls.RequestBlock = append(mock.calls.RequestBlock, callInfo) - mock.lockRequestBlock.Unlock() - return mock.RequestBlockFunc(blockHash) -} - -// RequestBlockCalls gets all the calls that were made to RequestBlock. -// Check the length with: -// -// len(mockedPeerManager.RequestBlockCalls()) -func (mock *PeerManagerMock) RequestBlockCalls() []struct { - BlockHash *chainhash.Hash -} { - var calls []struct { - BlockHash *chainhash.Hash - } - mock.lockRequestBlock.RLock() - calls = mock.calls.RequestBlock - mock.lockRequestBlock.RUnlock() - return calls -} - -// RequestTransaction calls RequestTransactionFunc. -func (mock *PeerManagerMock) RequestTransaction(txHash *chainhash.Hash) p2p.PeerI { - if mock.RequestTransactionFunc == nil { - panic("PeerManagerMock.RequestTransactionFunc: method is nil but PeerManager.RequestTransaction was just called") - } - callInfo := struct { - TxHash *chainhash.Hash - }{ - TxHash: txHash, - } - mock.lockRequestTransaction.Lock() - mock.calls.RequestTransaction = append(mock.calls.RequestTransaction, callInfo) - mock.lockRequestTransaction.Unlock() - return mock.RequestTransactionFunc(txHash) -} - -// RequestTransactionCalls gets all the calls that were made to RequestTransaction. -// Check the length with: -// -// len(mockedPeerManager.RequestTransactionCalls()) -func (mock *PeerManagerMock) RequestTransactionCalls() []struct { - TxHash *chainhash.Hash -} { - var calls []struct { - TxHash *chainhash.Hash - } - mock.lockRequestTransaction.RLock() - calls = mock.calls.RequestTransaction - mock.lockRequestTransaction.RUnlock() - return calls -} - -// Shutdown calls ShutdownFunc. -func (mock *PeerManagerMock) Shutdown() { - if mock.ShutdownFunc == nil { - panic("PeerManagerMock.ShutdownFunc: method is nil but PeerManager.Shutdown was just called") - } - callInfo := struct { - }{} - mock.lockShutdown.Lock() - mock.calls.Shutdown = append(mock.calls.Shutdown, callInfo) - mock.lockShutdown.Unlock() - mock.ShutdownFunc() -} - -// ShutdownCalls gets all the calls that were made to Shutdown. -// Check the length with: -// -// len(mockedPeerManager.ShutdownCalls()) -func (mock *PeerManagerMock) ShutdownCalls() []struct { -} { - var calls []struct { - } - mock.lockShutdown.RLock() - calls = mock.calls.Shutdown - mock.lockShutdown.RUnlock() - return calls -} diff --git a/internal/metamorph/mocks/peer_mock.go b/internal/metamorph/mocks/peer_mock.go deleted file mode 100644 index c186a7b3f..000000000 --- a/internal/metamorph/mocks/peer_mock.go +++ /dev/null @@ -1,512 +0,0 @@ -// Code generated by moq; DO NOT EDIT. -// github.com/matryer/moq - -package mocks - -import ( - "github.com/bitcoin-sv/arc/internal/metamorph" - "github.com/libsv/go-p2p/chaincfg/chainhash" - "github.com/libsv/go-p2p/wire" - "sync" -) - -// Ensure, that PeerIMock does implement metamorph.PeerI. -// If this is not the case, regenerate this file with moq. -var _ metamorph.PeerI = &PeerIMock{} - -// PeerIMock is a mock implementation of metamorph.PeerI. -// -// func TestSomethingThatUsesPeerI(t *testing.T) { -// -// // make and configure a mocked metamorph.PeerI -// mockedPeerI := &PeerIMock{ -// AnnounceBlockFunc: func(blockHash *chainhash.Hash) { -// panic("mock out the AnnounceBlock method") -// }, -// AnnounceTransactionFunc: func(txHash *chainhash.Hash) { -// panic("mock out the AnnounceTransaction method") -// }, -// ConnectedFunc: func() bool { -// panic("mock out the Connected method") -// }, -// IsHealthyFunc: func() bool { -// panic("mock out the IsHealthy method") -// }, -// IsUnhealthyChFunc: func() <-chan struct{} { -// panic("mock out the IsUnhealthyCh method") -// }, -// NetworkFunc: func() wire.BitcoinNet { -// panic("mock out the Network method") -// }, -// RequestBlockFunc: func(blockHash *chainhash.Hash) { -// panic("mock out the RequestBlock method") -// }, -// RequestTransactionFunc: func(txHash *chainhash.Hash) { -// panic("mock out the RequestTransaction method") -// }, -// RestartFunc: func() { -// panic("mock out the Restart method") -// }, -// ShutdownFunc: func() { -// panic("mock out the Shutdown method") -// }, -// StringFunc: func() string { -// panic("mock out the String method") -// }, -// WriteMsgFunc: func(msg wire.Message) error { -// panic("mock out the WriteMsg method") -// }, -// } -// -// // use mockedPeerI in code that requires metamorph.PeerI -// // and then make assertions. -// -// } -type PeerIMock struct { - // AnnounceBlockFunc mocks the AnnounceBlock method. - AnnounceBlockFunc func(blockHash *chainhash.Hash) - - // AnnounceTransactionFunc mocks the AnnounceTransaction method. - AnnounceTransactionFunc func(txHash *chainhash.Hash) - - // ConnectedFunc mocks the Connected method. - ConnectedFunc func() bool - - // IsHealthyFunc mocks the IsHealthy method. - IsHealthyFunc func() bool - - // IsUnhealthyChFunc mocks the IsUnhealthyCh method. - IsUnhealthyChFunc func() <-chan struct{} - - // NetworkFunc mocks the Network method. - NetworkFunc func() wire.BitcoinNet - - // RequestBlockFunc mocks the RequestBlock method. - RequestBlockFunc func(blockHash *chainhash.Hash) - - // RequestTransactionFunc mocks the RequestTransaction method. - RequestTransactionFunc func(txHash *chainhash.Hash) - - // RestartFunc mocks the Restart method. - RestartFunc func() - - // ShutdownFunc mocks the Shutdown method. - ShutdownFunc func() - - // StringFunc mocks the String method. - StringFunc func() string - - // WriteMsgFunc mocks the WriteMsg method. - WriteMsgFunc func(msg wire.Message) error - - // calls tracks calls to the methods. - calls struct { - // AnnounceBlock holds details about calls to the AnnounceBlock method. - AnnounceBlock []struct { - // BlockHash is the blockHash argument value. - BlockHash *chainhash.Hash - } - // AnnounceTransaction holds details about calls to the AnnounceTransaction method. - AnnounceTransaction []struct { - // TxHash is the txHash argument value. - TxHash *chainhash.Hash - } - // Connected holds details about calls to the Connected method. - Connected []struct { - } - // IsHealthy holds details about calls to the IsHealthy method. - IsHealthy []struct { - } - // IsUnhealthyCh holds details about calls to the IsUnhealthyCh method. - IsUnhealthyCh []struct { - } - // Network holds details about calls to the Network method. - Network []struct { - } - // RequestBlock holds details about calls to the RequestBlock method. - RequestBlock []struct { - // BlockHash is the blockHash argument value. - BlockHash *chainhash.Hash - } - // RequestTransaction holds details about calls to the RequestTransaction method. - RequestTransaction []struct { - // TxHash is the txHash argument value. - TxHash *chainhash.Hash - } - // Restart holds details about calls to the Restart method. - Restart []struct { - } - // Shutdown holds details about calls to the Shutdown method. - Shutdown []struct { - } - // String holds details about calls to the String method. - String []struct { - } - // WriteMsg holds details about calls to the WriteMsg method. - WriteMsg []struct { - // Msg is the msg argument value. - Msg wire.Message - } - } - lockAnnounceBlock sync.RWMutex - lockAnnounceTransaction sync.RWMutex - lockConnected sync.RWMutex - lockIsHealthy sync.RWMutex - lockIsUnhealthyCh sync.RWMutex - lockNetwork sync.RWMutex - lockRequestBlock sync.RWMutex - lockRequestTransaction sync.RWMutex - lockRestart sync.RWMutex - lockShutdown sync.RWMutex - lockString sync.RWMutex - lockWriteMsg sync.RWMutex -} - -// AnnounceBlock calls AnnounceBlockFunc. -func (mock *PeerIMock) AnnounceBlock(blockHash *chainhash.Hash) { - if mock.AnnounceBlockFunc == nil { - panic("PeerIMock.AnnounceBlockFunc: method is nil but PeerI.AnnounceBlock was just called") - } - callInfo := struct { - BlockHash *chainhash.Hash - }{ - BlockHash: blockHash, - } - mock.lockAnnounceBlock.Lock() - mock.calls.AnnounceBlock = append(mock.calls.AnnounceBlock, callInfo) - mock.lockAnnounceBlock.Unlock() - mock.AnnounceBlockFunc(blockHash) -} - -// AnnounceBlockCalls gets all the calls that were made to AnnounceBlock. -// Check the length with: -// -// len(mockedPeerI.AnnounceBlockCalls()) -func (mock *PeerIMock) AnnounceBlockCalls() []struct { - BlockHash *chainhash.Hash -} { - var calls []struct { - BlockHash *chainhash.Hash - } - mock.lockAnnounceBlock.RLock() - calls = mock.calls.AnnounceBlock - mock.lockAnnounceBlock.RUnlock() - return calls -} - -// AnnounceTransaction calls AnnounceTransactionFunc. -func (mock *PeerIMock) AnnounceTransaction(txHash *chainhash.Hash) { - if mock.AnnounceTransactionFunc == nil { - panic("PeerIMock.AnnounceTransactionFunc: method is nil but PeerI.AnnounceTransaction was just called") - } - callInfo := struct { - TxHash *chainhash.Hash - }{ - TxHash: txHash, - } - mock.lockAnnounceTransaction.Lock() - mock.calls.AnnounceTransaction = append(mock.calls.AnnounceTransaction, callInfo) - mock.lockAnnounceTransaction.Unlock() - mock.AnnounceTransactionFunc(txHash) -} - -// AnnounceTransactionCalls gets all the calls that were made to AnnounceTransaction. -// Check the length with: -// -// len(mockedPeerI.AnnounceTransactionCalls()) -func (mock *PeerIMock) AnnounceTransactionCalls() []struct { - TxHash *chainhash.Hash -} { - var calls []struct { - TxHash *chainhash.Hash - } - mock.lockAnnounceTransaction.RLock() - calls = mock.calls.AnnounceTransaction - mock.lockAnnounceTransaction.RUnlock() - return calls -} - -// Connected calls ConnectedFunc. -func (mock *PeerIMock) Connected() bool { - if mock.ConnectedFunc == nil { - panic("PeerIMock.ConnectedFunc: method is nil but PeerI.Connected was just called") - } - callInfo := struct { - }{} - mock.lockConnected.Lock() - mock.calls.Connected = append(mock.calls.Connected, callInfo) - mock.lockConnected.Unlock() - return mock.ConnectedFunc() -} - -// ConnectedCalls gets all the calls that were made to Connected. -// Check the length with: -// -// len(mockedPeerI.ConnectedCalls()) -func (mock *PeerIMock) ConnectedCalls() []struct { -} { - var calls []struct { - } - mock.lockConnected.RLock() - calls = mock.calls.Connected - mock.lockConnected.RUnlock() - return calls -} - -// IsHealthy calls IsHealthyFunc. -func (mock *PeerIMock) IsHealthy() bool { - if mock.IsHealthyFunc == nil { - panic("PeerIMock.IsHealthyFunc: method is nil but PeerI.IsHealthy was just called") - } - callInfo := struct { - }{} - mock.lockIsHealthy.Lock() - mock.calls.IsHealthy = append(mock.calls.IsHealthy, callInfo) - mock.lockIsHealthy.Unlock() - return mock.IsHealthyFunc() -} - -// IsHealthyCalls gets all the calls that were made to IsHealthy. -// Check the length with: -// -// len(mockedPeerI.IsHealthyCalls()) -func (mock *PeerIMock) IsHealthyCalls() []struct { -} { - var calls []struct { - } - mock.lockIsHealthy.RLock() - calls = mock.calls.IsHealthy - mock.lockIsHealthy.RUnlock() - return calls -} - -// IsUnhealthyCh calls IsUnhealthyChFunc. -func (mock *PeerIMock) IsUnhealthyCh() <-chan struct{} { - if mock.IsUnhealthyChFunc == nil { - panic("PeerIMock.IsUnhealthyChFunc: method is nil but PeerI.IsUnhealthyCh was just called") - } - callInfo := struct { - }{} - mock.lockIsUnhealthyCh.Lock() - mock.calls.IsUnhealthyCh = append(mock.calls.IsUnhealthyCh, callInfo) - mock.lockIsUnhealthyCh.Unlock() - return mock.IsUnhealthyChFunc() -} - -// IsUnhealthyChCalls gets all the calls that were made to IsUnhealthyCh. -// Check the length with: -// -// len(mockedPeerI.IsUnhealthyChCalls()) -func (mock *PeerIMock) IsUnhealthyChCalls() []struct { -} { - var calls []struct { - } - mock.lockIsUnhealthyCh.RLock() - calls = mock.calls.IsUnhealthyCh - mock.lockIsUnhealthyCh.RUnlock() - return calls -} - -// Network calls NetworkFunc. -func (mock *PeerIMock) Network() wire.BitcoinNet { - if mock.NetworkFunc == nil { - panic("PeerIMock.NetworkFunc: method is nil but PeerI.Network was just called") - } - callInfo := struct { - }{} - mock.lockNetwork.Lock() - mock.calls.Network = append(mock.calls.Network, callInfo) - mock.lockNetwork.Unlock() - return mock.NetworkFunc() -} - -// NetworkCalls gets all the calls that were made to Network. -// Check the length with: -// -// len(mockedPeerI.NetworkCalls()) -func (mock *PeerIMock) NetworkCalls() []struct { -} { - var calls []struct { - } - mock.lockNetwork.RLock() - calls = mock.calls.Network - mock.lockNetwork.RUnlock() - return calls -} - -// RequestBlock calls RequestBlockFunc. -func (mock *PeerIMock) RequestBlock(blockHash *chainhash.Hash) { - if mock.RequestBlockFunc == nil { - panic("PeerIMock.RequestBlockFunc: method is nil but PeerI.RequestBlock was just called") - } - callInfo := struct { - BlockHash *chainhash.Hash - }{ - BlockHash: blockHash, - } - mock.lockRequestBlock.Lock() - mock.calls.RequestBlock = append(mock.calls.RequestBlock, callInfo) - mock.lockRequestBlock.Unlock() - mock.RequestBlockFunc(blockHash) -} - -// RequestBlockCalls gets all the calls that were made to RequestBlock. -// Check the length with: -// -// len(mockedPeerI.RequestBlockCalls()) -func (mock *PeerIMock) RequestBlockCalls() []struct { - BlockHash *chainhash.Hash -} { - var calls []struct { - BlockHash *chainhash.Hash - } - mock.lockRequestBlock.RLock() - calls = mock.calls.RequestBlock - mock.lockRequestBlock.RUnlock() - return calls -} - -// RequestTransaction calls RequestTransactionFunc. -func (mock *PeerIMock) RequestTransaction(txHash *chainhash.Hash) { - if mock.RequestTransactionFunc == nil { - panic("PeerIMock.RequestTransactionFunc: method is nil but PeerI.RequestTransaction was just called") - } - callInfo := struct { - TxHash *chainhash.Hash - }{ - TxHash: txHash, - } - mock.lockRequestTransaction.Lock() - mock.calls.RequestTransaction = append(mock.calls.RequestTransaction, callInfo) - mock.lockRequestTransaction.Unlock() - mock.RequestTransactionFunc(txHash) -} - -// RequestTransactionCalls gets all the calls that were made to RequestTransaction. -// Check the length with: -// -// len(mockedPeerI.RequestTransactionCalls()) -func (mock *PeerIMock) RequestTransactionCalls() []struct { - TxHash *chainhash.Hash -} { - var calls []struct { - TxHash *chainhash.Hash - } - mock.lockRequestTransaction.RLock() - calls = mock.calls.RequestTransaction - mock.lockRequestTransaction.RUnlock() - return calls -} - -// Restart calls RestartFunc. -func (mock *PeerIMock) Restart() { - if mock.RestartFunc == nil { - panic("PeerIMock.RestartFunc: method is nil but PeerI.Restart was just called") - } - callInfo := struct { - }{} - mock.lockRestart.Lock() - mock.calls.Restart = append(mock.calls.Restart, callInfo) - mock.lockRestart.Unlock() - mock.RestartFunc() -} - -// RestartCalls gets all the calls that were made to Restart. -// Check the length with: -// -// len(mockedPeerI.RestartCalls()) -func (mock *PeerIMock) RestartCalls() []struct { -} { - var calls []struct { - } - mock.lockRestart.RLock() - calls = mock.calls.Restart - mock.lockRestart.RUnlock() - return calls -} - -// Shutdown calls ShutdownFunc. -func (mock *PeerIMock) Shutdown() { - if mock.ShutdownFunc == nil { - panic("PeerIMock.ShutdownFunc: method is nil but PeerI.Shutdown was just called") - } - callInfo := struct { - }{} - mock.lockShutdown.Lock() - mock.calls.Shutdown = append(mock.calls.Shutdown, callInfo) - mock.lockShutdown.Unlock() - mock.ShutdownFunc() -} - -// ShutdownCalls gets all the calls that were made to Shutdown. -// Check the length with: -// -// len(mockedPeerI.ShutdownCalls()) -func (mock *PeerIMock) ShutdownCalls() []struct { -} { - var calls []struct { - } - mock.lockShutdown.RLock() - calls = mock.calls.Shutdown - mock.lockShutdown.RUnlock() - return calls -} - -// String calls StringFunc. -func (mock *PeerIMock) String() string { - if mock.StringFunc == nil { - panic("PeerIMock.StringFunc: method is nil but PeerI.String was just called") - } - callInfo := struct { - }{} - mock.lockString.Lock() - mock.calls.String = append(mock.calls.String, callInfo) - mock.lockString.Unlock() - return mock.StringFunc() -} - -// StringCalls gets all the calls that were made to String. -// Check the length with: -// -// len(mockedPeerI.StringCalls()) -func (mock *PeerIMock) StringCalls() []struct { -} { - var calls []struct { - } - mock.lockString.RLock() - calls = mock.calls.String - mock.lockString.RUnlock() - return calls -} - -// WriteMsg calls WriteMsgFunc. -func (mock *PeerIMock) WriteMsg(msg wire.Message) error { - if mock.WriteMsgFunc == nil { - panic("PeerIMock.WriteMsgFunc: method is nil but PeerI.WriteMsg was just called") - } - callInfo := struct { - Msg wire.Message - }{ - Msg: msg, - } - mock.lockWriteMsg.Lock() - mock.calls.WriteMsg = append(mock.calls.WriteMsg, callInfo) - mock.lockWriteMsg.Unlock() - return mock.WriteMsgFunc(msg) -} - -// WriteMsgCalls gets all the calls that were made to WriteMsg. -// Check the length with: -// -// len(mockedPeerI.WriteMsgCalls()) -func (mock *PeerIMock) WriteMsgCalls() []struct { - Msg wire.Message -} { - var calls []struct { - Msg wire.Message - } - mock.lockWriteMsg.RLock() - calls = mock.calls.WriteMsg - mock.lockWriteMsg.RUnlock() - return calls -} diff --git a/internal/metamorph/mocks/processor_mock.go b/internal/metamorph/mocks/processor_mock.go index 7545f1e88..5475284e8 100644 --- a/internal/metamorph/mocks/processor_mock.go +++ b/internal/metamorph/mocks/processor_mock.go @@ -6,7 +6,7 @@ package mocks import ( "context" "github.com/bitcoin-sv/arc/internal/metamorph" - "github.com/libsv/go-p2p" + "github.com/bitcoin-sv/arc/internal/p2p" "sync" ) diff --git a/internal/metamorph/peer.go b/internal/metamorph/peer.go deleted file mode 100644 index 167f93aaf..000000000 --- a/internal/metamorph/peer.go +++ /dev/null @@ -1,21 +0,0 @@ -package metamorph - -import ( - "github.com/libsv/go-p2p/chaincfg/chainhash" - "github.com/libsv/go-p2p/wire" -) - -type PeerI interface { - Connected() bool - WriteMsg(msg wire.Message) error - String() string - AnnounceTransaction(txHash *chainhash.Hash) - RequestTransaction(txHash *chainhash.Hash) - AnnounceBlock(blockHash *chainhash.Hash) - RequestBlock(blockHash *chainhash.Hash) - Network() wire.BitcoinNet - IsHealthy() bool - IsUnhealthyCh() <-chan struct{} - Shutdown() - Restart() -} diff --git a/internal/metamorph/peer_handler.go b/internal/metamorph/peer_handler.go deleted file mode 100644 index 88c68de4a..000000000 --- a/internal/metamorph/peer_handler.go +++ /dev/null @@ -1,125 +0,0 @@ -package metamorph - -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/bitcoin-sv/arc/internal/metamorph/metamorph_api" - "github.com/bitcoin-sv/arc/internal/metamorph/store" - "github.com/libsv/go-p2p" - "github.com/libsv/go-p2p/wire" -) - -var ErrTxRejectedByPeer = errors.New("transaction rejected by peer") - -type PeerHandler struct { - store store.MetamorphStore - messageCh chan *TxStatusMessage - - cancelAll context.CancelFunc - ctx context.Context -} - -func NewPeerHandler(s store.MetamorphStore, messageCh chan *TxStatusMessage) *PeerHandler { - ph := &PeerHandler{ - store: s, - messageCh: messageCh, - } - - ctx, cancelAll := context.WithCancel(context.Background()) - ph.cancelAll = cancelAll - ph.ctx = ctx - - return ph -} - -// HandleTransactionSent is called when a transaction is sent to a peer. -func (m *PeerHandler) HandleTransactionSent(msg *wire.MsgTx, peer p2p.PeerI) error { - hash := msg.TxHash() - m.messageCh <- &TxStatusMessage{ - Hash: &hash, - Status: metamorph_api.Status_SENT_TO_NETWORK, - Peer: peer.String(), - Start: time.Now(), - } - - return nil -} - -// HandleTransactionAnnouncement is a message sent to the PeerHandler when a transaction INV message is received from a peer. -func (m *PeerHandler) HandleTransactionAnnouncement(msg *wire.InvVect, peer p2p.PeerI) error { - select { - case m.messageCh <- &TxStatusMessage{ - Hash: &msg.Hash, - Status: metamorph_api.Status_SEEN_ON_NETWORK, - Peer: peer.String(), - Start: time.Now(), - }: - default: // Ensure that writing to channel is non-blocking - } - - return nil -} - -// HandleTransactionRejection is called when a transaction is rejected by a peer. -func (m *PeerHandler) HandleTransactionRejection(rejMsg *wire.MsgReject, peer p2p.PeerI) error { - m.messageCh <- &TxStatusMessage{ - Hash: &rejMsg.Hash, - Status: metamorph_api.Status_REJECTED, - Peer: peer.String(), - Start: time.Now(), - Err: errors.Join(ErrTxRejectedByPeer, fmt.Errorf("peer: %s reason: %s", peer.String(), rejMsg.Reason)), - } - - return nil -} - -// HandleTransactionsGet is called when a peer requests a transaction. -func (m *PeerHandler) HandleTransactionsGet(msgs []*wire.InvVect, peer p2p.PeerI) ([][]byte, error) { - hashes := make([][]byte, len(msgs)) - - for i, msg := range msgs { - m.messageCh <- &TxStatusMessage{ - Hash: &msg.Hash, - Status: metamorph_api.Status_REQUESTED_BY_NETWORK, - Peer: peer.String(), - Start: time.Now(), - } - - hashes[i] = msg.Hash[:] - } - - return m.store.GetRawTxs(m.ctx, hashes) -} - -// HandleTransaction is called when a transaction is received from a peer. -func (m *PeerHandler) HandleTransaction(msg *wire.MsgTx, peer p2p.PeerI) error { - hash := msg.TxHash() - - m.messageCh <- &TxStatusMessage{ - Hash: &hash, - Status: metamorph_api.Status_SEEN_ON_NETWORK, - Peer: peer.String(), - Start: time.Now(), - } - - return nil -} - -// HandleBlockAnnouncement is called when a block INV message is received from a peer. -func (m *PeerHandler) HandleBlockAnnouncement(_ *wire.InvVect, _ p2p.PeerI) error { - return nil -} - -// HandleBlock is called when a block is received from a peer. -func (m *PeerHandler) HandleBlock(_ wire.Message, _ p2p.PeerI) error { - return nil -} - -func (m *PeerHandler) Shutdown() { - if m.cancelAll != nil { - m.cancelAll() - } -} diff --git a/internal/metamorph/peer_handler_test.go b/internal/metamorph/peer_handler_test.go deleted file mode 100644 index 2d9e1bda0..000000000 --- a/internal/metamorph/peer_handler_test.go +++ /dev/null @@ -1,184 +0,0 @@ -package metamorph_test - -import ( - "context" - "errors" - "testing" - "time" - - "github.com/bitcoin-sv/arc/internal/metamorph" - "github.com/bitcoin-sv/arc/internal/metamorph/metamorph_api" - storeMocks "github.com/bitcoin-sv/arc/internal/metamorph/store/mocks" - "github.com/libsv/go-p2p" - "github.com/libsv/go-p2p/chaincfg/chainhash" - "github.com/libsv/go-p2p/wire" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestPeerHandler(t *testing.T) { - messageCh := make(chan *metamorph.TxStatusMessage, 100) - mtmStore := &storeMocks.MetamorphStoreMock{ - GetRawTxsFunc: func(_ context.Context, _ [][]byte) ([][]byte, error) { - rawTx := []byte("1234") - return [][]byte{rawTx, rawTx}, nil - }, - } - - peerHandler := metamorph.NewPeerHandler(mtmStore, messageCh) - require.NotNil(t, peerHandler) - - peer, err := p2p.NewPeerMock("mock_peer", nil, wire.MainNet) - require.NoError(t, err) - - t.Run("HandleTransactionSent", func(t *testing.T) { - // given - msgTx := wire.NewMsgTx(70001) - hash := msgTx.TxHash() - - expectedMsg := &metamorph.TxStatusMessage{ - Hash: &hash, - Status: metamorph_api.Status_SENT_TO_NETWORK, - Peer: "mock_peer", - } - - // when - go func() { - _ = peerHandler.HandleTransactionSent(msgTx, peer) - }() - - // then - select { - case msg := <-messageCh: - assertEqualMsg(t, expectedMsg, msg) - case <-time.After(time.Second): - t.Fatal("test timed out or error while executing goroutine") - } - }) - - t.Run("HandleTransactionAnnouncement", func(t *testing.T) { - // given - hash, err := chainhash.NewHashFromStr("1234") - require.NoError(t, err) - - msgInv := wire.NewInvVect(wire.InvTypeBlock, hash) - require.NoError(t, err) - - expectedMsg := &metamorph.TxStatusMessage{ - Hash: &msgInv.Hash, - Status: metamorph_api.Status_SEEN_ON_NETWORK, - Peer: "mock_peer", - } - - // when - go func() { - _ = peerHandler.HandleTransactionAnnouncement(msgInv, peer) - }() - - // then - select { - case msg := <-messageCh: - assertEqualMsg(t, expectedMsg, msg) - case <-time.After(time.Second): - t.Fatal("test timed out or error while executing goroutine") - } - }) - - t.Run("HandleTransactionRejection", func(t *testing.T) { - // given - msgReject := wire.NewMsgReject("command", wire.RejectMalformed, "malformed") - - expectedMsg := &metamorph.TxStatusMessage{ - Hash: &msgReject.Hash, - Status: metamorph_api.Status_REJECTED, - Peer: "mock_peer", - Err: errors.Join(metamorph.ErrTxRejectedByPeer, errors.New("peer: mock_peer reason: malformed")), - } - - // when - go func() { - _ = peerHandler.HandleTransactionRejection(msgReject, peer) - }() - - // then - select { - case msg := <-messageCh: - assertEqualMsg(t, expectedMsg, msg) - case <-time.After(time.Second): - t.Fatal("test timed out or error while executing goroutine") - } - }) - - t.Run("HandleTransactionsGet", func(t *testing.T) { - // given - txsCount := 2 - invMsgs := make([]*wire.InvVect, txsCount) - expectedMsgs := make([]*metamorph.TxStatusMessage, txsCount) - - for i := 0; i < txsCount; i++ { - hash, err := chainhash.NewHashFromStr("1234") - require.NoError(t, err) - - msgInv := wire.NewInvVect(wire.InvTypeTx, hash) - require.NoError(t, err) - - invMsgs[i] = msgInv - - expectedMsgs[i] = &metamorph.TxStatusMessage{ - Hash: hash, - Status: metamorph_api.Status_REQUESTED_BY_NETWORK, - Peer: "mock_peer", - } - } - - // when - go func() { - _, _ = peerHandler.HandleTransactionsGet(invMsgs, peer) - }() - - // then - counter := 0 - for i := 0; i < txsCount; i++ { - select { - case msg := <-messageCh: - assertEqualMsg(t, expectedMsgs[counter], msg) - counter++ - case <-time.After(5 * time.Second): - t.Fatal("test timed out or error while executing goroutine") - } - } - }) - - t.Run("HandleTransaction", func(t *testing.T) { - // given - msgTx := wire.NewMsgTx(70001) - hash := msgTx.TxHash() - - expectedMsg := &metamorph.TxStatusMessage{ - Hash: &hash, - Status: metamorph_api.Status_SEEN_ON_NETWORK, - Peer: "mock_peer", - } - - // when - go func() { - _ = peerHandler.HandleTransaction(msgTx, peer) - }() - - // then - select { - case msg := <-messageCh: - assertEqualMsg(t, expectedMsg, msg) - case <-time.After(time.Second): - t.Fatal("test timed out or error while executing goroutine") - } - }) -} - -func assertEqualMsg(t *testing.T, expected, actual *metamorph.TxStatusMessage) { - assert.Equal(t, expected.Hash, actual.Hash) - assert.Equal(t, expected.Status, actual.Status) - assert.Equal(t, expected.Peer, actual.Peer) - assert.WithinDuration(t, time.Now(), actual.Start, time.Second) - assert.Equal(t, expected.Err, actual.Err) -} diff --git a/internal/metamorph/peer_manager.go b/internal/metamorph/peer_manager.go deleted file mode 100644 index c626b35ec..000000000 --- a/internal/metamorph/peer_manager.go +++ /dev/null @@ -1,16 +0,0 @@ -package metamorph - -import ( - "github.com/libsv/go-p2p" - "github.com/libsv/go-p2p/chaincfg/chainhash" -) - -type PeerManager interface { - AnnounceTransaction(txHash *chainhash.Hash, peers []p2p.PeerI) []p2p.PeerI - RequestTransaction(txHash *chainhash.Hash) p2p.PeerI - AnnounceBlock(blockHash *chainhash.Hash, peers []p2p.PeerI) []p2p.PeerI - RequestBlock(blockHash *chainhash.Hash) p2p.PeerI - AddPeer(peer p2p.PeerI) error - GetPeers() []p2p.PeerI - Shutdown() -} diff --git a/internal/metamorph/processor.go b/internal/metamorph/processor.go index dea8596b5..8a0a2c017 100644 --- a/internal/metamorph/processor.go +++ b/internal/metamorph/processor.go @@ -9,15 +9,17 @@ import ( "sync" "time" - "github.com/libsv/go-p2p" "github.com/libsv/go-p2p/chaincfg/chainhash" + "github.com/libsv/go-p2p/wire" "go.opentelemetry.io/otel/attribute" "google.golang.org/protobuf/proto" "github.com/bitcoin-sv/arc/internal/blocktx/blocktx_api" "github.com/bitcoin-sv/arc/internal/cache" + metamorph_p2p "github.com/bitcoin-sv/arc/internal/metamorph/bcnet/metamorph_p2p" "github.com/bitcoin-sv/arc/internal/metamorph/metamorph_api" "github.com/bitcoin-sv/arc/internal/metamorph/store" + "github.com/bitcoin-sv/arc/internal/p2p" "github.com/bitcoin-sv/arc/internal/tracing" ) @@ -52,7 +54,7 @@ const ( var ( ErrStoreNil = errors.New("store cannot be nil") - ErrPeerManagerNil = errors.New("peer manager cannot be nil") + ErrPeerMessengerNil = errors.New("p2p messenger cannot be nil") ErrFailedToUnmarshalMessage = errors.New("failed to unmarshal message") ErrFailedToSubscribe = errors.New("failed to subscribe to topic") ErrFailedToStartCollectingStats = errors.New("failed to start collecting stats") @@ -63,7 +65,7 @@ type Processor struct { store store.MetamorphStore cacheStore cache.Store hostname string - pm p2p.PeerManagerI + messenger *p2p.NetworkMessenger mqClient MessageQueue logger *slog.Logger mapExpiryTime time.Duration @@ -76,7 +78,7 @@ type Processor struct { callbackSender CallbackSender responseProcessor *ResponseProcessor - statusMessageCh chan *TxStatusMessage + statusMessageCh chan *metamorph_p2p.TxStatusMessage waitGroup *sync.WaitGroup @@ -113,13 +115,13 @@ type CallbackSender interface { SendCallback(ctx context.Context, data *store.Data) } -func NewProcessor(s store.MetamorphStore, c cache.Store, pm p2p.PeerManagerI, statusMessageChannel chan *TxStatusMessage, opts ...Option) (*Processor, error) { +func NewProcessor(s store.MetamorphStore, c cache.Store, messenger *p2p.NetworkMessenger, statusMessageChannel chan *metamorph_p2p.TxStatusMessage, opts ...Option) (*Processor, error) { if s == nil { return nil, ErrStoreNil } - if pm == nil { - return nil, ErrPeerManagerNil + if messenger == nil { + return nil, ErrPeerMessengerNil } hostname, err := os.Hostname() @@ -131,7 +133,7 @@ func NewProcessor(s store.MetamorphStore, c cache.Store, pm p2p.PeerManagerI, st store: s, cacheStore: c, hostname: hostname, - pm: pm, + messenger: messenger, mapExpiryTime: mapExpiryTimeDefault, recheckSeenFromAgo: recheckSeenFromAgo, recheckSeenUntilAgo: recheckSeenUntilAgoDefault, @@ -233,8 +235,6 @@ func (p *Processor) Start(statsEnabled bool) error { func (p *Processor) Shutdown() { p.logger.Info("Shutting down processor") - p.pm.Shutdown() - err := p.unlockRecords() if err != nil { p.logger.Error("Failed to unlock all hashes", slog.String("err", err.Error())) @@ -329,6 +329,7 @@ func (p *Processor) updateMined(ctx context.Context, txsBlocks []*blocktx_api.Tr } } +// StartProcessSubmittedTxs starts processing txs submitted to message queue func (p *Processor) StartProcessSubmittedTxs() { p.waitGroup.Add(1) ticker := time.NewTicker(p.processTransactionsInterval) @@ -648,17 +649,21 @@ func (p *Processor) StartRequestingSeenOnNetworkTxs() { }() } +// StartProcessExpiredTransactions periodically reads transactions with status lower then SEEN_ON_NETWORK from database and announce them again func (p *Processor) StartProcessExpiredTransactions() { - ticker := time.NewTicker(p.processExpiredTxsInterval) p.waitGroup.Add(1) go func() { defer p.waitGroup.Done() + + ticker := time.NewTicker(p.processExpiredTxsInterval) + defer ticker.Stop() + for { select { case <-p.ctx.Done(): return - case <-ticker.C: // Periodically read unmined transactions from database and announce them again + case <-ticker.C: ctx, span := tracing.StartTracing(p.ctx, "StartProcessExpiredTransactions", p.tracingEnabled, p.tracingAttributes...) // define from what point in time we are interested in unmined transactions @@ -701,17 +706,13 @@ func (p *Processor) StartProcessExpiredTransactions() { if tx.Retries%2 != 0 { // Send GETDATA to peers to see if they have it p.logger.Debug("Re-getting expired tx", slog.String("hash", tx.Hash.String())) - p.pm.RequestTransaction(tx.Hash) + p.messenger.RequestWithAutoBatch(tx.Hash, wire.InvTypeTx) requested++ continue } p.logger.Debug("Re-announcing expired tx", slog.String("hash", tx.Hash.String())) - peers := p.pm.AnnounceTransaction(tx.Hash, nil) - if len(peers) == 0 { - p.logger.Warn("transaction was not announced to any peer during rebroadcast", slog.String("hash", tx.Hash.String())) - continue - } + p.messenger.AnnounceWithAutoBatch(tx.Hash, wire.InvTypeTx) announced++ } } @@ -726,9 +727,9 @@ func (p *Processor) StartProcessExpiredTransactions() { }() } -// GetPeers returns a list of connected and a list of disconnected peers +// GetPeers returns a list of connected and disconnected peers func (p *Processor) GetPeers() []p2p.PeerI { - return p.pm.GetPeers() + return p.messenger.GetPeers() } func (p *Processor) ProcessTransaction(ctx context.Context, req *ProcessorRequest) { @@ -813,19 +814,14 @@ func (p *Processor) ProcessTransaction(ctx context.Context, req *ProcessorReques } // Send GETDATA to peers to see if they have it - ctx, requestTransactionSpan := tracing.StartTracing(ctx, "RequestTransaction", p.tracingEnabled, p.tracingAttributes...) - p.pm.RequestTransaction(req.Data.Hash) + _, requestTransactionSpan := tracing.StartTracing(ctx, "RequestWithAutoBatch", p.tracingEnabled, p.tracingAttributes...) + p.messenger.RequestWithAutoBatch(req.Data.Hash, wire.InvTypeTx) tracing.EndTracing(requestTransactionSpan, nil) // Announce transaction to network peers - p.logger.Debug("announcing transaction", slog.String("hash", req.Data.Hash.String())) - _, announceTransactionSpan := tracing.StartTracing(ctx, "AnnounceTransaction", p.tracingEnabled, p.tracingAttributes...) - peers := p.pm.AnnounceTransaction(req.Data.Hash, nil) + _, announceTransactionSpan := tracing.StartTracing(ctx, "AnnounceWithAutoBatch", p.tracingEnabled, p.tracingAttributes...) + p.messenger.AnnounceWithAutoBatch(req.Data.Hash, wire.InvTypeTx) tracing.EndTracing(announceTransactionSpan, nil) - if len(peers) == 0 { - p.logger.Warn("transaction was not announced to any peer", slog.String("hash", req.Data.Hash.String())) - return - } // update status in response statusResponse.UpdateStatus(StatusAndError{ @@ -840,6 +836,7 @@ func (p *Processor) ProcessTransaction(ctx context.Context, req *ProcessorReques } } +// ProcessTransactions processes txs submitted to message queue func (p *Processor) ProcessTransactions(ctx context.Context, sReq []*store.Data) { var err error ctx, span := tracing.StartTracing(ctx, "ProcessTransactions", p.tracingEnabled, p.tracingAttributes...) @@ -867,11 +864,7 @@ func (p *Processor) ProcessTransactions(ctx context.Context, sReq []*store.Data) } // Announce transaction to network and save peers - peers := p.pm.AnnounceTransaction(data.Hash, nil) - if len(peers) == 0 { - p.logger.Warn("transaction was not announced to any peer", slog.String("hash", data.Hash.String())) - continue - } + p.messenger.AnnounceWithAutoBatch(data.Hash, wire.InvTypeTx) // update status in storage p.storageStatusUpdateCh <- store.UpdateStatus{ @@ -883,13 +876,7 @@ func (p *Processor) ProcessTransactions(ctx context.Context, sReq []*store.Data) } func (p *Processor) Health() error { - healthyConnections := 0 - - for _, peer := range p.pm.GetPeers() { - if peer.Connected() && peer.IsHealthy() { - healthyConnections++ - } - } + healthyConnections := int(p.messenger.CountConnectedPeers()) // #nosec G115 if healthyConnections < p.minimumHealthyConnections { p.logger.Warn("Less than expected healthy peers", slog.Int("connections", healthyConnections)) diff --git a/internal/metamorph/processor_test.go b/internal/metamorph/processor_test.go index 925ec6844..e863c285f 100644 --- a/internal/metamorph/processor_test.go +++ b/internal/metamorph/processor_test.go @@ -6,13 +6,14 @@ import ( "database/sql" "errors" "log/slog" - "os" "sync" + "sync/atomic" "testing" "time" - "github.com/libsv/go-p2p" "github.com/libsv/go-p2p/chaincfg/chainhash" + + "github.com/libsv/go-p2p/wire" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" @@ -20,10 +21,13 @@ import ( "github.com/bitcoin-sv/arc/internal/blocktx/blocktx_api" "github.com/bitcoin-sv/arc/internal/cache" "github.com/bitcoin-sv/arc/internal/metamorph" + "github.com/bitcoin-sv/arc/internal/metamorph/bcnet/metamorph_p2p" "github.com/bitcoin-sv/arc/internal/metamorph/metamorph_api" "github.com/bitcoin-sv/arc/internal/metamorph/mocks" "github.com/bitcoin-sv/arc/internal/metamorph/store" storeMocks "github.com/bitcoin-sv/arc/internal/metamorph/store/mocks" + "github.com/bitcoin-sv/arc/internal/p2p" + p2pMocks "github.com/bitcoin-sv/arc/internal/p2p/mocks" "github.com/bitcoin-sv/arc/internal/testdata" ) @@ -35,13 +39,13 @@ func TestNewProcessor(t *testing.T) { SetUnlockedByNameFunc: func(_ context.Context, _ string) (int64, error) { return 0, nil }, } - pm := &mocks.PeerManagerMock{ShutdownFunc: func() {}} + nMessenger := &p2p.NetworkMessenger{} cStore := cache.NewMemoryStore() tt := []struct { - name string - store store.MetamorphStore - pm p2p.PeerManagerI + name string + store store.MetamorphStore + messenger *p2p.NetworkMessenger expectedError error expectedNonNilProcessor bool @@ -49,31 +53,31 @@ func TestNewProcessor(t *testing.T) { { name: "success", store: mtmStore, - pm: pm, + messenger: nMessenger, expectedNonNilProcessor: true, }, { - name: "no store", - store: nil, - pm: pm, + name: "no store", + store: nil, + messenger: nMessenger, expectedError: metamorph.ErrStoreNil, }, { - name: "no pm", - store: mtmStore, - pm: nil, + name: "no pm", + store: mtmStore, + messenger: nil, - expectedError: metamorph.ErrPeerManagerNil, + expectedError: metamorph.ErrPeerMessengerNil, }, } for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { // when - sut, actualErr := metamorph.NewProcessor(tc.store, cStore, tc.pm, nil, + sut, actualErr := metamorph.NewProcessor(tc.store, cStore, tc.messenger, nil, metamorph.WithCacheExpiryTime(time.Second*5), - metamorph.WithProcessorLogger(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: metamorph.LogLevelDefault}))), + metamorph.WithProcessorLogger(slog.Default()), ) // then @@ -97,18 +101,18 @@ func TestStartLockTransactions(t *testing.T) { name string setLockedErr error - expectedSetLockedCalls int + expectedSetLockedCallsGreaterThan int }{ { name: "no error", - expectedSetLockedCalls: 2, + expectedSetLockedCallsGreaterThan: 2, }, { name: "error", setLockedErr: errors.New("failed to set locked"), - expectedSetLockedCalls: 2, + expectedSetLockedCallsGreaterThan: 2, }, } @@ -123,19 +127,19 @@ func TestStartLockTransactions(t *testing.T) { SetUnlockedByNameFunc: func(_ context.Context, _ string) (int64, error) { return 0, nil }, } - pm := &mocks.PeerManagerMock{ShutdownFunc: func() {}} - + messenger := &p2p.NetworkMessenger{} cStore := cache.NewMemoryStore() // when - sut, err := metamorph.NewProcessor(metamorphStore, cStore, pm, nil, metamorph.WithLockTxsInterval(20*time.Millisecond)) + sut, err := metamorph.NewProcessor(metamorphStore, cStore, messenger, nil, metamorph.WithLockTxsInterval(20*time.Millisecond)) require.NoError(t, err) defer sut.Shutdown() + sut.StartLockTransactions() - time.Sleep(50 * time.Millisecond) + time.Sleep(60 * time.Millisecond) // then - require.Equal(t, tc.expectedSetLockedCalls, len(metamorphStore.SetLockedCalls())) + require.GreaterOrEqual(t, len(metamorphStore.SetLockedCalls()), tc.expectedSetLockedCallsGreaterThan) }) } } @@ -146,24 +150,23 @@ func TestProcessTransaction(t *testing.T) { storeData *store.Data storeDataGetErr error - expectedResponseMapItems int - expectedResponses []metamorph_api.Status - expectedSetCalls int - expectedAnnounceCalls int - expectedRequestCalls int + expectedResponses []metamorph_api.Status + expectedSetCalls int + expectedAnnounceCalls int + expectedRequestCalls int }{ { - name: "record not found", + name: "record not found - success", storeData: nil, storeDataGetErr: store.ErrNotFound, expectedResponses: []metamorph_api.Status{ metamorph_api.Status_STORED, + metamorph_api.Status_ANNOUNCED_TO_NETWORK, }, - expectedResponseMapItems: 1, - expectedSetCalls: 1, - expectedAnnounceCalls: 1, - expectedRequestCalls: 1, + expectedSetCalls: 1, + expectedAnnounceCalls: 1, + expectedRequestCalls: 1, }, { name: "record found", @@ -177,10 +180,9 @@ func TestProcessTransaction(t *testing.T) { expectedResponses: []metamorph_api.Status{ metamorph_api.Status_REJECTED, }, - expectedResponseMapItems: 0, - expectedSetCalls: 1, - expectedAnnounceCalls: 0, - expectedRequestCalls: 0, + expectedSetCalls: 1, + expectedAnnounceCalls: 0, + expectedRequestCalls: 0, }, { name: "store unavailable", @@ -190,10 +192,9 @@ func TestProcessTransaction(t *testing.T) { expectedResponses: []metamorph_api.Status{ metamorph_api.Status_RECEIVED, }, - expectedResponseMapItems: 0, - expectedSetCalls: 0, - expectedAnnounceCalls: 0, - expectedRequestCalls: 0, + expectedSetCalls: 0, + expectedAnnounceCalls: 0, + expectedRequestCalls: 0, }, } @@ -228,23 +229,37 @@ func TestProcessTransaction(t *testing.T) { }, } - pm := &mocks.PeerManagerMock{ - AnnounceTransactionFunc: func(txHash *chainhash.Hash, _ []p2p.PeerI) []p2p.PeerI { - require.True(t, testdata.TX1Hash.IsEqual(txHash)) - return nil + announceMsgCounter := &atomic.Int32{} + requestMsgCounter := &atomic.Int32{} + + peer := &p2pMocks.PeerIMock{ + WriteMsgFunc: func(msg wire.Message) { + if msg.Command() == wire.CmdInv { + announceMsgCounter.Add(1) + } else if msg.Command() == wire.CmdGetData { + requestMsgCounter.Add(1) + } }, - RequestTransactionFunc: func(_ *chainhash.Hash) p2p.PeerI { return nil }, + NetworkFunc: func() wire.BitcoinNet { return wire.TestNet }, + StringFunc: func() string { return "peer" }, + ConnectedFunc: func() bool { return true }, } cStore := cache.NewMemoryStore() + pm := p2p.NewPeerManager(slog.Default(), wire.TestNet) + err := pm.AddPeer(peer) + require.NoError(t, err) + + messenger := p2p.NewNetworkMessenger(slog.Default(), pm) + publisher := &mocks.MessageQueueMock{ PublishFunc: func(_ context.Context, _ string, _ []byte) error { return nil }, } - sut, err := metamorph.NewProcessor(s, cStore, pm, nil, metamorph.WithMessageQueueClient(publisher)) + sut, err := metamorph.NewProcessor(s, cStore, messenger, nil, metamorph.WithMessageQueueClient(publisher)) require.NoError(t, err) require.Equal(t, 0, sut.GetProcessorMapSize()) @@ -272,16 +287,11 @@ func TestProcessTransaction(t *testing.T) { ResponseChannel: responseChannel, }) wg.Wait() - + time.Sleep(250 * time.Millisecond) // then - require.Equal(t, tc.expectedResponseMapItems, sut.GetProcessorMapSize()) - if tc.expectedResponseMapItems > 0 { - require.Len(t, pm.AnnounceTransactionCalls(), 1) - } - require.Equal(t, tc.expectedSetCalls, len(s.SetCalls())) - require.Equal(t, tc.expectedAnnounceCalls, len(pm.AnnounceTransactionCalls())) - require.Equal(t, tc.expectedRequestCalls, len(pm.RequestTransactionCalls())) + require.Equal(t, tc.expectedAnnounceCalls, int(announceMsgCounter.Load())) + require.Equal(t, tc.expectedRequestCalls, int(requestMsgCounter.Load())) }) } } @@ -521,8 +531,7 @@ func TestStartSendStatusForTransaction(t *testing.T) { }, } - pm := &mocks.PeerManagerMock{ShutdownFunc: func() {}} - + messenger := &p2p.NetworkMessenger{} cStore := cache.NewMemoryStore() for _, i := range tc.inputs { if i.registered { @@ -531,7 +540,7 @@ func TestStartSendStatusForTransaction(t *testing.T) { } } - statusMessageChannel := make(chan *metamorph.TxStatusMessage, 10) + statusMessageChannel := make(chan *metamorph_p2p.TxStatusMessage, 10) mqClient := &mocks.MessageQueueMock{ PublishMarshalFunc: func(_ context.Context, _ string, _ protoreflect.ProtoMessage) error { @@ -542,7 +551,7 @@ func TestStartSendStatusForTransaction(t *testing.T) { sut, err := metamorph.NewProcessor( metamorphStore, cStore, - pm, + messenger, statusMessageChannel, metamorph.WithNow(func() time.Time { return time.Date(2023, 10, 1, 13, 0, 0, 0, time.UTC) }), metamorph.WithProcessStatusUpdatesInterval(200*time.Millisecond), @@ -557,7 +566,7 @@ func TestStartSendStatusForTransaction(t *testing.T) { require.Equal(t, 0, sut.GetProcessorMapSize()) for _, testInput := range tc.inputs { - statusMessageChannel <- &metamorph.TxStatusMessage{ + statusMessageChannel <- &metamorph_p2p.TxStatusMessage{ Hash: testInput.hash, Status: testInput.newStatus, Err: testInput.statusErr, @@ -581,8 +590,9 @@ func TestStartProcessSubmittedTxs(t *testing.T) { name string txReqs []*metamorph_api.TransactionRequest - expectedSetBulkCalls int - expectedAnnouncedTxCalls int + expectedSetBulkCalls int + expectedInvMessages int + expcetedInvInMessage int }{ { name: "2 submitted txs", @@ -603,8 +613,9 @@ func TestStartProcessSubmittedTxs(t *testing.T) { }, }, - expectedSetBulkCalls: 1, - expectedAnnouncedTxCalls: 2, + expectedSetBulkCalls: 1, + expectedInvMessages: 1, + expcetedInvInMessage: 2, }, { name: "5 submitted txs", @@ -646,8 +657,9 @@ func TestStartProcessSubmittedTxs(t *testing.T) { }, }, - expectedSetBulkCalls: 2, - expectedAnnouncedTxCalls: 5, + expectedSetBulkCalls: 2, + expectedInvMessages: 1, + expcetedInvInMessage: 5, }, } @@ -677,23 +689,31 @@ func TestStartProcessSubmittedTxs(t *testing.T) { }, SetUnlockedByNameFunc: func(_ context.Context, _ string) (int64, error) { return 0, nil }, } - counter := 0 - pm := &mocks.PeerManagerMock{ - AnnounceTransactionFunc: func(txHash *chainhash.Hash, _ []p2p.PeerI) []p2p.PeerI { - switch counter { - case 0: - require.True(t, testdata.TX1Hash.IsEqual(txHash)) - default: - require.True(t, testdata.TX6Hash.IsEqual(txHash)) + + announceMsgCounter := &atomic.Int32{} + peer := &p2pMocks.PeerIMock{ + WriteMsgFunc: func(msg wire.Message) { + if msg.Command() == wire.CmdInv { + announceMsgCounter.Add(1) + + invMsg, ok := msg.(*wire.MsgInv) + require.True(t, ok) + require.Equal(t, tc.expcetedInvInMessage, len(invMsg.InvList)) } - counter++ - return []p2p.PeerI{&mocks.PeerIMock{}} }, - ShutdownFunc: func() {}, + NetworkFunc: func() wire.BitcoinNet { return wire.TestNet }, + StringFunc: func() string { return "peer" }, + ConnectedFunc: func() bool { return true }, } cStore := cache.NewMemoryStore() + pm := p2p.NewPeerManager(slog.Default(), wire.TestNet) + err := pm.AddPeer(peer) + require.NoError(t, err) + + messenger := p2p.NewNetworkMessenger(slog.Default(), pm) + publisher := &mocks.MessageQueueMock{ PublishFunc: func(_ context.Context, _ string, _ []byte) error { return nil @@ -701,7 +721,7 @@ func TestStartProcessSubmittedTxs(t *testing.T) { } const submittedTxsBuffer = 5 submittedTxsChan := make(chan *metamorph_api.TransactionRequest, submittedTxsBuffer) - sut, err := metamorph.NewProcessor(s, cStore, pm, nil, + sut, err := metamorph.NewProcessor(s, cStore, messenger, nil, metamorph.WithMessageQueueClient(publisher), metamorph.WithSubmittedTxsChan(submittedTxsChan), metamorph.WithProcessStatusUpdatesInterval(20*time.Millisecond), @@ -732,10 +752,10 @@ func TestStartProcessSubmittedTxs(t *testing.T) { t.Fatal("submitted txs have not been stored within 2s") case <-c: } - + time.Sleep(250 * time.Millisecond) // then require.Equal(t, tc.expectedSetBulkCalls, len(s.SetBulkCalls())) - require.Equal(t, tc.expectedAnnouncedTxCalls, len(pm.AnnounceTransactionCalls())) + require.Equal(t, tc.expectedInvMessages, int(announceMsgCounter.Load())) }) } } @@ -746,15 +766,15 @@ func TestProcessExpiredTransactions(t *testing.T) { retries int getUnminedErr error - expectedRequests int - expectedAnnouncements int + expectedRequests int32 + expectedAnnouncements int32 }{ { name: "expired txs", retries: 4, - expectedAnnouncements: 2, - expectedRequests: 2, + expectedAnnouncements: 1, + expectedRequests: 1, }, { name: "expired txs - max retries exceeded", @@ -792,7 +812,7 @@ func TestProcessExpiredTransactions(t *testing.T) { StoredAt: time.Now(), Hash: testdata.TX4Hash, Status: metamorph_api.Status_ANNOUNCED_TO_NETWORK, - Retries: retries, + Retries: retries + 1, }, { StoredAt: time.Now(), @@ -802,34 +822,44 @@ func TestProcessExpiredTransactions(t *testing.T) { }, } - retries++ - return unminedData, tc.getUnminedErr }, IncrementRetriesFunc: func(_ context.Context, _ *chainhash.Hash) error { + retries++ return nil }, } - pm := &mocks.PeerManagerMock{ - RequestTransactionFunc: func(_ *chainhash.Hash) p2p.PeerI { - return nil - }, - AnnounceTransactionFunc: func(_ *chainhash.Hash, _ []p2p.PeerI) []p2p.PeerI { - return nil + var announceMsgCounter atomic.Int32 + var requestMsgCounter atomic.Int32 + peer := &p2pMocks.PeerIMock{ + WriteMsgFunc: func(msg wire.Message) { + if msg.Command() == wire.CmdInv { + announceMsgCounter.Add(1) + } else if msg.Command() == wire.CmdGetData { + requestMsgCounter.Add(1) + } }, - ShutdownFunc: func() {}, + NetworkFunc: func() wire.BitcoinNet { return wire.TestNet }, + StringFunc: func() string { return "peer" }, + ConnectedFunc: func() bool { return true }, } cStore := cache.NewMemoryStore() + pm := p2p.NewPeerManager(slog.Default(), wire.TestNet) + err := pm.AddPeer(peer) + require.NoError(t, err) + + messenger := p2p.NewNetworkMessenger(slog.Default(), pm) + publisher := &mocks.MessageQueueMock{ PublishFunc: func(_ context.Context, _ string, _ []byte) error { return nil }, } - sut, err := metamorph.NewProcessor(metamorphStore, cStore, pm, nil, + sut, err := metamorph.NewProcessor(metamorphStore, cStore, messenger, nil, metamorph.WithMessageQueueClient(publisher), metamorph.WithProcessExpiredTxsInterval(time.Millisecond*20), metamorph.WithMaxRetries(10), @@ -844,11 +874,11 @@ func TestProcessExpiredTransactions(t *testing.T) { require.Equal(t, 0, sut.GetProcessorMapSize()) - time.Sleep(50 * time.Millisecond) + time.Sleep(250 * time.Millisecond) // then - require.Equal(t, tc.expectedAnnouncements, len(pm.AnnounceTransactionCalls())) - require.Equal(t, tc.expectedRequests, len(pm.RequestTransactionCalls())) + require.Equal(t, tc.expectedAnnouncements, announceMsgCounter.Load()) + require.Equal(t, tc.expectedRequests, requestMsgCounter.Load()) }) } } @@ -905,7 +935,7 @@ func TestStartProcessMinedCallbacks(t *testing.T) { }, SetUnlockedByNameFunc: func(_ context.Context, _ string) (int64, error) { return 0, nil }, } - pm := &mocks.PeerManagerMock{ShutdownFunc: func() {}} + pm := &p2p.NetworkMessenger{} minedTxsChan := make(chan *blocktx_api.TransactionBlock, 5) mqClient := &mocks.MessageQueueMock{ @@ -991,23 +1021,20 @@ func TestProcessorHealth(t *testing.T) { }, } - pm := &mocks.PeerManagerMock{ - AddPeerFunc: func(_ p2p.PeerI) error { - return nil - }, - GetPeersFunc: func() []p2p.PeerI { - peers := make([]p2p.PeerI, tc.peersAdded) - for i := 0; i < tc.peersAdded; i++ { - peers[i] = &p2p.PeerMock{} - } - - return peers - }, - ShutdownFunc: func() {}, + pm := p2p.NewPeerManager(slog.Default(), wire.TestNet) + for range tc.peersAdded { + peer := p2pMocks.PeerIMock{ + NetworkFunc: func() wire.BitcoinNet { return wire.TestNet }, + ConnectedFunc: func() bool { return true }, + StringFunc: func() string { return "peer" }, + } + require.NoError(t, pm.AddPeer(&peer)) } cStore := cache.NewMemoryStore() - sut, err := metamorph.NewProcessor(metamorphStore, cStore, pm, nil, + messenger := p2p.NewNetworkMessenger(slog.Default(), pm) + + sut, err := metamorph.NewProcessor(metamorphStore, cStore, messenger, nil, metamorph.WithProcessExpiredTxsInterval(time.Millisecond*20), metamorph.WithNow(func() time.Time { return time.Date(2033, 1, 1, 1, 0, 0, 0, time.UTC) @@ -1059,7 +1086,7 @@ func TestStart(t *testing.T) { return 0, nil }} - pm := &mocks.PeerManagerMock{ShutdownFunc: func() {}} + pm := &p2p.NetworkMessenger{} cStore := cache.NewMemoryStore() diff --git a/internal/metamorph/server.go b/internal/metamorph/server.go index dc765120e..0d5604942 100644 --- a/internal/metamorph/server.go +++ b/internal/metamorph/server.go @@ -11,7 +11,7 @@ import ( "time" "github.com/bitcoin-sv/go-sdk/util" - "github.com/libsv/go-p2p" + "github.com/libsv/go-p2p/chaincfg/chainhash" "github.com/ordishs/go-bitcoin" "go.opentelemetry.io/otel/attribute" @@ -24,6 +24,7 @@ import ( "github.com/bitcoin-sv/arc/internal/grpc_opts" "github.com/bitcoin-sv/arc/internal/metamorph/metamorph_api" "github.com/bitcoin-sv/arc/internal/metamorph/store" + "github.com/bitcoin-sv/arc/internal/p2p" "github.com/bitcoin-sv/arc/internal/tracing" ) @@ -256,7 +257,12 @@ func (s *Server) processTransaction(ctx context.Context, waitForStatus metamorph defer func() { if span != nil { - span.SetAttributes(attribute.String("finalStatus", returnedStatus.Status.String()), attribute.String("txID", returnedStatus.Txid), attribute.Bool("timeout", returnedStatus.TimedOut), attribute.String("waitFor", waitForStatus.String())) + span.SetAttributes(attribute.String("finalStatus", + returnedStatus.Status.String()), + attribute.String("txID", returnedStatus.Txid), + attribute.Bool("timeout", returnedStatus.TimedOut), + attribute.String("waitFor", waitForStatus.String()), + ) } tracing.EndTracing(span, err) }() @@ -288,6 +294,7 @@ func (s *Server) processTransaction(ctx context.Context, waitForStatus metamorph } checkStatusTicker := time.NewTicker(s.checkStatusInterval) + defer checkStatusTicker.Stop() for { select { diff --git a/internal/metamorph/server_test.go b/internal/metamorph/server_test.go index 266e684fd..a487ce368 100644 --- a/internal/metamorph/server_test.go +++ b/internal/metamorph/server_test.go @@ -10,7 +10,7 @@ import ( "time" sdkTx "github.com/bitcoin-sv/go-sdk/transaction" - "github.com/libsv/go-p2p" + "github.com/libsv/go-p2p/chaincfg/chainhash" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -22,6 +22,7 @@ import ( "github.com/bitcoin-sv/arc/internal/metamorph/mocks" "github.com/bitcoin-sv/arc/internal/metamorph/store" storeMocks "github.com/bitcoin-sv/arc/internal/metamorph/store/mocks" + "github.com/bitcoin-sv/arc/internal/p2p" "github.com/bitcoin-sv/arc/internal/testdata" ) @@ -39,9 +40,7 @@ func TestHealth(t *testing.T) { // given processor := &mocks.ProcessorIMock{} processor.GetProcessorMapSizeFunc = func() int { return 22 } - processor.GetPeersFunc = func() []p2p.PeerI { - return []p2p.PeerI{} - } + processor.GetPeersFunc = func() []p2p.PeerI { return []p2p.PeerI{} } sut, err := metamorph.NewServer("", 0, slog.Default(), nil, processor, nil) require.NoError(t, err) diff --git a/internal/metamorph/stats_collector_test.go b/internal/metamorph/stats_collector_test.go index 2dd35de12..bc09069a5 100644 --- a/internal/metamorph/stats_collector_test.go +++ b/internal/metamorph/stats_collector_test.go @@ -8,8 +8,9 @@ import ( "testing" "time" + "github.com/bitcoin-sv/arc/internal/p2p" + "github.com/bitcoin-sv/arc/internal/metamorph" - "github.com/bitcoin-sv/arc/internal/metamorph/mocks" "github.com/bitcoin-sv/arc/internal/metamorph/store" storeMocks "github.com/bitcoin-sv/arc/internal/metamorph/store/mocks" "github.com/stretchr/testify/require" @@ -49,9 +50,9 @@ func TestStartCollectStats(t *testing.T) { SetUnlockedByNameFunc: func(_ context.Context, _ string) (int64, error) { return 0, nil }, } - pm := &mocks.PeerManagerMock{ShutdownFunc: func() {}} + messenger := &p2p.NetworkMessenger{} - processor, err := metamorph.NewProcessor(mtmStore, nil, pm, nil, + processor, err := metamorph.NewProcessor(mtmStore, nil, messenger, nil, metamorph.WithProcessorLogger(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: metamorph.LogLevelDefault}))), metamorph.WithStatCollectionInterval(10*time.Millisecond), ) diff --git a/internal/metamorph/types.go b/internal/metamorph/types.go index d8991cfb8..fda67c496 100644 --- a/internal/metamorph/types.go +++ b/internal/metamorph/types.go @@ -1,8 +1,6 @@ package metamorph import ( - "time" - "github.com/bitcoin-sv/arc/internal/metamorph/metamorph_api" "github.com/bitcoin-sv/arc/internal/metamorph/store" "github.com/libsv/go-p2p/chaincfg/chainhash" @@ -19,12 +17,3 @@ type StatusAndError struct { Err error CompetingTxs []string } - -type TxStatusMessage struct { - Start time.Time - Hash *chainhash.Hash - Status metamorph_api.Status - Peer string - Err error - CompetingTxs []string -} diff --git a/internal/metamorph/zmq.go b/internal/metamorph/zmq.go index a443ad5a7..715f919fd 100644 --- a/internal/metamorph/zmq.go +++ b/internal/metamorph/zmq.go @@ -11,10 +11,11 @@ import ( "strings" "time" + "github.com/bitcoin-sv/arc/internal/metamorph/bcnet/metamorph_p2p" + "github.com/bitcoin-sv/arc/internal/metamorph/metamorph_api" "github.com/libsv/go-p2p/chaincfg/chainhash" "github.com/bitcoin-sv/arc/internal/logger" - "github.com/bitcoin-sv/arc/internal/metamorph/metamorph_api" ) var allowedTopics = []string{ @@ -40,7 +41,7 @@ type subscriptionRequest struct { type ZMQ struct { url *url.URL - statusMessageCh chan<- *TxStatusMessage + statusMessageCh chan<- *metamorph_p2p.TxStatusMessage handler ZMQI logger *slog.Logger } @@ -85,7 +86,7 @@ type ZMQI interface { Subscribe(string, chan []string) error } -func NewZMQ(zmqURL *url.URL, statusMessageCh chan<- *TxStatusMessage, zmqHandler ZMQI, logger *slog.Logger) (*ZMQ, error) { +func NewZMQ(zmqURL *url.URL, statusMessageCh chan<- *metamorph_p2p.TxStatusMessage, zmqHandler ZMQI, logger *slog.Logger) (*ZMQ, error) { if zmqHandler == nil { return nil, ErrNilZMQHandler } @@ -121,7 +122,7 @@ func (z *ZMQ) Start() (func(), error) { continue } - z.statusMessageCh <- &TxStatusMessage{ + z.statusMessageCh <- &metamorph_p2p.TxStatusMessage{ Start: time.Now(), Hash: hash, Status: metamorph_api.Status_ACCEPTED_BY_NETWORK, @@ -137,7 +138,7 @@ func (z *ZMQ) Start() (func(), error) { } if len(competingTxs) == 0 { - z.statusMessageCh <- &TxStatusMessage{ + z.statusMessageCh <- &metamorph_p2p.TxStatusMessage{ Start: time.Now(), Hash: hash, Status: status, @@ -160,7 +161,7 @@ func (z *ZMQ) Start() (func(), error) { continue } - z.statusMessageCh <- &TxStatusMessage{ + z.statusMessageCh <- &metamorph_p2p.TxStatusMessage{ Start: time.Now(), Hash: hash, Status: metamorph_api.Status_REJECTED, @@ -248,8 +249,8 @@ func (z *ZMQ) parseTxInfo(c []string) (*ZMQTxInfo, error) { return &txInfo, nil } -func (z *ZMQ) prepareCompetingTxMsgs(hash *chainhash.Hash, competingTxs []string) []*TxStatusMessage { - msgs := []*TxStatusMessage{{ +func (z *ZMQ) prepareCompetingTxMsgs(hash *chainhash.Hash, competingTxs []string) []*metamorph_p2p.TxStatusMessage { + msgs := []*metamorph_p2p.TxStatusMessage{{ Start: time.Now(), Hash: hash, Status: metamorph_api.Status_DOUBLE_SPEND_ATTEMPTED, @@ -274,7 +275,7 @@ func (z *ZMQ) prepareCompetingTxMsgs(hash *chainhash.Hash, competingTxs []string // and return a copy of the slice txsWithoutSelf := removeCompetingSelf(allCompetingTxs, tx) - msgs = append(msgs, &TxStatusMessage{ + msgs = append(msgs, &metamorph_p2p.TxStatusMessage{ Start: time.Now(), Hash: competingHash, Status: metamorph_api.Status_DOUBLE_SPEND_ATTEMPTED, diff --git a/internal/metamorph/zmq_test.go b/internal/metamorph/zmq_test.go index d03243ac1..53cd581bc 100644 --- a/internal/metamorph/zmq_test.go +++ b/internal/metamorph/zmq_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/bitcoin-sv/arc/internal/metamorph" + "github.com/bitcoin-sv/arc/internal/metamorph/bcnet/metamorph_p2p" "github.com/bitcoin-sv/arc/internal/metamorph/metamorph_api" "github.com/bitcoin-sv/arc/internal/metamorph/mocks" "github.com/bitcoin-sv/arc/internal/testdata" @@ -68,7 +69,7 @@ func TestZMQ(t *testing.T) { }, } - statuses := make(chan *metamorph.TxStatusMessage, tc.expectedStatusesCount) + statuses := make(chan *metamorph_p2p.TxStatusMessage, tc.expectedStatusesCount) zmqURL, err := url.Parse("https://some-url.com") require.NoError(t, err) @@ -82,7 +83,7 @@ func TestZMQ(t *testing.T) { defer cleanup() // then - var status *metamorph.TxStatusMessage + var status *metamorph_p2p.TxStatusMessage sCounter := 0 for i := 0; i < tc.expectedStatusesCount; i++ { select { @@ -117,7 +118,7 @@ func TestZMQDoubleSpend(t *testing.T) { numberOfMsgs := 2 hashes := []string{"8e75ae10f86d8a43044a54c3c57d660d20cdb74e233be4b5c90ba752ebdc7e88", "d64adfce6b105dc6bdf475494925bf06802a41a0582586f33c2b16d537a0b7b6"} - statuses := make(chan *metamorph.TxStatusMessage, numberOfMsgs) + statuses := make(chan *metamorph_p2p.TxStatusMessage, numberOfMsgs) zmqURL, err := url.Parse("https://some-url.com") require.NoError(t, err) @@ -131,7 +132,7 @@ func TestZMQDoubleSpend(t *testing.T) { defer cleanup() // then - var status *metamorph.TxStatusMessage + var status *metamorph_p2p.TxStatusMessage sCounter := 0 for i := 0; i < numberOfMsgs; i++ { select { diff --git a/internal/p2p/wire_reader.go b/internal/p2p/wire_reader.go index dc24c7d4c..8413b9d15 100644 --- a/internal/p2p/wire_reader.go +++ b/internal/p2p/wire_reader.go @@ -64,7 +64,7 @@ func handleRead(r *WireReader, pver uint32, bsvnet wire.BitcoinNet, result chan< r.resetLimit() if err != nil { - if strings.Contains(err.Error(), "unhandled command [") { // TODO: change it with new go-p2p version + if strings.Contains(err.Error(), "unhandled command [") { // ignore unknown msg continue } diff --git a/test/config/config.yaml b/test/config/config.yaml index 09b5dfc62..5c97d5321 100644 --- a/test/config/config.yaml +++ b/test/config/config.yaml @@ -1,6 +1,6 @@ --- -logLevel: INFO logFormat: tint +logLevel: INFO profilerAddr: localhost:9999 prometheus: enabled: true