diff --git a/client.go b/client.go index f947cd08..da261661 100644 --- a/client.go +++ b/client.go @@ -21,6 +21,7 @@ import ( "github.com/lightninglabs/loop/sweep" "github.com/lightninglabs/loop/sweepbatcher" "github.com/lightninglabs/loop/utils" + "github.com/lightninglabs/loop/utils/chainhashutil" "github.com/lightninglabs/taproot-assets/rpcutils" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/lntypes" @@ -272,14 +273,9 @@ func NewClient(dbDir string, loopDB loopdb.SwapStore, } if len(cfg.SkippedTxns) != 0 { - skippedTxns := make(map[chainhash.Hash]struct{}) - for _, txid := range cfg.SkippedTxns { - txid, err := chainhash.NewHashFromStr(txid) - if err != nil { - return nil, nil, fmt.Errorf("failed to parse "+ - "txid to skip %v: %w", txid, err) - } - skippedTxns[*txid] = struct{}{} + skippedTxns, err := parseSkippedTxns(cfg.SkippedTxns) + if err != nil { + return nil, nil, err } batcherOpts = append(batcherOpts, sweepbatcher.WithSkippedTxns( skippedTxns, @@ -323,6 +319,24 @@ func NewClient(dbDir string, loopDB loopdb.SwapStore, return client, cleanup, nil } +// parseSkippedTxns parses the configured skipped transaction IDs and rejects +// any txid that is not fully specified. +func parseSkippedTxns(txids []string) (map[chainhash.Hash]struct{}, error) { + skippedTxns := make(map[chainhash.Hash]struct{}, len(txids)) + + for _, txid := range txids { + hash, err := chainhashutil.NewHashFromStrExact(txid) + if err != nil { + return nil, fmt.Errorf("failed to parse txid to skip %v: %w", + txid, err) + } + + skippedTxns[hash] = struct{}{} + } + + return skippedTxns, nil +} + // GetConn returns the gRPC connection to the server. func (s *Client) GetConn() *grpc.ClientConn { return s.clientConfig.Conn diff --git a/client_test.go b/client_test.go index 15b9c7c9..ede6e2ff 100644 --- a/client_test.go +++ b/client_test.go @@ -4,6 +4,7 @@ import ( "context" "crypto/sha256" "errors" + "strings" "testing" "github.com/btcsuite/btcd/btcutil" @@ -46,6 +47,50 @@ var ( defaultConfirmations = int32(loopdb.DefaultLoopOutHtlcConfirmations) ) +// TestParseSkippedTxns verifies that skipped txids must be fully specified. +func TestParseSkippedTxns(t *testing.T) { + t.Parallel() + + validTxid := strings.Repeat("01", 32) + validHash, err := chainhash.NewHashFromStr(validTxid) + require.NoError(t, err) + + tests := []struct { + name string + txids []string + expected map[chainhash.Hash]struct{} + expectedErr string + }{ + { + name: "valid", + txids: []string{validTxid}, + expected: map[chainhash.Hash]struct{}{ + *validHash: {}, + }, + }, + { + name: "short", + txids: []string{"abcd"}, + expectedErr: "failed to parse txid to skip abcd", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + skippedTxns, err := parseSkippedTxns(test.txids) + if test.expectedErr != "" { + require.ErrorContains(t, err, test.expectedErr) + return + } + + require.NoError(t, err) + require.Equal(t, test.expected, skippedTxns) + }) + } +} + var htlcKeys = func() loopdb.HtlcKeys { var senderKey, receiverKey [33]byte