Skip to content

Commit

Permalink
BPASS-1343:addressing review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
kameshraj23 committed Aug 29, 2024
1 parent 7fdf70e commit 83f5690
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 289 deletions.
168 changes: 62 additions & 106 deletions internal/broadcaster/broadcaster_test.go
Original file line number Diff line number Diff line change
@@ -1,129 +1,85 @@
package broadcaster_test

import (
"container/list"
"context"
"fmt"
"math"
"reflect"
"os"
"testing"
"time"

"log/slog"
"os"

"github.com/bitcoin-sv/arc/internal/broadcaster"
"github.com/bitcoin-sv/arc/internal/testdata"
"github.com/bitcoin-sv/arc/internal/broadcaster/mocks"
"github.com/bitcoin-sv/arc/pkg/keyset"
"github.com/libsv/go-bt/v2"
"github.com/libsv/go-bt/v2/bscript"
"github.com/bitcoin-sv/go-sdk/script"
sdkTx "github.com/bitcoin-sv/go-sdk/transaction"
"github.com/stretchr/testify/require"
)

const (
batchSizeDefault = 20
maxInputsDefault = 100
)
func TestBroadcaster(t *testing.T) {

mockedUtxoClient := &mocks.UtxoClientMock{
GetBalanceFunc: func(ctx context.Context, address string) (int64, int64, error) {
return 1000, 0, nil
},
GetBalanceWithRetriesFunc: func(ctx context.Context, address string, constantBackoff time.Duration, retries uint64) (int64, int64, error) {
return 1000, 0, nil
},
GetUTXOsFunc: func(ctx context.Context, lockingScript *script.Script, address string) (sdkTx.UTXOs, error) {
return sdkTx.UTXOs{
{
TxID: []byte("sample-txid-1"),
Vout: 0,
LockingScript: lockingScript,
Satoshis: 1000,
},
{
TxID: []byte("sample-txid-2"),
Vout: 1,
LockingScript: lockingScript,
Satoshis: 1000,
},
}, nil // Mock response for UTXOs retrieval with multiple UTXOs
},
GetUTXOsWithRetriesFunc: func(ctx context.Context, lockingScript *script.Script, address string, constantBackoff time.Duration, retries uint64) (sdkTx.UTXOs, error) {
return sdkTx.UTXOs{
{
TxID: []byte("sample-txid-1"),
Vout: 0,
LockingScript: lockingScript,
Satoshis: 1000,
},
{
TxID: []byte("sample-txid-2"),
Vout: 1,
LockingScript: lockingScript,
Satoshis: 1000,
},
}, nil
},
}

func TestBroadcasterWithPredefinedUTXOs(t *testing.T) {
// Set up a logger
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))

// Create a KeySet
ks1, err := keyset.New()
ks, err := keyset.New()
require.NoError(t, err)

// Define UTXOs
utxo1 := &bt.UTXO{
TxID: testdata.TX1Hash[:],
Vout: 0,
LockingScript: ks1.Script,
Satoshis: 1000,
}
utxo2 := &bt.UTXO{
TxID: testdata.TX2Hash[:],
Vout: 0,
LockingScript: ks1.Script,
Satoshis: 2000,
}
utxo3 := &bt.UTXO{
TxID: testdata.TX3Hash[:],
Vout: 0,
LockingScript: ks1.Script,
Satoshis: 3000,
}

// Mock UTXO Client
utxoClient := &MockUtxoClient{
utxos: []*bt.UTXO{utxo1, utxo2, utxo3},
}

// Initialize the Broadcaster
b, err := broadcaster.NewBroadcaster(logger, nil, utxoClient, false)
rb, err := broadcaster.NewRateBroadcaster(
logger,
nil,
ks,
mockedUtxoClient,
false,
2,
50,
broadcaster.WithBatchSize(2),
)
require.NoError(t, err)

// Use reflection to access the private fields (for testing purposes only)
batchSize := reflect.ValueOf(b).FieldByName("batchSize").Int()
maxInputs := reflect.ValueOf(b).FieldByName("maxInputs").Int()

// Ensure the broadcaster is using the default batch size and max inputs
require.Equal(t, int64(batchSizeDefault), batchSize)
require.Equal(t, int64(maxInputsDefault), maxInputs)

//To do include test for other functions

}

type MockUtxoClient struct {
utxos []*bt.UTXO
}

func (m *MockUtxoClient) GetUTXOs(ctx context.Context, mainnet bool, lockingScript *bscript.Script, address string) ([]*bt.UTXO, error) {
return m.utxos, nil
}

func (m *MockUtxoClient) GetUTXOsWithRetries(ctx context.Context, mainnet bool, lockingScript *bscript.Script, address string, constantBackoff time.Duration, retries uint64) ([]*bt.UTXO, error) {
return m.utxos, nil
}

func (m *MockUtxoClient) GetUTXOsList(ctx context.Context, mainnet bool, lockingScript *bscript.Script, address string) (*list.List, error) {
list := list.New()
for _, utxo := range m.utxos {
list.PushBack(utxo)
}
return list, nil
}

func (m *MockUtxoClient) GetUTXOsListWithRetries(ctx context.Context, mainnet bool, lockingScript *bscript.Script, address string, constantBackoff time.Duration, retries uint64) (*list.List, error) {
list := list.New()
for _, utxo := range m.utxos {
list.PushBack(utxo)
}
return list, nil
}

func (m *MockUtxoClient) GetBalance(ctx context.Context, mainnet bool, address string) (int64, int64, error) {
var balance int64
for _, utxo := range m.utxos {
if utxo.Satoshis > math.MaxInt64 {
return 0, 0, fmt.Errorf("Satoshis value is too large to convert to int64: %d", utxo.Satoshis)
}
balance += int64(utxo.Satoshis)
}
return balance, 0, nil
}

func (m *MockUtxoClient) GetBalanceWithRetries(ctx context.Context, mainnet bool, address string, constantBackoff time.Duration, retries uint64) (int64, int64, error) {
var balance int64
for _, utxo := range m.utxos {
if utxo.Satoshis > math.MaxInt64 {
return 0, 0, fmt.Errorf("Satoshis value is too large to convert to int64: %d", utxo.Satoshis)
}
balance += int64(utxo.Satoshis)
}
return balance, 0, nil
}
err = rb.Start()
require.NoError(t, err)

func (m *MockUtxoClient) TopUp(ctx context.Context, mainnet bool, address string) error {
return nil
require.Equal(t, 0, len(mockedUtxoClient.GetBalanceCalls()))
require.Equal(t, 1, len(mockedUtxoClient.GetBalanceWithRetriesCalls()))
require.Equal(t, 1, len(mockedUtxoClient.GetUTXOsWithRetriesCalls()))
}
157 changes: 29 additions & 128 deletions internal/broadcaster/multi_rate_broadcaster_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package broadcaster_test

import (
"context"
"errors"
"log/slog"
"os"
Expand All @@ -10,30 +9,23 @@ import (

"github.com/bitcoin-sv/arc/internal/broadcaster"
"github.com/bitcoin-sv/arc/internal/broadcaster/mocks"
"github.com/bitcoin-sv/arc/internal/metamorph/metamorph_api"
"github.com/bitcoin-sv/arc/internal/testdata"
"github.com/bitcoin-sv/arc/pkg/keyset"
"github.com/libsv/go-bt/v2"
"github.com/libsv/go-bt/v2/bscript"
"github.com/stretchr/testify/require"
)

func TestMultiRateBroadcasterStart(t *testing.T) {

tt := []struct {
name string
startErr error

expectedErrorStr string
name string
startErr error
expectedError string
}{
{
name: "start and shutdown",
name: "start and shutdown successfully",
},
{
name: "error - failed to start",
startErr: errors.New("failed to start"),

expectedErrorStr: "failed to start",
name: "error - failed to start",
startErr: errors.New("failed to start"),
expectedError: "failed to start",
},
}

Expand All @@ -42,129 +34,38 @@ func TestMultiRateBroadcasterStart(t *testing.T) {

logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))

cs := []broadcaster.RateBroadcaster{
&mocks.RateBroadcasterMock{
StartFunc: func() error { return nil },
WaitFunc: func() {},
ShutdownFunc: func() {},
GetTxCountFunc: func() int64 { return 5 },
GetConnectionCountFunc: func() int64 { return 2 },
GetLimitFunc: func() int64 { return 100 },
GetUtxoSetLenFunc: func() int { return 1000 },
},
&mocks.RateBroadcasterMock{
StartFunc: func() error { return tc.startErr },
WaitFunc: func() {},
ShutdownFunc: func() {},
GetTxCountFunc: func() int64 { return 10 },
GetConnectionCountFunc: func() int64 { return 1 },
GetLimitFunc: func() int64 { return 200 },
GetUtxoSetLenFunc: func() int { return 1000 },
},
rateBroadcaster1 := &mocks.RateBroadcasterMock{
StartFunc: func() error { return nil },
WaitFunc: func() {},
ShutdownFunc: func() {},
GetTxCountFunc: func() int64 { return 5 },
GetConnectionCountFunc: func() int64 { return 2 },
GetLimitFunc: func() int64 { return 100 },
GetUtxoSetLenFunc: func() int { return 1000 },
}

mcs := broadcaster.NewMultiKeyRateBroadcaster(logger, cs, broadcaster.WithLogInterval(20*time.Millisecond))
err := mcs.Start()
defer mcs.Shutdown()

if tc.expectedErrorStr != "" || err != nil {
require.ErrorContains(t, err, tc.expectedErrorStr)
return
} else {
require.NoError(t, err)
rateBroadcaster2 := &mocks.RateBroadcasterMock{
StartFunc: func() error { return tc.startErr },
WaitFunc: func() {},
ShutdownFunc: func() {},
GetTxCountFunc: func() int64 { return 10 },
GetConnectionCountFunc: func() int64 { return 1 },
GetLimitFunc: func() int64 { return 200 },
GetUtxoSetLenFunc: func() int { return 1000 },
}

time.Sleep(50 * time.Millisecond)
})
}

}


func TestMultiKeyRateBroadcaster_Start(t *testing.T) {
ks1, err := keyset.New()
require.NoError(t, err)
mcs := broadcaster.NewMultiKeyRateBroadcaster(logger, []broadcaster.RateBroadcaster{rateBroadcaster1, rateBroadcaster2})

ks2, err := keyset.New()
require.NoError(t, err)

utxo1 := &bt.UTXO{
TxID: testdata.TX1Hash[:],
Vout: 0,
LockingScript: ks1.Script,
Satoshis: 1000,
}
utxo2 := &bt.UTXO{
TxID: testdata.TX2Hash[:],
Vout: 0,
LockingScript: ks2.Script,
Satoshis: 1000,
}

tt := []struct {
name string
getBalanceWithRetriesErr error
getUTXOsWithRetriesErr error

expectedBroadcastTransactionsCalls int
expectedErrorStr string
}{
{
name: "success",

expectedBroadcastTransactionsCalls: 2,
},
{
name: "error - failed to get balance",
getBalanceWithRetriesErr: errors.New("utxo client error"),

expectedErrorStr: "failed to get balance",
expectedBroadcastTransactionsCalls: 0,
},
}

for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {

utxoClient := &mocks.UtxoClientMock{
GetBalanceWithRetriesFunc: func(ctx context.Context, mainnet bool, address string, constantBackoff time.Duration, retries uint64) (int64, int64, error) {
return 1000, 0, tc.getBalanceWithRetriesErr
},
GetUTXOsWithRetriesFunc: func(ctx context.Context, mainnet bool, lockingScript *bscript.Script, address string, constantBackoff time.Duration, retries uint64) ([]*bt.UTXO, error) {
return []*bt.UTXO{utxo1, utxo2}, tc.getUTXOsWithRetriesErr
},
}

logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
client := &mocks.ArcClientMock{
BroadcastTransactionsFunc: func(ctx context.Context, txs []*bt.Tx, waitForStatus metamorph_api.Status, callbackURL string, callbackToken string, fullStatusUpdates bool, skipFeeValidation bool) ([]*metamorph_api.TransactionStatus, error) {
var statuses []*metamorph_api.TransactionStatus

for _, tx := range txs {
statuses = append(statuses, &metamorph_api.TransactionStatus{
Txid: tx.TxID(),
Status: metamorph_api.Status_SEEN_ON_NETWORK,
})
}

return statuses, nil
},
}

mrb, err := broadcaster.NewMultiKeyRateBroadcaster(logger, client, []*keyset.KeySet{ks1, ks2}, utxoClient, false, broadcaster.WithBatchSize(2))
require.NoError(t, err)
err := mcs.Start()
defer mcs.Shutdown()

err = mrb.Start(10, 50)
if tc.expectedErrorStr != "" || err != nil {
require.ErrorContains(t, err, tc.expectedErrorStr)
return
if tc.expectedError != "" {
require.ErrorContains(t, err, tc.expectedError)
} else {
require.NoError(t, err)
}

time.Sleep(500 * time.Millisecond)

mrb.Shutdown()
time.Sleep(50 * time.Millisecond)
})
}
}
Loading

0 comments on commit 83f5690

Please sign in to comment.