diff --git a/.gitignore b/.gitignore index ceac748e7..734ea4b95 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ _testmain.go /loop-debug /loopd-debug +/tools/llformat output*.log diff --git a/.golangci.yml b/.golangci.yml index 914e57897..e0eef6bc6 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -54,6 +54,11 @@ linters: - funcorder - wsl_v5 - noinlineerr + # llformat now owns vertical whitespace for handwritten Go. The + # whitespace linter cannot express the project rule that multi-line + # function signatures keep a body separator while single-return control + # blocks stay compact. + - whitespace settings: gosec: excludes: @@ -67,9 +72,6 @@ linters: case: rules: json: snake - whitespace: - multi-if: true - multi-func: true exclusions: generated: lax presets: diff --git a/Makefile b/Makefile index f698ec9cb..5131757db 100644 --- a/Makefile +++ b/Makefile @@ -7,9 +7,11 @@ GOTEST := GO111MODULE=on go test -v GOIMPORTS_PKG := github.com/rinchsan/gosimports/cmd/gosimports +LLFORMAT_PKG := github.com/bhandras/llformat/cmd/llformat GO_BIN := ${GOPATH}/bin GOIMPORTS_BIN := $(GO_BIN)/gosimports +LLFORMAT_BIN := $(CURDIR)/$(TOOLS_DIR)/llformat GOBUILD := CGO_ENABLED=0 GO111MODULE=on go build -v GOINSTALL := CGO_ENABLED=0 GO111MODULE=on go install -v @@ -35,6 +37,7 @@ UNIT := $(GOLIST) | $(XARGS) env $(GOTEST) $(TEST_FLAGS) ifneq ($(workers),) LINT_WORKERS = --concurrency=$(workers) endif +FMT_BASE := $(if $(base),$(base),origin/master) DOCKER_TOOLS = docker run \ --rm \ @@ -64,6 +67,11 @@ $(GOIMPORTS_BIN): @$(call print, "Installing goimports.") cd $(TOOLS_DIR); go install -trimpath $(GOIMPORTS_PKG) +$(LLFORMAT_BIN): $(TOOLS_DIR)/go.mod $(TOOLS_DIR)/go.sum + @$(call print, "Installing llformat.") + cd $(TOOLS_DIR); GOBIN="$(CURDIR)/$(TOOLS_DIR)" \ + go install -trimpath $(LLFORMAT_PKG) + # ============ # INSTALLATION @@ -132,11 +140,15 @@ unit-postgres-race: # UTILITIES # ========= -fmt: $(GOIMPORTS_BIN) - @$(call print, "Fixing imports.") - gosimports -w $(GOFILES_NOVENDOR) - @$(call print, "Formatting source.") - gofmt -l -w -s $(GOFILES_NOVENDOR) +fmt: $(LLFORMAT_BIN) + @$(call print, "Formatting all handwritten Go source.") + @./scripts/llformat-files.sh all | \ + xargs -0 -n 1 $(LLFORMAT_BIN) -w + +fmt-changed: $(LLFORMAT_BIN) + @$(call print, "Formatting Go source changes against $(FMT_BASE).") + @./scripts/llformat-files.sh changed "$(FMT_BASE)" | \ + xargs -0 -n 1 $(LLFORMAT_BIN) -w lint: docker-tools @$(call print, "Linting source.") diff --git a/assets/client.go b/assets/client.go index c224f3335..c6d202c29 100644 --- a/assets/client.go +++ b/assets/client.go @@ -50,6 +50,7 @@ type TapdConfig struct { // assets daemon. func DefaultTapdConfig() *TapdConfig { defaultConf := tapcfg.DefaultConfig() + return &TapdConfig{ Activate: false, Host: "localhost:10029", @@ -83,14 +84,18 @@ func NewTapdClient(config *TapdConfig) (*TapdClient, error) { // Create the TapdClient. client := &TapdClient{ - assetNameCache: make(map[string]string), - cc: conn, - cfg: config, - TaprootAssetsClient: taprpc.NewTaprootAssetsClient(conn), - TaprootAssetChannelsClient: tapchannelrpc.NewTaprootAssetChannelsClient(conn), - PriceOracleClient: priceoraclerpc.NewPriceOracleClient(conn), - RfqClient: rfqrpc.NewRfqClient(conn), - UniverseClient: universerpc.NewUniverseClient(conn), + assetNameCache: make(map[string]string), + cc: conn, + cfg: config, + TaprootAssetsClient: taprpc.NewTaprootAssetsClient(conn), + TaprootAssetChannelsClient: tapchannelrpc.NewTaprootAssetChannelsClient( + conn, + ), + PriceOracleClient: priceoraclerpc.NewPriceOracleClient( + conn, + ), + RfqClient: rfqrpc.NewRfqClient(conn), + UniverseClient: universerpc.NewUniverseClient(conn), } return client, nil @@ -104,9 +109,8 @@ func (c *TapdClient) Close() { // GetRfqForAsset returns a RFQ for the given asset with the given amount and // to the given peer. func (c *TapdClient) GetRfqForAsset(ctx context.Context, - satAmount btcutil.Amount, assetId, peerPubkey []byte, - expiry int64, feeLimitMultiplier float64) ( - *rfqrpc.PeerAcceptedSellQuote, error) { + satAmount btcutil.Amount, assetId, peerPubkey []byte, expiry int64, + feeLimitMultiplier float64) (*rfqrpc.PeerAcceptedSellQuote, error) { // paymentMaxAmt is the maximum amount we are willing to pay for the // payment. @@ -149,8 +153,8 @@ func (c *TapdClient) GetRfqForAsset(ctx context.Context, } // GetAssetName returns the human-readable name of the asset. -func (c *TapdClient) GetAssetName(ctx context.Context, - assetId []byte) (string, error) { +func (c *TapdClient) GetAssetName(ctx context.Context, assetId []byte) (string, + error) { c.assetNameMutex.Lock() defer c.assetNameMutex.Unlock() @@ -220,13 +224,15 @@ func (c *TapdClient) GetAssetPrice(ctx context.Context, assetID string, } if rfq.GetInvalidQuote() != nil { - return 0, fmt.Errorf("peer %v sent an invalid quote response %v for "+ - "asset %v", peerPubkey, rfq.GetInvalidQuote(), assetID) + return 0, fmt.Errorf("peer %v sent an invalid quote response "+ + "%v for asset %v", peerPubkey, rfq.GetInvalidQuote(), + assetID) } if rfq.GetRejectedQuote() != nil { return 0, fmt.Errorf("peer %v rejected the quote request for "+ - "asset %v, %v", peerPubkey, assetID, rfq.GetRejectedQuote()) + "asset %v, %v", peerPubkey, assetID, + rfq.GetRejectedQuote()) } acceptedRes := rfq.GetAcceptedQuote() @@ -240,8 +246,8 @@ func (c *TapdClient) GetAssetPrice(ctx context.Context, assetID string, // getSatsFromAssetAmt returns the amount in satoshis for the given asset amount // and asset rate. -func getSatsFromAssetAmt(assetAmt uint64, assetRate *rfqrpc.FixedPoint) ( - btcutil.Amount, error) { +func getSatsFromAssetAmt(assetAmt uint64, + assetRate *rfqrpc.FixedPoint) (btcutil.Amount, error) { rateFP, err := rpcutils.UnmarshalRfqFixedPoint(assetRate) if err != nil { @@ -257,8 +263,8 @@ func getSatsFromAssetAmt(assetAmt uint64, assetRate *rfqrpc.FixedPoint) ( // getPaymentMaxAmount returns the milisat amount we are willing to pay for the // payment. -func getPaymentMaxAmount(satAmount btcutil.Amount, feeLimitMultiplier float64) ( - lnwire.MilliSatoshi, error) { +func getPaymentMaxAmount(satAmount btcutil.Amount, + feeLimitMultiplier float64) (lnwire.MilliSatoshi, error) { if satAmount == 0 { return 0, fmt.Errorf("satAmount cannot be zero") @@ -273,7 +279,10 @@ func getPaymentMaxAmount(satAmount btcutil.Amount, feeLimitMultiplier float64) ( // The resulting maximum amount we're willing to pay is 300k sats. // The response asset amount will be for those 300k sats. return lnrpc.UnmarshallAmt( - int64(satAmount.MulF64(feeLimitMultiplier)), 0, + int64( + satAmount.MulF64(feeLimitMultiplier), + ), + 0, ) } diff --git a/assets/client_test.go b/assets/client_test.go index 6b791aafe..1b704cdcb 100644 --- a/assets/client_test.go +++ b/assets/client_test.go @@ -76,27 +76,38 @@ func TestGetSatsFromAssetAmt(t *testing.T) { expectError bool }{ { - assetAmt: 1000, - assetRate: &rfqrpc.FixedPoint{Coefficient: "100000", Scale: 0}, + assetAmt: 1000, + assetRate: &rfqrpc.FixedPoint{ + Coefficient: "100000", + Scale: 0, + }, expected: btcutil.Amount(1000000), expectError: false, }, { - assetAmt: 500000, - assetRate: &rfqrpc.FixedPoint{Coefficient: "200000000", Scale: 0}, + assetAmt: 500000, + assetRate: &rfqrpc.FixedPoint{ + Coefficient: "200000000", + Scale: 0, + }, expected: btcutil.Amount(250000), expectError: false, }, { - assetAmt: 0, - assetRate: &rfqrpc.FixedPoint{Coefficient: "100000000", Scale: 0}, + assetAmt: 0, + assetRate: &rfqrpc.FixedPoint{ + Coefficient: "100000000", + Scale: 0, + }, expected: btcutil.Amount(0), expectError: false, }, } for _, test := range tests { - result, err := getSatsFromAssetAmt(test.assetAmt, test.assetRate) + result, err := getSatsFromAssetAmt( + test.assetAmt, test.assetRate, + ) if test.expectError { require.NotNil(t, err) } else { diff --git a/client.go b/client.go index f947cd08e..d5bd50d4f 100644 --- a/client.go +++ b/client.go @@ -86,8 +86,8 @@ var ( // defaultRFQExpiry is the default expiry time for RFQs. defaultRFQExpiry = 5 * time.Minute - // defaultRFQMaxLimitMultiplier is the default maximum fee multiplier for - // RFQs. + // defaultRFQMaxLimitMultiplier is the default maximum fee multiplier + // for RFQs. defaultRFQMaxLimitMultiplier = 1.2 ) @@ -179,8 +179,8 @@ type ClientConfig struct { // NewClient returns a new instance to initiate swaps with. func NewClient(dbDir string, loopDB loopdb.SwapStore, - sweeperDb sweepbatcher.BatcherStore, cfg *ClientConfig) ( - *Client, func(), error) { + sweeperDb sweepbatcher.BatcherStore, cfg *ClientConfig) (*Client, + func(), error) { l402Store, err := l402.NewFileStore(dbDir) if err != nil { @@ -209,7 +209,9 @@ func NewClient(dbDir string, loopDB loopdb.SwapStore, Lnd: cfg.Lnd, } - verifySchnorrSig := func(pubKey *btcec.PublicKey, hash, sig []byte) error { + verifySchnorrSig := func(pubKey *btcec.PublicKey, hash, + sig []byte) error { + schnorrSig, err := schnorr.ParseSignature(sig) if err != nil { return err @@ -226,8 +228,9 @@ func NewClient(dbDir string, loopDB loopdb.SwapStore, loopDB, cfg.Lnd.ChainParams, ) if err != nil { - return nil, nil, fmt.Errorf("sweepbatcher."+ - "NewSweepFetcherFromSwapStore failed: %w", err) + return nil, nil, fmt.Errorf( + "sweepbatcher.NewSweepFetcherFromSwapStore failed: %w", + err) } // There is circular dependency between executor and sweepbatcher, as @@ -281,9 +284,11 @@ func NewClient(dbDir string, loopDB loopdb.SwapStore, } skippedTxns[*txid] = struct{}{} } - batcherOpts = append(batcherOpts, sweepbatcher.WithSkippedTxns( - skippedTxns, - )) + batcherOpts = append( + batcherOpts, sweepbatcher.WithSkippedTxns( + skippedTxns, + ), + ) } batcher := sweepbatcher.NewBatcher( @@ -533,15 +538,14 @@ func (s *Client) resumeSwaps(ctx context.Context, // automatically after restarts. // // The return value is a hash that uniquely identifies the new swap. -func (s *Client) LoopOut(globalCtx context.Context, - request *OutRequest) (*LoopOutSwapInfo, error) { +func (s *Client) LoopOut(globalCtx context.Context, request *OutRequest) ( + *LoopOutSwapInfo, error) { if request.AssetId != nil { if request.AssetPrepayRfqId == nil || request.AssetSwapRfqId == nil { - - return nil, errors.New("asset prepay and swap rfq ids " + - "must be set when using an asset id") + return nil, errors.New("asset prepay and swap rfq " + + "ids must be set when using an asset id") } // Verify that if we have an asset id set, we have a valid asset @@ -551,14 +555,11 @@ func (s *Client) LoopOut(globalCtx context.Context, "when using an asset id") } - log.Infof("LoopOut %v sats to %v with asset %x", - request.Amount, request.DestAddr, request.AssetId, - ) + log.Infof("LoopOut %v sats to %v with asset %x", request.Amount, + request.DestAddr, request.AssetId) } else { - log.Infof("LoopOut %v to %v (channels: %v)", - request.Amount, request.DestAddr, - request.OutgoingChanSet, - ) + log.Infof("LoopOut %v to %v (channels: %v)", request.Amount, + request.DestAddr, request.OutgoingChanSet) } if err := s.waitForInitialized(globalCtx); err != nil { @@ -750,13 +751,10 @@ func (s *Client) waitForInitialized(ctx context.Context) error { } // LoopIn initiates a loop in swap. -func (s *Client) LoopIn(globalCtx context.Context, - request *LoopInRequest) (*LoopInSwapInfo, error) { +func (s *Client) LoopIn(globalCtx context.Context, request *LoopInRequest) ( + *LoopInSwapInfo, error) { - log.Infof("Loop in %v (last hop: %v)", - request.Amount, - request.LastHop, - ) + log.Infof("Loop in %v (last hop: %v)", request.Amount, request.LastHop) if err := s.waitForInitialized(globalCtx); err != nil { return nil, err @@ -802,8 +800,8 @@ func (s *Client) LoopIn(globalCtx context.Context, // LoopInQuote takes an amount and returns a breakdown of estimated costs for // the client. Both the swap server and the on-chain fee estimator are queried // to get to build the quote response. -func (s *Client) LoopInQuote(ctx context.Context, - request *LoopInQuoteRequest) (*LoopInQuote, error) { +func (s *Client) LoopInQuote(ctx context.Context, request *LoopInQuoteRequest) ( + *LoopInQuote, error) { // Retrieve current server terms to calculate swap fee. terms, err := s.Server.GetLoopInTerms(ctx, request.Initiator) @@ -945,8 +943,10 @@ func wrapGrpcError(message string, err error) error { grpcStatus, _ := status.FromError(err) return status.Error( - grpcStatus.Code(), fmt.Sprintf("%v: %v", message, - grpcStatus.Message()), + grpcStatus.Code(), + fmt.Sprintf( + "%v: %v", message, grpcStatus.Message(), + ), ) } @@ -976,6 +976,7 @@ func (s *Client) AbandonSwap(ctx context.Context, case s.abandonChans[req.SwapHash] <- struct{}{}: case <-ctx.Done(): return ctx.Err() + default: // This is to avoid writing to a full channel. } @@ -988,8 +989,8 @@ func (s *Client) getAssetRfq(ctx context.Context, quote *LoopOutQuote, request *LoopOutQuoteRequest) (*LoopOutRfq, error) { if s.AssetClient == nil { - return nil, errors.New("asset client must be set " + - "when trying to loop out with an asset") + return nil, errors.New("asset client must be set when trying " + + "to loop out with an asset") } rfqReq := request.AssetRFQRequest if rfqReq.Expiry == 0 { @@ -1002,9 +1003,8 @@ func (s *Client) getAssetRfq(ctx context.Context, quote *LoopOutQuote, // First we'll get the prepay rfq. prepayRfq, err := s.AssetClient.GetRfqForAsset( - ctx, quote.PrepayAmount, rfqReq.AssetId, - rfqReq.AssetEdgeNode, rfqReq.Expiry, - rfqReq.MaxLimitMultiplier, + ctx, quote.PrepayAmount, rfqReq.AssetId, rfqReq.AssetEdgeNode, + rfqReq.Expiry, rfqReq.MaxLimitMultiplier, ) if err != nil { return nil, err @@ -1023,9 +1023,8 @@ func (s *Client) getAssetRfq(ctx context.Context, quote *LoopOutQuote, quote.PrepayAmount swapRfq, err := s.AssetClient.GetRfqForAsset( - ctx, invoiceAmt, rfqReq.AssetId, - rfqReq.AssetEdgeNode, rfqReq.Expiry, - rfqReq.MaxLimitMultiplier, + ctx, invoiceAmt, rfqReq.AssetId, rfqReq.AssetEdgeNode, + rfqReq.Expiry, rfqReq.MaxLimitMultiplier, ) if err != nil { return nil, err diff --git a/client_test.go b/client_test.go index 15b9c7c9d..520c6f154 100644 --- a/client_test.go +++ b/client_test.go @@ -84,11 +84,13 @@ func TestLoopOutSuccess(t *testing.T) { signalPrepaymentResult := ctx.AssertPaid(prepayInvoiceDesc) // Expect client to register for conf. - confIntent := ctx.Context.AssertRegisterConf(false, req.HtlcConfirmations) + confIntent := ctx.Context.AssertRegisterConf( + false, req.HtlcConfirmations, + ) - testLoopOutSuccess(ctx, testRequest.Amount, info.SwapHash, - signalPrepaymentResult, signalSwapPaymentResult, false, - confIntent, swap.HtlcV3, + testLoopOutSuccess( + ctx, testRequest.Amount, info.SwapHash, signalPrepaymentResult, + signalSwapPaymentResult, false, confIntent, swap.HtlcV3, ) } @@ -267,8 +269,10 @@ func testLoopOutResume(t *testing.T, confs uint32, expired, preimageRevealed, }, }, Loop: loopdb.Loop{ - Events: []*loopdb.LoopEvent{&update}, - Hash: hash, + Events: []*loopdb.LoopEvent{ + &update, + }, + Hash: hash, }, } @@ -310,16 +314,17 @@ func testLoopOutResume(t *testing.T, confs uint32, expired, preimageRevealed, ctx.assertStatus(loopdb.StateFailTimeout) ctx.assertStoreFinished(loopdb.StateFailTimeout) ctx.finish() + return } // Because there is no reliable payment yet, an invoice is assumed to be // paid after resume. - testLoopOutSuccess(ctx, amt, hash, - func(r error) {}, + testLoopOutSuccess( + ctx, amt, hash, func(r error) {}, - preimageRevealed, - confIntent, utils.GetHtlcScriptVersion(protocolVersion), + func(r error) {}, preimageRevealed, confIntent, + utils.GetHtlcScriptVersion(protocolVersion), ) } diff --git a/cmd/loop/debug.go b/cmd/loop/debug.go index 9bb17106c..9fd701ec4 100644 --- a/cmd/loop/debug.go +++ b/cmd/loop/debug.go @@ -42,7 +42,9 @@ func forceAutoloop(ctx context.Context, cmd *cli.Command) error { return nil } -func getDebugClient(ctx context.Context, cmd *cli.Command) (looprpc.DebugClient, func(), error) { +func getDebugClient(ctx context.Context, cmd *cli.Command) (looprpc.DebugClient, + func(), error) { + rpcServer := cmd.String("rpcserver") tlsCertPath, macaroonPath, err := extractPathArgs(cmd) if err != nil { @@ -52,8 +54,11 @@ func getDebugClient(ctx context.Context, cmd *cli.Command) (looprpc.DebugClient, if err != nil { return nil, nil, err } - cleanup := func() { conn.Close() } + cleanup := func() { + conn.Close() + } debugClient := looprpc.NewDebugClient(conn) + return debugClient, cleanup, nil } diff --git a/cmd/loop/docs.go b/cmd/loop/docs.go index 7296675c8..1c2c24f53 100644 --- a/cmd/loop/docs.go +++ b/cmd/loop/docs.go @@ -51,11 +51,12 @@ func printMarkdown(_ context.Context, cmd *cli.Command) error { return nil } -// filterNestedHelpCommands clones cmd, drops nested help commands, and normalises -// flag defaults so generated documentation avoids absolute paths. +// filterNestedHelpCommands clones cmd, drops nested help commands, and +// normalises flag defaults so generated documentation avoids absolute paths. func filterNestedHelpCommands(cmd *cli.Command) *cli.Command { cloned := cloneCommand(cmd, 0) overrideDocFlags(cloned) + return cloned } @@ -86,6 +87,7 @@ func cloneCommand(cmd *cli.Command, depth int) *cli.Command { } cloned.Commands = filtered + return &cloned } diff --git a/cmd/loop/feecap.go b/cmd/loop/feecap.go index dfe580b57..a7bc1a6a9 100644 --- a/cmd/loop/feecap.go +++ b/cmd/loop/feecap.go @@ -27,8 +27,7 @@ const ( // server-quoted fee already exceeds the resolved cap the caller receives an // error so the swap can be rejected before confirmation. func resolveMaxSwapFee(quoteReq *looprpc.QuoteRequest, - quote *looprpc.InQuoteResponse, - satIsSet bool, maxFeeSat uint64, + quote *looprpc.InQuoteResponse, satIsSet bool, maxFeeSat uint64, ppmIsSet bool, maxFeePpm uint64) (btcutil.Amount, error) { // If a flag is used, make sure the value is within a reasonable range. @@ -71,8 +70,8 @@ func resolveMaxSwapFee(quoteReq *looprpc.QuoteRequest, ppmCapSat = ppmCapForSwapAmount(swapAmt, maxFeePpm) if ppmCapSat == 0 { return 0, fmt.Errorf("ppm cap rounds to 0 sat for "+ - "swap amount %d; use --max_swap_fee_sat "+ - "instead", swapAmt) + "swap amount %d; use "+ + "--max_swap_fee_sat instead", swapAmt) } } @@ -93,8 +92,8 @@ func resolveMaxSwapFee(quoteReq *looprpc.QuoteRequest, // Reject early if the quote already exceeds the cap. if quote.SwapFeeSat > int64(resolvedSat) { - return 0, fmt.Errorf("quoted swap fee %d sat exceeds "+ - "maximum allowed %d sat", quote.SwapFeeSat, resolvedSat) + return 0, fmt.Errorf("quoted swap fee %d sat exceeds maximum "+ + "allowed %d sat", quote.SwapFeeSat, resolvedSat) } return btcutil.Amount(int64(resolvedSat)), nil diff --git a/cmd/loop/feecap_test.go b/cmd/loop/feecap_test.go index 0f284c5a6..ab118b26b 100644 --- a/cmd/loop/feecap_test.go +++ b/cmd/loop/feecap_test.go @@ -147,8 +147,7 @@ func TestResolveMaxSwapFee(t *testing.T) { } got, err := resolveMaxSwapFee( - quoteReq, quote, - tc.satIsSet, tc.maxSat, + quoteReq, quote, tc.satIsSet, tc.maxSat, tc.ppmIsSet, tc.maxPpm, ) if tc.wantErr != "" { diff --git a/cmd/loop/instantout.go b/cmd/loop/instantout.go index 9783f0054..5cf3fa392 100644 --- a/cmd/loop/instantout.go +++ b/cmd/loop/instantout.go @@ -41,11 +41,14 @@ func instantOut(ctx context.Context, cmd *cli.Command) error { // element. var outgoingChanSet []uint64 if cmd.IsSet("channel") { - for chanString := range strings.SplitSeq(cmd.String("channel"), ",") { + for chanString := range strings.SplitSeq( + cmd.String("channel"), + ",", + ) { chanID, err := strconv.ParseUint(chanString, 10, 64) if err != nil { - return fmt.Errorf("error parsing channel id "+ - "\"%v\"", chanString) + return fmt.Errorf("error parsing "+ + "channel id \"%v\"", chanString) } outgoingChanSet = append(outgoingChanSet, chanID) } @@ -82,6 +85,7 @@ func instantOut(ctx context.Context, cmd *cli.Command) error { if len(confirmedReservations) == 0 { fmt.Printf("No confirmed reservations found \n") + return nil } @@ -89,14 +93,14 @@ func instantOut(ctx context.Context, cmd *cli.Command) error { for _, res := range confirmedReservations { idx++ if len(res.ReservationId) != reservation.IdLength { - return fmt.Errorf("invalid reservation id length: "+ - "got %d, expected %d", len(res.ReservationId), + return fmt.Errorf("invalid reservation id length: got "+ + "%d, expected %d", len(res.ReservationId), reservation.IdLength) } fmt.Printf("Reservation %v: shortid %x, amt %v, expiry "+ - "height %v \n", idx, res.ReservationId[:3], res.Amount, - res.Expiry) + "height %v \n", + idx, res.ReservationId[:3], res.Amount, res.Expiry) totalAmt += int64(res.Amount) } @@ -120,8 +124,7 @@ func instantOut(ctx context.Context, cmd *cli.Command) error { case "ALL": for _, res := range confirmedReservations { selectedReservations = append( - selectedReservations, - res.ReservationId, + selectedReservations, res.ReservationId, ) selectedAmt += res.Amount } diff --git a/cmd/loop/l402.go b/cmd/loop/l402.go index b859e1ba9..885d7bfe0 100644 --- a/cmd/loop/l402.go +++ b/cmd/loop/l402.go @@ -71,6 +71,7 @@ func listAuth(ctx context.Context, cmd *cli.Command) error { } printJSON(tokens) + return nil } @@ -99,5 +100,6 @@ func fetchL402(ctx context.Context, cmd *cli.Command) error { } printRespJSON(res) + return nil } diff --git a/cmd/loop/liquidity.go b/cmd/loop/liquidity.go index 5492f2045..421587e3f 100644 --- a/cmd/loop/liquidity.go +++ b/cmd/loop/liquidity.go @@ -92,8 +92,8 @@ func setRule(ctx context.Context, cmd *cli.Command) error { if err != nil { pubkey, err = route.NewVertexFromStr(cmd.Args().First()) if err != nil { - return fmt.Errorf("please provide a valid pubkey: "+ - "%v, or short channel ID", err) + return fmt.Errorf("please provide a valid pubkey: %v, "+ + "or short channel ID", err) } pubkeyRule = true } @@ -163,6 +163,7 @@ func setRule(ctx context.Context, cmd *cli.Command) error { Parameters: params, }, ) + return err } @@ -531,7 +532,10 @@ func setParams(ctx context.Context, cmd *cli.Command) error { if cmd.IsSet("autobudgetrefreshperiod") { params.AutoloopBudgetRefreshPeriodSec = - uint64(cmd.Duration("autobudgetrefreshperiod").Seconds()) + uint64( + cmd.Duration("autobudgetrefreshperiod"). + Seconds(), + ) flagSet = true } @@ -576,7 +580,9 @@ func setParams(ctx context.Context, cmd *cli.Command) error { } if cmd.IsSet("localbalancesat") { - params.EasyAutoloopLocalTargetSat = cmd.Uint64("localbalancesat") + params.EasyAutoloopLocalTargetSat = cmd.Uint64( + "localbalancesat", + ) flagSet = true } @@ -603,8 +609,8 @@ func setParams(ctx context.Context, cmd *cli.Command) error { } v, err := route.NewVertexFromStr(s) if err != nil { - return fmt.Errorf("invalid peer pubkey "+ - "%s: %v", s, err) + return fmt.Errorf("invalid peer pubkey %s: %v", + s, err) } params.EasyAutoloopExcludedPeers = append( params.EasyAutoloopExcludedPeers, v[:], @@ -704,6 +710,7 @@ func suggestSwap(ctx context.Context, cmd *cli.Command) error { ) if err == nil { printRespJSON(resp) + return nil } diff --git a/cmd/loop/loopin.go b/cmd/loop/loopin.go index 0f1401d28..33773d12d 100644 --- a/cmd/loop/loopin.go +++ b/cmd/loop/loopin.go @@ -96,8 +96,10 @@ func loopIn(ctx context.Context, cmd *cli.Command) error { switch { case cmd.IsSet("amt"): amtStr = strconv.FormatUint(cmd.Uint64("amt"), 10) + case cmd.NArg() == 1: amtStr = args.First() + default: // Show command help if no arguments and flags were provided. return showCommandHelp(ctx, cmd) @@ -171,11 +173,9 @@ func loopIn(ctx context.Context, cmd *cli.Command) error { // makes sense to abort the loop in this case. if !external && quote.HtlcPublishFeeSat == int64(loop.MinerFeeEstimationFailed) { - - return fmt.Errorf("miner fee estimation not " + - "possible, lnd has insufficient funds to " + - "create a sample transaction for selected " + - "amount") + return fmt.Errorf("miner fee estimation not possible, lnd " + + "has insufficient funds to create a sample " + + "transaction for selected amount") } limits := getInLimits(quote) diff --git a/cmd/loop/loopout.go b/cmd/loop/loopout.go index f1da46f61..b96c251cd 100644 --- a/cmd/loop/loopout.go +++ b/cmd/loop/loopout.go @@ -137,9 +137,11 @@ func loopOut(ctx context.Context, cmd *cli.Command) error { switch { case cmd.IsSet("amt"): amtStr = strconv.FormatUint(cmd.Uint64("amt"), 10) + case cmd.NArg() == 1 || cmd.NArg() == 2: amtStr = args.First() remaining = args.Tail() + default: // Show command help if no arguments and flags were provided. return showCommandHelp(ctx, cmd) @@ -156,14 +158,17 @@ func loopOut(ctx context.Context, cmd *cli.Command) error { var outgoingChanSet []uint64 if cmd.IsSet("channel") { if cmd.IsSet("asset_id") { - return fmt.Errorf("channel flag is not supported when " + - "looping out assets") + return fmt.Errorf("channel flag is not supported " + + "when looping out assets") } - for chanString := range strings.SplitSeq(cmd.String("channel"), ",") { + for chanString := range strings.SplitSeq( + cmd.String("channel"), + ",", + ) { chanID, err := strconv.ParseUint(chanString, 10, 64) if err != nil { - return fmt.Errorf("error parsing channel id "+ - "\"%v\"", chanString) + return fmt.Errorf("error parsing "+ + "channel id \"%v\"", chanString) } outgoingChanSet = append(outgoingChanSet, chanID) } @@ -275,8 +280,8 @@ func loopOut(ctx context.Context, cmd *cli.Command) error { if fast { warning = "Fast swap requested." } else { - warning = fmt.Sprintf("Regular swap speed requested, it "+ - "might take up to %v for the swap to be executed.", + warning = fmt.Sprintf("Regular swap speed requested, it might "+ + "take up to %v for the swap to be executed.", defaultSwapWaitTime) } @@ -302,8 +307,8 @@ func loopOut(ctx context.Context, cmd *cli.Command) error { if cmd.IsSet("payment_timeout") { parsedTimeout := cmd.Duration("payment_timeout") if parsedTimeout.Truncate(time.Second) != parsedTimeout { - return fmt.Errorf("payment timeout must be a " + - "whole number of seconds") + return fmt.Errorf("payment timeout must be a whole " + + "number of seconds") } paymentTimeout = int64(parsedTimeout.Seconds()) diff --git a/cmd/loop/main.go b/cmd/loop/main.go index 3f82be775..17081aa46 100644 --- a/cmd/loop/main.go +++ b/cmd/loop/main.go @@ -151,6 +151,7 @@ func printRespJSON(resp proto.Message) { jsonBytes, err := lnrpc.ProtoJSONMarshalOpts.Marshal(resp) if err != nil { fmt.Println("unable to decode response: ", err) + return } @@ -192,9 +193,7 @@ func main() { // getClient establishes a SwapClient RPC connection and returns the client and // a cleanup handler. -func getClient(cmd *cli.Command) (looprpc.SwapClientClient, - func(), error) { - +func getClient(cmd *cli.Command) (looprpc.SwapClientClient, func(), error) { client, _, cleanup, err := getClientWithConn(cmd) if err != nil { return nil, nil, err @@ -217,9 +216,12 @@ func getClientWithConn(cmd *cli.Command) (looprpc.SwapClientClient, if err != nil { return nil, nil, nil, err } - cleanup := func() { conn.Close() } + cleanup := func() { + conn.Close() + } loopClient := looprpc.NewSwapClientClient(conn) + return loopClient, conn, cleanup, nil } @@ -301,9 +303,11 @@ func getOutLimits(amt btcutil.Amount, quote *looprpc.OutQuoteResponse) *outLimits { maxSwapRoutingFee := getMaxRoutingFee(amt) - maxPrepayRoutingFee := getMaxRoutingFee(btcutil.Amount( - quote.PrepayAmtSat, - )) + maxPrepayRoutingFee := getMaxRoutingFee( + btcutil.Amount( + quote.PrepayAmtSat, + ), + ) maxPrepayAmt := btcutil.Amount(quote.PrepayAmtSat) return &outLimits{ @@ -319,8 +323,8 @@ func getOutLimits(amt btcutil.Amount, } } -func displayInDetails(req *looprpc.QuoteRequest, - resp *looprpc.InQuoteResponse, verbose bool) error { +func displayInDetails(req *looprpc.QuoteRequest, resp *looprpc.InQuoteResponse, + verbose bool) error { if req.ExternalHtlc { fmt.Printf("On-chain fee for external loop in is not " + @@ -351,9 +355,8 @@ func displayOutDetails(l *outLimits, warning string, req *looprpc.QuoteRequest, if verbose { fmt.Println() fmt.Printf(satAmtFmt, "Max on-chain fee:", l.maxMinerFee) - fmt.Printf(satAmtFmt, - "Max off-chain swap routing fee:", l.maxSwapRoutingFee, - ) + fmt.Printf(satAmtFmt, "Max off-chain swap routing fee:", + l.maxSwapRoutingFee) fmt.Printf(satAmtFmt, "Max off-chain prepay routing fee:", l.maxPrepayRoutingFee) } @@ -391,15 +394,15 @@ func logSwap(swap *looprpc.SwapStatus) { // If our swap failed, we add our failure reason to the state. swapState := fmt.Sprintf("%v", swap.State) if swap.State == looprpc.SwapState_FAILED { - swapState = fmt.Sprintf("%v (%v)", swapState, swap.FailureReason) + swapState = fmt.Sprintf("%v (%v)", swapState, + swap.FailureReason) } if swap.Type == looprpc.SwapType_LOOP_OUT { fmt.Printf("%v %v %v %v - %v", time.Unix(0, swap.LastUpdateTime).Format(time.RFC3339), swap.Type, swapState, btcutil.Amount(swap.Amt), - swap.HtlcAddressP2Wsh, - ) + swap.HtlcAddressP2Wsh) } else { fmt.Printf("%v %v %v %v -", time.Unix(0, swap.LastUpdateTime).Format(time.RFC3339), @@ -419,15 +422,14 @@ func logSwap(swap *looprpc.SwapStatus) { swap.State != looprpc.SwapState_PREIMAGE_REVEALED { fmt.Printf(" (cost: server %v, onchain %v, offchain %v)", - swap.CostServer, swap.CostOnchain, swap.CostOffchain, - ) + swap.CostServer, swap.CostOnchain, swap.CostOffchain) } fmt.Println() } -func getClientConn(address, tlsCertPath, macaroonPath string) (*grpc.ClientConn, - error) { +func getClientConn(address, tlsCertPath, + macaroonPath string) (*grpc.ClientConn, error) { // We always need to send a macaroon. macOption, err := readMacaroon(macaroonPath) @@ -450,8 +452,7 @@ func getClientConn(address, tlsCertPath, macaroonPath string) (*grpc.ClientConn, conn, err := grpc.NewClient(address, opts...) if err != nil { - return nil, fmt.Errorf("unable to create RPC client: %v", - err) + return nil, fmt.Errorf("unable to create RPC client: %v", err) } return conn, nil @@ -497,5 +498,6 @@ func readMacaroon(macPath string) (grpc.DialOption, error) { return nil, fmt.Errorf("error creating macaroon credential: %v", err) } + return grpc.WithPerRPCCredentials(cred), nil } diff --git a/cmd/loop/openchannel.go b/cmd/loop/openchannel.go index e7b18c60d..69471d128 100644 --- a/cmd/loop/openchannel.go +++ b/cmd/loop/openchannel.go @@ -204,6 +204,7 @@ func openChannel(ctx context.Context, cmd *cli.Command) error { // Show command help if no arguments provided if cmd.NArg() == 0 && cmd.NumFlags() == 0 { _ = cli.ShowCommandHelp(ctx, cmd, "openchannel") + return nil } @@ -224,26 +225,31 @@ func openChannel(ctx context.Context, cmd *cli.Command) error { case "sat_per_byte": feeRateSatPerByte := cmd.Int64(feeRateFlag) if feeRateSatPerByte < 0 { - return fmt.Errorf("%v must be non-negative", feeRateFlag) + return fmt.Errorf("%v must be non-negative", + feeRateFlag) } feeRateSatPerVbyte = uint64(feeRateSatPerByte) } minConfs := defaultUtxoMinConf req := &lnrpc.OpenChannelRequest{ - SatPerVbyte: feeRateSatPerVbyte, - FundMax: cmd.Bool("fundmax"), - MinHtlcMsat: cmd.Int64("min_htlc_msat"), - RemoteCsvDelay: uint32(cmd.Uint64("remote_csv_delay")), - MinConfs: int32(minConfs), - SpendUnconfirmed: minConfs == 0, - CloseAddress: cmd.String("close_address"), - RemoteMaxValueInFlightMsat: cmd.Uint64("remote_max_value_in_flight_msat"), - MaxLocalCsv: uint32(cmd.Uint64("max_local_csv")), - ZeroConf: cmd.Bool("zero_conf"), - ScidAlias: cmd.Bool("scid_alias"), - RemoteChanReserveSat: cmd.Uint64("remote_reserve_sats"), - Memo: cmd.String("memo"), + SatPerVbyte: feeRateSatPerVbyte, + FundMax: cmd.Bool("fundmax"), + MinHtlcMsat: cmd.Int64("min_htlc_msat"), + RemoteCsvDelay: uint32( + cmd.Uint64("remote_csv_delay"), + ), + MinConfs: int32(minConfs), + SpendUnconfirmed: minConfs == 0, + CloseAddress: cmd.String("close_address"), + RemoteMaxValueInFlightMsat: cmd.Uint64( + "remote_max_value_in_flight_msat", + ), + MaxLocalCsv: uint32(cmd.Uint64("max_local_csv")), + ZeroConf: cmd.Bool("zero_conf"), + ScidAlias: cmd.Bool("scid_alias"), + RemoteChanReserveSat: cmd.Uint64("remote_reserve_sats"), + Memo: cmd.String("memo"), } switch { @@ -322,6 +328,7 @@ func openChannel(ctx context.Context, cmd *cli.Command) error { switch channelType { case "": break + case channelTypeTweakless: req.CommitmentType = lnrpc.CommitmentType_STATIC_REMOTE_KEY @@ -330,6 +337,7 @@ func openChannel(ctx context.Context, cmd *cli.Command) error { case channelTypeSimpleTaproot: req.CommitmentType = lnrpc.CommitmentType_SIMPLE_TAPROOT + default: return fmt.Errorf("unsupported channel type %v", channelType) } @@ -353,9 +361,8 @@ func openChannel(ctx context.Context, cmd *cli.Command) error { // error. func checkNotBothSet(cmd *cli.Command, a, b string) (string, error) { if cmd.IsSet(a) && cmd.IsSet(b) { - return "", fmt.Errorf( - "either %s or %s should be set, but not both", a, b, - ) + return "", fmt.Errorf("either %s or %s should be set, but "+ + "not both", a, b) } if cmd.IsSet(a) { diff --git a/cmd/loop/quote.go b/cmd/loop/quote.go index d6e7b6c26..01b6dee0b 100644 --- a/cmd/loop/quote.go +++ b/cmd/loop/quote.go @@ -123,10 +123,12 @@ func quoteIn(ctx context.Context, cmd *cli.Command) error { // don't want to fail the quote. But the user should still be informed // why the fee shows as -1. if quoteResp.HtlcPublishFeeSat == int64(loop.MinerFeeEstimationFailed) { - _, _ = fmt.Fprintf(os.Stderr, "Warning: Miner fee estimation "+ - "not possible, lnd has insufficient funds to "+ - "create a sample transaction for selected "+ - "amount.\n") + _, _ = fmt.Fprintf( + os.Stderr, "Warning: Miner fee estimation not "+ + "possible, lnd has insufficient funds to "+ + "create a sample transaction for selected "+ + "amount.\n", + ) } // If the user specified static address deposits, we quoted for their @@ -136,6 +138,7 @@ func quoteIn(ctx context.Context, cmd *cli.Command) error { quoteReq.Amt = int64(depositAmt) } printQuoteInResp(quoteReq, quoteResp, cmd.Bool("verbose")) + return nil } @@ -222,11 +225,12 @@ func quoteOut(ctx context.Context, cmd *cli.Command) error { } printQuoteOutResp(quoteReq, quoteResp, cmd.Bool("verbose")) + return nil } -func printQuoteInResp(req *looprpc.QuoteRequest, - resp *looprpc.InQuoteResponse, verbose bool) { +func printQuoteInResp(req *looprpc.QuoteRequest, resp *looprpc.InQuoteResponse, + verbose bool) { totalFee := resp.HtlcPublishFeeSat + resp.SwapFeeSat amt := req.Amt @@ -235,8 +239,7 @@ func printQuoteInResp(req *looprpc.QuoteRequest, } if req.DepositOutpoints != nil { - fmt.Printf(satAmtFmt, "Previously deposited on-chain:", - amt) + fmt.Printf(satAmtFmt, "Previously deposited on-chain:", amt) } else { fmt.Printf(satAmtFmt, "Send on-chain:", amt) } @@ -255,15 +258,14 @@ func printQuoteInResp(req *looprpc.QuoteRequest, case verbose: fmt.Println() - fmt.Printf( - satAmtFmt, "Estimated on-chain fee:", - resp.HtlcPublishFeeSat, - ) + fmt.Printf(satAmtFmt, "Estimated on-chain fee:", + resp.HtlcPublishFeeSat) fmt.Printf(satAmtFmt, "Loop service fee:", resp.SwapFeeSat) fmt.Printf(satAmtFmt, "Estimated total fee:", totalFee) fmt.Println() fmt.Printf(blkFmt, "Conf target:", resp.ConfTarget) fmt.Printf(blkFmt, "CLTV expiry delta:", resp.CltvDelta) + default: fmt.Printf(satAmtFmt, "Estimated total fee:", totalFee) } @@ -280,13 +282,14 @@ func printQuoteOutResp(req *looprpc.QuoteRequest, ) if err != nil { fmt.Printf("Error converting asset amount: %v\n", err) + return } exchangeRate := float64(assetAmtSwap) / float64(req.Amt) - fmt.Printf(assetAmtFmt, "Send off-chain:", - assetAmtSwap, resp.AssetRfqInfo.AssetName) - fmt.Printf(rateFmt, "Exchange rate:", - exchangeRate, resp.AssetRfqInfo.AssetName) + fmt.Printf(assetAmtFmt, "Send off-chain:", assetAmtSwap, + resp.AssetRfqInfo.AssetName) + fmt.Printf(rateFmt, "Exchange rate:", exchangeRate, + resp.AssetRfqInfo.AssetName) fmt.Printf(assetAmtFmt, "Limit Send off-chain:", resp.AssetRfqInfo.MaxSwapAssetAmt, resp.AssetRfqInfo.AssetName) @@ -298,6 +301,7 @@ func printQuoteOutResp(req *looprpc.QuoteRequest, if !verbose { fmt.Printf(satAmtFmt, "Estimated total fee:", totalFee) + return } @@ -312,11 +316,11 @@ func printQuoteOutResp(req *looprpc.QuoteRequest, ) if err != nil { fmt.Printf("Error converting asset amount: %v\n", err) + return } fmt.Printf(assetAmtFmt, "No show penalty (prepay):", - assetAmtPrepay, - resp.AssetRfqInfo.AssetName) + assetAmtPrepay, resp.AssetRfqInfo.AssetName) fmt.Printf(assetAmtFmt, "Limit no show penalty (prepay):", resp.AssetRfqInfo.MaxPrepayAssetAmt, resp.AssetRfqInfo.AssetName) @@ -326,17 +330,13 @@ func printQuoteOutResp(req *looprpc.QuoteRequest, } fmt.Printf(blkFmt, "Conf target:", resp.ConfTarget) fmt.Printf(blkFmt, "CLTV expiry delta:", resp.CltvDelta) - fmt.Printf("%-38s %s\n", - "Publication deadline:", - time.Unix(int64(req.SwapPublicationDeadline), 0), - ) + fmt.Printf("%-38s %s\n", "Publication deadline:", + time.Unix(int64(req.SwapPublicationDeadline), 0)) } // getAssetAmt returns the asset amount for the given amount in satoshis and // the asset rate. -func getAssetAmt(amt int64, assetRate *looprpc.FixedPoint) ( - uint64, error) { - +func getAssetAmt(amt int64, assetRate *looprpc.FixedPoint) (uint64, error) { askAssetRate, err := unmarshalFixedPoint(assetRate) if err != nil { return 0, err diff --git a/cmd/loop/reservations.go b/cmd/loop/reservations.go index 122eda12f..ce95b0c34 100644 --- a/cmd/loop/reservations.go +++ b/cmd/loop/reservations.go @@ -51,5 +51,6 @@ func listReservations(ctx context.Context, cmd *cli.Command) error { } printRespJSON(resp) + return nil } diff --git a/cmd/loop/staticaddr.go b/cmd/loop/staticaddr.go index fc36597e4..a99fa3998 100644 --- a/cmd/loop/staticaddr.go +++ b/cmd/loop/staticaddr.go @@ -514,7 +514,9 @@ func staticAddressLoopIn(ctx context.Context, cmd *cli.Command) error { label = cmd.String(labelFlag.Name) hints []*swapserverrpc.RouteHint lastHop []byte - paymentTimeoutSeconds = uint32(loopin.DefaultPaymentTimeoutSeconds) + paymentTimeoutSeconds = uint32( + loopin.DefaultPaymentTimeoutSeconds, + ) ) // Validate our label early so that we can fail before getting a quote. @@ -606,9 +608,9 @@ func staticAddressLoopIn(ctx context.Context, cmd *cli.Command) error { // the two is used and checked against the current quote. Without // overrides the quoted fee is forwarded as before. maxSwapFee, err := resolveMaxSwapFee( - quoteReq, quote, - cmd.IsSet("max_swap_fee_sat"), cmd.Uint64("max_swap_fee_sat"), - cmd.IsSet("max_swap_fee_ppm"), cmd.Uint64("max_swap_fee_ppm"), + quoteReq, quote, cmd.IsSet("max_swap_fee_sat"), + cmd.Uint64("max_swap_fee_sat"), cmd.IsSet("max_swap_fee_ppm"), + cmd.Uint64("max_swap_fee_ppm"), ) if err != nil { return err @@ -622,7 +624,9 @@ func staticAddressLoopIn(ctx context.Context, cmd *cli.Command) error { } if cmd.IsSet("payment_timeout") { - paymentTimeoutSeconds = uint32(cmd.Duration("payment_timeout").Seconds()) + paymentTimeoutSeconds = uint32( + cmd.Duration("payment_timeout").Seconds(), + ) } req := &looprpc.StaticAddressLoopInRequest{ diff --git a/cmd/loop/swaps.go b/cmd/loop/swaps.go index 9aa298d20..227a6d625 100644 --- a/cmd/loop/swaps.go +++ b/cmd/loop/swaps.go @@ -55,8 +55,8 @@ func listSwaps(ctx context.Context, cmd *cli.Command) error { defer cleanup() if cmd.Bool("loop_out_only") && cmd.Bool("loop_in_only") { - return fmt.Errorf("only one of loop_out_only and loop_in_only " + - "can be set") + return fmt.Errorf("only one of loop_out_only and " + + "loop_in_only can be set") } filter := &looprpc.ListSwapsFilter{} @@ -65,6 +65,7 @@ func listSwaps(ctx context.Context, cmd *cli.Command) error { switch { case cmd.Bool("loop_out_only"): filter.SwapType = looprpc.ListSwapsFilter_LOOP_OUT + case cmd.Bool("loop_in_only"): filter.SwapType = looprpc.ListSwapsFilter_LOOP_IN } @@ -77,11 +78,14 @@ func listSwaps(ctx context.Context, cmd *cli.Command) error { // element. var outgoingChanSet []uint64 if cmd.IsSet(channelFlag.Name) { - for chanString := range strings.SplitSeq(cmd.String(channelFlag.Name), ",") { + for chanString := range strings.SplitSeq( + cmd.String(channelFlag.Name), + ",", + ) { chanID, err := strconv.ParseUint(chanString, 10, 64) if err != nil { - return fmt.Errorf("error parsing channel id "+ - "\"%v\"", chanString) + return fmt.Errorf("error parsing "+ + "channel id \"%v\"", chanString) } outgoingChanSet = append(outgoingChanSet, chanID) } @@ -123,6 +127,7 @@ func listSwaps(ctx context.Context, cmd *cli.Command) error { } printRespJSON(resp) + return nil } @@ -148,8 +153,10 @@ func swapInfo(ctx context.Context, cmd *cli.Command) error { switch { case cmd.IsSet("id"): id = cmd.String("id") + case cmd.NArg() > 0: id = args.First() + default: // Show command help if no arguments and flags were provided. return showCommandHelp(ctx, cmd) @@ -170,13 +177,16 @@ func swapInfo(ctx context.Context, cmd *cli.Command) error { defer cleanup() resp, err := client.SwapInfo( - ctx, &looprpc.SwapInfoRequest{Id: idBytes}, + ctx, &looprpc.SwapInfoRequest{ + Id: idBytes, + }, ) if err != nil { return err } printRespJSON(resp) + return nil } @@ -246,5 +256,6 @@ func abandonSwap(ctx context.Context, cmd *cli.Command) error { } printRespJSON(resp) + return nil } diff --git a/cmd/loop/sweephtlc.go b/cmd/loop/sweephtlc.go index 6f496bd1f..3ef6e639c 100644 --- a/cmd/loop/sweephtlc.go +++ b/cmd/loop/sweephtlc.go @@ -88,8 +88,9 @@ func sweepHtlc(ctx context.Context, cmd *cli.Command) error { // Report publish status in a user-friendly way based on response. switch { case resp.GetNotRequested() != nil: - fmt.Println("publish: not requested (pass --publish to " + - "broadcast)") + fmt.Println( + "publish: not requested (pass --publish to broadcast)", + ) case resp.GetPublished() != nil: fmt.Println("publish: success") diff --git a/cmd/loop/terms.go b/cmd/loop/terms.go index fd87c6b36..a173d064b 100644 --- a/cmd/loop/terms.go +++ b/cmd/loop/terms.go @@ -23,9 +23,8 @@ func terms(ctx context.Context, cmd *cli.Command) error { defer cleanup() printAmountRange := func(minAmt, maxAmt int64) { - fmt.Printf("Amount: %d - %d\n", - btcutil.Amount(minAmt), btcutil.Amount(maxAmt), - ) + fmt.Printf("Amount: %d - %d\n", btcutil.Amount(minAmt), + btcutil.Amount(maxAmt)) } fmt.Println("Loop Out") @@ -36,12 +35,10 @@ func terms(ctx context.Context, cmd *cli.Command) error { fmt.Println(err) } else { printAmountRange( - loopOutTerms.MinSwapAmount, - loopOutTerms.MaxSwapAmount, - ) - fmt.Printf("Cltv delta: %d - %d\n", - loopOutTerms.MinCltvDelta, loopOutTerms.MaxCltvDelta, + loopOutTerms.MinSwapAmount, loopOutTerms.MaxSwapAmount, ) + fmt.Printf("Cltv delta: %d - %d\n", loopOutTerms.MinCltvDelta, + loopOutTerms.MaxCltvDelta) } fmt.Println() @@ -55,8 +52,7 @@ func terms(ctx context.Context, cmd *cli.Command) error { fmt.Println(err) } else { printAmountRange( - loopInTerms.MinSwapAmount, - loopInTerms.MaxSwapAmount, + loopInTerms.MinSwapAmount, loopInTerms.MaxSwapAmount, ) } diff --git a/cmd/loop/utils.go b/cmd/loop/utils.go index 07a6016a6..c740c3120 100644 --- a/cmd/loop/utils.go +++ b/cmd/loop/utils.go @@ -16,8 +16,10 @@ func showCommandHelp(ctx context.Context, cmd *cli.Command) error { lineage := cmd.Lineage() if len(lineage) > 1 { parent := lineage[1] + return cli.ShowCommandHelp(ctx, parent, cmd.Name) } + return cli.ShowCommandHelp(ctx, cmd, cmd.Name) } @@ -30,9 +32,8 @@ func validateRouteHints(cmd *cli.Command) ([]*swapserverrpc.RouteHint, error) { if cmd.IsSet(routeHintsFlag.Name) { if cmd.IsSet(privateFlag.Name) { - return nil, fmt.Errorf( - "private and route_hints both set", - ) + return nil, fmt.Errorf("private and route_hints both " + + "set") } jsonHints := cmd.StringSlice(routeHintsFlag.Name) diff --git a/cost_migration_test.go b/cost_migration_test.go index 082895f2e..a3d16a5fd 100644 --- a/cost_migration_test.go +++ b/cost_migration_test.go @@ -161,7 +161,9 @@ func TestCostMigration(t *testing.T) { } // Now we can run the migration. - err = MigrateLoopOutCosts(context.Background(), lnd.LndServices, 1, store) + err = MigrateLoopOutCosts( + context.Background(), lnd.LndServices, 1, store, + ) require.NoError(t, err) // Finally check that the swap cost has been updated correctly. @@ -182,6 +184,8 @@ func TestCostMigration(t *testing.T) { // Now run the migration again to make sure it doesn't fail. This also // indicates that the migration did not run the second time as // otherwise the store mocks SetMigration function would fail. - err = MigrateLoopOutCosts(context.Background(), lnd.LndServices, 1, store) + err = MigrateLoopOutCosts( + context.Background(), lnd.LndServices, 1, store, + ) require.NoError(t, err) } diff --git a/executor.go b/executor.go index 8fccef631..4a0e17f44 100644 --- a/executor.go +++ b/executor.go @@ -115,8 +115,10 @@ func (s *executor) run(mainCtx context.Context, select { case h := <-blockEpochChan: setHeight(h) + case err := <-blockErrorChan: return err + case <-mainCtx.Done(): return mainCtx.Err() } @@ -163,16 +165,26 @@ func (s *executor) run(mainCtx context.Context, s.wg.Go(func() { err := newSwap.execute(mainCtx, &executeConfig{ - statusChan: statusChan, - sweeper: s.sweeper, - batcher: s.batcher, - blockEpochChan: queue.ChanOut(), - timerFactory: s.executorConfig.createExpiryTimer, - loopOutMaxParts: s.executorConfig.loopOutMaxParts, - totalPaymentTimeout: s.executorConfig.totalPaymentTimeout, - maxPaymentRetries: s.executorConfig.maxPaymentRetries, - cancelSwap: s.executorConfig.cancelSwap, - verifySchnorrSig: s.executorConfig.verifySchnorrSig, + statusChan: statusChan, + sweeper: s.sweeper, + batcher: s.batcher, + blockEpochChan: queue.ChanOut(), + timerFactory: s. + executorConfig. + createExpiryTimer, + loopOutMaxParts: s. + executorConfig. + loopOutMaxParts, + totalPaymentTimeout: s. + executorConfig. + totalPaymentTimeout, + maxPaymentRetries: s. + executorConfig. + maxPaymentRetries, + cancelSwap: s.executorConfig.cancelSwap, + verifySchnorrSig: s. + executorConfig. + verifySchnorrSig, }, height) if err != nil && !errors.Is( err, context.Canceled, @@ -201,9 +213,8 @@ func (s *executor) run(mainCtx context.Context, case doneID := <-swapDoneChan: queue, ok := blockEpochQueues[doneID] if !ok { - return fmt.Errorf( - "swap id %v not found in queues", - doneID) + return fmt.Errorf("swap id %v not found "+ + "in queues", doneID) } queue.Stop() delete(blockEpochQueues, doneID) @@ -231,9 +242,7 @@ func (s *executor) run(mainCtx context.Context, } // initiateSwap delivers a new swap to the executor main loop. -func (s *executor) initiateSwap(ctx context.Context, - swap genericSwap) { - +func (s *executor) initiateSwap(ctx context.Context, swap genericSwap) { select { case s.newSwaps <- swap: case <-ctx.Done(): diff --git a/fsm/example_fsm_test.go b/fsm/example_fsm_test.go index b3c3d85d5..9d79d225b 100644 --- a/fsm/example_fsm_test.go +++ b/fsm/example_fsm_test.go @@ -106,14 +106,12 @@ func TestExampleFSM(t *testing.T) { require.Equal(t, tc.sendEventErr, err) require.Equal( - t, - tc.expectedLastActionError, + t, tc.expectedLastActionError, exampleContext.LastActionError, ) err = cachedObserver.WaitForState( - context.Background(), - time.Second, + context.Background(), time.Second, tc.expectedState, ) require.NoError(t, err) @@ -206,7 +204,8 @@ func TestExampleFSMFlow(t *testing.T) { go func() { err := exampleContext.SendEvent( - ctxb, OnRequestStuff, newInitStuffRequest(), + ctxb, OnRequestStuff, + newInitStuffRequest(), ) require.NoError(t, err) @@ -214,8 +213,7 @@ func TestExampleFSMFlow(t *testing.T) { // Wait for the final state. err := cachedObserver.WaitForState( - context.Background(), - time.Second, + context.Background(), time.Second, tc.expectedStateFlow[len( tc.expectedStateFlow, )-1], @@ -227,13 +225,11 @@ func TestExampleFSMFlow(t *testing.T) { for index, notification := range allNotifications { require.Equal( - t, - tc.expectedStateFlow[index], + t, tc.expectedStateFlow[index], notification.NextState, ) require.Equal( - t, - tc.expectedEventFlow[index], + t, tc.expectedEventFlow[index], notification.Event, ) } @@ -289,7 +285,8 @@ func TestObserverAsyncWait(t *testing.T) { go func() { err := exampleContext.SendEvent( - ctxb, OnRequestStuff, newInitStuffRequest(), + ctxb, OnRequestStuff, + newInitStuffRequest(), ) require.NoError(t, err) diff --git a/fsm/fsm.go b/fsm/fsm.go index 8c1825474..5ec31b9f8 100644 --- a/fsm/fsm.go +++ b/fsm/fsm.go @@ -13,9 +13,8 @@ var ( ErrEventRejected = errors.New("event rejected") // ErrWaitForStateTimedOut is returned when waiting for state times out. - ErrWaitForStateTimedOut = errors.New( - "timed out while waiting for event", - ) + ErrWaitForStateTimedOut = errors.New("timed out while waiting for " + + "event") // ErrInvalidContextType is returned when an invalid context type is // passed. @@ -23,9 +22,8 @@ var ( // ErrWaitingForStateEarlyAbortError is returned when waiting for state // is aborted early. - ErrWaitingForStateEarlyAbortError = errors.New( - "waiting for state early abort", - ) + ErrWaitingForStateEarlyAbortError = errors.New("waiting for state " + + "early abort") ) const ( @@ -226,6 +224,7 @@ func (s *StateMachine) SendEvent(ctx context.Context, event EventType, if err != nil { log.Errorf("unable to get next state: %v from event: "+ "%v, current state: %v", err, event, s.current) + return ErrEventRejected } @@ -298,6 +297,7 @@ func (s *StateMachine) RemoveObserver(observer Observer) bool { s.observers = append( s.observers[:i], s.observers[i+1:]..., ) + return true } } @@ -310,6 +310,7 @@ func (s *StateMachine) RemoveObserver(observer Observer) bool { func (s *StateMachine) HandleError(err error) EventType { log.Errorf("StateMachine error: %s", err) s.LastActionError = err + return OnError } @@ -348,7 +349,9 @@ func (e ErrWaitingForStateTimeout) Error() string { } // NewErrWaitingForStateTimeout creates a new ErrWaitingForStateTimeout. -func NewErrWaitingForStateTimeout(expected StateType) ErrWaitingForStateTimeout { +func NewErrWaitingForStateTimeout( + expected StateType) ErrWaitingForStateTimeout { + return ErrWaitingForStateTimeout{ expected: expected, } diff --git a/fsm/fsm_test.go b/fsm/fsm_test.go index 8864503d0..c1b2af28f 100644 --- a/fsm/fsm_test.go +++ b/fsm/fsm_test.go @@ -23,7 +23,9 @@ type TestStateMachineContext struct { func (c *TestStateMachineContext) GetStates() States { return States{ "State1": State{ - Action: func(_ context.Context, ctx EventContext) EventType { + Action: func(_ context.Context, + ctx EventContext) EventType { + return "Event1" }, Transitions: Transitions{ @@ -31,7 +33,9 @@ func (c *TestStateMachineContext) GetStates() States { }, }, "State2": State{ - Action: func(_ context.Context, ctx EventContext) EventType { + Action: func(_ context.Context, + ctx EventContext) EventType { + return "NoOp" }, Transitions: Transitions{}, diff --git a/fsm/observer.go b/fsm/observer.go index 0adc4b381..0016282e3 100644 --- a/fsm/observer.go +++ b/fsm/observer.go @@ -113,7 +113,6 @@ func (c *CachedObserver) WaitForState(ctx context.Context, if options.initialWait > 0 { select { case <-time.After(options.initialWait): - case <-ctx.Done(): return ctx.Err() } @@ -171,6 +170,7 @@ func (c *CachedObserver) WaitForStateAsync(ctx context.Context, state StateType, // Check if the last state is the desired state. if c.lastNotification.NextState == state { writeResult(nil) + return } @@ -179,6 +179,7 @@ func (c *CachedObserver) WaitForStateAsync(ctx context.Context, state StateType, lastErr := c.lastNotification.LastActionError if abortOnEarlyError { writeResult(lastErr) + return } } diff --git a/fsm/stateparser/stateparser.go b/fsm/stateparser/stateparser.go index cafedd7d3..516db7646 100644 --- a/fsm/stateparser/stateparser.go +++ b/fsm/stateparser/stateparser.go @@ -23,7 +23,9 @@ func main() { func run() error { out := flag.String("out", "", "outfile") - stateMachine := flag.String("fsm", "", "the swap state machine to parse") + stateMachine := flag.String( + "fsm", "", "the swap state machine to parse", + ) flag.Parse() if filepath.Ext(*out) != ".md" { @@ -45,7 +47,10 @@ func run() error { case "reservation": reservationFSM := &reservation.FSM{} - err = writeMermaidFile(fp, reservationFSM.GetServerInitiatedReservationStates()) + err = writeMermaidFile( + fp, + reservationFSM.GetServerInitiatedReservationStates(), + ) if err != nil { return err } @@ -108,5 +113,6 @@ func sortedKeys(m fsm.States) []string { i++ } sort.Strings(keys) + return keys } diff --git a/instantout/actions.go b/instantout/actions.go index d1c405fd8..151259216 100644 --- a/instantout/actions.go +++ b/instantout/actions.go @@ -41,7 +41,8 @@ const ( // defaultMaxParts is the default maximum number of parts for the swap. defaultMaxParts = uint32(5) - // defaultSendpaymentTimeout is the default timeout for the swap invoice. + // defaultSendpaymentTimeout is the default timeout for the swap + // invoice. defaultSendpaymentTimeout = time.Minute * 5 // defaultPollPaymentTime is the default time to poll the server for the @@ -81,7 +82,8 @@ func (f *FSM) InitInstantOutAction(ctx context.Context, reservationAmt uint64 reservationIds = make([][]byte, 0, len(initCtx.reservations)) reservations = make( - []*reservation.Reservation, 0, len(initCtx.reservations), + []*reservation.Reservation, 0, + len(initCtx.reservations), ) ) @@ -95,8 +97,10 @@ func (f *FSM) InitInstantOutAction(ctx context.Context, // Check if the reservation is locked. if res.State == reservation.Locked { - return f.HandleError(fmt.Errorf("reservation %v is "+ - "locked", reservationId)) + return f.HandleError( + fmt.Errorf("reservation %v is locked", + reservationId), + ) } reservationAmt += uint64(res.Value) @@ -107,9 +111,12 @@ func (f *FSM) InitInstantOutAction(ctx context.Context, // expiry of the swap, with an additional delta to allow for // preimage reveal. if int32(res.Expiry) < initCtx.cltvExpiry+htlcExpiryDelta { - return f.HandleError(fmt.Errorf("reservation %x has "+ - "expiry %v which is less than the swap expiry %v", - resId, res.Expiry, initCtx.cltvExpiry+htlcExpiryDelta)) + return f.HandleError( + fmt.Errorf("reservation %x has expiry %v "+ + "which is less than the swap "+ + "expiry %v", resId, res.Expiry, + initCtx.cltvExpiry+htlcExpiryDelta), + ) } } @@ -131,6 +138,7 @@ func (f *FSM) InitInstantOutAction(ctx context.Context, feeRate, err := f.cfg.Wallet.EstimateFeeRate(ctx, urgentConfTarget) if err != nil { f.Infof("error estimating fee rate: %v", err) + return f.HandleError(err) } @@ -158,8 +166,12 @@ func (f *FSM) InitInstantOutAction(ctx context.Context, } if swapHash != payReq.Hash { - return f.HandleError(fmt.Errorf("invalid swap invoice hash: "+ - "expected %x got %x", preimage.Hash(), payReq.Hash)) + return f.HandleError( + fmt.Errorf( + "invalid swap invoice hash: expected %x got %x", + preimage.Hash(), payReq.Hash, + ), + ) } serverPubkey, err := btcec.ParsePubKey(instantOutResponse.SenderKey) if err != nil { @@ -234,6 +246,7 @@ func (f *FSM) PollPaymentAcceptedAction(ctx context.Context, ) if err != nil { f.Errorf("error sending payment: %v", err) + return f.handleErrorAndUnlockReservations(ctx, err) } @@ -252,8 +265,10 @@ func (f *FSM) PollPaymentAcceptedAction(ctx context.Context, payRes.FailureReason), ) } + case err := <-paymentErrChan: f.Errorf("error sending payment: %v", err) + return f.handleErrorAndUnlockReservations(ctx, err) case <-ctx.Done(): @@ -269,7 +284,9 @@ func (f *FSM) PollPaymentAcceptedAction(ctx context.Context, if err != nil { pollPaymentTries++ if pollPaymentTries > 20 { - return f.handleErrorAndUnlockReservations(ctx, err) + return f.handleErrorAndUnlockReservations( + ctx, err, + ) } } if res != nil && res.Accepted { @@ -407,6 +424,7 @@ func (f *FSM) PushPreimageAction(ctx context.Context, // we'll need to publish the htlc tx. if err != nil { f.LastActionError = err + return OnErrorPublishHtlc } @@ -417,12 +435,14 @@ func (f *FSM) PushPreimageAction(ctx context.Context, ) if err != nil { f.LastActionError = err + return OnErrorPublishHtlc } coopServerNonces, err := toNonces(pushPreImageRes.ServerNonces) if err != nil { f.LastActionError = err + return OnErrorPublishHtlc } @@ -433,6 +453,7 @@ func (f *FSM) PushPreimageAction(ctx context.Context, ) if err != nil { f.LastActionError = err + return OnErrorPublishHtlc } @@ -443,6 +464,7 @@ func (f *FSM) PushPreimageAction(ctx context.Context, ) if err != nil { f.LastActionError = err + return OnErrorPublishHtlc } @@ -453,6 +475,7 @@ func (f *FSM) PushPreimageAction(ctx context.Context, err = f.cfg.Wallet.PublishTransaction(ctx, sweepTx, txLabel) if err != nil { f.LastActionError = err + return OnErrorPublishHtlc } @@ -476,8 +499,8 @@ func (f *FSM) WaitForSweeplessSweepConfirmedAction(ctx context.Context, confChan, confErrChan, err := f.cfg.ChainNotifier. RegisterConfirmationsNtfn( - ctx, f.InstantOut.SweepTxHash, pkscript, - 1, f.InstantOut.initiationHeight, + ctx, f.InstantOut.SweepTxHash, pkscript, 1, + f.InstantOut.initiationHeight, ) if err != nil { return f.HandleError(err) @@ -522,8 +545,8 @@ func (f *FSM) PublishHtlcAction(ctx context.Context, confChan, confErrChan, err := f.cfg.ChainNotifier. RegisterConfirmationsNtfn( ctx, &txHash, - f.InstantOut.finalizedHtlcTx.TxOut[0].PkScript, - 1, f.InstantOut.initiationHeight, + f.InstantOut.finalizedHtlcTx.TxOut[0].PkScript, 1, + f.InstantOut.initiationHeight, ) if err != nil { return f.HandleError(err) @@ -567,6 +590,7 @@ func (f *FSM) PublishHtlcSweepAction(ctx context.Context, err = f.cfg.Wallet.PublishTransaction(ctx, htlcSweepTx, label) if err != nil { log.Errorf("error publishing htlc sweep tx: %v", err) + return f.HandleError(err) } @@ -590,8 +614,8 @@ func (f *FSM) WaitForHtlcSweepConfirmedAction(ctx context.Context, } confChan, confErrChan, err := f.cfg.ChainNotifier.RegisterConfirmationsNtfn( - ctx, f.InstantOut.SweepTxHash, sweepPkScript, - 1, f.InstantOut.initiationHeight, + ctx, f.InstantOut.SweepTxHash, sweepPkScript, 1, + f.InstantOut.initiationHeight, ) if err != nil { return f.HandleError(err) @@ -618,6 +642,7 @@ func (f *FSM) WaitForHtlcSweepConfirmedAction(ctx context.Context, // reservations. func (f *FSM) handleErrorAndUnlockReservations(ctx context.Context, err error) fsm.EventType { + // We might get here from a canceled context, we create a new context // with a timeout to unlock the reservations. ctx, cancel := context.WithTimeout(ctx, time.Second*30) @@ -630,6 +655,7 @@ func (f *FSM) handleErrorAndUnlockReservations(ctx context.Context, ) if err != nil { f.Errorf("error unlocking reservation: %v", err) + return f.HandleError(err) } } @@ -646,8 +672,8 @@ func (f *FSM) handleErrorAndUnlockReservations(ctx context.Context, }, ) if cancelErr != nil { - // We'll log the error but not return it as we want to return the - // original error. + // We'll log the error but not return it as we want to + // return the original error. f.Debugf("error sending cancel message: %v", cancelErr) } }() diff --git a/instantout/fsm.go b/instantout/fsm.go index c188b1582..334d60109 100644 --- a/instantout/fsm.go +++ b/instantout/fsm.go @@ -38,9 +38,8 @@ const ( ) var ( - ErrProtocolVersionNotSupported = errors.New( - "protocol version not supported", - ) + ErrProtocolVersionNotSupported = errors.New("protocol version not " + + "supported") ) // States. @@ -61,7 +60,8 @@ var ( // WaitForSweeplessSweepConfirmed is the state where we wait for the // sweepless sweep to be confirmed. WaitForSweeplessSweepConfirmed = fsm.StateType( - "WaitForSweeplessSweepConfirmed") + "WaitForSweeplessSweepConfirmed", + ) // FinishedSweeplessSweep is the state where the swap is finished by // publishing the sweepless sweep. @@ -340,48 +340,36 @@ func (f *FSM) updateInstantOut(ctx context.Context, f.InstantOut.State == fsm.EmptyState || (notification.PreviousState == Init && f.InstantOut.State == Failed) { - return } err := f.cfg.Store.UpdateInstantLoopOut(ctx, f.InstantOut) if err != nil { log.Errorf("Error updating instant out: %v", err) + return } } // Infof logs an info message with the reservation hash as prefix. func (f *FSM) Infof(format string, args ...any) { - log.Infof( - "InstantOut %v: "+format, - append( - []any{f.InstantOut.swapPreimage.Hash()}, - args..., - )..., - ) + log.Infof("InstantOut %v: "+format, append( + []any{f.InstantOut.swapPreimage.Hash()}, args..., + )...) } // Debugf logs a debug message with the reservation hash as prefix. func (f *FSM) Debugf(format string, args ...any) { - log.Debugf( - "InstantOut %v: "+format, - append( - []any{f.InstantOut.swapPreimage.Hash()}, - args..., - )..., - ) + log.Debugf("InstantOut %v: "+format, append( + []any{f.InstantOut.swapPreimage.Hash()}, args..., + )...) } // Errorf logs an error message with the reservation hash as prefix. func (f *FSM) Errorf(format string, args ...any) { - log.Errorf( - "InstantOut %v: "+format, - append( - []any{f.InstantOut.swapPreimage.Hash()}, - args..., - )..., - ) + log.Errorf("InstantOut %v: "+format, append( + []any{f.InstantOut.swapPreimage.Hash()}, args..., + )...) } // isFinalState returns true if the state is a final state. @@ -389,8 +377,8 @@ func isFinalState(state fsm.StateType) bool { switch state { case Failed, FinishedHtlcPreimageSweep, FinishedSweeplessSweep: - return true } + return false } diff --git a/instantout/instantout.go b/instantout/instantout.go index f8c89eb0c..4b555c41d 100644 --- a/instantout/instantout.go +++ b/instantout/instantout.go @@ -101,8 +101,8 @@ func (i *InstantOut) getHtlc(chainParams *chaincfg.Params) (*swap.Htlc, error) { // createMusig2Session creates a musig2 session for the instant out. func (i *InstantOut) createMusig2Session(ctx context.Context, - signer lndclient.SignerClient) ([]*input.MuSig2SessionInfo, - [][]byte, error) { + signer lndclient.SignerClient) ([]*input.MuSig2SessionInfo, [][]byte, + error) { // Create the htlc musig2 context. musig2Sessions := make([]*input.MuSig2SessionInfo, len(i.Reservations)) @@ -181,8 +181,7 @@ func (i *InstantOut) createHtlcTransaction(network *chaincfg.Params, return nil, err } if clamped { - return nil, errors.New("fee is higher than 20% of " + - "sweep value") + return nil, errors.New("fee is higher than 20% of sweep value") } htlc, err := i.getHtlc(network) @@ -203,8 +202,8 @@ func (i *InstantOut) createHtlcTransaction(network *chaincfg.Params, // createSweeplessSweepTx creates the sweepless sweep transaction for the // instant out. -func (i *InstantOut) createSweeplessSweepTx(feerate, - minRelayFeeRate chainfee.SatPerKWeight) (*wire.MsgTx, error) { +func (i *InstantOut) createSweeplessSweepTx( + feerate, minRelayFeeRate chainfee.SatPerKWeight) (*wire.MsgTx, error) { inputReservations, err := i.getInputReservations() if err != nil { @@ -232,8 +231,7 @@ func (i *InstantOut) createSweeplessSweepTx(feerate, return nil, err } if clamped { - return nil, errors.New("fee is higher than 20% of " + - "sweep value") + return nil, errors.New("fee is higher than 20% of sweep value") } pkscript, err := txscript.PayToAddrScript(i.sweepAddress) @@ -269,16 +267,16 @@ func (i *InstantOut) signMusig2Tx(ctx context.Context, sigs := make([][]byte, len(inputs)) for idx, reservation := range inputs { - if !reflect.DeepEqual(tx.TxIn[idx].PreviousOutPoint, - reservation.Outpoint) { - + if !reflect.DeepEqual( + tx.TxIn[idx].PreviousOutPoint, reservation.Outpoint, + ) { return nil, fmt.Errorf("tx input does not match " + "reservation") } taprootSigHash, err := txscript.CalcTaprootSignatureHash( - sigHashes, txscript.SigHashDefault, - tx, idx, prevOutFetcher, + sigHashes, txscript.SigHashDefault, tx, idx, + prevOutFetcher, ) if err != nil { return nil, err @@ -322,8 +320,8 @@ func (i *InstantOut) signMusig2Tx(ctx context.Context, // the htlc or the cooperative close. func (i *InstantOut) finalizeMusig2Transaction(ctx context.Context, signer lndclient.SignerClient, - musig2Sessions []*input.MuSig2SessionInfo, - tx *wire.MsgTx, serverSigs [][]byte) (*wire.MsgTx, error) { + musig2Sessions []*input.MuSig2SessionInfo, tx *wire.MsgTx, + serverSigs [][]byte) (*wire.MsgTx, error) { inputs, err := i.getInputReservations() if err != nil { @@ -352,8 +350,7 @@ func (i *InstantOut) finalizeMusig2Transaction(ctx context.Context, // generateHtlcSweepTx creates the htlc sweep transaction for the instant out. func (i *InstantOut) generateHtlcSweepTx(ctx context.Context, signer lndclient.SignerClient, feeRate chainfee.SatPerKWeight, - network *chaincfg.Params, blockheight uint32) ( - *wire.MsgTx, error) { + network *chaincfg.Params, blockheight uint32) (*wire.MsgTx, error) { if network == nil { return nil, errors.New("no network provided") diff --git a/instantout/interfaces.go b/instantout/interfaces.go index 2f9018e72..517f63df7 100644 --- a/instantout/interfaces.go +++ b/instantout/interfaces.go @@ -25,7 +25,8 @@ type InstantLoopOutStore interface { // GetInstantLoopOut returns the instant loop out for the given swap // hash. - GetInstantLoopOut(ctx context.Context, swapHash []byte) (*InstantOut, error) + GetInstantLoopOut(ctx context.Context, + swapHash []byte) (*InstantOut, error) // ListInstantLoopOuts returns all instant loop outs that are in the // store. @@ -35,8 +36,8 @@ type InstantLoopOutStore interface { // ReservationManager handles fetching and locking of reservations. type ReservationManager interface { // GetReservation returns the reservation for the given id. - GetReservation(ctx context.Context, id reservation.ID) ( - *reservation.Reservation, error) + GetReservation(ctx context.Context, + id reservation.ID) (*reservation.Reservation, error) // LockReservation locks the reservation for the given id. LockReservation(ctx context.Context, id reservation.ID) error diff --git a/instantout/manager.go b/instantout/manager.go index 37ccb681b..9dae2c575 100644 --- a/instantout/manager.go +++ b/instantout/manager.go @@ -65,13 +65,17 @@ func (m *Manager) Run(ctx context.Context, initChan chan struct{}) error { err := m.recoverInstantOuts(runCtx) if err != nil { close(initChan) + return err } newBlockChan, newBlockErrChan, err := m.cfg.ChainNotifier. - RegisterBlockEpochNtfn(ctx) + RegisterBlockEpochNtfn( + ctx, + ) if err != nil { close(initChan) + return err } @@ -163,6 +167,7 @@ func (m *Manager) NewInstantOut(ctx context.Context, instantOut, err := NewFSM(m.cfg, ProtocolVersionFullReservation) if err != nil { m.Unlock() + return nil, err } m.activeInstantOuts[instantOut.InstantOut.SwapHash] = instantOut @@ -217,8 +222,8 @@ type Quote struct { } // GetInstantOutQuote returns a quote for an instant out. -func (m *Manager) GetInstantOutQuote(ctx context.Context, - amt btcutil.Amount, reservationIDs [][]byte) (Quote, error) { +func (m *Manager) GetInstantOutQuote(ctx context.Context, amt btcutil.Amount, + reservationIDs [][]byte) (Quote, error) { if len(reservationIDs) == 0 { return Quote{}, fmt.Errorf("no reservations selected") @@ -248,7 +253,11 @@ func (m *Manager) GetInstantOutQuote(ctx context.Context, // The on chain chainFee is the chainFee rate times the estimated // sweepless sweep transaction size. - chainFee := feeRate.FeeForWeight(sweeplessSweepWeight(len(reservationIDs))) + chainFee := feeRate.FeeForWeight( + sweeplessSweepWeight( + len(reservationIDs), + ), + ) return Quote{ ServiceFee: btcutil.Amount(quoteRes.SwapFee), diff --git a/instantout/reservation/actions.go b/instantout/reservation/actions.go index 9e62c0151..92c67f11f 100644 --- a/instantout/reservation/actions.go +++ b/instantout/reservation/actions.go @@ -52,12 +52,9 @@ func (f *FSM) InitAction(ctx context.Context, reservation, err := NewReservation( reservationRequest.reservationID, - reservationRequest.serverPubkey, - keyRes.PubKey, - reservationRequest.value, - reservationRequest.expiry, - reservationRequest.heightHint, - keyRes.KeyLocator, + reservationRequest.serverPubkey, keyRes.PubKey, + reservationRequest.value, reservationRequest.expiry, + reservationRequest.heightHint, keyRes.KeyLocator, ProtocolVersionServerInitiated, ) if err != nil { @@ -100,6 +97,7 @@ func (f *FSM) SubscribeToConfirmationAction(ctx context.Context, ) if err != nil { f.Errorf("unable to subscribe to conf notification: %v", err) + return f.HandleError(err) } @@ -108,6 +106,7 @@ func (f *FSM) SubscribeToConfirmationAction(ctx context.Context, ) if err != nil { f.Errorf("unable to subscribe to block notifications: %v", err) + return f.HandleError(err) } @@ -116,10 +115,12 @@ func (f *FSM) SubscribeToConfirmationAction(ctx context.Context, select { case err := <-errConfChan: f.Errorf("conf subscription error: %v", err) + return f.HandleError(err) case err := <-errBlockChan: f.Errorf("block subscription error: %v", err) + return f.HandleError(err) case confInfo := <-confChan: @@ -159,15 +160,19 @@ func (f *FSM) AsyncWaitForExpiredOrSweptAction(ctx context.Context, notifCtx, cancel := context.WithCancel(ctx) blockHeightChan, errEpochChan, err := f.cfg.ChainNotifier. - RegisterBlockEpochNtfn(notifCtx) + RegisterBlockEpochNtfn( + notifCtx, + ) if err != nil { cancel() + return f.HandleError(err) } pkScript, err := f.reservation.GetPkScript() if err != nil { cancel() + return f.HandleError(err) } @@ -177,6 +182,7 @@ func (f *FSM) AsyncWaitForExpiredOrSweptAction(ctx context.Context, ) if err != nil { cancel() + return f.HandleError(err) } @@ -188,6 +194,7 @@ func (f *FSM) AsyncWaitForExpiredOrSweptAction(ctx context.Context, ) if err != nil { f.handleAsyncError(ctx, err) + return } if op == fsm.NoOp { @@ -204,8 +211,8 @@ func (f *FSM) AsyncWaitForExpiredOrSweptAction(ctx context.Context, func (f *FSM) handleSubcriptions(ctx context.Context, blockHeightChan <-chan int32, spendChan <-chan *chainntnfs.SpendDetail, - errEpochChan <-chan error, errSpendChan <-chan error, -) (fsm.EventType, error) { + errEpochChan <-chan error, errSpendChan <-chan error) (fsm.EventType, + error) { for { select { @@ -220,6 +227,7 @@ func (f *FSM) handleSubcriptions(ctx context.Context, if expired { f.Debugf("Reservation expired") + return OnTimedOut, nil } diff --git a/instantout/reservation/actions_test.go b/instantout/reservation/actions_test.go index 40e6509b1..5815717f5 100644 --- a/instantout/reservation/actions_test.go +++ b/instantout/reservation/actions_test.go @@ -23,8 +23,11 @@ import ( ) var ( - defaultPubkeyBytes, _ = hex.DecodeString("021c97a90a411ff2b10dc2a8e32de2f29d2fa49d41bfbb52bd416e460db0747d0d") - defaultPubkey, _ = btcec.ParsePubKey(defaultPubkeyBytes) + defaultPubkeyBytes, _ = hex.DecodeString( + "021c97a90a411ff2b10dc2a8e32de2f29d2fa49d41bfbb52bd416e460db" + + "0747d0d", + ) + defaultPubkey, _ = btcec.ParsePubKey(defaultPubkeyBytes) defaultValue = btcutil.Amount(100) @@ -33,11 +36,13 @@ var ( func newValidInitReservationContext() *InitReservationContext { return &InitReservationContext{ - reservationID: ID{0x01}, - serverPubkey: defaultPubkey, - value: defaultValue, - expiry: defaultExpiry, - heightHint: 0, + reservationID: ID{ + 0x01, + }, + serverPubkey: defaultPubkey, + value: defaultValue, + expiry: defaultExpiry, + heightHint: 0, } } @@ -51,11 +56,12 @@ type mockReservationClient struct { func (m *mockReservationClient) ReservationNotificationStream( ctx context.Context, in *swapserverrpc.ReservationNotificationRequest, - opts ...grpc.CallOption, -) (swapserverrpc.ReservationService_ReservationNotificationStreamClient, + opts ...grpc.CallOption) ( + swapserverrpc.ReservationService_ReservationNotificationStreamClient, error) { args := m.Called(ctx, in, opts) + return args.Get(0).(swapserverrpc.ReservationService_ReservationNotificationStreamClient), args.Error(1) } @@ -66,13 +72,14 @@ func (m *mockReservationClient) OpenReservation(ctx context.Context, error) { args := m.Called(ctx, in, opts) + return args.Get(0).(*swapserverrpc.ServerOpenReservationResponse), args.Error(1) } func (m *mockReservationClient) FetchL402(ctx context.Context, - in *swapserverrpc.FetchL402Request, - opts ...grpc.CallOption) (*swapserverrpc.FetchL402Response, error) { + in *swapserverrpc.FetchL402Request, opts ...grpc.CallOption) ( + *swapserverrpc.FetchL402Response, error) { args := m.Called(ctx, in, opts) @@ -90,6 +97,7 @@ func (m *mockStore) CreateReservation(ctx context.Context, reservation *Reservation) error { args := m.Called(ctx, reservation) + return args.Error(0) } @@ -163,9 +171,8 @@ type MockChainNotifier struct { mock.Mock } -func (m *MockChainNotifier) RawClientWithMacAuth( - ctx context.Context) (context.Context, time.Duration, - chainrpc.ChainNotifierClient) { +func (m *MockChainNotifier) RawClientWithMacAuth(ctx context.Context) ( + context.Context, time.Duration, chainrpc.ChainNotifierClient) { return ctx, 0, nil } @@ -176,23 +183,30 @@ func (m *MockChainNotifier) RegisterConfirmationsNtfn(ctx context.Context, chan error, error) { args := m.Called(ctx, txid, pkScript, numConfs, heightHint) - return args.Get(0).(chan *chainntnfs.TxConfirmation), args.Get(1).(chan error), args.Error(2) + + return args.Get(0).(chan *chainntnfs.TxConfirmation), args.Get(1).(chan error), args.Error( + 2, + ) } func (m *MockChainNotifier) RegisterBlockEpochNtfn(ctx context.Context) ( chan int32, chan error, error) { args := m.Called(ctx) + return args.Get(0).(chan int32), args.Get(1).(chan error), args.Error(2) } func (m *MockChainNotifier) RegisterSpendNtfn(ctx context.Context, outpoint *wire.OutPoint, pkScript []byte, heightHint int32, - _ ...lndclient.NotifierOption) ( - chan *chainntnfs.SpendDetail, chan error, error) { + _ ...lndclient.NotifierOption) (chan *chainntnfs.SpendDetail, + chan error, error) { args := m.Called(ctx, pkScript, heightHint) - return args.Get(0).(chan *chainntnfs.SpendDetail), args.Get(1).(chan error), args.Error(2) + + return args.Get(0).(chan *chainntnfs.SpendDetail), args.Get(1).(chan error), args.Error( + 2, + ) } // TestSubscribeToConfirmationAction tests the SubscribeToConfirmationAction of @@ -261,9 +275,9 @@ func TestSubscribeToConfirmationAction(t *testing.T) { mock.Anything, mock.Anything, mock.Anything, ).Return(confChan, confErrChan, nil) - chainNotifier.On("RegisterBlockEpochNtfn", mock.Anything).Return( - blockChan, blockErrChan, nil, - ) + chainNotifier. + On("RegisterBlockEpochNtfn", mock.Anything). + Return(blockChan, blockErrChan, nil) go func() { // Send the tx confirmation. @@ -273,7 +287,9 @@ func TestSubscribeToConfirmationAction(t *testing.T) { TxIn: []*wire.TxIn{}, TxOut: []*wire.TxOut{ { - Value: int64(defaultValue), + Value: int64( + defaultValue, + ), PkScript: pkScript, }, }, @@ -307,7 +323,8 @@ func TestSubscribeToConfirmationAction(t *testing.T) { // Assert that the return value is as expected require.Equal(t, tc.expectedEvent, eventType) - // Assert that the expected functions were called on the mocks + // Assert that the expected functions were called on the + // mocks chainNotifier.AssertExpectations(t) }) } @@ -357,9 +374,13 @@ func TestAsyncWaitForExpiredOrSweptAction(t *testing.T) { ) // Define the expected return values for your mocks - chainNotifier.On("RegisterBlockEpochNtfn", mock.Anything).Return( - make(chan int32), make(chan error), tc.blockErr, - ) + chainNotifier. + On("RegisterBlockEpochNtfn", mock.Anything). + Return( + make(chan int32), + make(chan error), + tc.blockErr, + ) chainNotifier.On( "RegisterSpendNtfn", mock.Anything, @@ -369,7 +390,9 @@ func TestAsyncWaitForExpiredOrSweptAction(t *testing.T) { make(chan error), tc.spendErr, ) - eventType := r.AsyncWaitForExpiredOrSweptAction(ctxb, nil) + eventType := r.AsyncWaitForExpiredOrSweptAction( + ctxb, nil, + ) // Assert that the return value is as expected require.Equal(t, tc.expectedEvent, eventType) }) diff --git a/instantout/reservation/fsm.go b/instantout/reservation/fsm.go index 946d5102d..033a541ac 100644 --- a/instantout/reservation/fsm.go +++ b/instantout/reservation/fsm.go @@ -235,11 +235,9 @@ func (r *FSM) updateReservation(ctx context.Context, return } - r.Debugf( - "NextState: %v, PreviousState: %v, Event: %v", + r.Debugf("NextState: %v, PreviousState: %v, Event: %v", notification.NextState, notification.PreviousState, - notification.Event, - ) + notification.Event) r.reservation.State = notification.NextState @@ -249,7 +247,6 @@ func (r *FSM) updateReservation(ctx context.Context, r.reservation.State == Init || (notification.PreviousState == Init && r.reservation.State == Failed) { - return } @@ -260,27 +257,21 @@ func (r *FSM) updateReservation(ctx context.Context, } func (r *FSM) Infof(format string, args ...any) { - log.Infof( - "Reservation %v %x: "+format, - append([]any{r.reservation.ProtocolVersion, r.reservation.ID}, - args...)..., - ) + log.Infof("Reservation %v %x: "+format, append( + []any{r.reservation.ProtocolVersion, r.reservation.ID}, args..., + )...) } func (r *FSM) Debugf(format string, args ...any) { - log.Debugf( - "Reservation %v %x: "+format, - append([]any{r.reservation.ProtocolVersion, r.reservation.ID}, - args...)..., - ) + log.Debugf("Reservation %v %x: "+format, append( + []any{r.reservation.ProtocolVersion, r.reservation.ID}, args..., + )...) } func (r *FSM) Errorf(format string, args ...any) { - log.Errorf( - "Reservation %v %x: "+format, - append([]any{r.reservation.ProtocolVersion, r.reservation.ID}, - args...)..., - ) + log.Errorf("Reservation %v %x: "+format, append( + []any{r.reservation.ProtocolVersion, r.reservation.ID}, args..., + )...) } // isFinalState returns true if the state is a final state. @@ -289,5 +280,6 @@ func isFinalState(state fsm.StateType) bool { case Failed, TimedOut, Spent: return true } + return false } diff --git a/instantout/reservation/manager.go b/instantout/reservation/manager.go index 600febfe9..379351f3a 100644 --- a/instantout/reservation/manager.go +++ b/instantout/reservation/manager.go @@ -50,7 +50,9 @@ func (m *Manager) Run(ctx context.Context, height int32, } newBlockChan, newBlockErrChan, err := m.cfg.ChainNotifier. - RegisterBlockEpochNtfn(runCtx) + RegisterBlockEpochNtfn( + runCtx, + ) if err != nil { return err } @@ -70,7 +72,9 @@ func (m *Manager) Run(ctx context.Context, height int32, if !ok { // The channel has been closed, we'll stop the // reservation manager. - log.Debugf("Stopping reservation manager (ntfnChan closed)") + log.Debugf("Stopping reservation manager " + + "(ntfnChan closed)") + return nil } @@ -88,6 +92,7 @@ func (m *Manager) Run(ctx context.Context, height int32, case <-runCtx.Done(): log.Debugf("Stopping reservation manager") + return nil } } @@ -130,7 +135,9 @@ func (m *Manager) newReservation(ctx context.Context, currentHeight uint32, // Send the init event to the state machine. go func() { - err = reservationFSM.SendEvent(ctx, OnServerRequest, initContext) + err = reservationFSM.SendEvent( + ctx, OnServerRequest, initContext, + ) if err != nil { log.Errorf("Error sending init event: %v", err) } @@ -144,10 +151,11 @@ func (m *Manager) newReservation(ctx context.Context, currentHeight uint32, ) if err != nil { if reservationFSM.LastActionError != nil { - return nil, fmt.Errorf("error waiting for "+ - "state: %v, last action error: %v", - err, reservationFSM.LastActionError) + return nil, fmt.Errorf("error waiting for state: %v, "+ + "last action error: %v", err, + reservationFSM.LastActionError) } + return nil, err } @@ -237,6 +245,7 @@ func (m *Manager) UnlockReservation(ctx context.Context, id ID) error { // Try to send the unlock event to the reservation. err := reservation.SendEvent(ctx, OnUnlocked, nil) if err != nil && strings.Contains(err.Error(), "config error") { + // If the error is a config error, we can ignore it, as the // reservation is already unlocked. return nil diff --git a/instantout/reservation/manager_test.go b/instantout/reservation/manager_test.go index 79455750c..e6be3dfc0 100644 --- a/instantout/reservation/manager_test.go +++ b/instantout/reservation/manager_test.go @@ -17,7 +17,10 @@ import ( ) var ( - defaultReservationId = mustDecodeID("17cecc61ab4aafebdc0542dabdae0d0cb8907ec1c9c8ae387bc5a3309ca8b600") + defaultReservationId = mustDecodeID( + "17cecc61ab4aafebdc0542dabdae0d0cb8907ec1c9c8ae387bc5a3309ca" + + "8b600", + ) ) func TestManager(t *testing.T) { @@ -28,7 +31,9 @@ func TestManager(t *testing.T) { initChan := make(chan struct{}) // Start the manager. go func() { - err := testContext.manager.Run(ctxb, testContext.mockLnd.Height, initChan) + err := testContext.manager.Run( + ctxb, testContext.mockLnd.Height, initChan, + ) require.NoError(t, err) }() @@ -42,7 +47,8 @@ func TestManager(t *testing.T) { ReservationId: defaultReservationId[:], Value: uint64(defaultValue), ServerKey: defaultPubkeyBytes, - Expiry: uint32(testContext.mockLnd.Height) + defaultExpiry, + Expiry: uint32(testContext.mockLnd.Height) + + defaultExpiry, }, ) require.NoError(t, err) @@ -68,7 +74,9 @@ func TestManager(t *testing.T) { } // We'll now expect the reservation to be confirmed. - err = reservationFSM.DefaultObserver.WaitForState(ctxb, 5*time.Second, Confirmed) + err = reservationFSM.DefaultObserver.WaitForState( + ctxb, 5*time.Second, Confirmed, + ) require.NoError(t, err) // We'll now expect a spend registration. @@ -95,7 +103,9 @@ func TestManager(t *testing.T) { } // We'll now expect the reservation to be expired. - err = reservationFSM.DefaultObserver.WaitForState(ctxb, 5*time.Second, Spent) + err = reservationFSM.DefaultObserver.WaitForState( + ctxb, 5*time.Second, Spent, + ) require.NoError(t, err) } @@ -169,5 +179,6 @@ func mustDecodeID(id string) ID { } var decoded ID copy(decoded[:], bytes) + return decoded } diff --git a/instantout/reservation/reservation.go b/instantout/reservation/reservation.go index 5a167d2e1..d94a2ac8f 100644 --- a/instantout/reservation/reservation.go +++ b/instantout/reservation/reservation.go @@ -99,6 +99,7 @@ func NewReservation(id ID, serverPubkey, clientPubkey *btcec.PublicKey, if keyLocator.Family == 0 { return nil, errors.New("key locator family is 0") } + return &Reservation{ ID: id, Value: value, diff --git a/instantout/reservation/script/script.go b/instantout/reservation/script/script.go index a86504f52..3d1bd46bf 100644 --- a/instantout/reservation/script/script.go +++ b/instantout/reservation/script/script.go @@ -34,13 +34,14 @@ const ( // - : 39 bytes // - control_block_varint_len: 1 byte (control block length) // - : 33 bytes - TaprootExpiryWitnessSize = 1 + 1 + 64 + 1 + TaprootExpiryScriptSize + 1 + 33 + TaprootExpiryWitnessSize = 1 + 1 + 64 + 1 + TaprootExpiryScriptSize + + 1 + 33 ) // ReservationScript returns the tapscript pkscript for the given reservation // parameters. -func ReservationScript(expiry uint32, serverKey, - clientKey *btcec.PublicKey) ([]byte, error) { +func ReservationScript(expiry uint32, serverKey, clientKey *btcec.PublicKey) ( + []byte, error) { aggregatedKey, err := TaprootKey(expiry, serverKey, clientKey) if err != nil { @@ -51,8 +52,8 @@ func ReservationScript(expiry uint32, serverKey, } // TaprootKey returns the aggregated MuSig2 combined key. -func TaprootKey(expiry uint32, serverKey, - clientKey *btcec.PublicKey) (*musig2.AggregateKey, error) { +func TaprootKey(expiry uint32, serverKey, clientKey *btcec.PublicKey) ( + *musig2.AggregateKey, error) { expiryLeaf, err := TaprootExpiryScript(expiry, serverKey) if err != nil { @@ -108,6 +109,7 @@ func TaprootExpiryScript(expiry uint32, } leaf := txscript.NewBaseTapLeaf(script) + return &leaf, nil } diff --git a/instantout/reservation/store.go b/instantout/reservation/store.go index 117d02c69..d0d3f0fae 100644 --- a/instantout/reservation/store.go +++ b/instantout/reservation/store.go @@ -144,8 +144,8 @@ func (r *SQLStore) UpdateReservation(ctx context.Context, } // GetReservation retrieves the reservation from the database. -func (r *SQLStore) GetReservation(ctx context.Context, - reservationId ID) (*Reservation, error) { +func (r *SQLStore) GetReservation(ctx context.Context, reservationId ID) ( + *Reservation, error) { var reservation *Reservation err := r.baseDb.ExecTx(ctx, loopdb.NewSqlReadOpts(), @@ -210,9 +210,8 @@ func (r *SQLStore) ListReservations(ctx context.Context) ([]*Reservation, } if len(reservationUpdates) == 0 { - return errors.New( - "no reservation updates", - ) + return errors.New("no reservation " + + "updates") } res, err := sqlReservationToReservation( @@ -238,8 +237,7 @@ func (r *SQLStore) ListReservations(ctx context.Context) ([]*Reservation, // sqlReservationToReservation converts a sql reservation to a reservation. func sqlReservationToReservation(row sqlc.Reservation, - lastUpdate sqlc.ReservationUpdate) (*Reservation, - error) { + lastUpdate sqlc.ReservationUpdate) (*Reservation, error) { id := ID{} err := id.FromByteSlice(row.ReservationID) @@ -268,7 +266,10 @@ func sqlReservationToReservation(row sqlc.Reservation, var outpoint *wire.OutPoint if row.OutIndex.Valid { outpoint = wire.NewOutPoint( - txHash, uint32(unmarshalSqlNullInt32(row.OutIndex)), + txHash, + uint32( + unmarshalSqlNullInt32(row.OutIndex), + ), ) } diff --git a/instantout/reservation/store_test.go b/instantout/reservation/store_test.go index e5ed49636..ba8918e59 100644 --- a/instantout/reservation/store_test.go +++ b/instantout/reservation/store_test.go @@ -55,7 +55,9 @@ func TestSqlStore(t *testing.T) { // Add an outpoint to the reservation and compare it. reservation.Outpoint = &wire.OutPoint{ - Hash: chainhash.Hash{0x01}, + Hash: chainhash.Hash{ + 0x01, + }, Index: 0, } reservation.State = Confirmed @@ -93,5 +95,6 @@ func TestSqlStore(t *testing.T) { func getRandomReservationID() ID { var id ID rand.Read(id[:]) // nolint: errcheck + return id } diff --git a/instantout/store.go b/instantout/store.go index 25d7fe704..2a15bad45 100644 --- a/instantout/store.go +++ b/instantout/store.go @@ -51,8 +51,10 @@ type Querier interface { swapHash []byte) ([]sqlc.InstantoutUpdate, error) // GetInstantOutSwaps retrieves all instant out swaps. - GetInstantOutSwaps(ctx context.Context) ([]sqlc.GetInstantOutSwapsRow, - error) + GetInstantOutSwaps(ctx context.Context) ( + []sqlc.GetInstantOutSwapsRow, + error, + ) } // InstantOutBaseDB is the interface that contains all the queries generated @@ -70,8 +72,8 @@ type InstantOutBaseDB interface { // based on the stored reservation ids. type ReservationStore interface { // GetReservation returns the reservation for the given id. - GetReservation(ctx context.Context, id reservation.ID) ( - *reservation.Reservation, error) + GetReservation(ctx context.Context, + id reservation.ID) (*reservation.Reservation, error) } type SQLStore struct { @@ -301,9 +303,11 @@ func (s *SQLStore) sqlInstantOutToInstantOut(ctx context.Context, var finalizedHtlcTx *wire.MsgTx if row.FinalizedHtlcTx != nil { finalizedHtlcTx = &wire.MsgTx{} - err := finalizedHtlcTx.Deserialize(bytes.NewReader( - row.FinalizedHtlcTx, - )) + err := finalizedHtlcTx.Deserialize( + bytes.NewReader( + row.FinalizedHtlcTx, + ), + ) if err != nil { return nil, err } @@ -312,9 +316,11 @@ func (s *SQLStore) sqlInstantOutToInstantOut(ctx context.Context, var finalizedSweepLessSweepTx *wire.MsgTx if row.FinalizedSweeplessSweepTx != nil { finalizedSweepLessSweepTx = &wire.MsgTx{} - err := finalizedSweepLessSweepTx.Deserialize(bytes.NewReader( - row.FinalizedSweeplessSweepTx, - )) + err := finalizedSweepLessSweepTx.Deserialize( + bytes.NewReader( + row.FinalizedSweeplessSweepTx, + ), + ) if err != nil { return nil, err } @@ -372,17 +378,21 @@ func (s *SQLStore) sqlInstantOutToInstantOut(ctx context.Context, Family: keychain.KeyFamily(row.ClientKeyFamily), Index: uint32(row.ClientKeyIndex), }, - clientPubkey: clientKey, - serverPubkey: serverKey, - swapInvoice: row.SwapInvoice, - htlcFeeRate: chainfee.SatPerKWeight(row.HtlcFeeRate), + clientPubkey: clientKey, + serverPubkey: serverKey, + swapInvoice: row.SwapInvoice, + htlcFeeRate: chainfee.SatPerKWeight( + row.HtlcFeeRate, + ), sweepAddress: sweepAddress, finalizedHtlcTx: finalizedHtlcTx, SweepTxHash: sweepTxHash, FinalizedSweeplessSweepTx: finalizedSweepLessSweepTx, - sweepConfirmationHeight: uint32(deserializeNullInt32( - row.SweepConfirmationHeight, - )), + sweepConfirmationHeight: uint32( + deserializeNullInt32( + row.SweepConfirmationHeight, + ), + ), } if len(updates) > 0 { diff --git a/instantout/store_test.go b/instantout/store_test.go index c8e6c33ff..2ec5d52b8 100644 --- a/instantout/store_test.go +++ b/instantout/store_test.go @@ -21,7 +21,11 @@ func TestConvertingReservations(t *testing.T) { } reservations := []*reservation.Reservation{ - {ID: resId1}, {ID: resId2}, + { + ID: resId1, + }, { + ID: resId2, + }, } byteSlice := reservationIdsToByteSlice(reservations) diff --git a/interface.go b/interface.go index 10a86bfae..c6b379c91 100644 --- a/interface.go +++ b/interface.go @@ -240,7 +240,8 @@ type LoopOutRfq struct { // to pay for the swap invoice. MaxSwapAssetAmt uint64 - // SwapAssetRate is the rate at which the asset is exchanged for bitcoin. + // SwapAssetRate is the rate at which the asset is exchanged for + // bitcoin. SwapAssetRate *rfqmath.BigIntFixedPoint // AssetName is the human readable name of the asset. diff --git a/liquidity/autoloop_test.go b/liquidity/autoloop_test.go index d0b7ecc02..4e14ef255 100644 --- a/liquidity/autoloop_test.go +++ b/liquidity/autoloop_test.go @@ -288,7 +288,9 @@ func TestAutoLoopEnabled(t *testing.T) { { request: chan1Swap, response: &loop.LoopOutSwapInfo{ - SwapHash: lntypes.Hash{3}, + SwapHash: lntypes.Hash{ + 3, + }, }, }, } @@ -736,7 +738,9 @@ func TestAutoLoopInEnabled(t *testing.T) { MaxAutoInFlight: 2, FailureBackOff: time.Hour, FeeLimit: NewFeePortion(swapFeePPM), - ChannelRules: make(map[lnwire.ShortChannelID]*SwapRule), + ChannelRules: make( + map[lnwire.ShortChannelID]*SwapRule, + ), PeerRules: map[route.Vertex]*SwapRule{ peer1: rule, peer2: rule, @@ -804,7 +808,9 @@ func TestAutoLoopInEnabled(t *testing.T) { { request: peer1Swap, response: &loop.LoopInSwapInfo{ - SwapHash: lntypes.Hash{1}, + SwapHash: lntypes.Hash{ + 1, + }, }, }, }, @@ -851,7 +857,9 @@ func TestAutoLoopInEnabled(t *testing.T) { { request: peer2Swap, response: &loop.LoopInSwapInfo{ - SwapHash: lntypes.Hash{2}, + SwapHash: lntypes.Hash{ + 2, + }, }, }, }, @@ -1027,7 +1035,9 @@ func TestAutoloopBothTypes(t *testing.T) { { request: loopOutSwap, response: &loop.LoopOutSwapInfo{ - SwapHash: lntypes.Hash{1}, + SwapHash: lntypes.Hash{ + 1, + }, }, }, }, @@ -1035,7 +1045,9 @@ func TestAutoloopBothTypes(t *testing.T) { { request: loopInSwap, response: &loop.LoopInSwapInfo{ - SwapHash: lntypes.Hash{2}, + SwapHash: lntypes.Hash{ + 2, + }, }, }, }, @@ -1599,23 +1611,25 @@ func TestEasyAssetAutoloop(t *testing.T) { c := newAutoloopTestCtx(t, params, channels, testRestrictions) // For testing, simply return asset units 1:1 to satoshis. assetPriceFunc := func(ctx context.Context, assetId string, - peerPubkey []byte, assetAmt uint64, minSatAmt btcutil.Amount) ( - btcutil.Amount, error) { + peerPubkey []byte, assetAmt uint64, + minSatAmt btcutil.Amount) (btcutil.Amount, error) { return btcutil.Amount(assetAmt), nil } c.manager.cfg.GetAssetPrice = assetPriceFunc c.start() - // In this scenario we expect a swap of maxAmt (here chosen as 50000) - // on our single asset channel. + // In this scenario we expect a swap of maxAmt (here chosen as + // 50000) on our single asset channel. maxAmt := 50000 chanSwap := &loop.OutRequest{ - Amount: btcutil.Amount(maxAmt), - DestAddr: addr, - OutgoingChanSet: loopdb.ChannelSet{assetChan.ChannelID}, - Label: labels.AutoloopLabel(swap.TypeOut), - Initiator: autoloopSwapInitiator, + Amount: btcutil.Amount(maxAmt), + DestAddr: addr, + OutgoingChanSet: loopdb.ChannelSet{ + assetChan.ChannelID, + }, + Label: labels.AutoloopLabel(swap.TypeOut), + Initiator: autoloopSwapInitiator, } quotesOut := []quoteRequestResp{ { @@ -1641,7 +1655,9 @@ func TestEasyAssetAutoloop(t *testing.T) { { request: chanSwap, response: &loop.LoopOutSwapInfo{ - SwapHash: lntypes.Hash{1}, + SwapHash: lntypes.Hash{ + 1, + }, }, }, } @@ -1685,8 +1701,9 @@ func TestEasyAssetAutoloop(t *testing.T) { CustomChannelData: customChanDataBytes1, } assetChan2 := lndclient.ChannelInfo{ - Active: true, - ChannelID: chanID2.ToUint64(), // different channel ID + Active: true, + // different channel ID + ChannelID: chanID2.ToUint64(), PubKeyBytes: peer2, CustomChannelData: customChanDataBytes2, } @@ -1713,22 +1730,25 @@ func TestEasyAssetAutoloop(t *testing.T) { c := newAutoloopTestCtx(t, params, channels, testRestrictions) assetPriceFunc := func(ctx context.Context, assetId string, - peerPubkey []byte, assetAmt uint64, minSatAmt btcutil.Amount) ( - btcutil.Amount, error) { + peerPubkey []byte, assetAmt uint64, + minSatAmt btcutil.Amount) (btcutil.Amount, error) { return btcutil.Amount(assetAmt), nil } c.manager.cfg.GetAssetPrice = assetPriceFunc c.start() - // Expect a swap on the channel with the higher local balance (assetChan2). + // Expect a swap on the channel with the higher local balance + // (assetChan2). maxAmt := 40000 chanSwap := &loop.OutRequest{ - Amount: btcutil.Amount(maxAmt), - DestAddr: addr, - OutgoingChanSet: loopdb.ChannelSet{assetChan2.ChannelID}, - Label: labels.AutoloopLabel(swap.TypeOut), - Initiator: autoloopSwapInitiator, + Amount: btcutil.Amount(maxAmt), + DestAddr: addr, + OutgoingChanSet: loopdb.ChannelSet{ + assetChan2.ChannelID, + }, + Label: labels.AutoloopLabel(swap.TypeOut), + Initiator: autoloopSwapInitiator, } quotesOut := []quoteRequestResp{ { @@ -1754,7 +1774,9 @@ func TestEasyAssetAutoloop(t *testing.T) { { request: chanSwap, response: &loop.LoopOutSwapInfo{ - SwapHash: lntypes.Hash{1}, + SwapHash: lntypes.Hash{ + 1, + }, }, }, } @@ -1827,8 +1849,8 @@ func TestEasyAssetAutoloop(t *testing.T) { c := newAutoloopTestCtx(t, params, channels, testRestrictions) assetPriceFunc := func(ctx context.Context, assetId string, - peerPubkey []byte, assetAmt uint64, minSatAmt btcutil.Amount) ( - btcutil.Amount, error) { + peerPubkey []byte, assetAmt uint64, + minSatAmt btcutil.Amount) (btcutil.Amount, error) { return btcutil.Amount(assetAmt), nil } @@ -1838,11 +1860,13 @@ func TestEasyAssetAutoloop(t *testing.T) { maxAmtAsset := 50000 assetSwap := &loop.OutRequest{ - Amount: btcutil.Amount(maxAmtAsset), - DestAddr: addr, - OutgoingChanSet: loopdb.ChannelSet{assetChan.ChannelID}, - Label: labels.AutoloopLabel(swap.TypeOut), - Initiator: autoloopSwapInitiator, + Amount: btcutil.Amount(maxAmtAsset), + DestAddr: addr, + OutgoingChanSet: loopdb.ChannelSet{ + assetChan.ChannelID, + }, + Label: labels.AutoloopLabel(swap.TypeOut), + Initiator: autoloopSwapInitiator, } quotesOut := []quoteRequestResp{ { @@ -1868,7 +1892,9 @@ func TestEasyAssetAutoloop(t *testing.T) { { request: assetSwap, response: &loop.LoopOutSwapInfo{ - SwapHash: lntypes.Hash{1}, + SwapHash: lntypes.Hash{ + 1, + }, }, }, } diff --git a/liquidity/autoloop_testcontext_test.go b/liquidity/autoloop_testcontext_test.go index 380e9cb39..70c9ec059 100644 --- a/liquidity/autoloop_testcontext_test.go +++ b/liquidity/autoloop_testcontext_test.go @@ -104,7 +104,8 @@ func newAutoloopTestCtx(t *testing.T, parameters Parameters, categories, ok := parameters.FeeLimit.(*FeeCategoryLimit) if ok { lnd.SetFeeEstimate( - parameters.SweepConfTarget, categories.SweepFeeRateLimit, + parameters.SweepConfTarget, + categories.SweepFeeRateLimit, ) } @@ -135,8 +136,8 @@ func newAutoloopTestCtx(t *testing.T, parameters Parameters, cfg := &Config{ AutoloopTicker: ticker.NewForce(DefaultAutoloopTicker), - Restrictions: func(_ context.Context, swapType swap.Type, initiator string) (*Restrictions, - error) { + Restrictions: func(_ context.Context, swapType swap.Type, + initiator string) (*Restrictions, error) { if swapType == swap.TypeOut { return <-testCtx.loopOutRestrictions, nil @@ -147,8 +148,8 @@ func newAutoloopTestCtx(t *testing.T, parameters Parameters, ListLoopOut: func(context.Context) ([]*loopdb.LoopOut, error) { return <-testCtx.loopOuts, nil }, - GetLoopOut: func(ctx context.Context, - hash lntypes.Hash) (*loopdb.LoopOut, error) { + GetLoopOut: func(ctx context.Context, hash lntypes.Hash) ( + *loopdb.LoopOut, error) { return testCtx.loopOutSingle, nil }, @@ -163,23 +164,23 @@ func newAutoloopTestCtx(t *testing.T, parameters Parameters, return <-testCtx.quotes, nil }, - LoopOut: func(_ context.Context, - req *loop.OutRequest) (*loop.LoopOutSwapInfo, - error) { + LoopOut: func(_ context.Context, req *loop.OutRequest) ( + *loop.LoopOutSwapInfo, error) { testCtx.outRequest <- req return <-testCtx.loopOut, nil }, LoopInQuote: func(_ context.Context, - req *loop.LoopInQuoteRequest) (*loop.LoopInQuote, error) { + req *loop.LoopInQuoteRequest) (*loop.LoopInQuote, + error) { testCtx.quoteRequestIn <- req return <-testCtx.quotesIn, nil }, - LoopIn: func(_ context.Context, - req *loop.LoopInRequest) (*loop.LoopInSwapInfo, error) { + LoopIn: func(_ context.Context, req *loop.LoopInRequest) ( + *loop.LoopInSwapInfo, error) { testCtx.inRequest <- req @@ -214,6 +215,7 @@ func newAutoloopTestCtx(t *testing.T, parameters Parameters, testCtx.manager.params.CustomPaymentCheckInterval = 150 * time.Millisecond <-done + return testCtx } @@ -398,8 +400,11 @@ func (c *autoloopTestCtx) easyautoloop(step *easyAutoloopStep, noop bool) { // If easy autoloop is not meant to be triggered we skip sending the // mock response for restrictions, as this is never called. if !noop { - // Send a mocked response from the server with the swap size limits. - c.loopOutRestrictions <- NewRestrictions(step.minAmt, step.maxAmt) + // Send a mocked response from the server with the swap size + // limits. + c.loopOutRestrictions <- NewRestrictions( + step.minAmt, step.maxAmt, + ) } for _, expected := range step.quotesOut { @@ -488,9 +493,7 @@ func (c *autoloopTestCtx) matchLoopOuts(swaps []loopOutRequestResp, // matchLoopIns checks that the actual loop in requests we got match the // expected ones. -func (c *autoloopTestCtx) matchLoopIns( - swaps []loopInRequestResp) bool { - +func (c *autoloopTestCtx) matchLoopIns(swaps []loopInRequestResp) bool { swapsCopy := make([]loopInRequestResp, len(swaps)) copy(swapsCopy, swaps) diff --git a/liquidity/fees.go b/liquidity/fees.go index dbc92acda..e70540068 100644 --- a/liquidity/fees.go +++ b/liquidity/fees.go @@ -135,20 +135,21 @@ func NewFeeCategoryLimit(swapFeePPM, routingFeePPM, prepayFeePPM uint64, } func defaultFeeCategoryLimit() *FeeCategoryLimit { - return NewFeeCategoryLimit(defaultSwapFeePPM, defaultRoutingFeePPM, + return NewFeeCategoryLimit( + defaultSwapFeePPM, defaultRoutingFeePPM, defaultPrepayRoutingFeePPM, defaultMaximumMinerFee, - defaultMaximumPrepay, defaultSweepFeeRateLimit) + defaultMaximumPrepay, defaultSweepFeeRateLimit, + ) } // String returns the string representation of our fee category limits. func (f *FeeCategoryLimit) String() string { - return fmt.Sprintf("fee categories: maximum prepay: %v, maximum "+ - "miner fee: %v, maximum swap fee ppm: %v, maximum "+ - "routing fee ppm: %v, maximum prepay routing fee ppm: %v,"+ - "sweep fee limit: %v", f.MaximumPrepay, f.MaximumMinerFee, - f.MaximumSwapFeePPM, f.MaximumRoutingFeePPM, - f.MaximumPrepayRoutingFeePPM, f.SweepFeeRateLimit, - ) + return fmt.Sprintf("fee categories: maximum prepay: %v, maximum miner "+ + "fee: %v, maximum swap fee ppm: %v, maximum routing fee ppm: "+ + "%v, maximum prepay routing fee ppm: %v,sweep fee limit: %v", + f.MaximumPrepay, f.MaximumMinerFee, f.MaximumSwapFeePPM, + f.MaximumRoutingFeePPM, f.MaximumPrepayRoutingFeePPM, + f.SweepFeeRateLimit) } func (f *FeeCategoryLimit) validate() error { @@ -211,8 +212,8 @@ func (f *FeeCategoryLimit) loopOutLimits(amount btcutil.Amount, } if quote.MinerFee > f.MaximumMinerFee { - log.Debugf("quoted miner fee: %v > maximum miner "+ - "fee: %v", quote.MinerFee, f.MaximumMinerFee) + log.Debugf("quoted miner fee: %v > maximum miner fee: %v", + quote.MinerFee, f.MaximumMinerFee) return newReasonError(ReasonMinerFee) } @@ -239,8 +240,8 @@ func (f *FeeCategoryLimit) loopInLimits(amount btcutil.Amount, } if quote.MinerFee > f.MaximumMinerFee { - log.Debugf("quoted miner fee: %v > maximum miner "+ - "fee: %v", quote.MinerFee, f.MaximumMinerFee) + log.Debugf("quoted miner fee: %v > maximum miner fee: %v", + quote.MinerFee, f.MaximumMinerFee) return newReasonError(ReasonMinerFee) } @@ -320,15 +321,15 @@ func (f *FeePortion) loopOutLimits(swapAmt btcutil.Amount, minerFee := scaleMinerFee(quote.MinerFee) if minerFee > feeLimit { - log.Debugf("miner fee: %v greater than fee limit: %v, at "+ - "%v ppm", minerFee, feeLimit, f.PartsPerMillion) + log.Debugf("miner fee: %v greater than fee limit: %v, "+ + "at %v ppm", minerFee, feeLimit, f.PartsPerMillion) return newReasonError(ReasonMinerFee) } if quote.SwapFee > feeLimit { - log.Debugf("swap fee: %v greater than fee limit: %v, at "+ - "%v ppm", quote.SwapFee, feeLimit, f.PartsPerMillion) + log.Debugf("swap fee: %v greater than fee limit: %v, at %v ppm", + quote.SwapFee, feeLimit, f.PartsPerMillion) return newReasonError(ReasonSwapFee) } @@ -358,8 +359,8 @@ func (f *FeePortion) loopOutLimits(swapAmt btcutil.Amount, ) if fees > feeLimit { - log.Debugf("total fees for swap: %v > fee limit: %v, at "+ - "%v ppm", fees, feeLimit, f.PartsPerMillion) + log.Debugf("total fees for swap: %v > fee limit: %v, at %v ppm", + fees, feeLimit, f.PartsPerMillion) return newReasonError(ReasonFeePPMInsufficient) } @@ -433,17 +434,16 @@ func (f *FeePortion) loopInLimits(amount btcutil.Amount, // Check individual fee components so that we can give more specific // feedback. if quote.MinerFee > totalFeeSpend { - log.Debugf("miner fee: %v greater than fee limit: %v, at "+ - "%v ppm", quote.MinerFee, totalFeeSpend, + log.Debugf("miner fee: %v greater than fee limit: %v, "+ + "at %v ppm", quote.MinerFee, totalFeeSpend, f.PartsPerMillion) return newReasonError(ReasonMinerFee) } if quote.SwapFee > totalFeeSpend { - log.Debugf("swap fee: %v greater than fee limit: %v, at "+ - "%v ppm", quote.SwapFee, totalFeeSpend, - f.PartsPerMillion) + log.Debugf("swap fee: %v greater than fee limit: %v, at %v ppm", + quote.SwapFee, totalFeeSpend, f.PartsPerMillion) return newReasonError(ReasonSwapFee) } @@ -453,8 +453,8 @@ func (f *FeePortion) loopInLimits(amount btcutil.Amount, ) if fees > totalFeeSpend { - log.Debugf("total fees for swap: %v > fee limit: %v, at "+ - "%v ppm", fees, totalFeeSpend, f.PartsPerMillion) + log.Debugf("total fees for swap: %v > fee limit: %v, at %v ppm", + fees, totalFeeSpend, f.PartsPerMillion) return newReasonError(ReasonFeePPMInsufficient) } diff --git a/liquidity/interface.go b/liquidity/interface.go index 96c5b064b..06dd7ab06 100644 --- a/liquidity/interface.go +++ b/liquidity/interface.go @@ -35,8 +35,7 @@ type FeeLimit interface { // loopInLimits checks whether the quote provided is within our fee // limits for the swap amount. - loopInLimits(amount btcutil.Amount, - quote *loop.LoopInQuote) error + loopInLimits(amount btcutil.Amount, quote *loop.LoopInQuote) error } // swapBuilder is an interface used to build our different swap types. @@ -62,8 +61,8 @@ type swapBuilder interface { // is just for a dry run. buildSwap(ctx context.Context, peer route.Vertex, channels []lnwire.ShortChannelID, amount btcutil.Amount, - params Parameters, swapOpts ...buildSwapOption) (swapSuggestion, - error) + params Parameters, + swapOpts ...buildSwapOption) (swapSuggestion, error) } // swapSuggestion is an interface implemented by suggested swaps for our diff --git a/liquidity/liquidity.go b/liquidity/liquidity.go index b31e2b003..a0976de0f 100644 --- a/liquidity/liquidity.go +++ b/liquidity/liquidity.go @@ -11,21 +11,21 @@ // Fee restrictions are placed on swap suggestions to ensure that we only // suggest swaps that fit the configured fee preferences. // - Sweep Fee Rate Limit: the maximum sat/vByte fee estimate for our sweep -// transaction to confirm within our configured number of confirmations -// that we will suggest swaps for. +// transaction to confirm within our configured number of confirmations that +// we will suggest swaps for. // - Maximum Swap Fee PPM: the maximum server fee, expressed as parts per // million of the full swap amount // - Maximum Routing Fee PPM: the maximum off-chain routing fees for the swap // invoice, expressed as parts per million of the swap amount. -// - Maximum Prepay Routing Fee PPM: the maximum off-chain routing fees for the -// swap prepayment, expressed as parts per million of the prepay amount. +// - Maximum Prepay Routing Fee PPM: the maximum off-chain routing fees for +// the swap prepayment, expressed as parts per million of the prepay amount. // - Maximum Prepay: the maximum now-show fee, expressed in satoshis. This -// amount is only payable in the case where the swap server broadcasts a htlc -// and the client fails to sweep the preimage. -// - Maximum miner fee: the maximum miner fee we are willing to pay to sweep the -// on chain htlc. Note that the client will use current fee estimates to -// sweep, so this value acts more as a sanity check in the case of a large fee -// spike. +// amount is only payable in the case where the swap server broadcasts a +// htlc and the client fails to sweep the preimage. +// - Maximum miner fee: the maximum miner fee we are willing to pay to sweep +// the on chain htlc. Note that the client will use current fee estimates to +// sweep, so this value acts more as a sanity check in the case of a large +// fee spike. // // The maximum fee per-swap is calculated as follows: // (swap amount * serverPPM/1e6) + miner fee + (swap amount * routingPPM/1e6) @@ -166,8 +166,8 @@ var ( // ErrAccountAndAddrType indicates if an account is set but the // account address type is not or vice versa. - ErrAccountAndAddrType = errors.New("account and address type have " + - "to be both either set or unset") + ErrAccountAndAddrType = errors.New("account and address type have to " + + "be both either set or unset") ) // Config contains the external functionality required to run the @@ -307,7 +307,6 @@ func (m *Manager) Run(ctx context.Context) error { "autoloop") case nil: - default: log.Errorf("autoloop failed: %v", err) } @@ -371,9 +370,7 @@ func (m *Manager) SetParameters(ctx context.Context, // setParameters updates our current set of parameters if the new parameters // provided are valid. -func (m *Manager) setParameters(ctx context.Context, - params Parameters) error { - +func (m *Manager) setParameters(ctx context.Context, params Parameters) error { restrictions, err := m.cfg.Restrictions( ctx, swap.TypeOut, getInitiator(m.params), ) @@ -458,8 +455,8 @@ func (m *Manager) autoloop(ctx context.Context) error { // If we don't actually have dispatch of swaps enabled, log // suggestions. if !m.params.Autoloop { - log.Debugf("recommended autoloop out: %v sats over "+ - "%v", swap.Amount, swap.OutgoingChanSet) + log.Debugf("recommended autoloop out: %v sats over %v", + swap.Amount, swap.OutgoingChanSet) continue } @@ -481,8 +478,8 @@ func (m *Manager) autoloop(ctx context.Context) error { // If we don't actually have dispatch of swaps enabled, log // suggestions. if !m.params.Autoloop { - log.Debugf("recommended autoloop in: %v sats over "+ - "%v", in.Amount, in.LastHop) + log.Debugf("recommended autoloop in: %v sats over %v", + in.Amount, in.LastHop) continue } @@ -523,12 +520,12 @@ func (m *Manager) easyAutoLoop(ctx context.Context) error { return nil } -// easyAssetAutoloop is the main entry point for the easy auto loop functionality -// for assets. This function will try to dispatch a swap in order to meet the -// easy autoloop requirements for the given asset. For easyAutoloop to work -// there needs to be an EasyAutoloopTarget defined in the parameters. Easy -// autoloop also uses the configured max inflight swaps and budget rules defined -// in the parameters. +// easyAssetAutoloop is the main entry point for the easy auto loop +// functionality for assets. This function will try to dispatch a swap in order +// to meet the easy autoloop requirements for the given asset. For easyAutoloop +// to work there needs to be an EasyAutoloopTarget defined in the parameters. +// Easy autoloop also uses the configured max inflight swaps and budget rules +// defined in the parameters. func (m *Manager) easyAssetAutoloop(ctx context.Context, assetID string) error { if !m.params.Autoloop { return nil @@ -607,8 +604,9 @@ func (m *Manager) dispatchBestEasyAutoloopSwap(ctx context.Context) error { // Since we're only autolooping-out we need to check if we are below // the target, meaning that we already meet the requirements. if localTotal <= m.params.EasyAutoloopTarget { - log.Debugf("total local balance %v below target %v", - localTotal, m.params.EasyAutoloopTarget) + log.Debugf("total local balance %v below target %v", localTotal, + m.params.EasyAutoloopTarget) + return nil } @@ -630,12 +628,12 @@ func (m *Manager) dispatchBestEasyAutoloopSwap(ctx context.Context) error { log.Debugf("easy autoloop: swap amount is below minimum swap "+ "size, minimum=%v, need to swap %v", restrictions.Minimum, amount) + return nil } - log.Debugf("easy autoloop: local_total=%v, target=%v, "+ - "attempting to loop out %v", localTotal, - m.params.EasyAutoloopTarget, amount) + log.Debugf("easy autoloop: local_total=%v, target=%v, attempting to "+ + "loop out %v", localTotal, m.params.EasyAutoloopTarget, amount) // Start building that swap. builder := newLoopOutBuilder(m.cfg) @@ -651,7 +649,9 @@ func (m *Manager) dispatchBestEasyAutoloopSwap(ctx context.Context) error { channel.ChannelID, channel.LocalBalance) swapAmt, err := btcutil.NewAmount( - math.Min(channel.LocalBalance.ToBTC(), amount.ToBTC()), + math.Min( + channel.LocalBalance.ToBTC(), amount.ToBTC(), + ), ) if err != nil { return err @@ -668,6 +668,7 @@ func (m *Manager) dispatchBestEasyAutoloopSwap(ctx context.Context) error { PartsPerMillion: defaultFeePPM, } } + default: easyParams.FeeLimit = &FeePortion{ PartsPerMillion: defaultFeePPM, @@ -764,8 +765,8 @@ func (m *Manager) dispatchBestAssetEasyAutoloopSwap(ctx context.Context, channel.LocalBalance = btcutil.Amount(assetData.LocalBalance) usableChannels = append(usableChannels, channel) - // We'll use a random peer pubkey in order to get a rfq for the asset - // to get a rough amount of sats to swap amount. + // We'll use a random peer pubkey in order to get a rfq for the + // asset to get a rough amount of sats to swap amount. assetPeerPubkey = channel.PubKeyBytes[:] localTotal += assetData.LocalBalance @@ -774,8 +775,9 @@ func (m *Manager) dispatchBestAssetEasyAutoloopSwap(ctx context.Context, // Since we're only autolooping-out we need to check if we are below // the target, meaning that we already meet the requirements. if localTotal <= localTarget { - log.Debugf("Asset: %v... total local balance %v below target %v", - assetID[:8], localTotal, localTarget) + log.Debugf("Asset: %v... total local balance %v below "+ + "target %v", assetID[:8], localTotal, localTarget) + return nil } @@ -792,7 +794,9 @@ func (m *Manager) dispatchBestAssetEasyAutoloopSwap(ctx context.Context, // We need a request sat amount for the asset price request. We'll use // the average of the min and max restrictions. - assetPriceRequestSatAmt := (restrictions.Minimum + restrictions.Maximum) / 2 + assetPriceRequestSatAmt := (restrictions.Minimum + + restrictions.Maximum) / + 2 // If we run a custom asset, we'll need to convert the asset amount // we want to swap to the satoshi amount. @@ -806,17 +810,18 @@ func (m *Manager) dispatchBestAssetEasyAutoloopSwap(ctx context.Context, if satAmount > restrictions.Maximum { log.Debugf("Asset %v easy autoloop: using maximum allowed "+ - "swap amount, maximum=%v, need to swap %v", - assetID[:8], restrictions.Maximum, satAmount) + "swap amount, maximum=%v, need to swap %v", assetID[:8], + restrictions.Maximum, satAmount) satAmount = restrictions.Maximum } // If the amount we want to loop out is less than the minimum we can't // proceed with a swap, so we return early. if satAmount < restrictions.Minimum { - log.Debugf("Asset %v easy autoloop: swap amount is below"+ - " minimum swap size, minimum=%v, need to swap %v", + log.Debugf("Asset %v easy autoloop: swap amount is below "+ + "minimum swap size, minimum=%v, need to swap %v", assetID[:8], restrictions.Minimum, satAmount) + return nil } @@ -954,9 +959,7 @@ func (m *Manager) singleReasonSuggestion(reason Reason) *Suggestions { // suggestions are being used for our internal autolooper. This boolean is used // to determine the information we add to our swap suggestion and whether we // return any suggestions. -func (m *Manager) SuggestSwaps(ctx context.Context) ( - *Suggestions, error) { - +func (m *Manager) SuggestSwaps(ctx context.Context) (*Suggestions, error) { m.paramsLock.Lock() defer m.paramsLock.Unlock() @@ -1481,6 +1484,7 @@ func (m *Manager) refreshAutoloopBudget(ctx context.Context) { if err != nil { log.Errorf("Error converting parameters to rpc: %v", err) + return } @@ -1501,6 +1505,7 @@ func (m *Manager) dispatchStickyLoopOut(ctx context.Context, m.activeStickyLock.Lock() if m.activeStickyLoops >= m.params.MaxAutoInFlight { m.activeStickyLock.Unlock() + return } @@ -1518,8 +1523,9 @@ func (m *Manager) dispatchStickyLoopOut(ctx context.Context, // Dispatch the swap. swap, err := m.cfg.LoopOut(ctx, &out) if err != nil { - log.Errorf("unable to dispatch loop out, amt: %v, "+ - "err: %v", out.Amount, err) + log.Errorf("unable to dispatch loop out, amt: "+ + "%v, err: %v", out.Amount, err) + return } @@ -1546,11 +1552,9 @@ func (m *Manager) dispatchStickyLoopOut(ctx context.Context, // If update is nil then no update occurred // within the defined timeout period. It's // better to return and not attempt a retry. - log.Debug( - "No payment update received for swap "+ - "%v, skipping amount backoff", - swap.SwapHash, - ) + log.Debug("No payment update received for "+ + "swap %v, skipping amount backoff", + swap.SwapHash) return } @@ -1566,12 +1570,13 @@ func (m *Manager) dispatchStickyLoopOut(ctx context.Context, float64(out.Amount) * amountBackoff, ) - log.Infof("swap %v: amount backoff old amount="+ - "%v, new amount=%v", swap.SwapHash, - oldAmt, out.Amount) + log.Infof("swap %v: amount backoff old "+ + "amount=%v, new amount=%v", + swap.SwapHash, oldAmt, out.Amount) continue } else { + // If the update channel did not return an // off-chain payment failure we won't retry. return @@ -1603,15 +1608,14 @@ func (m *Manager) waitForSwapPayment(ctx context.Context, swapHash lntypes.Hash, select { case <-ctx.Done(): return + case <-time.After(interval): } swap, err = m.cfg.GetLoopOut(ctx, swapHash) if err != nil { - log.Errorf( - "Error getting swap with hash %x: %v", swapHash, - err, - ) + log.Errorf("Error getting swap with hash %x: %v", + swapHash, err) continue } @@ -1627,16 +1631,22 @@ func (m *Manager) waitForSwapPayment(ctx context.Context, swapHash lntypes.Hash, switch update.State { case loopdb.StateFailInsufficientValue: fallthrough + case loopdb.StateSuccess: fallthrough + case loopdb.StateFailSweepTimeout: fallthrough + case loopdb.StateFailTimeout: fallthrough + case loopdb.StatePreimageRevealed: fallthrough + case loopdb.StateFailOffchainPayments: updateChan <- &update.State + return } } @@ -1676,7 +1686,8 @@ func (m *Manager) pickEasyAutoloopChannel(channels []lndclient.ChannelInfo, // Check each channel, since channels are already sorted, we return the // first channel that passes all checks. for _, channel := range channels { - // Skip channels whose remote peer is excluded for easy autoloop. + // Skip channels whose remote peer is excluded for easy + // autoloop. if _, ok := excluded[channel.PubKeyBytes]; ok { log.Debugf("Channel %v cannot be used for easy "+ "autoloop: peer %v manually excluded", @@ -1718,10 +1729,11 @@ func (m *Manager) pickEasyAutoloopChannel(channels []lndclient.ChannelInfo, if localBalance < restrictions.Minimum { log.Debugf("Channel %v cannot be used for easy "+ - "autoloop: insufficient local balance %v,"+ - "minimum is %v, skipping remaining channels", + "autoloop: insufficient local balance "+ + "%v,minimum is %v, skipping remaining channels", channel.ChannelID, channel.LocalBalance, restrictions.Minimum) + return nil } @@ -1740,18 +1752,18 @@ func (m *Manager) numActiveStickyLoops() int { func (m *Manager) checkSummaryBudget(summary *existingAutoLoopSummary) error { if summary.totalFees() >= m.params.AutoFeeBudget { - return fmt.Errorf("autoloop fee budget: %v exhausted, %v spent on "+ - "completed swaps, %v reserved for ongoing swaps "+ - "(upper limit)", - m.params.AutoFeeBudget, summary.spentFees, - summary.pendingFees) + return fmt.Errorf("autoloop fee budget: %v exhausted, %v "+ + "spent on completed swaps, %v reserved for ongoing "+ + "swaps (upper limit)", m.params.AutoFeeBudget, + summary.spentFees, summary.pendingFees) } return nil } -func (m *Manager) checkSummaryInflight( - summary *existingAutoLoopSummary) (int, error) { +func (m *Manager) checkSummaryInflight(summary *existingAutoLoopSummary) (int, + error) { + // If we have already reached our total allowed number of in flight // swaps we return early. allowedSwaps := m.params.MaxAutoInFlight - summary.inFlightCount @@ -1822,6 +1834,7 @@ func ppmToSat(amount btcutil.Amount, ppm uint64) btcutil.Amount { // channelIsCustom returns true if the channel has custom channel data. // we'll want to ignore these channels for autoloop recommendations. func channelIsCustom(channel lndclient.ChannelInfo) bool { + // If the channel has custom channel data, the channel is a // non-standard channel, such as an asset channel and we // don't want to consider it for swaps. @@ -1841,6 +1854,7 @@ func getCustomAssetData(channel lndclient.ChannelInfo, if err != nil { log.Errorf("Error unmarshalling custom channel %v data: %v", channel.ChannelID, err) + return nil } diff --git a/liquidity/liquidity_test.go b/liquidity/liquidity_test.go index 00f2cb58f..4651980a4 100644 --- a/liquidity/liquidity_test.go +++ b/liquidity/liquidity_test.go @@ -147,8 +147,8 @@ func newTestConfig() (*Config, *test.LndMockServices) { ) return &Config{ - Restrictions: func(_ context.Context, _ swap.Type, initiator string) (*Restrictions, - error) { + Restrictions: func(_ context.Context, _ swap.Type, + initiator string) (*Restrictions, error) { return testRestrictions, nil }, @@ -271,7 +271,9 @@ func TestPersistParams(t *testing.T) { var paramsBytes []byte // Mock the read method to return empty data. - manager.cfg.FetchLiquidityParams = func(context.Context) ([]byte, error) { + manager.cfg.FetchLiquidityParams = func(context.Context) ([]byte, + error) { + return paramsBytes, nil } @@ -285,6 +287,7 @@ func TestPersistParams(t *testing.T) { data []byte) error { paramsBytes = data + return nil } @@ -567,10 +570,14 @@ func TestRestrictedSuggestions(t *testing.T) { // Create a manager config which will return the test // case's set of existing swaps. cfg, lnd := newTestConfig() - cfg.ListLoopOut = func(context.Context) ([]*loopdb.LoopOut, error) { + cfg.ListLoopOut = func(context.Context) ( + []*loopdb.LoopOut, error) { + return testCase.loopOut, nil } - cfg.ListLoopIn = func(context.Context) ([]*loopdb.LoopIn, error) { + cfg.ListLoopIn = func(context.Context) ( + []*loopdb.LoopIn, error) { + return testCase.loopIn, nil } @@ -614,7 +621,8 @@ func TestSweepFeeLimit(t *testing.T) { suggestions: &Suggestions{ OutSwaps: []loop.OutRequest{ applyFeeCategoryQuote( - chan1Rec, defaultMaximumMinerFee, + chan1Rec, + defaultMaximumMinerFee, defaultPrepayRoutingFeePPM, defaultRoutingFeePPM, *quote, ), @@ -640,8 +648,8 @@ func TestSweepFeeLimit(t *testing.T) { cfg, lnd := newTestConfig() cfg.LoopOutQuote = func(_ context.Context, - _ *loop.LoopOutQuoteRequest) (*loop.LoopOutQuote, - error) { + _ *loop.LoopOutQuoteRequest) ( + *loop.LoopOutQuote, error) { return quote, nil } @@ -776,7 +784,9 @@ func TestSuggestSwaps(t *testing.T) { MaxPrepayRoutingFee: prepay, MaxSwapRoutingFee: routing, MaxMinerFee: scaleMaxMinerFee( - scaleMinerFee(testQuote.MinerFee), + scaleMinerFee( + testQuote.MinerFee, + ), ), MaxSwapFee: testQuote.SwapFee, MaxPrepayAmount: testQuote.PrepayAmount, @@ -858,7 +868,8 @@ func TestFeeLimits(t *testing.T) { suggestions: &Suggestions{ OutSwaps: []loop.OutRequest{ applyFeeCategoryQuote( - chan1Rec, defaultMaximumMinerFee, + chan1Rec, + defaultMaximumMinerFee, defaultPrepayRoutingFeePPM, defaultRoutingFeePPM, *quote, ), @@ -1110,13 +1121,15 @@ func TestFeeBudget(t *testing.T) { }) } - cfg.ListLoopOut = func(context.Context) ([]*loopdb.LoopOut, error) { + cfg.ListLoopOut = func(context.Context) ( + []*loopdb.LoopOut, error) { + return swaps, nil } cfg.LoopOutQuote = func(_ context.Context, - _ *loop.LoopOutQuoteRequest) (*loop.LoopOutQuote, - error) { + _ *loop.LoopOutQuoteRequest) ( + *loop.LoopOutQuote, error) { return quote, nil } @@ -1285,10 +1298,14 @@ func TestInFlightLimit(t *testing.T) { for _, testCase := range tests { t.Run(testCase.name, func(t *testing.T) { cfg, lnd := newTestConfig() - cfg.ListLoopOut = func(context.Context) ([]*loopdb.LoopOut, error) { + cfg.ListLoopOut = func(context.Context) ( + []*loopdb.LoopOut, error) { + return testCase.existingSwaps, nil } - cfg.ListLoopIn = func(context.Context) ([]*loopdb.LoopIn, error) { + cfg.ListLoopIn = func(context.Context) ( + []*loopdb.LoopIn, error) { + return testCase.existingInSwaps, nil } @@ -1330,8 +1347,7 @@ type mockServer struct { // Restrictions mocks a call to the server to get swap size restrictions. func (m *mockServer) Restrictions(ctx context.Context, swapType swap.Type, - initiator string) ( - *Restrictions, error) { + initiator string) (*Restrictions, error) { args := m.Called(ctx, swapType) @@ -1595,8 +1611,8 @@ func TestFeePercentage(t *testing.T) { cfg, lnd := newTestConfig() cfg.LoopOutQuote = func(_ context.Context, - _ *loop.LoopOutQuoteRequest) (*loop.LoopOutQuote, - error) { + _ *loop.LoopOutQuoteRequest) ( + *loop.LoopOutQuote, error) { return testCase.quote, nil } @@ -1765,13 +1781,15 @@ func TestBudgetWithLoopin(t *testing.T) { channel1, } - cfg.ListLoopIn = func(context.Context) ([]*loopdb.LoopIn, error) { + cfg.ListLoopIn = func(context.Context) ( + []*loopdb.LoopIn, error) { + return testCase.loopIns, nil } cfg.LoopOutQuote = func(_ context.Context, - _ *loop.LoopOutQuoteRequest) (*loop.LoopOutQuote, - error) { + _ *loop.LoopOutQuoteRequest) ( + *loop.LoopOutQuote, error) { return okQuote, nil } @@ -2035,9 +2053,16 @@ func TestCurrentTraffic(t *testing.T) { params := m.GetParameters() params.FailureBackOff = backoff - require.NoError(t, m.setParameters(context.Background(), params)) + require.NoError( + t, + m.setParameters( + context.Background(), params, + ), + ) - actual := m.currentSwapTraffic(testCase.loopOut, testCase.loopIn) + actual := m.currentSwapTraffic( + testCase.loopOut, testCase.loopIn, + ) require.Equal(t, testCase.expected, actual) } } diff --git a/liquidity/loopin_builder.go b/liquidity/loopin_builder.go index 91c8c3076..642ed6d29 100644 --- a/liquidity/loopin_builder.go +++ b/liquidity/loopin_builder.go @@ -68,9 +68,8 @@ func (b *loopInBuilder) inUse(traffic *swapTraffic, peer route.Vertex, lastFail, recentFail := traffic.failedLoopIn[peer] if recentFail { - log.Debugf("Peer: %v not eligible for suggestions, "+ - "was part of a failed swap at: %v", peer, - lastFail) + log.Debugf("Peer: %v not eligible for suggestions, was part "+ + "of a failed swap at: %v", peer, lastFail) return newReasonError(ReasonFailureBackoff) } @@ -83,9 +82,8 @@ func (b *loopInBuilder) inUse(traffic *swapTraffic, peer route.Vertex, // // For loop in, we do not add the autoloop label for dry runs. func (b *loopInBuilder) buildSwap(ctx context.Context, pubkey route.Vertex, - _ []lnwire.ShortChannelID, amount btcutil.Amount, - params Parameters, swapOpts ...buildSwapOption) (swapSuggestion, - error) { + _ []lnwire.ShortChannelID, amount btcutil.Amount, params Parameters, + swapOpts ...buildSwapOption) (swapSuggestion, error) { quote, err := b.cfg.LoopInQuote(ctx, &loop.LoopInQuoteRequest{ Amount: amount, diff --git a/liquidity/loopin_builder_test.go b/liquidity/loopin_builder_test.go index 35990bd01..c74424a20 100644 --- a/liquidity/loopin_builder_test.go +++ b/liquidity/loopin_builder_test.go @@ -116,9 +116,13 @@ func TestLoopinBuildSwap(t *testing.T) { Initiator: autoloopSwapInitiator, } - errPrecondition = status.Error(codes.FailedPrecondition, "failed") - errOtherCode = status.Error(codes.DeadlineExceeded, "timeout") - errNoCode = errors.New("failure") + errPrecondition = status.Error( + codes.FailedPrecondition, "failed", + ) + errOtherCode = status.Error( + codes.DeadlineExceeded, "timeout", + ) + errNoCode = errors.New("failure") ) tests := []struct { diff --git a/liquidity/loopout.go b/liquidity/loopout.go index 17758e7f9..f997e5a12 100644 --- a/liquidity/loopout.go +++ b/liquidity/loopout.go @@ -26,8 +26,9 @@ func (l *loopOutSwapSuggestion) amount() btcutil.Amount { // fees returns the maximum fees we could possibly pay for this swap. func (l *loopOutSwapSuggestion) fees() btcutil.Amount { return worstCaseOutFees( - l.OutRequest.MaxPrepayRoutingFee, l.OutRequest.MaxSwapRoutingFee, - l.OutRequest.MaxSwapFee, l.OutRequest.MaxMinerFee, + l.OutRequest.MaxPrepayRoutingFee, + l.OutRequest.MaxSwapRoutingFee, l.OutRequest.MaxSwapFee, + l.OutRequest.MaxMinerFee, ) } diff --git a/liquidity/loopout_builder.go b/liquidity/loopout_builder.go index e0c147ed7..463ab30e0 100644 --- a/liquidity/loopout_builder.go +++ b/liquidity/loopout_builder.go @@ -178,9 +178,9 @@ func (b *loopOutBuilder) buildSwap(ctx context.Context, pubkey route.Vertex, return nil, err } - log.Debugf("quote for suggestion: %v, swap fee: %v, "+ - "miner fee: %v, prepay: %v", amount, quote.SwapFee, - quote.MinerFee, quote.PrepayAmount) + log.Debugf("quote for suggestion: %v, swap fee: %v, miner fee: %v, "+ + "prepay: %v", amount, quote.SwapFee, quote.MinerFee, + quote.PrepayAmount) // Check that the estimated fees for the suggested swap are below the // fee limits configured. diff --git a/liquidity/mock.go b/liquidity/mock.go index a23de576d..b96abb298 100644 --- a/liquidity/mock.go +++ b/liquidity/mock.go @@ -29,5 +29,6 @@ func (m *mockCfg) LoopInQuote(ctx context.Context, request *loop.LoopInQuoteRequest) (*loop.LoopInQuote, error) { args := m.Called(ctx, request) + return args.Get(0).(*loop.LoopInQuote), args.Error(1) } diff --git a/liquidity/parameters.go b/liquidity/parameters.go index 7032ff83a..0767785fb 100644 --- a/liquidity/parameters.go +++ b/liquidity/parameters.go @@ -25,13 +25,15 @@ var ( AutoloopBudgetLastRefresh: time.Now(), DestAddr: nil, MaxAutoInFlight: defaultMaxInFlight, - ChannelRules: make(map[lnwire.ShortChannelID]*SwapRule), - PeerRules: make(map[route.Vertex]*SwapRule), - FailureBackOff: defaultFailureBackoff, - SweepConfTarget: defaultConfTarget, - HtlcConfTarget: defaultHtlcConfTarget, - FeeLimit: defaultFeePortion(), - FastSwapPublication: true, + ChannelRules: make( + map[lnwire.ShortChannelID]*SwapRule, + ), + PeerRules: make(map[route.Vertex]*SwapRule), + FailureBackOff: defaultFailureBackoff, + SweepConfTarget: defaultConfTarget, + HtlcConfTarget: defaultHtlcConfTarget, + FeeLimit: defaultFeePortion(), + FastSwapPublication: true, } ) @@ -157,14 +159,14 @@ func (p Parameters) String() string { ) } - return fmt.Sprintf("rules: %v, failure backoff: %v, sweep "+ - "sweep conf target: %v, htlc conf target: %v,fees: %v, "+ - "auto budget: %v, budget refresh: %v, max auto in flight: %v, "+ - "minimum swap size=%v, maximum swap size=%v", - strings.Join(ruleList, ","), p.FailureBackOff, - p.SweepConfTarget, p.HtlcConfTarget, p.FeeLimit, - p.AutoFeeBudget, p.AutoFeeRefreshPeriod, p.MaxAutoInFlight, - p.ClientRestrictions.Minimum, p.ClientRestrictions.Maximum) + return fmt.Sprintf("rules: %v, failure backoff: %v, sweep conf "+ + "target: %v, htlc conf target: %v,fees: %v, auto budget: %v, "+ + "budget refresh: %v, max auto in flight: %v, minimum swap "+ + "size=%v, maximum swap size=%v", strings.Join(ruleList, ","), + p.FailureBackOff, p.SweepConfTarget, p.HtlcConfTarget, + p.FeeLimit, p.AutoFeeBudget, p.AutoFeeRefreshPeriod, + p.MaxAutoInFlight, p.ClientRestrictions.Minimum, + p.ClientRestrictions.Maximum) } // haveRules returns a boolean indicating whether we have any rules configured. @@ -209,7 +211,8 @@ func (p Parameters) validate(minConfs int32, openChans []lndclient.ChannelInfo, _, ok = p.ChannelRules[shortID] if ok { log.Debugf("Rules for peer: %v and its channel: %v "+ - "can't both be set", channel.PubKeyBytes, shortID) + "can't both be set", channel.PubKeyBytes, + shortID) return ErrExclusiveRules } @@ -221,8 +224,9 @@ func (p Parameters) validate(minConfs int32, openChans []lndclient.ChannelInfo, } if rule.Type == swap.TypeIn { - return errors.New("channel level rules not supported for " + - "loop in swaps, only peer-level rules allowed") + return errors.New("channel level rules not supported " + + "for loop in swaps, only peer-level rules " + + "allowed") } if err := rule.validate(); err != nil { @@ -233,8 +237,8 @@ func (p Parameters) validate(minConfs int32, openChans []lndclient.ChannelInfo, for peer, rule := range p.PeerRules { if err := rule.validate(); err != nil { - return fmt.Errorf("peer: %v has invalid rule: %v", - peer, err) + return fmt.Errorf("peer: %v has invalid rule: %v", peer, + err) } } @@ -263,7 +267,6 @@ func (p Parameters) validate(minConfs int32, openChans []lndclient.ChannelInfo, // Destination address and account cannot be set at the same time. if p.DestAddr != nil && len(p.DestAddr.String()) > 0 && len(p.Account) > 0 { - return ErrAmbiguousDestAddr } @@ -271,7 +274,6 @@ func (p Parameters) validate(minConfs int32, openChans []lndclient.ChannelInfo, // specified as well, or both must be unset. if len(p.Account) == 0 != (p.AccountAddrType == walletrpc.AddressType_UNKNOWN) { - return ErrAccountAndAddrType } @@ -325,8 +327,7 @@ func validateRestrictions(server, client *Restrictions) error { func cloneParameters(params Parameters) Parameters { paramCopy := params paramCopy.ChannelRules = make( - map[lnwire.ShortChannelID]*SwapRule, - len(params.ChannelRules), + map[lnwire.ShortChannelID]*SwapRule, len(params.ChannelRules), ) for channel, rule := range params.ChannelRules { @@ -335,8 +336,7 @@ func cloneParameters(params Parameters) Parameters { } paramCopy.PeerRules = make( - map[route.Vertex]*SwapRule, - len(params.PeerRules), + map[route.Vertex]*SwapRule, len(params.PeerRules), ) for peer, rule := range params.PeerRules { @@ -362,6 +362,7 @@ func rpcToFee(req *clientrpc.LiquidityParameters) (FeeLimit, error) { case isFeePPM && isCategories: return nil, errors.New("set either fee ppm, or individual " + "fee categories") + case isFeePPM: return NewFeePortion(req.FeePpm), nil @@ -371,8 +372,7 @@ func rpcToFee(req *clientrpc.LiquidityParameters) (FeeLimit, error) { ) return NewFeeCategoryLimit( - req.MaxSwapFeePpm, - req.MaxRoutingFeePpm, + req.MaxSwapFeePpm, req.MaxRoutingFeePpm, req.MaxPrepayRoutingFeePpm, btcutil.Amount(req.MaxMinerFeeSat), btcutil.Amount(req.MaxPrepaySat), @@ -411,9 +411,7 @@ func rpcToRule(rule *clientrpc.LiquidityRule) (*SwapRule, error) { // RpcToParameters takes a `LiquidityParameters` and creates a `Parameters` // from it. -func RpcToParameters(req *clientrpc.LiquidityParameters) (*Parameters, - error) { - +func RpcToParameters(req *clientrpc.LiquidityParameters) (*Parameters, error) { feeLimit, err := rpcToFee(req) if err != nil { return nil, err @@ -509,7 +507,8 @@ func RpcToParameters(req *clientrpc.LiquidityParameters) (*Parameters, params.AutoFeeRefreshPeriod = InfiniteDuration params.AutoloopBudgetLastRefresh = time.Unix( - int64(req.AutoloopBudgetStartSec), 0) + int64(req.AutoloopBudgetStartSec), 0, + ) } for _, rule := range req.Rules { @@ -561,9 +560,7 @@ func RpcToParameters(req *clientrpc.LiquidityParameters) (*Parameters, // ParametersToRpc takes a `Parameters` and creates a `LiquidityParameters` // from it. -func ParametersToRpc(cfg Parameters) (*clientrpc.LiquidityParameters, - error) { - +func ParametersToRpc(cfg Parameters) (*clientrpc.LiquidityParameters, error) { totalRules := len(cfg.ChannelRules) + len(cfg.PeerRules) var destaddr string diff --git a/liquidity/threshold_rule.go b/liquidity/threshold_rule.go index 8e50dfbf8..ed2142cb0 100644 --- a/liquidity/threshold_rule.go +++ b/liquidity/threshold_rule.go @@ -131,8 +131,8 @@ func (r *ThresholdRule) swapAmount(channel *balances, // liquidity. We aim for this liquidity to reach the threshold amount set. // - reserve: this is the side of the channel(s) that we will move liquidity // away from. This may not drop below a certain reserve threshold. -func calculateSwapAmount(targetAmount, reserveAmount, - capacity btcutil.Amount, targetThresholdPercentage, +func calculateSwapAmount(targetAmount, reserveAmount, capacity btcutil.Amount, + targetThresholdPercentage, reserveThresholdPercentage uint64) btcutil.Amount { targetGoal := btcutil.Amount( diff --git a/loopd/config.go b/loopd/config.go index 63c472690..f53299044 100644 --- a/loopd/config.go +++ b/loopd/config.go @@ -80,8 +80,7 @@ var ( // LND. DefaultLndMacaroonPath = filepath.Join( btcutil.AppDataDir("lnd", false), - "data", "chain", "bitcoin", DefaultNetwork, - defaultLndMacaroon, + "data", "chain", "bitcoin", DefaultNetwork, defaultLndMacaroon, ) // DefaultLndRPCTimeout is the default timeout to use when communicating @@ -281,8 +280,8 @@ func Validate(cfg *Config) error { } if dataDirSet { - return fmt.Errorf("loopdir overwrites datadir, please " + - "only set one value") + return fmt.Errorf("loopdir overwrites datadir, " + + "please only set one value") } if tlsCertPathSet { @@ -378,8 +377,8 @@ func Validate(cfg *Config) error { // Allow at most 2x the default total payment timeout. if cfg.TotalPaymentTimeout > 2*defaultTotalPaymentTimeout { - return fmt.Errorf("max total payment timeout allowed is at "+ - "most %v", 2*defaultTotalPaymentTimeout) + return fmt.Errorf("max total payment timeout allowed is "+ + "at most %v", 2*defaultTotalPaymentTimeout) } // At least one retry. @@ -389,11 +388,13 @@ func Validate(cfg *Config) error { // TLS Validity period to be at least 24 hours if cfg.TLSValidity < time.Hour*24 { - return fmt.Errorf("TLS certificate minimum validity period is 24h") + return fmt.Errorf("TLS certificate minimum validity period " + + "is 24h") } if cfg.MigrationRPCBatchSize <= 0 { - return fmt.Errorf("migrationrpcbatchsize must be greater than 0") + return fmt.Errorf("migrationrpcbatchsize must be greater " + + "than 0") } // Initialize the log manager with the actual logging configuration. We @@ -425,8 +426,10 @@ func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials, // If the certificate expired or it was outdated, delete it and the TLS // key and generate a new pair. if time.Now().After(parsedCert.NotAfter) { - infof("TLS certificate is expired or outdated, " + - "removing old file then generating a new one") + infof( + "TLS certificate is expired or outdated, removing " + + "old file then generating a new one", + ) err := os.Remove(cfg.TLSCertPath) if err != nil { diff --git a/loopd/daemon.go b/loopd/daemon.go index 880e19621..c9ed9ba5f 100644 --- a/loopd/daemon.go +++ b/loopd/daemon.go @@ -162,11 +162,12 @@ func (d *Daemon) Start() error { // and we can just return the error. err = d.initialize(true) if errors.Is(err, bbolt.ErrTimeout) { + // We're trying to be started as a standalone Loop daemon, most // likely LiT is already running and blocking the DB return fmt.Errorf("%v: make sure no other loop daemon process "+ - "(standalone or embedded in lightning-terminal) is"+ - "running", err) + "(standalone or embedded in lightning-terminal) "+ + "isrunning", err) } if err != nil { return err @@ -182,6 +183,7 @@ func (d *Daemon) Start() error { if stopErr != nil { errorf("Error while stopping daemon: %v", stopErr) } + return startErr } @@ -212,11 +214,13 @@ func (d *Daemon) StartAsSubserver(lndGrpc *lndclient.GrpcLndServices, // can just return the error. err := d.initialize(withMacaroonService) if errors.Is(err, bbolt.ErrTimeout) { + // We're trying to be started inside LiT so there most likely is // another standalone Loop process blocking the DB. - return fmt.Errorf("%v: make sure no other loop daemon "+ - "process is running", err) + return fmt.Errorf("%v: make sure no other loop daemon process "+ + "is running", err) } + return err } @@ -337,8 +341,10 @@ func (d *Daemon) startWebServers() error { } d.wg.Go(func() { - infof("REST proxy listening on %s", - d.restListener.Addr()) + infof( + "REST proxy listening on %s", + d.restListener.Addr(), + ) err := d.restServer.Serve(d.restListener) // ErrServerClosed is always returned when the proxy is // shut down, so don't log it. @@ -443,8 +449,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { } sweeperDb := sweepbatcher.NewSQLStore( - loopdb.NewTypedStore[sweepbatcher.Querier](baseDb), - chainParams, + loopdb.NewTypedStore[sweepbatcher.Querier](baseDb), chainParams, ) // We need to know the current block height to properly initialize @@ -465,12 +470,13 @@ func (d *Daemon) initialize(withMacaroonService bool) error { d.mainCtx, &taprpc.GetInfoRequest{}, ) if err != nil { - return fmt.Errorf("unable to get asset client info: %v", err) + return fmt.Errorf("unable to get asset client info: %v", + err) } if getInfo.LndIdentityPubkey != d.lnd.NodePubkey.String() { - return fmt.Errorf("asset client pubkey %v does not match "+ - "lnd pubkey %v", getInfo.LndIdentityPubkey, - d.lnd.NodePubkey) + return fmt.Errorf("asset client pubkey %v does not "+ + "match lnd pubkey %v", + getInfo.LndIdentityPubkey, d.lnd.NodePubkey) } infof("Using asset client with version %v", getInfo.Version) @@ -542,6 +548,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { ) if err != nil { cleanupMacaroonStore() + return err } @@ -551,6 +558,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { // shut down at this point. cleanupMacaroonStore() clientCleanup() + return err } } @@ -610,8 +618,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { // Static address deposit withdrawal manager setup. withdrawalStore := withdraw.NewSqlStore( - loopdb.NewTypedStore[withdraw.Querier](baseDb), - depositStore, + loopdb.NewTypedStore[withdraw.Querier](baseDb), depositStore, ) withdrawalCfg := &withdraw.ManagerConfig{ StaticAddressServerClient: staticAddressClient, @@ -668,22 +675,26 @@ func (d *Daemon) initialize(withMacaroonService bool) error { } staticLoopInManager, err = loopin.NewManager(&loopin.Config{ - Server: staticAddressClient, - QuoteGetter: swapClient.Server, - LndClient: d.lnd.Client, - InvoicesClient: d.lnd.Invoices, - NodePubkey: d.lnd.NodePubkey, - AddressManager: staticAddressManager, - DepositManager: depositManager, - Store: staticAddressLoopInStore, - WalletKit: d.lnd.WalletKit, - ChainNotifier: d.lnd.ChainNotifier, - NotificationManager: notificationManager, - ChainParams: d.lnd.ChainParams, - Signer: d.lnd.Signer, - ValidateLoopInContract: loop.ValidateLoopInContract, - MaxStaticAddrHtlcFeePercentage: d.cfg.MaxStaticAddrHtlcFeePercentage, - MaxStaticAddrHtlcBackupFeePercentage: d.cfg.MaxStaticAddrHtlcBackupFeePercentage, + Server: staticAddressClient, + QuoteGetter: swapClient.Server, + LndClient: d.lnd.Client, + InvoicesClient: d.lnd.Invoices, + NodePubkey: d.lnd.NodePubkey, + AddressManager: staticAddressManager, + DepositManager: depositManager, + Store: staticAddressLoopInStore, + WalletKit: d.lnd.WalletKit, + ChainNotifier: d.lnd.ChainNotifier, + NotificationManager: notificationManager, + ChainParams: d.lnd.ChainParams, + Signer: d.lnd.Signer, + ValidateLoopInContract: loop.ValidateLoopInContract, + MaxStaticAddrHtlcFeePercentage: d. + cfg. + MaxStaticAddrHtlcFeePercentage, + MaxStaticAddrHtlcBackupFeePercentage: d. + cfg. + MaxStaticAddrHtlcBackupFeePercentage, }, blockHeight) if err != nil { return fmt.Errorf("unable to create loop-in manager: %w", err) @@ -762,6 +773,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { if d.macaroonService == nil { cleanupMacaroonStore() clientCleanup() + return err } @@ -774,6 +786,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { } cleanupMacaroonStore() clientCleanup() + return err } @@ -836,6 +849,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { select { case <-timeOutCtx.Done(): cancel() + return fmt.Errorf("reservation server not ready: %v", timeOutCtx.Err()) @@ -865,6 +879,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { select { case <-timeOutCtx.Done(): cancel() + return fmt.Errorf("instantout server not ready: %v", timeOutCtx.Err()) @@ -894,6 +909,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { select { case <-timeOutCtx.Done(): cancel() + return fmt.Errorf("static address manager not "+ "ready: %v", timeOutCtx.Err()) @@ -923,8 +939,9 @@ func (d *Daemon) initialize(withMacaroonService bool) error { select { case <-timeOutCtx.Done(): cancel() - return fmt.Errorf("static address deposit manager "+ - "not ready: %v", timeOutCtx.Err()) + + return fmt.Errorf("static address deposit manager not "+ + "ready: %v", timeOutCtx.Err()) case <-initChan: cancel() @@ -957,6 +974,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { select { case <-timeOutCtx.Done(): cancel() + return fmt.Errorf("static address withdrawal manager "+ "server not ready: %v", timeOutCtx.Err()) @@ -988,16 +1006,17 @@ func (d *Daemon) initialize(withMacaroonService bool) error { } }) - // Wait for the static address loop-in manager to be ready before - // starting the grpc server. + // Wait for the static address loop-in manager to be ready + // before starting the grpc server. timeOutCtx, cancel := context.WithTimeout( d.mainCtx, initManagerTimeout, ) select { case <-timeOutCtx.Done(): cancel() - return fmt.Errorf("static address loop-in manager "+ - "not ready: %v", timeOutCtx.Err()) + + return fmt.Errorf("static address loop-in manager not "+ + "ready: %v", timeOutCtx.Err()) case <-initChan: cancel() @@ -1005,8 +1024,8 @@ func (d *Daemon) initialize(withMacaroonService bool) error { } loop.Resume( - d.mainCtx, notificationManager, swapClient.Store, - d.impl.Conn, d.lnd, clock.NewDefaultClock(), + d.mainCtx, notificationManager, swapClient.Store, d.impl.Conn, + d.lnd, clock.NewDefaultClock(), ) // Last, start our internal error handler. This will return exactly one @@ -1024,8 +1043,10 @@ func (d *Daemon) initialize(withMacaroonService bool) error { // signal the caller that we're done. select { case runtimeErr = <-d.internalErrChan: - errorf("Runtime error in daemon, shutting down: "+ - "%v", runtimeErr) + errorf( + "Runtime error in daemon, shutting down: %v", + runtimeErr, + ) case <-d.quit: } diff --git a/loopd/log.go b/loopd/log.go index 6d102bc59..e12c0e6d7 100644 --- a/loopd/log.go +++ b/loopd/log.go @@ -87,7 +87,8 @@ func SetupLoggers(root *build.SubLoggerManager, intercept signal.Interceptor) { root, instantout.Subsystem, intercept, instantout.UseLogger, ) lnd.AddSubLogger( - root, notifications.Subsystem, intercept, notifications.UseLogger, + root, notifications.Subsystem, intercept, + notifications.UseLogger, ) lnd.AddSubLogger( root, sweep.Subsystem, intercept, sweep.UseLogger, diff --git a/loopd/swapclient_server.go b/loopd/swapclient_server.go index 62187d3e5..67abe2193 100644 --- a/loopd/swapclient_server.go +++ b/loopd/swapclient_server.go @@ -71,15 +71,12 @@ var ( // errBalanceTooLow is returned when the loop out amount can't be // satisfied given total balance of the selection of channels to loop // out on. - errBalanceTooLow = errors.New( - "channel balance too low for loop out amount", - ) + errBalanceTooLow = errors.New("channel balance too low for loop out " + + "amount") // errInvalidAddress is returned when the destination address is of // an unsupported format such as P2PK or P2TR addresses. - errInvalidAddress = errors.New( - "invalid or unsupported address", - ) + errInvalidAddress = errors.New("invalid or unsupported address") ) // swapClientServer implements the grpc service exposed by loopd. @@ -117,8 +114,7 @@ type swapClientServer struct { // progress can be tracked via the LoopOutStatus stream that is returned from // Monitor(). func (s *swapClientServer) LoopOut(ctx context.Context, - in *looprpc.LoopOutRequest) ( - *looprpc.SwapResponse, error) { + in *looprpc.LoopOutRequest) (*looprpc.SwapResponse, error) { infof("Loop out request received") @@ -154,7 +150,8 @@ func (s *swapClientServer) LoopOut(ctx context.Context, isExternalAddr = true - case in.Account != "" && in.AccountAddrType == looprpc.AddressType_ADDRESS_TYPE_UNKNOWN: + case in.Account != "" && + in.AccountAddrType == looprpc.AddressType_ADDRESS_TYPE_UNKNOWN: return nil, liquidity.ErrAccountAndAddrType case in.Account != "": @@ -174,8 +171,8 @@ func (s *swapClientServer) LoopOut(ctx context.Context, ctx, in.Account, addrType, false, ) if err != nil { - return nil, fmt.Errorf("NextAddr from account error: "+ - "%v", err) + return nil, fmt.Errorf("NextAddr from account "+ + "error: %v", err) } isExternalAddr = true @@ -200,7 +197,9 @@ func (s *swapClientServer) LoopOut(ctx context.Context, } // Infer if the publication deadline is set in milliseconds. - publicationDeadline := getPublicationDeadline(in.SwapPublicationDeadline) + publicationDeadline := getPublicationDeadline( + in.SwapPublicationDeadline, + ) req := &loop.OutRequest{ Amount: btcutil.Amount(in.Amt), @@ -224,26 +223,20 @@ func (s *swapClientServer) LoopOut(ctx context.Context, if in.AssetInfo != nil { if len(in.AssetInfo.AssetId) != 0 && len(in.AssetInfo.AssetId) != 32 { - - return nil, fmt.Errorf( - "asset id must be set to a 32 byte value", - ) + return nil, fmt.Errorf("asset id must be set to a 32 " + + "byte value") } if len(in.AssetRfqInfo.PrepayRfqId) != 0 && len(in.AssetRfqInfo.PrepayRfqId) != 32 { - - return nil, fmt.Errorf( - "prepay rfq id must be set to a 32 byte value", - ) + return nil, fmt.Errorf("prepay rfq id must be set to " + + "a 32 byte value") } if len(in.AssetRfqInfo.SwapRfqId) != 0 && len(in.AssetRfqInfo.SwapRfqId) != 32 { - - return nil, fmt.Errorf( - "swap rfq id must be set to a 32 byte value", - ) + return nil, fmt.Errorf("swap rfq id must be set to a " + + "32 byte value") } req.AssetId = in.AssetInfo.AssetId @@ -252,12 +245,15 @@ func (s *swapClientServer) LoopOut(ctx context.Context, } switch { - case in.LoopOutChannel != 0 && len(in.OutgoingChanSet) > 0: // nolint:staticcheck - return nil, errors.New("loop_out_channel and outgoing_" + - "chan_ids are mutually exclusive") + case in.LoopOutChannel != 0 && + len(in.OutgoingChanSet) > 0: // nolint:staticcheck + return nil, errors.New("loop_out_channel and " + + "outgoing_chan_ids are mutually exclusive") case in.LoopOutChannel != 0: // nolint:staticcheck - req.OutgoingChanSet = loopdb.ChannelSet{in.LoopOutChannel} // nolint:staticcheck + req.OutgoingChanSet = loopdb.ChannelSet{ + in.LoopOutChannel, + } // nolint:staticcheck default: req.OutgoingChanSet = in.OutgoingChanSet @@ -266,6 +262,7 @@ func (s *swapClientServer) LoopOut(ctx context.Context, info, err := s.impl.LoopOut(ctx, req) if err != nil { errorf("LoopOut: %v", err) + return nil, err } @@ -441,7 +438,9 @@ func (s *swapClientServer) marshallSwap(ctx context.Context, } assetInfo = &looprpc.AssetLoopOutInfo{ - AssetId: hex.EncodeToString(loopSwap.AssetSwapInfo.AssetId), // nolint:lll + AssetId: hex.EncodeToString( + loopSwap.AssetSwapInfo.AssetId, + ), // nolint:lll AssetCostOffchain: loopSwap.AssetSwapInfo.PrepayPaidAmt + loopSwap.AssetSwapInfo.SwapPaidAmt, // nolint:lll AssetName: assetName, @@ -636,6 +635,7 @@ func (s *swapClientServer) ListSwaps(ctx context.Context, Swaps: rpcSwaps, NextStartTime: nextStartTime, } + return &response, nil } @@ -666,10 +666,10 @@ func filterSwap(swapInfo *loop.SwapInfo, filter *looprpc.ListSwapsFilter) bool { return false } - // If timestamp filters are set, only return swaps within the specified time range. + // If timestamp filters are set, only return swaps within the specified + // time range. if filter.StartTimestampNs > 0 && swapInfo.InitiationTime.UnixNano() < filter.StartTimestampNs { - return false } @@ -683,9 +683,9 @@ func filterSwap(swapInfo *loop.SwapInfo, filter *looprpc.ListSwapsFilter) bool { // Compare the outgoing channel set by using reflect.DeepEqual // which compares the underlying arrays. - if !reflect.DeepEqual(swapInfo.OutgoingChanSet, - filter.OutgoingChanSet) { - + if !reflect.DeepEqual( + swapInfo.OutgoingChanSet, filter.OutgoingChanSet, + ) { return false } } @@ -732,13 +732,13 @@ func (s *swapClientServer) SwapInfo(ctx context.Context, if !ok { return nil, fmt.Errorf("swap with hash %s not found", req.Id) } + return s.marshallSwap(ctx, &swp) } // AbandonSwap requests the server to abandon a swap with the given hash. func (s *swapClientServer) AbandonSwap(ctx context.Context, - req *looprpc.AbandonSwapRequest) (*looprpc.AbandonSwapResponse, - error) { + req *looprpc.AbandonSwapRequest) (*looprpc.AbandonSwapResponse, error) { if !req.IKnowWhatIAmDoing { return nil, fmt.Errorf("please read the AbandonSwap API " + @@ -787,6 +787,7 @@ func (s *swapClientServer) LoopOutTerms(ctx context.Context, terms, err := s.impl.LoopOutTerms(ctx, defaultLoopdInitiator) if err != nil { errorf("Terms request: %v", err) + return nil, err } @@ -824,9 +825,8 @@ func (s *swapClientServer) LoopOutQuote(ctx context.Context, if req.AssetInfo != nil { if req.AssetInfo.AssetId == nil || req.AssetInfo.AssetEdgeNode == nil { - - return nil, fmt.Errorf( - "asset id and edge node must both be set") + return nil, fmt.Errorf("asset id and edge node must " + + "both be set") } loopOutQuoteReq.AssetRFQRequest = &loop.AssetRFQRequest{ AssetId: req.AssetInfo.AssetId, @@ -877,6 +877,7 @@ func (s *swapClientServer) GetLoopInTerms(ctx context.Context, terms, err := s.impl.LoopInTerms(ctx, defaultLoopdInitiator) if err != nil { errorf("Terms request: %v", err) + return nil, err } @@ -901,7 +902,10 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context, htlcConfTarget, err := validateLoopInRequest( req.ConfTarget, req.ExternalHtlc, - uint32(len(req.DepositOutpoints)), selectedAmount, + uint32( + len(req.DepositOutpoints), + ), + selectedAmount, autoSelectDeposits, ) if err != nil { @@ -911,8 +915,8 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context, // The fast flag is only available for static loop in quote requests. if req.Fast { if !autoSelectDeposits && len(req.DepositOutpoints) == 0 { - return nil, fmt.Errorf("fast flag is only " + - "available for static address requests") + return nil, fmt.Errorf("fast flag is only available " + + "for static address requests") } } @@ -972,7 +976,6 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context, if len(req.DepositOutpoints) != len(depositList.FilteredDeposits) { - return nil, fmt.Errorf("expected %d deposits, got %d", len(req.DepositOutpoints), len(depositList.FilteredDeposits)) @@ -996,9 +999,8 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context, totalDepositAmount, selectedAmount, ) if err != nil { - return nil, fmt.Errorf("error calculating "+ - "swap amount from selected amount: %v", - err) + return nil, fmt.Errorf("error calculating swap amount "+ + "from selected amount: %v", err) } } @@ -1171,6 +1173,7 @@ func (s *swapClientServer) LoopIn(ctx context.Context, swapInfo, err := s.impl.LoopIn(ctx, req) if err != nil { errorf("Loop in: %v", err) + return nil, err } @@ -1213,7 +1216,9 @@ func (s *swapClientServer) GetL402Tokens(ctx context.Context, } id, err := l402.DecodeIdentifier( - bytes.NewReader(token.BaseMacaroon().Id()), + bytes.NewReader( + token.BaseMacaroon().Id(), + ), ) if err != nil { return nil, err @@ -1247,8 +1252,10 @@ func (s *swapClientServer) GetL402Tokens(ctx context.Context, func (s *swapClientServer) GetLsatTokens(ctx context.Context, req *looprpc.TokensRequest) (*looprpc.TokensResponse, error) { - warnf("Received deprecated call GetLsatTokens. Please update the " + - "client software. Calling GetL402Tokens now.") + warnf( + "Received deprecated call GetLsatTokens. Please update the " + + "client software. Calling GetL402Tokens now.", + ) return s.GetL402Tokens(ctx, req) } @@ -1353,8 +1360,9 @@ func (s *swapClientServer) StopDaemon(ctx context.Context, // Ensure we have a shutdown handler to invoke. if s.stopDaemon == nil { - return nil, status.Error(codes.Unimplemented, - "stop daemon not supported") + return nil, status.Error( + codes.Unimplemented, "stop daemon not supported", + ) } // Initiate the shutdown sequence. @@ -1369,8 +1377,8 @@ func (s *swapClientServer) SweepHtlc(ctx context.Context, req *looprpc.SweepHtlcRequest) (*looprpc.SweepHtlcResponse, error) { return sweepHtlc( - ctx, req, s.lnd.ChainParams, s.impl.Store, - s.lnd.ChainNotifier, s.lnd.WalletKit, s.lnd.Signer, + ctx, req, s.lnd.ChainParams, s.impl.Store, s.lnd.ChainNotifier, + s.lnd.WalletKit, s.lnd.Signer, ) } @@ -1392,8 +1400,8 @@ func (s *swapClientServer) GetLiquidityParams(_ context.Context, // SetLiquidityParams attempts to set our current liquidity manager's // parameters. func (s *swapClientServer) SetLiquidityParams(ctx context.Context, - in *looprpc.SetLiquidityParamsRequest) (*looprpc.SetLiquidityParamsResponse, - error) { + in *looprpc.SetLiquidityParamsRequest) ( + *looprpc.SetLiquidityParamsResponse, error) { err := s.liquidityMgr.SetParameters(ctx, in.Parameters) if err != nil { @@ -1414,7 +1422,6 @@ func (s *swapClientServer) SuggestSwaps(ctx context.Context, return nil, status.Error(codes.FailedPrecondition, err.Error()) case nil: - default: return nil, err } @@ -1492,12 +1499,13 @@ func (s *swapClientServer) SuggestSwaps(ctx context.Context, // ListReservations lists all existing reservations the client has ever made. func (s *swapClientServer) ListReservations(ctx context.Context, - _ *looprpc.ListReservationsRequest) ( - *looprpc.ListReservationsResponse, error) { + _ *looprpc.ListReservationsRequest) (*looprpc.ListReservationsResponse, + error) { if s.reservationManager == nil { - return nil, status.Error(codes.Unimplemented, - "Restart loop with --experimental") + return nil, status.Error( + codes.Unimplemented, "Restart loop with --experimental", + ) } reservations, err := s.reservationManager.GetReservations( ctx, @@ -1515,12 +1523,12 @@ func (s *swapClientServer) ListReservations(ctx context.Context, // InstantOut initiates an instant out swap. func (s *swapClientServer) InstantOut(ctx context.Context, - req *looprpc.InstantOutRequest) (*looprpc.InstantOutResponse, - error) { + req *looprpc.InstantOutRequest) (*looprpc.InstantOutResponse, error) { if s.instantOutManager == nil { - return nil, status.Error(codes.Unimplemented, - "Restart loop with --experimental") + return nil, status.Error( + codes.Unimplemented, "Restart loop with --experimental", + ) } reservationIds := make([]reservation.ID, len(req.ReservationIds)) @@ -1559,12 +1567,13 @@ func (s *swapClientServer) InstantOut(ctx context.Context, // InstantOutQuote returns a quote for an instant out swap with the provided // parameters. func (s *swapClientServer) InstantOutQuote(ctx context.Context, - req *looprpc.InstantOutQuoteRequest) ( - *looprpc.InstantOutQuoteResponse, error) { + req *looprpc.InstantOutQuoteRequest) (*looprpc.InstantOutQuoteResponse, + error) { if s.instantOutManager == nil { - return nil, status.Error(codes.Unimplemented, - "Restart loop with --experimental") + return nil, status.Error( + codes.Unimplemented, "Restart loop with --experimental", + ) } quote, err := s.instantOutManager.GetInstantOutQuote( @@ -1583,12 +1592,13 @@ func (s *swapClientServer) InstantOutQuote(ctx context.Context, // ListInstantOuts returns a list of all currently known instant out swaps and // their current status. func (s *swapClientServer) ListInstantOuts(ctx context.Context, - _ *looprpc.ListInstantOutsRequest) ( - *looprpc.ListInstantOutsResponse, error) { + _ *looprpc.ListInstantOutsRequest) (*looprpc.ListInstantOutsResponse, + error) { if s.instantOutManager == nil { - return nil, status.Error(codes.Unimplemented, - "Restart loop with --experimental") + return nil, status.Error( + codes.Unimplemented, "Restart loop with --experimental", + ) } instantOuts, err := s.instantOutManager.ListInstantOuts(ctx) @@ -1629,8 +1639,8 @@ func rpcInstantOut(instantOut *instantout.InstantOut) *looprpc.InstantOut { // NewStaticAddress is the rpc endpoint for loop clients to request a new static // address. func (s *swapClientServer) NewStaticAddress(ctx context.Context, - _ *looprpc.NewStaticAddressRequest) ( - *looprpc.NewStaticAddressResponse, error) { + _ *looprpc.NewStaticAddressRequest) (*looprpc.NewStaticAddressResponse, + error) { staticAddress, expiry, err := s.staticAddressManager.NewAddress(ctx) if err != nil { @@ -1790,7 +1800,6 @@ func (s *swapClientServer) ListStaticAddressDeposits(ctx context.Context, outpoints := req.Outpoints if req.StateFilter != looprpc.DepositState_UNKNOWN_STATE && len(outpoints) > 0 { - return nil, fmt.Errorf("can either filter by state or " + "outpoints") } @@ -1815,6 +1824,7 @@ func (s *swapClientServer) ListStaticAddressDeposits(ctx context.Context, } else { f := func(d *deposit.Deposit) bool { if req.StateFilter == looprpc.DepositState_UNKNOWN_STATE { + // Per default, we return deposits in all // states. return true @@ -2112,7 +2122,8 @@ func (s *swapClientServer) StaticAddressLoopIn(ctx context.Context, // Build a list of used deposits for the response. usedDeposits := filter( - loopIn.Deposits, func(d *deposit.Deposit) bool { return true }, + loopIn.Deposits, + func(d *deposit.Deposit) bool { return true }, ) err = s.populateBlocksUntilExpiry(ctx, usedDeposits) @@ -2174,14 +2185,15 @@ func (s *swapClientServer) populateBlocksUntilExpiry(ctx context.Context, deposits[i].ConfirmationHeight + int64(params.Expiry) - bestBlockHeight } + return nil } // StaticOpenChannel initiates an open channel request using static address // deposits. func (s *swapClientServer) StaticOpenChannel(ctx context.Context, - req *looprpc.StaticOpenChannelRequest) (*looprpc.StaticOpenChannelResponse, - error) { + req *looprpc.StaticOpenChannelRequest) ( + *looprpc.StaticOpenChannelResponse, error) { infof("Static open channel request received") @@ -2432,6 +2444,7 @@ func (s *swapClientServer) processStatusUpdates(mainCtx context.Context) { case subscriber <- swp: case <-mainCtx.Done(): s.swapsLock.Unlock() + return } } @@ -2454,8 +2467,8 @@ func validateConfTarget(target, defaultTarget int32) (int32, error) { // Ensure the target respects our minimum threshold. case target < minConfTarget: - return 0, fmt.Errorf("%w: A confirmation target of at "+ - "least %v must be provided", errConfTargetTooLow, + return 0, fmt.Errorf("%w: A confirmation target of at least "+ + "%v must be provided", errConfTargetTooLow, minConfTarget) default: @@ -2536,6 +2549,7 @@ func validateLoopOutRequest(ctx context.Context, lnd lndclient.LightningClient, // If this is an asset payment, we'll check that we have the necessary // outbound asset capacaity to fulfill the request. if req.AssetInfo != nil { + // Todo(sputn1ck) actually check outbound capacity. return validateConfTarget( req.SweepConfTarget, loop.DefaultSweepConfTarget, @@ -2578,14 +2592,15 @@ func validateLoopOutRequest(ctx context.Context, lnd lndclient.LightningClient, // the available channel set and the fact that equal splitting is // used for MPP. requiredBalance := btcutil.Amount(req.Amt + req.MaxSwapRoutingFee) - isRoutable, _ := hasBandwidth(activeChannelSet, requiredBalance, - int(maxParts)) + isRoutable, _ := hasBandwidth( + activeChannelSet, requiredBalance, int(maxParts), + ) if !isRoutable { - return 0, fmt.Errorf("%w: Requested swap amount of %d "+ - "sats along with the maximum routing fee of %d sats "+ - "is more than what can be routed given current state "+ - "of the channel set", errBalanceTooLow, req.Amt, + return 0, fmt.Errorf("%w: Requested swap amount of %d sats "+ + "along with the maximum routing fee of %d sats is "+ + "more than what can be routed given current state of "+ + "the channel set", errBalanceTooLow, req.Amt, req.MaxSwapRoutingFee) } @@ -2605,14 +2620,19 @@ func validateLoopOutRequest(ctx context.Context, lnd lndclient.LightningClient, func hasBandwidth(channels []lndclient.ChannelInfo, amt btcutil.Amount, maxParts int) (bool, int) { - tracef("Checking if %v sats can be routed with %v parts over "+ - "channel set of length %v", amt, maxParts, len(channels)) + tracef( + "Checking if %v sats can be routed with %v parts over "+ + "channel set of length %v", amt, maxParts, + len(channels), + ) localBalances := make([]btcutil.Amount, len(channels)) var totalBandwidth btcutil.Amount for i, channel := range channels { - tracef("Channel %v: local=%v remote=%v", channel.ChannelID, - channel.LocalBalance, channel.RemoteBalance) + tracef( + "Channel %v: local=%v remote=%v", channel.ChannelID, + channel.LocalBalance, channel.RemoteBalance, + ) localBalances[i] = channel.LocalBalance totalBandwidth += channel.LocalBalance @@ -2626,8 +2646,10 @@ func hasBandwidth(channels []lndclient.ChannelInfo, amt btcutil.Amount, logLocalBalances := func(shard int) { tracef("Local balances for %v shards:", shard) for i, balance := range localBalances { - tracef("Channel %v: localBalances[%v]=%v", - channels[i].ChannelID, i, balance) + tracef( + "Channel %v: localBalances[%v]=%v", + channels[i].ChannelID, i, balance, + ) } } @@ -2640,18 +2662,24 @@ func hasBandwidth(channels []lndclient.ChannelInfo, amt btcutil.Amount, // TODO(hieblmi): Consider channel reserves because the // channel can't send its full local balance. if localBalances[i] >= split { - tracef("len(shards)=%v: Local channel "+ - "balance %v can pay %v sats", - shard, localBalances[i], split) + tracef( + "len(shards)=%v: Local channel "+ + "balance %v can pay %v sats", + shard, localBalances[i], split, + ) localBalances[i] -= split - tracef("len(shards)=%v: Subtracted "+ - "%v sats from localBalance[%v]=%v", - shard, split, i, localBalances[i]) + tracef( + "len(shards)=%v: Subtracted %v sats "+ + "from localBalance[%v]=%v", + shard, split, i, localBalances[i], + ) amt -= split - tracef("len(shards)=%v: Remaining total "+ - "amount amt=%v", shard, amt) + tracef( + "len(shards)=%v: Remaining total "+ + "amount amt=%v", shard, amt, + ) paid = true shard++ @@ -2669,21 +2697,27 @@ func hasBandwidth(channels []lndclient.ChannelInfo, amt btcutil.Amount, } if !paid { - tracef("len(shards)=%v: No channel could pay %v "+ - "sats, halving payment to %v and trying again", - split/2) + tracef( + "len(shards)=%v: No channel could pay %v "+ + "sats, halving payment to %v and "+ + "trying again", split/2, + ) split /= 2 } else { - tracef("len(shards)=%v: Payment was made, trying "+ - "to pay remaining sats %v", shard, amt) + tracef( + "len(shards)=%v: Payment was made, trying "+ + "to pay remaining sats %v", shard, amt, + ) split = amt } } - tracef("Payment is not routable, remaining amount that can't be "+ - "sent: %v sats", amt) + tracef( + "Payment is not routable, remaining amount that can't be "+ + "sent: %v sats", amt, + ) logLocalBalances(maxParts) @@ -2699,8 +2733,10 @@ func getPublicationDeadline(unixTimestamp uint64) time.Time { // Likely a millisecond timestamp secs := unixTimestamp / 1000 nsecs := (unixTimestamp % 1000) * 1e6 + return time.Unix(int64(secs), int64(nsecs)) } else { + // Likely a second timestamp return time.Unix(int64(unixTimestamp), 0) } diff --git a/loopd/swapclient_server_debug.go b/loopd/swapclient_server_debug.go index e54da3e5f..aa533a565 100644 --- a/loopd/swapclient_server_debug.go +++ b/loopd/swapclient_server_debug.go @@ -30,12 +30,14 @@ func (d *Daemon) registerDebugServer() { // if one is suggested. This endpoint is only for testing purposes and cannot be // used on mainnet. func (s *swapClientServer) ForceAutoLoop(ctx context.Context, - _ *looprpc.ForceAutoLoopRequest) (*looprpc.ForceAutoLoopResponse, error) { + _ *looprpc.ForceAutoLoopRequest) (*looprpc.ForceAutoLoopResponse, + error) { if s.network == lndclient.NetworkMainnet { return nil, fmt.Errorf("force autoloop not allowed on mainnet") } err := s.liquidityMgr.ForceAutoLoop(ctx) + return &looprpc.ForceAutoLoopResponse{}, err } diff --git a/loopd/swapclient_server_test.go b/loopd/swapclient_server_test.go index a3f29443f..800fa40dc 100644 --- a/loopd/swapclient_server_test.go +++ b/loopd/swapclient_server_test.go @@ -645,8 +645,9 @@ func TestHasBandwidth(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { t.Parallel() - res, shards := hasBandwidth(test.channels, test.amt, - test.maxParts) + res, shards := hasBandwidth( + test.channels, test.amt, test.maxParts, + ) require.Equal(t, test.expectedRes, res) require.Equal(t, test.expectedShards, shards) }) @@ -671,8 +672,10 @@ func TestListSwapsFilterAndPagination(t *testing.T) { SwapContract: loopdb.SwapContract{ InitiationTime: firstSwapStartTime, }, - LastUpdate: time.Now(), - SwapHash: lntypes.Hash{1}, + LastUpdate: time.Now(), + SwapHash: lntypes.Hash{ + 1, + }, SwapType: swap.TypeIn, HtlcAddressP2WSH: testnetAddr, HtlcAddressP2TR: testnetAddr, @@ -686,8 +689,10 @@ func TestListSwapsFilterAndPagination(t *testing.T) { SwapContract: loopdb.SwapContract{ InitiationTime: secondSwapStartTime, }, - LastUpdate: time.Now(), - SwapHash: lntypes.Hash{2}, + LastUpdate: time.Now(), + SwapHash: lntypes.Hash{ + 2, + }, SwapType: swap.TypeOut, HtlcAddressP2WSH: testnetAddr, HtlcAddressP2TR: testnetAddr, @@ -701,8 +706,10 @@ func TestListSwapsFilterAndPagination(t *testing.T) { SwapContract: loopdb.SwapContract{ InitiationTime: thirdSwapStartTime, }, - LastUpdate: time.Now(), - SwapHash: lntypes.Hash{3}, + LastUpdate: time.Now(), + SwapHash: lntypes.Hash{ + 3, + }, SwapType: swap.TypeOut, HtlcAddressP2WSH: testnetAddr, HtlcAddressP2TR: testnetAddr, @@ -715,7 +722,8 @@ func TestListSwapsFilterAndPagination(t *testing.T) { // Define the mock swaps that will be stored in the mock client. mockSwaps []loop.SwapInfo req *looprpc.ListSwapsRequest - // These hashes must be in the correct return order as the response. + // These hashes must be in the correct return order as the + // response. expectedReturnedSwaps []lntypes.Hash expectedNextStartTime int64 }{ @@ -766,7 +774,8 @@ func TestListSwapsFilterAndPagination(t *testing.T) { swapInOrder0.SwapHash, swapOutOrder1.SwapHash, }, - expectedNextStartTime: secondSwapStartTime.UnixNano() + 1, + expectedNextStartTime: secondSwapStartTime.UnixNano() + + 1, }, { name: "fetch with limit set to default", @@ -786,7 +795,9 @@ func TestListSwapsFilterAndPagination(t *testing.T) { mockSwaps: mockSwaps, req: &looprpc.ListSwapsRequest{ ListSwapFilter: &looprpc.ListSwapsFilter{ - StartTimestampNs: unixTime.Add(25 * time.Minute).UnixNano(), + StartTimestampNs: unixTime. + Add(25 * time.Minute). + UnixNano(), }, }, expectedReturnedSwaps: []lntypes.Hash{ @@ -799,7 +810,9 @@ func TestListSwapsFilterAndPagination(t *testing.T) { mockSwaps: mockSwaps, req: &looprpc.ListSwapsRequest{ ListSwapFilter: &looprpc.ListSwapsFilter{ - StartTimestampNs: unixTime.Add(5 * time.Minute).UnixNano(), + StartTimestampNs: unixTime. + Add(5 * time.Minute). + UnixNano(), }, }, expectedReturnedSwaps: []lntypes.Hash{ @@ -814,15 +827,18 @@ func TestListSwapsFilterAndPagination(t *testing.T) { mockSwaps: mockSwaps, req: &looprpc.ListSwapsRequest{ ListSwapFilter: &looprpc.ListSwapsFilter{ - SwapType: looprpc.ListSwapsFilter_LOOP_OUT, - StartTimestampNs: unixTime.Add(15 * time.Minute).UnixNano(), + SwapType: looprpc.ListSwapsFilter_LOOP_OUT, + StartTimestampNs: unixTime. + Add(15 * time.Minute). + UnixNano(), }, MaxSwaps: 1, }, expectedReturnedSwaps: []lntypes.Hash{ swapOutOrder1.SwapHash, }, - expectedNextStartTime: secondSwapStartTime.UnixNano() + 1, + expectedNextStartTime: secondSwapStartTime.UnixNano() + + 1, }, { name: "fetch with time filter, limit set", @@ -837,7 +853,8 @@ func TestListSwapsFilterAndPagination(t *testing.T) { swapInOrder0.SwapHash, swapOutOrder1.SwapHash, }, - expectedNextStartTime: secondSwapStartTime.UnixNano() + 1, + expectedNextStartTime: secondSwapStartTime.UnixNano() + + 1, }, { name: "fetch with time filter, limit set 2", @@ -874,7 +891,8 @@ func TestListSwapsFilterAndPagination(t *testing.T) { mockSwaps: mockSwaps, req: &looprpc.ListSwapsRequest{ ListSwapFilter: &looprpc.ListSwapsFilter{ - StartTimestampNs: secondSwapStartTime.UnixNano() + 1, + StartTimestampNs: secondSwapStartTime.UnixNano() + + 1, }, }, expectedReturnedSwaps: []lntypes.Hash{ @@ -887,7 +905,8 @@ func TestListSwapsFilterAndPagination(t *testing.T) { mockSwaps: mockSwaps, req: &looprpc.ListSwapsRequest{ ListSwapFilter: &looprpc.ListSwapsFilter{ - StartTimestampNs: secondSwapStartTime.UnixNano() - 1, + StartTimestampNs: secondSwapStartTime.UnixNano() - + 1, }, }, expectedReturnedSwaps: []lntypes.Hash{ @@ -913,33 +932,31 @@ func TestListSwapsFilterAndPagination(t *testing.T) { } // Call the ListSwaps method. - resp, err := server.ListSwaps(context.Background(), test.req) + resp, err := server.ListSwaps( + context.Background(), test.req, + ) require.NoError(t, err) require.Len( - t, - resp.Swaps, - len(test.expectedReturnedSwaps), + t, resp.Swaps, len(test.expectedReturnedSwaps), "incorrect returned count", ) // Check order of returned swaps is exactly as expected. for idx, aswap := range resp.Swaps { - newhash, err := lntypes.MakeHash(aswap.GetIdBytes()) + newhash, err := lntypes.MakeHash( + aswap.GetIdBytes(), + ) require.NoError(t, err) require.Equal( - t, - test.expectedReturnedSwaps[idx], - newhash, - "iteration order mismatch", + t, test.expectedReturnedSwaps[idx], + newhash, "iteration order mismatch", ) } require.Equal( - t, - test.expectedNextStartTime, - resp.NextStartTime, - "incorrect next start time", + t, test.expectedNextStartTime, + resp.NextStartTime, "incorrect next start time", ) }) } @@ -954,6 +971,7 @@ func (s *mockAddressStore) CreateStaticAddress(_ context.Context, p *address.Parameters) error { s.params = append(s.params, p) + return nil } @@ -990,8 +1008,8 @@ func (s *mockDepositStore) UpdateDeposit(_ context.Context, return nil } -func (s *mockDepositStore) GetDeposit(_ context.Context, - _ deposit.ID) (*deposit.Deposit, error) { +func (s *mockDepositStore) GetDeposit(_ context.Context, _ deposit.ID) ( + *deposit.Deposit, error) { return nil, nil } @@ -1002,6 +1020,7 @@ func (s *mockDepositStore) DepositForOutpoint(_ context.Context, if d, ok := s.byOutpoint[outpoint]; ok { return d, nil } + return nil, nil } func (s *mockDepositStore) AllDeposits(_ context.Context) ([]*deposit.Deposit, @@ -1026,7 +1045,11 @@ func TestListUnspentDeposits(t *testing.T) { PkScript: pkScript, } - addrStore := &mockAddressStore{params: []*address.Parameters{addrParams}} + addrStore := &mockAddressStore{ + params: []*address.Parameters{ + addrParams, + }, + } // Build an address manager using our mock lnd and fake address store. addrMgr, err := address.NewManager(&address.ManagerConfig{ @@ -1045,7 +1068,9 @@ func TestListUnspentDeposits(t *testing.T) { Confirmations: confs, PkScript: pkScript, OutPoint: wire.OutPoint{ - Hash: chainhash.Hash{byte(idx + 1)}, + Hash: chainhash.Hash{ + byte(idx + 1), + }, Index: idx, }, } @@ -1080,11 +1105,13 @@ func TestListUnspentDeposits(t *testing.T) { utxoBelow, utxoAt, utxoAbove1, utxoAbove2, }) - depMgr := buildDepositMgr(map[wire.OutPoint]fsm.StateType{ - utxoAt.OutPoint: deposit.Deposited, - utxoAbove1.OutPoint: deposit.Withdrawn, - utxoAbove2.OutPoint: deposit.LoopingIn, - }) + depMgr := buildDepositMgr( + map[wire.OutPoint]fsm.StateType{ + utxoAt.OutPoint: deposit.Deposited, + utxoAbove1.OutPoint: deposit.Withdrawn, + utxoAbove2.OutPoint: deposit.LoopingIn, + }, + ) server := &swapClientServer{ staticAddressManager: addrMgr, @@ -1120,11 +1147,13 @@ func TestListUnspentDeposits(t *testing.T) { utxoAbove2, }) - depMgr := buildDepositMgr(map[wire.OutPoint]fsm.StateType{ - utxoAt.OutPoint: deposit.Withdrawn, - utxoAbove1.OutPoint: deposit.Deposited, - utxoAbove2.OutPoint: deposit.Withdrawn, - }) + depMgr := buildDepositMgr( + map[wire.OutPoint]fsm.StateType{ + utxoAt.OutPoint: deposit.Withdrawn, + utxoAbove1.OutPoint: deposit.Deposited, + utxoAbove2.OutPoint: deposit.Withdrawn, + }, + ) server := &swapClientServer{ staticAddressManager: addrMgr, diff --git a/loopd/sweep_htlc.go b/loopd/sweep_htlc.go index c613b5d7c..27eb6f2ef 100644 --- a/loopd/sweep_htlc.go +++ b/loopd/sweep_htlc.go @@ -74,16 +74,19 @@ func sweepHtlc(ctx context.Context, req *looprpc.SweepHtlcRequest, // Make sure that the request has all required inputs. if req.Outpoint == "" { - return nil, status.Error(codes.InvalidArgument, - "outpoint required") + return nil, status.Error( + codes.InvalidArgument, "outpoint required", + ) } if req.HtlcAddress == "" { - return nil, status.Error(codes.InvalidArgument, - "htlc_address required") + return nil, status.Error( + codes.InvalidArgument, "htlc_address required", + ) } if req.SatPerVbyte == 0 { - return nil, status.Error(codes.InvalidArgument, - "sat_per_vbyte required") + return nil, status.Error( + codes.InvalidArgument, "sat_per_vbyte required", + ) } // Parse the inputs. @@ -91,14 +94,14 @@ func sweepHtlc(ctx context.Context, req *looprpc.SweepHtlcRequest, req.HtlcAddress, chainParams, ) if err != nil { - return nil, status.Errorf(codes.InvalidArgument, - "invalid htlc_address: %v", err) + return nil, status.Errorf(codes.InvalidArgument, "invalid "+ + "htlc_address: %v", err) } htlcPkScript, err := txscript.PayToAddrScript(htlcAddr) if err != nil { - return nil, status.Errorf(codes.InvalidArgument, - "invalid htlc_address script: %v", err) + return nil, status.Errorf(codes.InvalidArgument, "invalid "+ + "htlc_address script: %v", err) } htlcOutpoint, err := wire.NewOutPointFromString(req.Outpoint) @@ -120,15 +123,16 @@ func sweepHtlc(ctx context.Context, req *looprpc.SweepHtlcRequest, } else { sweepAddr, err = wallet.NextAddr( ctx, lnwallet.DefaultAccountName, - walletrpc.AddressType_TAPROOT_PUBKEY, - false, + walletrpc.AddressType_TAPROOT_PUBKEY, false, ) if err != nil { - return nil, status.Errorf(codes.Internal, - "derive sweep address: %v", err) + return nil, status.Errorf(codes.Internal, "derive "+ + "sweep address: %v", err) } - infof("sweephtlc: generated new destination address: %v", - sweepAddr.EncodeAddress()) + infof( + "sweephtlc: generated new destination address: %v", + sweepAddr.EncodeAddress(), + ) } sweepPkScript, err := txscript.PayToAddrScript(sweepAddr) @@ -136,8 +140,10 @@ func sweepHtlc(ctx context.Context, req *looprpc.SweepHtlcRequest, return nil, err } - infof("sweephtlc: start sweep for %v -> %v", req.Outpoint, - sweepAddr.EncodeAddress()) + infof( + "sweephtlc: start sweep for %v -> %v", req.Outpoint, + sweepAddr.EncodeAddress(), + ) // Locate the loop-out swap whose HTLC script matches the outpoint so // we can obtain keys and the stored preimage. @@ -153,8 +159,7 @@ func sweepHtlc(ctx context.Context, req *looprpc.SweepHtlcRequest, for _, swp := range swaps { htlc, htlcErr := utils.GetHtlc( - swp.Hash, &swp.Contract.SwapContract, - chainParams, + swp.Hash, &swp.Contract.SwapContract, chainParams, ) if htlcErr != nil { return nil, htlcErr @@ -168,30 +173,35 @@ func sweepHtlc(ctx context.Context, req *looprpc.SweepHtlcRequest, } if targetSwap == nil || targetHtlc == nil { - return nil, status.Error(codes.NotFound, - "no matching swap HTLC found") + return nil, status.Error( + codes.NotFound, "no matching swap HTLC found", + ) } - infof("sweephtlc: matched swap %v at height hint %v", - targetSwap.Hash, targetSwap.Contract.InitiationHeight) + infof( + "sweephtlc: matched swap %v at height hint %v", targetSwap.Hash, + targetSwap.Contract.InitiationHeight, + ) if targetSwap.Contract.InitiationHeight <= 0 { - return nil, status.Errorf(codes.InvalidArgument, - "invalid initiation height %d", + return nil, status.Errorf(codes.InvalidArgument, "invalid "+ + "initiation height %d", targetSwap.Contract.InitiationHeight) } // Wait for a confirmation so we can read the full transaction even if // it's not in our wallet. - infof("sweephtlc: registering conf ntfn for %v hint=%v", - req.Outpoint, targetSwap.Contract.InitiationHeight) + infof( + "sweephtlc: registering conf ntfn for %v hint=%v", req.Outpoint, + targetSwap.Contract.InitiationHeight, + ) confChan, errChan, err := notifier.RegisterConfirmationsNtfn( ctx, &htlcOutpoint.Hash, htlcPkScript, 1, targetSwap.Contract.InitiationHeight, ) if err != nil { - return nil, status.Errorf(codes.Internal, - "register conf ntfn: %v", err) + return nil, status.Errorf(codes.Internal, "register conf "+ + "ntfn: %v", err) } var ( @@ -203,34 +213,42 @@ func sweepHtlc(ctx context.Context, req *looprpc.SweepHtlcRequest, select { case conf := <-confChan: fundingTx = conf.Tx - infof("sweephtlc: funding confirmed at height %v", - conf.BlockHeight) + infof( + "sweephtlc: funding confirmed at height %v", + conf.BlockHeight, + ) case ntfnErr := <-errChan: - infof("sweephtlc: conf ntfn error for %v: %v", - req.Outpoint, ntfnErr) + infof( + "sweephtlc: conf ntfn error for %v: %v", req.Outpoint, + ntfnErr, + ) - return nil, status.Errorf(codes.Internal, - "conf ntfn: %v", ntfnErr) + return nil, status.Errorf(codes.Internal, "conf ntfn: %v", + ntfnErr) case <-ctx.Done(): - infof("sweephtlc: context done waiting for %v: %v", - req.Outpoint, ctx.Err()) + infof( + "sweephtlc: context done waiting for %v: %v", + req.Outpoint, ctx.Err(), + ) - return nil, status.Errorf(codes.DeadlineExceeded, - "waiting for transaction details") + return nil, status.Errorf(codes.DeadlineExceeded, "waiting "+ + "for transaction details") } if int(htlcOutpoint.Index) >= len(fundingTx.TxOut) { - return nil, status.Errorf(codes.InvalidArgument, - "vout %d out of range", htlcOutpoint.Index) + return nil, status.Errorf(codes.InvalidArgument, "vout %d out "+ + "of range", htlcOutpoint.Index) } htlcTxOut = fundingTx.TxOut[htlcOutpoint.Index] if !bytes.Equal(htlcTxOut.PkScript, htlcPkScript) { - return nil, status.Error(codes.InvalidArgument, - "outpoint script does not match HTLC address") + return nil, status.Error( + codes.InvalidArgument, + "outpoint script does not match HTLC address", + ) } infof("sweephtlc: swap hash validated for %v", req.Outpoint) @@ -249,24 +267,28 @@ func sweepHtlc(ctx context.Context, req *looprpc.SweepHtlcRequest, } if preimage.Hash() != targetHtlc.Hash { - return nil, status.Error(codes.InvalidArgument, - "preimage does not match HTLC hash") + return nil, status.Error( + codes.InvalidArgument, + "preimage does not match HTLC hash", + ) } - infof("sweephtlc: sweeping to %v with feerate %v sat/vbyte", - sweepAddr.EncodeAddress(), req.SatPerVbyte) + infof( + "sweephtlc: sweeping to %v with feerate %v sat/vbyte", + sweepAddr.EncodeAddress(), req.SatPerVbyte, + ) // Estimate fee for the success-path spend weight. var estimator input.TxWeightEstimator err = targetHtlc.AddSuccessToEstimator(&estimator) if err != nil { - return nil, status.Errorf(codes.InvalidArgument, - "failed to estimate tx input weight: %v", err) + return nil, status.Errorf(codes.InvalidArgument, "failed to "+ + "estimate tx input weight: %v", err) } err = sweep.AddOutputEstimate(&estimator, sweepAddr) if err != nil { - return nil, status.Errorf(codes.InvalidArgument, - "failed to estimate tx output weight: %v", err) + return nil, status.Errorf(codes.InvalidArgument, "failed to "+ + "estimate tx output weight: %v", err) } // Convert the requested fee rate to sat/kw for fee computation. @@ -276,14 +298,15 @@ func sweepHtlc(ctx context.Context, req *looprpc.SweepHtlcRequest, // Make sure the fee is fine. htlcValue := btcutil.Amount(htlcTxOut.Value) if htlcValue <= fee { - return nil, status.Error(codes.InvalidArgument, - "fee exceeds HTLC value") + return nil, status.Error( + codes.InvalidArgument, "fee exceeds HTLC value", + ) } minRelayFeeRate, err := wallet.MinRelayFee(ctx) if err != nil { - return nil, status.Errorf(codes.Internal, - "min relay fee: %v", err) + return nil, status.Errorf(codes.Internal, "min relay fee: %v", + err) } fee, clamped, err := utils.ClampSweepFee( @@ -291,14 +314,13 @@ func sweepHtlc(ctx context.Context, req *looprpc.SweepHtlcRequest, estimator.Weight(), ) if err != nil { - return nil, status.Errorf(codes.InvalidArgument, - "fee too low for relay after clamp: %v", err) + return nil, status.Errorf(codes.InvalidArgument, "fee too low "+ + "for relay after clamp: %v", err) } if clamped { - return nil, status.Errorf(codes.InvalidArgument, - "fee exceeds %.0f%% of HTLC value; lower sat_per_vbyte", - utils.MaxFeeToAmountRatio*100, - ) + return nil, status.Errorf(codes.InvalidArgument, "fee exceeds "+ + "%.0f%% of HTLC value; lower sat_per_vbyte", + utils.MaxFeeToAmountRatio*100) } // Build the sweep transaction spending the HTLC via the success path. @@ -342,8 +364,10 @@ func sweepHtlc(ctx context.Context, req *looprpc.SweepHtlcRequest, } sig := rawSigs[0] - infof("sweephtlc: witness assembled, tx size=%d vbytes", - sweepTx.SerializeSize()) + infof( + "sweephtlc: witness assembled, tx size=%d vbytes", + sweepTx.SerializeSize(), + ) // Assemble the success witness using the signature and preimage. witness, err := targetHtlc.GenSuccessWitness(sig, preimage) @@ -364,11 +388,15 @@ func sweepHtlc(ctx context.Context, req *looprpc.SweepHtlcRequest, if req.Publish { err = wallet.PublishTransaction( ctx, sweepTx, - labels.LoopOutSweepSuccess(targetSwap.Hash.String()), + labels.LoopOutSweepSuccess( + targetSwap.Hash.String(), + ), ) if err != nil { - errorf("sweephtlc: publish failed for %v: %v", - req.Outpoint, err) + errorf( + "sweephtlc: publish failed for %v: %v", + req.Outpoint, err, + ) return &looprpc.SweepHtlcResponse{ SweepTx: rawTx, diff --git a/loopd/sweep_htlc_test.go b/loopd/sweep_htlc_test.go index 139eca823..a125aff18 100644 --- a/loopd/sweep_htlc_test.go +++ b/loopd/sweep_htlc_test.go @@ -388,7 +388,6 @@ func TestSweepHtlc(t *testing.T) { go func() { select { case <-lnd.SignOutputRawChannel: - case <-ctx.Done(): } }() @@ -452,8 +451,7 @@ func TestSweepHtlc(t *testing.T) { // Invoke sweepHtlc and forward the result. resp, err := sweepHtlc( ctx, req, lnd.ChainParams, store, - lnd.ChainNotifier, lnd.WalletKit, - lnd.Signer, + lnd.ChainNotifier, lnd.WalletKit, lnd.Signer, ) // Handle confirmation registration caused by the call diff --git a/loopd/utils.go b/loopd/utils.go index 9b439ac5a..ca5652300 100644 --- a/loopd/utils.go +++ b/loopd/utils.go @@ -32,23 +32,28 @@ func getClient(cfg *Config, swapDb loopdb.SwapStore, defaultFee = l402.DefaultMaxRoutingFeeSats ) if cfg.MaxL402Cost != defaultCost && cfg.MaxLSATCost != 0 { - return nil, nil, fmt.Errorf("both maxl402cost and maxlsatcost" + - " were specified; they are not allowed together") + return nil, nil, fmt.Errorf("both maxl402cost and " + + "maxlsatcost were specified; they are not allowed " + + "together") } if cfg.MaxL402Fee != defaultFee && cfg.MaxLSATFee != 0 { - return nil, nil, fmt.Errorf("both maxl402fee and maxlsatfee" + - " were specified; they are not allowed together") + return nil, nil, fmt.Errorf("both maxl402fee and maxlsatfee " + + "were specified; they are not allowed together") } clientConfig := &loop.ClientConfig{ - ServerAddress: cfg.Server.Host, - ProxyAddress: cfg.Server.Proxy, - SwapServerNoTLS: cfg.Server.NoTLS, - TLSPathServer: cfg.Server.TLSPath, - Lnd: lnd, - AssetClient: assets, - MaxL402Cost: btcutil.Amount(cfg.MaxL402Cost), - MaxL402Fee: btcutil.Amount(cfg.MaxL402Fee), + ServerAddress: cfg.Server.Host, + ProxyAddress: cfg.Server.Proxy, + SwapServerNoTLS: cfg.Server.NoTLS, + TLSPathServer: cfg.Server.TLSPath, + Lnd: lnd, + AssetClient: assets, + MaxL402Cost: btcutil.Amount( + cfg.MaxL402Cost, + ), + MaxL402Fee: btcutil.Amount( + cfg.MaxL402Fee, + ), LoopOutMaxParts: cfg.LoopOutMaxParts, SkippedTxns: cfg.SkippedTxns, TotalPaymentTimeout: cfg.TotalPaymentTimeout, @@ -58,13 +63,17 @@ func getClient(cfg *Config, swapDb loopdb.SwapStore, } if cfg.MaxL402Cost == defaultCost && cfg.MaxLSATCost != 0 { - warnf("Option maxlsatcost is deprecated and will be " + - "removed. Switch to maxl402cost.") + warnf( + "Option maxlsatcost is deprecated and will be " + + "removed. Switch to maxl402cost.", + ) clientConfig.MaxL402Cost = btcutil.Amount(cfg.MaxLSATCost) } if cfg.MaxL402Fee == defaultFee && cfg.MaxLSATFee != 0 { - warnf("Option maxlsatfee is deprecated and will be " + - "removed. Switch to maxl402fee.") + warnf( + "Option maxlsatfee is deprecated and will be " + + "removed. Switch to maxl402fee.", + ) clientConfig.MaxL402Fee = btcutil.Amount(cfg.MaxLSATFee) } @@ -88,8 +97,10 @@ func openDatabase(cfg *Config, chainParams *chaincfg.Params) (loopdb.SwapStore, ) switch cfg.DatabaseBackend { case DatabaseBackendSqlite: - infof("Opening sqlite3 database at: %v", - cfg.Sqlite.DatabaseFileName) + infof( + "Opening sqlite3 database at: %v", + cfg.Sqlite.DatabaseFileName, + ) db, err = loopdb.NewSqliteStore(cfg.Sqlite, chainParams) if err != nil { @@ -98,8 +109,10 @@ func openDatabase(cfg *Config, chainParams *chaincfg.Params) (loopdb.SwapStore, baseDb = *db.(*loopdb.SqliteSwapStore).BaseDB case DatabaseBackendPostgres: - infof("Opening postgres database at: %v", - cfg.Postgres.DSN(true)) + infof( + "Opening postgres database at: %v", + cfg.Postgres.DSN(true), + ) db, err = loopdb.NewPostgresStore(cfg.Postgres, chainParams) if err != nil { @@ -124,17 +137,22 @@ func getLiquidityManager(client *loop.Client) *liquidity.Manager { initiator string) (*liquidity.Restrictions, error) { if swapType == swap.TypeOut { - outTerms, err := client.Server.GetLoopOutTerms(ctx, initiator) + outTerms, err := client.Server.GetLoopOutTerms( + ctx, initiator, + ) if err != nil { return nil, err } return liquidity.NewRestrictions( - outTerms.MinSwapAmount, outTerms.MaxSwapAmount, + outTerms.MinSwapAmount, + outTerms.MaxSwapAmount, ), nil } - inTerms, err := client.Server.GetLoopInTerms(ctx, initiator) + inTerms, err := client.Server.GetLoopInTerms( + ctx, initiator, + ) if err != nil { return nil, err } diff --git a/loopd/view.go b/loopd/view.go index 73b401d86..9d0467c25 100644 --- a/loopd/view.go +++ b/loopd/view.go @@ -34,8 +34,7 @@ func view(config *Config, lisCfg *ListenerCfg) error { } sweeperDb := sweepbatcher.NewSQLStore( - loopdb.NewTypedStore[sweepbatcher.Querier](baseDb), - chainParams, + loopdb.NewTypedStore[sweepbatcher.Querier](baseDb), chainParams, ) var assetClient *assets.TapdClient @@ -81,8 +80,7 @@ func viewOut(swapClient *loop.Client, chainParams *chaincfg.Params) error { fmt.Printf("OUT %v\n", s.Hash) fmt.Printf(" Created: %v (height %v)\n", - s.Contract.InitiationTime, s.Contract.InitiationHeight, - ) + s.Contract.InitiationTime, s.Contract.InitiationHeight) fmt.Printf(" Preimage: %v\n", s.Contract.Preimage) fmt.Printf(" Htlc address (%s): %v\n", htlc.OutputType, htlc.Address) @@ -91,19 +89,14 @@ func viewOut(swapClient *loop.Client, chainParams *chaincfg.Params) error { s.Contract.OutgoingChanSet) fmt.Printf(" Dest: %v\n", s.Contract.DestAddr) fmt.Printf(" Amt: %v, Expiry: %v\n", - s.Contract.AmountRequested, s.Contract.CltvExpiry, - ) + s.Contract.AmountRequested, s.Contract.CltvExpiry) for i, e := range s.Events { - fmt.Printf(" Update %v, Time %v, State: %v", - i, e.Time, e.State, - ) + fmt.Printf(" Update %v, Time %v, State: %v", i, + e.Time, e.State) if e.State.Type() != loopdb.StateTypePending { fmt.Printf(", Cost: server=%v, onchain=%v, "+ - "offchain=%v", - e.Cost.Server, - e.Cost.Onchain, - e.Cost.Offchain, - ) + "offchain=%v", e.Cost.Server, + e.Cost.Onchain, e.Cost.Offchain) } fmt.Println() @@ -130,18 +123,15 @@ func viewIn(swapClient *loop.Client, chainParams *chaincfg.Params) error { fmt.Printf("IN %v\n", s.Hash) fmt.Printf(" Created: %v (height %v)\n", - s.Contract.InitiationTime, s.Contract.InitiationHeight, - ) + s.Contract.InitiationTime, s.Contract.InitiationHeight) fmt.Printf(" Preimage: %v\n", s.Contract.Preimage) fmt.Printf(" Htlc address (%s): %v\n", htlc.OutputType, htlc.Address) fmt.Printf(" Amt: %v, Expiry: %v\n", - s.Contract.AmountRequested, s.Contract.CltvExpiry, - ) + s.Contract.AmountRequested, s.Contract.CltvExpiry) for i, e := range s.Events { - fmt.Printf(" Update %v, Time %v, State: %v\n", - i, e.Time, e.State, - ) + fmt.Printf(" Update %v, Time %v, State: %v\n", i, + e.Time, e.State) } fmt.Println() } diff --git a/loopdb/codec.go b/loopdb/codec.go index fd3fa6ec6..eb3d8d4eb 100644 --- a/loopdb/codec.go +++ b/loopdb/codec.go @@ -12,6 +12,7 @@ import ( func itob(v uint64) []byte { b := make([]byte, 8) byteOrder.PutUint64(b, v) + return b } diff --git a/loopdb/interface.go b/loopdb/interface.go index 9684035bc..b1347f3a6 100644 --- a/loopdb/interface.go +++ b/loopdb/interface.go @@ -14,7 +14,8 @@ type SwapStore interface { FetchLoopOutSwaps(ctx context.Context) ([]*LoopOut, error) // FetchLoopOutSwap returns the loop out swap with the given hash. - FetchLoopOutSwap(ctx context.Context, hash lntypes.Hash) (*LoopOut, error) + FetchLoopOutSwap(ctx context.Context, + hash lntypes.Hash) (*LoopOut, error) // CreateLoopOut adds an initiated swap to the store. CreateLoopOut(ctx context.Context, hash lntypes.Hash, diff --git a/loopdb/loop.go b/loopdb/loop.go index 867476bc1..395275415 100644 --- a/loopdb/loop.go +++ b/loopdb/loop.go @@ -108,14 +108,13 @@ func (s *Loop) LastUpdate() *LoopEvent { } lastEvent := s.Events[eventCount-1] + return lastEvent } // serializeLoopEvent serializes a state update of a swap. This is used for both // in and out swaps. -func serializeLoopEvent(time time.Time, state SwapStateData) ( - []byte, error) { - +func serializeLoopEvent(time time.Time, state SwapStateData) ([]byte, error) { var b bytes.Buffer if err := binary.Write(&b, byteOrder, time.UnixNano()); err != nil { diff --git a/loopdb/loopin.go b/loopdb/loopin.go index 0982fff0b..9b19c09f0 100644 --- a/loopdb/loopin.go +++ b/loopdb/loopin.go @@ -46,12 +46,12 @@ func (s *LoopIn) LastUpdateTime() time.Time { } // serializeLoopInContract serialize the loop in contract into a byte slice. -func serializeLoopInContract(swap *LoopInContract) ( - []byte, error) { - +func serializeLoopInContract(swap *LoopInContract) ([]byte, error) { var b bytes.Buffer - if err := binary.Write(&b, byteOrder, swap.InitiationTime.UnixNano()); err != nil { + if err := binary.Write( + &b, byteOrder, swap.InitiationTime.UnixNano(), + ); err != nil { return nil, err } @@ -59,7 +59,9 @@ func serializeLoopInContract(swap *LoopInContract) ( return nil, err } - if err := binary.Write(&b, byteOrder, swap.AmountRequested); err != nil { + if err := binary.Write( + &b, byteOrder, swap.AmountRequested, + ); err != nil { return nil, err } @@ -91,7 +93,9 @@ func serializeLoopInContract(swap *LoopInContract) ( return nil, err } - if err := binary.Write(&b, byteOrder, swap.InitiationHeight); err != nil { + if err := binary.Write( + &b, byteOrder, swap.InitiationHeight, + ); err != nil { return nil, err } @@ -140,7 +144,8 @@ func getLabel(bucket *bbolt.Bucket) string { return string(label) } -// deserializeLoopInContract deserializes the loop in contract from a byte slice. +// deserializeLoopInContract deserializes the loop in contract from a byte +// slice. func deserializeLoopInContract(value []byte) (*LoopInContract, error) { r := bytes.NewReader(value) @@ -188,11 +193,15 @@ func deserializeLoopInContract(value []byte) (*LoopInContract, error) { return nil, err } - if err := binary.Read(r, byteOrder, &contract.InitiationHeight); err != nil { + if err := binary.Read( + r, byteOrder, &contract.InitiationHeight, + ); err != nil { return nil, err } - if err := binary.Read(r, byteOrder, &contract.HtlcConfTarget); err != nil { + if err := binary.Read( + r, byteOrder, &contract.HtlcConfTarget, + ); err != nil { return nil, err } @@ -205,7 +214,9 @@ func deserializeLoopInContract(value []byte) (*LoopInContract, error) { contract.LastHop = &lastHop } - if err := binary.Read(r, byteOrder, &contract.ExternalHtlc); err != nil { + if err := binary.Read( + r, byteOrder, &contract.ExternalHtlc, + ); err != nil { return nil, err } diff --git a/loopdb/loopout.go b/loopdb/loopout.go index 7f80af902..1c65e07d4 100644 --- a/loopdb/loopout.go +++ b/loopdb/loopout.go @@ -72,7 +72,8 @@ type LoopOutContract struct { } type LoopOutAssetSwap struct { - // AssetId is the optional asset id that is used to pay the swap invoice. + // AssetId is the optional asset id that is used to pay the swap + // invoice. AssetId []byte // PrepayRfqId is the rfq id that is used to pay the prepay invoice. @@ -98,6 +99,7 @@ func (c ChannelSet) String() string { for i, chanID := range c { channelStrings[i] = strconv.FormatUint(chanID, 10) } + return strings.Join(channelStrings, ",") } @@ -137,8 +139,8 @@ func (s *LoopOut) LastUpdateTime() time.Time { return lastUpdate.Time } -func deserializeLoopOutContract(value []byte, chainParams *chaincfg.Params) ( - *LoopOutContract, error) { +func deserializeLoopOutContract(value []byte, + chainParams *chaincfg.Params) (*LoopOutContract, error) { r := bytes.NewReader(value) @@ -191,10 +193,14 @@ func deserializeLoopOutContract(value []byte, chainParams *chaincfg.Params) ( return nil, err } - if err := binary.Read(r, byteOrder, &contract.MaxPrepayRoutingFee); err != nil { + if err := binary.Read( + r, byteOrder, &contract.MaxPrepayRoutingFee, + ); err != nil { return nil, err } - if err := binary.Read(r, byteOrder, &contract.InitiationHeight); err != nil { + if err := binary.Read( + r, byteOrder, &contract.InitiationHeight, + ); err != nil { return nil, err } @@ -212,11 +218,15 @@ func deserializeLoopOutContract(value []byte, chainParams *chaincfg.Params) ( return nil, err } - if err := binary.Read(r, byteOrder, &contract.SweepConfTarget); err != nil { + if err := binary.Read( + r, byteOrder, &contract.SweepConfTarget, + ); err != nil { return nil, err } - if err := binary.Read(r, byteOrder, &contract.MaxSwapRoutingFee); err != nil { + if err := binary.Read( + r, byteOrder, &contract.MaxSwapRoutingFee, + ); err != nil { return nil, err } @@ -238,12 +248,12 @@ func deserializeLoopOutContract(value []byte, chainParams *chaincfg.Params) ( return &contract, nil } -func serializeLoopOutContract(swap *LoopOutContract) ( - []byte, error) { - +func serializeLoopOutContract(swap *LoopOutContract) ([]byte, error) { var b bytes.Buffer - if err := binary.Write(&b, byteOrder, swap.InitiationTime.UnixNano()); err != nil { + if err := binary.Write( + &b, byteOrder, swap.InitiationTime.UnixNano(), + ); err != nil { return nil, err } @@ -251,7 +261,9 @@ func serializeLoopOutContract(swap *LoopOutContract) ( return nil, err } - if err := binary.Write(&b, byteOrder, swap.AmountRequested); err != nil { + if err := binary.Write( + &b, byteOrder, swap.AmountRequested, + ); err != nil { return nil, err } @@ -287,11 +299,15 @@ func serializeLoopOutContract(swap *LoopOutContract) ( return nil, err } - if err := binary.Write(&b, byteOrder, swap.MaxPrepayRoutingFee); err != nil { + if err := binary.Write( + &b, byteOrder, swap.MaxPrepayRoutingFee, + ); err != nil { return nil, err } - if err := binary.Write(&b, byteOrder, swap.InitiationHeight); err != nil { + if err := binary.Write( + &b, byteOrder, swap.InitiationHeight, + ); err != nil { return nil, err } @@ -304,11 +320,15 @@ func serializeLoopOutContract(swap *LoopOutContract) ( return nil, err } - if err := binary.Write(&b, byteOrder, swap.SweepConfTarget); err != nil { + if err := binary.Write( + &b, byteOrder, swap.SweepConfTarget, + ); err != nil { return nil, err } - if err := binary.Write(&b, byteOrder, swap.MaxSwapRoutingFee); err != nil { + if err := binary.Write( + &b, byteOrder, swap.MaxSwapRoutingFee, + ); err != nil { return nil, err } @@ -319,7 +339,9 @@ func serializeLoopOutContract(swap *LoopOutContract) ( return nil, err } - err = binary.Write(&b, byteOrder, swap.SwapPublicationDeadline.UnixNano()) + err = binary.Write( + &b, byteOrder, swap.SwapPublicationDeadline.UnixNano(), + ) if err != nil { return nil, err } diff --git a/loopdb/meta.go b/loopdb/meta.go index 817957698..2dc6be47f 100644 --- a/loopdb/meta.go +++ b/loopdb/meta.go @@ -76,6 +76,7 @@ func setDBVersion(tx *bbolt.Tx, version uint32) error { scratch := make([]byte, 4) byteOrder.PutUint32(scratch, version) + return metaBucket.Put(dbVersionKey, scratch) } @@ -96,9 +97,8 @@ func syncVersions(db *bbolt.DB, chainParams *chaincfg.Params) error { // user is probably trying to revert to a prior version of lnd. We fail // here to prevent reversions and unintended corruption. case currentVersion > latestDBVersion: - log.Errorf("Refusing to revert from db_version=%d to "+ - "lower version=%d", currentVersion, - latestDBVersion) + log.Errorf("Refusing to revert from db_version=%d to lower "+ + "version=%d", currentVersion, latestDBVersion) return ErrDBReversion @@ -118,8 +118,8 @@ func syncVersions(db *bbolt.DB, chainParams *chaincfg.Params) error { migration := migrations[v] if err := migration(tx, chainParams); err != nil { - log.Infof("Unable to apply migration #%v", - v+1) + log.Infof("Unable to apply migration #%v", v+1) + return err } } diff --git a/loopdb/migrate.go b/loopdb/migrate.go index 954807d67..910220bfa 100644 --- a/loopdb/migrate.go +++ b/loopdb/migrate.go @@ -210,7 +210,10 @@ func (m *MigratorManager) checkLoopOuts(ctx context.Context) error { // Check that the number of loop outs is the same. if len(fromLoopOuts) != len(toLoopOuts) { return NewMigrationError( - fmt.Errorf("from: %d, to: %d", len(fromLoopOuts), len(toLoopOuts)), + fmt.Errorf( + "from: %d, to: %d", len(fromLoopOuts), + len(toLoopOuts), + ), ) } @@ -254,7 +257,10 @@ func (m *MigratorManager) checkLoopIns(ctx context.Context) error { // Check that the number of loop ins is the same. if len(fromLoopIns) != len(toLoopIns) { return NewMigrationError( - fmt.Errorf("from: %d, to: %d", len(fromLoopIns), len(toLoopIns)), + fmt.Errorf( + "from: %d, to: %d", len(fromLoopIns), + len(toLoopIns), + ), ) } @@ -311,7 +317,6 @@ func (m *MigratorManager) checkLiquidityParams(ctx context.Context) error { func equalizeLoopOut(fromLoopOut, toLoopOut *LoopOut) error { if fromLoopOut.Contract.InitiationTime.Unix() != toLoopOut.Contract.InitiationTime.Unix() { - return fmt.Errorf("initiation time mismatch") } @@ -319,12 +324,13 @@ func equalizeLoopOut(fromLoopOut, toLoopOut *LoopOut) error { if fromLoopOut.Contract.SwapPublicationDeadline.Unix() != toLoopOut.Contract.SwapPublicationDeadline.Unix() { - return fmt.Errorf("swap publication deadline mismatch") } toLoopOut.Contract. - SwapPublicationDeadline = fromLoopOut.Contract.SwapPublicationDeadline + SwapPublicationDeadline = fromLoopOut. + Contract. + SwapPublicationDeadline for i, event := range fromLoopOut.Events { if event.Time.Unix() != toLoopOut.Events[i].Time.Unix() { @@ -339,7 +345,6 @@ func equalizeLoopOut(fromLoopOut, toLoopOut *LoopOut) error { func equalizeLoopIns(fromLoopIn, toLoopIn *LoopIn) error { if fromLoopIn.Contract.InitiationTime.Unix() != toLoopIn.Contract.InitiationTime.Unix() { - return fmt.Errorf("initiation time mismatch") } @@ -383,6 +388,7 @@ func (e *migrationError) Unwrap() error { func (e *migrationError) Is(target error) bool { _, ok := target.(*migrationError) + return ok } diff --git a/loopdb/migration_01_costs.go b/loopdb/migration_01_costs.go index 93ba0442f..ba8089ead 100644 --- a/loopdb/migration_01_costs.go +++ b/loopdb/migration_01_costs.go @@ -17,6 +17,7 @@ func migrateCosts(tx *bbolt.Tx, _ *chaincfg.Params) error { if err := migrateCostsForBucket(tx, loopOutBucketKey); err != nil { return err } + return nil } @@ -40,8 +41,7 @@ func migrateCostsForBucket(tx *bbolt.Tx, bucketKey []byte) error { // bucket for this swap from its swaphash. swapBucket := rootBucket.Bucket(swapHash) if swapBucket == nil { - return fmt.Errorf("swap bucket %x not found", - swapHash) + return fmt.Errorf("swap bucket %x not found", swapHash) } // Get the updates bucket. @@ -54,6 +54,7 @@ func migrateCostsForBucket(tx *bbolt.Tx, bucketKey []byte) error { var ids [][]byte err := updatesBucket.ForEach(func(k, v []byte) error { ids = append(ids, k) + return nil }) if err != nil { diff --git a/loopdb/migration_02_swap_publication_deadline.go b/loopdb/migration_02_swap_publication_deadline.go index 5347993f9..c5c434930 100644 --- a/loopdb/migration_02_swap_publication_deadline.go +++ b/loopdb/migration_02_swap_publication_deadline.go @@ -11,7 +11,9 @@ import ( // migrateSwapPublicationDeadline migrates the database to v02, by adding the // SwapPublicationDeadline field to loop out contracts. -func migrateSwapPublicationDeadline(tx *bbolt.Tx, chainParams *chaincfg.Params) error { +func migrateSwapPublicationDeadline(tx *bbolt.Tx, + chainParams *chaincfg.Params) error { + rootBucket := tx.Bucket(loopOutBucketKey) if rootBucket == nil { return errors.New("bucket does not exist") @@ -28,8 +30,7 @@ func migrateSwapPublicationDeadline(tx *bbolt.Tx, chainParams *chaincfg.Params) // bucket for this swap from its swaphash. swapBucket := rootBucket.Bucket(swapHash) if swapBucket == nil { - return fmt.Errorf("swap bucket %x not found", - swapHash) + return fmt.Errorf("swap bucket %x not found", swapHash) } // With the main swap bucket obtained, we'll grab the diff --git a/loopdb/migration_03_last_hop.go b/loopdb/migration_03_last_hop.go index 340f650d0..db8565414 100644 --- a/loopdb/migration_03_last_hop.go +++ b/loopdb/migration_03_last_hop.go @@ -28,8 +28,7 @@ func migrateLastHop(tx *bbolt.Tx, chainParams *chaincfg.Params) error { // bucket for this swap from its swaphash. swapBucket := rootBucket.Bucket(swapHash) if swapBucket == nil { - return fmt.Errorf("swap bucket %x not found", - swapHash) + return fmt.Errorf("swap bucket %x not found", swapHash) } // With the main swap bucket obtained, we'll grab the diff --git a/loopdb/migration_04_updates.go b/loopdb/migration_04_updates.go index cb870a054..7cb535ac3 100644 --- a/loopdb/migration_04_updates.go +++ b/loopdb/migration_04_updates.go @@ -48,8 +48,7 @@ func migrateSwapTypeUpdates(rootBucket *bbolt.Bucket) error { for _, swapHash := range swaps { swapBucket := rootBucket.Bucket(swapHash) if swapBucket == nil { - return fmt.Errorf("swap bucket %x not found", - swapHash) + return fmt.Errorf("swap bucket %x not found", swapHash) } err := migrateSwapUpdates(swapBucket) @@ -80,6 +79,7 @@ func migrateSwapUpdates(swapBucket *bbolt.Bucket) error { // Do not modify inside the for each. err := updatesBucket.ForEach(func(k, v []byte) error { existingStates = append(existingStates, state{id: k, state: v}) + return nil }) if err != nil { diff --git a/loopdb/migration_04_updates_test.go b/loopdb/migration_04_updates_test.go index 37f05466c..c36130305 100644 --- a/loopdb/migration_04_updates_test.go +++ b/loopdb/migration_04_updates_test.go @@ -24,22 +24,120 @@ func TestMigrationUpdates(t *testing.T) { "dbp": legacyDbVersion, }, "loop-in": map[string]any{ - Hex("acae09fec9020b7996042613eede68a9eaf29eb28c21ea9943b19e344365a4bb"): map[string]any{ - "contract": Hex("161b25277262bdb5c7c2827b975b2cbc7eb13e222b30cf88ea6daef4bcf22bdac4116c23071472cb000000000000ea6003f2f513a8fd7958b6a229dfb8835f6ab2c9c63cc3e138784d3e8c0e0ebbdd4e61033f26c40666977ed497eea4694d6dd3f07dbcf037089234ff665cd0a07fea329400007b8a00000000000059a600000000000009ca000077a20000000600000000000000000000000000000000000000000000000000000000000000000000"), + Hex( + "acae09fec9020b7996042613eede68a9eaf29eb28c2" + + "1ea9943b19e344365a4bb", + ): map[string]any{ + "contract": Hex( + "161b25277262bdb5c7c2827b975b2cbc7eb" + + "13e222b30cf88ea6daef4bcf22b" + + "dac4116c23071472cb000000000" + + "000ea6003f2f513a8fd7958b6a2" + + "29dfb8835f6ab2c9c63cc3e1387" + + "84d3e8c0e0ebbdd4e61033f26c4" + + "0666977ed497eea4694d6dd3f07" + + "dbcf037089234ff665cd0a07fea" + + "329400007b8a00000000000059a" + + "600000000000009ca000077a200" + + "000006000000000000000000000" + + "000000000000000000000000000" + + "00000000000000000000", + ), "updates": map[string]any{ - Hex("0000000000000001"): Hex("161b252772cb524508000000000000000000000000000000000000000000000000"), - Hex("0000000000000002"): Hex("161b252837115e9b09ffffffffffff1f6a00000000000000000000000000000000"), - Hex("0000000000000003"): Hex("161b252ab670360d0200000000000009ca00000000000000000000000000000000"), + Hex("0000000000000001"): Hex( + "161b252772cb524508000000000" + + "0000000000000000000" + + "00000000000000000000", + ), + Hex("0000000000000002"): Hex( + "161b252837115e9b09fffffffff" + + "fff1f6a000000000000" + + "00000000000000000000", + ), + Hex("0000000000000003"): Hex( + "161b252ab670360d02000000000" + + "00009ca000000000000" + + "00000000000000000000", + ), }, }, }, "uncharge-swaps": map[string]any{ - Hex("c3b3d7a145dbd2bab5aa1f505305f31ee432fe23b0801f065fac453dd9b1f923"): map[string]any{ - "contract": Hex("161b2526643767387ca76e58c964a8f2b6c0a13392b2dea93bde260226a263fb836954054ed1756b000000000000c350fd11016c6e6263727431333337306e3170303072343775707035366c7671663836753565766135647868686c706c78303733756a70676e3979767977376130766a37746d307678793276683576716471327770657832757270307963717a7279787139377a76757173703570373232733970686a6e6e6e706c3778716e796a78353373706863346c396735306b396e347836703761793577707539306b6673397179397173717a353766676a7a67676838343439377375716b383436787a3333336a713036736c6b38637a323872657466363672796b7876396a746e6a3072683979666a6170777065617265713071396679797a666664676d6874687973617370757565746e6b72306b32376370326173366a750269d66fd2cea620dc06f1f7de7838f0c8b145b82c7033080c398862f3421a23230382cb637badbb07f9926a06ecd88b6150513ea0060dc8d6dc1c1fb623926b0a0f000077d400000000000b458c00000000000005f10000000000000024000077a22c6263727431713271756332666777737971376463617a73666e3332636a7874667671647671366a6c70706574fd0f016c6e626372743530313834306e317030307234377570703563776561306732396d30667434646432726167397870306e726d6a72396c33726b7a717037706a6c34337a6e6d6b64336c79337364713877646d6b7a757163717a7279787139377a767571737035616478717538766168643730743776747165777578366d6d64337977636639767835736476717567753833327230676e373466733971793971737168746773636638386e377664767136716e71307a657775366d7471616e326c7a306e7534737a72376c6b36646d343673336c78726572656e333972616b7a6c777378346c613538733966773630356d6767766b766879716e743339713976737367777879367571707236713273780000000600000000000003f20000000000000000161b25262710ce00"), + Hex( + "c3b3d7a145dbd2bab5aa1f505305f31ee432fe23b08" + + "01f065fac453dd9b1f923", + ): map[string]any{ + "contract": Hex( + "161b2526643767387ca76e58c964a8f2b6c" + + "0a13392b2dea93bde260226a263" + + "fb836954054ed1756b000000000" + + "000c350fd11016c6e6263727431" + + "333337306e31703030723437757" + + "07035366c767166383675356576" + + "6135647868686c706c783037337" + + "56a70676e397976797737613076" + + "6a37746d3076787932766835767" + + "164713277706578327572703079" + + "63717a7279787139377a7675717" + + "3703570373232733970686a6e6e" + + "6e706c3778716e796a783533737" + + "06863346c396735306b396e3478" + + "36703761793577707539306b667" + + "3397179397173717a353766676a" + + "7a67676838343439377375716b3" + + "83436787a3333336a713036736c" + + "6b38637a3238726574663636727" + + "96b7876396a746e6a3072683979" + + "666a61707770656172657130713" + + "96679797a666664676d68746879" + + "73617370757565746e6b72306b3" + + "2376370326173366a750269d66f" + + "d2cea620dc06f1f7de7838f0c8b" + + "145b82c7033080c398862f3421a" + + "23230382cb637badbb07f9926a0" + + "6ecd88b6150513ea0060dc8d6dc" + + "1c1fb623926b0a0f000077d4000" + + "00000000b458c00000000000005" + + "f10000000000000024000077a22" + + "c62637274317132717563326667" + + "77737971376463617a73666e333" + + "2636a7874667671647671366a6c" + + "70706574fd0f016c6e626372743" + + "530313834306e31703030723437" + + "7570703563776561306732396d3" + + "066743464643272616739787030" + + "6e726d6a72396c33726b7a71703" + + "7706a6c34337a6e6d6b64336c79" + + "337364713877646d6b7a7571637" + + "17a7279787139377a7675717370" + + "356164787175387661686437307" + + "43776747165777578366d6d6433" + + "797763663976783573647671756" + + "7753833327230676e3734667339" + + "717939717371687467736366383" + + "86e377664767136716e71307a65" + + "7775366d7471616e326c7a306e7" + + "534737a72376c6b36646d343673" + + "336c78726572656e333972616b7" + + "a6c777378346c61353873396677" + + "3630356d6767766b766879716e7" + + "433397139767373677778793675" + + "717072367132737800000006000" + + "00000000003f200000000000000" + + "00161b25262710ce00", + ), "outgoing-chan-set": nil, "updates": map[string]any{ - Hex("0000000000000001"): Hex("161b252a770e649b01000000000000053900000000000000000000000000000001"), - Hex("0000000000000002"): Hex("161b252ab671bdd90200000000000005f10000000000001a9c0000000000000003"), + Hex("0000000000000001"): Hex( + "161b252a770e649b01000000000" + + "0000539000000000000" + + "00000000000000000001", + ), + Hex("0000000000000002"): Hex( + "161b252ab671bdd902000000000" + + "00005f1000000000000" + + "1a9c0000000000000003", + ), }, }, }, diff --git a/loopdb/migrations.go b/loopdb/migrations.go index 94b68b742..01360fb83 100644 --- a/loopdb/migrations.go +++ b/loopdb/migrations.go @@ -98,8 +98,8 @@ type replacerFile struct { // interface. var _ fs.File = (*replacerFile)(nil) -func newReplacerFile(parent fs.File, replaces map[string]string) (*replacerFile, - error) { +func newReplacerFile(parent fs.File, + replaces map[string]string) (*replacerFile, error) { content, err := io.ReadAll(parent) if err != nil { @@ -141,6 +141,7 @@ func (t *replacerFile) Read(bytes []byte) (int, error) { // // NOTE: This is part of the fs.File interface. func (t *replacerFile) Close() error { + // We already fully read and then closed the file when creating this // instance, so there's nothing to do for us here. return nil diff --git a/loopdb/postgres.go b/loopdb/postgres.go index 3058b1a62..cb0fc8111 100644 --- a/loopdb/postgres.go +++ b/loopdb/postgres.go @@ -134,6 +134,7 @@ func NewPostgresStore(cfg *PostgresConfig, err = baseDB.FixFaultyTimestamps(ctx) if err != nil { log.Errorf("Failed to fix faulty timestamps: %v", err) + return nil, err } diff --git a/loopdb/postgres_fixture.go b/loopdb/postgres_fixture.go index 0b4b5a495..47aef6e69 100644 --- a/loopdb/postgres_fixture.go +++ b/loopdb/postgres_fixture.go @@ -90,6 +90,7 @@ func NewTestPgFixture(t *testing.T, expiry time.Duration) *TestPgFixture { if err != nil { return err } + return testDB.Ping() }) require.NoError(t, err, "Could not connect to docker") diff --git a/loopdb/protocol_version_test.go b/loopdb/protocol_version_test.go index b9bbc69c8..01125597a 100644 --- a/loopdb/protocol_version_test.go +++ b/loopdb/protocol_version_test.go @@ -54,15 +54,20 @@ func TestProtocolVersionSanity(t *testing.T) { versions[len(versions)-1], ) - require.Equal(t, - uint32(CurrentProtocolVersion()), - uint32(CurrentRPCProtocolVersion()), + require.Equal( + t, + uint32( + CurrentProtocolVersion(), + ), + uint32( + CurrentRPCProtocolVersion(), + ), ) EnableExperimentalProtocol() - require.Equal(t, - CurrentProtocolVersion(), + require.Equal( + t, CurrentProtocolVersion(), ProtocolVersion(experimentalRPCProtocolVersion), ) } diff --git a/loopdb/raw_db_test.go b/loopdb/raw_db_test.go index ac52946b0..7f97ffc53 100644 --- a/loopdb/raw_db_test.go +++ b/loopdb/raw_db_test.go @@ -21,6 +21,7 @@ import ( // }, // } . func DumpDB(tx *bbolt.Tx) error { // nolint: unused + return tx.ForEach(func(k []byte, bucket *bbolt.Bucket) error { key := toString(k) fmt.Printf("%v: ", key) @@ -71,8 +72,8 @@ func RestoreDB(tx *bbolt.Tx, data map[string]any) error { subBucket, err := tx.CreateBucket(key) if err != nil { - return fmt.Errorf("create bucket %v: %v", - string(key), err) + return fmt.Errorf("create bucket %v: %v", string(key), + err) } if err := restoreDB(subBucket, value); err != nil { @@ -149,5 +150,6 @@ func Hex(value string) string { if err != nil { panic(err) } + return string(b) } diff --git a/loopdb/sql_store.go b/loopdb/sql_store.go index da83f49ad..05f0c1bad 100644 --- a/loopdb/sql_store.go +++ b/loopdb/sql_store.go @@ -18,9 +18,7 @@ import ( ) // FetchLoopOutSwaps returns all swaps currently in the store. -func (db *BaseDB) FetchLoopOutSwaps(ctx context.Context) ([]*LoopOut, - error) { - +func (db *BaseDB) FetchLoopOutSwaps(ctx context.Context) ([]*LoopOut, error) { var loopOuts []*LoopOut err := db.ExecTx(ctx, NewSqlReadOpts(), func(tx *sqlc.Queries) error { @@ -60,8 +58,8 @@ func (db *BaseDB) FetchLoopOutSwaps(ctx context.Context) ([]*LoopOut, } // FetchLoopOutSwap returns the loop out swap with the given hash. -func (db *BaseDB) FetchLoopOutSwap(ctx context.Context, - hash lntypes.Hash) (*LoopOut, error) { +func (db *BaseDB) FetchLoopOutSwap(ctx context.Context, hash lntypes.Hash) ( + *LoopOut, error) { var loopOut *LoopOut @@ -97,6 +95,7 @@ func (db *BaseDB) CreateLoopOut(ctx context.Context, hash lntypes.Hash, swap *LoopOutContract) error { writeOpts := NewSqlWriteOpts() + return db.ExecTx(ctx, writeOpts, func(tx *sqlc.Queries) error { insertArgs := loopToInsertArgs( hash, &swap.SwapContract, @@ -165,6 +164,7 @@ func (db *BaseDB) BatchCreateLoopOut(ctx context.Context, swaps map[lntypes.Hash]*LoopOutContract) error { writeOpts := NewSqlWriteOpts() + return db.ExecTx(ctx, writeOpts, func(tx *sqlc.Queries) error { for swapHash, swap := range swaps { insertArgs := loopToInsertArgs( @@ -195,6 +195,7 @@ func (db *BaseDB) BatchCreateLoopOut(ctx context.Context, return err } } + return nil }) } @@ -209,9 +210,7 @@ func (db *BaseDB) UpdateLoopOut(ctx context.Context, hash lntypes.Hash, } // FetchLoopInSwaps returns all swaps currently in the store. -func (db *BaseDB) FetchLoopInSwaps(ctx context.Context) ( - []*LoopIn, error) { - +func (db *BaseDB) FetchLoopInSwaps(ctx context.Context) ([]*LoopIn, error) { var loopIns []*LoopIn err := db.ExecTx(ctx, NewSqlReadOpts(), func(tx *sqlc.Queries) error { @@ -252,6 +251,7 @@ func (db *BaseDB) CreateLoopIn(ctx context.Context, hash lntypes.Hash, swap *LoopInContract) error { writeOpts := NewSqlWriteOpts() + return db.ExecTx(ctx, writeOpts, func(tx *sqlc.Queries) error { insertArgs := loopToInsertArgs( hash, &swap.SwapContract, @@ -289,6 +289,7 @@ func (db *BaseDB) BatchCreateLoopIn(ctx context.Context, swaps map[lntypes.Hash]*LoopInContract) error { writeOpts := NewSqlWriteOpts() + return db.ExecTx(ctx, writeOpts, func(tx *sqlc.Queries) error { for swapHash, swap := range swaps { insertArgs := loopToInsertArgs( @@ -338,9 +339,7 @@ func (db *BaseDB) UpdateLoopIn(ctx context.Context, hash lntypes.Hash, // // NOTE: it's the caller's responsibility to encode the param. Atm, // it's encoding using the proto package's `Marshal` method. -func (db *BaseDB) PutLiquidityParams(ctx context.Context, - params []byte) error { - +func (db *BaseDB) PutLiquidityParams(ctx context.Context, params []byte) error { err := db.Queries.UpsertLiquidityParams(ctx, params) if err != nil { return err @@ -354,9 +353,7 @@ func (db *BaseDB) PutLiquidityParams(ctx context.Context, // // NOTE: it's the caller's responsibility to decode the param. Atm, // it's decoding using the proto package's `Unmarshal` method. -func (db *BaseDB) FetchLiquidityParams(ctx context.Context) ([]byte, - error) { - +func (db *BaseDB) FetchLiquidityParams(ctx context.Context) ([]byte, error) { var params []byte params, err := db.Queries.FetchLiquidityParams(ctx) if errors.Is(err, sql.ErrNoRows) { @@ -378,6 +375,7 @@ func (db *BaseDB) updateLoop(ctx context.Context, hash lntypes.Hash, time time.Time, state SwapStateData) error { writeOpts := NewSqlWriteOpts() + return db.ExecTx(ctx, writeOpts, func(tx *sqlc.Queries) error { updateParams := sqlc.InsertSwapUpdateParams{ SwapHash: hash[:], @@ -406,16 +404,31 @@ func (db *BaseDB) BatchInsertUpdate(ctx context.Context, updateData map[lntypes.Hash][]BatchInsertUpdateData) error { writeOpts := NewSqlWriteOpts() + return db.ExecTx(ctx, writeOpts, func(tx *sqlc.Queries) error { for swapHash, updates := range updateData { for _, update := range updates { updateParams := sqlc.InsertSwapUpdateParams{ SwapHash: swapHash[:], UpdateTimestamp: update.Time.UTC(), - UpdateState: int32(update.State.State), - ServerCost: int64(update.State.Cost.Server), - OnchainCost: int64(update.State.Cost.Onchain), - OffchainCost: int64(update.State.Cost.Offchain), + UpdateState: int32( + update.State.State, + ), + ServerCost: int64( + update.State.Cost.Server, + ), + OnchainCost: int64( + update. + State. + Cost. + Onchain, + ), + OffchainCost: int64( + update. + State. + Cost. + Offchain, + ), } if update.State.HtlcTxHash != nil { @@ -439,6 +452,7 @@ func (db *BaseDB) BatchUpdateLoopOutSwapCosts(ctx context.Context, costs map[lntypes.Hash]SwapCost) error { writeOpts := NewSqlWriteOpts() + return db.ExecTx(ctx, writeOpts, func(tx *sqlc.Queries) error { for swapHash, cost := range costs { lastUpdateID, err := tx.GetLastUpdateID( @@ -466,8 +480,8 @@ func (db *BaseDB) BatchUpdateLoopOutSwapCosts(ctx context.Context, } // HasMigration returns true if the migration with the given ID has been done. -func (db *BaseDB) HasMigration(ctx context.Context, migrationID string) ( - bool, error) { +func (db *BaseDB) HasMigration(ctx context.Context, migrationID string) (bool, + error) { migration, err := db.GetMigration(ctx, migrationID) if err != nil && !errors.Is(err, sql.ErrNoRows) { @@ -569,8 +583,7 @@ func swapToHtlcKeysInsertArgs(hash lntypes.Hash, // ConvertLoopOutRow converts a database row containing a loop out swap to a // LoopOut struct. func ConvertLoopOutRow(network *chaincfg.Params, row sqlc.GetLoopOutSwapRow, - updates []sqlc.SwapUpdate) (*LoopOut, - error) { + updates []sqlc.SwapUpdate) (*LoopOut, error) { htlcKeys, err := fetchHtlcKeys( row.SenderScriptPubkey, row.ReceiverScriptPubkey, @@ -599,25 +612,35 @@ func ConvertLoopOutRow(network *chaincfg.Params, row sqlc.GetLoopOutSwapRow, loopOut := &LoopOut{ Contract: &LoopOutContract{ SwapContract: SwapContract{ - Preimage: preimage, - AmountRequested: btcutil.Amount(row.AmountRequested), - HtlcKeys: htlcKeys, - CltvExpiry: row.CltvExpiry, - MaxSwapFee: btcutil.Amount(row.MaxSwapFee), - MaxMinerFee: btcutil.Amount(row.MaxMinerFee), + Preimage: preimage, + AmountRequested: btcutil.Amount( + row.AmountRequested, + ), + HtlcKeys: htlcKeys, + CltvExpiry: row.CltvExpiry, + MaxSwapFee: btcutil.Amount(row.MaxSwapFee), + MaxMinerFee: btcutil.Amount( + row.MaxMinerFee, + ), InitiationHeight: row.InitiationHeight, InitiationTime: row.InitiationTime, Label: row.Label, - ProtocolVersion: ProtocolVersion(row.ProtocolVersion), + ProtocolVersion: ProtocolVersion( + row.ProtocolVersion, + ), }, - DestAddr: destAddress, - IsExternalAddr: row.SingleSweep, - SwapInvoice: row.SwapInvoice, - MaxSwapRoutingFee: btcutil.Amount(row.MaxSwapRoutingFee), - SweepConfTarget: row.SweepConfTarget, - HtlcConfirmations: uint32(row.HtlcConfirmations), - PrepayInvoice: row.PrepayInvoice, - MaxPrepayRoutingFee: btcutil.Amount(row.MaxPrepayRoutingFee), + DestAddr: destAddress, + IsExternalAddr: row.SingleSweep, + SwapInvoice: row.SwapInvoice, + MaxSwapRoutingFee: btcutil.Amount( + row.MaxSwapRoutingFee, + ), + SweepConfTarget: row.SweepConfTarget, + HtlcConfirmations: uint32(row.HtlcConfirmations), + PrepayInvoice: row.PrepayInvoice, + MaxPrepayRoutingFee: btcutil.Amount( + row.MaxPrepayRoutingFee, + ), SwapPublicationDeadline: row.PublicationDeadline, PaymentTimeout: time.Duration( row.PaymentTimeout, @@ -693,16 +716,22 @@ func (db *BaseDB) convertLoopInRow(row sqlc.GetLoopInSwapsRow, loopIn := &LoopIn{ Contract: &LoopInContract{ SwapContract: SwapContract{ - Preimage: preimage, - AmountRequested: btcutil.Amount(row.AmountRequested), - HtlcKeys: htlcKeys, - CltvExpiry: row.CltvExpiry, - MaxSwapFee: btcutil.Amount(row.MaxSwapFee), - MaxMinerFee: btcutil.Amount(row.MaxMinerFee), + Preimage: preimage, + AmountRequested: btcutil.Amount( + row.AmountRequested, + ), + HtlcKeys: htlcKeys, + CltvExpiry: row.CltvExpiry, + MaxSwapFee: btcutil.Amount(row.MaxSwapFee), + MaxMinerFee: btcutil.Amount( + row.MaxMinerFee, + ), InitiationHeight: row.InitiationHeight, InitiationTime: row.InitiationTime, Label: row.Label, - ProtocolVersion: ProtocolVersion(row.ProtocolVersion), + ProtocolVersion: ProtocolVersion( + row.ProtocolVersion, + ), }, HtlcConfTarget: row.HtlcConfTarget, ExternalHtlc: row.ExternalHtlc, @@ -745,16 +774,24 @@ func getSwapEvents(updates []sqlc.SwapUpdate) ([]*LoopEvent, error) { SwapStateData: SwapStateData{ State: SwapState(updates[i].UpdateState), Cost: SwapCost{ - Server: btcutil.Amount(updates[i].ServerCost), - Onchain: btcutil.Amount(updates[i].OnchainCost), - Offchain: btcutil.Amount(updates[i].OffchainCost), + Server: btcutil.Amount( + updates[i].ServerCost, + ), + Onchain: btcutil.Amount( + updates[i].OnchainCost, + ), + Offchain: btcutil.Amount( + updates[i].OffchainCost, + ), }, }, Time: updates[i].UpdateTimestamp.UTC(), } if updates[i].HtlcTxhash != "" { - chainHash, err := chainhash.NewHashFromStr(updates[i].HtlcTxhash) + chainHash, err := chainhash.NewHashFromStr( + updates[i].HtlcTxhash, + ) if err != nil { return nil, err } @@ -773,7 +810,8 @@ func ConvertOutgoingChanSet(outgoingChanSet string) (ChannelSet, error) { chanStrings := strings.Split(outgoingChanSet, ",") channels := make([]uint64, len(chanStrings)) - // Iterate over the chanStrings slice and convert each string to ChannelID + // Iterate over the chanStrings slice and convert each string to + // ChannelID for i, chanString := range chanStrings { chanID, err := strconv.ParseInt(chanString, 10, 64) if err != nil { @@ -787,8 +825,8 @@ func ConvertOutgoingChanSet(outgoingChanSet string) (ChannelSet, error) { // fetchHtlcKeys converts the blob encoded htlc keys into a HtlcKeys struct. func fetchHtlcKeys(senderScriptPubkey, receiverScriptPubkey, - senderInternalPubkey, receiverInternalPubkey []byte, - clientKeyFamily, clientKeyIndex int32) (HtlcKeys, error) { + senderInternalPubkey, receiverInternalPubkey []byte, clientKeyFamily, + clientKeyIndex int32) (HtlcKeys, error) { senderScriptKey, err := blobTo33ByteSlice(senderScriptPubkey) if err != nil { diff --git a/loopdb/sql_test.go b/loopdb/sql_test.go index 8312a7937..6981dc5b0 100644 --- a/loopdb/sql_test.go +++ b/loopdb/sql_test.go @@ -303,8 +303,8 @@ func testSqliteLoopInStore(t *testing.T, pendingSwap LoopInContract) { require.NoError(t, err) } -// TestSqliteLiquidityParams checks that reading and writing to liquidty bucket are -// as expected. +// TestSqliteLiquidityParams checks that reading and writing to liquidty bucket +// are as expected. func TestSqliteLiquidityParams(t *testing.T) { ctxb := context.Background() @@ -558,6 +558,7 @@ func randomString(length int) string { for i := range b { b[i] = charset[rand.Intn(len(charset))] } + return string(b) } @@ -566,6 +567,7 @@ func randomBytes(length int) []byte { for i := range b { b[i] = byte(rand.Intn(256)) } + return b } diff --git a/loopdb/sqlite.go b/loopdb/sqlite.go index 0a0188e55..4bce1e250 100644 --- a/loopdb/sqlite.go +++ b/loopdb/sqlite.go @@ -47,7 +47,9 @@ type SqliteSwapStore struct { // NewSqliteStore attempts to open a new sqlite database based on the passed // config. -func NewSqliteStore(cfg *SqliteConfig, network *chaincfg.Params) (*SqliteSwapStore, error) { +func NewSqliteStore(cfg *SqliteConfig, + network *chaincfg.Params) (*SqliteSwapStore, error) { + // The set of pragma options are accepted using query options. For now // we only want to ensure that foreign key constraints are properly // enforced. @@ -95,9 +97,8 @@ func NewSqliteStore(cfg *SqliteConfig, network *chaincfg.Params) (*SqliteSwapSto // with the series of pragma options as a query URL string. For more // details on the formatting here, see the modernc.org/sqlite docs: // https://pkg.go.dev/modernc.org/sqlite#Driver.Open. - dsn := fmt.Sprintf( - "%v?%v", cfg.DatabaseFileName, sqliteOptions.Encode(), - ) + dsn := fmt.Sprintf("%v?%v", cfg.DatabaseFileName, + sqliteOptions.Encode()) db, err := sql.Open("sqlite", dsn) if err != nil { return nil, err @@ -140,6 +141,7 @@ func NewSqliteStore(cfg *SqliteConfig, network *chaincfg.Params) (*SqliteSwapSto err = baseDB.FixFaultyTimestamps(ctx) if err != nil { log.Errorf("Failed to fix faulty timestamps: %v", err) + return nil, err } @@ -184,12 +186,13 @@ type BaseDB struct { // BeginTx wraps the normal sql specific BeginTx method with the TxOptions // interface. This interface is then mapped to the concrete sql tx options // struct. -func (db *BaseDB) BeginTx(ctx context.Context, - opts TxOptions) (*sql.Tx, error) { +func (db *BaseDB) BeginTx(ctx context.Context, opts TxOptions) (*sql.Tx, + error) { sqlOptions := sql.TxOptions{ ReadOnly: opts.ReadOnly(), } + return db.DB.BeginTx(ctx, &sqlOptions) } @@ -227,7 +230,8 @@ func (db *BaseDB) ExecTx(ctx context.Context, txOptions TxOptions, func (db *BaseDB) FixFaultyTimestamps(ctx context.Context) error { // Manually fetch all the loop out swaps. rows, err := db.DB.QueryContext( - ctx, "SELECT swap_hash, swap_invoice, publication_deadline FROM loopout_swaps", + ctx, "SELECT swap_hash, swap_invoice, publication_deadline "+ + "FROM loopout_swaps", ) if err != nil { return err @@ -249,7 +253,8 @@ func (db *BaseDB) FixFaultyTimestamps(ctx context.Context) error { for rows.Next() { var swap LoopOutRow err := rows.Scan( - &swap.Hash, &swap.SwapInvoice, &swap.PublicationDeadline, + &swap.Hash, &swap.SwapInvoice, + &swap.PublicationDeadline, ) if err != nil { return err diff --git a/loopdb/store.go b/loopdb/store.go index fe71c19cf..6184d55ba 100644 --- a/loopdb/store.go +++ b/loopdb/store.go @@ -77,7 +77,8 @@ var ( // for the serialized swap contract. It is nested within the sub-bucket // for each active swap. // - // path: loopInBucket/loopOutBucket -> swapBucket[hash] -> protocolVersionKey + // path: loopInBucket/loopOutBucket -> swapBucket[hash] -> + // protocolVersionKey // // value: protocol version as specified in server.proto protocolVersionKey = []byte("protocol-version") @@ -180,8 +181,8 @@ type boltSwapStore struct { var _ = (*boltSwapStore)(nil) // NewBoltSwapStore creates a new client swap store. -func NewBoltSwapStore(dbPath string, chainParams *chaincfg.Params) ( - *boltSwapStore, error) { +func NewBoltSwapStore(dbPath string, + chainParams *chaincfg.Params) (*boltSwapStore, error) { // If the target path for the swap store doesn't exist, then we'll // create it now before we proceed. @@ -464,8 +465,8 @@ func (s *boltSwapStore) FetchLoopInSwaps(ctx context.Context) ([]*LoopIn, } // createLoopBucket creates the bucket for a particular swap. -func createLoopBucket(tx *bbolt.Tx, swapTypeKey []byte, hash lntypes.Hash) ( - *bbolt.Bucket, error) { +func createLoopBucket(tx *bbolt.Tx, swapTypeKey []byte, + hash lntypes.Hash) (*bbolt.Bucket, error) { // First, we'll grab the root bucket that houses all of our // swaps of this type. @@ -551,7 +552,8 @@ func (s *boltSwapStore) CreateLoopOut(ctx context.Context, hash lntypes.Hash, } // Store the current protocol version. - err = swapBucket.Put(protocolVersionKey, + err = swapBucket.Put( + protocolVersionKey, MarshalProtocolVersion(swap.ProtocolVersion), ) if err != nil { @@ -567,13 +569,14 @@ func (s *boltSwapStore) CreateLoopOut(ctx context.Context, hash lntypes.Hash, // Finally, we'll create an empty updates bucket for this swap // to track any future updates to the swap itself. _, err = swapBucket.CreateBucket(updatesBucketKey) + return err }) } // UpdateLoopOutAssetInfo is unused for the bolt swap store. -func (db *boltSwapStore) UpdateLoopOutAssetInfo(ctx context.Context, hash lntypes.Hash, - asset *LoopOutAssetSwap) error { +func (db *boltSwapStore) UpdateLoopOutAssetInfo(ctx context.Context, + hash lntypes.Hash, asset *LoopOutAssetSwap) error { return errors.New("unimplemented") } @@ -610,7 +613,8 @@ func (s *boltSwapStore) CreateLoopIn(ctx context.Context, hash lntypes.Hash, } // Store the current protocol version. - err = swapBucket.Put(protocolVersionKey, + err = swapBucket.Put( + protocolVersionKey, MarshalProtocolVersion(swap.ProtocolVersion), ) if err != nil { @@ -631,6 +635,7 @@ func (s *boltSwapStore) CreateLoopIn(ctx context.Context, hash lntypes.Hash, // Finally, we'll create an empty updates bucket for this swap // to track any future updates to the swap itself. _, err = swapBucket.CreateBucket(updatesBucketKey) + return err }) } @@ -698,8 +703,8 @@ func (s *boltSwapStore) updateLoop(bucketKey []byte, hash lntypes.Hash, // a particular swap as it goes through the various stages in its lifetime. // // NOTE: Part of the loopdb.SwapStore interface. -func (s *boltSwapStore) UpdateLoopOut(ctx context.Context, - hash lntypes.Hash, time time.Time, state SwapStateData) error { +func (s *boltSwapStore) UpdateLoopOut(ctx context.Context, hash lntypes.Hash, + time time.Time, state SwapStateData) error { return s.updateLoop(loopOutBucketKey, hash, time, state) } @@ -735,6 +740,7 @@ func (s *boltSwapStore) PutLiquidityParams(ctx context.Context, if rootBucket == nil { return errors.New("liquidity bucket does not exist") } + return rootBucket.Put(liquidtyParamsKey, params) }) } @@ -757,6 +763,7 @@ func (s *boltSwapStore) FetchLiquidityParams(ctx context.Context) ([]byte, } params = rootBucket.Get(liquidtyParamsKey) + return nil }) @@ -803,6 +810,7 @@ func fetchUpdates(swapBucket *bbolt.Bucket) ([]*LoopEvent, error) { } updates = append(updates, event) + return nil }) if err != nil { @@ -821,8 +829,7 @@ func (s *boltSwapStore) fetchLoopOutSwap(rootBucket *bbolt.Bucket, // bucket for this swap from its swaphash. swapBucket := rootBucket.Bucket(swapHash) if swapBucket == nil { - return nil, fmt.Errorf("swap bucket %x not found", - swapHash) + return nil, fmt.Errorf("swap bucket %x not found", swapHash) } hash, err := lntypes.MakeHash(swapHash) @@ -859,13 +866,13 @@ func (s *boltSwapStore) fetchLoopOutSwap(rootBucket *bbolt.Bucket, switch { case err == io.EOF: break readLoop + case err != nil: return nil, err } contract.OutgoingChanSet = append( - contract.OutgoingChanSet, - chanID, + contract.OutgoingChanSet, chanID, ) } } @@ -936,8 +943,7 @@ func (s *boltSwapStore) fetchLoopInSwap(rootBucket *bbolt.Bucket, // bucket for this swap from its swaphash. swapBucket := rootBucket.Bucket(swapHash) if swapBucket == nil { - return nil, fmt.Errorf("swap bucket %x not found", - swapHash) + return nil, fmt.Errorf("swap bucket %x not found", swapHash) } hash, err := lntypes.MakeHash(swapHash) diff --git a/loopdb/store_mock.go b/loopdb/store_mock.go index 93dab2687..d55ee80bc 100644 --- a/loopdb/store_mock.go +++ b/loopdb/store_mock.go @@ -83,8 +83,8 @@ func (s *StoreMock) FetchLoopOutSwaps(ctx context.Context) ([]*LoopOut, error) { // FetchLoopOutSwap returns a swap currently in the store. // // NOTE: Part of the SwapStore interface. -func (s *StoreMock) FetchLoopOutSwap(ctx context.Context, - hash lntypes.Hash) (*LoopOut, error) { +func (s *StoreMock) FetchLoopOutSwap(ctx context.Context, hash lntypes.Hash) ( + *LoopOut, error) { s.RLock() defer s.RUnlock() @@ -135,9 +135,7 @@ func (s *StoreMock) CreateLoopOut(ctx context.Context, hash lntypes.Hash, } // FetchLoopInSwaps returns all in swaps currently in the store. -func (s *StoreMock) FetchLoopInSwaps(ctx context.Context) ([]*LoopIn, - error) { - +func (s *StoreMock) FetchLoopInSwaps(ctx context.Context) ([]*LoopIn, error) { s.RLock() defer s.RUnlock() @@ -266,14 +264,17 @@ func (s *StoreMock) IsDone() error { select { case <-s.loopOutStoreChan: return errors.New("storeChan not empty") + default: } select { case <-s.loopOutUpdateChan: return errors.New("updateChan not empty") + default: } + return nil } @@ -296,6 +297,7 @@ func (s *StoreMock) AssertLoopOutState(expectedState SwapState) { select { case state := <-s.loopOutUpdateChan: require.Equal(s.t, expectedState, state.State) + case <-time.After(test.Timeout): s.t.Fatalf("expected swap state to be stored") } @@ -314,9 +316,7 @@ func (s *StoreMock) AssertLoopInStored() { // AssertLoopInState asserts that a specified state transition is persisted to // disk. -func (s *StoreMock) AssertLoopInState( - expectedState SwapState) SwapStateData { - +func (s *StoreMock) AssertLoopInState(expectedState SwapState) SwapStateData { s.t.Helper() state := <-s.loopInUpdateChan @@ -397,8 +397,8 @@ func (s *StoreMock) BatchUpdateLoopOutSwapCosts(ctx context.Context, } // HasMigration returns true if the migration with the given ID has been done. -func (s *StoreMock) HasMigration(ctx context.Context, migrationID string) ( - bool, error) { +func (s *StoreMock) HasMigration(ctx context.Context, migrationID string) (bool, + error) { s.RLock() defer s.RUnlock() diff --git a/loopdb/store_test.go b/loopdb/store_test.go index 4a61062b2..5e649518c 100644 --- a/loopdb/store_test.go +++ b/loopdb/store_test.go @@ -425,6 +425,7 @@ func createVersionZeroDb(t *testing.T, dbPath string) { err = bdb.Update(func(tx *bbolt.Tx) error { _, err := tx.CreateBucket(metaBucketKey) + return err }) if err != nil { @@ -448,11 +449,41 @@ func TestLegacyOutgoingChannel(t *testing.T) { "dbp": legacyDbVersion, }, "uncharge-swaps": map[string]any{ - Hex("2a595d79a55168970532805ae20c9b5fac98f04db79ba4c6ae9b9ac0f206359e"): map[string]any{ - "contract": Hex("1562d6fbec140000010101010202020203030303040404040101010102020202030303030404040400000000000000640d707265706179696e766f69636501010101010101010101010101010101010101010101010101010101010101010201010101010101010101010101010101010101010101010101010101010101010300000090000000000000000a0000000000000014000000000000002800000063223347454e556d6e4552745766516374344e65676f6d557171745a757a5947507742530b73776170696e766f69636500000002000000000000001e") + legacyOutgoingChannel + Hex("1562d6fbec140000"), + Hex( + "2a595d79a55168970532805ae20c9b5fac98f04db79" + + "ba4c6ae9b9ac0f206359e", + ): map[string]any{ + "contract": Hex( + "1562d6fbec1400000101010102020202030"+ + "303030404040401010101020202"+ + "020303030304040404000000000"+ + "00000640d707265706179696e76"+ + "6f6963650101010101010101010"+ + "101010101010101010101010101"+ + "010101010101010101020101010"+ + "101010101010101010101010101"+ + "010101010101010101010101010"+ + "101030000009000000000000000"+ + "0a0000000000000014000000000"+ + "000002800000063223347454e55"+ + "6d6e4552745766516374344e656"+ + "76f6d557171745a757a59475077"+ + "42530b73776170696e766f69636"+ + "500000002000000000000001e", + ) + legacyOutgoingChannel + Hex( + "1562d6fbec140000", + ), "updates": map[string]any{ - Hex("0000000000000001"): Hex("1508290a92d4c00001000000000000000000000000000000000000000000000000"), - Hex("0000000000000002"): Hex("1508290a92d4c00006000000000000000000000000000000000000000000000000"), + Hex("0000000000000001"): Hex( + "1508290a92d4c00001000000000" + + "0000000000000000000" + + "00000000000000000000", + ), + Hex("0000000000000002"): Hex( + "1508290a92d4c00006000000000" + + "0000000000000000000" + + "00000000000000000000", + ), }, }, }, diff --git a/loopdb/typed_store.go b/loopdb/typed_store.go index eb7bfc43b..a56c2798c 100644 --- a/loopdb/typed_store.go +++ b/loopdb/typed_store.go @@ -38,8 +38,8 @@ func NewTypedStore[Q any](db BatchedQuerier) *TypedStore[Q] { // ExecTx will execute the passed txBody, operating upon generic parameter Q // (usually a storage interface) in a single transaction. The set of TxOptions // are passed in to allow the caller to specify if a transaction is read-only. -func (s *TypedStore[Q]) ExecTx(ctx context.Context, - txOptions TxOptions, txBody func(Q) error) error { +func (s *TypedStore[Q]) ExecTx(ctx context.Context, txOptions TxOptions, + txBody func(Q) error) error { return s.BatchedQuerier.ExecTx(ctx, txOptions, func(q *sqlc.Queries) error { diff --git a/loopin.go b/loopin.go index ae6802c80..a7639db5b 100644 --- a/loopin.go +++ b/loopin.go @@ -91,8 +91,8 @@ type loopInInitResult struct { // newLoopInSwap initiates a new loop in swap. func newLoopInSwap(globalCtx context.Context, cfg *swapConfig, - currentHeight int32, request *LoopInRequest) (*loopInInitResult, - error) { + currentHeight int32, + request *LoopInRequest) (*loopInInitResult, error) { var err error @@ -142,8 +142,8 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig, swapFee := quote.SwapFee if swapFee > request.MaxSwapFee { - log.Warnf("Swap fee %v exceeding maximum of %v", - swapFee, request.MaxSwapFee) + log.Warnf("Swap fee %v exceeding maximum of %v", swapFee, + request.MaxSwapFee) return nil, ErrSwapFeeTooHigh } @@ -230,6 +230,7 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig, probeResult, err := awaitProbe(probeWaitCtx, *cfg.lnd, probeHash) if err != nil { probeWaitCancel() + return nil, fmt.Errorf("probe failed: %v", err) } @@ -237,9 +238,10 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig, // the server success key and the expiry height of the on-chain swap // htlc. log.Infof("Initiating swap request at height %v", currentHeight) - swapResp, err := cfg.server.NewLoopInSwap(globalCtx, swapHash, - request.Amount, senderKey, senderInternalPubKey, swapInvoice, - probeInvoice, request.LastHop, request.Initiator, + swapResp, err := cfg.server.NewLoopInSwap( + globalCtx, swapHash, request.Amount, senderKey, + senderInternalPubKey, swapInvoice, probeInvoice, + request.LastHop, request.Initiator, ) probeWaitCancel() @@ -297,8 +299,7 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig, } swapKit := newSwapKit( - swapHash, swap.TypeIn, - cfg, &contract.SwapContract, + swapHash, swap.TypeIn, cfg, &contract.SwapContract, ) swapKit.lastUpdateTime = initiationTime @@ -365,15 +366,17 @@ func awaitProbe(ctx context.Context, lnd lndclient.LndServices, probeHash, ) if err != nil { - log.Errorf("Cancel probe "+ - "invoice: %v", err) + log.Errorf( + "Cancel probe "+ + "invoice: %v", + err) } return case invpkg.ContractCanceled: - probeResult <- errors.New( - "probe invoice expired") + probeResult <- errors.New("probe " + + "invoice expired") return @@ -387,10 +390,12 @@ func awaitProbe(ctx context.Context, lnd lndclient.LndServices, case err := <-errChan: probeResult <- err + return case <-ctx.Done(): probeResult <- ctx.Err() + return } } @@ -409,8 +414,7 @@ func resumeLoopInSwap(_ context.Context, cfg *swapConfig, log.Infof("Resuming loop in swap %v", hash) swapKit := newSwapKit( - hash, swap.TypeIn, cfg, - &pend.Contract.SwapContract, + hash, swap.TypeIn, cfg, &pend.Contract.SwapContract, ) swap := &loopInSwap{ @@ -515,8 +519,8 @@ func (s *loopInSwap) sendUpdate(ctx context.Context) error { // execute starts/resumes the swap. It is a thin wrapper around executeSwap to // conveniently handle the error case. -func (s *loopInSwap) execute(mainCtx context.Context, - cfg *executeConfig, height int32) error { +func (s *loopInSwap) execute(mainCtx context.Context, cfg *executeConfig, + height int32) error { defer s.wg.Wait() @@ -577,13 +581,9 @@ func (s *loopInSwap) execute(mainCtx context.Context, return err } - s.log.Infof("Loop in swap completed: %v "+ - "(final cost: server %v, onchain %v, offchain %v)", - s.state, - s.cost.Server, - s.cost.Onchain, - s.cost.Offchain, - ) + s.log.Infof("Loop in swap completed: %v (final cost: server %v, "+ + "onchain %v, offchain %v)", s.state, s.cost.Server, + s.cost.Onchain, s.cost.Offchain) return nil } @@ -776,6 +776,7 @@ func (s *loopInSwap) publishOnChainHtlc(ctx context.Context) (bool, error) { // Verify whether it still makes sense to publish the htlc. if blocksRemaining < MinLoopInPublishDelta { s.setState(loopdb.StateFailTimeout) + return false, s.persistAndAnnounceState(ctx) } @@ -1051,6 +1052,7 @@ func (s *loopInSwap) tryPushHtlcKey(ctx context.Context) bool { err = s.server.PushKey(ctx, s.ProtocolVersion, s.hash, internalPrivKey) if err != nil { s.log.Warnf("Internal HTLC key reveal failed: %v", err) + return false } @@ -1149,7 +1151,9 @@ func (s *loopInSwap) publishTimeoutTx(ctx context.Context, err = s.lnd.WalletKit.PublishTransaction( ctx, timeoutTx, - labels.LoopInSweepTimeout(swap.ShortHash(&s.hash)), + labels.LoopInSweepTimeout( + swap.ShortHash(&s.hash), + ), ) if err != nil { s.log.Warnf("publish timeout: %v", err) @@ -1177,9 +1181,7 @@ func (s *loopInSwap) setStateAbandoned(ctx context.Context) error { // If the invoice is already settled or canceled, this is a nop. _ = s.lnd.Invoices.CancelInvoice(ctx, s.hash) - return fmt.Errorf("swap hash "+ - "abandoned by client, "+ - "swap ID: %v, %v", + return fmt.Errorf("swap hash abandoned by client, swap ID: %v, %v", s.hash, err) } diff --git a/loopin_test.go b/loopin_test.go index a1a095845..e27e03d5a 100644 --- a/loopin_test.go +++ b/loopin_test.go @@ -136,7 +136,9 @@ func TestLoopInSwapInvoiceRouteHintsMatchProbe(t *testing.T) { ctx := newLoopInTestContext(t) cfg := newSwapConfig( &ctx.lnd.LndServices, ctx.store, ctx.server, nil, - clock.NewTestClock(time.Unix(123, 0)), + clock.NewTestClock( + time.Unix(123, 0), + ), ) req := testLoopInRequest @@ -178,8 +180,7 @@ func testLoopInSuccess(t *testing.T) { req.LastHop = expectedLastHop initResult, err := newLoopInSwap( - context.Background(), cfg, - height, req, + context.Background(), cfg, height, req, ) require.NoError(t, err) @@ -358,7 +359,9 @@ func testLoopInTimeout(t *testing.T, externalValue int64) { cfg := newSwapConfig( &ctx.lnd.LndServices, ctx.store, ctx.server, nil, - clock.NewTestClock(time.Unix(123, 0)), + clock.NewTestClock( + time.Unix(123, 0), + ), ) req := testLoopInRequest @@ -367,8 +370,7 @@ func testLoopInTimeout(t *testing.T, externalValue int64) { } initResult, err := newLoopInSwap( - context.Background(), cfg, - height, &req, + context.Background(), cfg, height, &req, ) require.NoError(t, err) inSwap := initResult.swap @@ -544,9 +546,8 @@ func TestLoopInResume(t *testing.T) { for _, next := range []bool{false, true} { for _, version := range storedVersion { for _, testCase := range testCases { - name := fmt.Sprintf( - "%v %v", testCase, version.String(), - ) + name := fmt.Sprintf("%v %v", testCase, + version.String()) if next { name += " next protocol" } @@ -554,8 +555,7 @@ func TestLoopInResume(t *testing.T) { t.Run(name, func(t *testing.T) { testLoopInResume( t, testCase.state, - testCase.expired, - version, + testCase.expired, version, ) }) } @@ -572,7 +572,9 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool, ctx := newLoopInTestContext(t) cfg := newSwapConfig( &ctx.lnd.LndServices, ctx.store, ctx.server, nil, - clock.NewTestClock(time.Unix(123, 0)), + clock.NewTestClock( + time.Unix(123, 0), + ), ) // Create sender and receiver keys. @@ -658,12 +660,14 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool, select { case <-ctx.lnd.SendPaymentChannel: t.Fatal("unexpected payment sent") + default: } select { case <-ctx.lnd.SendOutputsChannel: t.Fatal("unexpected tx published") + default: } }() @@ -674,6 +678,7 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool, if expired { ctx.assertState(loopdb.StateFailTimeout) + return } @@ -874,7 +879,9 @@ func TestAbandonSettledInvoiceState(t *testing.T) { Hash: testPreimage.Hash(), }, } - resumedSwap, err := resumeLoopInSwap(context.Background(), cfg, pendSwap) + resumedSwap, err := resumeLoopInSwap( + context.Background(), cfg, pendSwap, + ) require.NoError(t, err) // Execute the abandoned swap. @@ -922,6 +929,7 @@ func advanceToPublishedHtlc(t *testing.T, ctx *loopInTestContext) SwapInfo { // Client starts listening for spend of htlc. <-ctx.lnd.RegisterSpendChannel + return swapInfo } @@ -930,14 +938,15 @@ func startNewLoopIn(t *testing.T, ctx *loopInTestContext, height int32) ( cfg := newSwapConfig( &ctx.lnd.LndServices, ctx.store, ctx.server, nil, - clock.NewTestClock(time.Unix(123, 0)), + clock.NewTestClock( + time.Unix(123, 0), + ), ) req := &testLoopInRequest initResult, err := newLoopInSwap( - context.Background(), cfg, - height, req, + context.Background(), cfg, height, req, ) require.NoError(t, err) diff --git a/loopout.go b/loopout.go index a18c83ed1..8a36ce2a9 100644 --- a/loopout.go +++ b/loopout.go @@ -151,7 +151,6 @@ func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig, if request.AssetId != nil { if request.AssetPrepayRfqId == nil || request.AssetSwapRfqId == nil { - return nil, errors.New("both rfq ids must be set for " + "asset swaps") } @@ -271,7 +270,9 @@ func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig, // Persist the data before exiting this function, so that the caller // can trust that this swap will be resumed on restart. - err = cfg.store.CreateLoopOut(globalCtx, swapHash, &swap.LoopOutContract) + err = cfg.store.CreateLoopOut( + globalCtx, swapHash, &swap.LoopOutContract, + ) if err != nil { return nil, fmt.Errorf("cannot store swap: %v", err) } @@ -288,8 +289,8 @@ func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig, // resumeLoopOutSwap returns a swap object representing a pending swap that has // been restored from the database. -func resumeLoopOutSwap(cfg *swapConfig, pend *loopdb.LoopOut, -) (*loopOutSwap, error) { +func resumeLoopOutSwap(cfg *swapConfig, + pend *loopdb.LoopOut) (*loopOutSwap, error) { hash := lntypes.Hash(sha256.Sum256(pend.Contract.Preimage[:])) @@ -352,7 +353,9 @@ func (s *loopOutSwap) sendUpdate(ctx context.Context) error { // In order to avoid potentially dangerous ownership sharing // we copy the outgoing channel set. if s.OutgoingChanSet != nil { - outgoingChanSet := make(loopdb.ChannelSet, len(s.OutgoingChanSet)) + outgoingChanSet := make( + loopdb.ChannelSet, len(s.OutgoingChanSet), + ) copy(outgoingChanSet[:], s.OutgoingChanSet[:]) info.OutgoingChanSet = outgoingChanSet @@ -373,8 +376,8 @@ func (s *loopOutSwap) sendUpdate(ctx context.Context) error { // execute starts/resumes the swap. It is a thin wrapper around // executeAndFinalize to conveniently handle the error case. -func (s *loopOutSwap) execute(mainCtx context.Context, - cfg *executeConfig, height int32) error { +func (s *loopOutSwap) execute(mainCtx context.Context, cfg *executeConfig, + height int32) error { defer s.wg.Wait() @@ -476,13 +479,9 @@ func (s *loopOutSwap) executeAndFinalize(globalCtx context.Context) error { } // Mark swap completed in store. - s.log.Infof("Swap completed: %v "+ - "(final cost: server %v, onchain %v, offchain %v)", - s.state, - s.cost.Server, - s.cost.Onchain, - s.cost.Offchain, - ) + s.log.Infof("Swap completed: %v (final cost: server %v, onchain %v, "+ + "offchain %v)", s.state, s.cost.Server, s.cost.Onchain, + s.cost.Offchain) return s.persistState(globalCtx) } @@ -589,11 +588,14 @@ func (s *loopOutSwap) executeSwap(globalCtx context.Context) error { s.log.Infof("Htlc value: %v", htlcValue) // Verify amount if preimage hasn't been revealed yet. - if s.state != loopdb.StatePreimageRevealed && htlcValue < s.AmountRequested { + if s.state != loopdb.StatePreimageRevealed && + htlcValue < s.AmountRequested { + log.Warnf("Swap amount too low, expected %v but received %v", s.AmountRequested, htlcValue) s.state = loopdb.StateFailInsufficientValue + return nil } @@ -745,6 +747,7 @@ func (s *loopOutSwap) payInvoice(ctx context.Context, invoice string, if err != nil { result.err = err sendResult(result) + return } @@ -766,11 +769,11 @@ func (s *loopOutSwap) payInvoice(ctx context.Context, invoice string, } // payInvoiceAsync is the asynchronously executed part of paying an invoice. -func (s *loopOutSwap) payInvoiceAsync(ctx context.Context, - invoice string, maxFee btcutil.Amount, - outgoingChanIds loopdb.ChannelSet, paymentTimeout time.Duration, - pluginType RoutingPluginType, reportPluginResult bool, rfqId []byte) ( - *lndclient.PaymentStatus, error) { +func (s *loopOutSwap) payInvoiceAsync(ctx context.Context, invoice string, + maxFee btcutil.Amount, outgoingChanIds loopdb.ChannelSet, + paymentTimeout time.Duration, pluginType RoutingPluginType, + reportPluginResult bool, rfqId []byte) (*lndclient.PaymentStatus, + error) { // Extract hash from payment request. Unfortunately the request // components aren't available directly. @@ -851,7 +854,10 @@ func (s *loopOutSwap) payInvoiceAsync(ctx context.Context, // Newer versions will ignore the old field and only use the new // field. htlc := rfqmsg.NewHtlc( - nil, fn.Some(rfq), fn.Some([]rfqmsg.ID{rfq}), + nil, fn.Some(rfq), + fn.Some( + []rfqmsg.ID{rfq}, + ), ) htlcMapRecords, err := tlv.RecordsToMap(htlc.Records()) if err != nil { @@ -883,10 +889,10 @@ func (s *loopOutSwap) payInvoiceAsync(ctx context.Context, } if err := s.swapKit.server.ReportRoutingResult( - ctx, s.hash, s.swapInvoicePaymentAddr, - reportType, paymentSuccess, int32(attempts), - dt.Milliseconds(), + ctx, s.hash, s.swapInvoicePaymentAddr, reportType, + paymentSuccess, int32(attempts), dt.Milliseconds(), ); err != nil { + s.log.Warnf("Failed to report routing result: %v", err) } } @@ -929,7 +935,6 @@ func (s *loopOutSwap) sendPaymentWithRetry(ctx context.Context, // Retry if the payment has timed out, or return here. if tryCount > maxRetries || paymentStatus.FailureReason != lnrpc.PaymentFailureReason_FAILURE_REASON_TIMEOUT { - return paymentStatus, tryCount, nil } @@ -993,10 +998,8 @@ func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) ( // Wait for confirmation of the on-chain htlc by watching for a tx // producing the swap script output. - s.log.Infof( - "Register %v conf ntfn for swap script on chain (hh=%v)", - s.HtlcConfirmations, s.InitiationHeight, - ) + s.log.Infof("Register %v conf ntfn for swap script on chain (hh=%v)", + s.HtlcConfirmations, s.InitiationHeight) // If we've revealed the preimage in a previous run, we expect to have // recorded the htlc tx hash. We use this to re-register for @@ -1036,9 +1039,9 @@ func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) ( return false } - s.log.Infof("Max preimage reveal height %v "+ - "exceeded (current height %v)", - maxPreimageRevealHeight, s.height) + s.log.Infof("Max preimage reveal height %v exceeded "+ + "(current height %v)", maxPreimageRevealHeight, + s.height) s.state = loopdb.StateFailTimeout @@ -1051,8 +1054,8 @@ func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) ( if checkMaxRevealHeightExceeded() { return nil, nil } - s.log.Infof("Waiting for either htlc on-chain confirmation or " + - "off-chain payment failure") + s.log.Infof("Waiting for either htlc on-chain confirmation " + + "or off-chain payment failure") loop: for { select { @@ -1074,6 +1077,7 @@ func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) ( ctx, paymentTypeInvoice, result.status, ) + return nil, nil } @@ -1132,8 +1136,10 @@ func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) ( select { case err := <-htlcErrChan: return nil, err + case htlcConfNtfn := <-htlcConfChan: txConf = htlcConfNtfn + case <-globalCtx.Done(): return nil, globalCtx.Err() } @@ -1269,14 +1275,12 @@ func (s *loopOutSwap) waitForHtlcSpendConfirmedV2(globalCtx context.Context, // either. canSweep := s.canSweep() if !canSweep { - s.log.Infof("Aborting swap, timed " + - "out on-chain") + s.log.Infof("Aborting swap, timed out on-chain") s.state = loopdb.StateFailTimeout err := s.persistState(ctx) if err != nil { - log.Warnf("unable to persist " + - "state") + log.Warnf("unable to persist state") } return nil, nil @@ -1439,9 +1443,9 @@ func validateLoopOutContract(lnd *lndclient.LndServices, request *OutRequest, } if swapInvoiceHash != swapHash { - return fmt.Errorf( - "cannot initiate swap, swap invoice hash %v not equal "+ - "generated swap hash %v", swapInvoiceHash, swapHash) + return fmt.Errorf("cannot initiate swap, swap invoice hash %v "+ + "not equal generated swap hash %v", swapInvoiceHash, + swapHash) } _, _, _, prepayInvoiceAmt, err := swap.DecodeInvoice( @@ -1453,8 +1457,8 @@ func validateLoopOutContract(lnd *lndclient.LndServices, request *OutRequest, swapFee := swapInvoiceAmt + prepayInvoiceAmt - request.Amount if swapFee > request.MaxSwapFee { - log.Warnf("Swap fee %v exceeding maximum of %v", - swapFee, request.MaxSwapFee) + log.Warnf("Swap fee %v exceeding maximum of %v", swapFee, + request.MaxSwapFee) return ErrSwapFeeTooHigh } @@ -1487,6 +1491,7 @@ func (s *loopOutSwap) canSweep() bool { s.height) s.state = loopdb.StateFailTimeout + return false } @@ -1552,8 +1557,7 @@ type resumeManager struct { // Resume starts the resume manager which listens for unfinished swaps // from the server and attempts to recover them. func Resume(ctx context.Context, ntfnManager NotificationManager, - swapStore loopdb.SwapStore, - swapClientConn *grpc.ClientConn, + swapStore loopdb.SwapStore, swapClientConn *grpc.ClientConn, lnd *lndclient.GrpcLndServices, clock clock.Clock) { resumeManager := &resumeManager{ @@ -1562,7 +1566,9 @@ func Resume(ctx context.Context, ntfnManager NotificationManager, swapClient: swapserverrpc.NewSwapServerClient(swapClientConn), lnd: lnd, clock: clock, - reqChan: make(chan *swapserverrpc.ServerUnfinishedSwapNotification, 1), + reqChan: make( + chan *swapserverrpc.ServerUnfinishedSwapNotification, 1, + ), } go resumeManager.start(ctx) } @@ -1657,6 +1663,7 @@ trackChanLoop: } if swap.LastUpdate().Cost.Server == 0 { + // If the server cost is zero resume the payment. return m.resumeLoopOutPayment(ctx, swap) } @@ -1694,8 +1701,8 @@ func (m *resumeManager) resumeLoopOutPayment(ctx context.Context, amtRequested := swap.Contract.AmountRequested if inv.Value.ToSatoshis() > swap.Contract.MaxSwapFee*2+amtRequested { - return fmt.Errorf("invoice amount %v exceeds max "+ - "allowed %v", inv.Value.ToSatoshis(), + return fmt.Errorf("invoice amount %v exceeds max allowed %v", + inv.Value.ToSatoshis(), swap.Contract.MaxSwapFee+amtRequested) } @@ -1713,11 +1720,13 @@ func (m *resumeManager) resumeLoopOutPayment(ctx context.Context, select { case payResp := <-payChan: if payResp.FailureReason.String() != "" { - return fmt.Errorf("payment error: %v", payResp.FailureReason) + return fmt.Errorf("payment error: %v", + payResp.FailureReason) } if payResp.State == lnrpc.Payment_SUCCEEDED { cost := swap.LastUpdate().Cost - cost.Server = payResp.Value.ToSatoshis() - amtRequested + cost.Server = payResp.Value.ToSatoshis() - + amtRequested cost.Offchain = payResp.Fee.ToSatoshis() // Payment succeeded. updateTime := m.clock.Now() diff --git a/loopout_feerate.go b/loopout_feerate.go index ec1b5d453..81d036bde 100644 --- a/loopout_feerate.go +++ b/loopout_feerate.go @@ -72,8 +72,8 @@ func newLoopOutSweepFeerateProvider(sweeper sweeper, // GetMinFeeRate returns minimum required feerate for a sweep by swap hash. func (p *loopOutSweepFeerateProvider) GetMinFeeRate(ctx context.Context, - swapHash lntypes.Hash, - _ wire.OutPoint) (chainfee.SatPerKWeight, error) { + swapHash lntypes.Hash, _ wire.OutPoint) (chainfee.SatPerKWeight, + error) { _, feeRate, err := p.GetConfTargetAndFeeRate(ctx, swapHash) @@ -158,9 +158,10 @@ func (p *loopOutSweepFeerateProvider) GetConfTargetAndFeeRate( newConfTarget := min(confTarget, int32(urgentSweepConfTarget)) log.Infof("Swap %x is about to expire (blocksUntilExpiry=%d), "+ - "reducing its confTarget from %d to %d and multiplying"+ - " feerate by %v.", swapHash[:6], blocksUntilExpiry, - confTarget, newConfTarget, urgentSweepConfTargetFactor) + "reducing its confTarget from %d to %d and "+ + "multiplying feerate by %v.", swapHash[:6], + blocksUntilExpiry, confTarget, newConfTarget, + urgentSweepConfTargetFactor) confTarget = newConfTarget feeFactor = urgentSweepConfTargetFactor diff --git a/loopout_feerate_test.go b/loopout_feerate_test.go index 33efae2ac..e1cdf1ed6 100644 --- a/loopout_feerate_test.go +++ b/loopout_feerate_test.go @@ -25,9 +25,8 @@ type testSweeper struct { // weight estimator. It returns also the fee rate and transaction weight. func (s testSweeper) GetSweepFeeDetails(ctx context.Context, addInputEstimate func(*input.TxWeightEstimator) error, - destAddr btcutil.Address, sweepConfTarget int32, - label string) (btcutil.Amount, chainfee.SatPerKWeight, - lntypes.WeightUnit, error) { + destAddr btcutil.Address, sweepConfTarget int32, label string) ( + btcutil.Amount, chainfee.SatPerKWeight, lntypes.WeightUnit, error) { var feeRate chainfee.SatPerKWeight switch { diff --git a/loopout_test.go b/loopout_test.go index 026d571cb..ba79b3a78 100644 --- a/loopout_test.go +++ b/loopout_test.go @@ -199,7 +199,9 @@ func testLateHtlcPublish(t *testing.T) { cfg := newSwapConfig( &lnd.LndServices, store, server, nil, - clock.NewTestClock(time.Unix(123, 0)), + clock.NewTestClock( + time.Unix(123, 0), + ), ) testRequest.Expiry = height + testLoopOutMinOnChainCltvDelta @@ -303,7 +305,9 @@ func testCustomSweepConfTarget(t *testing.T) { cfg := newSwapConfig( &lnd.LndServices, loopdb.NewStoreMock(t), server, nil, - clock.NewTestClock(time.Unix(123, 0)), + clock.NewTestClock( + time.Unix(123, 0), + ), ) initResult, err := newLoopOutSwap( @@ -451,7 +455,9 @@ func testCustomSweepConfTarget(t *testing.T) { htlcTx.TxOut[0].Value - sweepTx.TxOut[0].Value, ) - weight := blockchain.GetTransactionWeight(btcutil.NewTx(sweepTx)) + weight := blockchain.GetTransactionWeight( + btcutil.NewTx(sweepTx), + ) feeRate, err := ctx.Lnd.WalletKit.EstimateFeeRate( context.Background(), expConfTarget, ) @@ -547,7 +553,9 @@ func testPreimagePush(t *testing.T) { cfg := newSwapConfig( &lnd.LndServices, loopdb.NewStoreMock(t), server, nil, - clock.NewTestClock(time.Unix(123, 0)), + clock.NewTestClock( + time.Unix(123, 0), + ), ) initResult, err := newLoopOutSwap( @@ -677,9 +685,11 @@ func testPreimagePush(t *testing.T) { // Now we decrease our fees for the swap's confirmation target to less // than the maximum miner fee. - ctx.Lnd.SetFeeEstimate(testReq.SweepConfTarget, chainfee.SatPerKWeight( - testReq.MaxMinerFee/2, - )) + ctx.Lnd.SetFeeEstimate( + testReq.SweepConfTarget, chainfee.SatPerKWeight( + testReq.MaxMinerFee/2, + ), + ) // Now when we report a new block and tick our expiry fee timer, and // fees are acceptably low so we expect our sweep to be published. @@ -806,7 +816,9 @@ func testFailedOffChainCancelation(t *testing.T) { cfg := newSwapConfig( &lnd.LndServices, loopdb.NewStoreMock(t), server, nil, - clock.NewTestClock(time.Unix(123, 0)), + clock.NewTestClock( + time.Unix(123, 0), + ), ) initResult, err := newLoopOutSwap( @@ -961,7 +973,9 @@ func TestLoopOutMuSig2Sweep(t *testing.T) { cfg := newSwapConfig( &lnd.LndServices, loopdb.NewStoreMock(t), server, nil, - clock.NewTestClock(time.Unix(123, 0)), + clock.NewTestClock( + time.Unix(123, 0), + ), ) initResult, err := newLoopOutSwap( @@ -1095,9 +1109,11 @@ func TestLoopOutMuSig2Sweep(t *testing.T) { // Now we decrease our fees for the swap's confirmation target to less // than the maximum miner fee. - ctx.Lnd.SetFeeEstimate(testReq.SweepConfTarget, chainfee.SatPerKWeight( - testReq.MaxMinerFee/2, - )) + ctx.Lnd.SetFeeEstimate( + testReq.SweepConfTarget, chainfee.SatPerKWeight( + testReq.MaxMinerFee/2, + ), + ) // Now when we report a new block and tick our expiry fee timer, and // fees are acceptably low so we expect our sweep to be published. diff --git a/notifications/manager.go b/notifications/manager.go index bd1ac4170..b90811d74 100644 --- a/notifications/manager.go +++ b/notifications/manager.go @@ -26,8 +26,8 @@ const ( // static loop in sweep requests. NotificationTypeStaticLoopInSweepRequest - // NotificationTypeUnfinishedSwap is the notification type for unfinished - // swap notifications. + // NotificationTypeUnfinishedSwap is the notification type for + // unfinished swap notifications. NotificationTypeUnfinishedSwap ) @@ -44,7 +44,8 @@ const ( // Client is the interface that the notification manager needs to implement in // order to be able to subscribe to notifications. type Client interface { - // SubscribeNotifications subscribes to the notifications from the server. + // SubscribeNotifications subscribes to the notifications from the + // server. SubscribeNotifications(ctx context.Context, in *swapserverrpc.SubscribeNotificationsRequest, opts ...grpc.CallOption) ( @@ -134,8 +135,7 @@ func (m *Manager) SubscribeStaticLoopInSweepRequests(ctx context.Context, context.AfterFunc(ctx, func() { m.removeSubscriber( - NotificationTypeStaticLoopInSweepRequest, - sub, + NotificationTypeStaticLoopInSweepRequest, sub, ) close(notifChan) }) @@ -289,12 +289,13 @@ func (m *Manager) subscribeNotifications(ctx context.Context) error { // handleNotification handles an incoming notification from the server, // forwarding it to the appropriate subscribers. -func (m *Manager) handleNotification(ntfn *swapserverrpc. - SubscribeNotificationsResponse) { +func (m *Manager) handleNotification( + ntfn *swapserverrpc.SubscribeNotificationsResponse) { switch ntfn.Notification.(type) { case *swapserverrpc.SubscribeNotificationsResponse_ReservationNotification: // nolint: lll - // We'll forward the reservation notification to all subscribers. + // We'll forward the reservation notification to all + // subscribers. reservationNtfn := ntfn.GetReservationNotification() m.Lock() defer m.Unlock() @@ -305,6 +306,7 @@ func (m *Manager) handleNotification(ntfn *swapserverrpc. recvChan <- reservationNtfn } + case *swapserverrpc.SubscribeNotificationsResponse_StaticLoopInSweep: // nolint: lll // We'll forward the static loop in sweep request to all // subscribers. @@ -334,8 +336,7 @@ func (m *Manager) handleNotification(ntfn *swapserverrpc. } default: - log.Warnf("Received unknown notification type: %v", - ntfn) + log.Warnf("Received unknown notification type: %v", ntfn) } } diff --git a/notifications/manager_test.go b/notifications/manager_test.go index 9a06503e8..d630f0e75 100644 --- a/notifications/manager_test.go +++ b/notifications/manager_test.go @@ -21,7 +21,8 @@ var ( testReservationId2 = []byte{0x01, 0x02} ) -// mockNotificationsClient implements the NotificationsClient interface for testing. +// mockNotificationsClient implements the NotificationsClient interface for +// testing. type mockNotificationsClient struct { sync.Mutex @@ -44,6 +45,7 @@ func (m *mockNotificationsClient) SubscribeNotifications(ctx context.Context, if m.subscribeErr != nil { return nil, m.subscribeErr } + return m.mockStream, nil } @@ -61,10 +63,12 @@ func (m *mockSubscribeNotificationsClient) Recv() ( select { case err := <-m.recvErrChan: return nil, err + case notif, ok := <-m.recvChan: if !ok { return nil, io.EOF } + return notif, nil } } @@ -113,9 +117,14 @@ func TestManager_ReservationNotification(t *testing.T) { mgr := NewManager(&Config{ Client: mockClient, CurrentToken: func() (*l402.Token, error) { + // Simulate successful fetching of L402 return &l402.Token{ - Preimage: lntypes.Preimage{1, 2, 3}, + Preimage: lntypes.Preimage{ + 1, + 2, + 3, + }, }, nil }, }) @@ -144,6 +153,7 @@ func TestManager_ReservationNotification(t *testing.T) { require.Eventually(t, func() bool { mockClient.Lock() defer mockClient.Unlock() + return mockClient.timesCalled == 1 }, time.Second*5, 10*time.Millisecond) @@ -156,7 +166,8 @@ func TestManager_ReservationNotification(t *testing.T) { // Collect the notification in the callback receivedNotification := <-subChan - // Now, check that the notification received in the callback matches the one sent + // Now, check that the notification received in the callback matches the + // one sent require.NotNil(t, receivedNotification) require.Equal(t, testReservationId, receivedNotification.ReservationId) @@ -172,13 +183,16 @@ func TestManager_ReservationNotification(t *testing.T) { select { case _, ok := <-subChan: return !ok + default: return false } }, time.Second*5, 10*time.Millisecond) } -func getTestNotification(resId []byte) *swapserverrpc.SubscribeNotificationsResponse { +func getTestNotification( + resId []byte) *swapserverrpc.SubscribeNotificationsResponse { + return &swapserverrpc.SubscribeNotificationsResponse{ Notification: &swapserverrpc.SubscribeNotificationsResponse_ReservationNotification{ ReservationNotification: &swapserverrpc.ServerReservationNotification{ @@ -216,9 +230,14 @@ func TestManager_Backoff(t *testing.T) { mgr := NewManager(&Config{ Client: mockClient, CurrentToken: func() (*l402.Token, error) { + // Simulate successful fetching of L402 return &l402.Token{ - Preimage: lntypes.Preimage{1, 2, 3}, + Preimage: lntypes.Preimage{ + 1, + 2, + 3, + }, }, nil }, }) @@ -247,7 +266,8 @@ func TestManager_Backoff(t *testing.T) { wg.Wait() // Check how many attempts we made. - require.GreaterOrEqual(t, len(mockClient.attemptTimes), 3, + require.GreaterOrEqual( + t, len(mockClient.attemptTimes), 3, "expected at least 3 attempts within 5 seconds", ) @@ -306,9 +326,14 @@ func TestManager_MinAliveConnTime(t *testing.T) { Client: mockClient, MinAliveConnTime: minAlive, CurrentToken: func() (*l402.Token, error) { + // Simulate successful fetching of L402 return &l402.Token{ - Preimage: lntypes.Preimage{1, 2, 3}, + Preimage: lntypes.Preimage{ + 1, + 2, + 3, + }, }, nil }, }) @@ -386,13 +411,18 @@ func TestManager_Backoff_Pending_Token(t *testing.T) { CurrentToken: func() (*l402.Token, error) { tokenCalls = append(tokenCalls, time.Now()) if len(tokenCalls) < 3 { + // Simulate a pending token. return &l402.Token{}, nil } // Simulate successful fetching of L402 return &l402.Token{ - Preimage: lntypes.Preimage{1, 2, 3}, + Preimage: lntypes.Preimage{ + 1, + 2, + 3, + }, }, nil }, }) diff --git a/routing_plugin.go b/routing_plugin.go index 9e1553052..d66b282c2 100644 --- a/routing_plugin.go +++ b/routing_plugin.go @@ -54,8 +54,8 @@ type RoutingPlugin interface { } // makeRoutingPlugin is a helper to instantiate routing plugins. -func makeRoutingPlugin(pluginType RoutingPluginType, - lnd lndclient.LndServices, clock clock.Clock) RoutingPlugin { +func makeRoutingPlugin(pluginType RoutingPluginType, lnd lndclient.LndServices, + clock clock.Clock) RoutingPlugin { if pluginType == RoutingPluginLowHigh { return &lowToHighRoutingPlugin{ @@ -73,8 +73,8 @@ func makeRoutingPlugin(pluginType RoutingPluginType, // instance a nil is returned. func AcquireRoutingPlugin(ctx context.Context, pluginType RoutingPluginType, lnd lndclient.LndServices, target route.Vertex, - routeHints [][]zpay32.HopHint, amt btcutil.Amount) ( - RoutingPlugin, error) { + routeHints [][]zpay32.HopHint, + amt btcutil.Amount) (RoutingPlugin, error) { routingPluginMx.Lock() defer routingPluginMx.Unlock() @@ -109,6 +109,7 @@ func AcquireRoutingPlugin(ctx context.Context, pluginType RoutingPluginType, } routingPluginInstance = nil + return nil, err } @@ -308,6 +309,7 @@ func (r *lowToHighRoutingPlugin) saveMissionControlState(ctx context.Context, } log.Debugf("Saved MC state: %v", spew.Sdump(r.mcState)) + return nil } @@ -338,8 +340,8 @@ func nodesByMaxFee(amt btcutil.Amount, target route.Vertex, totalCapacity += ch.Capacity - log.Debugf("'%v', policy=%v", - node.Alias, spew.Sdump(policy)) + log.Debugf("'%v', policy=%v", node.Alias, + spew.Sdump(policy)) fee := policy.FeeBaseMsat + policy.FeeRateMilliMsat*amtMsat @@ -609,6 +611,7 @@ func (r *lowToHighRoutingPlugin) Done(ctx context.Context) error { // If none of the selected pairs were manipulated we can skip ahead. if !r.mcChanged { log.Debugf("MC state not changed, skipping restore") + return nil } @@ -658,8 +661,7 @@ func (r *lowToHighRoutingPlugin) Done(ctx context.Context) error { return err } - log.Debugf("Restored partial MC state: %v", - spew.Sdump(entries)) + log.Debugf("Restored partial MC state: %v", spew.Sdump(entries)) return nil } diff --git a/routing_plugin_test.go b/routing_plugin_test.go index dd6088f8a..f2c9bbb1f 100644 --- a/routing_plugin_test.go +++ b/routing_plugin_test.go @@ -96,7 +96,16 @@ func TestLowHighRoutingPlugin(t *testing.T) { // Alice --- Loop // channels: []testChan{ - {alice, loopNode, 1, 1000, 1000, 1, 1000, 1}, + { + alice, + loopNode, + 1, + 1000, + 1000, + 1, + 1000, + 1, + }, }, initError: ErrRoutingPluginNotApplicable, missionControlState: [][]lndclient.MissionControlEntry{ @@ -118,9 +127,27 @@ func TestLowHighRoutingPlugin(t *testing.T) { // channels: []testChan{ // Alice - Bob - {alice, bob, 1, 1000, 1000, 1, 1000, 1}, + { + alice, + bob, + 1, + 1000, + 1000, + 1, + 1000, + 1, + }, // Bob - Loop - {bob, loopNode, 2, 1000, 1000, 1, 1000, 1}, + { + bob, + loopNode, + 2, + 1000, + 1000, + 1, + 1000, + 1, + }, }, initError: ErrRoutingPluginNotApplicable, missionControlState: [][]lndclient.MissionControlEntry{ @@ -140,11 +167,47 @@ func TestLowHighRoutingPlugin(t *testing.T) { // Charlie // channels: []testChan{ - {alice, bob, 1, 1000, 1000, 1, 1000, 1}, - {alice, charlie, 2, 1000, 1000, 1, 1000, 1}, + { + alice, + bob, + 1, + 1000, + 1000, + 1, + 1000, + 1, + }, + { + alice, + charlie, + 2, + 1000, + 1000, + 1, + 1000, + 1, + }, // Bob - Dave (cheap) - {bob, dave, 3, 1000, 1000, 1, 1000, 1}, - {dave, loopNode, 5, 1000, 1000, 1, 1000, 1}, + { + bob, + dave, + 3, + 1000, + 1000, + 1, + 1000, + 1, + }, + { + dave, + loopNode, + 5, + 1000, + 1000, + 1, + 1000, + 1, + }, }, initError: ErrRoutingPluginNotApplicable, missionControlState: [][]lndclient.MissionControlEntry{ @@ -181,13 +244,58 @@ func TestLowHighRoutingPlugin(t *testing.T) { // Charlie // channels: []testChan{ - {alice, bob, 1, 1000, 1000, 1, 1000, 1}, - {alice, charlie, 2, 1000, 1000, 1, 1000, 1}, + { + alice, + bob, + 1, + 1000, + 1000, + 1, + 1000, + 1, + }, + { + alice, + charlie, + 2, + 1000, + 1000, + 1, + 1000, + 1, + }, // Bob - Dave (cheap) - {bob, dave, 3, 1000, 1000, 1, 1000, 1}, + { + bob, + dave, + 3, + 1000, + 1000, + 1, + 1000, + 1, + }, // Charlie - Dave (expensive) - {charlie, dave, 4, 1000, 1000, 100, 1000, 1}, - {dave, loopNode, 5, 1000, 1000, 1, 1000, 1}, + { + charlie, + dave, + 4, + 1000, + 1000, + 100, + 1000, + 1, + }, + { + dave, + loopNode, + 5, + 1000, + 1000, + 1, + 1000, + 1, + }, }, initError: nil, missionControlState: [][]lndclient.MissionControlEntry{ @@ -249,13 +357,58 @@ func TestLowHighRoutingPlugin(t *testing.T) { // Charlie // channels: []testChan{ - {alice, bob, 1, 999, 1000, 1, 1000, 1}, - {alice, charlie, 2, 9999, 1000, 1, 1000, 1}, + { + alice, + bob, + 1, + 999, + 1000, + 1, + 1000, + 1, + }, + { + alice, + charlie, + 2, + 9999, + 1000, + 1, + 1000, + 1, + }, // Bob - Dave (expensive) - {bob, dave, 3, 999, 1000, 100, 1000, 1}, + { + bob, + dave, + 3, + 999, + 1000, + 100, + 1000, + 1, + }, // Charlie - Dave (expensive) - {charlie, dave, 4, 999, 1000, 100, 1000, 1}, - {dave, loopNode, 5, 999, 1000, 1, 1000, 1}, + { + charlie, + dave, + 4, + 999, + 1000, + 100, + 1000, + 1, + }, + { + dave, + loopNode, + 5, + 999, + 1000, + 1, + 1000, + 1, + }, }, initError: nil, missionControlState: [][]lndclient.MissionControlEntry{ @@ -330,16 +483,79 @@ func TestLowHighRoutingPlugin(t *testing.T) { // Dave // channels: []testChan{ - {alice, bob, 1, 1000, 1000, 1, 1000, 1}, - {alice, charlie, 2, 1000, 1000, 1, 1000, 1}, - {alice, dave, 3, 1000, 1000, 1, 1000, 1}, + { + alice, + bob, + 1, + 1000, + 1000, + 1, + 1000, + 1, + }, + { + alice, + charlie, + 2, + 1000, + 1000, + 1, + 1000, + 1, + }, + { + alice, + dave, + 3, + 1000, + 1000, + 1, + 1000, + 1, + }, // Bob - Eugene (cheap) - {bob, eugene, 4, 1000, 1000, 1, 1000, 1}, + { + bob, + eugene, + 4, + 1000, + 1000, + 1, + 1000, + 1, + }, // Charlie - Eugene (more expensive) - {charlie, eugene, 5, 1000, 1000, 2, 1000, 1}, + { + charlie, + eugene, + 5, + 1000, + 1000, + 2, + 1000, + 1, + }, // Dave - Eugene (most expensive) - {dave, eugene, 6, 1000, 1001, 2, 1000, 1}, - {eugene, frank, 7, 1000, 1000, 1, 1000, 1}, + { + dave, + eugene, + 6, + 1000, + 1001, + 2, + 1000, + 1, + }, + { + eugene, + frank, + 7, + 1000, + 1000, + 1, + 1000, + 1, + }, }, // Private channels: Frank - George - Loop routeHints: [][]zpay32.HopHint{{ @@ -457,23 +673,104 @@ func TestLowHighRoutingPlugin(t *testing.T) { // channels: []testChan{ // Alice - Bob - {alice, bob, 1, 1000, 1000, 1, 1000, 1}, + { + alice, + bob, + 1, + 1000, + 1000, + 1, + 1000, + 1, + }, // Alice - Charlie - {alice, charlie, 2, 1000, 1000, 1, 1000, 1}, + { + alice, + charlie, + 2, + 1000, + 1000, + 1, + 1000, + 1, + }, // Alice - Dave - {alice, dave, 3, 1000, 1000, 1, 1000, 1}, + { + alice, + dave, + 3, + 1000, + 1000, + 1, + 1000, + 1, + }, // Bob - Eugene - {bob, eugene, 4, 1000, 1000, 1, 1000, 1}, + { + bob, + eugene, + 4, + 1000, + 1000, + 1, + 1000, + 1, + }, // Charlie - Eugene - {charlie, eugene, 5, 1000, 1000, 2, 1000, 1}, + { + charlie, + eugene, + 5, + 1000, + 1000, + 2, + 1000, + 1, + }, // Dave - George (expensive) - {dave, george, 6, 1000, 1001, 2, 1000, 1}, + { + dave, + george, + 6, + 1000, + 1001, + 2, + 1000, + 1, + }, // Eugene - Frank - {eugene, frank, 7, 1000, 1000, 1, 1000, 1}, + { + eugene, + frank, + 7, + 1000, + 1000, + 1, + 1000, + 1, + }, // Frank - George (cheap) - {frank, george, 8, 1000, 1000, 1, 1000, 1}, + { + frank, + george, + 8, + 1000, + 1000, + 1, + 1000, + 1, + }, // George - Loop - {george, loopNode, 9, 1000, 1000, 1, 1000, 1}, + { + george, + loopNode, + 9, + 1000, + 1000, + 1, + 1000, + 1, + }, }, initError: nil, missionControlState: [][]lndclient.MissionControlEntry{ @@ -582,9 +879,10 @@ func TestLowHighRoutingPlugin(t *testing.T) { // Check that after each step, MC state is what // we expect it to be. require.NoError( - t, plugin.BeforePayment( - context.TODO(), - i+1, maxAttempts, + t, + plugin.BeforePayment( + context.TODO(), i+1, + maxAttempts, ), ) @@ -598,7 +896,8 @@ func TestLowHighRoutingPlugin(t *testing.T) { require.Error( t, ErrRoutingPluginNoMoreRetries, plugin.BeforePayment( - context.TODO(), maxAttempts, maxAttempts, + context.TODO(), maxAttempts, + maxAttempts, ), ) @@ -625,11 +924,56 @@ func TestRoutingPluginAcquireRelease(t *testing.T) { // Charlie // channels := []testChan{ - {alice, bob, 1, 1000, 1000, 1, 1000, 1}, - {alice, charlie, 2, 1000, 1000, 1, 1000, 1}, - {bob, dave, 3, 1000, 1000, 1, 1000, 1}, - {charlie, dave, 4, 1000, 1000, 100, 1000, 1}, - {dave, loopNode, 5, 1000, 1000, 1, 1000, 1}, + { + alice, + bob, + 1, + 1000, + 1000, + 1, + 1000, + 1, + }, + { + alice, + charlie, + 2, + 1000, + 1000, + 1, + 1000, + 1, + }, + { + bob, + dave, + 3, + 1000, + 1000, + 1, + 1000, + 1, + }, + { + charlie, + dave, + 4, + 1000, + 1000, + 100, + 1000, + 1, + }, + { + dave, + loopNode, + 5, + 1000, + 1000, + 1, + 1000, + 1, + }, } mockLnd.Channels, mockLnd.ChannelEdges = makeTestNetwork(channels) @@ -710,6 +1054,7 @@ func (m *mockRoutingPlugin) Init(_ context.Context, _ route.Vertex, // context. func (m *mockRoutingPlugin) Done(ctx context.Context) error { m.doneCtxErr = ctx.Err() + return nil } diff --git a/scripts/llformat-files.sh b/scripts/llformat-files.sh new file mode 100755 index 000000000..8cfcd239f --- /dev/null +++ b/scripts/llformat-files.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash + +set -euo pipefail + +usage() { + echo "usage: $0 all|changed [base]" >&2 +} + +is_format_file() { + local file="$1" + + [[ -f "$file" ]] || return 1 + [[ "$file" == *.go ]] || return 1 + + case "$file" in + *.pb.go | *.pb.gw.go | *.pb.json.go | *.pb.validate.go | \ + *.connect.go | *.gen.go | *_gen.go | *_generated.go | \ + *.sql.go | db.go | */db.go | models.go | */models.go | \ + querier.go | */querier.go | .git/* | */.git/* | \ + vendor/* | */vendor/* | third_party/* | */third_party/* | \ + testdata/* | */testdata/*) + return 1 + ;; + esac + + return 0 +} + +print_file() { + local file="$1" + + if is_format_file "$file"; then + printf '%s\0' "$file" + fi +} + +list_all() { + local file + + while IFS= read -r -d '' file; do + print_file "${file#./}" + done < <(find . -type f -name '*.go' -print0) +} + +list_changed() { + local base="$1" + local file + + { + git diff --name-only --diff-filter=ACMR "$base"...HEAD + git diff --name-only --diff-filter=ACMR + git diff --cached --name-only --diff-filter=ACMR + git ls-files --others --exclude-standard + } | sort -u | while IFS= read -r file; do + print_file "$file" + done +} + +mode="${1:-}" + +case "$mode" in +all) + list_all + ;; +changed) + list_changed "${2:-origin/master}" + ;; +*) + usage + exit 2 + ;; +esac diff --git a/server_mock_test.go b/server_mock_test.go index 0e8c029b6..ae3ae0e83 100644 --- a/server_mock_test.go +++ b/server_mock_test.go @@ -74,8 +74,8 @@ func newServerMock(lnd *test.LndMockServices) *serverMock { } func (s *serverMock) NewLoopOutSwap(_ context.Context, swapHash lntypes.Hash, - amount btcutil.Amount, _ int32, _ [33]byte, _ time.Time, - _ string) (*newLoopOutResponse, error) { + amount btcutil.Amount, _ int32, _ [33]byte, _ time.Time, _ string) ( + *newLoopOutResponse, error) { _, senderKey := test.CreateKey(100) @@ -84,8 +84,9 @@ func (s *serverMock) NewLoopOutSwap(_ context.Context, swapHash lntypes.Hash, } s.swapHash = swapHash - swapPayReqString, err := getInvoice(swapHash, s.swapInvoiceAmt, - swapInvoiceDesc) + swapPayReqString, err := getInvoice( + swapHash, s.swapInvoiceAmt, swapInvoiceDesc, + ) if err != nil { return nil, err } @@ -93,8 +94,9 @@ func (s *serverMock) NewLoopOutSwap(_ context.Context, swapHash lntypes.Hash, // Set the prepay hash to be different from the swap hash. s.prepayHash = swapHash s.prepayHash[0] ^= 1 - prePayReqString, err := getInvoice(s.prepayHash, s.prepayInvoiceAmt, - prepayInvoiceDesc) + prePayReqString, err := getInvoice( + s.prepayHash, s.prepayInvoiceAmt, prepayInvoiceDesc, + ) if err != nil { return nil, err } @@ -132,7 +134,9 @@ func (s *serverMock) GetLoopOutQuote(ctx context.Context, amt btcutil.Amount, }, nil } -func getInvoice(hash lntypes.Hash, amt btcutil.Amount, memo string) (string, error) { +func getInvoice(hash lntypes.Hash, amt btcutil.Amount, + memo string) (string, error) { + // Set different payment addresses for swap invoices. payAddr := [32]byte{1, 2, 3} if memo == swapInvoiceDesc { @@ -142,7 +146,9 @@ func getInvoice(hash lntypes.Hash, amt btcutil.Amount, memo string) (string, err req, err := zpay32.NewInvoice( &chaincfg.TestNet3Params, hash, testTime, zpay32.Description(memo), - zpay32.Amount(lnwire.MilliSatoshi(1000*amt)), + zpay32.Amount( + lnwire.MilliSatoshi(1000*amt), + ), zpay32.PaymentAddr(payAddr), ) if err != nil { @@ -212,10 +218,13 @@ func (s *serverMock) CancelLoopOutSwap(ctx context.Context, details *outCancelDetails) error { s.cancelSwap <- details + return nil } -func (s *serverMock) assertSwapCanceled(t *testing.T, details *outCancelDetails) { +func (s *serverMock) assertSwapCanceled(t *testing.T, + details *outCancelDetails) { + require.Equal(t, details, <-s.cancelSwap) } @@ -229,8 +238,8 @@ func (s *serverMock) GetLoopInTerms(ctx context.Context, initiator string) ( } func (s *serverMock) GetLoopInQuote(context.Context, btcutil.Amount, - route.Vertex, *route.Vertex, [][]zpay32.HopHint, string, - uint32, bool) (*LoopInQuote, error) { + route.Vertex, *route.Vertex, [][]zpay32.HopHint, string, uint32, bool) ( + *LoopInQuote, error) { return &LoopInQuote{ SwapFee: testSwapFee, @@ -246,9 +255,10 @@ func (s *serverMock) SubscribeLoopOutUpdates(_ context.Context, return nil, nil, nil } -// SubscribeLoopInUpdates provides a mocked implementation of state subscriptions. -func (s *serverMock) SubscribeLoopInUpdates(_ context.Context, - _ lntypes.Hash) (<-chan *ServerUpdate, <-chan error, error) { +// SubscribeLoopInUpdates provides a mocked implementation of state +// subscriptions. +func (s *serverMock) SubscribeLoopInUpdates(_ context.Context, _ lntypes.Hash) ( + <-chan *ServerUpdate, <-chan error, error) { return nil, nil, nil } @@ -272,9 +282,9 @@ func (s *serverMock) ReportRoutingResult(_ context.Context, _ lntypes.Hash, return nil } -func (s *serverMock) MuSig2SignSweep(_ context.Context, _ loopdb.ProtocolVersion, - _ lntypes.Hash, _ [32]byte, _ []byte, _ []byte) ([]byte, - []byte, error) { +func (s *serverMock) MuSig2SignSweep(_ context.Context, + _ loopdb.ProtocolVersion, _ lntypes.Hash, _ [32]byte, _ []byte, + _ []byte) ([]byte, []byte, error) { return nil, nil, nil } @@ -282,8 +292,7 @@ func (s *serverMock) MuSig2SignSweep(_ context.Context, _ loopdb.ProtocolVersion func (s *serverMock) MultiMuSig2SignSweep(ctx context.Context, protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash, paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte, - prevoutMap map[wire.OutPoint]*wire.TxOut) ( - []byte, []byte, error) { + prevoutMap map[wire.OutPoint]*wire.TxOut) ([]byte, []byte, error) { return nil, nil, nil } diff --git a/staticaddr/address/interface.go b/staticaddr/address/interface.go index 6f626b00b..5949c2deb 100644 --- a/staticaddr/address/interface.go +++ b/staticaddr/address/interface.go @@ -16,8 +16,7 @@ type Store interface { CreateStaticAddress(ctx context.Context, addrParams *Parameters) error // GetAllStaticAddresses retrieves all static addresses from the store. - GetAllStaticAddresses(ctx context.Context) ([]*Parameters, - error) + GetAllStaticAddresses(ctx context.Context) ([]*Parameters, error) } // Parameters holds all the necessary information for the 2-of-2 multisig diff --git a/staticaddr/address/manager.go b/staticaddr/address/manager.go index 3382210d2..a1cd98d80 100644 --- a/staticaddr/address/manager.go +++ b/staticaddr/address/manager.go @@ -261,8 +261,8 @@ func validateServerAddressParams( return fmt.Errorf("static address CSV expiry must be non-zero") case expiry&^wire.SequenceLockTimeMask != 0: - return fmt.Errorf("static address expiry does not fit into "+ - "CSV: %x", expiry) + return fmt.Errorf("static address expiry does not fit "+ + "into CSV: %x", expiry) case expiry > maxStaticAddressCSVExpiry: return fmt.Errorf("static address CSV expiry %v exceeds "+ @@ -375,8 +375,8 @@ func (m *Manager) GetStaticAddress(ctx context.Context) (*script.StaticAddress, } // ListUnspent returns a list of utxos at the static address. -func (m *Manager) ListUnspent(ctx context.Context, minConfs, - maxConfs int32) ([]*lnwallet.Utxo, error) { +func (m *Manager) ListUnspent(ctx context.Context, minConfs, maxConfs int32) ( + []*lnwallet.Utxo, error) { _, utxos, err := m.ListUnspentRaw(ctx, minConfs, maxConfs) if err != nil { diff --git a/staticaddr/address/manager_test.go b/staticaddr/address/manager_test.go index 5881bf848..4bd870e1b 100644 --- a/staticaddr/address/manager_test.go +++ b/staticaddr/address/manager_test.go @@ -22,7 +22,10 @@ import ( ) var ( - defaultServerPubkeyBytes, _ = hex.DecodeString("021c97a90a411ff2b10dc2a8e32de2f29d2fa49d41bfbb52bd416e460db0747d0d") + defaultServerPubkeyBytes, _ = hex.DecodeString( + "021c97a90a411ff2b10dc2a8e32de2f29d2fa49d41bfbb52bd416e460db" + + "0747d0d", + ) defaultServerPubkey, _ = btcec.ParsePubKey(defaultServerPubkeyBytes) @@ -44,7 +47,8 @@ func (m *mockStaticAddressClient) ServerStaticAddressLoopIn(ctx context.Context, args.Error(1) } -func (m *mockStaticAddressClient) PushStaticAddressSweeplessSigs(ctx context.Context, +func (m *mockStaticAddressClient) PushStaticAddressSweeplessSigs( + ctx context.Context, in *swapserverrpc.PushStaticAddressSweeplessSigsRequest, opts ...grpc.CallOption) ( *swapserverrpc.PushStaticAddressSweeplessSigsResponse, error) { @@ -67,9 +71,8 @@ func (m *mockStaticAddressClient) PushStaticAddressHtlcSigs(ctx context.Context, } func (m *mockStaticAddressClient) ServerWithdrawDeposits(ctx context.Context, - in *swapserverrpc.ServerWithdrawRequest, - opts ...grpc.CallOption) (*swapserverrpc.ServerWithdrawResponse, - error) { + in *swapserverrpc.ServerWithdrawRequest, opts ...grpc.CallOption) ( + *swapserverrpc.ServerWithdrawResponse, error) { args := m.Called(ctx, in, opts) @@ -77,8 +80,8 @@ func (m *mockStaticAddressClient) ServerWithdrawDeposits(ctx context.Context, args.Error(1) } -func (m *mockStaticAddressClient) ServerPsbtWithdrawDeposits(ctx context.Context, - in *swapserverrpc.ServerPsbtWithdrawRequest, +func (m *mockStaticAddressClient) ServerPsbtWithdrawDeposits( + ctx context.Context, in *swapserverrpc.ServerPsbtWithdrawRequest, opts ...grpc.CallOption) (*swapserverrpc.ServerPsbtWithdrawResponse, error) { @@ -160,8 +163,10 @@ func TestNewAddressValidatesServerResponse(t *testing.T) { name: "uncompressed server key", resp: &swapserverrpc.ServerNewAddressResponse{ Params: &swapserverrpc.ServerAddressParameters{ - ServerKey: []byte{0x04}, - Expiry: defaultExpiry, + ServerKey: []byte{ + 0x04, + }, + Expiry: defaultExpiry, }, }, expected: "server public key is not a compressed", @@ -317,7 +322,9 @@ func NewAddressManagerTestContextWithResponse(t *testing.T, // newServerNewAddressResponse returns a valid server response with the given // CSV expiry. -func newServerNewAddressResponse(expiry uint32) *swapserverrpc.ServerNewAddressResponse { +func newServerNewAddressResponse( + expiry uint32) *swapserverrpc.ServerNewAddressResponse { + return &swapserverrpc.ServerNewAddressResponse{ Params: &swapserverrpc.ServerAddressParameters{ ServerKey: defaultServerPubkeyBytes, diff --git a/staticaddr/address/sql_store.go b/staticaddr/address/sql_store.go index 8d78e03c9..8be73eb85 100644 --- a/staticaddr/address/sql_store.go +++ b/staticaddr/address/sql_store.go @@ -65,8 +65,8 @@ func (s *SqlStore) GetAllStaticAddresses(ctx context.Context) ([]*Parameters, // toAddressParameters transforms a database representation of a static address // to an AddressParameters struct. -func (s *SqlStore) toAddressParameters(row sqlc.StaticAddress) ( - *Parameters, error) { +func (s *SqlStore) toAddressParameters(row sqlc.StaticAddress) (*Parameters, + error) { clientPubkey, err := btcec.ParsePubKey(row.ClientPubkey) if err != nil { diff --git a/staticaddr/deposit/actions.go b/staticaddr/deposit/actions.go index 362417e7d..2fce81e67 100644 --- a/staticaddr/deposit/actions.go +++ b/staticaddr/deposit/actions.go @@ -44,14 +44,18 @@ func (f *FSM) PublishDepositExpirySweepAction(ctx context.Context, ctx, DefaultConfTarget, ) if err != nil { - return f.HandleError(fmt.Errorf("timeout sweep fee "+ - "estimation failed: %w", err)) + return f.HandleError( + fmt.Errorf("timeout sweep fee estimation failed: %w", + err), + ) } minRelayFeeRate, err := f.cfg.WalletKit.MinRelayFee(ctx) if err != nil { - return f.HandleError(fmt.Errorf("timeout sweep min relay "+ - "query failed: %w", err)) + return f.HandleError( + fmt.Errorf("timeout sweep min relay query failed: %w", + err), + ) } weight := script.ExpirySpendWeight() @@ -67,8 +71,10 @@ func (f *FSM) PublishDepositExpirySweepAction(ctx context.Context, return f.HandleError(err) } if clamped { - return f.HandleError(errors.New("fee is greater than 20% of " + - "the deposit value")) + return f.HandleError( + errors.New("fee is greater than 20% of the deposit " + + "value"), + ) } output := &wire.TxOut{ @@ -115,6 +121,7 @@ func (f *FSM) PublishDepositExpirySweepAction(ctx context.Context, if !strings.Contains(err.Error(), "output already spent") { log.Errorf("%v: %v", txLabel, err) f.LastActionError = err + return fsm.OnError } } else { @@ -148,10 +155,12 @@ func (f *FSM) WaitForExpirySweepAction(ctx context.Context, select { case err = <-errSpendChan: log.Debugf("error while sweeping expired deposit: %v", err) + return fsm.OnError case confirmedTx := <-spendChan: f.deposit.ExpirySweepTxid = confirmedTx.Tx.TxHash() + return OnExpirySwept case <-ctx.Done(): diff --git a/staticaddr/deposit/deposit.go b/staticaddr/deposit/deposit.go index 4cb64bc95..c4eaa298e 100644 --- a/staticaddr/deposit/deposit.go +++ b/staticaddr/deposit/deposit.go @@ -114,5 +114,6 @@ func (d *Deposit) IsInStateNoLock(state fsm.StateType) bool { func GetRandomDepositID() (ID, error) { var id ID _, err := rand.Read(id[:]) + return id, err } diff --git a/staticaddr/deposit/fsm.go b/staticaddr/deposit/fsm.go index 197bf2ee3..b2d799d6a 100644 --- a/staticaddr/deposit/fsm.go +++ b/staticaddr/deposit/fsm.go @@ -199,15 +199,13 @@ func NewFSM(ctx context.Context, deposit *Deposit, cfg *ManagerConfig, depositStates := depoFsm.DepositStatesV0() switch params.ProtocolVersion { case version.ProtocolVersion_V0: - default: return nil, ErrProtocolVersionNotSupported } if recoverStateMachine { depoFsm.StateMachine = fsm.NewStateMachineWithState( - depositStates, deposit.GetState(), - DefaultObserverSize, + depositStates, deposit.GetState(), DefaultObserverSize, ) } else { depoFsm.StateMachine = fsm.NewStateMachine( @@ -438,8 +436,7 @@ func (f *FSM) updateDeposit(ctx context.Context, f.Debugf("NextState: %v, PreviousState: %v, Event: %v", notification.NextState, notification.PreviousState, - notification.Event, - ) + notification.Event) err := f.cfg.Store.UpdateDeposit(ctx, f.deposit) if err != nil { @@ -447,8 +444,8 @@ func (f *FSM) updateDeposit(ctx context.Context, } } -// isUpdateSkipped returns true if the deposit should not be updated for the given -// notification. +// isUpdateSkipped returns true if the deposit should not be updated for the +// given notification. func isUpdateSkipped(notification fsm.Notification, checkStateFunc func(stateType fsm.StateType) bool) bool { @@ -476,35 +473,23 @@ func isUpdateSkipped(notification fsm.Notification, // Infof logs an info message with the deposit outpoint. func (f *FSM) Infof(format string, args ...any) { - log.Infof( - "Deposit %v: "+format, - append( - []any{f.deposit.OutPoint}, - args..., - )..., - ) + log.Infof("Deposit %v: "+format, append( + []any{f.deposit.OutPoint}, args..., + )...) } // Debugf logs a debug message with the deposit outpoint. func (f *FSM) Debugf(format string, args ...any) { - log.Debugf( - "Deposit %v: "+format, - append( - []any{f.deposit.OutPoint}, - args..., - )..., - ) + log.Debugf("Deposit %v: "+format, append( + []any{f.deposit.OutPoint}, args..., + )...) } // Errorf logs an error message with the deposit outpoint. func (f *FSM) Errorf(format string, args ...any) { - log.Errorf( - "Deposit %v: "+format, - append( - []any{f.deposit.OutPoint}, - args..., - )..., - ) + log.Errorf("Deposit %v: "+format, append( + []any{f.deposit.OutPoint}, args..., + )...) } // SignDescriptor returns the sign descriptor for the static address output. diff --git a/staticaddr/deposit/interface.go b/staticaddr/deposit/interface.go index c18011bc3..df8056261 100644 --- a/staticaddr/deposit/interface.go +++ b/staticaddr/deposit/interface.go @@ -27,8 +27,8 @@ type Store interface { GetDeposit(ctx context.Context, depositID ID) (*Deposit, error) // DepositForOutpoint retrieves the deposit with the given outpoint. - DepositForOutpoint(ctx context.Context, outpoint string) (*Deposit, - error) + DepositForOutpoint(ctx context.Context, + outpoint string) (*Deposit, error) // AllDeposits retrieves all deposits from the store. AllDeposits(ctx context.Context) ([]*Deposit, error) @@ -37,8 +37,10 @@ type Store interface { // AddressManager handles fetching of address parameters. type AddressManager interface { // GetStaticAddressParameters returns the static address parameters. - GetStaticAddressParameters(ctx context.Context) (*address.Parameters, - error) + GetStaticAddressParameters(ctx context.Context) ( + *address.Parameters, + error, + ) // GetStaticAddress returns the deposit address for the given // client and server public keys. diff --git a/staticaddr/deposit/manager.go b/staticaddr/deposit/manager.go index af8820302..7bffa49b2 100644 --- a/staticaddr/deposit/manager.go +++ b/staticaddr/deposit/manager.go @@ -91,7 +91,9 @@ func NewManager(cfg *ManagerConfig) *Manager { // Run runs the address manager. func (m *Manager) Run(ctx context.Context, initChan chan struct{}) error { - newBlockChan, newBlockErrChan, err := m.cfg.ChainNotifier.RegisterBlockEpochNtfn(ctx) //nolint:lll + newBlockChan, newBlockErrChan, err := m.cfg.ChainNotifier.RegisterBlockEpochNtfn( + ctx, + ) //nolint:lll if err != nil { log.Errorf("unable to register block epoch notifier: %v", err) @@ -134,7 +136,6 @@ func (m *Manager) Run(ctx context.Context, initChan chan struct{}) error { for _, fsm := range activeDeposits { select { case fsm.blockNtfnChan <- uint32(height): - case <-fsm.quitChan: continue @@ -248,6 +249,7 @@ func (m *Manager) reconcileDeposits(ctx context.Context) error { newDeposits := m.filterNewDeposits(utxos) if len(newDeposits) == 0 { log.Tracef("No new deposits...") + return nil } @@ -271,8 +273,8 @@ func (m *Manager) reconcileDeposits(ctx context.Context) error { // createNewDeposit transforms the wallet utxo into a deposit struct and stores // it in our database and manager memory. -func (m *Manager) createNewDeposit(ctx context.Context, - utxo *lnwallet.Utxo) (*Deposit, error) { +func (m *Manager) createNewDeposit(ctx context.Context, utxo *lnwallet.Utxo) ( + *Deposit, error) { blockHeight, err := m.getBlockHeight(ctx, utxo) if err != nil { @@ -319,8 +321,8 @@ func (m *Manager) createNewDeposit(ctx context.Context, } // getBlockHeight retrieves the block height of a given utxo. -func (m *Manager) getBlockHeight(ctx context.Context, - utxo *lnwallet.Utxo) (uint32, error) { +func (m *Manager) getBlockHeight(ctx context.Context, utxo *lnwallet.Utxo) ( + uint32, error) { addressParams, err := m.cfg.AddressManager.GetStaticAddressParameters( ctx, @@ -569,15 +571,15 @@ func (m *Manager) toActiveDeposits(outpoints *[]wire.OutPoint) ([]*FSM, // DepositsForOutpoints returns all deposits that are behind the given // outpoints. -func (m *Manager) DepositsForOutpoints(ctx context.Context, - outpoints []string, ignoreUnknown bool) ([]*Deposit, error) { +func (m *Manager) DepositsForOutpoints(ctx context.Context, outpoints []string, + ignoreUnknown bool) ([]*Deposit, error) { // Check for duplicates. existingOutpoints := make(map[string]struct{}, len(outpoints)) for i, o := range outpoints { if _, ok := existingOutpoints[o]; ok { - return nil, fmt.Errorf("duplicate outpoint %s "+ - "at index %d", o, i) + return nil, fmt.Errorf("duplicate outpoint %s at "+ + "index %d", o, i) } existingOutpoints[o] = struct{}{} } @@ -594,6 +596,7 @@ func (m *Manager) DepositsForOutpoints(ctx context.Context, if ignoreUnknown && errors.Is(err, ErrDepositNotFound) { continue } + return nil, err } diff --git a/staticaddr/deposit/manager_test.go b/staticaddr/deposit/manager_test.go index ab8aaa7a8..e68ae70f3 100644 --- a/staticaddr/deposit/manager_test.go +++ b/staticaddr/deposit/manager_test.go @@ -26,7 +26,10 @@ import ( ) var ( - defaultServerPubkeyBytes, _ = hex.DecodeString("021c97a90a411ff2b10dc2a8e32de2f29d2fa49d41bfbb52bd416e460db0747d0d") + defaultServerPubkeyBytes, _ = hex.DecodeString( + "021c97a90a411ff2b10dc2a8e32de2f29d2fa49d41bfbb52bd416e460db" + + "0747d0d", + ) defaultServerPubkey, _ = btcec.ParsePubKey(defaultServerPubkeyBytes) @@ -50,7 +53,8 @@ func (m *mockStaticAddressClient) ServerStaticAddressLoopIn(ctx context.Context, args.Error(1) } -func (m *mockStaticAddressClient) PushStaticAddressSweeplessSigs(ctx context.Context, +func (m *mockStaticAddressClient) PushStaticAddressSweeplessSigs( + ctx context.Context, in *swapserverrpc.PushStaticAddressSweeplessSigsRequest, opts ...grpc.CallOption) ( *swapserverrpc.PushStaticAddressSweeplessSigsResponse, error) { @@ -73,9 +77,8 @@ func (m *mockStaticAddressClient) PushStaticAddressHtlcSigs(ctx context.Context, } func (m *mockStaticAddressClient) ServerWithdrawDeposits(ctx context.Context, - in *swapserverrpc.ServerWithdrawRequest, - opts ...grpc.CallOption) (*swapserverrpc.ServerWithdrawResponse, - error) { + in *swapserverrpc.ServerWithdrawRequest, opts ...grpc.CallOption) ( + *swapserverrpc.ServerWithdrawResponse, error) { args := m.Called(ctx, in, opts) @@ -83,8 +86,8 @@ func (m *mockStaticAddressClient) ServerWithdrawDeposits(ctx context.Context, args.Error(1) } -func (m *mockStaticAddressClient) ServerPsbtWithdrawDeposits(ctx context.Context, - in *swapserverrpc.ServerPsbtWithdrawRequest, +func (m *mockStaticAddressClient) ServerPsbtWithdrawDeposits( + ctx context.Context, in *swapserverrpc.ServerPsbtWithdrawRequest, opts ...grpc.CallOption) (*swapserverrpc.ServerPsbtWithdrawResponse, error) { @@ -126,8 +129,8 @@ func (m *mockAddressManager) GetStaticAddress(ctx context.Context) ( args.Error(1) } -func (m *mockAddressManager) ListUnspent(ctx context.Context, - minConfs, maxConfs int32) ([]*lnwallet.Utxo, error) { +func (m *mockAddressManager) ListUnspent(ctx context.Context, minConfs, + maxConfs int32) ([]*lnwallet.Utxo, error) { args := m.Called(ctx, minConfs, maxConfs) @@ -135,9 +138,9 @@ func (m *mockAddressManager) ListUnspent(ctx context.Context, args.Error(1) } -func (m *mockAddressManager) GetTaprootAddress(clientPubkey, - serverPubkey *btcec.PublicKey, expiry int64) (*btcutil.AddressTaproot, - error) { +func (m *mockAddressManager) GetTaprootAddress( + clientPubkey, serverPubkey *btcec.PublicKey, expiry int64) ( + *btcutil.AddressTaproot, error) { args := m.Called(clientPubkey, serverPubkey, expiry) @@ -151,11 +154,13 @@ type mockStore struct { func (s *mockStore) CreateDeposit(ctx context.Context, deposit *Deposit) error { args := s.Called(ctx, deposit) + return args.Error(0) } func (s *mockStore) UpdateDeposit(ctx context.Context, deposit *Deposit) error { args := s.Called(ctx, deposit) + return args.Error(0) } @@ -163,18 +168,21 @@ func (s *mockStore) GetDeposit(ctx context.Context, depositID ID) (*Deposit, error) { args := s.Called(ctx, depositID) + return args.Get(0).(*Deposit), args.Error(1) } -func (s *mockStore) DepositForOutpoint(ctx context.Context, - outpoint string) (*Deposit, error) { +func (s *mockStore) DepositForOutpoint(ctx context.Context, outpoint string) ( + *Deposit, error) { args := s.Called(ctx, outpoint) + return args.Get(0).(*Deposit), args.Error(1) } func (s *mockStore) AllDeposits(ctx context.Context) ([]*Deposit, error) { args := s.Called(ctx) + return args.Get(0).([]*Deposit), args.Error(1) } @@ -182,9 +190,8 @@ type MockChainNotifier struct { mock.Mock } -func (m *MockChainNotifier) RawClientWithMacAuth( - ctx context.Context) (context.Context, time.Duration, - chainrpc.ChainNotifierClient) { +func (m *MockChainNotifier) RawClientWithMacAuth(ctx context.Context) ( + context.Context, time.Duration, chainrpc.ChainNotifierClient) { return ctx, 0, nil } @@ -195,6 +202,7 @@ func (m *MockChainNotifier) RegisterConfirmationsNtfn(ctx context.Context, chan error, error) { args := m.Called(ctx, txid, pkScript, numConfs, heightHint) + return args.Get(0).(chan *chainntnfs.TxConfirmation), args.Get(1).(chan error), args.Error(2) } @@ -203,6 +211,7 @@ func (m *MockChainNotifier) RegisterBlockEpochNtfn(ctx context.Context) ( chan int32, chan error, error) { args := m.Called(ctx) + return args.Get(0).(chan int32), args.Get(1).(chan error), args.Error(2) } @@ -212,6 +221,7 @@ func (m *MockChainNotifier) RegisterSpendNtfn(ctx context.Context, chan error, error) { args := m.Called(ctx, pkScript, heightHint) + return args.Get(0).(chan *chainntnfs.SpendDetail), args.Get(1).(chan error), args.Error(2) } @@ -265,7 +275,6 @@ func TestManager(t *testing.T) { // Ensure that the deposit state machine signed the expiry tx. select { case <-testContext.mockLnd.SignOutputRawChannel: - case <-time.After(defaultTimeout): t.Fatal("did not receive sign request") } @@ -274,7 +283,6 @@ func TestManager(t *testing.T) { var expiryTx *wire.MsgTx select { case expiryTx = <-testContext.mockLnd.TxPublishChannel: - case <-time.After(defaultTimeout): t.Fatal("did not receive published expiry tx") } @@ -346,12 +354,16 @@ func newManagerTestContext(t *testing.T) *ManagerTestContext { require.NoError(t, err) storedDeposits := []*Deposit{ { - ID: ID, - state: Deposited, - OutPoint: utxo.OutPoint, - Value: utxo.Value, - ConfirmationHeight: 3, - TimeOutSweepPkScript: []byte{0x42, 0x21, 0x69}, + ID: ID, + state: Deposited, + OutPoint: utxo.OutPoint, + Value: utxo.Value, + ConfirmationHeight: 3, + TimeOutSweepPkScript: []byte{ + 0x42, + 0x21, + 0x69, + }, }, } diff --git a/staticaddr/deposit/sql_store.go b/staticaddr/deposit/sql_store.go index d9f4249fe..5989f20f2 100644 --- a/staticaddr/deposit/sql_store.go +++ b/staticaddr/deposit/sql_store.go @@ -159,8 +159,8 @@ func (s *SqlStore) GetDeposit(ctx context.Context, id ID) (*Deposit, error) { // DepositForOutpoint retrieves the deposit with the given outpoint from the // database. -func (s *SqlStore) DepositForOutpoint(ctx context.Context, - outpoint string) (*Deposit, error) { +func (s *SqlStore) DepositForOutpoint(ctx context.Context, outpoint string) ( + *Deposit, error) { var deposit *Deposit err := s.baseDB.ExecTx(ctx, loopdb.NewSqlReadOpts(), @@ -242,8 +242,8 @@ func (s *SqlStore) AllDeposits(ctx context.Context) ([]*Deposit, error) { } // ToDeposit converts an sql deposit to a deposit. -func ToDeposit(row sqlc.Deposit, lastUpdate sqlc.DepositUpdate) (*Deposit, - error) { +func ToDeposit(row sqlc.Deposit, + lastUpdate sqlc.DepositUpdate) (*Deposit, error) { id := ID{} err := id.FromByteSlice(row.DepositID) diff --git a/staticaddr/deposit/sql_store_test.go b/staticaddr/deposit/sql_store_test.go index 5656e386a..5fdc3a60d 100644 --- a/staticaddr/deposit/sql_store_test.go +++ b/staticaddr/deposit/sql_store_test.go @@ -66,7 +66,12 @@ func TestToDeposit(t *testing.T) { } else { require.NoError(t, err) require.NotNil(t, result) - require.Equal(t, fsm.StateType(test.lastUpdate.UpdateState), result.state) + require.Equal( + t, fsm.StateType( + test.lastUpdate.UpdateState, + ), + result.state, + ) } }) } diff --git a/staticaddr/loopin/actions.go b/staticaddr/loopin/actions.go index 70a27811f..825646e40 100644 --- a/staticaddr/loopin/actions.go +++ b/staticaddr/loopin/actions.go @@ -182,8 +182,8 @@ func (f *FSM) InitHtlcAction(ctx context.Context, ) if err != nil { pushEmptySigs() - err = fmt.Errorf("server response parameters are outside "+ - "our allowed range: %w", err) + err = fmt.Errorf("server response parameters are outside our "+ + "allowed range: %w", err) return f.HandleError(err) } @@ -217,11 +217,15 @@ func (f *FSM) InitHtlcAction(ctx context.Context, // tx since we might have to sweep the timeout path. We maximally allow // a configured percentage of the swap value to be spent on fees. amt := float64(swapAmount) - maxHtlcTxFee := btcutil.Amount(amt * - f.cfg.MaxStaticAddrHtlcFeePercentage) + maxHtlcTxFee := btcutil.Amount( + amt * + f.cfg.MaxStaticAddrHtlcFeePercentage, + ) - maxHtlcTxBackupFee := btcutil.Amount(amt * - f.cfg.MaxStaticAddrHtlcBackupFeePercentage) + maxHtlcTxBackupFee := btcutil.Amount( + amt * + f.cfg.MaxStaticAddrHtlcBackupFeePercentage, + ) feeRate := chainfee.SatPerKWeight(loopInResp.StandardHtlcInfo.FeeRate) fee := feeRate.FeeForWeight(f.loopIn.htlcWeight(hasChange)) @@ -236,7 +240,9 @@ func (f *FSM) InitHtlcAction(ctx context.Context, } f.loopIn.HtlcTxFeeRate = feeRate - highFeeRate := chainfee.SatPerKWeight(loopInResp.HighFeeHtlcInfo.FeeRate) + highFeeRate := chainfee.SatPerKWeight( + loopInResp.HighFeeHtlcInfo.FeeRate, + ) fee = highFeeRate.FeeForWeight(f.loopIn.htlcWeight(hasChange)) if fee > maxHtlcTxBackupFee { // Abort the swap by pushing empty sigs to the server. @@ -304,8 +310,8 @@ func (f *FSM) SignHtlcTxAction(ctx context.Context, f.cfg.AddressManager.GetStaticAddressParameters(ctx) if err != nil { - err = fmt.Errorf("unable to get static address parameters: "+ - "%w", err) + err = fmt.Errorf("unable to get static address parameters: %w", + err) return f.HandleError(err) } @@ -346,6 +352,7 @@ func (f *FSM) SignHtlcTxAction(ctx context.Context, ) if err != nil { err = fmt.Errorf("unable to convert nonces: %w", err) + return f.HandleError(err) } defer f.cleanUpSessions(ctx, htlcSessionsExtremelyHighFee) @@ -371,6 +378,7 @@ func (f *FSM) SignHtlcTxAction(ctx context.Context, ) if err != nil { err = fmt.Errorf("unable to create the htlc tx: %w", err) + return f.HandleError(err) } @@ -380,6 +388,7 @@ func (f *FSM) SignHtlcTxAction(ctx context.Context, ) if err != nil { err = fmt.Errorf("unable to sign htlc tx: %w", err) + return f.HandleError(err) } @@ -392,7 +401,8 @@ func (f *FSM) SignHtlcTxAction(ctx context.Context, } htlcSigsExtremelyHighFee, err := f.loopIn.signMusig2Tx( ctx, htlcTxExtremelyHighFee, f.cfg.Signer, - htlcSessionsExtremelyHighFee, f.htlcServerNoncesExtremelyHighFee, + htlcSessionsExtremelyHighFee, + f.htlcServerNoncesExtremelyHighFee, ) if err != nil { return f.HandleError(err) @@ -469,8 +479,7 @@ func (f *FSM) MonitorInvoiceAndHtlcTxAction(ctx context.Context, subscribeCtx, f.loopIn.SwapHash, ) if err != nil { - err = fmt.Errorf("unable to subscribe to swap "+ - "invoice: %w", err) + err = fmt.Errorf("unable to subscribe to swap invoice: %w", err) return f.HandleError(err) } @@ -551,16 +560,16 @@ func (f *FSM) MonitorInvoiceAndHtlcTxAction(ctx context.Context, } cancelInvoice := func() { - f.Errorf("timeout waiting for invoice to be " + - "paid, canceling invoice") + f.Errorf("timeout waiting for invoice to be paid, canceling " + + "invoice") // Cancel the lndclient invoice subscription. cancelInvoiceSubscription() err = f.cfg.InvoicesClient.CancelInvoice(ctx, f.loopIn.SwapHash) if err != nil { - f.Warnf("unable to cancel invoice "+ - "for swap hash: %v", err) + f.Warnf("unable to cancel invoice for swap hash: %v", + err) } } @@ -572,11 +581,12 @@ func (f *FSM) MonitorInvoiceAndHtlcTxAction(ctx context.Context, htlcConfirmed = true case err = <-htlcErrConfChan: - f.Errorf("htlc tx conf chan error, re-registering: "+ - "%v", err) + f.Errorf("htlc tx conf chan error, re-registering: %v", + err) // A previous confirmation may no longer be valid if the - // subscription failed, so reset and wait for a fresh one. + // subscription failed, so reset and wait for a fresh + // one. htlcConfirmed = false // Re-register for htlc confirmation. @@ -659,9 +669,9 @@ func (f *FSM) MonitorInvoiceAndHtlcTxAction(ctx context.Context, deposit.SweepHtlcTimeout, ) if err != nil { - log.Errorf("unable to transition "+ - "deposits to the htlc timeout "+ - "sweeping state: %v", err) + log.Errorf("unable to transition deposits to "+ + "the htlc timeout sweeping state: %v", + err) } return OnSweepHtlcTimeout @@ -676,8 +686,8 @@ func (f *FSM) MonitorInvoiceAndHtlcTxAction(ctx context.Context, case invoices.ContractOpen: case invoices.ContractAccepted: case invoices.ContractSettled: - f.Debugf("received off-chain payment update "+ - "%v", update.State) + f.Debugf("received off-chain payment update %v", + update.State) return OnPaymentReceived @@ -721,8 +731,8 @@ func (f *FSM) SweepHtlcTimeoutAction(ctx context.Context, return OnHtlcTimeoutSweepPublished } - f.Errorf("unable to create and publish htlc timeout sweep "+ - "tx: %v, retrying in %v", err, htlcTimeoutSweepRetryDelay) + f.Errorf("unable to create and publish htlc timeout sweep tx: "+ + "%v, retrying in %v", err, htlcTimeoutSweepRetryDelay) select { // The context is cancelled when the server is shutting @@ -862,9 +872,7 @@ func (f *FSM) createAndPublishHtlcTimeoutSweepTx(ctx context.Context) error { } // Broadcast htlc timeout transaction. - txLabel := fmt.Sprintf( - "htlc-timeout-sweep-%v", f.loopIn.SwapHash, - ) + txLabel := fmt.Sprintf("htlc-timeout-sweep-%v", f.loopIn.SwapHash) err = f.cfg.WalletKit.PublishTransaction(ctx, timeoutTx, txLabel) if err != nil { @@ -874,6 +882,7 @@ func (f *FSM) createAndPublishHtlcTimeoutSweepTx(ctx context.Context) error { f.Errorf("%v: %v", txLabel, err) f.LastActionError = err + return err } } else { diff --git a/staticaddr/loopin/actions_test.go b/staticaddr/loopin/actions_test.go index 40983e151..7a736cc02 100644 --- a/staticaddr/loopin/actions_test.go +++ b/staticaddr/loopin/actions_test.go @@ -124,6 +124,7 @@ func TestMonitorInvoiceAndHtlcTxReRegistersOnConfErr(t *testing.T) { select { case event := <-resultChan: require.Equal(t, OnPaymentReceived, event) + case <-ctx.Done(): t.Fatalf("fsm did not return: %v", ctx.Err()) } @@ -146,15 +147,21 @@ func TestInitHtlcActionPreservesRouteHints(t *testing.T) { dep := &deposit.Deposit{ OutPoint: wire.OutPoint{ - Hash: chainhash.Hash{1}, + Hash: chainhash.Hash{ + 1, + }, Index: 0, }, Value: 500_000, } loopIn := &StaticAddressLoopIn{ - Deposits: []*deposit.Deposit{dep}, - DepositOutpoints: []string{dep.OutPoint.String()}, + Deposits: []*deposit.Deposit{ + dep, + }, + DepositOutpoints: []string{ + dep.OutPoint.String(), + }, SelectedAmount: dep.Value, QuotedSwapFee: 1_000, RouteHints: testStaticAddressRouteHints(), @@ -202,8 +209,8 @@ type mockStaticAddressServer struct { // ServerStaticAddressLoopIn records the request and returns the prepared // response. -func (m *mockStaticAddressServer) ServerStaticAddressLoopIn( - _ context.Context, in *swapserverrpc.ServerStaticAddressLoopInRequest, +func (m *mockStaticAddressServer) ServerStaticAddressLoopIn(_ context.Context, + in *swapserverrpc.ServerStaticAddressLoopInRequest, _ ...grpc.CallOption) (*swapserverrpc.ServerStaticAddressLoopInResponse, error) { @@ -302,8 +309,8 @@ func (n *noopDepositManager) GetAllDeposits(_ context.Context) ( } // AllStringOutpointsActiveDeposits implements DepositManager with a no-op. -func (n *noopDepositManager) AllStringOutpointsActiveDeposits( - _ []string, _ fsm.StateType) ([]*deposit.Deposit, bool) { +func (n *noopDepositManager) AllStringOutpointsActiveDeposits(_ []string, + _ fsm.StateType) ([]*deposit.Deposit, bool) { return nil, false } diff --git a/staticaddr/loopin/deduce_amount_test.go b/staticaddr/loopin/deduce_amount_test.go index a78a64052..96f3c2b18 100644 --- a/staticaddr/loopin/deduce_amount_test.go +++ b/staticaddr/loopin/deduce_amount_test.go @@ -76,6 +76,7 @@ func TestDeduceSwapAmount(t *testing.T) { if tc.wantErr != "" { require.Error(t, err) require.ErrorContains(t, err, tc.wantErr) + return } require.NoError(t, err) diff --git a/staticaddr/loopin/deposit_swaphash_migration.go b/staticaddr/loopin/deposit_swaphash_migration.go index 512cb06ca..86088f353 100644 --- a/staticaddr/loopin/deposit_swaphash_migration.go +++ b/staticaddr/loopin/deposit_swaphash_migration.go @@ -28,8 +28,7 @@ func MigrateDepositSwapHash(ctx context.Context, db loopdb.SwapStore, return fmt.Errorf("unable to check migration status: %w", err) } if migrationDone { - log.Infof("Deposit swap hash migration already done, " + - "skipping") + log.Infof("Deposit swap hash migration already done, skipping") return nil } @@ -69,8 +68,8 @@ func MigrateDepositSwapHash(ctx context.Context, db loopdb.SwapStore, depositsToSwapHashes[deposit.ID] = swap.SwapHash } else { log.Warnf("Duplicate deposit ID %s found for "+ - "outpoint %s, skipping", - deposit.ID, outpoint) + "outpoint %s, skipping", deposit.ID, + outpoint) } } } diff --git a/staticaddr/loopin/deposit_swaphash_migration_test.go b/staticaddr/loopin/deposit_swaphash_migration_test.go index ab93a10d2..dbdf528a6 100644 --- a/staticaddr/loopin/deposit_swaphash_migration_test.go +++ b/staticaddr/loopin/deposit_swaphash_migration_test.go @@ -48,7 +48,12 @@ func TestDepositSwapHashMigration(t *testing.T) { d1, d2 := &deposit.Deposit{ ID: newID(), OutPoint: wire.OutPoint{ - Hash: chainhash.Hash{0x1a, 0x2b, 0x3c, 0x4d}, + Hash: chainhash.Hash{ + 0x1a, + 0x2b, + 0x3c, + 0x4d, + }, Index: 0, }, Value: btcutil.Amount(100_000), @@ -59,7 +64,12 @@ func TestDepositSwapHashMigration(t *testing.T) { &deposit.Deposit{ ID: newID(), OutPoint: wire.OutPoint{ - Hash: chainhash.Hash{0x2a, 0x2b, 0x3c, 0x4e}, + Hash: chainhash.Hash{ + 0x2a, + 0x2b, + 0x3c, + 0x4e, + }, Index: 1, }, Value: btcutil.Amount(200_000), @@ -96,10 +106,12 @@ func TestDepositSwapHashMigration(t *testing.T) { err = swapStore.baseDB.ExecTx(ctxb, loopdb.NewSqlWriteOpts(), func(q Querier) error { swapArgs := sqlc.InsertSwapParams{ - SwapHash: loopIn.SwapHash[:], - Preimage: loopIn.SwapPreimage[:], - InitiationTime: loopIn.InitiationTime, - AmountRequested: int64(loopIn.TotalDepositAmount()), + SwapHash: loopIn.SwapHash[:], + Preimage: loopIn.SwapPreimage[:], + InitiationTime: loopIn.InitiationTime, + AmountRequested: int64( + loopIn.TotalDepositAmount(), + ), CltvExpiry: loopIn.HtlcCltvExpiry, MaxSwapFee: int64(loopIn.MaxSwapFee), InitiationHeight: int32(loopIn.InitiationHeight), @@ -111,13 +123,17 @@ func TestDepositSwapHashMigration(t *testing.T) { SwapHash: loopIn.SwapHash[:], SenderScriptPubkey: loopIn.ClientPubkey.SerializeCompressed(), ReceiverScriptPubkey: loopIn.ServerPubkey.SerializeCompressed(), - ClientKeyFamily: int32(loopIn.HtlcKeyLocator.Family), - ClientKeyIndex: int32(loopIn.HtlcKeyLocator.Index), + ClientKeyFamily: int32( + loopIn.HtlcKeyLocator.Family, + ), + ClientKeyIndex: int32( + loopIn.HtlcKeyLocator.Index, + ), } - // Sanity check, if any of the outpoints contain the outpoint separator. - // If so, we reject the loop-in to prevent potential issues with - // parsing. + // Sanity check, if any of the outpoints contain the + // outpoint separator. If so, we reject the loop-in to + // prevent potential issues with parsing. for _, outpoint := range loopIn.DepositOutpoints { if strings.Contains(outpoint, OutpointSeparator) { return ErrInvalidOutpoint @@ -128,14 +144,20 @@ func TestDepositSwapHashMigration(t *testing.T) { loopIn.DepositOutpoints, OutpointSeparator, ) staticAddressLoopInParams := sqlc.InsertStaticAddressLoopInParams{ - SwapHash: loopIn.SwapHash[:], - SwapInvoice: loopIn.SwapInvoice, - LastHop: loopIn.LastHop, - QuotedSwapFeeSatoshis: int64(loopIn.QuotedSwapFee), + SwapHash: loopIn.SwapHash[:], + SwapInvoice: loopIn.SwapInvoice, + LastHop: loopIn.LastHop, + QuotedSwapFeeSatoshis: int64( + loopIn.QuotedSwapFee, + ), HtlcTimeoutSweepAddress: loopIn.HtlcTimeoutSweepAddress.String(), - HtlcTxFeeRateSatKw: int64(loopIn.HtlcTxFeeRate), - DepositOutpoints: joinedOutpoints, - PaymentTimeoutSeconds: int32(loopIn.PaymentTimeoutSeconds), + HtlcTxFeeRateSatKw: int64( + loopIn.HtlcTxFeeRate, + ), + DepositOutpoints: joinedOutpoints, + PaymentTimeoutSeconds: int32( + loopIn.PaymentTimeoutSeconds, + ), } updateArgs := sqlc.InsertStaticAddressMetaUpdateParams{ diff --git a/staticaddr/loopin/fsm.go b/staticaddr/loopin/fsm.go index ea6e610f7..0d679a137 100644 --- a/staticaddr/loopin/fsm.go +++ b/staticaddr/loopin/fsm.go @@ -54,7 +54,6 @@ func NewFSM(ctx context.Context, loopIn *StaticAddressLoopIn, cfg *Config, loopInStates := loopInFsm.LoopInStatesV0() switch params.ProtocolVersion { case version.ProtocolVersion_V0: - default: return nil, deposit.ErrProtocolVersionNotSupported } @@ -117,7 +116,9 @@ var ( // SucceededTransitioningFailed is the state the swap is in if the swap // payment was received but the client was not able to transition // the deposits to the looped-in state. - SucceededTransitioningFailed = fsm.StateType("SucceededTransitioningFailed") //nolint:lll + SucceededTransitioningFailed = fsm.StateType( + "SucceededTransitioningFailed", + ) //nolint:lll // UnlockDeposits is the state where the deposits are reset. This // happens when the state machine encountered an error and the swap @@ -138,7 +139,10 @@ var FinalStates = []fsm.StateType{ } var AllStates = append( - append([]fsm.StateType{}, PendingStates...), FinalStates..., + append( + []fsm.StateType{}, PendingStates..., + ), + FinalStates..., ) // Events. @@ -147,12 +151,14 @@ var ( OnHtlcInitiated = fsm.EventType("OnHtlcInitiated") OnHtlcTxSigned = fsm.EventType("OnHtlcTxSigned") OnSweepHtlcTimeout = fsm.EventType("OnSweepHtlcTimeout") - OnHtlcTimeoutSweepPublished = fsm.EventType("OnHtlcTimeoutSweepPublished") - OnHtlcTimeoutSwept = fsm.EventType("OnHtlcTimeoutSwept") - OnPaymentReceived = fsm.EventType("OnPaymentReceived") - OnSwapTimedOut = fsm.EventType("OnSwapTimedOut") - OnSucceeded = fsm.EventType("OnSucceeded") - OnRecover = fsm.EventType("OnRecover") + OnHtlcTimeoutSweepPublished = fsm.EventType( + "OnHtlcTimeoutSweepPublished", + ) + OnHtlcTimeoutSwept = fsm.EventType("OnHtlcTimeoutSwept") + OnPaymentReceived = fsm.EventType("OnPaymentReceived") + OnSwapTimedOut = fsm.EventType("OnSwapTimedOut") + OnSucceeded = fsm.EventType("OnSucceeded") + OnRecover = fsm.EventType("OnRecover") ) // LoopInStatesV0 returns the state and transition map for the loop-in state @@ -306,46 +312,42 @@ func isUpdateSkipped(notification fsm.Notification, func (f *FSM) Infof(format string, args ...any) { if f.loopIn == nil { log.Infof(format, args...) + return } - log.Infof( - "StaticAddr loop-in %s: %s", f.loopIn.SwapHash.String(), - fmt.Sprintf(format, args...), - ) + log.Infof("StaticAddr loop-in %s: %s", f.loopIn.SwapHash.String(), + fmt.Sprintf(format, args...)) } // Debugf logs a debug message with the loop-in swap hash. func (f *FSM) Debugf(format string, args ...any) { if f.loopIn == nil { log.Debugf(format, args...) + return } - log.Debugf( - "StaticAddr loop-in %s: %s", f.loopIn.SwapHash.String(), - fmt.Sprintf(format, args...), - ) + log.Debugf("StaticAddr loop-in %s: %s", f.loopIn.SwapHash.String(), + fmt.Sprintf(format, args...)) } // Warnf logs a warning message with the loop-in swap hash. func (f *FSM) Warnf(format string, args ...any) { if f.loopIn == nil { log.Warnf(format, args...) + return } - log.Warnf( - "StaticAddr loop-in %s: %s", f.loopIn.SwapHash.String(), - fmt.Sprintf(format, args...), - ) + log.Warnf("StaticAddr loop-in %s: %s", f.loopIn.SwapHash.String(), + fmt.Sprintf(format, args...)) } // Errorf logs an error message with the loop-in swap hash. func (f *FSM) Errorf(format string, args ...any) { if f.loopIn == nil { log.Errorf(format, args...) + return } - log.Errorf( - "StaticAddr loop-in %s: %s", f.loopIn.SwapHash.String(), - fmt.Sprintf(format, args...), - ) + log.Errorf("StaticAddr loop-in %s: %s", f.loopIn.SwapHash.String(), + fmt.Sprintf(format, args...)) } diff --git a/staticaddr/loopin/interface.go b/staticaddr/loopin/interface.go index 1bf32235a..c2a5a08c6 100644 --- a/staticaddr/loopin/interface.go +++ b/staticaddr/loopin/interface.go @@ -35,8 +35,10 @@ type ( // AddressManager handles fetching of address parameters. type AddressManager interface { // GetStaticAddressParameters returns the static address parameters. - GetStaticAddressParameters(ctx context.Context) (*address.Parameters, - error) + GetStaticAddressParameters(ctx context.Context) ( + *address.Parameters, + error, + ) // GetStaticAddress returns the deposit address for the given client and // server public keys. @@ -68,8 +70,10 @@ type DepositManager interface { // GetActiveDepositsInState returns all active deposits in the given // state. - GetActiveDepositsInState(stateFilter fsm.StateType) ([]*deposit.Deposit, - error) + GetActiveDepositsInState(stateFilter fsm.StateType) ( + []*deposit.Deposit, + error, + ) } // StaticAddressLoopInStore provides access to the static address loop-in DB. @@ -89,8 +93,8 @@ type StaticAddressLoopInStore interface { IsStored(ctx context.Context, swapHash lntypes.Hash) (bool, error) // GetLoopInByHash returns the loop-in swap with the given hash. - GetLoopInByHash(ctx context.Context, swapHash lntypes.Hash) ( - *StaticAddressLoopIn, error) + GetLoopInByHash(ctx context.Context, + swapHash lntypes.Hash) (*StaticAddressLoopIn, error) // SwapHashesForDepositIDs returns a map of swap hashes to deposit IDs // for the given deposit IDs. diff --git a/staticaddr/loopin/loopin.go b/staticaddr/loopin/loopin.go index 37616c675..f9e9c970e 100644 --- a/staticaddr/loopin/loopin.go +++ b/staticaddr/loopin/loopin.go @@ -172,8 +172,8 @@ func (l *StaticAddressLoopIn) getHtlc(chainParams *chaincfg.Params) (*swap.Htlc, // signMusig2Tx adds the server nonces to the musig2 sessions and signs the // transaction. -func (l *StaticAddressLoopIn) signMusig2Tx(ctx context.Context, - tx *wire.MsgTx, signer lndclient.SignerClient, +func (l *StaticAddressLoopIn) signMusig2Tx(ctx context.Context, tx *wire.MsgTx, + signer lndclient.SignerClient, musig2sessions []*input.MuSig2SessionInfo, counterPartyNonces [][musig2.PubNonceSize]byte) ([][]byte, error) { @@ -192,7 +192,6 @@ func (l *StaticAddressLoopIn) signMusig2Tx(ctx context.Context, for idx, outpoint := range outpoints { if !reflect.DeepEqual(tx.TxIn[idx].PreviousOutPoint, outpoint) { - return nil, fmt.Errorf("tx input does not match " + "deposits") } @@ -277,8 +276,8 @@ func (l *StaticAddressLoopIn) createHtlcTx(chainParams *chaincfg.Params, // Check if the server breaches our fee limits. feeLimit := btcutil.Amount(float64(swapAmt) * maxFeePercentage) if fee > feeLimit { - return nil, fmt.Errorf("htlc tx fee %v exceeds max fee %v", - fee, feeLimit) + return nil, fmt.Errorf("htlc tx fee %v exceeds max fee %v", fee, + feeLimit) } htlc, err := l.getHtlc(chainParams) @@ -379,10 +378,9 @@ func (l *StaticAddressLoopIn) createHtlcSweepTx(ctx context.Context, if bytes.Equal( htlcTx.TxOut[0].PkScript, l.AddressParams.PkScript, ) { - return nil, fmt.Errorf("htlc tx output layout " + - "invariant violated: expected HTLC output " + - "at index 0, got change output") + "invariant violated: expected HTLC output at " + + "index 0, got change output") } } @@ -461,6 +459,7 @@ func (l *StaticAddressLoopIn) TotalDepositAmount() btcutil.Amount { for _, d := range l.Deposits { total += d.Value } + return total } diff --git a/staticaddr/loopin/loopin_test.go b/staticaddr/loopin/loopin_test.go index 16ed8af14..05bd66f1b 100644 --- a/staticaddr/loopin/loopin_test.go +++ b/staticaddr/loopin/loopin_test.go @@ -75,7 +75,9 @@ func TestCreateHtlcSweepTxSweepValue(t *testing.T) { deposits := []*deposit.Deposit{ { OutPoint: wire.OutPoint{ - Hash: chainhash.Hash{0xaa}, + Hash: chainhash.Hash{ + 0xaa, + }, Index: 0, }, Value: depositValue, @@ -124,33 +126,36 @@ func TestCreateHtlcSweepTxSweepValue(t *testing.T) { htlcValue := htlcTx.TxOut[htlcIdx].Value changeValue := htlcTx.TxOut[1-htlcIdx].Value - require.NotEqual(t, htlcValue, changeValue, - "HTLC and change values must differ for this test to be "+ - "meaningful") + require.NotEqual( + t, htlcValue, changeValue, "HTLC and change values must "+ + "differ for this test to be meaningful", + ) // Call createHtlcSweepTx and verify that the sweep output is derived // from the HTLC value, not the change. sweepTx, err := loopIn.createHtlcSweepTx( - t.Context(), signer, sweepAddr, feeRate, - network, uint32(loopIn.HtlcCltvExpiry)+1, - maxFeePercentage, + t.Context(), signer, sweepAddr, feeRate, network, + uint32(loopIn.HtlcCltvExpiry)+1, maxFeePercentage, ) require.NoError(t, err) require.Len(t, sweepTx.TxOut, 1) sweepValue := sweepTx.TxOut[0].Value require.Greater(t, sweepValue, int64(0)) - require.LessOrEqual(t, sweepValue, htlcValue, - "sweep value must not exceed HTLC output value") - require.Greater(t, sweepValue, changeValue, - "sweep value should be greater than change "+ - "value, confirming it was derived from "+ - "the HTLC output") + require.LessOrEqual( + t, sweepValue, htlcValue, + "sweep value must not exceed HTLC output value", + ) + require.Greater( + t, sweepValue, changeValue, "sweep value should be greater "+ + "than change value, confirming it was derived from "+ + "the HTLC output", + ) } // newStaticAddress creates a StaticAddress for testing. -func newStaticAddress(clientKey, serverKey *btcec.PublicKey, - csvExpiry int64) (*script.StaticAddress, error) { +func newStaticAddress(clientKey, serverKey *btcec.PublicKey, csvExpiry int64) ( + *script.StaticAddress, error) { return script.NewStaticAddress( input.MuSig2Version100RC2, csvExpiry, clientKey, serverKey, diff --git a/staticaddr/loopin/manager.go b/staticaddr/loopin/manager.go index 444ab5856..3ed9b627c 100644 --- a/staticaddr/loopin/manager.go +++ b/staticaddr/loopin/manager.go @@ -178,7 +178,9 @@ func (m *Manager) Run(ctx context.Context, initChan chan struct{}) error { // Register for notifications of loop-in sweep requests. sweepReqs := m.cfg.NotificationManager. - SubscribeStaticLoopInSweepRequests(ctx) + SubscribeStaticLoopInSweepRequests( + ctx, + ) // Communicate to the caller that the address manager has completed its // initialization. @@ -210,7 +212,6 @@ func (m *Manager) Run(ctx context.Context, initChan chan struct{}) error { } select { case request.respChan <- resp: - case <-ctx.Done(): // Notify subroutines that the main loop has // been canceled. @@ -312,8 +313,8 @@ func (m *Manager) handleLoopInSweepReq(ctx context.Context, // We'll notify the server that we don't consider the swap // finished yet, so it can retry later. _ = m.notifyNotFinished(ctx, swapHash, sweepTx.TxHash()) - return fmt.Errorf("loop-in %v not in Succeeded state", - swapHash) + + return fmt.Errorf("loop-in %v not in Succeeded state", swapHash) } // Perform a sanity check on the number of unsigned tx inputs and @@ -399,8 +400,8 @@ func (m *Manager) handleLoopInSweepReq(ctx context.Context, musig2Session.SessionID, ) if err != nil { - log.Errorf("Error cleaning up musig2 session: "+ - " %v", err) + log.Errorf("Error cleaning up musig2 "+ + "session: %v", err) } }() @@ -444,6 +445,7 @@ func (m *Manager) handleLoopInSweepReq(ctx context.Context, SigningInfo: responseMap, }, ) + return err } @@ -451,8 +453,8 @@ func (m *Manager) handleLoopInSweepReq(ctx context.Context, // back to our static address. An edge case arises if a batch contains two // swaps with identical change outputs. The client needs to ensure that any // swap referenced by the inputs has a respective change output in the batch. -func (m *Manager) checkChange(ctx context.Context, - sweepTx *wire.MsgTx, changeAddr *address.Parameters) error { +func (m *Manager) checkChange(ctx context.Context, sweepTx *wire.MsgTx, + changeAddr *address.Parameters) error { prevOuts := make([]string, len(sweepTx.TxIn)) for i, in := range sweepTx.TxIn { @@ -487,8 +489,8 @@ func (m *Manager) checkChange(ctx context.Context, totalDepositAmount := loopIn.TotalDepositAmount() changeAmt := totalDepositAmount - loopIn.SelectedAmount if changeAmt > 0 && changeAmt < totalDepositAmount { - log.Debugf("expected change output to our "+ - "static address, total_deposit_amount=%v, "+ + log.Debugf("expected change output to our static "+ + "address, total_deposit_amount=%v, "+ "selected_amount=%v, "+ "expected_change_amount=%v ", totalDepositAmount, loopIn.SelectedAmount, @@ -505,14 +507,13 @@ func (m *Manager) checkChange(ctx context.Context, for _, out := range sweepTx.TxOut { if out.Value == int64(expectedChange) && bytes.Equal(out.PkScript, changeAddr.PkScript) { - // We found the expected change output. return nil } } - return fmt.Errorf("couldn't find expected change of %v "+ - "satoshis sent to our static address", expectedChange) + return fmt.Errorf("couldn't find expected change of %v satoshis sent "+ + "to our static address", expectedChange) } // recover stars a loop-in state machine for each non-final loop-in to pick up @@ -570,8 +571,8 @@ func (m *Manager) recoverLoopIns(ctx context.Context) error { go func() { err := fsm.SendEvent(ctx, OnRecover, nil) if err != nil { - log.Errorf("Error sending OnRecover "+ - "event: %v", err) + log.Errorf("Error sending OnRecover event: %v", + err) } }() } @@ -592,13 +593,12 @@ func (m *Manager) DeliverLoopInRequest(ctx context.Context, // Send the new loop-in request to the manager run loop. select { case m.newLoopInChan <- request: - case <-m.exitChan: return nil, fmt.Errorf("loop-in manager has been canceled") case <-ctx.Done(): - return nil, fmt.Errorf("context canceled while initiating " + - "a loop-in swap") + return nil, fmt.Errorf("context canceled while initiating a " + + "loop-in swap") } // Wait for the response from the manager run loop. @@ -642,15 +642,17 @@ func (m *Manager) initiateLoopIn(ctx context.Context, selectedOutpoints, deposit.Deposited, ) if !active { - return nil, fmt.Errorf("one or more deposits are not in "+ - "state %s", deposit.Deposited) + return nil, fmt.Errorf("one or more deposits are not "+ + "in state %s", deposit.Deposited) } case len(selectedOutpoints) == 0: // If an amount was provided, we'll coin-select deposits to // cover for the amount. allDeposits, err := m.cfg.DepositManager. - GetActiveDepositsInState(deposit.Deposited) + GetActiveDepositsInState( + deposit.Deposited, + ) if err != nil { return nil, fmt.Errorf("unable to retrieve all "+ "deposits: %w", err) @@ -677,8 +679,9 @@ func (m *Manager) initiateLoopIn(ctx context.Context, selectedOutpoints = make([]string, 0, len(selectedDeposits)) for _, deposit := range selectedDeposits { - selectedOutpoints = append(selectedOutpoints, - deposit.String()) + selectedOutpoints = append( + selectedOutpoints, deposit.String(), + ) } } @@ -747,8 +750,8 @@ func (m *Manager) initiateLoopIn(ctx context.Context, // If the previously accepted quote fee is lower than what is quoted, we // abort the swap. if quote.SwapFee > req.MaxSwapFee { - log.Warnf("Swap fee %v exceeding maximum of %v", - quote.SwapFee, req.MaxSwapFee) + log.Warnf("Swap fee %v exceeding maximum of %v", quote.SwapFee, + req.MaxSwapFee) return nil, loop.ErrSwapFeeTooHigh } @@ -805,8 +808,7 @@ func (m *Manager) startLoopInFsm(ctx context.Context, // If an error occurs before SignHtlcTx is reached we consider the swap // failed and abort early. err = loopInFsm.DefaultObserver.WaitForState( - ctx, time.Minute, SignHtlcTx, - fsm.WithAbortEarlyOnErrorOption(), + ctx, time.Minute, SignHtlcTx, fsm.WithAbortEarlyOnErrorOption(), ) if err != nil { return nil, err @@ -886,6 +888,7 @@ func SelectDeposits(targetAmount btcutil.Amount, return iExp < jExp } + return deposits[i].Value > deposits[j].Value }) @@ -906,9 +909,9 @@ func SelectDeposits(targetAmount btcutil.Amount, } } - return nil, fmt.Errorf("not enough deposits to cover "+ - "requested amount or prevent dust change, have %d but need %d", - selectedAmount, targetAmount) + return nil, fmt.Errorf("not enough deposits to cover requested amount "+ + "or prevent dust change, have %d but need %d", selectedAmount, + targetAmount) } // IsSwappable checks if a deposit is swappable. It returns true if the deposit @@ -949,20 +952,20 @@ func DeduceSwapAmount(totalDepositAmount btcutil.Amount, selectedAmount) case selectedAmount > 0 && selectedAmount < dustLimit: - return 0, fmt.Errorf("selected amount %v is dust, "+ - "need at least %v", selectedAmount, dustLimit) + return 0, fmt.Errorf("selected amount %v is dust, need at "+ + "least %v", selectedAmount, dustLimit) case totalDepositAmount < dustLimit: - return 0, fmt.Errorf("total deposit value %v is dust, "+ - "need at least %v", totalDepositAmount, dustLimit) + return 0, fmt.Errorf("total deposit value %v is dust, need at "+ + "least %v", totalDepositAmount, dustLimit) case remainingAmount < 0: return 0, fmt.Errorf("selected amount %v exceeds total "+ "deposit value %v", selectedAmount, totalDepositAmount) case remainingAmount > 0 && remainingAmount < dustLimit: - return 0, fmt.Errorf("selected amount %v leaves dust change "+ - "%v", selectedAmount, remainingAmount) + return 0, fmt.Errorf("selected amount %v leaves dust change %v", + selectedAmount, remainingAmount) default: // If the remaining amount is 0 or equal or greater than the @@ -982,8 +985,8 @@ func DeduceSwapAmount(totalDepositAmount btcutil.Amount, // in the sweep transaction. func mapDepositsToIndices( req *swapserverrpc.ServerStaticLoopInSweepNotification, - loopIn *StaticAddressLoopIn, sweepTx *wire.MsgTx) (map[string]int, - error) { + loopIn *StaticAddressLoopIn, + sweepTx *wire.MsgTx) (map[string]int, error) { depositToIdxMap := make(map[string]int) for reqOutpoint := range req.DepositToNonces { @@ -1015,6 +1018,7 @@ func mapDepositsToIndices( "of sweep tx", reqOutpoint) } } + return depositToIdxMap, nil } diff --git a/staticaddr/loopin/manager_test.go b/staticaddr/loopin/manager_test.go index d908a9e16..3bf81a1db 100644 --- a/staticaddr/loopin/manager_test.go +++ b/staticaddr/loopin/manager_test.go @@ -51,74 +51,123 @@ func TestSelectDeposits(t *testing.T) { testCases := []testCase{ { - name: "single deposit exact target", - deposits: []*deposit.Deposit{d1}, + name: "single deposit exact target", + deposits: []*deposit.Deposit{ + d1, + }, targetValue: 1_000_000, - expected: []*deposit.Deposit{d1}, + expected: []*deposit.Deposit{ + d1, + }, expectedErr: "", }, { - name: "prefer larger deposit when both cover", - deposits: []*deposit.Deposit{d1, d2}, + name: "prefer larger deposit when both cover", + deposits: []*deposit.Deposit{ + d1, + d2, + }, targetValue: 1_000_000, - expected: []*deposit.Deposit{d2}, + expected: []*deposit.Deposit{ + d2, + }, expectedErr: "", }, { - name: "prefer largest among three when one is enough", - deposits: []*deposit.Deposit{d1, d2, d3}, + name: "prefer largest among three when one is enough", + deposits: []*deposit.Deposit{ + d1, + d2, + d3, + }, targetValue: 1_000_000, - expected: []*deposit.Deposit{d3}, + expected: []*deposit.Deposit{ + d3, + }, expectedErr: "", }, { - name: "single deposit insufficient by 1", - deposits: []*deposit.Deposit{d1}, + name: "single deposit insufficient by 1", + deposits: []*deposit.Deposit{ + d1, + }, targetValue: 1_000_001, expected: []*deposit.Deposit{}, expectedErr: "not enough deposits to cover", }, { - name: "target leaves exact dust limit change", - deposits: []*deposit.Deposit{d1}, + name: "target leaves exact dust limit change", + deposits: []*deposit.Deposit{ + d1, + }, targetValue: 1_000_000 - dustLimit, - expected: []*deposit.Deposit{d1}, + expected: []*deposit.Deposit{ + d1, + }, expectedErr: "", }, { - name: "target leaves dust change (just over)", - deposits: []*deposit.Deposit{d1}, + name: "target leaves dust change (just over)", + deposits: []*deposit.Deposit{ + d1, + }, targetValue: 1_000_000 - dustLimit + 1, expected: []*deposit.Deposit{}, expectedErr: "not enough deposits to cover", }, { - name: "all deposits exactly match target", - deposits: []*deposit.Deposit{d1, d2, d3}, + name: "all deposits exactly match target", + deposits: []*deposit.Deposit{ + d1, + d2, + d3, + }, targetValue: d1.Value + d2.Value + d3.Value, - expected: []*deposit.Deposit{d1, d2, d3}, + expected: []*deposit.Deposit{ + d1, + d2, + d3, + }, expectedErr: "", }, { - name: "sum minus dust limit is allowed (change == dust)", - deposits: []*deposit.Deposit{d1, d2, d3}, + name: "sum minus dust limit is allowed (change == dust)", + deposits: []*deposit.Deposit{ + d1, + d2, + d3, + }, targetValue: d1.Value + d2.Value + d3.Value - dustLimit, - expected: []*deposit.Deposit{d1, d2, d3}, + expected: []*deposit.Deposit{ + d1, + d2, + d3, + }, expectedErr: "", }, { - name: "sum minus dust limit plus 1 is not allowed (dust change)", - deposits: []*deposit.Deposit{d1, d2, d3}, - targetValue: d1.Value + d2.Value + d3.Value - dustLimit + 1, + name: "sum minus dust limit plus 1 is not allowed (dust change)", + deposits: []*deposit.Deposit{ + d1, + d2, + d3, + }, + targetValue: d1.Value + d2.Value + d3.Value - dustLimit + + 1, expected: []*deposit.Deposit{}, expectedErr: "not enough deposits to cover", }, { - name: "tie by value, prefer earlier expiry", - deposits: []*deposit.Deposit{d3, d4}, + name: "tie by value, prefer earlier expiry", + deposits: []*deposit.Deposit{ + d3, + d4, + }, targetValue: d4.Value - dustLimit, // d3/d4 have the // same value but different expiration. - expected: []*deposit.Deposit{d3}, + expected: []*deposit.Deposit{ + d3, + }, expectedErr: "", }, { @@ -132,15 +181,23 @@ func TestSelectDeposits(t *testing.T) { Value: 3_000_000, ConfirmationHeight: 3000, } - dClose.Hash = chainhash.Hash{5} + dClose.Hash = chainhash.Hash{ + 5, + } dClose.Index = 0 dOK := &deposit.Deposit{ Value: 2_000_000, ConfirmationHeight: 3050, } - dOK.Hash = chainhash.Hash{6} + dOK.Hash = chainhash.Hash{ + 6, + } dOK.Index = 0 - return []*deposit.Deposit{dClose, dOK} + + return []*deposit.Deposit{ + dClose, + dOK, + } }(), targetValue: 1_000_000, csvExpiry: 1000, @@ -152,9 +209,14 @@ func TestSelectDeposits(t *testing.T) { Value: 2_000_000, ConfirmationHeight: 3050, } - dOK.Hash = chainhash.Hash{6} + dOK.Hash = chainhash.Hash{ + 6, + } dOK.Index = 0 - return []*deposit.Deposit{dOK} + + return []*deposit.Deposit{ + dOK, + } }(), expectedErr: "", }, @@ -208,6 +270,7 @@ func (m *mockDepositManager) DepositsForOutpoints(_ context.Context, res = append(res, d) } } + return res, nil } @@ -244,13 +307,14 @@ func (s *mockStore) IsStored(_ context.Context, _ lntypes.Hash) (bool, error) { return false, nil } -func (s *mockStore) GetLoopInByHash(_ context.Context, - swapHash lntypes.Hash) (*StaticAddressLoopIn, error) { +func (s *mockStore) GetLoopInByHash(_ context.Context, swapHash lntypes.Hash) ( + *StaticAddressLoopIn, error) { li, ok := s.loopIns[swapHash] if !ok { return nil, nil } + return li, nil } func (s *mockStore) SwapHashesForDepositIDs(_ context.Context, @@ -326,6 +390,7 @@ func TestCheckChange(t *testing.T) { SelectedAmount: selected, AddressParams: changeAddr, } + return hash, li } @@ -348,10 +413,19 @@ func TestCheckChange(t *testing.T) { // Mapping deposits -> swaps (by deposit IDs). mapIDs := map[lntypes.Hash][]deposit.ID{ - hA: {s1d1.ID, s1d2.ID}, - hB: {s2d1.ID}, - hC: {s3d1.ID}, - hD: {s4d1.ID}, + hA: { + s1d1.ID, + s1d2.ID, + }, + hB: { + s2d1.ID, + }, + hC: { + s3d1.ID, + }, + hD: { + s4d1.ID, + }, } loopIns := map[lntypes.Hash]*StaticAddressLoopIn{ @@ -385,8 +459,11 @@ func TestCheckChange(t *testing.T) { cases := []testCase{ { - name: "no change expected (selected == total)", - inDeps: []*deposit.Deposit{s1d1, s1d2}, + name: "no change expected (selected == total)", + inDeps: []*deposit.Deposit{ + s1d1, + s1d2, + }, // No change output required. outputs: []*wire.TxOut{ { @@ -397,8 +474,10 @@ func TestCheckChange(t *testing.T) { addr: changeAddr, }, { - name: "single swap change present", - inDeps: []*deposit.Deposit{s2d1}, // B -> change 500 + name: "single swap change present", + inDeps: []*deposit.Deposit{ + s2d1, + }, // B -> change 500 outputs: []*wire.TxOut{ { Value: 1337, @@ -412,8 +491,12 @@ func TestCheckChange(t *testing.T) { addr: changeAddr, }, { - name: "multiple swaps different change amounts", - inDeps: []*deposit.Deposit{s2d1, s3d1}, // B(500)+C(400)=900 + name: "multiple swaps different change amounts", + // B(500)+C(400)=900 + inDeps: []*deposit.Deposit{ + s2d1, + s3d1, + }, outputs: []*wire.TxOut{ { Value: 1337, @@ -427,8 +510,12 @@ func TestCheckChange(t *testing.T) { addr: changeAddr, }, { - name: "two swaps with identical change values sum correctly", - inDeps: []*deposit.Deposit{s3d1, s4d1}, // C(400)+D(400)=800 + name: "two swaps with identical change values sum correctly", + // C(400)+D(400)=800 + inDeps: []*deposit.Deposit{ + s3d1, + s4d1, + }, outputs: []*wire.TxOut{ { Value: 1337, @@ -442,16 +529,20 @@ func TestCheckChange(t *testing.T) { addr: changeAddr, }, { - name: "missing change output results in error", - inDeps: []*deposit.Deposit{s2d1}, // expect 500 + name: "missing change output results in error", + inDeps: []*deposit.Deposit{ + s2d1, + }, // expect 500 outputs: []*wire.TxOut{}, addr: changeAddr, expectErr: true, expectedErrMsg: "couldn't find expected change", }, { - name: "wrong address for change output", - inDeps: []*deposit.Deposit{s2d1}, // expect 500 + name: "wrong address for change output", + inDeps: []*deposit.Deposit{ + s2d1, + }, // expect 500 outputs: []*wire.TxOut{ { Value: 1337, @@ -467,8 +558,10 @@ func TestCheckChange(t *testing.T) { expectedErrMsg: "couldn't find expected change", }, { - name: "wrong amount for change output", - inDeps: []*deposit.Deposit{s2d1}, // expect 500 + name: "wrong amount for change output", + inDeps: []*deposit.Deposit{ + s2d1, + }, // expect 500 outputs: []*wire.TxOut{ { Value: 1337, @@ -484,8 +577,13 @@ func TestCheckChange(t *testing.T) { expectedErrMsg: "couldn't find expected change", }, { - name: "mixed swaps some with change some without", - inDeps: []*deposit.Deposit{s1d1, s1d2, s3d1}, // A(0)+C(400)=400 + name: "mixed swaps some with change some without", + // A(0)+C(400)=400 + inDeps: []*deposit.Deposit{ + s1d1, + s1d2, + s3d1, + }, outputs: []*wire.TxOut{ { Value: 1337, diff --git a/staticaddr/loopin/selected_amount_migration_test.go b/staticaddr/loopin/selected_amount_migration_test.go index b68269fc0..f0d1ea9c9 100644 --- a/staticaddr/loopin/selected_amount_migration_test.go +++ b/staticaddr/loopin/selected_amount_migration_test.go @@ -42,7 +42,12 @@ func TestMigrateSelectedSwapAmount(t *testing.T) { d1, d2 := &deposit.Deposit{ ID: newID(), OutPoint: wire.OutPoint{ - Hash: chainhash.Hash{0x1a, 0x2b, 0x3c, 0x4d}, + Hash: chainhash.Hash{ + 0x1a, + 0x2b, + 0x3c, + 0x4d, + }, Index: 0, }, Value: btcutil.Amount(100_000), @@ -53,7 +58,12 @@ func TestMigrateSelectedSwapAmount(t *testing.T) { &deposit.Deposit{ ID: newID(), OutPoint: wire.OutPoint{ - Hash: chainhash.Hash{0x2a, 0x2b, 0x3c, 0x4e}, + Hash: chainhash.Hash{ + 0x2a, + 0x2b, + 0x3c, + 0x4e, + }, Index: 1, }, Value: btcutil.Amount(200_000), @@ -78,12 +88,20 @@ func TestMigrateSelectedSwapAmount(t *testing.T) { require.NoError(t, err) swap := StaticAddressLoopIn{ - SwapHash: lntypes.Hash{0x1, 0x2, 0x3, 0x4}, + SwapHash: lntypes.Hash{ + 0x1, + 0x2, + 0x3, + 0x4, + }, DepositOutpoints: outpoints, ClientPubkey: clientPubKey, ServerPubkey: serverPubKey, HtlcTimeoutSweepAddress: addr, - Deposits: []*deposit.Deposit{d1, d2}, + Deposits: []*deposit.Deposit{ + d1, + d2, + }, } swap.SetState(Succeeded) diff --git a/staticaddr/loopin/sql_store.go b/staticaddr/loopin/sql_store.go index 1b70bbc48..3ce4f16fe 100644 --- a/staticaddr/loopin/sql_store.go +++ b/staticaddr/loopin/sql_store.go @@ -59,8 +59,8 @@ type Querier interface { // given states. The states string is an input for the IN primitive in // sqlite, hence the format needs to be '{State1,State2,...}'. GetStaticAddressLoopInSwapsByStates(ctx context.Context, - states sql.NullString) ([]sqlc.GetStaticAddressLoopInSwapsByStatesRow, - error) + states sql.NullString) ( + []sqlc.GetStaticAddressLoopInSwapsByStatesRow, error) // GetLoopInSwapUpdates retrieves all updates for a loop-in swap. GetLoopInSwapUpdates(ctx context.Context, @@ -125,8 +125,8 @@ func NewSqlStore(db BaseDB, clock clock.Clock, } // GetLoopInByHash returns the loop-in swap with the given hash. -func (s *SqlStore) GetLoopInByHash(ctx context.Context, - swapHash lntypes.Hash) (*StaticAddressLoopIn, error) { +func (s *SqlStore) GetLoopInByHash(ctx context.Context, swapHash lntypes.Hash) ( + *StaticAddressLoopIn, error) { var ( err error @@ -576,10 +576,12 @@ func toStaticAddressLoopIn(_ context.Context, network *chaincfg.Params, SwapInvoice: swap.SwapInvoice, PaymentTimeoutSeconds: uint32(swap.PaymentTimeoutSeconds), LastHop: swap.LastHop, - QuotedSwapFee: btcutil.Amount(swap.QuotedSwapFeeSatoshis), - DepositOutpoints: depositOutpoints, - SelectedAmount: btcutil.Amount(swap.SelectedAmount), - Fast: swap.Fast, + QuotedSwapFee: btcutil.Amount( + swap.QuotedSwapFeeSatoshis, + ), + DepositOutpoints: depositOutpoints, + SelectedAmount: btcutil.Amount(swap.SelectedAmount), + Fast: swap.Fast, HtlcTxFeeRate: chainfee.SatPerKWeight( swap.HtlcTxFeeRateSatKw, ), diff --git a/staticaddr/loopin/sql_store_test.go b/staticaddr/loopin/sql_store_test.go index 356049bc7..a83769989 100644 --- a/staticaddr/loopin/sql_store_test.go +++ b/staticaddr/loopin/sql_store_test.go @@ -45,7 +45,12 @@ func TestGetStaticAddressLoopInSwapsByStates(t *testing.T) { d1, d2 := &deposit.Deposit{ ID: loopingDepositID, OutPoint: wire.OutPoint{ - Hash: chainhash.Hash{0x1a, 0x2b, 0x3c, 0x4d}, + Hash: chainhash.Hash{ + 0x1a, + 0x2b, + 0x3c, + 0x4d, + }, Index: 0, }, Value: btcutil.Amount(100_000), @@ -56,7 +61,12 @@ func TestGetStaticAddressLoopInSwapsByStates(t *testing.T) { &deposit.Deposit{ ID: loopedInDepositID, OutPoint: wire.OutPoint{ - Hash: chainhash.Hash{0x2a, 0x2b, 0x3c, 0x4e}, + Hash: chainhash.Hash{ + 0x2a, + 0x2b, + 0x3c, + 0x4e, + }, Index: 1, }, Value: btcutil.Amount(200_000), @@ -95,10 +105,19 @@ func TestGetStaticAddressLoopInSwapsByStates(t *testing.T) { // Create pending swap. swapHashPending := lntypes.Hash{0x1, 0x2, 0x3, 0x4} swapPending := StaticAddressLoopIn{ - SwapHash: swapHashPending, - SwapPreimage: lntypes.Preimage{0x1, 0x2, 0x3, 0x4}, - DepositOutpoints: []string{d1.OutPoint.String()}, - Deposits: []*deposit.Deposit{d1}, + SwapHash: swapHashPending, + SwapPreimage: lntypes.Preimage{ + 0x1, + 0x2, + 0x3, + 0x4, + }, + DepositOutpoints: []string{ + d1.OutPoint.String(), + }, + Deposits: []*deposit.Deposit{ + d1, + }, ClientPubkey: clientPubKey, ServerPubkey: serverPubKey, HtlcTimeoutSweepAddress: addr, @@ -111,10 +130,19 @@ func TestGetStaticAddressLoopInSwapsByStates(t *testing.T) { // Create succeeded swap. swapHashSucceeded := lntypes.Hash{0x2, 0x2, 0x3, 0x5} swapSucceeded := StaticAddressLoopIn{ - SwapHash: swapHashSucceeded, - SwapPreimage: lntypes.Preimage{0x2, 0x2, 0x3, 0x5}, - DepositOutpoints: []string{d2.OutPoint.String()}, - Deposits: []*deposit.Deposit{d2}, + SwapHash: swapHashSucceeded, + SwapPreimage: lntypes.Preimage{ + 0x2, + 0x2, + 0x3, + 0x5, + }, + DepositOutpoints: []string{ + d2.OutPoint.String(), + }, + Deposits: []*deposit.Deposit{ + d2, + }, ClientPubkey: clientPubKey, ServerPubkey: serverPubKey, HtlcTimeoutSweepAddress: addr, @@ -124,12 +152,17 @@ func TestGetStaticAddressLoopInSwapsByStates(t *testing.T) { err = swapStore.CreateLoopIn(ctxb, &swapSucceeded) require.NoError(t, err) - pendingSwaps, err := swapStore.GetStaticAddressLoopInSwapsByStates(ctxb, PendingStates) + pendingSwaps, err := swapStore.GetStaticAddressLoopInSwapsByStates( + ctxb, PendingStates, + ) require.NoError(t, err) require.Len(t, pendingSwaps, 1) require.Equal(t, swapHashPending, pendingSwaps[0].SwapHash) - require.Equal(t, []string{d1.OutPoint.String()}, pendingSwaps[0].DepositOutpoints) + require.Equal( + t, []string{d1.OutPoint.String()}, + pendingSwaps[0].DepositOutpoints, + ) require.Equal(t, SignHtlcTx, pendingSwaps[0].GetState()) pendingDeposits := pendingSwaps[0].Deposits @@ -139,12 +172,17 @@ func TestGetStaticAddressLoopInSwapsByStates(t *testing.T) { require.Equal(t, d1.Value, pendingDeposits[0].Value) require.Equal(t, deposit.LoopingIn, pendingDeposits[0].GetState()) - finalizedSwaps, err := swapStore.GetStaticAddressLoopInSwapsByStates(ctxb, FinalStates) + finalizedSwaps, err := swapStore.GetStaticAddressLoopInSwapsByStates( + ctxb, FinalStates, + ) require.NoError(t, err) require.Len(t, finalizedSwaps, 1) require.Equal(t, swapHashSucceeded, finalizedSwaps[0].SwapHash) - require.Equal(t, []string{d2.OutPoint.String()}, finalizedSwaps[0].DepositOutpoints) + require.Equal( + t, []string{d2.OutPoint.String()}, + finalizedSwaps[0].DepositOutpoints, + ) require.Equal(t, Succeeded, finalizedSwaps[0].GetState()) finalizedDeposits := finalizedSwaps[0].Deposits @@ -180,7 +218,12 @@ func TestCreateLoopIn(t *testing.T) { d1, d2 := &deposit.Deposit{ ID: newID(), OutPoint: wire.OutPoint{ - Hash: chainhash.Hash{0x1a, 0x2b, 0x3c, 0x4d}, + Hash: chainhash.Hash{ + 0x1a, + 0x2b, + 0x3c, + 0x4d, + }, Index: 0, }, Value: btcutil.Amount(100_000), @@ -191,7 +234,12 @@ func TestCreateLoopIn(t *testing.T) { &deposit.Deposit{ ID: newID(), OutPoint: wire.OutPoint{ - Hash: chainhash.Hash{0x2a, 0x2b, 0x3c, 0x4e}, + Hash: chainhash.Hash{ + 0x2a, + 0x2b, + 0x3c, + 0x4e, + }, Index: 1, }, Value: btcutil.Amount(200_000), @@ -221,11 +269,19 @@ func TestCreateLoopIn(t *testing.T) { // Create pending swap. swapHashPending := lntypes.Hash{0x1, 0x2, 0x3, 0x4} swapPending := StaticAddressLoopIn{ - SwapHash: swapHashPending, - SwapPreimage: lntypes.Preimage{0x1, 0x2, 0x3, 0x4}, + SwapHash: swapHashPending, + SwapPreimage: lntypes.Preimage{ + 0x1, + 0x2, + 0x3, + 0x4, + }, DepositOutpoints: []string{d1.OutPoint.String(), d2.OutPoint.String()}, - Deposits: []*deposit.Deposit{d1, d2}, + Deposits: []*deposit.Deposit{ + d1, + d2, + }, ClientPubkey: clientPubKey, ServerPubkey: serverPubKey, HtlcTimeoutSweepAddress: addr, @@ -255,8 +311,10 @@ func TestCreateLoopIn(t *testing.T) { swap, err := swapStore.GetLoopInByHash(ctxb, swapHashPending) require.NoError(t, err) require.Equal(t, swapHashPending, swap.SwapHash) - require.Equal(t, []string{d1.OutPoint.String(), d2.OutPoint.String()}, - swap.DepositOutpoints) + require.Equal( + t, []string{d1.OutPoint.String(), d2.OutPoint.String()}, + swap.DepositOutpoints, + ) require.Equal(t, SignHtlcTx, swap.GetState()) require.Len(t, swap.Deposits, 2) diff --git a/staticaddr/openchannel/interface.go b/staticaddr/openchannel/interface.go index 73010a2b3..d06cc72f7 100644 --- a/staticaddr/openchannel/interface.go +++ b/staticaddr/openchannel/interface.go @@ -20,8 +20,10 @@ type DepositManager interface { // GetActiveDepositsInState returns all deposits that are in the // given state. - GetActiveDepositsInState(stateFilter fsm.StateType) ([]*deposit.Deposit, - error) + GetActiveDepositsInState(stateFilter fsm.StateType) ( + []*deposit.Deposit, + error, + ) // TransitionDeposits transitions the deposits to the given state. TransitionDeposits(ctx context.Context, deposits []*deposit.Deposit, @@ -31,7 +33,7 @@ type DepositManager interface { type WithdrawalManager interface { CreateFinalizedWithdrawalTx(ctx context.Context, deposits []*deposit.Deposit, withdrawalAddress btcutil.Address, - feeRate chainfee.SatPerKWeight, - selectedWithdrawalAmount int64, - commitmentType lnrpc.CommitmentType) (*wire.MsgTx, []byte, error) + feeRate chainfee.SatPerKWeight, selectedWithdrawalAmount int64, + commitmentType lnrpc.CommitmentType) (*wire.MsgTx, []byte, + error) } diff --git a/staticaddr/openchannel/manager.go b/staticaddr/openchannel/manager.go index 2274ce506..68dde19c8 100644 --- a/staticaddr/openchannel/manager.go +++ b/staticaddr/openchannel/manager.go @@ -119,7 +119,6 @@ func (m *Manager) Run(ctx context.Context) error { select { case req.respChan <- resp: - case <-ctx.Done(): // Notify subroutines that the main loop has // been canceled. @@ -147,8 +146,8 @@ func (m *Manager) recoverOpeningChannelDeposits(ctx context.Context) error { deposit.OpeningChannel, ) if err != nil { - return fmt.Errorf("unable to fetch opening channel deposits: %w", - err) + return fmt.Errorf("unable to fetch opening channel "+ + "deposits: %w", err) } if len(openingDeposits) == 0 { @@ -162,8 +161,8 @@ func (m *Manager) recoverOpeningChannelDeposits(ctx context.Context) error { ctx, 0, 0, ) if err != nil { - return fmt.Errorf("unable to list unspent outputs for recovery: %w", - err) + return fmt.Errorf("unable to list unspent outputs for "+ + "recovery: %w", err) } unspentOutpoints := make(map[wire.OutPoint]struct{}, len(utxos)) @@ -207,8 +206,8 @@ func (m *Manager) recoverOpeningChannelDeposits(ctx context.Context) error { } } - log.Infof("Recovered opening channel deposits: %d returned to Deposited, "+ - "%d marked ChannelPublished", len(deposited), + log.Infof("Recovered opening channel deposits: %d returned to "+ + "Deposited, %d marked ChannelPublished", len(deposited), len(channelPublished)) return nil @@ -287,8 +286,8 @@ func (m *Manager) OpenChannel(ctx context.Context, seen := make(map[wire.OutPoint]struct{}, len(outpoints)) for _, op := range outpoints { if _, ok := seen[op]; ok { - return nil, fmt.Errorf("duplicate outpoint "+ - "%v in request", op) + return nil, fmt.Errorf("duplicate outpoint %v "+ + "in request", op) } seen[op] = struct{}{} } @@ -312,8 +311,8 @@ func (m *Manager) OpenChannel(ctx context.Context, if req.LocalFundingAmount != 0 { deposits, err = staticutil.SelectDeposits( - deposits, req.LocalFundingAmount, - feeRate, chanCommitmentType, + deposits, req.LocalFundingAmount, feeRate, + chanCommitmentType, ) if err != nil { return nil, fmt.Errorf("error selecting "+ @@ -380,19 +379,16 @@ func (m *Manager) OpenChannel(ctx context.Context, if errors.Is(err, errPsbtFinalized) { recoverErr := m.recoverOpeningChannelDeposits(ctx) if recoverErr != nil { - log.Errorf("failed recovering deposits "+ - "after PSBT finalize: %v", - recoverErr) + log.Errorf("failed recovering deposits after PSBT "+ + "finalize: %v", recoverErr) } } else { err2 := m.cfg.DepositManager.TransitionDeposits( - ctx, deposits, fsm.OnError, - deposit.Deposited, + ctx, deposits, fsm.OnError, deposit.Deposited, ) if err2 != nil { - log.Errorf("failed transitioning deposits "+ - "after failed channel open: %v", - err2) + log.Errorf("failed transitioning deposits after "+ + "failed channel open: %v", err2) } } @@ -452,8 +448,8 @@ func (m *Manager) openChannelPsbt(ctx context.Context, // Generate a new, random pending channel ID that we'll use as the main // identifier when sending update messages to the RPC server. if _, err := rand.Read(pendingChanID[:]); err != nil { - return nil, fmt.Errorf("unable to generate random chan ID: "+ - "%w", err) + return nil, fmt.Errorf("unable to generate random chan ID: %w", + err) } log.Infof("Starting PSBT funding flow with pending channel ID %x.\n", pendingChanID) @@ -466,7 +462,8 @@ func (m *Manager) openChannelPsbt(ctx context.Context, // with the wallet, release the resources now. if shimPending { log.Infof("Canceling PSBT funding flow for pending "+ - "channel ID %x.\n", pendingChanID) + "channel ID %x.\n", + pendingChanID) cancelMsg := &lnrpc.FundingTransitionMsg{ Trigger: &lnrpc.FundingTransitionMsg_ShimCancel{ @@ -506,8 +503,8 @@ func (m *Manager) openChannelPsbt(ctx context.Context, rawCtx, _, rawClient := m.cfg.LightningClient.RawClientWithMacAuth(ctx) stream, err := rawClient.OpenChannel(rawCtx, req) if err != nil { - return nil, fmt.Errorf("opening stream to server "+ - "failed: %w", err) + return nil, fmt.Errorf("opening stream to server failed: %w", + err) } // We also need to spawn a goroutine that reads from the server. This @@ -523,8 +520,8 @@ func (m *Manager) openChannelPsbt(ctx context.Context, return } else if err != nil { - srvErr <- fmt.Errorf("got error from server: "+ - "%v", err) + srvErr <- fmt.Errorf("got error from "+ + "server: %v", err) return } @@ -533,7 +530,6 @@ func (m *Manager) openChannelPsbt(ctx context.Context, select { case srvMsg <- resp: case <-quit: - return } } @@ -550,7 +546,8 @@ func (m *Manager) openChannelPsbt(ctx context.Context, case err := <-srvErr: log.Errorf("OpenChannel lnd server error received: "+ - "%v\n", err) + "%v\n", + err) // If the remote peer canceled on us, the reservation // has already been deleted. We don't need to try to @@ -586,6 +583,7 @@ func (m *Manager) openChannelPsbt(ctx context.Context, return nil, fmt.Errorf("%w: %v", errPsbtFinalized, cancelErr) } + return nil, cancelErr } @@ -593,21 +591,18 @@ func (m *Manager) openChannelPsbt(ctx context.Context, case *lnrpc.OpenStatusUpdate_PsbtFund: fundingAmount := update.PsbtFund.FundingAmount if req.LocalFundingAmount != fundingAmount { - err := fmt.Errorf("funding amount "+ - "%v doesn't match local "+ - "funding amount %v", - fundingAmount, - req.LocalFundingAmount) + err := fmt.Errorf("funding amount %v doesn't "+ + "match local funding amount %v", + fundingAmount, req.LocalFundingAmount) return nil, err } addr := update.PsbtFund.FundingAddress - log.Infof("PSBT funding initiated with peer "+ - "%x, funding amount %v, funding "+ - "address %v", req.NodePubkey, - fundingAmount, addr) + log.Infof("PSBT funding initiated with peer %x, "+ + "funding amount %v, funding address %v", + req.NodePubkey, fundingAmount, addr) // Create the psbt funding transaction for the // channel. Ensure the selected deposits amount @@ -653,8 +648,8 @@ func (m *Manager) openChannelPsbt(ctx context.Context, var buffer bytes.Buffer err = signedTx.Serialize(&buffer) if err != nil { - return nil, fmt.Errorf("error serializing "+ - "tx: %w", err) + return nil, fmt.Errorf("error "+ + "serializing tx: %w", err) } transitionMsg := &lnrpc.FundingTransitionMsg{ Trigger: &lnrpc.FundingTransitionMsg_PsbtFinalize{ @@ -689,8 +684,8 @@ func (m *Manager) openChannelPsbt(ctx context.Context, update.ChanPending.Txid, ) if err != nil { - return nil, fmt.Errorf("error creating "+ - "hash for channel open tx: %w", err) + return nil, fmt.Errorf("error creating hash "+ + "for channel open tx: %w", err) } chanOutpoint := &wire.OutPoint{ @@ -729,8 +724,8 @@ func validateInitialPsbtFlags(req *lnrpc.OpenChannelRequest) error { } if req.SpendUnconfirmed { - return fmt.Errorf("SpendUnconfirmed is not supported " + - "for PSBT funding") + return fmt.Errorf("SpendUnconfirmed is not supported for " + + "PSBT funding") } if req.TargetConf != 0 { @@ -744,8 +739,8 @@ func validateInitialPsbtFlags(req *lnrpc.OpenChannelRequest) error { } if req.NodePubkeyString != "" { //nolint:staticcheck - return fmt.Errorf("NodePubkeyString is not supported, " + - "use NodePubkey instead") + return fmt.Errorf("NodePubkeyString is not supported, use " + + "NodePubkey instead") } if req.FundingShim != nil { @@ -775,8 +770,8 @@ func resolveCommitmentType(commitmentType lnrpc.CommitmentType) ( default: return lnrpc.CommitmentType_UNKNOWN_COMMITMENT_TYPE, fmt.Errorf( - "unsupported commitment type %v", commitmentType, - ) + "unsupported "+ + "commitment type %v", commitmentType) } } @@ -787,10 +782,13 @@ func checkPsbtFlags(req *lnrpc.OpenChannelRequest) error { return fmt.Errorf("specifying minimum confirmations for PSBT " + "funding is not supported") } - if req.TargetConf != 0 || req.SatPerByte != 0 || req.SatPerVbyte != 0 { // nolint:staticcheck + if req.TargetConf != 0 || req.SatPerByte != 0 || + req.SatPerVbyte != 0 { // nolint:staticcheck + return fmt.Errorf("setting fee estimation parameters not " + "supported for PSBT funding") } + return nil } @@ -807,14 +805,11 @@ func (m *Manager) DeliverOpenChannelRequest(ctx context.Context, // Send the open channel request to the manager run loop. select { case m.newOpenChannelRequestChan <- request: - case <-m.exitChan: - return nil, fmt.Errorf("open channel manager has been " + - "canceled") + return nil, fmt.Errorf("open channel manager has been canceled") case <-ctx.Done(): - return nil, fmt.Errorf("context canceled while opening " + - "channel") + return nil, fmt.Errorf("context canceled while opening channel") } // Wait for the response from the manager run loop. @@ -823,11 +818,10 @@ func (m *Manager) DeliverOpenChannelRequest(ctx context.Context, return resp.ChanOutpoint, resp.err case <-m.exitChan: - return nil, fmt.Errorf("open channel manager has been " + - "canceled") + return nil, fmt.Errorf("open channel manager has been canceled") case <-ctx.Done(): - return nil, fmt.Errorf("context canceled while waiting " + - "for open channel response") + return nil, fmt.Errorf("context canceled while waiting for " + + "open channel response") } } diff --git a/staticaddr/openchannel/manager_test.go b/staticaddr/openchannel/manager_test.go index f408da169..e2965f171 100644 --- a/staticaddr/openchannel/manager_test.go +++ b/staticaddr/openchannel/manager_test.go @@ -41,8 +41,8 @@ func (m *mockDepositManager) AllOutpointsActiveDeposits([]wire.OutPoint, return nil, false } -func (m *mockDepositManager) GetActiveDepositsInState(stateFilter fsm.StateType) ( - []*deposit.Deposit, error) { +func (m *mockDepositManager) GetActiveDepositsInState( + stateFilter fsm.StateType) ([]*deposit.Deposit, error) { if stateFilter != deposit.OpeningChannel { return nil, nil @@ -106,12 +106,19 @@ func TestRecoverOpeningChannelDepositsMixed(t *testing.T) { spentDeposit := &deposit.Deposit{OutPoint: testOutPoint(2)} depositManager := &mockDepositManager{ - openingDeposits: []*deposit.Deposit{unspentDeposit, spentDeposit}, + openingDeposits: []*deposit.Deposit{ + unspentDeposit, + spentDeposit, + }, } walletKit := &mockWalletKit{ utxos: []*lnwallet.Utxo{ - {OutPoint: unspentDeposit.OutPoint}, - {OutPoint: testOutPoint(99)}, + { + OutPoint: unspentDeposit.OutPoint, + }, + { + OutPoint: testOutPoint(99), + }, }, } @@ -128,13 +135,17 @@ func TestRecoverOpeningChannelDepositsMixed(t *testing.T) { require.Len(t, depositManager.calls, 2) require.Equal(t, fsm.OnError, depositManager.calls[0].event) - require.Equal(t, deposit.Deposited, depositManager.calls[0].expectedState) + require.Equal( + t, deposit.Deposited, depositManager.calls[0].expectedState, + ) require.Equal( t, []wire.OutPoint{unspentDeposit.OutPoint}, depositManager.calls[0].outpoints, ) - require.Equal(t, deposit.OnChannelPublished, depositManager.calls[1].event) + require.Equal( + t, deposit.OnChannelPublished, depositManager.calls[1].event, + ) require.Equal( t, deposit.ChannelPublished, depositManager.calls[1].expectedState, @@ -172,7 +183,9 @@ func TestRecoverOpeningChannelDepositsListUnspentError(t *testing.T) { depositManager := &mockDepositManager{ openingDeposits: []*deposit.Deposit{ - {OutPoint: testOutPoint(1)}, + { + OutPoint: testOutPoint(1), + }, }, } walletKit := &mockWalletKit{ @@ -197,14 +210,18 @@ func TestRecoverOpeningChannelDepositsTransitionError(t *testing.T) { unspentDeposit := &deposit.Deposit{OutPoint: testOutPoint(1)} depositManager := &mockDepositManager{ - openingDeposits: []*deposit.Deposit{unspentDeposit}, + openingDeposits: []*deposit.Deposit{ + unspentDeposit, + }, transitionErrs: map[fsm.EventType]error{ fsm.OnError: errors.New("transition failed"), }, } walletKit := &mockWalletKit{ utxos: []*lnwallet.Utxo{ - {OutPoint: unspentDeposit.OutPoint}, + { + OutPoint: unspentDeposit.OutPoint, + }, }, } manager := &Manager{ @@ -215,7 +232,9 @@ func TestRecoverOpeningChannelDepositsTransitionError(t *testing.T) { } err := manager.recoverOpeningChannelDeposits(context.Background()) - require.ErrorContains(t, err, "unable to recover unspent opening deposits") + require.ErrorContains( + t, err, "unable to recover unspent opening deposits", + ) require.Len(t, depositManager.calls, 1) } @@ -229,13 +248,20 @@ func TestRecoverAfterReorg(t *testing.T) { d2 := &deposit.Deposit{OutPoint: testOutPoint(2)} depositManager := &mockDepositManager{ - openingDeposits: []*deposit.Deposit{d1, d2}, + openingDeposits: []*deposit.Deposit{ + d1, + d2, + }, } walletKit := &mockWalletKit{ utxos: []*lnwallet.Utxo{ // After reorg both UTXOs are unspent again. - {OutPoint: d1.OutPoint}, - {OutPoint: d2.OutPoint}, + { + OutPoint: d1.OutPoint, + }, + { + OutPoint: d2.OutPoint, + }, }, } @@ -256,8 +282,7 @@ func TestRecoverAfterReorg(t *testing.T) { t, deposit.Deposited, depositManager.calls[0].expectedState, ) require.ElementsMatch( - t, - []wire.OutPoint{d1.OutPoint, d2.OutPoint}, + t, []wire.OutPoint{d1.OutPoint, d2.OutPoint}, depositManager.calls[0].outpoints, ) } @@ -270,12 +295,16 @@ func TestRecoverAfterMempoolEviction(t *testing.T) { d := &deposit.Deposit{OutPoint: testOutPoint(1)} depositManager := &mockDepositManager{ - openingDeposits: []*deposit.Deposit{d}, + openingDeposits: []*deposit.Deposit{ + d, + }, } walletKit := &mockWalletKit{ utxos: []*lnwallet.Utxo{ // UTXO reappears after mempool eviction. - {OutPoint: d.OutPoint}, + { + OutPoint: d.OutPoint, + }, }, } @@ -307,14 +336,24 @@ func TestRecoverAfterMempoolRejection(t *testing.T) { d3 := &deposit.Deposit{OutPoint: testOutPoint(3)} depositManager := &mockDepositManager{ - openingDeposits: []*deposit.Deposit{d1, d2, d3}, + openingDeposits: []*deposit.Deposit{ + d1, + d2, + d3, + }, } walletKit := &mockWalletKit{ utxos: []*lnwallet.Utxo{ // All UTXOs still unspent since tx was never accepted. - {OutPoint: d1.OutPoint}, - {OutPoint: d2.OutPoint}, - {OutPoint: d3.OutPoint}, + { + OutPoint: d1.OutPoint, + }, + { + OutPoint: d2.OutPoint, + }, + { + OutPoint: d3.OutPoint, + }, }, } @@ -345,7 +384,10 @@ func TestRecoverDaemonRestartChannelPublished(t *testing.T) { d2 := &deposit.Deposit{OutPoint: testOutPoint(2)} depositManager := &mockDepositManager{ - openingDeposits: []*deposit.Deposit{d1, d2}, + openingDeposits: []*deposit.Deposit{ + d1, + d2, + }, } walletKit := &mockWalletKit{ // No UTXOs returned - all deposit outpoints have been spent. @@ -363,16 +405,14 @@ func TestRecoverDaemonRestartChannelPublished(t *testing.T) { require.NoError(t, err) require.Len(t, depositManager.calls, 1) require.Equal( - t, deposit.OnChannelPublished, - depositManager.calls[0].event, + t, deposit.OnChannelPublished, depositManager.calls[0].event, ) require.Equal( t, deposit.ChannelPublished, depositManager.calls[0].expectedState, ) require.ElementsMatch( - t, - []wire.OutPoint{d1.OutPoint, d2.OutPoint}, + t, []wire.OutPoint{d1.OutPoint, d2.OutPoint}, depositManager.calls[0].outpoints, ) } @@ -384,7 +424,9 @@ func TestRecoverChannelPublishedTransitionError(t *testing.T) { d := &deposit.Deposit{OutPoint: testOutPoint(1)} depositManager := &mockDepositManager{ - openingDeposits: []*deposit.Deposit{d}, + openingDeposits: []*deposit.Deposit{ + d, + }, transitionErrs: map[fsm.EventType]error{ deposit.OnChannelPublished: errors.New( "transition failed", @@ -404,7 +446,9 @@ func TestRecoverChannelPublishedTransitionError(t *testing.T) { } err := manager.recoverOpeningChannelDeposits(context.Background()) - require.ErrorContains(t, err, "unable to recover spent opening deposits") + require.ErrorContains( + t, err, "unable to recover spent opening deposits", + ) } // TestRecoverGetActiveDepositsError verifies that a failure to fetch opening @@ -423,12 +467,16 @@ func TestRecoverGetActiveDepositsError(t *testing.T) { } err := manager.recoverOpeningChannelDeposits(context.Background()) - require.ErrorContains(t, err, "unable to fetch opening channel deposits") + require.ErrorContains( + t, err, "unable to fetch opening channel deposits", + ) } func testOutPoint(b byte) wire.OutPoint { return wire.OutPoint{ - Hash: chainhash.Hash{b}, + Hash: chainhash.Hash{ + b, + }, Index: uint32(b), } } @@ -534,6 +582,7 @@ func TestValidateInitialPsbtFlags(t *testing.T) { err := validateInitialPsbtFlags(tc.req) if tc.expectedErrSubstr == "" { require.NoError(t, err) + return } @@ -587,7 +636,10 @@ func TestResolveCommitmentType(t *testing.T) { ) if tc.expectedErrSubstr == "" { require.NoError(t, err) - require.Equal(t, tc.expectedType, commitmentType) + require.Equal( + t, tc.expectedType, commitmentType, + ) + return } @@ -614,9 +666,8 @@ type mockLndClient struct { fundingStepCtxErrs []error } -func (m *mockLndClient) RawClientWithMacAuth( - ctx context.Context) (context.Context, time.Duration, - lnrpc.LightningClient) { +func (m *mockLndClient) RawClientWithMacAuth(ctx context.Context) ( + context.Context, time.Duration, lnrpc.LightningClient) { return ctx, 0, m.rawClient } @@ -642,8 +693,8 @@ type mockRawLnrpcClient struct { } func (m *mockRawLnrpcClient) OpenChannel(_ context.Context, - _ *lnrpc.OpenChannelRequest, - _ ...grpc.CallOption) (lnrpc.Lightning_OpenChannelClient, error) { + _ *lnrpc.OpenChannelRequest, _ ...grpc.CallOption) ( + lnrpc.Lightning_OpenChannelClient, error) { return m.stream, m.openErr } @@ -696,10 +747,9 @@ type mockWithdrawManager struct { err error } -func (m *mockWithdrawManager) CreateFinalizedWithdrawalTx( - _ context.Context, _ []*deposit.Deposit, - _ btcutil.Address, _ chainfee.SatPerKWeight, _ int64, - _ lnrpc.CommitmentType) (*wire.MsgTx, []byte, error) { +func (m *mockWithdrawManager) CreateFinalizedWithdrawalTx(_ context.Context, + _ []*deposit.Deposit, _ btcutil.Address, _ chainfee.SatPerKWeight, + _ int64, _ lnrpc.CommitmentType) (*wire.MsgTx, []byte, error) { return m.tx, m.psbt, m.err } diff --git a/staticaddr/script/script.go b/staticaddr/script/script.go index 37e78a072..ebcaa7a64 100644 --- a/staticaddr/script/script.go +++ b/staticaddr/script/script.go @@ -32,7 +32,8 @@ const ( // - : 39 bytes // - control_block_varint_len: 1 byte (control block length) // - : 33 bytes - TaprootExpiryWitnessSize = 1 + 1 + 64 + 1 + TaprootExpiryScriptSize + 1 + 33 + TaprootExpiryWitnessSize = 1 + 1 + 64 + 1 + TaprootExpiryScriptSize + + 1 + 33 ) // StaticAddress encapsulates the static address script. @@ -113,8 +114,8 @@ func (s *StaticAddress) StaticAddressScript() ([]byte, error) { // GenTimeoutPathScript constructs a csv timeout script for the client. // // OP_CHECKSIGVERIFY OP_CHECKSEQUENCEVERIFY -func GenTimeoutPathScript(clientKey *btcec.PublicKey, csvExpiry int64) ([]byte, - error) { +func GenTimeoutPathScript(clientKey *btcec.PublicKey, + csvExpiry int64) ([]byte, error) { builder := txscript.NewScriptBuilder() diff --git a/staticaddr/script/script_test.go b/staticaddr/script/script_test.go index 16fa7991e..d02dc8fbd 100644 --- a/staticaddr/script/script_test.go +++ b/staticaddr/script/script_test.go @@ -78,8 +78,8 @@ func TestStaticAddressScript(t *testing.T) { // This is what gets signed. taprootSigHash, err := txscript.CalcTaprootSignatureHash( - sigHashes, txscript.SigHashDefault, tx, 0, - prevOutFetcher, + sigHashes, txscript.SigHashDefault, tx, + 0, prevOutFetcher, ) require.NoError(t, err) @@ -227,19 +227,21 @@ func assertEngineExecution(t *testing.T, valid bool, done, err = vm.Step() if err != nil && valid { fmt.Println(debugBuf.String()) - t.Fatalf("spend test case failed, spend "+ - "should be valid: %v", err) + t.Fatalf("spend test case failed, spend should be "+ + "valid: %v", err) } else if err == nil && !valid && done { fmt.Println(debugBuf.String()) - t.Fatalf("spend test case succeed, spend "+ - "should be invalid: %v", err) + t.Fatalf("spend test case succeed, spend should be "+ + "invalid: %v", err) } debugBuf.WriteString( fmt.Sprintf("Stack: %v", vm.GetStack()), ) debugBuf.WriteString( - fmt.Sprintf("AltStack: %v", vm.GetAltStack()), + fmt.Sprintf( + "AltStack: %v", vm.GetAltStack(), + ), ) } @@ -252,7 +254,5 @@ func assertEngineExecution(t *testing.T, valid bool, } fmt.Println(debugBuf.String()) - t.Fatalf( - "%v spend test case execution ended with: %v", validity, vmErr, - ) + t.Fatalf("%v spend test case execution ended with: %v", validity, vmErr) } diff --git a/staticaddr/staticutil/utils.go b/staticaddr/staticutil/utils.go index 7ef33201b..5e213c359 100644 --- a/staticaddr/staticutil/utils.go +++ b/staticaddr/staticutil/utils.go @@ -46,9 +46,8 @@ func ToPrevOuts(deposits []*deposit.Deposit, } // CreateMusig2Sessions creates a musig2 session for a number of deposits. -func CreateMusig2Sessions(ctx context.Context, - signer lndclient.SignerClient, deposits []*deposit.Deposit, - addrParams *address.Parameters, +func CreateMusig2Sessions(ctx context.Context, signer lndclient.SignerClient, + deposits []*deposit.Deposit, addrParams *address.Parameters, staticAddress *script.StaticAddress) ([]*input.MuSig2SessionInfo, [][]byte, error) { @@ -75,8 +74,7 @@ func CreateMusig2Sessions(ctx context.Context, // deposits. func CreateMusig2SessionsPerDeposit(ctx context.Context, signer lndclient.SignerClient, deposits []*deposit.Deposit, - addrParams *address.Parameters, - staticAddress *script.StaticAddress) ( + addrParams *address.Parameters, staticAddress *script.StaticAddress) ( map[string]*input.MuSig2SessionInfo, map[string][]byte, map[string]int, error) { @@ -102,8 +100,8 @@ func CreateMusig2SessionsPerDeposit(ctx context.Context, } // CreateMusig2Session creates a musig2 session for the deposit. -func CreateMusig2Session(ctx context.Context, - signer lndclient.SignerClient, addrParams *address.Parameters, +func CreateMusig2Session(ctx context.Context, signer lndclient.SignerClient, + addrParams *address.Parameters, staticAddress *script.StaticAddress) (*input.MuSig2SessionInfo, error) { signers := [][]byte{ @@ -116,8 +114,8 @@ func CreateMusig2Session(ctx context.Context, rootHash := expiryLeaf.TapHash() return signer.MuSig2CreateSession( - ctx, input.MuSig2Version100RC2, &addrParams.KeyLocator, - signers, lndclient.MuSig2TaprootTweakOpt(rootHash[:], false), + ctx, input.MuSig2Version100RC2, &addrParams.KeyLocator, signers, + lndclient.MuSig2TaprootTweakOpt(rootHash[:], false), ) } @@ -165,6 +163,7 @@ func bip69inputLess(input1, input2 *swapserverrpc.PrevoutInfo) bool { ihash[b], ihash[hashSize-1-b] = ihash[hashSize-1-b], ihash[b] jhash[b], jhash[hashSize-1-b] = jhash[hashSize-1-b], jhash[b] } + return bytes.Compare(ihash[:], jhash[:]) == -1 } @@ -215,8 +214,8 @@ func SelectDeposits(deposits []*deposit.Deposit, amount int64, } // We exhausted all deposits without meeting the threshold. - return nil, fmt.Errorf("insufficient funds to cover swap " + - "amount plus fees, try manually selecting deposits") + return nil, fmt.Errorf("insufficient funds to cover swap amount plus " + + "fees, try manually selecting deposits") } // estimateFee returns the estimated fee for a transaction with the given @@ -235,6 +234,7 @@ func estimateFee(numInputs int, feeRate chainfee.SatPerKWeight, switch commitmentType { case lnrpc.CommitmentType_SIMPLE_TAPROOT: we.AddP2TROutput() + default: we.AddP2WSHOutput() } diff --git a/staticaddr/staticutil/utils_test.go b/staticaddr/staticutil/utils_test.go index 0694f8c84..efecf9003 100644 --- a/staticaddr/staticutil/utils_test.go +++ b/staticaddr/staticutil/utils_test.go @@ -27,6 +27,7 @@ func mustHash(t *testing.T, s string) chainhash.Hash { t.Helper() h, err := chainhash.NewHashFromStr(s) require.NoError(t, err) + return *h } @@ -34,7 +35,10 @@ func TestToPrevOuts_Success(t *testing.T) { // Prepare two distinct deposits with different outpoints and values. d1 := &deposit.Deposit{ OutPoint: wire.OutPoint{ - Hash: mustHash(t, "0000000000000000000000000000000000000000000000000000000000000001"), + Hash: mustHash( + t, "0000000000000000000000000000000000000000"+ + "000000000000000000000001", + ), Index: 0, }, Value: btcutil.Amount(12345), @@ -42,7 +46,10 @@ func TestToPrevOuts_Success(t *testing.T) { d2 := &deposit.Deposit{ OutPoint: wire.OutPoint{ - Hash: mustHash(t, "1111111111111111111111111111111111111111111111111111111111111111"), + Hash: mustHash( + t, "1111111111111111111111111111111111111111"+ + "111111111111111111111111", + ), Index: 7, }, Value: btcutil.Amount(987654321), @@ -75,9 +82,13 @@ func TestToPrevOuts_Success(t *testing.T) { } func TestToPrevOuts_DuplicateOutpoint(t *testing.T) { - // Two deposits that share the exact same outpoint should cause an error. + // Two deposits that share the exact same outpoint should cause an + // error. shared := wire.OutPoint{ - Hash: mustHash(t, "2222222222222222222222222222222222222222222222222222222222222222"), + Hash: mustHash( + t, "222222222222222222222222222222222222222222222222"+ + "2222222222222222", + ), Index: 2, } @@ -93,20 +104,45 @@ func TestGetPrevoutInfo_ConversionAndSorting(t *testing.T) { must := func(s string) chainhash.Hash { h, err := chainhash.NewHashFromStr(s) require.NoError(t, err) + return *h } // Choose txids such that after reversal, ordering is determined by the // last byte of the original hex string. - txidA := must("0000000000000000000000000000000000000000000000000000000000000001") - txidB := must("0000000000000000000000000000000000000000000000000000000000000002") + txidA := must( + "00000000000000000000000000000000000000000000000000000000000" + + "00001", + ) + txidB := must( + "00000000000000000000000000000000000000000000000000000000000" + + "00002", + ) pkScript := []byte{0xaa, 0xbb} prevOuts := map[wire.OutPoint]*wire.TxOut{ - {Hash: txidA, Index: 5}: {Value: 11, PkScript: pkScript}, - {Hash: txidA, Index: 2}: {Value: 22, PkScript: pkScript}, - {Hash: txidB, Index: 0}: {Value: 33, PkScript: pkScript}, + { + Hash: txidA, + Index: 5, + }: { + Value: 11, + PkScript: pkScript, + }, + { + Hash: txidA, + Index: 2, + }: { + Value: 22, + PkScript: pkScript, + }, + { + Hash: txidB, + Index: 0, + }: { + Value: 33, + PkScript: pkScript, + }, } infos := GetPrevoutInfo(prevOuts) @@ -154,8 +190,14 @@ func TestBip69InputLess_DifferentHashes(t *testing.T) { // txid1 ends with 0x01, txid2 ends with 0x02. After reversing for // comparison, txid1 should still come before txid2 in lexicographic // order. - h1, _ := chainhash.NewHashFromStr("0000000000000000000000000000000000000000000000000000000000000001") - h2, _ := chainhash.NewHashFromStr("0000000000000000000000000000000000000000000000000000000000000002") + h1, _ := chainhash.NewHashFromStr( + "00000000000000000000000000000000000000000000000000000000000" + + "00001", + ) + h2, _ := chainhash.NewHashFromStr( + "00000000000000000000000000000000000000000000000000000000000" + + "00002", + ) a := &swapserverrpc.PrevoutInfo{TxidBytes: h1[:], OutputIndex: 9} b := &swapserverrpc.PrevoutInfo{TxidBytes: h2[:], OutputIndex: 0} @@ -179,17 +221,25 @@ func TestCreateMusig2Session_Success(t *testing.T) { ClientPubkey: clientKey.PubKey(), ServerPubkey: serverKey.PubKey(), Expiry: 10, - PkScript: []byte{0x51}, - KeyLocator: keychain.KeyLocator{Family: 1, Index: 2}, + PkScript: []byte{ + 0x51, + }, + KeyLocator: keychain.KeyLocator{ + Family: 1, + Index: 2, + }, } // Build a static address for tweak options. staticAddr, err := script.NewStaticAddress( - input.MuSig2Version100RC2, int64(params.Expiry), params.ClientPubkey, params.ServerPubkey, + input.MuSig2Version100RC2, int64(params.Expiry), + params.ClientPubkey, params.ServerPubkey, ) require.NoError(t, err) - sess, err := CreateMusig2Session(context.Background(), signer, params, staticAddr) + sess, err := CreateMusig2Session( + context.Background(), signer, params, staticAddr, + ) require.NoError(t, err) require.NotNil(t, sess) } @@ -208,20 +258,38 @@ func TestCreateMusig2Sessions_Multiple(t *testing.T) { ClientPubkey: clientKey.PubKey(), ServerPubkey: serverKey.PubKey(), Expiry: 12, - PkScript: []byte{0xaa}, - KeyLocator: keychain.KeyLocator{Family: 9, Index: 8}, + PkScript: []byte{ + 0xaa, + }, + KeyLocator: keychain.KeyLocator{ + Family: 9, + Index: 8, + }, } staticAddr, err := script.NewStaticAddress( - input.MuSig2Version100RC2, int64(params.Expiry), params.ClientPubkey, params.ServerPubkey, + input.MuSig2Version100RC2, int64(params.Expiry), + params.ClientPubkey, params.ServerPubkey, ) require.NoError(t, err) // Prepare N deposits; only the length matters for session count. deposits := []*deposit.Deposit{ - {OutPoint: wire.OutPoint{Index: 0}}, - {OutPoint: wire.OutPoint{Index: 1}}, - {OutPoint: wire.OutPoint{Index: 2}}, + { + OutPoint: wire.OutPoint{ + Index: 0, + }, + }, + { + OutPoint: wire.OutPoint{ + Index: 1, + }, + }, + { + OutPoint: wire.OutPoint{ + Index: 2, + }, + }, } sessions, nonces, err := CreateMusig2Sessions( @@ -234,7 +302,9 @@ func TestCreateMusig2Sessions_Multiple(t *testing.T) { // The mock signer returns a zero-value PublicNonce; assert consistency. for i := range sessions { require.NotNil(t, sessions[i]) - require.True(t, bytes.Equal(nonces[i], sessions[i].PublicNonce[:])) + require.True( + t, bytes.Equal(nonces[i], sessions[i].PublicNonce[:]), + ) } } @@ -249,6 +319,7 @@ func makeDeposits(values ...btcutil.Amount) []*deposit.Deposit { for i, v := range values { deps[i] = makeDeposit(v) } + return deps } @@ -258,6 +329,7 @@ func depositSum(deps []*deposit.Deposit) btcutil.Amount { for _, d := range deps { total += d.Value } + return total } @@ -320,17 +392,18 @@ func TestSelectDeposits(t *testing.T) { // Many tiny deposits should not cause a false // rejection in the early check. deposits: append( - makeDeposits(300_000, 200_000), - makeDeposits( - 100, 100, 100, 100, 100, - 100, 100, 100, 100, 100, + makeDeposits(300_000, 200_000), makeDeposits( + 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, )..., ), amount: 400_000, feeRate: highFeeRate, commitmentType: anchors, wantCount: 2, - validate: func(t *testing.T, selected []*deposit.Deposit) { + validate: func(t *testing.T, + selected []*deposit.Deposit) { + require.Equal( t, btcutil.Amount(300_000), selected[0].Value, @@ -364,7 +437,9 @@ func TestSelectDeposits(t *testing.T) { feeRate: lowFeeRate, commitmentType: anchors, wantCount: 1, - validate: func(t *testing.T, selected []*deposit.Deposit) { + validate: func(t *testing.T, + selected []*deposit.Deposit) { + // Should pick the 200k deposit. require.Equal( t, btcutil.Amount(200_000), @@ -391,11 +466,12 @@ func TestSelectDeposits(t *testing.T) { feeRate: highFeeRate, commitmentType: anchors, wantCount: 3, - validate: func(t *testing.T, selected []*deposit.Deposit) { + validate: func(t *testing.T, + selected []*deposit.Deposit) { + total := depositSum(selected) fee := estimateFee( - len(selected), highFeeRate, - anchors, + len(selected), highFeeRate, anchors, ) require.GreaterOrEqual( t, total, @@ -418,7 +494,9 @@ func TestSelectDeposits(t *testing.T) { feeRate: 0, commitmentType: anchors, wantCount: 1, - validate: func(t *testing.T, selected []*deposit.Deposit) { + validate: func(t *testing.T, + selected []*deposit.Deposit) { + // With zero fee, 100k covers 99k + 0 + dust. require.Equal( t, btcutil.Amount(100_000), @@ -432,11 +510,12 @@ func TestSelectDeposits(t *testing.T) { amount: 100_000, feeRate: highFeeRate, commitmentType: anchors, - validate: func(t *testing.T, selected []*deposit.Deposit) { + validate: func(t *testing.T, + selected []*deposit.Deposit) { + total := depositSum(selected) fee := estimateFee( - len(selected), highFeeRate, - anchors, + len(selected), highFeeRate, anchors, ) require.GreaterOrEqual( t, total, @@ -455,17 +534,18 @@ func TestSelectDeposits(t *testing.T) { { name: "many small deposits accumulate", deposits: makeDeposits( - 10_000, 10_000, 10_000, 10_000, 10_000, - 10_000, 10_000, 10_000, 10_000, 10_000, + 10_000, 10_000, 10_000, 10_000, 10_000, 10_000, + 10_000, 10_000, 10_000, 10_000, ), amount: 50_000, feeRate: lowFeeRate, commitmentType: anchors, - validate: func(t *testing.T, selected []*deposit.Deposit) { + validate: func(t *testing.T, + selected []*deposit.Deposit) { + total := depositSum(selected) fee := estimateFee( - len(selected), lowFeeRate, - anchors, + len(selected), lowFeeRate, anchors, ) require.GreaterOrEqual( t, total, @@ -486,11 +566,12 @@ func TestSelectDeposits(t *testing.T) { amount: 150_000, feeRate: lowFeeRate, commitmentType: anchors, - validate: func(t *testing.T, selected []*deposit.Deposit) { + validate: func(t *testing.T, + selected []*deposit.Deposit) { + total := depositSum(selected) fee := estimateFee( - len(selected), lowFeeRate, - anchors, + len(selected), lowFeeRate, anchors, ) // Core invariant: selected amount covers // requested amount + fee + dust. @@ -509,7 +590,9 @@ func TestSelectDeposits(t *testing.T) { feeRate: lowFeeRate, commitmentType: anchors, wantCount: 1, - validate: func(t *testing.T, selected []*deposit.Deposit) { + validate: func(t *testing.T, + selected []*deposit.Deposit) { + require.Equal( t, btcutil.Amount(300_000), selected[0].Value, @@ -541,6 +624,7 @@ func TestSelectDeposits(t *testing.T) { if tc.wantErr != "" { require.Error(t, err) require.ErrorContains(t, err, tc.wantErr) + return } @@ -555,8 +639,7 @@ func TestSelectDeposits(t *testing.T) { // cover amount + fee + dust. total := depositSum(selected) fee := estimateFee( - len(selected), tc.feeRate, - tc.commitmentType, + len(selected), tc.feeRate, tc.commitmentType, ) require.GreaterOrEqual( t, total, diff --git a/staticaddr/withdraw/funding_values_test.go b/staticaddr/withdraw/funding_values_test.go index 97e5fd149..b807b2512 100644 --- a/staticaddr/withdraw/funding_values_test.go +++ b/staticaddr/withdraw/funding_values_test.go @@ -151,8 +151,12 @@ func TestCalculateFundingTxValues(t *testing.T) { { name: "change >= input triggers efficiency error", deposits: []*deposit.Deposit{ - {Value: 40_000}, - {Value: 60_000}, + { + Value: 40_000, + }, + { + Value: 60_000, + }, }, localAmount: 40_000, satPerVbyte: 1, @@ -164,7 +168,9 @@ func TestCalculateFundingTxValues(t *testing.T) { { name: "channel funding below minimum", deposits: []*deposit.Deposit{ - {Value: 30_000}, + { + Value: 30_000, + }, }, localAmount: 20_000 - 1, satPerVbyte: 1, @@ -204,8 +210,12 @@ func TestCalculateWithdrawalTxValuesCommitmentTypeParity(t *testing.T) { feeRate := chainfee.SatPerKVByte(1000).FeePerKWeight() deposits := []*deposit.Deposit{ - {Value: 500_000}, - {Value: 300_000}, + { + Value: 500_000, + }, + { + Value: 300_000, + }, } p2wshAddr, err := btcutil.NewAddressWitnessScriptHash( diff --git a/staticaddr/withdraw/interface.go b/staticaddr/withdraw/interface.go index da79cf5a0..846cd931c 100644 --- a/staticaddr/withdraw/interface.go +++ b/staticaddr/withdraw/interface.go @@ -13,8 +13,10 @@ import ( // AddressManager handles fetching of address parameters. type AddressManager interface { // GetStaticAddressParameters returns the static address parameters. - GetStaticAddressParameters(ctx context.Context) (*address.Parameters, - error) + GetStaticAddressParameters(ctx context.Context) ( + *address.Parameters, + error, + ) // GetStaticAddress returns the deposit address for the given // client and server public keys. @@ -22,8 +24,10 @@ type AddressManager interface { } type DepositManager interface { - GetActiveDepositsInState(stateFilter fsm.StateType) ([]*deposit.Deposit, - error) + GetActiveDepositsInState(stateFilter fsm.StateType) ( + []*deposit.Deposit, + error, + ) AllOutpointsActiveDeposits(outpoints []wire.OutPoint, stateFilter fsm.StateType) ([]*deposit.Deposit, bool) diff --git a/staticaddr/withdraw/manager.go b/staticaddr/withdraw/manager.go index 99fddd267..a030e8441 100644 --- a/staticaddr/withdraw/manager.go +++ b/staticaddr/withdraw/manager.go @@ -206,7 +206,6 @@ func (m *Manager) Run(ctx context.Context, initChan chan struct{}) error { } select { case req.respChan <- resp: - case <-ctx.Done(): // Notify subroutines that the main loop has // been canceled. @@ -453,8 +452,7 @@ func (m *Manager) WithdrawDeposits(ctx context.Context, // Persist info about the finalized withdrawal. err = m.cfg.Store.CreateWithdrawal(ctx, deposits) if err != nil { - log.Errorf("Error persisting "+ - "withdrawal: %v", err) + log.Errorf("Error persisting withdrawal: %v", err) } err = m.handleWithdrawal( @@ -508,8 +506,8 @@ func (m *Manager) WithdrawDeposits(ctx context.Context, for _, d := range deposits { err = m.cfg.DepositManager.UpdateDeposit(ctx, d) if err != nil { - return "", "", fmt.Errorf("failed to update "+ - "deposit %w", err) + return "", "", fmt.Errorf("failed to update deposit %w", + err) } } @@ -521,8 +519,7 @@ func (m *Manager) WithdrawDeposits(ctx context.Context, // signed *wire.MsgTx representation and the unsigned psbt. func (m *Manager) CreateFinalizedWithdrawalTx(ctx context.Context, deposits []*deposit.Deposit, withdrawalAddress btcutil.Address, - feeRate chainfee.SatPerKWeight, - selectedWithdrawalAmount int64, + feeRate chainfee.SatPerKWeight, selectedWithdrawalAmount int64, commitmentType lnrpc.CommitmentType) (*wire.MsgTx, []byte, error) { // Create a musig2 session for each deposit. @@ -545,8 +542,8 @@ func (m *Manager) CreateFinalizedWithdrawalTx(ctx context.Context, params, err := m.cfg.AddressManager.GetStaticAddressParameters(ctx) if err != nil { - return nil, nil, fmt.Errorf("couldn't get confirmation "+ - "height for deposit, %w", err) + return nil, nil, fmt.Errorf("couldn't get confirmation height "+ + "for deposit, %w", err) } outpoints := toOutpoints(deposits) @@ -588,8 +585,8 @@ func (m *Manager) CreateFinalizedWithdrawalTx(ctx context.Context, } if len(sigResp.SigningInfo) != len(deposits) { - return nil, nil, errors.New("invalid number of " + - "deposit signatures") + return nil, nil, errors.New("invalid number of deposit " + + "signatures") } // Verify 1:1 matching between deposits and SigningInfo entries. @@ -632,10 +629,13 @@ func (m *Manager) publishFinalizedWithdrawalTx(ctx context.Context, // Publish the withdrawal sweep transaction. err := m.cfg.WalletKit.PublishTransaction(ctx, tx, txLabel) if err != nil { - if !strings.Contains(err.Error(), chain.ErrSameNonWitnessData.Error()) && + if !strings.Contains( + err.Error(), chain.ErrSameNonWitnessData.Error(), + ) && !strings.Contains(err.Error(), "output already spent") && - !strings.Contains(err.Error(), chain.ErrInsufficientFee.Error()) { - + !strings.Contains( + err.Error(), chain.ErrInsufficientFee.Error(), + ) { return false, err } else { if strings.Contains(err.Error(), "output already spent") { @@ -686,7 +686,9 @@ func (m *Manager) handleWithdrawal(ctx context.Context, m.cfg.ChainNotifier.RegisterConfirmationsNtfn( ctx, spentTx.SpenderTxHash, withdrawalPkscript, MinConfs, - int32(m.initiationHeight.Load()), + int32( + m.initiationHeight.Load(), + ), ) if err != nil { // TODO(#1087): Retry registration on @@ -805,8 +807,8 @@ func (m *Manager) signMusig2Tx(ctx context.Context, [][musig2.PubNonceSize]byte{nonce}, ) if err != nil { - return nil, fmt.Errorf("error registering nonces: "+ - "%w", err) + return nil, fmt.Errorf("error registering nonces: %w", + err) } if !haveAllNonces { @@ -819,8 +821,8 @@ func (m *Manager) signMusig2Tx(ctx context.Context, depositsToIdx[deposit], prevOutFetcher, ) if err != nil { - return nil, fmt.Errorf("error calculating taproot "+ - "sig hash: %w", err) + return nil, fmt.Errorf("error calculating taproot sig "+ + "hash: %w", err) } copy(sigHash[:], taprootSigHash) @@ -835,12 +837,11 @@ func (m *Manager) signMusig2Tx(ctx context.Context, // Combine the signature with the client signature. haveAllSigs, sig, err := signer.MuSig2CombineSig( - ctx, session.SessionID, - [][]byte{sigAndNonce.Sig}, + ctx, session.SessionID, [][]byte{sigAndNonce.Sig}, ) if err != nil { - return nil, fmt.Errorf("error combining signature: "+ - "%w", err) + return nil, fmt.Errorf("error combining signature: %w", + err) } if !haveAllSigs { @@ -860,8 +861,8 @@ func (m *Manager) createWithdrawalTx(ctx context.Context, outpoints []wire.OutPoint, deposits []*deposit.Deposit, prevOuts map[wire.OutPoint]*wire.TxOut, selectedWithdrawalAmount btcutil.Amount, withdrawAddr btcutil.Address, - feeRate chainfee.SatPerKWeight, - commitmentType lnrpc.CommitmentType) (*wire.MsgTx, []byte, error) { + feeRate chainfee.SatPerKWeight, commitmentType lnrpc.CommitmentType) ( + *wire.MsgTx, []byte, error) { // First Create the tx. msgTx := wire.NewMsgTx(2) @@ -875,8 +876,8 @@ func (m *Manager) createWithdrawalTx(ctx context.Context, } withdrawalAmount, changeAmount, err := CalculateWithdrawalTxValues( - deposits, selectedWithdrawalAmount, feeRate, - withdrawAddr, commitmentType, + deposits, selectedWithdrawalAmount, feeRate, withdrawAddr, + commitmentType, ) if err != nil { return nil, nil, fmt.Errorf("error calculating funding tx "+ @@ -889,8 +890,8 @@ func (m *Manager) createWithdrawalTx(ctx context.Context, // transaction, saving fees. for outpoint, txOut := range prevOuts { if changeAmount >= btcutil.Amount(txOut.Value) { - return nil, nil, fmt.Errorf("change amount %v "+ - "is higher than an input value %v of input %v", + return nil, nil, fmt.Errorf("change amount %v is "+ + "higher than an input value %v of input %v", changeAmount, btcutil.Amount(txOut.Value), outpoint) } @@ -974,7 +975,6 @@ func CalculateWithdrawalTxValues(deposits []*deposit.Deposit, if withdrawalAddress == nil && commitmentType == lnrpc.CommitmentType_UNKNOWN_COMMITMENT_TYPE { - return 0, 0, fmt.Errorf("either address or commitment type " + "must be specified") } @@ -1034,9 +1034,9 @@ func CalculateWithdrawalTxValues(deposits []*deposit.Deposit, default: // If the fees eat into our selected amount, we fail the // withdrawal. - return 0, 0, fmt.Errorf("the change doesn't " + - "cover for fees. Consider lowering the fee " + - "rate or decrease the selected amount") + return 0, 0, fmt.Errorf("the change doesn't cover " + + "for fees. Consider lowering the fee rate or " + + "decrease the selected amount") } } else { // If the user wants to withdraw the total value of deposits, we @@ -1066,8 +1066,8 @@ func CalculateWithdrawalTxValues(deposits []*deposit.Deposit, // transaction, saving fees. for _, d := range deposits { if changeAmount >= d.Value { - return 0, 0, fmt.Errorf("change amount %v is "+ - "higher than an input value %v of input %v", + return 0, 0, fmt.Errorf("change amount %v is higher "+ + "than an input value %v of input %v", changeAmount, d.Value, d.OutPoint.String()) } } @@ -1163,7 +1163,6 @@ func (m *Manager) DeliverWithdrawalRequest(ctx context.Context, // Send the new loop-in request to the manager run loop. select { case m.newWithdrawalRequestChan <- request: - case <-m.exitChan: return "", "", fmt.Errorf("withdrawal manager has been " + "canceled") diff --git a/staticaddr/withdraw/manager_test.go b/staticaddr/withdraw/manager_test.go index 4ffd4e1e8..c19cd9e8b 100644 --- a/staticaddr/withdraw/manager_test.go +++ b/staticaddr/withdraw/manager_test.go @@ -79,10 +79,14 @@ func TestSignMusig2Tx_MissingSigningInfo(t *testing.T) { // Create sessions for both deposits. sessions := map[string]*input.MuSig2SessionInfo{ deposit1Key: { - SessionID: [32]byte{1}, + SessionID: [32]byte{ + 1, + }, }, deposit2Key: { - SessionID: [32]byte{2}, + SessionID: [32]byte{ + 2, + }, }, } @@ -142,12 +146,14 @@ func TestSignMusig2Tx_MissingSigningInfo(t *testing.T) { // Expect an error. The function should validate that sigInfo has // entries for all sessions before attempting to sign. - require.ErrorContains(t, err, "unexpected number of partial "+ - "signatures from server") + require.ErrorContains( + t, err, "unexpected number of partial signatures from server", + ) } // TestSignMusig2Tx_MismatchedIndex tests that signMusig2Tx should error when -// sigInfo has all expected keys but one maps to the wrong index in depositsToIdx. +// sigInfo has all expected keys but one maps to the wrong index in +// depositsToIdx. // // This test documents expected behavior. The function should validate that // each deposit maps to a unique index in [0, len(tx.TxIn)-1] before signing, @@ -191,10 +197,14 @@ func TestSignMusig2Tx_MismatchedIndex(t *testing.T) { // Create sessions for both deposits. sessions := map[string]*input.MuSig2SessionInfo{ deposit1Key: { - SessionID: [32]byte{1}, + SessionID: [32]byte{ + 1, + }, }, deposit2Key: { - SessionID: [32]byte{2}, + SessionID: [32]byte{ + 2, + }, }, } @@ -306,10 +316,14 @@ func TestSignMusig2Tx_MissingOutpointInDepositMap(t *testing.T) { // Create sessions for both deposits. sessions := map[string]*input.MuSig2SessionInfo{ deposit1Key: { - SessionID: [32]byte{1}, + SessionID: [32]byte{ + 1, + }, }, deposit2Key: { - SessionID: [32]byte{2}, + SessionID: [32]byte{ + 2, + }, }, } @@ -392,9 +406,12 @@ func TestCalculateWithdrawalTxValues(t *testing.T) { feeRate := chainfee.SatPerKWeight(1000) // Helper to create deposits. - createDeposit := func(value btcutil.Amount, idx uint32) *deposit.Deposit { + createDeposit := func(value btcutil.Amount, + idx uint32) *deposit.Deposit { + hash := chainhash.Hash{} hash[0] = byte(idx) + return &deposit.Deposit{ OutPoint: wire.OutPoint{ Hash: hash, @@ -431,8 +448,8 @@ func TestCalculateWithdrawalTxValues(t *testing.T) { createDeposit(100000, 0), }, // Set localAmount such that change after feeWithChange - // would be dust, but change after feeWithoutChange >= 0. - // This triggers case: change-feeWithoutChange >= 0 + // would be dust, but change after feeWithoutChange >= + // 0. This triggers case: change-feeWithoutChange >= 0 localAmount: 99300, // Leaves ~700 sats which is dust feeRate: feeRate, withdrawAddr: taprootAddr, @@ -477,7 +494,9 @@ func TestCalculateWithdrawalTxValues(t *testing.T) { { name: "min channel size guard - exactly minimum", deposits: []*deposit.Deposit{ - createDeposit(funding.MinChanFundingSize+1000, 0), + createDeposit( + funding.MinChanFundingSize+1000, 0, + ), }, localAmount: 0, feeRate: feeRate, @@ -553,6 +572,7 @@ func TestCalculateWithdrawalTxValues(t *testing.T) { if tc.expectedErr != "" { require.Error(t, err) require.ErrorContains(t, err, tc.expectedErr) + return } @@ -567,18 +587,23 @@ func TestCalculateWithdrawalTxValues(t *testing.T) { // If this is a channel open, verify min channel size. if tc.commitmentType != lnrpc.CommitmentType_UNKNOWN_COMMITMENT_TYPE { require.GreaterOrEqual( - t, withdrawAmt, funding.MinChanFundingSize, + t, withdrawAmt, + funding.MinChanFundingSize, ) } // If expecting dust to be given to miners, verify // changeAmt is 0. if tc.expectDustFee { - require.Equal(t, btcutil.Amount(0), changeAmt, - "change should be 0 when dust is given to miners") + require.Equal( + t, btcutil.Amount(0), changeAmt, "ch"+ + "ange should be 0 when "+ + "dust is given to miners", + ) } - // Verify total accounting: inputs = withdrawal + change + fees. + // Verify total accounting: inputs = withdrawal + change + // + fees. totalInputs := btcutil.Amount(0) for _, d := range tc.deposits { totalInputs += d.Value @@ -596,12 +621,20 @@ func TestCalculateWithdrawalTxValues(t *testing.T) { // When dust is given to miners, the "fee" includes both // the transaction fee and the dust amount. if tc.expectDustFee { - // Total should equal withdrawal + implicit fee (including dust) + // Total should equal withdrawal + implicit fee + // (including dust) implicitFee := totalInputs - withdrawAmt - changeAmt - require.Greater(t, implicitFee, fee, - "implicit fee should be greater than tx fee when dust is given to miners") + require.Greater( + t, implicitFee, fee, "implicit fee "+ + "should be greater than tx "+ + "fee when dust is given to "+ + "miners", + ) } else { - require.Equal(t, totalInputs, withdrawAmt+changeAmt+fee) + require.Equal( + t, totalInputs, + withdrawAmt+changeAmt+fee, + ) } }) } diff --git a/staticaddr/withdraw/sql_store.go b/staticaddr/withdraw/sql_store.go index df6f27f69..0b63e5724 100644 --- a/staticaddr/withdraw/sql_store.go +++ b/staticaddr/withdraw/sql_store.go @@ -25,8 +25,8 @@ type Querier interface { // GetWithdrawalIDByDepositID retrieves the withdrawal ID associated // with a given deposit ID. - GetWithdrawalIDByDepositID(ctx context.Context, depositID []byte) ( - []byte, error) + GetWithdrawalIDByDepositID(ctx context.Context, + depositID []byte) ([]byte, error) // CreateWithdrawalDeposit links withdrawal to deposits. CreateWithdrawalDeposit(ctx context.Context, @@ -34,8 +34,8 @@ type Querier interface { // GetWithdrawalDeposits retrieves the deposit IDs associated with a // withdrawal. - GetWithdrawalDeposits(ctx context.Context, withdrawalID []byte) ( - [][]byte, error) + GetWithdrawalDeposits(ctx context.Context, + withdrawalID []byte) ([][]byte, error) // GetAllWithdrawals retrieves all withdrawals from the database. GetAllWithdrawals(ctx context.Context) ([]sqlc.Withdrawal, error) @@ -88,6 +88,7 @@ func (s *SqlStore) CreateWithdrawal(ctx context.Context, TotalDepositAmount: int64(totalAmount), InitiationTime: s.clock.Now().UTC(), } + return s.baseDB.ExecTx(ctx, &loopdb.SqliteTxOptions{}, func(q Querier) error { err := q.CreateWithdrawal(ctx, createArgs) @@ -182,8 +183,9 @@ func (s *SqlStore) GetAllWithdrawals(ctx context.Context) ([]Withdrawal, result := make([]Withdrawal, 0, len(withdrawals)) for _, w := range withdrawals { - depositIDs, err := s.baseDB.GetWithdrawalDeposits(ctx, - w.WithdrawalID) + depositIDs, err := s.baseDB.GetWithdrawalDeposits( + ctx, w.WithdrawalID, + ) if err != nil { return nil, err @@ -210,7 +212,9 @@ func (s *SqlStore) GetAllWithdrawals(ctx context.Context) ([]Withdrawal, TxID: *txID, Deposits: deposits, TotalDepositAmount: btcutil.Amount(w.TotalDepositAmount), - WithdrawnAmount: btcutil.Amount(w.WithdrawnAmount.Int64), + WithdrawnAmount: btcutil.Amount( + w.WithdrawnAmount.Int64, + ), ChangeAmount: btcutil.Amount(w.ChangeAmount.Int64), InitiationTime: w.InitiationTime, ConfirmationHeight: w.ConfirmationHeight.Int64, diff --git a/staticaddr/withdraw/sql_store_test.go b/staticaddr/withdraw/sql_store_test.go index 5897f20ef..96febb32f 100644 --- a/staticaddr/withdraw/sql_store_test.go +++ b/staticaddr/withdraw/sql_store_test.go @@ -18,7 +18,9 @@ func TestSqlStore(t *testing.T) { defer testDb.Close() depositStore := deposit.NewSqlStore(testDb.BaseDB) - store := NewSqlStore(loopdb.NewTypedStore[Querier](testDb), depositStore) + store := NewSqlStore( + loopdb.NewTypedStore[Querier](testDb), depositStore, + ) newID := func() deposit.ID { did, err := deposit.GetRandomDepositID() diff --git a/staticaddr/withdraw/withdrawal.go b/staticaddr/withdraw/withdrawal.go index 9c32ec736..8b9a07b98 100644 --- a/staticaddr/withdraw/withdrawal.go +++ b/staticaddr/withdraw/withdrawal.go @@ -64,5 +64,6 @@ type Withdrawal struct { func GetRandomWithdrawalID() (ID, error) { var id ID _, err := rand.Read(id[:]) + return id, err } diff --git a/swap.go b/swap.go index 43a445eb1..041663cc6 100644 --- a/swap.go +++ b/swap.go @@ -75,8 +75,7 @@ func (s *swapKit) swapInfo() *SwapInfo { } type genericSwap interface { - execute(mainCtx context.Context, cfg *executeConfig, - height int32) error + execute(mainCtx context.Context, cfg *executeConfig, height int32) error } type swapConfig struct { diff --git a/swap/htlc.go b/swap/htlc.go index 2e87d8aee..a1502dbda 100644 --- a/swap/htlc.go +++ b/swap/htlc.go @@ -223,8 +223,8 @@ func NewHtlcV3(muSig2Version input.MuSig2Version, cltvExpiry int32, // segwitV0LockingConditions provides the address, pkScript and sigScript (if // required) for the segwit v0 script and output type provided. func segwitV0LockingConditions(outputType HtlcOutputType, - chainParams *chaincfg.Params, script []byte) (btcutil.Address, - []byte, []byte, error) { + chainParams *chaincfg.Params, script []byte) (btcutil.Address, []byte, + []byte, error) { switch outputType { case HtlcP2WSH: @@ -234,8 +234,7 @@ func segwitV0LockingConditions(outputType HtlcOutputType, } address, err := btcutil.NewAddressWitnessScriptHash( - pkScript[2:], - chainParams, + pkScript[2:], chainParams, ) if err != nil { return nil, nil, nil, err @@ -338,17 +337,17 @@ type HtlcScriptV2 struct { // // OP_CHECKSIG OP_NOTIF // -// OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIGVERIFY -// OP_CHECKLOCKTIMEVERIFY +// OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIGVERIFY +// OP_CHECKLOCKTIMEVERIFY // // OP_ELSE // -// OP_SIZE <20> OP_EQUALVERIFY OP_HASH160 OP_EQUALVERIFY 1 -// OP_CHECKSEQUENCEVERIFY +// OP_SIZE <20> OP_EQUALVERIFY OP_HASH160 OP_EQUALVERIFY 1 +// OP_CHECKSEQUENCEVERIFY // // OP_ENDIF . -func newHTLCScriptV2(cltvExpiry int32, senderHtlcKey, - receiverHtlcKey [33]byte, swapHash lntypes.Hash) (*HtlcScriptV2, error) { +func newHTLCScriptV2(cltvExpiry int32, senderHtlcKey, receiverHtlcKey [33]byte, + swapHash lntypes.Hash) (*HtlcScriptV2, error) { builder := txscript.NewScriptBuilder() builder.AddData(receiverHtlcKey[:]) @@ -415,8 +414,8 @@ func (h *HtlcScriptV2) IsSuccessWitness(witness wire.TxWitness) bool { // GenTimeoutWitness returns the timeout script to spend this htlc after // timeout. -func (h *HtlcScriptV2) GenTimeoutWitness( - senderSig []byte) (wire.TxWitness, error) { +func (h *HtlcScriptV2) GenTimeoutWitness(senderSig []byte) (wire.TxWitness, + error) { witnessStack := make(wire.TxWitness, 4) witnessStack[0] = append(senderSig, byte(txscript.SigHashAll)) @@ -445,6 +444,7 @@ func (h *HtlcScriptV2) SuccessScript() []byte { // MaxSuccessWitnessSize returns maximum success witness size. func (h *HtlcScriptV2) MaxSuccessWitnessSize() lntypes.WeightUnit { + // Calculate maximum success witness size // // - number_of_witness_elements: 1 byte @@ -459,6 +459,7 @@ func (h *HtlcScriptV2) MaxSuccessWitnessSize() lntypes.WeightUnit { // MaxTimeoutWitnessSize returns maximum timeout witness size. func (h *HtlcScriptV2) MaxTimeoutWitnessSize() lntypes.WeightUnit { + // Calculate maximum timeout witness size // // - number_of_witness_elements: 1 byte @@ -514,8 +515,8 @@ type HtlcScriptV3 struct { // parsePubKey will parse a serialized public key into a btcec.PublicKey // depending on the passed MuSig2 version. -func parsePubKey(muSig2Version input.MuSig2Version, key [33]byte) ( - *btcec.PublicKey, error) { +func parsePubKey(muSig2Version input.MuSig2Version, + key [33]byte) (*btcec.PublicKey, error) { // Make sure that we have the correct public keys depending on the // MuSig2 version. @@ -535,7 +536,8 @@ func parsePubKey(muSig2Version input.MuSig2Version, key [33]byte) ( // newHTLCScriptV3 constructs a HtlcScript with the HTLC V3 taproot script. func newHTLCScriptV3(muSig2Version input.MuSig2Version, cltvExpiry int32, senderInternalKey, receiverInternalKey, senderHtlcKey, - receiverHtlcKey [33]byte, swapHash lntypes.Hash) (*HtlcScriptV3, error) { + receiverHtlcKey [33]byte, + swapHash lntypes.Hash) (*HtlcScriptV3, error) { senderPubKey, err := parsePubKey(muSig2Version, senderHtlcKey) if err != nil { @@ -619,14 +621,15 @@ func newHTLCScriptV3(muSig2Version input.MuSig2Version, cltvExpiry int32, // Largest possible bytesize of the script is 32 + 1 + 2 + 1 = 36. // // OP_CHECKSIGVERIFY OP_CHECKLOCKTIMEVERIFY -func GenTimeoutPathScript(senderHtlcKey *btcec.PublicKey, cltvExpiry int64) ( - []byte, error) { +func GenTimeoutPathScript(senderHtlcKey *btcec.PublicKey, + cltvExpiry int64) ([]byte, error) { builder := txscript.NewScriptBuilder() builder.AddData(schnorr.SerializePubKey(senderHtlcKey)) builder.AddOp(txscript.OP_CHECKSIGVERIFY) builder.AddInt64(cltvExpiry) builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY) + return builder.Script() } @@ -684,8 +687,8 @@ func (h *HtlcScriptV3) genControlBlock(leafScript []byte) ([]byte, error) { // genSuccessWitness returns the success script to spend this htlc with // the preimage. -func (h *HtlcScriptV3) genSuccessWitness( - receiverSig []byte, preimage lntypes.Preimage) (wire.TxWitness, error) { +func (h *HtlcScriptV3) genSuccessWitness(receiverSig []byte, + preimage lntypes.Preimage) (wire.TxWitness, error) { controlBlockBytes, err := h.genControlBlock(h.timeoutScript) if err != nil { @@ -702,8 +705,8 @@ func (h *HtlcScriptV3) genSuccessWitness( // GenTimeoutWitness returns the timeout script to spend this htlc after // timeout. -func (h *HtlcScriptV3) GenTimeoutWitness( - senderSig []byte) (wire.TxWitness, error) { +func (h *HtlcScriptV3) GenTimeoutWitness(senderSig []byte) (wire.TxWitness, + error) { controlBlockBytes, err := h.genControlBlock(h.successScript) if err != nil { @@ -720,6 +723,7 @@ func (h *HtlcScriptV3) GenTimeoutWitness( // IsSuccessWitness checks whether the given stack is valid for // redeeming the htlc. func (h *HtlcScriptV3) IsSuccessWitness(witness wire.TxWitness) bool { + // The witness has four elements if this is a script spend or one // element if this is a keyspend. return len(witness) == 4 || len(witness) == 1 @@ -744,6 +748,7 @@ func (h *HtlcScriptV3) SuccessScript() []byte { // MaxSuccessWitnessSize returns the maximum witness size for the // success case witness. func (h *HtlcScriptV3) MaxSuccessWitnessSize() lntypes.WeightUnit { + // Calculate maximum success witness size // // - number_of_witness_elements: 1 byte @@ -764,6 +769,7 @@ func (h *HtlcScriptV3) MaxSuccessWitnessSize() lntypes.WeightUnit { // MaxTimeoutWitnessSize returns the maximum witness size for the // timeout case witness. func (h *HtlcScriptV3) MaxTimeoutWitnessSize() lntypes.WeightUnit { + // Calculate maximum timeout witness size // // - number_of_witness_elements: 1 byte diff --git a/swap/htlc_test.go b/swap/htlc_test.go index 87407f615..39c74158a 100644 --- a/swap/htlc_test.go +++ b/swap/htlc_test.go @@ -60,19 +60,21 @@ func assertEngineExecution(t *testing.T, valid bool, done, err = vm.Step() if err != nil && valid { fmt.Println(debugBuf.String()) - t.Fatalf("spend test case failed, spend "+ - "should be valid: %v", err) + t.Fatalf("spend test case failed, spend should be "+ + "valid: %v", err) } else if err == nil && !valid && done { fmt.Println(debugBuf.String()) - t.Fatalf("spend test case succeed, spend "+ - "should be invalid: %v", err) + t.Fatalf("spend test case succeed, spend should be "+ + "invalid: %v", err) } debugBuf.WriteString( fmt.Sprintf("Stack: %v", vm.GetStack()), ) debugBuf.WriteString( - fmt.Sprintf("AltStack: %v", vm.GetAltStack()), + fmt.Sprintf( + "AltStack: %v", vm.GetAltStack(), + ), ) } @@ -85,9 +87,7 @@ func assertEngineExecution(t *testing.T, valid bool, } fmt.Println(debugBuf.String()) - t.Fatalf( - "%v spend test case execution ended with: %v", validity, vmErr, - ) + t.Fatalf("%v spend test case execution ended with: %v", validity, vmErr) } // TestHtlcV2 tests the HTLC V2 script success and timeout spend cases. @@ -146,10 +146,14 @@ func TestHtlcV2(t *testing.T) { // Create signers for sender and receiver. senderSigner := &input.MockSigner{ - Privkeys: []*btcec.PrivateKey{senderPrivKey}, + Privkeys: []*btcec.PrivateKey{ + senderPrivKey, + }, } receiverSigner := &input.MockSigner{ - Privkeys: []*btcec.PrivateKey{receiverPrivKey}, + Privkeys: []*btcec.PrivateKey{ + receiverPrivKey, + }, } prevOutFetcher := txscript.NewCannedPrevOutputFetcher( htlc.PkScript, 800_000, @@ -282,7 +286,11 @@ func TestHtlcV2(t *testing.T) { // key. "timeout case cannot spend with wrong key", func(t *testing.T) wire.TxWitness { - bogusKey := [33]byte{0xb, 0xa, 0xd} + bogusKey := [33]byte{ + 0xb, + 0xa, + 0xd, + } // Create the htlc with the bogus key. htlc, err = NewHtlcV2( @@ -321,8 +329,8 @@ func TestHtlcV2(t *testing.T) { newEngine := func() (*txscript.Engine, error) { return txscript.NewEngine( htlc.PkScript, sweepTx, 0, - txscript.StandardVerifyFlags, nil, - nil, int64(htlcValue), prevOutFetcher, + txscript.StandardVerifyFlags, nil, nil, + int64(htlcValue), prevOutFetcher, ) } diff --git a/swap/log.go b/swap/log.go index 5345e68b9..afb10a936 100644 --- a/swap/log.go +++ b/swap/log.go @@ -19,10 +19,8 @@ type PrefixLog struct { // Infof formats message according to format specifier and writes to // log with LevelInfo. func (s *PrefixLog) Infof(format string, params ...any) { - s.Logger.Infof( - fmt.Sprintf("%v %s", ShortHash(&s.Hash), format), - params..., - ) + s.Logger.Infof(fmt.Sprintf("%v %s", ShortHash(&s.Hash), + format), params...) } // Warnf formats message according to format specifier and writes to log with diff --git a/swap/tx.go b/swap/tx.go index 289cb1b62..d6dbde382 100644 --- a/swap/tx.go +++ b/swap/tx.go @@ -11,8 +11,8 @@ import ( // GetScriptOutput locates the given script in the outputs of a transaction and // returns its outpoint and value. -func GetScriptOutput(htlcTx *wire.MsgTx, scriptHash []byte) ( - *wire.OutPoint, btcutil.Amount, error) { +func GetScriptOutput(htlcTx *wire.MsgTx, scriptHash []byte) (*wire.OutPoint, + btcutil.Amount, error) { for idx, output := range htlcTx.TxOut { if bytes.Equal(output.PkScript, scriptHash) { @@ -27,8 +27,8 @@ func GetScriptOutput(htlcTx *wire.MsgTx, scriptHash []byte) ( } // GetTxInputByOutpoint returns a tx input based on a given input outpoint. -func GetTxInputByOutpoint(tx *wire.MsgTx, input *wire.OutPoint) ( - *wire.TxIn, error) { +func GetTxInputByOutpoint(tx *wire.MsgTx, + input *wire.OutPoint) (*wire.TxIn, error) { for _, in := range tx.TxIn { if in.PreviousOutPoint == *input { diff --git a/swap/type.go b/swap/type.go index 20942c747..58e151224 100644 --- a/swap/type.go +++ b/swap/type.go @@ -21,8 +21,10 @@ func (t Type) String() string { switch t { case TypeIn: return "In" + case TypeOut: return "Out" + default: return "Unknown" } diff --git a/swap_server_client.go b/swap_server_client.go index fcec06be4..57c938b1b 100644 --- a/swap_server_client.go +++ b/swap_server_client.go @@ -70,50 +70,48 @@ func (r RoutingPluginType) String() string { } type swapServerClient interface { - GetLoopOutTerms(ctx context.Context, initiator string) ( - *LoopOutTerms, error) + GetLoopOutTerms(ctx context.Context, + initiator string) (*LoopOutTerms, error) GetLoopOutQuote(ctx context.Context, amt btcutil.Amount, expiry int32, - swapPublicationDeadline time.Time, initiator string) ( - *LoopOutQuote, error) + swapPublicationDeadline time.Time, + initiator string) (*LoopOutQuote, error) - GetLoopInTerms(ctx context.Context, initiator string) ( - *LoopInTerms, error) + GetLoopInTerms(ctx context.Context, + initiator string) (*LoopInTerms, error) GetLoopInQuote(ctx context.Context, amt btcutil.Amount, pubKey route.Vertex, lastHop *route.Vertex, - routeHints [][]zpay32.HopHint, - initiator string, numDeposits uint32, - fast bool) (*LoopInQuote, error) + routeHints [][]zpay32.HopHint, initiator string, + numDeposits uint32, fast bool) (*LoopInQuote, error) Probe(ctx context.Context, amt btcutil.Amount, target route.Vertex, lastHop *route.Vertex, routeHints [][]zpay32.HopHint) error - NewLoopOutSwap(ctx context.Context, - swapHash lntypes.Hash, amount btcutil.Amount, expiry int32, - receiverKey [33]byte, swapPublicationDeadline time.Time, + NewLoopOutSwap(ctx context.Context, swapHash lntypes.Hash, + amount btcutil.Amount, expiry int32, receiverKey [33]byte, + swapPublicationDeadline time.Time, initiator string) (*newLoopOutResponse, error) PushLoopOutPreimage(ctx context.Context, preimage lntypes.Preimage) error - NewLoopInSwap(ctx context.Context, - swapHash lntypes.Hash, amount btcutil.Amount, senderScriptKey, + NewLoopInSwap(ctx context.Context, swapHash lntypes.Hash, + amount btcutil.Amount, senderScriptKey, senderInternalKey [33]byte, swapInvoice, probeInvoice string, - lastHop *route.Vertex, initiator string) ( - *newLoopInResponse, error) + lastHop *route.Vertex, + initiator string) (*newLoopInResponse, error) // SubscribeLoopOutUpdates subscribes to loop out server state. - SubscribeLoopOutUpdates(ctx context.Context, - hash lntypes.Hash) (<-chan *ServerUpdate, <-chan error, error) + SubscribeLoopOutUpdates(ctx context.Context, hash lntypes.Hash) ( + <-chan *ServerUpdate, <-chan error, error) // SubscribeLoopInUpdates subscribes to loop in server state. - SubscribeLoopInUpdates(ctx context.Context, - hash lntypes.Hash) (<-chan *ServerUpdate, <-chan error, error) + SubscribeLoopInUpdates(ctx context.Context, hash lntypes.Hash) ( + <-chan *ServerUpdate, <-chan error, error) // CancelLoopOutSwap cancels a loop out swap. - CancelLoopOutSwap(ctx context.Context, - details *outCancelDetails) error + CancelLoopOutSwap(ctx context.Context, details *outCancelDetails) error // RecommendRoutingPlugin asks the server for routing plugin // recommendation for off-chain payment(s) of a swap. @@ -121,23 +119,21 @@ type swapServerClient interface { paymentAddr [32]byte) (RoutingPluginType, error) // ReportRoutingResult reports a routing result corresponding to a swap. - ReportRoutingResult(ctx context.Context, - swapHash lntypes.Hash, paymentAddr [32]byte, - plugin RoutingPluginType, success bool, attempts int32, - totalTime int64) error + ReportRoutingResult(ctx context.Context, swapHash lntypes.Hash, + paymentAddr [32]byte, plugin RoutingPluginType, success bool, + attempts int32, totalTime int64) error // MuSig2SignSweep calls the server to cooperatively sign the MuSig2 // htlc spend. Returns the server's nonce and partial signature. MuSig2SignSweep(ctx context.Context, protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash, - paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte) ( - []byte, []byte, error) + paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte) ([]byte, + []byte, error) // PushKey sends the client's HTLC internal key associated with the // swap to the server. - PushKey(ctx context.Context, - protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash, - clientInternalPrivateKey [32]byte) error + PushKey(ctx context.Context, protocolVersion loopdb.ProtocolVersion, + swapHash lntypes.Hash, clientInternalPrivateKey [32]byte) error // FetchL402 is a helper function that tries to fetch an l402 token // from the server. @@ -163,8 +159,8 @@ func (s *grpcSwapServerClient) stop() { var _ swapServerClient = (*grpcSwapServerClient)(nil) -func newSwapServerClient(cfg *ClientConfig, l402Store l402.Store) ( - *grpcSwapServerClient, error) { +func newSwapServerClient(cfg *ClientConfig, + l402Store l402.Store) (*grpcSwapServerClient, error) { // Create the server connection with the interceptor that will handle // the L402 protocol for us. @@ -270,8 +266,8 @@ func (s *grpcSwapServerClient) GetLoopInTerms(ctx context.Context, func (s *grpcSwapServerClient) GetLoopInQuote(ctx context.Context, amt btcutil.Amount, pubKey route.Vertex, lastHop *route.Vertex, - routeHints [][]zpay32.HopHint, initiator string, - numDeposits uint32, fast bool) (*LoopInQuote, error) { + routeHints [][]zpay32.HopHint, initiator string, numDeposits uint32, + fast bool) (*LoopInQuote, error) { err := s.Probe(ctx, amt, pubKey, lastHop, routeHints) if err != nil && status.Code(err) != codes.Unavailable { @@ -380,6 +376,7 @@ func (s *grpcSwapServerClient) Probe(ctx context.Context, amt btcutil.Amount, } _, err = s.server.Probe(rpcCtx, req) + return err } @@ -525,6 +522,7 @@ func (s *grpcSwapServerClient) SubscribeLoopInUpdates(ctx context.Context, } updateChan, errChan := s.makeServerUpdate(ctx, receive) + return updateChan, errChan, nil } @@ -556,6 +554,7 @@ func (s *grpcSwapServerClient) SubscribeLoopOutUpdates(ctx context.Context, } updateChan, errChan := s.makeServerUpdate(ctx, receive) + return updateChan, errChan, nil } @@ -584,13 +583,14 @@ func (s *grpcSwapServerClient) makeServerUpdate(ctx context.Context, // If we get a nil error, we proceed with to delivering // the update we have just received. case nil: - // If we get an EOF error, the server is finished // sending us updates, so we return with a non-nil // a subscription complete error to inform the caller // that they will no longer receive updates. + case io.EOF: errChan <- errServerSubscriptionComplete + return // If we receive a non-nil error, we exit. @@ -612,11 +612,12 @@ func (s *grpcSwapServerClient) makeServerUpdate(ctx context.Context, select { // Try to send our update to the update channel. case updateChan <- response: - // If the client cancels their context, we exit with // no error. + case <-ctx.Done(): errChan <- nil + return } } @@ -683,13 +684,15 @@ func (s *grpcSwapServerClient) CancelLoopOutSwap(ctx context.Context, } _, err = s.server.CancelLoopOutSwap(ctx, req) + return err } // RecommendRoutingPlugin asks the server for routing plugin recommendation for // off-chain payment(s) of a swap. func (s *grpcSwapServerClient) RecommendRoutingPlugin(ctx context.Context, - swapHash lntypes.Hash, paymentAddr [32]byte) (RoutingPluginType, error) { + swapHash lntypes.Hash, paymentAddr [32]byte) (RoutingPluginType, + error) { req := &swapserverrpc.RecommendRoutingPluginReq{ ProtocolVersion: loopdb.CurrentRPCProtocolVersion(), @@ -749,6 +752,7 @@ func (s *grpcSwapServerClient) ReportRoutingResult(ctx context.Context, defer rpcCancel() _, err := s.server.ReportRoutingResult(rpcCtx, req) + return err } @@ -756,8 +760,8 @@ func (s *grpcSwapServerClient) ReportRoutingResult(ctx context.Context, // spend. Returns the server's nonce and partial signature. func (s *grpcSwapServerClient) MuSig2SignSweep(ctx context.Context, protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash, - paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte) ( - []byte, []byte, error) { + paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte) ([]byte, []byte, + error) { req := &swapserverrpc.MuSig2SignSweepReq{ ProtocolVersion: swapserverrpc.ProtocolVersion(protocolVersion), @@ -786,8 +790,7 @@ func (s *grpcSwapServerClient) MuSig2SignSweep(ctx context.Context, func (s *grpcSwapServerClient) MultiMuSig2SignSweep(ctx context.Context, protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash, paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte, - prevoutMap map[wire.OutPoint]*wire.TxOut) ( - []byte, []byte, error) { + prevoutMap map[wire.OutPoint]*wire.TxOut) ([]byte, []byte, error) { prevOutInfo := make([]*swapserverrpc.PrevoutInfo, 0, len(prevoutMap)) for prevOut, txOut := range prevoutMap { @@ -837,6 +840,7 @@ func (s *grpcSwapServerClient) PushKey(ctx context.Context, defer rpcCancel() _, err := s.server.PushKey(rpcCtx, req) + return err } @@ -849,6 +853,7 @@ func (s *grpcSwapServerClient) FetchL402(ctx context.Context) error { defer rpcCancel() _, err := s.server.FetchL402(rpcCtx, req) + return err } @@ -895,8 +900,8 @@ func rpcRouteCancel(details *outCancelDetails) ( // proxyAddr indicates that a SOCKS proxy found at the address should be used to // establish the connection. func getSwapServerConn(address, proxyAddress string, skipCertCheck bool, - tlsPath string, interceptor *l402.ClientInterceptor) (*grpc.ClientConn, - error) { + tlsPath string, + interceptor *l402.ClientInterceptor) (*grpc.ClientConn, error) { // Create a dial options array. opts := []grpc.DialOption{ @@ -935,7 +940,9 @@ func getSwapServerConn(address, proxyAddress string, skipCertCheck bool, if proxyAddress != "" { log.Infof("Proxying connection to %v over Tor SOCKS proxy %v", address, proxyAddress) - torDialer := func(_ context.Context, addr string) (net.Conn, error) { + torDialer := func(_ context.Context, addr string) (net.Conn, + error) { + return tor.Dial( addr, proxyAddress, false, false, tor.DefaultConnTimeout, diff --git a/sweep/sweeper.go b/sweep/sweeper.go index edcce0256..220d1c105 100644 --- a/sweep/sweeper.go +++ b/sweep/sweeper.go @@ -26,11 +26,10 @@ type Sweeper struct { // CreateUnsignedTaprootKeySpendSweepTx creates a taproot htlc sweep tx using // keyspend. Returns the raw unsigned txn, the psbt serialized txn, the sighash // or an error. -func (s *Sweeper) CreateUnsignedTaprootKeySpendSweepTx( - ctx context.Context, lockTime uint32, - htlc *swap.Htlc, htlcOutpoint wire.OutPoint, - amount, fee btcutil.Amount, destAddr btcutil.Address) ( - *wire.MsgTx, []byte, []byte, error) { +func (s *Sweeper) CreateUnsignedTaprootKeySpendSweepTx(ctx context.Context, + lockTime uint32, htlc *swap.Htlc, htlcOutpoint wire.OutPoint, + amount, fee btcutil.Amount, destAddr btcutil.Address) (*wire.MsgTx, + []byte, []byte, error) { if htlc.Version != swap.HtlcV3 { return nil, nil, nil, fmt.Errorf("invalid htlc version") @@ -90,13 +89,12 @@ func (s *Sweeper) CreateUnsignedTaprootKeySpendSweepTx( } // CreateSweepTx creates an htlc sweep tx. -func (s *Sweeper) CreateSweepTx( - globalCtx context.Context, height int32, sequence uint32, - htlc *swap.Htlc, htlcOutpoint wire.OutPoint, +func (s *Sweeper) CreateSweepTx(globalCtx context.Context, height int32, + sequence uint32, htlc *swap.Htlc, htlcOutpoint wire.OutPoint, keyBytes [33]byte, witnessScript []byte, witnessFunc func(sig []byte) (wire.TxWitness, error), - amount, fee btcutil.Amount, - destAddr btcutil.Address) (*wire.MsgTx, error) { + amount, fee btcutil.Amount, destAddr btcutil.Address) (*wire.MsgTx, + error) { // Compose tx. sweepTx := wire.NewMsgTx(2) diff --git a/sweepbatcher/greedy_batch_selection.go b/sweepbatcher/greedy_batch_selection.go index 6ad5288c8..f3259d134 100644 --- a/sweepbatcher/greedy_batch_selection.go +++ b/sweepbatcher/greedy_batch_selection.go @@ -41,8 +41,8 @@ func (b *Batcher) greedyAddSweeps(ctx context.Context, sweeps []*sweep) error { sweeps, ) if err != nil { - return fmt.Errorf("failed to estimate tx weight for "+ - "sweep %x: %w", swap[:6], err) + return fmt.Errorf("failed to estimate tx weight for sweep "+ + "%x: %w", swap[:6], err) } // Collect weight and fee rate info about existing batches. @@ -78,6 +78,7 @@ func (b *Batcher) greedyAddSweeps(ctx context.Context, sweeps []*sweep) error { // If the best option is to start a new batch, do it. if batchId == newBatchSignal { fast := false + return b.spinUpNewBatch(ctx, sweeps, fast) } @@ -93,16 +94,18 @@ func (b *Batcher) greedyAddSweeps(ctx context.Context, sweeps []*sweep) error { accepted, err := bestBatch.addSweeps(ctx, sweeps) if err != nil { return fmt.Errorf("batch selection algorithm returned "+ - "batch id %d for sweep %x, but adding failed: "+ - "%w", batchId, swap[:6], err) + "batch id %d for sweep %x, but adding "+ + "failed: %w", batchId, swap[:6], err) } if accepted { return nil } - debugf("Batch selection algorithm returned batch id %d "+ - "for sweep %x, but acceptance failed.", batchId, - swap[:6]) + debugf( + "Batch selection algorithm returned batch id %d for "+ + "sweep %x, but acceptance failed.", batchId, + swap[:6], + ) } return fmt.Errorf("no batch accepted sweep group %x", swap[:6]) @@ -110,8 +113,8 @@ func (b *Batcher) greedyAddSweeps(ctx context.Context, sweeps []*sweep) error { // estimateSweepFeeIncrement returns fee details for adding the sweeps to // a batch and for creating new batch with these sweeps only. -func estimateSweepFeeIncrement( - sweeps []*sweep) (feeDetails, feeDetails, error) { +func estimateSweepFeeIncrement(sweeps []*sweep) (feeDetails, feeDetails, + error) { if len(sweeps) == 0 { return feeDetails{}, feeDetails{}, fmt.Errorf("estimating an " + @@ -192,8 +195,8 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) { var destAddr btcutil.Address if theSweep.isExternalAddr { if theSweep.destAddr == nil { - return feeDetails{}, errors.New("isExternalAddr=true," + - " but destAddr is nil") + return feeDetails{}, errors.New( + "isExternalAddr=true, but destAddr is nil") } destAddr = theSweep.destAddr } else { @@ -234,8 +237,7 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) { err = sweep.htlcSuccessEstimator(&weight) if err != nil { return feeDetails{}, fmt.Errorf( - "htlcSuccessEstimator failed: %w", err, - ) + "htlcSuccessEstimator failed: %w", err) } } else { weight.AddTaprootKeySpendInput( @@ -299,8 +301,8 @@ func (e1 feeDetails) combine(e2 feeDetails) feeDetails { // Each fee details has also IsExternalAddr flag. There is a rule that sweeps // having flag IsExternalAddr must go in individual batches. Cooperative // spending may only be available for some sweeps supporting it, not for all. -func selectBatches(batches []feeDetails, - added, newBatch feeDetails) ([]int32, error) { +func selectBatches(batches []feeDetails, added, + newBatch feeDetails) ([]int32, error) { // If the sweep has IsExternalAddr flag, the sweep can't be added to // a batch, so create new batch for it. diff --git a/sweepbatcher/greedy_batch_selection_test.go b/sweepbatcher/greedy_batch_selection_test.go index 97c087f18..21bcdfd4e 100644 --- a/sweepbatcher/greedy_batch_selection_test.go +++ b/sweepbatcher/greedy_batch_selection_test.go @@ -35,11 +35,15 @@ const ( ) * 4 coopTwoSweepBatchWeight = coopNewBatchWeight + coopInputWeight - coopSingleSweepChangeBatchWeight = coopInputWeight + batchOutputWeight + changeOutputWeight - coopDoubleSweepChangeBatchWeight = 2*coopInputWeight + batchOutputWeight + changeOutputWeight - nonCoopTwoSweepBatchWeight = coopTwoSweepBatchWeight + 2*nonCoopPenalty - v2v3BatchWeight = nonCoopTwoSweepBatchWeight - 25 - mixedTwoSweepBatchWeight = coopTwoSweepBatchWeight + nonCoopPenalty + coopSingleSweepChangeBatchWeight = coopInputWeight + batchOutputWeight + + changeOutputWeight + coopDoubleSweepChangeBatchWeight = 2*coopInputWeight + + batchOutputWeight + changeOutputWeight + nonCoopTwoSweepBatchWeight = coopTwoSweepBatchWeight + + 2*nonCoopPenalty + v2v3BatchWeight = nonCoopTwoSweepBatchWeight - 25 + mixedTwoSweepBatchWeight = coopTwoSweepBatchWeight + + nonCoopPenalty ) // testHtlcV2SuccessEstimator adds weight of non-cooperative input to estimator @@ -53,6 +57,7 @@ func testHtlcV2SuccessEstimator(estimator *input.TxWeightEstimator) error { if err != nil { return err } + return htlc.AddSuccessToEstimator(estimator) } @@ -61,14 +66,15 @@ func testHtlcV2SuccessEstimator(estimator *input.TxWeightEstimator) error { func testHtlcV3SuccessEstimator(estimator *input.TxWeightEstimator) error { swapHash := lntypes.Hash{1, 1, 1} htlc, err := swap.NewHtlcV3( - input.MuSig2Version100RC2, 111, - htlcKeys.SenderInternalPubKey, htlcKeys.ReceiverInternalPubKey, - htlcKeys.SenderScriptKey, htlcKeys.ReceiverScriptKey, swapHash, + input.MuSig2Version100RC2, 111, htlcKeys.SenderInternalPubKey, + htlcKeys.ReceiverInternalPubKey, htlcKeys.SenderScriptKey, + htlcKeys.ReceiverScriptKey, swapHash, &chaincfg.RegressionNetParams, ) if err != nil { return err } + return htlc.AddSuccessToEstimator(estimator) } @@ -81,11 +87,19 @@ func TestEstimateSweepFeeIncrement(t *testing.T) { p2pkhAddr := (*btcutil.AddressPubKeyHash)(nil) outpoint1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + 1, + }, Index: 1, } outpoint2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + 2, + }, Index: 2, } @@ -260,11 +274,19 @@ func TestEstimateSweepFeeIncrement(t *testing.T) { func TestEstimateBatchWeight(t *testing.T) { // Useful variables reused in test cases. outpoint1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + 1, + }, Index: 1, } outpoint2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + 2, + }, Index: 2, } se2 := testHtlcV2SuccessEstimator @@ -548,7 +570,9 @@ func TestSelectBatches(t *testing.T) { FeeRate: lowFeeRate, Weight: coopNewBatchWeight, }, - wantBestBatchesIds: []int32{newBatchSignal}, + wantBestBatchesIds: []int32{ + newBatchSignal, + }, }, { @@ -568,7 +592,10 @@ func TestSelectBatches(t *testing.T) { FeeRate: lowFeeRate, Weight: coopNewBatchWeight, }, - wantBestBatchesIds: []int32{1, newBatchSignal}, + wantBestBatchesIds: []int32{ + 1, + newBatchSignal, + }, }, { @@ -588,7 +615,10 @@ func TestSelectBatches(t *testing.T) { FeeRate: lowFeeRate, Weight: coopNewBatchWeight, }, - wantBestBatchesIds: []int32{newBatchSignal, 1}, + wantBestBatchesIds: []int32{ + newBatchSignal, + 1, + }, }, { @@ -613,7 +643,11 @@ func TestSelectBatches(t *testing.T) { FeeRate: lowFeeRate, Weight: coopNewBatchWeight, }, - wantBestBatchesIds: []int32{1, newBatchSignal, 2}, + wantBestBatchesIds: []int32{ + 1, + newBatchSignal, + 2, + }, }, { @@ -638,7 +672,11 @@ func TestSelectBatches(t *testing.T) { FeeRate: highFeeRate, Weight: coopNewBatchWeight, }, - wantBestBatchesIds: []int32{2, newBatchSignal, 1}, + wantBestBatchesIds: []int32{ + 2, + newBatchSignal, + 1, + }, }, { @@ -663,7 +701,11 @@ func TestSelectBatches(t *testing.T) { FeeRate: highFeeRate, Weight: nonCoopNewBatchWeight, }, - wantBestBatchesIds: []int32{2, newBatchSignal, 1}, + wantBestBatchesIds: []int32{ + 2, + newBatchSignal, + 1, + }, }, { @@ -688,7 +730,11 @@ func TestSelectBatches(t *testing.T) { FeeRate: highFeeRate, Weight: nonCoopNewBatchWeight, }, - wantBestBatchesIds: []int32{2, newBatchSignal, 1}, + wantBestBatchesIds: []int32{ + 2, + newBatchSignal, + 1, + }, }, { @@ -713,7 +759,11 @@ func TestSelectBatches(t *testing.T) { FeeRate: highFeeRate, Weight: nonCoopNewBatchWeight, }, - wantBestBatchesIds: []int32{2, newBatchSignal, 1}, + wantBestBatchesIds: []int32{ + 2, + newBatchSignal, + 1, + }, }, { @@ -738,7 +788,11 @@ func TestSelectBatches(t *testing.T) { FeeRate: lowFeeRate, Weight: nonCoopNewBatchWeight, }, - wantBestBatchesIds: []int32{1, newBatchSignal, 2}, + wantBestBatchesIds: []int32{ + 1, + newBatchSignal, + 2, + }, }, { @@ -763,7 +817,11 @@ func TestSelectBatches(t *testing.T) { FeeRate: lowFeeRate, Weight: nonCoopNewBatchWeight, }, - wantBestBatchesIds: []int32{1, newBatchSignal, 2}, + wantBestBatchesIds: []int32{ + 1, + newBatchSignal, + 2, + }, }, { @@ -788,7 +846,11 @@ func TestSelectBatches(t *testing.T) { FeeRate: lowFeeRate, Weight: nonCoopNewBatchWeight, }, - wantBestBatchesIds: []int32{1, newBatchSignal, 2}, + wantBestBatchesIds: []int32{ + 1, + newBatchSignal, + 2, + }, }, { @@ -815,7 +877,9 @@ func TestSelectBatches(t *testing.T) { Weight: coopNewBatchWeight, IsExternalAddr: true, }, - wantBestBatchesIds: []int32{newBatchSignal}, + wantBestBatchesIds: []int32{ + newBatchSignal, + }, }, { @@ -841,7 +905,10 @@ func TestSelectBatches(t *testing.T) { FeeRate: highFeeRate, Weight: coopNewBatchWeight, }, - wantBestBatchesIds: []int32{1, newBatchSignal}, + wantBestBatchesIds: []int32{ + 1, + newBatchSignal, + }, }, { @@ -861,7 +928,10 @@ func TestSelectBatches(t *testing.T) { FeeRate: lowFeeRate, Weight: coopNewBatchWeight, }, - wantBestBatchesIds: []int32{newBatchSignal, 1}, + wantBestBatchesIds: []int32{ + newBatchSignal, + 1, + }, }, { @@ -882,7 +952,10 @@ func TestSelectBatches(t *testing.T) { FeeRate: lowFeeRate, Weight: coopNewBatchWeight, }, - wantBestBatchesIds: []int32{1, newBatchSignal}, + wantBestBatchesIds: []int32{ + 1, + newBatchSignal, + }, }, } diff --git a/sweepbatcher/presigned.go b/sweepbatcher/presigned.go index 870e82a21..b004b4fa3 100644 --- a/sweepbatcher/presigned.go +++ b/sweepbatcher/presigned.go @@ -85,8 +85,8 @@ func ensurePresigned(ctx context.Context, newSweeps []*sweep, sweeps, destAddr, currentHeight, feeRate, minRelayFeeRate, ) if err != nil { - return fmt.Errorf("failed to construct unsigned tx "+ - "for feeRate %v: %w", feeRate, err) + return fmt.Errorf("failed to construct unsigned tx for "+ + "feeRate %v: %w", feeRate, err) } // Check of a presigned transaction exists. @@ -99,10 +99,10 @@ func ensurePresigned(ctx context.Context, newSweeps []*sweep, ctx, primarySweepID, tx, batchAmt, feeRate, feeRate, loadOnly, ) if err != nil { - return fmt.Errorf("failed to find a presigned transaction "+ - "for feeRate %v, txid of the template is %v, inputs: %d, "+ - "outputs: %d: %w", feeRate, tx.TxHash(), - len(tx.TxIn), len(tx.TxOut), err) + return fmt.Errorf("failed to find a presigned transaction for "+ + "feeRate %v, txid of the template is %v, inputs: %d, "+ + "outputs: %d: %w", feeRate, tx.TxHash(), len(tx.TxIn), + len(tx.TxOut), err) } // Check the SignTx worked correctly. @@ -277,8 +277,8 @@ func (b *batch) presign(ctx context.Context, newSweeps []*sweep) error { sweeps, nextBlockFeeRate, minRelayFeeRate, ) if err != nil { - return fmt.Errorf("failed to presign a transaction "+ - "of %d sweeps: %w", len(sweeps), err) + return fmt.Errorf("failed to presign a transaction of "+ + "%d sweeps: %w", len(sweeps), err) } // Cut a group to proceed to next suffix of original groups. @@ -304,8 +304,8 @@ type presigner interface { // It is only called with loadOnly=true by ensurePresigned. SignTx(ctx context.Context, primarySweepID wire.OutPoint, tx *wire.MsgTx, inputAmt btcutil.Amount, - minRelayFee, feeRate chainfee.SatPerKWeight, - loadOnly bool) (*wire.MsgTx, error) + minRelayFee, feeRate chainfee.SatPerKWeight, loadOnly bool) ( + *wire.MsgTx, error) } // presign tries to presign batch sweep transactions of the sweeps. It signs @@ -435,8 +435,8 @@ func (b *batch) publishPresigned(ctx context.Context) (btcutil.Amount, error, // Make sure that no external address is used. for _, sweep := range b.sweeps { if sweep.isExternalAddr { - return 0, fmt.Errorf("external address was used with " + - "a custom transaction signer"), false + return 0, fmt.Errorf("external address was " + + "used with a custom transaction signer"), false } } @@ -461,12 +461,11 @@ func (b *batch) publishPresigned(ctx context.Context) (btcutil.Amount, error, // Cache the destination address. address, err := getPresignedSweepsDestAddr( - ctx, b.cfg.presignedHelper, b.primarySweepID, - b.cfg.chainParams, + ctx, b.cfg.presignedHelper, b.primarySweepID, b.cfg.chainParams, ) if err != nil { - return 0, fmt.Errorf("failed to find destination address: %w", - err), false + return 0, fmt.Errorf("failed to find destination "+ + "address: %w", err), false } // Construct unsigned batch transaction. @@ -510,11 +509,13 @@ func (b *batch) publishPresigned(ctx context.Context) (btcutil.Amount, error, // Make sure tx weight matches the expected value. realWeight := lntypes.WeightUnit( - blockchain.GetTransactionWeight(btcutil.NewTx(tx)), + blockchain.GetTransactionWeight( + btcutil.NewTx(tx), + ), ) if realWeight != weight { - b.Warnf("actual weight of tx %v is %v, estimated as %d", - txHash, realWeight, weight) + b.Warnf("actual weight of tx %v is %v, estimated as %d", txHash, + realWeight, weight) } // Find actual fee rate of the signed transaction. It may differ from @@ -528,11 +529,10 @@ func (b *batch) publishPresigned(ctx context.Context) (btcutil.Amount, error, numSweeps := len(tx.TxIn) numChange := len(tx.TxOut) - 1 - b.Infof("attempting to publish custom signed tx=%v, desiredFeerate=%v,"+ - " signedFeeRate=%v, weight=%v, fee=%v, sweeps=%d, "+ - "changeOutputs=%d, destAddr=%s", - txHash, feeRate, signedFeeRate, realWeight, fee, numSweeps, - numChange, address) + b.Infof("attempting to publish custom signed tx=%v, "+ + "desiredFeerate=%v, signedFeeRate=%v, weight=%v, fee=%v, "+ + "sweeps=%d, changeOutputs=%d, destAddr=%s", txHash, feeRate, + signedFeeRate, realWeight, fee, numSweeps, numChange, address) b.debugLogTx("serialized batch", tx) // Publish the transaction. @@ -585,9 +585,9 @@ func getPresignedSweepsDestAddr(ctx context.Context, helper destPkScripter, address, err := pkScript.Address(chainParams) if err != nil { - return nil, fmt.Errorf("pkScript.Address failed for "+ - "pkScript %x returned for primarySweepID %v: %w", - pkScriptBytes, primarySweepID, err) + return nil, fmt.Errorf("pkScript.Address failed for pkScript "+ + "%x returned for primarySweepID %v: %w", pkScriptBytes, + primarySweepID, err) } return address, nil @@ -618,8 +618,8 @@ func CheckSignedTx(unsignedTx, signedTx *wire.MsgTx, inputAmt btcutil.Amount, txIn.PreviousOutPoint) } if seq != txIn.Sequence { - return fmt.Errorf("sequence mismatch in input %s: "+ - "%d in unsigned, %d in signed", + return fmt.Errorf("sequence mismatch in input %s: %d "+ + "in unsigned, %d in signed", txIn.PreviousOutPoint, seq, txIn.Sequence) } delete(unsignedMap, txIn.PreviousOutPoint) @@ -655,7 +655,9 @@ func CheckSignedTx(unsignedTx, signedTx *wire.MsgTx, inputAmt btcutil.Amount, // Find the feerate of signedTx. fee := inputAmt - totalOutputValue weight := lntypes.WeightUnit( - blockchain.GetTransactionWeight(btcutil.NewTx(signedTx)), + blockchain.GetTransactionWeight( + btcutil.NewTx(signedTx), + ), ) feeRate := chainfee.NewSatPerKWeight(fee, weight) if feeRate < minRelayFee { diff --git a/sweepbatcher/presigned_test.go b/sweepbatcher/presigned_test.go index bd37d83ad..f592148b1 100644 --- a/sweepbatcher/presigned_test.go +++ b/sweepbatcher/presigned_test.go @@ -316,8 +316,8 @@ func TestOrderedSweeps(t *testing.T) { switch { // Don't create DB sweeps at all. case tc.skipStore: - // Reverse the order of DB sweeps to ID incorrect IDs. + case tc.reverseStore: for i := len(tc.sweeps) - 1; i >= 0; i-- { s := tc.sweeps[i] @@ -355,12 +355,14 @@ func TestOrderedSweeps(t *testing.T) { orderedSweeps, err := b.getOrderedSweeps(ctx) if tc.wantErr1 != "" { require.ErrorContains(t, err, tc.wantErr1) + return } require.NoError(t, err) if tc.reverseStore { require.NotEqual(t, tc.sweeps, orderedSweeps) + return } @@ -378,6 +380,7 @@ func TestOrderedSweeps(t *testing.T) { groups, err := b.getSweepsGroups(ctx) if tc.wantErr2 != "" { require.ErrorContains(t, err, tc.wantErr2) + return } require.NoError(t, err) @@ -436,8 +439,8 @@ func (m *mockPresignedTxChecker) DestPkScript(ctx context.Context, // SignTx records all its argumentd and returned a "presigned" tx. func (m *mockPresignedTxChecker) SignTx(ctx context.Context, primarySweepID wire.OutPoint, tx *wire.MsgTx, inputAmt btcutil.Amount, - minRelayFee, feeRate chainfee.SatPerKWeight, - loadOnly bool) (*wire.MsgTx, error) { + minRelayFee, feeRate chainfee.SatPerKWeight, loadOnly bool) ( + *wire.MsgTx, error) { m.signTxCalls++ @@ -469,11 +472,19 @@ func (m *mockPresignedTxChecker) SignTx(ctx context.Context, func TestEnsurePresigned(t *testing.T) { // Prepare the necessary data for test cases. op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + 1, + }, Index: 1, } op2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + 2, + }, Index: 2, } @@ -588,8 +599,10 @@ func TestEnsurePresigned(t *testing.T) { switch { case tc.destPkScriptErr != nil: require.ErrorIs(t, err, tc.destPkScriptErr) + case tc.signedTxErr != nil: require.ErrorIs(t, err, tc.signedTxErr) + default: require.NoError(t, err) require.Equal(t, 1, c.signTxCalls) @@ -640,8 +653,8 @@ type mockPresigner struct { // calls previously made is failAt. func (p *mockPresigner) SignTx(ctx context.Context, primarySweepID wire.OutPoint, tx *wire.MsgTx, inputAmt btcutil.Amount, - minRelayFee, feeRate chainfee.SatPerKWeight, - loadOnly bool) (*wire.MsgTx, error) { + minRelayFee, feeRate chainfee.SatPerKWeight, loadOnly bool) ( + *wire.MsgTx, error) { p.mu.Lock() defer p.mu.Unlock() @@ -670,11 +683,19 @@ func (p *mockPresigner) SignTx(ctx context.Context, func TestPresign(t *testing.T) { // Prepare the necessary data for test cases. op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + 1, + }, Index: 1, } op2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + 2, + }, Index: 2, } @@ -1144,11 +1165,19 @@ func TestPresign(t *testing.T) { func TestCheckSignedTx(t *testing.T) { // Prepare the necessary data for test cases. op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + 1, + }, Index: 1, } op2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + 2, + }, Index: 2, } @@ -1667,8 +1696,12 @@ func TestCheckSignedTx(t *testing.T) { }, TxOut: []*wire.TxOut{ { - Value: 2999374, - PkScript: []byte{0xaf, 0xfe}, // Just to make it different. + Value: 2999374, + // Just to make it different. + PkScript: []byte{ + 0xaf, + 0xfe, + }, }, }, LockTime: 799_999, @@ -1709,7 +1742,8 @@ func TestCheckSignedTx(t *testing.T) { }, TxOut: []*wire.TxOut{ { - Value: 1_337_000, // Just to make it different. + // Just to make it different. + Value: 1_337_000, PkScript: batchPkScript, }, }, @@ -1770,7 +1804,8 @@ func TestCheckSignedTx(t *testing.T) { PkScript: batchPkScript, }, { - Value: 1_338, // Just to make it different. + // Just to make it different. + Value: 1_338, PkScript: batchPkScript, }, }, diff --git a/sweepbatcher/store.go b/sweepbatcher/store.go index 4cf899c85..567348a30 100644 --- a/sweepbatcher/store.go +++ b/sweepbatcher/store.go @@ -18,8 +18,7 @@ import ( // by sqlc for sweep batcher. type Querier interface { // GetBatchSweeps fetches all the sweeps that are part a batch. - GetBatchSweeps(ctx context.Context, batchID int32) ( - []sqlc.Sweep, error) + GetBatchSweeps(ctx context.Context, batchID int32) ([]sqlc.Sweep, error) // GetBatchSweptAmount returns the total amount of sats swept by a // (confirmed) batch. @@ -38,8 +37,8 @@ type Querier interface { // InsertBatch inserts a batch into the database, returning the id of // the inserted batch. - InsertBatch(ctx context.Context, arg sqlc.InsertBatchParams) ( - int32, error) + InsertBatch(ctx context.Context, + arg sqlc.InsertBatchParams) (int32, error) // CancelBatch marks the batch as cancelled. CancelBatch(ctx context.Context, id int32) error @@ -147,8 +146,8 @@ func (s *SQLStore) ConfirmBatchWithSweeps(ctx context.Context, batch *dbBatch, } // FetchBatchSweeps fetches all the sweeps that are part a batch. -func (s *SQLStore) FetchBatchSweeps(ctx context.Context, id int32) ( - []*dbSweep, error) { +func (s *SQLStore) FetchBatchSweeps(ctx context.Context, id int32) ([]*dbSweep, + error) { readOpts := loopdb.NewSqlReadOpts() var sweeps []*dbSweep diff --git a/sweepbatcher/store_mock.go b/sweepbatcher/store_mock.go index fac6ffec6..0beed4ad2 100644 --- a/sweepbatcher/store_mock.go +++ b/sweepbatcher/store_mock.go @@ -48,8 +48,8 @@ func (s *StoreMock) FetchUnconfirmedSweepBatches(ctx context.Context) ( // InsertSweepBatch inserts a batch into the database, returning the id of the // inserted batch. -func (s *StoreMock) InsertSweepBatch(ctx context.Context, - batch *dbBatch) (int32, error) { +func (s *StoreMock) InsertSweepBatch(ctx context.Context, batch *dbBatch) ( + int32, error) { s.mu.Lock() defer s.mu.Unlock() @@ -58,12 +58,14 @@ func (s *StoreMock) InsertSweepBatch(ctx context.Context, s.batchID++ s.batches[id] = *batch + return id, nil } // CancelBatch drops a batch from the database. func (s *StoreMock) CancelBatch(ctx context.Context, id int32) error { delete(s.batches, id) + return nil } @@ -75,12 +77,13 @@ func (s *StoreMock) UpdateSweepBatch(ctx context.Context, defer s.mu.Unlock() s.batches[batch.ID] = *batch + return nil } // ConfirmBatchWithSweeps updates the batch and the provided sweeps atomically. -func (s *StoreMock) ConfirmBatchWithSweeps(ctx context.Context, - batch *dbBatch, sweeps []*dbSweep) error { +func (s *StoreMock) ConfirmBatchWithSweeps(ctx context.Context, batch *dbBatch, + sweeps []*dbSweep) error { s.mu.Lock() defer s.mu.Unlock() @@ -105,8 +108,8 @@ func (s *StoreMock) ConfirmBatchWithSweeps(ctx context.Context, } // FetchBatchSweeps fetches all the sweeps that belong to a batch. -func (s *StoreMock) FetchBatchSweeps(ctx context.Context, - id int32) ([]*dbSweep, error) { +func (s *StoreMock) FetchBatchSweeps(ctx context.Context, id int32) ([]*dbSweep, + error) { s.mu.Lock() defer s.mu.Unlock() @@ -172,6 +175,7 @@ func (s *StoreMock) AssertSweepStored(outpoint wire.OutPoint) bool { defer s.mu.Unlock() _, ok := s.sweeps[outpoint] + return ok } @@ -188,6 +192,7 @@ func (s *StoreMock) GetParentBatch(ctx context.Context, if !ok { return nil, errors.New("batch not found") } + return &batch, nil } } diff --git a/sweepbatcher/sweep_batch.go b/sweepbatcher/sweep_batch.go index b77024f9d..1b32e28df 100644 --- a/sweepbatcher/sweep_batch.go +++ b/sweepbatcher/sweep_batch.go @@ -360,7 +360,6 @@ type batchKit struct { func (b *batch) scheduleNextCall() (func(), error) { select { case b.callEnter <- struct{}{}: - case <-b.quit: return func() {}, ErrBatcherShuttingDown @@ -409,6 +408,7 @@ func NewBatch(cfg batchConfig, bk batchKit) *batch { func NewBatchFromDB(cfg batchConfig, bk batchKit) (*batch, error) { // Make sure the batch is not empty. if len(bk.sweeps) == 0 { + // This should never happen, as this precondition is already // ensured in spinUpBatchFromDB. return nil, fmt.Errorf("empty batch is not allowed") @@ -643,14 +643,14 @@ func (b *batch) addSweeps(ctx context.Context, sweeps []*sweep) (bool, error) { switch { // We don't need to run checks if existing sweeps are updated. case numExisting == len(sweeps): - // If new sweeps are added to the batch, we need to presign new // version of batch transaction. + case len(b.sweeps) != 0: if err := b.presign(ctx, sweeps); err != nil { b.Warnf("Failed to add sweep %x to the batch, "+ - "because failed to presign new version"+ - " of batch tx: %v", + "because failed to presign new "+ + "version of batch tx: %v", sweeps[0].swapHash[:6], err) return false, nil @@ -664,10 +664,10 @@ func (b *batch) addSweeps(ctx context.Context, sweeps []*sweep) (bool, error) { ctx, sweeps, allowNonEmptyBatch, ) if err != nil { - b.Warnf("Failed to check signing of input %x,"+ - " this means that PresignSweepsGroup "+ - "was not called prior to AddSweep for"+ - " this input: %v", + b.Warnf("Failed to check signing of input %x, "+ + "this means that PresignSweepsGroup "+ + "was not called prior to AddSweep for "+ + "this input: %v", sweeps[0].swapHash[:6], err) return false, nil @@ -725,8 +725,8 @@ func (b *batch) addSweeps(ctx context.Context, sweeps []*sweep) (bool, error) { // lower that minFeeRate of other sweeps (so it is // applied). if b.rbfCache.FeeRate < s.minFeeRate { - b.Infof("Increasing feerate of the batch "+ - "from %v to %v", b.rbfCache.FeeRate, + b.Infof("Increasing feerate of the batch from "+ + "%v to %v", b.rbfCache.FeeRate, s.minFeeRate) b.rbfCache.FeeRate = s.minFeeRate } @@ -734,10 +734,11 @@ func (b *batch) addSweeps(ctx context.Context, sweeps []*sweep) (bool, error) { return true, nil } else if numNew != len(sweeps) { + // Sanity check: all the sweeps must be either existing or new. // We have checked this above, let's check here as well. - return false, fmt.Errorf("bug in numExisting and numNew logic:"+ - " numExisting=%d, numNew=%d, len(sweeps)=%d, "+ + return false, fmt.Errorf("bug in numExisting and numNew "+ + "logic: numExisting=%d, numNew=%d, len(sweeps)=%d, "+ "len(b.sweeps)=%d", numExisting, numNew, len(sweeps), len(b.sweeps)) } @@ -774,9 +775,8 @@ func (b *batch) addSweeps(ctx context.Context, sweeps []*sweep) (bool, error) { // Update FeeRate. Max(s.minFeeRate) for all the sweeps of // the batch is the basis for fee bumps. if b.rbfCache.FeeRate < s.minFeeRate { - b.Infof("Increasing feerate of the batch "+ - "from %v to %v", b.rbfCache.FeeRate, - s.minFeeRate) + b.Infof("Increasing feerate of the batch from %v to %v", + b.rbfCache.FeeRate, s.minFeeRate) b.rbfCache.FeeRate = s.minFeeRate b.rbfCache.SkipNextBump = true } @@ -952,14 +952,14 @@ func (b *batch) Run(ctx context.Context) error { case <-timerChan: // Check that batch is still open. if b.state != Open { - b.Debugf("Skipping publishing, because "+ - "the batch is not open (%v).", b.state) + b.Debugf("Skipping publishing, because the "+ + "batch is not open (%v).", b.state) continue } if skipBefore == nil { - b.Debugf("Skipping publishing, because " + - "the batch is empty.") + b.Debugf("Skipping publishing, because the " + + "batch is empty.") continue } @@ -1011,8 +1011,8 @@ func (b *batch) Run(ctx context.Context) error { // block. We can accept more sweeps and try to publish. case <-b.reorgChan: b.state = Open - b.Warnf("reorg detected, batch is able to " + - "accept new sweeps") + b.Warnf("reorg detected, batch is able to accept new " + + "sweeps") case testReq := <-b.testReqs: testReq.handler() @@ -1036,8 +1036,8 @@ func (b *batch) Run(ctx context.Context) error { func (b *batch) updateFeeRate(ctx context.Context) { for outpoint, s := range b.sweeps { minFeeRate, err := minimumSweepFeeRate( - ctx, b.cfg.customFeeRate, b.wallet, - s.swapHash, s.outpoint, s.confTarget, + ctx, b.cfg.customFeeRate, b.wallet, s.swapHash, + s.outpoint, s.confTarget, ) if err != nil { b.Warnf("failed to determine feerate for sweep %v of "+ @@ -1050,8 +1050,8 @@ func (b *batch) updateFeeRate(ctx context.Context) { continue } - b.Infof("Increasing feerate of sweep %v of swap %x from %v "+ - "to %v", s.outpoint, s.swapHash[:6], s.minFeeRate, + b.Infof("Increasing feerate of sweep %v of swap %x from "+ + "%v to %v", s.outpoint, s.swapHash[:6], s.minFeeRate, minFeeRate) s.minFeeRate = minFeeRate b.sweeps[outpoint] = s @@ -1075,6 +1075,7 @@ func (b *batch) testRunInEventLoop(ctx context.Context, handler func()) { handler() return + default: } @@ -1118,13 +1119,14 @@ func (b *batch) isUrgent(skipBefore time.Time) bool { if timeout <= 0 { // This may happen if the batch is empty or if SweepInfo.Timeout // is not set, may be possible in tests or if there is a bug. - b.Warnf("Method timeout() returned %v. Number of "+ - "sweeps: %d. It may be an empty batch.", - timeout, len(b.sweeps)) + b.Warnf("Method timeout() returned %v. Number of sweeps: %d. "+ + "It may be an empty batch.", timeout, len(b.sweeps)) + return false } if b.currentHeight == 0 { + // currentHeight is not initiated yet. return false } @@ -1138,6 +1140,7 @@ func (b *batch) isUrgent(skipBefore time.Time) bool { remainingWaiting := skipBefore.Sub(b.cfg.clock.Now()) if timeBank >= safetyFactor*remainingWaiting { + // There is enough time, keep waiting. return false } @@ -1240,8 +1243,8 @@ func (b *batch) publish(ctx context.Context) error { b.Infof("published, total sweeps: %v, fees: %v", len(b.sweeps), fee) for _, sweep := range b.sweeps { - b.Infof("published sweep %x, value: %v", - sweep.swapHash[:6], sweep.value) + b.Infof("published sweep %x, value: %v", sweep.swapHash[:6], + sweep.value) } return b.persist(ctx) @@ -1340,8 +1343,9 @@ func constructUnsignedTx(sweeps []sweep, address btcutil.Address, err := sweep.htlcSuccessEstimator(&weightEstimate) if err != nil { - return nil, 0, 0, 0, fmt.Errorf("sweep."+ - "htlcSuccessEstimator failed: %w", err) + return nil, 0, 0, 0, fmt.Errorf( + "sweep.htlcSuccessEstimator failed: %w", + err) } } else { // Cooperative sweep. @@ -1415,8 +1419,8 @@ func constructUnsignedTx(sweeps []sweep, address btcutil.Address, utils.MaxFeeToAmountRatio, minRelayFeeRate, weight, ) if err != nil { - return nil, 0, 0, 0, fmt.Errorf("failed to clamp batch "+ - "fee: %w", err) + return nil, 0, 0, 0, fmt.Errorf("failed to clamp batch fee: %w", + err) } // Ensure that batch amount is equal or exceeds the sum of change @@ -1424,10 +1428,9 @@ func constructUnsignedTx(sweeps []sweep, address btcutil.Address, // for the main output. dustLimit := utils.DustLimitForPkScript(batchPkScript) if fee+btcutil.Amount(sumChange)+dustLimit > batchAmt { - return nil, 0, 0, 0, fmt.Errorf("batch amount %v is < the "+ - "sum of change outputs %v plus fee %v and dust "+ - "limit %v", batchAmt, btcutil.Amount(sumChange), - fee, dustLimit) + return nil, 0, 0, 0, fmt.Errorf("batch amount %v is < the sum "+ + "of change outputs %v plus fee %v and dust limit %v", + batchAmt, btcutil.Amount(sumChange), fee, dustLimit) } // Add the main output first. @@ -1472,9 +1475,9 @@ func constructUnsignedTx(sweeps []sweep, address btcutil.Address, for swapHash, inputs := range swap2Inputs { change := swap2Change[swapHash] if inputs <= change { - return nil, 0, 0, 0, fmt.Errorf(""+ - "inputs %v <= change %v for swap %x", - inputs, change, swapHash[:6]) + return nil, 0, 0, 0, fmt.Errorf("inputs %v <= "+ + "change %v for swap %x", inputs, change, + swapHash[:6]) } } } @@ -1529,8 +1532,8 @@ func (b *batch) publishMixedBatch(ctx context.Context) (btcutil.Amount, error, if addrOverride { // Sanity check, there should be exactly 1 sweep in this batch. if len(sweeps) != 1 { - return 0, fmt.Errorf("external address sweep batched " + - "with other sweeps"), false + return 0, fmt.Errorf("external address sweep " + + "batched with other sweeps"), false } address = sweeps[0].destAddr @@ -1600,8 +1603,8 @@ func (b *batch) publishMixedBatch(ctx context.Context) (btcutil.Amount, error, ctx, i, sweep, tx, prevOutsMap, psbtBytes, ) if err != nil { - b.Infof("cooperative signing failed for "+ - "sweep %x: %v", sweep.swapHash[:6], err) + b.Infof("cooperative signing failed for sweep "+ + "%x: %v", sweep.swapHash[:6], err) // Set coopFailed flag for this sweep in all the // places we store the sweep. @@ -1653,8 +1656,8 @@ func (b *batch) publishMixedBatch(ctx context.Context) (btcutil.Amount, error, sweep.htlcKeys.ReceiverScriptKey[:], ) if err != nil { - return 0, fmt.Errorf("btcec.ParsePubKey failed: %w", - err), false + return 0, fmt.Errorf("btcec.ParsePubKey "+ + "failed: %w", err), false } // Create and store the sign descriptor for this sweep. @@ -1677,11 +1680,13 @@ func (b *batch) publishMixedBatch(ctx context.Context) (btcutil.Amount, error, // Sanity checks. if len(signDescs) != nonCoopInputs { + // This must not happen by construction. - return 0, fmt.Errorf("unexpected size of signDescs: %d != %d", - len(signDescs), nonCoopInputs), false + return 0, fmt.Errorf("unexpected size of signDescs: "+ + "%d != %d", len(signDescs), nonCoopInputs), false } if len(prevOutsList) != len(sweeps) { + // This must not happen by construction. return 0, fmt.Errorf("unexpected size of prevOutsList: "+ "%d != %d", len(prevOutsList), len(sweeps)), false @@ -1695,16 +1700,18 @@ func (b *batch) publishMixedBatch(ctx context.Context) (btcutil.Amount, error, ctx, tx, signDescs, prevOutsList, ) if err != nil { - return 0, fmt.Errorf("signerClient.SignOutputRaw "+ - "failed: %w", err), false + return 0, fmt.Errorf( + "signerClient.SignOutputRaw failed: %w", + err), false } } // Sanity checks. if len(rawSigs) != nonCoopInputs { + // This must not happen by construction. - return 0, fmt.Errorf("unexpected size of rawSigs: %d != %d", - len(rawSigs), nonCoopInputs), false + return 0, fmt.Errorf("unexpected size of rawSigs: "+ + "%d != %d", len(rawSigs), nonCoopInputs), false } // Generate success witnesses for non-cooperative sweeps. @@ -1719,8 +1726,9 @@ func (b *batch) publishMixedBatch(ctx context.Context) (btcutil.Amount, error, rawSigs[sigIndex], sweep.preimage, ) if err != nil { - return 0, fmt.Errorf("sweep.htlc.GenSuccessWitness "+ - "failed: %w", err), false + return 0, fmt.Errorf( + "sweep.htlc.GenSuccessWitness "+ + "failed: %w", err), false } sigIndex++ @@ -1740,8 +1748,8 @@ func (b *batch) publishMixedBatch(ctx context.Context) (btcutil.Amount, error, } txHash := tx.TxHash() b.Infof("attempting to publish batch tx=%v with feerate=%v, "+ - "weight=%v, feeForWeight=%v, fee=%v, sweeps=%d, "+ - "%d cooperative: (%s) and %d non-cooperative (%s), destAddr=%s", + "weight=%v, feeForWeight=%v, fee=%v, sweeps=%d, %d "+ + "cooperative: (%s) and %d non-cooperative (%s), destAddr=%s", txHash, b.rbfCache.FeeRate, weight, feeForWeight, fee, len(tx.TxIn), coopInputs, strings.Join(coopHexs, ", "), nonCoopInputs, strings.Join(nonCoopHexs, ", "), address) @@ -1750,11 +1758,13 @@ func (b *batch) publishMixedBatch(ctx context.Context) (btcutil.Amount, error, // Make sure tx weight matches the expected value. realWeight := lntypes.WeightUnit( - blockchain.GetTransactionWeight(btcutil.NewTx(tx)), + blockchain.GetTransactionWeight( + btcutil.NewTx(tx), + ), ) if realWeight != weight { - b.Warnf("actual weight of tx %v is %v, estimated as %d", - txHash, realWeight, weight) + b.Warnf("actual weight of tx %v is %v, estimated as %d", txHash, + realWeight, weight) } // Publish the transaction. @@ -1778,6 +1788,7 @@ func (b *batch) debugLogTx(msg string, tx *wire.MsgTx) { buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) if err := tx.Serialize(buf); err != nil { b.Errorf("failed to serialize tx for debug log: %v", err) + return } @@ -1835,8 +1846,8 @@ func (b *batch) musig2sign(ctx context.Context, inputIndex int, sweep sweep, if b.cfg.customMuSig2Signer != nil { // Produce a signature. finalSig, err := b.cfg.customMuSig2Signer( - ctx, muSig2Version, sweep.swapHash, - htlcScript.RootHash, digest, + ctx, muSig2Version, sweep.swapHash, htlcScript.RootHash, + digest, ) if err != nil { return nil, fmt.Errorf("customMuSig2Signer failed: %w", @@ -1875,8 +1886,8 @@ func (b *batch) musig2sign(ctx context.Context, inputIndex int, sweep sweep, // signature. serverNonce, serverSig, err := b.muSig2SignSweep( ctx, sweep.protocolVersion, sweep.swapHash, - sweep.swapInvoicePaymentAddr, - musig2SessionInfo.PublicNonce[:], psbt, prevOuts, + sweep.swapInvoicePaymentAddr, musig2SessionInfo.PublicNonce[:], + psbt, prevOuts, ) if err != nil { return nil, err @@ -1897,8 +1908,7 @@ func (b *batch) musig2sign(ctx context.Context, inputIndex int, sweep sweep, // Sanity check that we have all the nonces. if !haveAllNonces { - return nil, fmt.Errorf("invalid MuSig2 session: " + - "nonces missing") + return nil, fmt.Errorf("invalid MuSig2 session: nonces missing") } // Since our MuSig2 session has all nonces, we can now create @@ -2031,6 +2041,7 @@ func (b *batch) monitorConfirmations(ctx context.Context) error { ) if err != nil { cancel() + return err } @@ -2041,15 +2052,16 @@ func (b *batch) monitorConfirmations(ctx context.Context) error { case conf := <-confChan: select { case b.confChan <- conf: - case <-ctx.Done(): } case err := <-errChan: b.writeToConfErrChan(ctx, err) - b.writeToErrChan(fmt.Errorf("confirmations "+ - "monitoring error: %w", err)) + b.writeToErrChan( + fmt.Errorf("confirmations monitoring "+ + "error: %w", err), + ) case <-ctx.Done(): } @@ -2158,8 +2170,8 @@ func (b *batch) handleSpend(ctx context.Context, spendTx *wire.MsgTx) error { // hash, otherwise our fee calculation is incorrect. fee, has := swap2fee[sweep.swapHash] if !has { - return fmt.Errorf("no fee for swap %v; maybe "+ - "multiple sweeps with a notifier per swap?", + return fmt.Errorf("no fee for swap %v; maybe multiple "+ + "sweeps with a notifier per swap?", sweep.swapHash) } delete(swap2fee, sweep.swapHash) @@ -2232,8 +2244,7 @@ func (b *batch) handleConf(ctx context.Context, // same way for simplicity. allSweeps, err := b.getOrderedSweeps(ctx) if err != nil { - return fmt.Errorf("getOrderedSweeps(%d) failed: %w", - b.id, err) + return fmt.Errorf("getOrderedSweeps(%d) failed: %w", b.id, err) } // Make a set of confirmed sweeps. @@ -2316,9 +2327,9 @@ func (b *batch) handleConf(ctx context.Context, } } - b.Infof("Fully confirmed sweeps: %v, purged sweeps: %v, "+ - "purged swaps: %v. Saving the batch and sweeps to DB", - confirmedSweeps, purgedSweeps, purgedSwaps) + b.Infof("Fully confirmed sweeps: %v, purged sweeps: %v, purged swaps: "+ + "%v. Saving the batch and sweeps to DB", confirmedSweeps, + purgedSweeps, purgedSwaps) if err := b.persistConfirmedBatch(ctx, dbConfirmed); err != nil { return fmt.Errorf("saving confirmed batch failed: %w", err) @@ -2356,8 +2367,8 @@ func (b *batch) handleConf(ctx context.Context, err := b.cfg.presignedHelper.CleanupTransactions(ctx, inputs) if err != nil { - return fmt.Errorf("failed to clean up store for "+ - "batch %d, inputs %v: %w", b.id, inputs, err) + return fmt.Errorf("failed to clean up store for batch "+ + "%d, inputs %v: %w", b.id, inputs, err) } } @@ -2391,9 +2402,8 @@ func (b *batch) handleConf(ctx context.Context, // hash, otherwise our fee calculation is incorrect. fee, has := swap2fee[s.swapHash] if !has { - return fmt.Errorf("no fee for swap %v; maybe "+ - "multiple sweeps with a notifier per swap?", - s.swapHash) + return fmt.Errorf("no fee for swap %v; maybe multiple "+ + "sweeps with a notifier per swap?", s.swapHash) } delete(swap2fee, s.swapHash) @@ -2411,9 +2421,9 @@ func (b *batch) handleConf(ctx context.Context, // Try to write the confirmation to the notification // channel. case notifier.ConfChan <- confDetail: - // If a quit signal was provided by the swap, // continue. + case <-notifier.QuitChan: } }(s.notifier) @@ -2528,11 +2538,11 @@ func (s *sweep) notifySweepSpend(ctx context.Context, select { // Try to write the update to the notification channel. case s.notifier.SpendChan <- spendDetail: - // If a quit signal was provided by the swap, continue. - case <-s.notifier.QuitChan: + case <-s.notifier.QuitChan: // If the context was canceled, return. + case <-ctx.Done(): } } @@ -2563,12 +2573,12 @@ func (b *batch) writeToSpendErrChan(ctx context.Context, spendErr error) { // Try to write the error to the notification // channel. case notifier.SpendErrChan <- spendErr: - // If a quit signal was provided by the swap, // continue. - case <-notifier.QuitChan: + case <-notifier.QuitChan: // If the context was canceled, stop. + case <-ctx.Done(): } } @@ -2601,12 +2611,12 @@ func (b *batch) writeToConfErrChan(ctx context.Context, confErr error) { // Try to write the error to the notification // channel. case notifier.ConfErrChan <- confErr: - // If a quit signal was provided by the swap, // continue. - case <-notifier.QuitChan: + case <-notifier.QuitChan: // If the context was canceled, stop. + case <-ctx.Done(): } } diff --git a/sweepbatcher/sweep_batch_test.go b/sweepbatcher/sweep_batch_test.go index 34cdb87e6..a5f200b33 100644 --- a/sweepbatcher/sweep_batch_test.go +++ b/sweepbatcher/sweep_batch_test.go @@ -22,15 +22,27 @@ import ( func TestConstructUnsignedTx(t *testing.T) { // Prepare the necessary data for test cases. op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + 1, + }, Index: 1, } op2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + 2, + }, Index: 2, } op3 := wire.OutPoint{ - Hash: chainhash.Hash{3, 3, 3}, + Hash: chainhash.Hash{ + 3, + 3, + 3, + }, Index: 3, } @@ -55,11 +67,13 @@ func TestConstructUnsignedTx(t *testing.T) { PkScript: change1Pkscript, } - change1PrimeAddr := "bc1pdx9ggvtjjcpaqfqk375qhdmzx9xu8dcu7w94lqfcxhh0rj" + - "lwyyeq5ryn6r" + change1PrimeAddr := "bc1pdx9ggvtjjcpaqfqk375qhdmzx9xu8dcu7w94lqfcxhh0" + + "rjlwyyeq5ryn6r" change1PrimeAddress, err := btcutil.DecodeAddress(change1PrimeAddr, nil) require.NoError(t, err) - change1PrimePkscript, err := txscript.PayToAddrScript(change1PrimeAddress) + change1PrimePkscript, err := txscript.PayToAddrScript( + change1PrimeAddress, + ) require.NoError(t, err) change1Prime := &wire.TxOut{ Value: 200_000, @@ -498,7 +512,8 @@ func TestConstructUnsignedTx(t *testing.T) { PkScript: p2trPkScript, }, { - Value: change1.Value + change1Prime.Value, + Value: change1.Value + + change1Prime.Value, PkScript: change1.PkScript, }, }, @@ -513,8 +528,10 @@ func TestConstructUnsignedTx(t *testing.T) { sweeps: []sweep{ { outpoint: op2, - value: btcutil.Amount(change1.Value - 1), - change: change1, + value: btcutil.Amount( + change1.Value - 1, + ), + change: change1, }, }, address: p2trAddress, @@ -569,8 +586,11 @@ func TestConstructUnsignedTx(t *testing.T) { outpoint: op1, value: 1_000_000, change: &wire.TxOut{ - Value: int64(dustLimit - 1), - PkScript: []byte{0xaf, 0xfe}, + Value: int64(dustLimit - 1), + PkScript: []byte{ + 0xaf, + 0xfe, + }, }, }, }, @@ -588,7 +608,8 @@ func TestConstructUnsignedTx(t *testing.T) { outpoint: op1, value: btcutil.SatoshiPerBitcoin, change: &wire.TxOut{ - Value: btcutil.SatoshiPerBitcoin * 0.9, + Value: btcutil.SatoshiPerBitcoin * + 0.9, PkScript: change1Pkscript, }, }, @@ -606,11 +627,13 @@ func TestConstructUnsignedTx(t *testing.T) { }, TxOut: []*wire.TxOut{ { - Value: btcutil.SatoshiPerBitcoin * 0.08, + Value: btcutil.SatoshiPerBitcoin * + 0.08, PkScript: p2trPkScript, }, { - Value: btcutil.SatoshiPerBitcoin * 0.9, + Value: btcutil.SatoshiPerBitcoin * + 0.9, PkScript: change1.PkScript, }, }, diff --git a/sweepbatcher/sweep_batcher.go b/sweepbatcher/sweep_batcher.go index 05b273863..c9a16b9d2 100644 --- a/sweepbatcher/sweep_batcher.go +++ b/sweepbatcher/sweep_batcher.go @@ -191,8 +191,8 @@ type PresignedHelper interface { // These rules are enforced by CheckSignedTx function. SignTx(ctx context.Context, primarySweepID wire.OutPoint, tx *wire.MsgTx, inputAmt btcutil.Amount, - minRelayFee, feeRate chainfee.SatPerKWeight, - loadOnly bool) (*wire.MsgTx, error) + minRelayFee, feeRate chainfee.SatPerKWeight, loadOnly bool) ( + *wire.MsgTx, error) // CleanupTransactions removes all transactions related to any of the // outpoints. Should be called after sweep batch tx is reorg-safely @@ -218,8 +218,8 @@ type InitialDelayProvider func(ctx context.Context, numSweeps int, value btcutil.Amount, fast bool) (time.Duration, error) // zeroInitialDelay returns no delay for any sweeps. -func zeroInitialDelay(_ context.Context, _ int, - _ btcutil.Amount, _ bool) (time.Duration, error) { +func zeroInitialDelay(_ context.Context, _ int, _ btcutil.Amount, + _ bool) (time.Duration, error) { return 0, nil } @@ -595,8 +595,8 @@ func NewBatcher(wallet lndclient.WalletKitClient, opts ...BatcherOption) *Batcher { badTx1, err := chainhash.NewHashFromStr( - "7028bdac753a254785d29506f311abcda323706b531345105f38999" + - "aecd6f3d1", + "7028bdac753a254785d29506f311abcda323706b531345105f38999aecd" + + "6f3d1", ) if err != nil { panic(err) @@ -626,8 +626,10 @@ func NewBatcher(wallet lndclient.WalletKitClient, } if cfg.customMuSig2Signer != nil && musig2ServerSigner != nil { - panic("customMuSig2Signer must not be used with " + - "musig2ServerSigner") + panic( + "customMuSig2Signer must not be used with " + + "musig2ServerSigner", + ) } return &Batcher{ @@ -747,10 +749,12 @@ func (b *Batcher) PresignSweepsGroup(ctx context.Context, inputs []Input, if err != nil { return fmt.Errorf("txscript.PayToAddrScript failed: %w", err) } - infof("PresignSweepsGroup: nextBlockFeeRate is %v, inputs: %v, "+ - "destAddress: %v, destPkscript: %x sweepTimeout: %d", + infof( + "PresignSweepsGroup: nextBlockFeeRate is %v, inputs: %v, "+ + "destAddress: %v, destPkscript: %x sweepTimeout: %d", nextBlockFeeRate, inputs, destAddress, destPkscript, - sweepTimeout) + sweepTimeout, + ) sweeps := make([]sweep, len(inputs)) for i, input := range inputs { @@ -845,9 +849,11 @@ func (b *Batcher) AddSweep(ctx context.Context, sweepReq *SweepRequest) error { } } - infof("Batcher adding sweep group of %d sweeps with primarySweep %x, "+ - "presigned=%v, fully_confirmed=%v", len(sweeps), - sweep.swapHash[:6], sweep.presigned, completed) + infof( + "Batcher adding sweep group of %d sweeps with primarySweep "+ + "%x, presigned=%v, fully_confirmed=%v", len(sweeps), + sweep.swapHash[:6], sweep.presigned, completed, + ) req := &addSweepsRequest{ sweeps: sweeps, @@ -873,6 +879,7 @@ func (b *Batcher) testRunInEventLoop(ctx context.Context, handler func()) { handler() return + default: } @@ -953,19 +960,21 @@ func (b *Batcher) handleSweeps(ctx context.Context, sweeps []*sweep, // If the sweep has already been completed in a confirmed batch then we // can't attach its notifier to the batch as that is no longer running. // Instead we directly detect and return the spend here. We cannot reuse - // the values gathered in AddSweep because the sweep status may change in - // the meantime. If the status flips while handleSweeps is running, the - // re-add path above will handle it. A batch is removed from b.batches - // only after the code below finds the sweep fully confirmed and switches - // to the monitorSpendAndNotify path. + // the values gathered in AddSweep because the sweep status may change + // in the meantime. If the status flips while handleSweeps is running, + // the re-add path above will handle it. A batch is removed from + // b.batches only after the code below finds the sweep fully confirmed + // and switches to the monitorSpendAndNotify path. completed, err := b.store.GetSweepStatus(ctx, sweep.outpoint) if err != nil { return fmt.Errorf("failed to get the status of sweep %v: %w", sweep.outpoint, err) } - debugf("Status of the sweep group of %d sweeps with primarySweep %x: "+ - "presigned=%v, fully_confirmed=%v", len(sweeps), - sweep.swapHash[:6], sweep.presigned, completed) + debugf( + "Status of the sweep group of %d sweeps with primarySweep "+ + "%x: presigned=%v, fully_confirmed=%v", len(sweeps), + sweep.swapHash[:6], sweep.presigned, completed, + ) if completed { // Verify that the parent batch is confirmed. Note that a batch // is only considered confirmed after it has received three @@ -976,16 +985,21 @@ func (b *Batcher) handleSweeps(ctx context.Context, sweeps []*sweep, "sweep %x: %w", sweep.swapHash[:6], err) } - debugf("Status of the parent batch of the sweep group of %d "+ - "sweeps with primarySweep %x: confirmed=%v", - len(sweeps), sweep.swapHash[:6], parentBatch.Confirmed) + debugf( + "Status of the parent batch of the sweep group of "+ + "%d sweeps with primarySweep %x: confirmed=%v", + len(sweeps), sweep.swapHash[:6], parentBatch.Confirmed, + ) // Batch + sweeps are persisted atomically, so if the sweep // shows as completed its parent batch must be confirmed. if parentBatch.Confirmed { - debugf("Sweep group of %d sweeps with primarySweep %x "+ - "is fully confirmed, switching directly to "+ - "monitoring", len(sweeps), sweep.swapHash[:6]) + debugf( + "Sweep group of %d sweeps with primarySweep "+ + "%x is fully confirmed, switching "+ + "directly to monitoring", len(sweeps), + sweep.swapHash[:6], + ) return b.monitorSpendAndNotify( ctx, sweeps, parentBatch.ID, notifier, @@ -1002,12 +1016,16 @@ func (b *Batcher) handleSweeps(ctx context.Context, sweeps []*sweep, // Try to run the greedy algorithm of batch selection to minimize costs. err = b.greedyAddSweeps(ctx, sweeps) if err == nil { + // The greedy algorithm succeeded. return nil } - warnf("Greedy batch selection algorithm failed for sweep %x: %v."+ - " Falling back to old approach.", sweep.swapHash[:6], err) + warnf( + "Greedy batch selection algorithm failed for sweep %x: %v. "+ + "Falling back to old approach.", sweep.swapHash[:6], + err, + ) // If one of the batches accepts the sweep, we provide it to that batch. for _, batch := range b.batches { @@ -1131,15 +1149,19 @@ func (b *Batcher) spinUpBatchFromDB(ctx context.Context, batch *batch) error { dbSweeps = filterDbSweeps(b.skippedTxns, dbSweeps) if len(dbSweeps) == 0 { - infof("skipping restored batch %d as it has no sweeps", - batch.id) + infof( + "skipping restored batch %d as it has no sweeps", + batch.id, + ) // It is safe to cancel this empty batch as it has no sweeps // that are not skipped. err := b.store.CancelBatch(ctx, batch.id) if err != nil { - warnf("unable to drop empty batch %d: %v", - batch.id, err) + warnf( + "unable to drop empty batch %d: %v", batch.id, + err, + ) } return nil @@ -1302,8 +1324,10 @@ func (b *Batcher) monitorSpendAndNotify(ctx context.Context, sweeps []*sweep, b.wg.Go(func() { defer cancel() - infof("Batcher monitoring spend for swap %x", - sweep.swapHash[:6]) + infof( + "Batcher monitoring spend for swap %x", + sweep.swapHash[:6], + ) select { case spend := <-spendChan: @@ -1313,8 +1337,7 @@ func (b *Batcher) monitorSpendAndNotify(ctx context.Context, sweeps []*sweep, // for the batch. feePortionPerSweep, roundingDifference := getFeePortionForSweep( - spendTx, len(spendTx.TxIn), - totalSwept, + spendTx, len(spendTx.TxIn), totalSwept, ) // Sum onchain fee across all the sweeps of the swap. @@ -1340,20 +1363,21 @@ func (b *Batcher) monitorSpendAndNotify(ctx context.Context, sweeps []*sweep, // Try to write the update to the notification channel. case notifier.SpendChan <- spendDetail: err := b.monitorConfAndNotify( - ctx, sweep, notifier, spendTx, - fee, + ctx, sweep, notifier, spendTx, fee, ) if err != nil { b.writeToErrChan( - ctx, fmt.Errorf("monitor conf "+ - "failed: %w", err), + ctx, fmt.Errorf( + "monitor conf failed: "+ + "%w", + err), ) } // If a quit signal was provided by the swap, continue. case <-notifier.QuitChan: - // If the context was canceled, stop. + case <-ctx.Done(): } @@ -1364,12 +1388,12 @@ func (b *Batcher) monitorSpendAndNotify(ctx context.Context, sweeps []*sweep, // Try to write the error to the notification // channel. case notifier.SpendErrChan <- err: - // If a quit signal was provided by the swap, // continue. - case <-notifier.QuitChan: + case <-notifier.QuitChan: // If the context was canceled, stop. + case <-ctx.Done(): } @@ -1422,6 +1446,7 @@ func (b *Batcher) monitorConfAndNotify(ctx context.Context, sweep *sweep, ) if err != nil { cancel() + return err } @@ -1452,8 +1477,10 @@ func (b *Batcher) monitorConfAndNotify(ctx context.Context, sweep *sweep, } } - b.writeToErrChan(ctx, fmt.Errorf("confirmations "+ - "monitoring error: %w", err)) + b.writeToErrChan( + ctx, fmt.Errorf("confirmations monitoring "+ + "error: %w", err), + ) case <-reorgChan: // A re-org has been detected, but the batch is fully @@ -1477,11 +1504,12 @@ func (b *Batcher) writeToErrChan(ctx context.Context, err error) { // convertSweep converts a fetched sweep from the database to a sweep that is // ready to be processed by the batcher. It loads swap from loopdb by calling // the method FetchLoopOutSwap. -func (b *Batcher) convertSweep(ctx context.Context, dbSweep *dbSweep) ( - *sweep, error) { +func (b *Batcher) convertSweep(ctx context.Context, dbSweep *dbSweep) (*sweep, + error) { - return b.loadSweep(ctx, dbSweep.SwapHash, dbSweep.Outpoint, - dbSweep.Amount) + return b.loadSweep( + ctx, dbSweep.SwapHash, dbSweep.Outpoint, dbSweep.Amount, + ) } // LoopOutFetcher is used to load LoopOut swaps from the database. @@ -1555,18 +1583,17 @@ func NewSweepFetcherFromSwapStore(swapStore LoopOutFetcher, } // fetchSweeps fetches the sweep related information from the database. -func (b *Batcher) fetchSweeps(ctx context.Context, - sweepReq SweepRequest) ([]*sweep, error) { +func (b *Batcher) fetchSweeps(ctx context.Context, sweepReq SweepRequest) ( + []*sweep, error) { sweeps := make([]*sweep, len(sweepReq.Inputs)) for i, utxo := range sweepReq.Inputs { s, err := b.loadSweep( - ctx, sweepReq.SwapHash, utxo.Outpoint, - utxo.Value, + ctx, sweepReq.SwapHash, utxo.Outpoint, utxo.Value, ) if err != nil { - return nil, fmt.Errorf("failed to load "+ - "sweep %v: %w", utxo.Outpoint, err) + return nil, fmt.Errorf("failed to load sweep %v: %w", + utxo.Outpoint, err) } sweeps[i] = s } @@ -1595,8 +1622,8 @@ func (b *Batcher) loadSweep(ctx context.Context, swapHash lntypes.Hash, // Find minimum fee rate for the sweep. Use customFeeRate if it is // provided, otherwise use wallet's EstimateFeeRate. minFeeRate, err := minimumSweepFeeRate( - ctx, b.customFeeRate, b.wallet, - swapHash, outpoint, s.ConfTarget, + ctx, b.customFeeRate, b.wallet, swapHash, outpoint, + s.ConfTarget, ) if err != nil { return nil, err @@ -1645,8 +1672,8 @@ func minimumSweepFeeRate(ctx context.Context, customFeeRate FeeRateProvider, "for %x: %w", swapHash[:6], err) } if minFeeRate < chainfee.AbsoluteFeePerKwFloor { - return 0, fmt.Errorf("min fee rate too low (%v) for "+ - "%x", minFeeRate, swapHash[:6]) + return 0, fmt.Errorf("min fee rate too low (%v) for %x", + minFeeRate, swapHash[:6]) } return minFeeRate, nil @@ -1658,17 +1685,18 @@ func minimumSweepFeeRate(ctx context.Context, customFeeRate FeeRateProvider, // merged and that LND version becomes a requirement, we can decrease // this from 2 to 1. if sweepConfTarget < 2 { - warnf("Fee estimation was requested for confTarget=%d for "+ - "sweep %x; changing confTarget to 2", sweepConfTarget, - swapHash[:6]) + warnf( + "Fee estimation was requested for confTarget=%d for "+ + "sweep %x; changing confTarget to 2", + sweepConfTarget, swapHash[:6], + ) sweepConfTarget = 2 } minFeeRate, err := wallet.EstimateFeeRate(ctx, sweepConfTarget) if err != nil { - return 0, fmt.Errorf("failed to estimate fee rate "+ - "for %x, confTarget=%d: %w", swapHash[:6], - sweepConfTarget, err) + return 0, fmt.Errorf("failed to estimate fee rate for %x, "+ + "confTarget=%d: %w", swapHash[:6], sweepConfTarget, err) } return minFeeRate, nil diff --git a/sweepbatcher/sweep_batcher_presigned_test.go b/sweepbatcher/sweep_batcher_presigned_test.go index 606c5a887..a5b60dda6 100644 --- a/sweepbatcher/sweep_batcher_presigned_test.go +++ b/sweepbatcher/sweep_batcher_presigned_test.go @@ -105,7 +105,9 @@ func (h *mockPresignedHelper) getTxFeerate(tx *wire.MsgTx, tx2 := tx.Copy() h.sign(tx2) weight := lntypes.WeightUnit( - blockchain.GetTransactionWeight(btcutil.NewTx(tx2)), + blockchain.GetTransactionWeight( + btcutil.NewTx(tx2), + ), ) fee := inputAmt - btcutil.Amount(tx.TxOut[0].Value) @@ -133,8 +135,8 @@ func (h *mockPresignedHelper) DestPkScript(ctx context.Context, // looks for a transaction in presignedBatches satisfying the criteria. func (h *mockPresignedHelper) SignTx(ctx context.Context, primarySweepID wire.OutPoint, tx *wire.MsgTx, inputAmt btcutil.Amount, - minRelayFee, feeRate chainfee.SatPerKWeight, - loadOnly bool) (*wire.MsgTx, error) { + minRelayFee, feeRate chainfee.SatPerKWeight, loadOnly bool) ( + *wire.MsgTx, error) { h.mu.Lock() defer h.mu.Unlock() @@ -218,8 +220,8 @@ const sweepTimeout = 1000 // FetchSweep returns blank SweepInfo. // This method implements SweepFetcher interface. -func (h *mockPresignedHelper) FetchSweep(_ context.Context, - _ lntypes.Hash, utxo wire.OutPoint) (*SweepInfo, error) { +func (h *mockPresignedHelper) FetchSweep(_ context.Context, _ lntypes.Hash, + utxo wire.OutPoint) (*SweepInfo, error) { h.mu.Lock() defer h.mu.Unlock() @@ -237,7 +239,11 @@ func (h *mockPresignedHelper) FetchSweep(_ context.Context, IsPresigned: isPresigned, HTLC: swap.Htlc{ - PkScript: []byte{10, 11, 12}, + PkScript: []byte{ + 10, + 11, + 12, + }, }, Change: change, }, nil @@ -263,11 +269,12 @@ func testPresigned_forgotten_presign(t *testing.T, presignedHelper := newMockPresignedHelper() - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, presignedHelper, - WithCustomFeeRate(customFeeRate), - WithPresignedHelper(presignedHelper)) + batcherStore, presignedHelper, WithCustomFeeRate(customFeeRate), + WithPresignedHelper(presignedHelper), + ) go func() { err := batcher.Run(ctx) @@ -277,7 +284,10 @@ func testPresigned_forgotten_presign(t *testing.T, // Create the first sweep. swapHash1 := lntypes.Hash{1, 1, 1} op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } sweepReq1 := SweepRequest{ @@ -292,8 +302,12 @@ func testPresigned_forgotten_presign(t *testing.T, // This should fail, because the input is offline. presignedHelper.SetOutpointOnline(op1, false) err := batcher.PresignSweepsGroup( - ctx, []Input{{Outpoint: op1, Value: 1_000_000}}, - sweepTimeout, destAddr, nil, + ctx, []Input{ + {Outpoint: op1, Value: 1_000_000}, + }, + sweepTimeout, + destAddr, + nil, ) require.Error(t, err) require.ErrorContains(t, err, "offline") @@ -339,11 +353,12 @@ func testPresigned_input1_offline_then_input2(t *testing.T, presignedHelper := newMockPresignedHelper() - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, presignedHelper, - WithCustomFeeRate(customFeeRate), - WithPresignedHelper(presignedHelper)) + batcherStore, presignedHelper, WithCustomFeeRate(customFeeRate), + WithPresignedHelper(presignedHelper), + ) go func() { err := batcher.Run(ctx) checkBatcherError(t, err) @@ -354,7 +369,10 @@ func testPresigned_input1_offline_then_input2(t *testing.T, // Create the first sweep. swapHash1 := lntypes.Hash{1, 1, 1} op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } sweepReq1 := SweepRequest{ @@ -369,8 +387,12 @@ func testPresigned_input1_offline_then_input2(t *testing.T, // Enable the input and presign. presignedHelper.SetOutpointOnline(op1, true) err = batcher.PresignSweepsGroup( - ctx, []Input{{Outpoint: op1, Value: 1_000_000}}, - sweepTimeout, destAddr, nil, + ctx, []Input{ + {Outpoint: op1, Value: 1_000_000}, + }, + sweepTimeout, + destAddr, + nil, ) require.NoError(t, err) @@ -419,7 +441,10 @@ func testPresigned_input1_offline_then_input2(t *testing.T, // offline, so another input should go to another batch. swapHash2 := lntypes.Hash{2, 2, 2} op2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, } sweepReq2 := SweepRequest{ @@ -432,8 +457,12 @@ func testPresigned_input1_offline_then_input2(t *testing.T, } presignedHelper.SetOutpointOnline(op2, true) err = batcher.PresignSweepsGroup( - ctx, []Input{{Outpoint: op2, Value: 2_000_000}}, - sweepTimeout, destAddr, nil, + ctx, []Input{ + {Outpoint: op2, Value: 2_000_000}, + }, + sweepTimeout, + destAddr, + nil, ) require.NoError(t, err) @@ -489,9 +518,7 @@ func testPresigned_input1_offline_then_input2(t *testing.T, // testPresigned_min_relay_fee tests that online and presigned transactions // comply with min_relay_fee. -func testPresigned_min_relay_fee(t *testing.T, - batcherStore testBatcherStore) { - +func testPresigned_min_relay_fee(t *testing.T, batcherStore testBatcherStore) { defer test.Guard(t)() lnd := test.NewMockLnd() @@ -508,11 +535,12 @@ func testPresigned_min_relay_fee(t *testing.T, presignedHelper := newMockPresignedHelper() - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, presignedHelper, - WithCustomFeeRate(customFeeRate), - WithPresignedHelper(presignedHelper)) + batcherStore, presignedHelper, WithCustomFeeRate(customFeeRate), + WithPresignedHelper(presignedHelper), + ) go func() { err := batcher.Run(ctx) checkBatcherError(t, err) @@ -524,7 +552,10 @@ func testPresigned_min_relay_fee(t *testing.T, // Create the first sweep. swapHash1 := lntypes.Hash{1, 1, 1} op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } sweepReq1 := SweepRequest{ @@ -539,8 +570,12 @@ func testPresigned_min_relay_fee(t *testing.T, // Enable the input and presign. presignedHelper.SetOutpointOnline(op1, true) err := batcher.PresignSweepsGroup( - ctx, []Input{{Outpoint: op1, Value: inputAmt}}, - sweepTimeout, destAddr, nil, + ctx, []Input{ + {Outpoint: op1, Value: inputAmt}, + }, + sweepTimeout, + destAddr, + nil, ) require.NoError(t, err) @@ -635,8 +670,7 @@ func testPresigned_two_inputs_one_goes_offline(t *testing.T, batcher := NewBatcher( lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, presignedHelper, - WithCustomFeeRate(customFeeRate), + batcherStore, presignedHelper, WithCustomFeeRate(customFeeRate), WithPresignedHelper(presignedHelper), ) @@ -650,7 +684,10 @@ func testPresigned_two_inputs_one_goes_offline(t *testing.T, // Create the first sweep. swapHash1 := lntypes.Hash{1, 1, 1} op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } sweepReq1 := SweepRequest{ @@ -663,8 +700,12 @@ func testPresigned_two_inputs_one_goes_offline(t *testing.T, } presignedHelper.SetOutpointOnline(op1, true) err = batcher.PresignSweepsGroup( - ctx, []Input{{Outpoint: op1, Value: 1_000_000}}, - sweepTimeout, destAddr, nil, + ctx, []Input{ + {Outpoint: op1, Value: 1_000_000}, + }, + sweepTimeout, + destAddr, + nil, ) require.NoError(t, err) require.NoError(t, batcher.AddSweep(ctx, &sweepReq1)) @@ -676,7 +717,10 @@ func testPresigned_two_inputs_one_goes_offline(t *testing.T, // Add second sweep. swapHash2 := lntypes.Hash{2, 2, 2} op2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, } sweepReq2 := SweepRequest{ @@ -689,8 +733,12 @@ func testPresigned_two_inputs_one_goes_offline(t *testing.T, } presignedHelper.SetOutpointOnline(op2, true) err = batcher.PresignSweepsGroup( - ctx, []Input{{Outpoint: op2, Value: 2_000_000}}, - sweepTimeout, destAddr, nil, + ctx, []Input{ + {Outpoint: op2, Value: 2_000_000}, + }, + sweepTimeout, + destAddr, + nil, ) require.NoError(t, err) require.NoError(t, batcher.AddSweep(ctx, &sweepReq2)) @@ -771,8 +819,7 @@ func testPresigned_first_publish_fails(t *testing.T, batcher := NewBatcher( lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, presignedHelper, - WithCustomFeeRate(customFeeRate), + batcherStore, presignedHelper, WithCustomFeeRate(customFeeRate), WithPresignedHelper(presignedHelper), ) @@ -786,7 +833,10 @@ func testPresigned_first_publish_fails(t *testing.T, // Create the first sweep. swapHash1 := lntypes.Hash{1, 1, 1} op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } sweepReq1 := SweepRequest{ @@ -799,8 +849,12 @@ func testPresigned_first_publish_fails(t *testing.T, } presignedHelper.SetOutpointOnline(op1, true) err = batcher.PresignSweepsGroup( - ctx, []Input{{Outpoint: op1, Value: 1_000_000}}, - sweepTimeout, destAddr, nil, + ctx, []Input{ + {Outpoint: op1, Value: 1_000_000}, + }, + sweepTimeout, + destAddr, + nil, ) require.NoError(t, err) presignedHelper.SetOutpointOnline(op1, false) @@ -861,9 +915,7 @@ func testPresigned_first_publish_fails(t *testing.T, // locktime logic, so the published transaction has medium feerate (maximum // feerate among transactions without locktime protection). Then blocks are // mined and a transaction with a higher feerate is published. -func testPresigned_locktime(t *testing.T, - batcherStore testBatcherStore) { - +func testPresigned_locktime(t *testing.T, batcherStore testBatcherStore) { defer test.Guard(t)() batchPkScript, err := txscript.PayToAddrScript(destAddr) @@ -894,8 +946,7 @@ func testPresigned_locktime(t *testing.T, batcher := NewBatcher( lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, presignedHelper, - WithCustomFeeRate(customFeeRate), + batcherStore, presignedHelper, WithCustomFeeRate(customFeeRate), WithPresignedHelper(presignedHelper), ) @@ -909,7 +960,10 @@ func testPresigned_locktime(t *testing.T, // Create the first sweep. swapHash1 := lntypes.Hash{1, 1, 1} op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } sweepReq1 := SweepRequest{ @@ -922,8 +976,12 @@ func testPresigned_locktime(t *testing.T, } presignedHelper.SetOutpointOnline(op1, true) err = batcher.PresignSweepsGroup( - ctx, []Input{{Outpoint: op1, Value: 1_000_000}}, - sweepTimeout, destAddr, nil, + ctx, []Input{ + {Outpoint: op1, Value: 1_000_000}, + }, + sweepTimeout, + destAddr, + nil, ) require.NoError(t, err) presignedHelper.SetOutpointOnline(op1, false) @@ -978,8 +1036,7 @@ func testPresigned_presigned_group(t *testing.T, batcher := NewBatcher( lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, presignedHelper, - WithCustomFeeRate(customFeeRate), + batcherStore, presignedHelper, WithCustomFeeRate(customFeeRate), WithPresignedHelper(presignedHelper), ) @@ -991,11 +1048,17 @@ func testPresigned_presigned_group(t *testing.T, // Create a swap of two sweeps. swapHash1 := lntypes.Hash{1, 1, 1} op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } op2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, } group1 := []Input{ @@ -1029,11 +1092,16 @@ func testPresigned_presigned_group(t *testing.T, require.NoError(t, err) // Add the sweep, triggering the publish attempt. - require.NoError(t, batcher.AddSweep(ctx, &SweepRequest{ - SwapHash: swapHash1, - Inputs: group1, - Notifier: &dummyNotifier, - })) + require.NoError( + t, + batcher.AddSweep( + ctx, &SweepRequest{ + SwapHash: swapHash1, + Inputs: group1, + Notifier: &dummyNotifier, + }, + ), + ) // Since a batch was created we check that it registered for its primary // sweep's spend. @@ -1056,11 +1124,17 @@ func testPresigned_presigned_group(t *testing.T, // Add another group of sweeps. swapHash2 := lntypes.Hash{2, 2, 2} op3 := wire.OutPoint{ - Hash: chainhash.Hash{3, 3}, + Hash: chainhash.Hash{ + 3, + 3, + }, Index: 3, } op4 := wire.OutPoint{ - Hash: chainhash.Hash{4, 4}, + Hash: chainhash.Hash{ + 4, + 4, + }, Index: 4, } group2 := []Input{ @@ -1083,11 +1157,16 @@ func testPresigned_presigned_group(t *testing.T, require.NoError(t, err) // Add the sweep. It should go to the same batch. - require.NoError(t, batcher.AddSweep(ctx, &SweepRequest{ - SwapHash: swapHash2, - Inputs: group2, - Notifier: &dummyNotifier, - })) + require.NoError( + t, + batcher.AddSweep( + ctx, &SweepRequest{ + SwapHash: swapHash2, + Inputs: group2, + Notifier: &dummyNotifier, + }, + ), + ) // Mine a blocks to trigger republishing. require.NoError(t, lnd.NotifyHeight(601)) @@ -1112,11 +1191,17 @@ func testPresigned_presigned_group(t *testing.T, swapHash3 := lntypes.Hash{3, 3, 3} op5 := wire.OutPoint{ - Hash: chainhash.Hash{5, 5}, + Hash: chainhash.Hash{ + 5, + 5, + }, Index: 5, } op6 := wire.OutPoint{ - Hash: chainhash.Hash{6, 6}, + Hash: chainhash.Hash{ + 6, + 6, + }, Index: 6, } group3 := []Input{ @@ -1139,11 +1224,16 @@ func testPresigned_presigned_group(t *testing.T, require.NoError(t, err) // Add the sweep. It should go to the same batch. - require.NoError(t, batcher.AddSweep(ctx, &SweepRequest{ - SwapHash: swapHash3, - Inputs: group3, - Notifier: &dummyNotifier, - })) + require.NoError( + t, + batcher.AddSweep( + ctx, &SweepRequest{ + SwapHash: swapHash3, + Inputs: group3, + Notifier: &dummyNotifier, + }, + ), + ) // Since a batch was created we check that it registered for its primary // sweep's spend. @@ -1190,8 +1280,7 @@ func testPresigned_presigned_group_with_change(t *testing.T, batcher := NewBatcher( lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, presignedHelper, - WithCustomFeeRate(customFeeRate), + batcherStore, presignedHelper, WithCustomFeeRate(customFeeRate), WithPresignedHelper(presignedHelper), ) @@ -1203,11 +1292,17 @@ func testPresigned_presigned_group_with_change(t *testing.T, // Create a swap of two sweeps. swapHash1 := lntypes.Hash{1, 1, 1} op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } op2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, } group1 := []Input{ @@ -1221,8 +1316,11 @@ func testPresigned_presigned_group_with_change(t *testing.T, }, } change := &wire.TxOut{ - Value: 500_000, - PkScript: []byte{0xaf, 0xfe}, + Value: 500_000, + PkScript: []byte{ + 0xaf, + 0xfe, + }, } presignedHelper.setChangeForPrimaryDeposit(op1, change) @@ -1293,8 +1391,7 @@ func testPresigned_fee_portion_with_change(t *testing.T, batcher := NewBatcher( lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, presignedHelper, - WithCustomFeeRate(customFeeRate), + batcherStore, presignedHelper, WithCustomFeeRate(customFeeRate), WithPresignedHelper(presignedHelper), ) @@ -1305,7 +1402,10 @@ func testPresigned_fee_portion_with_change(t *testing.T, swapHash := lntypes.Hash{2, 2, 2} op := wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, } group := []Input{ @@ -1315,16 +1415,21 @@ func testPresigned_fee_portion_with_change(t *testing.T, }, } change := &wire.TxOut{ - Value: 250_000, - PkScript: []byte{0xca, 0xfe}, + Value: 250_000, + PkScript: []byte{ + 0xca, + 0xfe, + }, } presignedHelper.setChangeForPrimaryDeposit(op, change) presignedHelper.SetOutpointOnline(op, true) - require.NoError(t, batcher.PresignSweepsGroup( - ctx, group, sweepTimeout, destAddr, change, - )) + require.NoError( + t, batcher.PresignSweepsGroup( + ctx, group, sweepTimeout, destAddr, change, + ), + ) spendChan := make(chan *SpendDetail, 1) confChan := make(chan *ConfDetail, 1) @@ -1336,11 +1441,16 @@ func testPresigned_fee_portion_with_change(t *testing.T, QuitChan: make(chan bool, 1), } - require.NoError(t, batcher.AddSweep(ctx, &SweepRequest{ - SwapHash: swapHash, - Inputs: group, - Notifier: notifier, - })) + require.NoError( + t, + batcher.AddSweep( + ctx, &SweepRequest{ + SwapHash: swapHash, + Inputs: group, + Notifier: notifier, + }, + ), + ) spendReg := <-lnd.RegisterSpendChannel require.NotNil(t, spendReg) @@ -1398,8 +1508,10 @@ func testPresigned_fee_portion_with_change(t *testing.T, require.Equal(t, expectedFee, spend.OnChainFeePortion) confReg := <-lnd.RegisterConfChannel - require.True(t, bytes.Equal(tx.TxOut[0].PkScript, confReg.PkScript) || - bytes.Equal(tx.TxOut[1].PkScript, confReg.PkScript)) + require.True( + t, bytes.Equal(tx.TxOut[0].PkScript, confReg.PkScript) || + bytes.Equal(tx.TxOut[1].PkScript, confReg.PkScript), + ) require.NoError( t, lnd.NotifyHeight(spendReg.HeightHint+batchConfHeight+1), @@ -1410,6 +1522,7 @@ func testPresigned_fee_portion_with_change(t *testing.T, select { case <-presignedHelper.cleanupCalled: return true + default: return false } @@ -1419,9 +1532,10 @@ func testPresigned_fee_portion_with_change(t *testing.T, require.Equal(t, expectedFee, conf.OnChainFeePortion) } -// testPresigned_presigned_group_with_identical_change_pkscript tests passing multiple sweeps to -// the method PresignSweepsGroup. It tests that a change output of a primary -// deposit sweep is properly added to the presigned transaction. +// testPresigned_presigned_group_with_identical_change_pkscript tests passing +// multiple sweeps to the method PresignSweepsGroup. It tests that a change +// output of a primary deposit sweep is properly added to the presigned +// transaction. func testPresigned_presigned_group_with_identical_change_pkscript(t *testing.T, batcherStore testBatcherStore) { @@ -1446,8 +1560,7 @@ func testPresigned_presigned_group_with_identical_change_pkscript(t *testing.T, batcher := NewBatcher( lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, presignedHelper, - WithCustomFeeRate(customFeeRate), + batcherStore, presignedHelper, WithCustomFeeRate(customFeeRate), WithPresignedHelper(presignedHelper), ) @@ -1460,11 +1573,17 @@ func testPresigned_presigned_group_with_identical_change_pkscript(t *testing.T, swapHash1 := lntypes.Hash{1, 1, 1} swapHash2 := lntypes.Hash{2, 2, 2} op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } op2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, } group1 := []Input{ @@ -1474,8 +1593,11 @@ func testPresigned_presigned_group_with_identical_change_pkscript(t *testing.T, }, } change1 := &wire.TxOut{ - Value: 500_000, - PkScript: []byte{0xaf, 0xfe}, + Value: 500_000, + PkScript: []byte{ + 0xaf, + 0xfe, + }, } group2 := []Input{ { @@ -1484,8 +1606,11 @@ func testPresigned_presigned_group_with_identical_change_pkscript(t *testing.T, }, } change2 := &wire.TxOut{ - Value: 600_000, - PkScript: []byte{0xaf, 0xfe}, + Value: 600_000, + PkScript: []byte{ + 0xaf, + 0xfe, + }, } presignedHelper.setChangeForPrimaryDeposit(op1, change1) @@ -1575,8 +1700,7 @@ func testPresigned_presigned_group_with_dust_main_output(t *testing.T, batcher := NewBatcher( lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, presignedHelper, - WithCustomFeeRate(customFeeRate), + batcherStore, presignedHelper, WithCustomFeeRate(customFeeRate), WithPresignedHelper(presignedHelper), ) @@ -1588,7 +1712,10 @@ func testPresigned_presigned_group_with_dust_main_output(t *testing.T, // Create a swap of two sweeps. swapHash1 := lntypes.Hash{1, 1, 1} op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } inputValue := int64(1_000_000) @@ -1614,8 +1741,11 @@ func testPresigned_presigned_group_with_dust_main_output(t *testing.T, change := &wire.TxOut{ // If "+1" is removed, the group would be added successfully. - Value: inputValue - dustLimit - clampedFee + 1, - PkScript: []byte{0xaf, 0xfe}, + Value: inputValue - dustLimit - clampedFee + 1, + PkScript: []byte{ + 0xaf, + 0xfe, + }, } presignedHelper.setChangeForPrimaryDeposit(op1, change) @@ -1627,10 +1757,12 @@ func testPresigned_presigned_group_with_dust_main_output(t *testing.T, err = batcher.PresignSweepsGroup( ctx, group1, sweepTimeout, destAddr, change, ) - require.EqualError(t, err, "failed to construct unsigned tx for "+ - "feeRate 166 sat/kw: batch amount 0.01000000 BTC is < the "+ - "sum of change outputs 0.00999634 BTC plus fee "+ - "0.00000073 BTC and dust limit 0.00000294 BTC") + require.EqualError( + t, err, "failed to construct unsigned tx for feeRate 166 "+ + "sat/kw: batch amount 0.01000000 BTC is < the sum "+ + "of change outputs 0.00999634 BTC plus fee "+ + "0.00000073 BTC and dust limit 0.00000294 BTC", + ) // Add the sweep, triggering the publishing attempt. err = batcher.AddSweep(ctx, &SweepRequest{ @@ -1687,8 +1819,7 @@ func testPresigned_presigned_group_with_dust_below_relay_fee(t *testing.T, batcher := NewBatcher( lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, presignedHelper, - WithCustomFeeRate(customFeeRate), + batcherStore, presignedHelper, WithCustomFeeRate(customFeeRate), WithPresignedHelper(presignedHelper), ) @@ -1700,7 +1831,10 @@ func testPresigned_presigned_group_with_dust_below_relay_fee(t *testing.T, // Create a swap of two sweeps. swapHash1 := lntypes.Hash{1, 1, 1} op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } inputValue := int64(1_000_000) @@ -1713,8 +1847,11 @@ func testPresigned_presigned_group_with_dust_below_relay_fee(t *testing.T, dustLimit := int64(utils.DustLimitForPkScript(batchPkScript)) change := &wire.TxOut{ // Note that there is no space for fee. - Value: inputValue - dustLimit, - PkScript: []byte{0xaf, 0xfe}, + Value: inputValue - dustLimit, + PkScript: []byte{ + 0xaf, + 0xfe, + }, } presignedHelper.setChangeForPrimaryDeposit(op1, change) @@ -1726,9 +1863,12 @@ func testPresigned_presigned_group_with_dust_below_relay_fee(t *testing.T, err = batcher.PresignSweepsGroup( ctx, group1, sweepTimeout, destAddr, change, ) - require.EqualError(t, err, "failed to construct unsigned tx for "+ - "feeRate 253 sat/kw: failed to clamp batch fee: clamped "+ - "fee rate 132 sat/kw is less than minimum relay fee 253 sat/kw") + require.EqualError( + t, err, "failed to construct unsigned tx for feeRate 253 "+ + "sat/kw: failed to clamp batch fee: clamped fee "+ + "rate 132 sat/kw is less than minimum relay fee "+ + "253 sat/kw", + ) // Add the sweep, triggering the publishing attempt. err = batcher.AddSweep(ctx, &SweepRequest{ @@ -1763,8 +1903,7 @@ func testPresigned_presigned_group_with_dust_change(t *testing.T, batcher := NewBatcher( lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, presignedHelper, - WithCustomFeeRate(customFeeRate), + batcherStore, presignedHelper, WithCustomFeeRate(customFeeRate), WithPresignedHelper(presignedHelper), ) @@ -1776,11 +1915,17 @@ func testPresigned_presigned_group_with_dust_change(t *testing.T, // Create a swap of two sweeps. swapHash1 := lntypes.Hash{1, 1, 1} op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } op2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, } group1 := []Input{ @@ -1811,9 +1956,11 @@ func testPresigned_presigned_group_with_dust_change(t *testing.T, err := batcher.PresignSweepsGroup( ctx, group1, sweepTimeout, destAddr, change, ) - require.EqualError(t, err, "failed to construct unsigned tx for "+ - "feeRate 253 sat/kw: output 0.00000476 BTC is below dust "+ - "limit 0.00000477 BTC") + require.EqualError( + t, err, "failed to construct unsigned tx for feeRate 253 "+ + "sat/kw: output 0.00000476 BTC is below dust limit "+ + "0.00000477 BTC", + ) // Add the sweep, triggering the publishing attempt. err = batcher.AddSweep(ctx, &SweepRequest{ @@ -1907,8 +2054,7 @@ func testPresigned_presigned_and_regular_sweeps(t *testing.T, store testStore, batcher := NewBatcher( lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepFetcher, - WithCustomFeeRate(customFeeRate), + batcherStore, sweepFetcher, WithCustomFeeRate(customFeeRate), WithPresignedHelper(presignedHelper), ) @@ -1924,7 +2070,10 @@ func testPresigned_presigned_and_regular_sweeps(t *testing.T, store testStore, ///////////////////////////////////// swapHash1 := lntypes.Hash{1, 1, 1} op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } sweepReq1 := SweepRequest{ @@ -1944,7 +2093,9 @@ func testPresigned_presigned_and_regular_sweeps(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{1}, + Preimage: lntypes.Preimage{ + 1, + }, }, DestAddr: destAddr, @@ -1973,7 +2124,10 @@ func testPresigned_presigned_and_regular_sweeps(t *testing.T, store testStore, /////////////////////////////////////// swapHash2 := lntypes.Hash{2, 2, 2} op2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, } @@ -1985,7 +2139,9 @@ func testPresigned_presigned_and_regular_sweeps(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{2}, + Preimage: lntypes.Preimage{ + 2, + }, }, DestAddr: destAddr, @@ -2007,8 +2163,12 @@ func testPresigned_presigned_and_regular_sweeps(t *testing.T, store testStore, } presignedHelper.SetOutpointOnline(op2, true) err = batcher.PresignSweepsGroup( - ctx, []Input{{Outpoint: op2, Value: 2_000_000}}, - sweepTimeout, destAddr, nil, + ctx, []Input{ + {Outpoint: op2, Value: 2_000_000}, + }, + sweepTimeout, + destAddr, + nil, ) require.NoError(t, err) require.NoError(t, batcher.AddSweep(ctx, &sweepReq2)) @@ -2028,7 +2188,10 @@ func testPresigned_presigned_and_regular_sweeps(t *testing.T, store testStore, ////////////////////////////////////// swapHash3 := lntypes.Hash{3, 3, 3} op3 := wire.OutPoint{ - Hash: chainhash.Hash{3, 3}, + Hash: chainhash.Hash{ + 3, + 3, + }, Index: 3, } sweepReq3 := SweepRequest{ @@ -2048,7 +2211,9 @@ func testPresigned_presigned_and_regular_sweeps(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{3}, + Preimage: lntypes.Preimage{ + 3, + }, }, DestAddr: destAddr, @@ -2068,7 +2233,10 @@ func testPresigned_presigned_and_regular_sweeps(t *testing.T, store testStore, //////////////////////////////////////// swapHash4 := lntypes.Hash{4, 4, 4} op4 := wire.OutPoint{ - Hash: chainhash.Hash{4, 4}, + Hash: chainhash.Hash{ + 4, + 4, + }, Index: 4, } @@ -2080,7 +2248,9 @@ func testPresigned_presigned_and_regular_sweeps(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{4}, + Preimage: lntypes.Preimage{ + 4, + }, }, DestAddr: destAddr, @@ -2102,8 +2272,12 @@ func testPresigned_presigned_and_regular_sweeps(t *testing.T, store testStore, } presignedHelper.SetOutpointOnline(op4, true) err = batcher.PresignSweepsGroup( - ctx, []Input{{Outpoint: op4, Value: 3_000_000}}, - sweepTimeout, destAddr, nil, + ctx, []Input{ + {Outpoint: op4, Value: 3_000_000}, + }, + sweepTimeout, + destAddr, + nil, ) require.NoError(t, err) require.NoError(t, batcher.AddSweep(ctx, &sweepReq4)) @@ -2180,8 +2354,7 @@ func testPresigned_purging(t *testing.T, numSwaps, numConfirmedSwaps int, batcher := NewBatcher( lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, presignedHelper, - WithCustomFeeRate(customFeeRate), + batcherStore, presignedHelper, WithCustomFeeRate(customFeeRate), WithPresignedHelper(presignedHelper), ) @@ -2205,7 +2378,9 @@ func testPresigned_purging(t *testing.T, numSwaps, numConfirmedSwaps int, group := make([]Input, sweepsPerSwap) for j := range sweepsPerSwap { ops[j] = wire.OutPoint{ - Hash: chainhash.Hash{byte(1 + i*2 + j)}, + Hash: chainhash.Hash{ + byte(1 + i*2 + j), + }, Index: uint32(1 + i*2 + j), } allOps = append(allOps, ops[j]) @@ -2242,11 +2417,16 @@ func testPresigned_purging(t *testing.T, numSwaps, numConfirmedSwaps int, } // Add the sweep, triggering the publish attempt. - require.NoError(t, batcher.AddSweep(ctx, &SweepRequest{ - SwapHash: swapHash, - Inputs: group, - Notifier: notifier, - })) + require.NoError( + t, + batcher.AddSweep( + ctx, &SweepRequest{ + SwapHash: swapHash, + Inputs: group, + Notifier: notifier, + }, + ), + ) // For the first group it should register for the sweep's spend // and publish a transaction. @@ -2293,11 +2473,16 @@ func testPresigned_purging(t *testing.T, numSwaps, numConfirmedSwaps int, require.NoError(t, err) // Add the sweep, triggering the publishing attempt. - require.NoError(t, batcher.AddSweep(ctx, &SweepRequest{ - SwapHash: swapHash, - Inputs: group, - Notifier: &dummyNotifier, - })) + require.NoError( + t, + batcher.AddSweep( + ctx, &SweepRequest{ + SwapHash: swapHash, + Inputs: group, + Notifier: &dummyNotifier, + }, + ), + ) <-lnd.RegisterSpendChannel tx := <-lnd.TxPublishChannel @@ -2353,9 +2538,12 @@ func testPresigned_purging(t *testing.T, numSwaps, numConfirmedSwaps int, } <-lnd.RegisterConfChannel - require.NoError(t, lnd.NotifyHeight( - int32(601+numSwaps+1+batchConfHeight), - )) + require.NoError( + t, + lnd.NotifyHeight( + int32(601+numSwaps+1+batchConfHeight), + ), + ) lnd.ConfChannel <- &chainntnfs.TxConfirmation{ Tx: tx, } @@ -2399,11 +2587,16 @@ func testPresigned_purging(t *testing.T, numSwaps, numConfirmedSwaps int, } // Add the sweep, triggering the publish attempt. - require.NoError(t, batcher.AddSweep(ctx, &SweepRequest{ - SwapHash: swapHashes[i], - Inputs: groups[i], - Notifier: notifier, - })) + require.NoError( + t, + batcher.AddSweep( + ctx, &SweepRequest{ + SwapHash: swapHashes[i], + Inputs: groups[i], + Notifier: notifier, + }, + ), + ) spendReg := <-lnd.RegisterSpendChannel spendReg.SpendChannel <- spendDetail @@ -2451,9 +2644,14 @@ func testPresigned_purging(t *testing.T, numSwaps, numConfirmedSwaps int, }, test.Timeout, eventuallyCheckFrequency) // Now trigger batch publishing and inspect the published tx. - require.NoError(t, lnd.NotifyHeight(int32( - 601+numSwaps+1+batchConfHeight+1, - ))) + require.NoError( + t, + lnd.NotifyHeight( + int32( + 601+numSwaps+1+batchConfHeight+1, + ), + ), + ) tx2 := <-lnd.TxPublishChannel wantOps := allOps[numConfirmedSwaps*sweepsPerSwap:] if online { @@ -2512,7 +2710,9 @@ func TestPresigned(t *testing.T) { }) t.Run("identical change pkscript", func(t *testing.T) { - testPresigned_presigned_group_with_identical_change_pkscript(t, NewStoreMock()) + testPresigned_presigned_group_with_identical_change_pkscript( + t, NewStoreMock(), + ) }) t.Run("dust_main_output", func(t *testing.T) { @@ -2528,7 +2728,9 @@ func TestPresigned(t *testing.T) { }) t.Run("dust_change", func(t *testing.T) { - testPresigned_presigned_group_with_dust_change(t, NewStoreMock()) + testPresigned_presigned_group_with_dust_change( + t, NewStoreMock(), + ) }) t.Run("presigned_and_regular_sweeps", func(t *testing.T) { diff --git a/sweepbatcher/sweep_batcher_test.go b/sweepbatcher/sweep_batcher_test.go index a57541f2f..b927f0097 100644 --- a/sweepbatcher/sweep_batcher_test.go +++ b/sweepbatcher/sweep_batcher_test.go @@ -55,6 +55,7 @@ var destAddr = func() btcutil.Address { if err != nil { panic(err) } + return addr }() @@ -82,14 +83,14 @@ func testVerifySchnorrSig(pubKey *btcec.PublicKey, hash, sig []byte) error { func testMuSig2SignSweep(ctx context.Context, protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash, paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte, - prevoutMap map[wire.OutPoint]*wire.TxOut) ( - []byte, []byte, error) { + prevoutMap map[wire.OutPoint]*wire.TxOut) ([]byte, []byte, error) { return nil, nil, nil } var customSignature = func() []byte { sig := [64]byte{10, 20, 30} + return sig[:] }() @@ -204,30 +205,45 @@ func testSweepBatcherBatchCreation(t *testing.T, store testStore, sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams) require.NoError(t, err) - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore) + batcherStore, sweepStore, + ) go func() { err := batcher.Run(ctx) checkBatcherError(t, err) }() op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } op2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, } op3 := wire.OutPoint{ - Hash: chainhash.Hash{3, 3}, + Hash: chainhash.Hash{ + 3, + 3, + }, Index: 3, } // Create a sweep request. sweepReq1 := SweepRequest{ - SwapHash: lntypes.Hash{1, 1, 1}, + SwapHash: lntypes.Hash{ + 1, + 1, + 1, + }, Inputs: []Input{{ Value: 1111, Outpoint: op1, @@ -274,7 +290,11 @@ func testSweepBatcherBatchCreation(t *testing.T, store testStore, // Create a second sweep request that has a timeout distance less than // our configured threshold. sweepReq2 := SweepRequest{ - SwapHash: lntypes.Hash{2, 2, 2}, + SwapHash: lntypes.Hash{ + 2, + 2, + 2, + }, Inputs: []Input{{ Value: 2222, Outpoint: op2, @@ -290,7 +310,9 @@ func testSweepBatcherBatchCreation(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{2}, + Preimage: lntypes.Preimage{ + 2, + }, }, DestAddr: destAddr, @@ -320,7 +342,11 @@ func testSweepBatcherBatchCreation(t *testing.T, store testStore, // Create a third sweep request that has more timeout distance than // the default. sweepReq3 := SweepRequest{ - SwapHash: lntypes.Hash{3, 3, 3}, + SwapHash: lntypes.Hash{ + 3, + 3, + 3, + }, Inputs: []Input{{ Value: 3333, Outpoint: op3, @@ -336,7 +362,9 @@ func testSweepBatcherBatchCreation(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{3}, + Preimage: lntypes.Preimage{ + 3, + }, }, DestAddr: destAddr, @@ -419,9 +447,11 @@ func testFeeBumping(t *testing.T, store testStore, opts = append(opts, WithCustomFeeRate(customFeeRate)) } - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore, opts...) + batcherStore, sweepStore, opts..., + ) go func() { err := batcher.Run(ctx) checkBatcherError(t, err) @@ -429,11 +459,18 @@ func testFeeBumping(t *testing.T, store testStore, // Create a sweep request. sweepReq1 := SweepRequest{ - SwapHash: lntypes.Hash{1, 1, 1}, + SwapHash: lntypes.Hash{ + 1, + 1, + 1, + }, Inputs: []Input{{ Value: 1_000_000, Outpoint: wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, }, }}, @@ -516,9 +553,10 @@ func testTxLabeler(t *testing.T, store testStore, walletKit := &walletKitWrapper{WalletKitClient: lnd.WalletKit} - batcher := NewBatcher(walletKit, lnd.ChainNotifier, lnd.Signer, - testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore) + batcher := NewBatcher( + walletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, + testVerifySchnorrSig, lnd.ChainParams, batcherStore, sweepStore, + ) var ( runErr error @@ -531,11 +569,18 @@ func testTxLabeler(t *testing.T, store testStore, // Create a sweep request. op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } sweepReq1 := SweepRequest{ - SwapHash: lntypes.Hash{1, 1, 1}, + SwapHash: lntypes.Hash{ + 1, + 1, + 1, + }, Inputs: []Input{{ Value: 1111, Outpoint: op1, @@ -580,9 +625,8 @@ func testTxLabeler(t *testing.T, store testStore, for _, btch := range getBatches(ctx, batcher) { btch := btch.snapshot(ctx) if btch.primarySweepID == op1 { - wantLabel = fmt.Sprintf( - "BatchOutSweepSuccess -- %d", btch.id, - ) + wantLabel = fmt.Sprintf("BatchOutSweepSuccess -- %d", + btch.id) } } @@ -600,9 +644,11 @@ func testTxLabeler(t *testing.T, store testStore, } // Now try it with option WithTxLabeler. - batcher = NewBatcher(walletKit, lnd.ChainNotifier, lnd.Signer, - testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore, WithTxLabeler(txLabeler)) + batcher = NewBatcher( + walletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, + testVerifySchnorrSig, lnd.ChainParams, batcherStore, sweepStore, + WithTxLabeler(txLabeler), + ) ctx, cancel = context.WithCancel(context.Background()) wg.Go(func() { @@ -669,9 +715,11 @@ func testPublishErrorHandler(t *testing.T, store testStore, publishErrorChan <- err } - batcher := NewBatcher(walletKit, lnd.ChainNotifier, lnd.Signer, - testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore, WithPublishErrorHandler(errorHandler)) + batcher := NewBatcher( + walletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, + testVerifySchnorrSig, lnd.ChainParams, batcherStore, sweepStore, + WithPublishErrorHandler(errorHandler), + ) var ( runErr error @@ -684,11 +732,18 @@ func testPublishErrorHandler(t *testing.T, store testStore, // Create a sweep request. sweepReq1 := SweepRequest{ - SwapHash: lntypes.Hash{1, 1, 1}, + SwapHash: lntypes.Hash{ + 1, + 1, + 1, + }, Inputs: []Input{{ Value: 1111, Outpoint: wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, }, }}, @@ -754,9 +809,11 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore, sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams) require.NoError(t, err) - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore) + batcherStore, sweepStore, + ) runErrChan := make(chan error) go func() { runErrChan <- batcher.Run(ctx) @@ -764,7 +821,10 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore, // Create a sweep request. op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } const ( @@ -779,7 +839,11 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore, QuitChan: make(chan bool, 1), } sweepReq1 := SweepRequest{ - SwapHash: lntypes.Hash{1, 1, 1}, + SwapHash: lntypes.Hash{ + 1, + 1, + 1, + }, Inputs: []Input{{ Value: inputValue, Outpoint: op1, @@ -866,9 +930,11 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore, require.ErrorIs(t, runErr, testError) // Now launch the batcher again. - batcher = NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher = NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore) + batcherStore, sweepStore, + ) go func() { runErrChan <- batcher.Run(ctx) }() @@ -919,8 +985,12 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore, }, TxOut: []*wire.TxOut{ { - Value: outputValue, - PkScript: []byte{3, 2, 1}, + Value: outputValue, + PkScript: []byte{ + 3, + 2, + 1, + }, }, }, } @@ -974,9 +1044,11 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore, require.ErrorIs(t, runErr, testError) // Now launch the batcher again. - batcher = NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher = NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore) + batcherStore, sweepStore, + ) go func() { runErrChan <- batcher.Run(ctx) }() @@ -1130,9 +1202,11 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore, require.ErrorIs(t, runErr, testError) // Now launch the batcher again. - batcher = NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher = NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore) + batcherStore, sweepStore, + ) go func() { runErrChan <- batcher.Run(ctx) }() @@ -1196,7 +1270,10 @@ func testSweepBatcherSkippedTxns(t *testing.T, store testStore, // Create a sweep request. op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } swapHash := lntypes.Hash{1, 1, 1} @@ -1224,14 +1301,19 @@ func testSweepBatcherSkippedTxns(t *testing.T, store testStore, store.AssertLoopOutStored() // Deliver sweep request to batcher. - require.NoError(t, batcher.AddSweep(ctx, &SweepRequest{ - SwapHash: swapHash, - Inputs: []Input{{ - Value: inputValue, - Outpoint: op1, - }}, - Notifier: &dummyNotifier, - })) + require.NoError( + t, + batcher.AddSweep( + ctx, &SweepRequest{ + SwapHash: swapHash, + Inputs: []Input{{ + Value: inputValue, + Outpoint: op1, + }}, + Notifier: &dummyNotifier, + }, + ), + ) // When batch is successfully created it will execute it's first step, // which leads to a spend monitor of the primary sweep. @@ -1264,9 +1346,11 @@ func testSweepBatcherSkippedTxns(t *testing.T, store testStore, lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, batcherStore, sweepStore, - WithSkippedTxns(map[chainhash.Hash]struct{}{ - op1.Hash: {}, - }), + WithSkippedTxns( + map[chainhash.Hash]struct{}{ + op1.Hash: {}, + }, + ), ) wg.Go(func() { runErr = batcher.Run(ctx) @@ -1276,17 +1360,25 @@ func testSweepBatcherSkippedTxns(t *testing.T, store testStore, // Add the same swap with another outpoint. op2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, } - require.NoError(t, batcher.AddSweep(ctx, &SweepRequest{ - SwapHash: swapHash, - Inputs: []Input{{ - Value: inputValue, - Outpoint: op2, - }}, - Notifier: &dummyNotifier, - })) + require.NoError( + t, + batcher.AddSweep( + ctx, &SweepRequest{ + SwapHash: swapHash, + Inputs: []Input{{ + Value: inputValue, + Outpoint: op2, + }}, + Notifier: &dummyNotifier, + }, + ), + ) // Make sure it is launched in a new batch. <-lnd.RegisterSpendChannel @@ -1363,8 +1455,8 @@ func testDelays(t *testing.T, store testStore, batcherStore testBatcherStore) { publishDelay = 3 * time.Second ) - initialDelayProvider := func(_ context.Context, _ int, - _ btcutil.Amount, fast bool) (time.Duration, error) { + initialDelayProvider := func(_ context.Context, _ int, _ btcutil.Amount, + fast bool) (time.Duration, error) { return initialDelay, nil } @@ -1401,11 +1493,18 @@ func testDelays(t *testing.T, store testStore, batcherStore testBatcherStore) { // Create a sweep request. op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } sweepReq := SweepRequest{ - SwapHash: lntypes.Hash{1, 1, 1}, + SwapHash: lntypes.Hash{ + 1, + 1, + 1, + }, Inputs: []Input{{ Value: 1111, Outpoint: op1, @@ -1676,11 +1775,18 @@ func testDelays(t *testing.T, store testStore, batcherStore testBatcherStore) { // Create a sweep request which is not urgent, but close to. sweepReq2 := SweepRequest{ - SwapHash: lntypes.Hash{2, 2, 2}, + SwapHash: lntypes.Hash{ + 2, + 2, + 2, + }, Inputs: []Input{{ Value: 1111, Outpoint: wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, }, }}, @@ -1698,7 +1804,9 @@ func testDelays(t *testing.T, store testStore, batcherStore testBatcherStore) { HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{2}, + Preimage: lntypes.Preimage{ + 2, + }, }, DestAddr: destAddr, @@ -1753,11 +1861,18 @@ func testDelays(t *testing.T, store testStore, batcherStore testBatcherStore) { // Add another sweep which is urgent. It will go to the same batch // to make sure minimum timeout is calculated properly. sweepReq3 := SweepRequest{ - SwapHash: lntypes.Hash{3, 3, 3}, + SwapHash: lntypes.Hash{ + 3, + 3, + 3, + }, Inputs: []Input{{ Value: 1111, Outpoint: wire.OutPoint{ - Hash: chainhash.Hash{3, 3}, + Hash: chainhash.Hash{ + 3, + 3, + }, Index: 3, }, }}, @@ -1773,7 +1888,9 @@ func testDelays(t *testing.T, store testStore, batcherStore testBatcherStore) { HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{3}, + Preimage: lntypes.Preimage{ + 3, + }, }, DestAddr: destAddr, @@ -1893,7 +2010,10 @@ func testCustomDelays(t *testing.T, store testStore, Inputs: []Input{{ Value: swapSize1, Outpoint: wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, }, }}, @@ -1955,7 +2075,10 @@ func testCustomDelays(t *testing.T, store testStore, Inputs: []Input{{ Value: swapSize2, Outpoint: wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, }, }}, @@ -1970,7 +2093,9 @@ func testCustomDelays(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{2}, + Preimage: lntypes.Preimage{ + 2, + }, }, DestAddr: destAddr, @@ -2057,8 +2182,8 @@ func testMaxSweepsPerBatch(t *testing.T, store testStore, muSig2SignSweep := func(ctx context.Context, protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash, paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte, - prevoutMap map[wire.OutPoint]*wire.TxOut) ( - []byte, []byte, error) { + prevoutMap map[wire.OutPoint]*wire.TxOut) ([]byte, []byte, + error) { return nil, nil, fmt.Errorf("test error") } @@ -2067,10 +2192,9 @@ func testMaxSweepsPerBatch(t *testing.T, store testStore, const publishDelay = 3 * time.Second batcher := NewBatcher( - lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, - muSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore, WithPublishDelay(publishDelay), - WithClock(testClock), + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, muSig2SignSweep, + testVerifySchnorrSig, lnd.ChainParams, batcherStore, sweepStore, + WithPublishDelay(publishDelay), WithClock(testClock), ) var wg sync.WaitGroup @@ -2094,7 +2218,9 @@ func testMaxSweepsPerBatch(t *testing.T, store testStore, swapHash := preimage.Hash() outpoint := wire.OutPoint{ - Hash: chainhash.Hash{byte(i + 1)}, + Hash: chainhash.Hash{ + byte(i + 1), + }, Index: uint32(i + 1), } @@ -2173,7 +2299,9 @@ func testMaxSweepsPerBatch(t *testing.T, store testStore, // Make sure the transaction size is standard. weight := lntypes.WeightUnit( - blockchain.GetTransactionWeight(btcutil.NewTx(tx)), + blockchain.GetTransactionWeight( + btcutil.NewTx(tx), + ), ) require.Less(t, weight, maxWeight) t.Logf("tx weight: %v", weight) @@ -2205,9 +2333,11 @@ func testSweepBatcherSweepReentry(t *testing.T, store testStore, sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams) require.NoError(t, err) - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore) + batcherStore, sweepStore, + ) go func() { err := batcher.Run(ctx) checkBatcherError(t, err) @@ -2216,12 +2346,19 @@ func testSweepBatcherSweepReentry(t *testing.T, store testStore, // Create some sweep requests with timeouts not too far away, in order // to enter the same batch. op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } value1 := btcutil.Amount(1111) sweepReq1 := SweepRequest{ - SwapHash: lntypes.Hash{1, 1, 1}, + SwapHash: lntypes.Hash{ + 1, + 1, + 1, + }, Inputs: []Input{{ Value: value1, Outpoint: op1, @@ -2246,11 +2383,18 @@ func testSweepBatcherSweepReentry(t *testing.T, store testStore, store.AssertLoopOutStored() sweepReq2 := SweepRequest{ - SwapHash: lntypes.Hash{2, 2, 2}, + SwapHash: lntypes.Hash{ + 2, + 2, + 2, + }, Inputs: []Input{{ Value: 2222, Outpoint: wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, }, }}, @@ -2265,7 +2409,9 @@ func testSweepBatcherSweepReentry(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{2}, + Preimage: lntypes.Preimage{ + 2, + }, }, DestAddr: destAddr, SwapInvoice: swapInvoice, @@ -2277,11 +2423,18 @@ func testSweepBatcherSweepReentry(t *testing.T, store testStore, store.AssertLoopOutStored() sweepReq3 := SweepRequest{ - SwapHash: lntypes.Hash{3, 3, 3}, + SwapHash: lntypes.Hash{ + 3, + 3, + 3, + }, Inputs: []Input{{ Value: 3333, Outpoint: wire.OutPoint{ - Hash: chainhash.Hash{3, 3}, + Hash: chainhash.Hash{ + 3, + 3, + }, Index: 3, }, }}, @@ -2296,7 +2449,9 @@ func testSweepBatcherSweepReentry(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{3}, + Preimage: lntypes.Preimage{ + 3, + }, }, DestAddr: destAddr, SwapInvoice: swapInvoice, @@ -2375,10 +2530,16 @@ func testSweepBatcherSweepReentry(t *testing.T, store testStore, }, TxOut: []*wire.TxOut{ { - Value: int64(value1.ToUnit( - btcutil.AmountSatoshi, - )), - PkScript: []byte{3, 2, 1}, + Value: int64( + value1.ToUnit( + btcutil.AmountSatoshi, + ), + ), + PkScript: []byte{ + 3, + 2, + 1, + }, }, }, } @@ -2464,9 +2625,11 @@ func testSweepBatcherGroup(t *testing.T, store testStore, sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams) require.NoError(t, err) - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore) + batcherStore, sweepStore, + ) go func() { err := batcher.Run(ctx) checkBatcherError(t, err) @@ -2475,11 +2638,17 @@ func testSweepBatcherGroup(t *testing.T, store testStore, swapHash := lntypes.Hash{1, 1, 1} outpoint1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } outpoint2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, } @@ -2501,7 +2670,11 @@ func testSweepBatcherGroup(t *testing.T, store testStore, // Create sweep request with a group of two UTXOs. sweepReq := SweepRequest{ - SwapHash: lntypes.Hash{1, 1, 1}, + SwapHash: lntypes.Hash{ + 1, + 1, + 1, + }, Inputs: []Input{ { Outpoint: outpoint1, @@ -2546,30 +2719,45 @@ func testSweepBatcherNonWalletAddr(t *testing.T, store testStore, sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams) require.NoError(t, err) - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore) + batcherStore, sweepStore, + ) go func() { err := batcher.Run(ctx) checkBatcherError(t, err) }() op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } op2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, } op3 := wire.OutPoint{ - Hash: chainhash.Hash{3, 3}, + Hash: chainhash.Hash{ + 3, + 3, + }, Index: 3, } // Create a sweep request. sweepReq1 := SweepRequest{ - SwapHash: lntypes.Hash{1, 1, 1}, + SwapHash: lntypes.Hash{ + 1, + 1, + 1, + }, Inputs: []Input{{ Value: 1111, Outpoint: op1, @@ -2616,7 +2804,11 @@ func testSweepBatcherNonWalletAddr(t *testing.T, store testStore, // Create a second sweep request that has a timeout distance less than // our configured threshold. sweepReq2 := SweepRequest{ - SwapHash: lntypes.Hash{2, 2, 2}, + SwapHash: lntypes.Hash{ + 2, + 2, + 2, + }, Inputs: []Input{{ Value: 2222, Outpoint: op2, @@ -2632,7 +2824,9 @@ func testSweepBatcherNonWalletAddr(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{2}, + Preimage: lntypes.Preimage{ + 2, + }, }, IsExternalAddr: true, DestAddr: destAddr, @@ -2662,7 +2856,11 @@ func testSweepBatcherNonWalletAddr(t *testing.T, store testStore, // Create a third sweep request that has more timeout distance than // the default. sweepReq3 := SweepRequest{ - SwapHash: lntypes.Hash{3, 3, 3}, + SwapHash: lntypes.Hash{ + 3, + 3, + 3, + }, Inputs: []Input{{ Value: 3333, Outpoint: op3, @@ -2678,7 +2876,9 @@ func testSweepBatcherNonWalletAddr(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{3}, + Preimage: lntypes.Preimage{ + 3, + }, }, IsExternalAddr: true, DestAddr: destAddr, @@ -2754,42 +2954,66 @@ func testSweepBatcherComposite(t *testing.T, store testStore, sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams) require.NoError(t, err) - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore) + batcherStore, sweepStore, + ) go func() { err := batcher.Run(ctx) checkBatcherError(t, err) }() op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } op2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, } op3 := wire.OutPoint{ - Hash: chainhash.Hash{3, 3}, + Hash: chainhash.Hash{ + 3, + 3, + }, Index: 3, } op4 := wire.OutPoint{ - Hash: chainhash.Hash{4, 4}, + Hash: chainhash.Hash{ + 4, + 4, + }, Index: 4, } op5 := wire.OutPoint{ - Hash: chainhash.Hash{5, 5}, + Hash: chainhash.Hash{ + 5, + 5, + }, Index: 5, } op6 := wire.OutPoint{ - Hash: chainhash.Hash{6, 6}, + Hash: chainhash.Hash{ + 6, + 6, + }, Index: 6, } // Create a sweep request. sweepReq1 := SweepRequest{ - SwapHash: lntypes.Hash{1, 1, 1}, + SwapHash: lntypes.Hash{ + 1, + 1, + 1, + }, Inputs: []Input{{ Value: 1111, Outpoint: op1, @@ -2816,7 +3040,11 @@ func testSweepBatcherComposite(t *testing.T, store testStore, // Create a second sweep request that has a timeout distance less than // our configured threshold. sweepReq2 := SweepRequest{ - SwapHash: lntypes.Hash{2, 2, 2}, + SwapHash: lntypes.Hash{ + 2, + 2, + 2, + }, Inputs: []Input{{ Value: 2222, Outpoint: op2, @@ -2832,7 +3060,9 @@ func testSweepBatcherComposite(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{2}, + Preimage: lntypes.Preimage{ + 2, + }, }, DestAddr: destAddr, SwapInvoice: swapInvoice, @@ -2846,7 +3076,11 @@ func testSweepBatcherComposite(t *testing.T, store testStore, // Create a third sweep request that has less timeout distance than the // default max, but is not spending to a wallet address. sweepReq3 := SweepRequest{ - SwapHash: lntypes.Hash{3, 3, 3}, + SwapHash: lntypes.Hash{ + 3, + 3, + 3, + }, Inputs: []Input{{ Value: 3333, Outpoint: op3, @@ -2862,7 +3096,9 @@ func testSweepBatcherComposite(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{3}, + Preimage: lntypes.Preimage{ + 3, + }, }, DestAddr: destAddr, SwapInvoice: swapInvoice, @@ -2877,7 +3113,11 @@ func testSweepBatcherComposite(t *testing.T, store testStore, // Create a fourth sweep request that has a timeout which is not valid // for the first batch, so it will cause it to create a new batch. sweepReq4 := SweepRequest{ - SwapHash: lntypes.Hash{4, 4, 4}, + SwapHash: lntypes.Hash{ + 4, + 4, + 4, + }, Inputs: []Input{{ Value: 444, Outpoint: op4, @@ -2893,7 +3133,9 @@ func testSweepBatcherComposite(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{4}, + Preimage: lntypes.Preimage{ + 4, + }, }, DestAddr: destAddr, SwapInvoice: swapInvoice, @@ -2907,7 +3149,11 @@ func testSweepBatcherComposite(t *testing.T, store testStore, // Create a fifth sweep request that has a timeout which is not valid // for the first batch, but a valid timeout for the new batch. sweepReq5 := SweepRequest{ - SwapHash: lntypes.Hash{5, 5, 5}, + SwapHash: lntypes.Hash{ + 5, + 5, + 5, + }, Inputs: []Input{{ Value: 555, Outpoint: op5, @@ -2923,7 +3169,9 @@ func testSweepBatcherComposite(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{5}, + Preimage: lntypes.Preimage{ + 5, + }, }, DestAddr: destAddr, SwapInvoice: swapInvoice, @@ -2937,7 +3185,11 @@ func testSweepBatcherComposite(t *testing.T, store testStore, // Create a sixth sweep request that has a valid timeout for the new // batch, but is paying to a non-wallet address. sweepReq6 := SweepRequest{ - SwapHash: lntypes.Hash{6, 6, 6}, + SwapHash: lntypes.Hash{ + 6, + 6, + 6, + }, Inputs: []Input{{ Value: 666, Outpoint: op6, @@ -2953,7 +3205,9 @@ func testSweepBatcherComposite(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{6}, + Preimage: lntypes.Preimage{ + 6, + }, }, DestAddr: destAddr, SwapInvoice: swapInvoice, @@ -3111,6 +3365,7 @@ func testSweepBatcherComposite(t *testing.T, store testStore, func makeTestTx(value int64) *wire.MsgTx { tx := wire.NewMsgTx(wire.TxVersion) tx.AddTxOut(wire.NewTxOut(value, nil)) + return tx } @@ -3169,9 +3424,11 @@ func testRestoringEmptyBatch(t *testing.T, store testStore, _, err = batcherStore.InsertSweepBatch(ctx, &dbBatch{}) require.NoError(t, err) - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore) + batcherStore, sweepStore, + ) var wg sync.WaitGroup @@ -3184,13 +3441,20 @@ func testRestoringEmptyBatch(t *testing.T, store testStore, <-batcher.initDone op := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } // Create a sweep request. sweepReq := SweepRequest{ - SwapHash: lntypes.Hash{1, 1, 1}, + SwapHash: lntypes.Hash{ + 1, + 1, + 1, + }, Inputs: []Input{{ Value: 1111, Outpoint: op, @@ -3291,12 +3555,14 @@ func (s *loopStoreMock) putLoopOutSwap(hash lntypes.Hash, out *loopdb.LoopOut) { s.loops[hash] = out if existed { + // The swap exists, no need to create one in backend, since it // stores fake data anyway. return } if _, ok := s.backend.(*loopdb.StoreMock); ok { + // Do not create a fake loop in loopdb.StoreMock, because it // blocks on notification channels and this is not needed. return @@ -3348,9 +3614,11 @@ func testHandleSweepTwice(t *testing.T, backend testStore, sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams) require.NoError(t, err) - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore) + batcherStore, sweepStore, + ) var wg sync.WaitGroup @@ -3368,11 +3636,18 @@ func testHandleSweepTwice(t *testing.T, backend testStore, // Create two sweep requests with CltvExpiry distant from each other // to go assigned to separate batches. op1 := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } sweepReq1 := SweepRequest{ - SwapHash: lntypes.Hash{1, 1, 1}, + SwapHash: lntypes.Hash{ + 1, + 1, + 1, + }, Inputs: []Input{{ Value: 1111, Outpoint: op1, @@ -3382,7 +3657,11 @@ func testHandleSweepTwice(t *testing.T, backend testStore, loopOut1 := &loopdb.LoopOut{ Loop: loopdb.Loop{ - Hash: lntypes.Hash{1, 1, 1}, + Hash: lntypes.Hash{ + 1, + 1, + 1, + }, }, Contract: &loopdb.LoopOutContract{ SwapContract: loopdb.SwapContract{ @@ -3398,11 +3677,18 @@ func testHandleSweepTwice(t *testing.T, backend testStore, } op2 := wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 2, } sweepReq2 := SweepRequest{ - SwapHash: lntypes.Hash{2, 2, 2}, + SwapHash: lntypes.Hash{ + 2, + 2, + 2, + }, Inputs: []Input{{ Value: 2222, Outpoint: op2, @@ -3412,7 +3698,11 @@ func testHandleSweepTwice(t *testing.T, backend testStore, loopOut2 := &loopdb.LoopOut{ Loop: loopdb.Loop{ - Hash: lntypes.Hash{2, 2, 2}, + Hash: lntypes.Hash{ + 2, + 2, + 2, + }, }, Contract: &loopdb.LoopOutContract{ SwapContract: loopdb.SwapContract{ @@ -3467,7 +3757,11 @@ func testHandleSweepTwice(t *testing.T, backend testStore, // Change CltvExpiry. loopOut2 = &loopdb.LoopOut{ Loop: loopdb.Loop{ - Hash: lntypes.Hash{2, 2, 2}, + Hash: lntypes.Hash{ + 2, + 2, + 2, + }, }, Contract: &loopdb.LoopOutContract{ SwapContract: loopdb.SwapContract{ @@ -3552,9 +3846,11 @@ func testRestoringPreservesConfTarget(t *testing.T, store testStore, sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams) require.NoError(t, err) - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore) + batcherStore, sweepStore, + ) var wg sync.WaitGroup @@ -3568,11 +3864,18 @@ func testRestoringPreservesConfTarget(t *testing.T, store testStore, // Create a sweep request. op := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } sweepReq := SweepRequest{ - SwapHash: lntypes.Hash{1, 1, 1}, + SwapHash: lntypes.Hash{ + 1, + 1, + 1, + }, Inputs: []Input{{ Value: 1111, Outpoint: op, @@ -3645,9 +3948,11 @@ func testRestoringPreservesConfTarget(t *testing.T, store testStore, checkBatcherError(t, runErr) // Now launch it again. - batcher = NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher = NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore) + batcherStore, sweepStore, + ) ctx, cancel = context.WithCancel(context.Background()) wg.Go(func() { runErr = batcher.Run(ctx) @@ -3764,7 +4069,10 @@ func testSweepFetcher(t *testing.T, store testStore, // Create a sweep request. op := wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, } sweepReq := SweepRequest{ @@ -3796,10 +4104,12 @@ func testSweepFetcher(t *testing.T, store testStore, return feeRate, nil } - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, - nil, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepFetcher, WithCustomFeeRate(customFeeRate), - WithCustomSignMuSig2(testSignMuSig2func)) + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, nil, + testVerifySchnorrSig, lnd.ChainParams, batcherStore, + sweepFetcher, WithCustomFeeRate(customFeeRate), + WithCustomSignMuSig2(testSignMuSig2func), + ) var wg sync.WaitGroup @@ -3848,7 +4158,9 @@ func testSweepFetcher(t *testing.T, store testStore, gotFee := amt - out require.Equal(t, expectedFee, gotFee, "fees don't match") gotWeight := lntypes.WeightUnit( - blockchain.GetTransactionWeight(btcutil.NewTx(tx)), + blockchain.GetTransactionWeight( + btcutil.NewTx(tx), + ), ) require.Equal(t, weight, gotWeight, "weights don't match") gotFeeRate := chainfee.NewSatPerKWeight(gotFee, gotWeight) @@ -3881,9 +4193,11 @@ func testSweepBatcherCloseDuringAdding(t *testing.T, store testStore, sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams) require.NoError(t, err) - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore) + batcherStore, sweepStore, + ) go func() { err := batcher.Run(ctx) checkBatcherError(t, err) @@ -3900,7 +4214,9 @@ func testSweepBatcherCloseDuringAdding(t *testing.T, store testStore, AmountRequested: 1111, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{i}, + Preimage: lntypes.Preimage{ + i, + }, }, DestAddr: destAddr, @@ -3919,11 +4235,18 @@ func testSweepBatcherCloseDuringAdding(t *testing.T, store testStore, for i := byte(1); i < 255; i++ { // Create a sweep request. sweepReq := SweepRequest{ - SwapHash: lntypes.Hash{i, i, i}, + SwapHash: lntypes.Hash{ + i, + i, + i, + }, Inputs: []Input{{ Value: 1111, Outpoint: wire.OutPoint{ - Hash: chainhash.Hash{i, i}, + Hash: chainhash.Hash{ + i, + i, + }, Index: 1, }, }}, @@ -4009,7 +4332,12 @@ func testSweepBatcherHandleSweepRace(t *testing.T, store testStore, ) sweepOutpoint := wire.OutPoint{ - Hash: chainhash.Hash{0, 0, 0, 1}, + Hash: chainhash.Hash{ + 0, + 0, + 0, + 1, + }, Index: 5, } @@ -4041,7 +4369,9 @@ func testSweepBatcherHandleSweepRace(t *testing.T, store testStore, AmountRequested: sweepValue, ProtocolVersion: loopdb.ProtocolVersionMuSig2, HtlcKeys: htlcKeys, - Preimage: lntypes.Preimage{7}, + Preimage: lntypes.Preimage{ + 7, + }, }, DestAddr: destAddr, SwapInvoice: swapInvoice, @@ -4067,6 +4397,7 @@ func testSweepBatcherHandleSweepRace(t *testing.T, store testStore, } originalBatchID = batch.snapshot(ctx).id + return true }, test.Timeout, eventuallyCheckFrequency) @@ -4089,6 +4420,7 @@ func testSweepBatcherHandleSweepRace(t *testing.T, store testStore, select { case <-addCtx.Done(): return + default: } @@ -4164,6 +4496,7 @@ func testSweepBatcherHandleSweepRace(t *testing.T, store testStore, select { case err := <-addErrChan: require.NoError(t, err, "error from a goroutine") + default: } @@ -4172,6 +4505,7 @@ func testSweepBatcherHandleSweepRace(t *testing.T, store testStore, if err != nil { return false } + return len(running) == 0 }, test.Timeout, eventuallyCheckFrequency) @@ -4205,7 +4539,12 @@ func testSweepBatcherHandleBatchShutdown(t *testing.T, store testStore, ctx := context.Background() swapHash := lntypes.Hash{2, 2, 2} sweepOutpoint := wire.OutPoint{ - Hash: chainhash.Hash{0, 0, 0, 2}, + Hash: chainhash.Hash{ + 0, + 0, + 0, + 2, + }, Index: 1, } @@ -4215,7 +4554,9 @@ func testSweepBatcherHandleBatchShutdown(t *testing.T, store testStore, AmountRequested: 1_000, ProtocolVersion: loopdb.ProtocolVersionMuSig2, HtlcKeys: htlcKeys, - Preimage: lntypes.Preimage{2}, + Preimage: lntypes.Preimage{ + 2, + }, }, DestAddr: destAddr, SwapInvoice: swapInvoice, @@ -4302,7 +4643,9 @@ func testSweepBatcherHandleBatchShutdown(t *testing.T, store testStore, }() testBatcher := &Batcher{ - batches: map[int32]*batch{batchID: completedBatch}, + batches: map[int32]*batch{ + batchID: completedBatch, + }, store: batcherStore, chainParams: &chaincfg.TestNet3Params, clock: clock.NewTestClock(time.Unix(0, 0)), @@ -4368,6 +4711,7 @@ func (f *failingBaseDB) shouldFail() bool { if f.armed && !f.failed { f.failed = true f.armed = false + return true } @@ -4468,7 +4812,12 @@ func TestSweepBatcherConfirmedBatchIncompleteSweeps(t *testing.T) { ctx := context.Background() sweepOutpoint := wire.OutPoint{ - Hash: chainhash.Hash{0, 0, 0, 3}, + Hash: chainhash.Hash{ + 0, + 0, + 0, + 3, + }, Index: 7, } swapHash := lntypes.Hash{3, 3, 3} @@ -4494,7 +4843,9 @@ func TestSweepBatcherConfirmedBatchIncompleteSweeps(t *testing.T) { AmountRequested: sweepValue, ProtocolVersion: loopdb.ProtocolVersionMuSig2, HtlcKeys: htlcKeys, - Preimage: lntypes.Preimage{3}, + Preimage: lntypes.Preimage{ + 3, + }, }, DestAddr: destAddr, SwapInvoice: swapInvoice, @@ -4564,8 +4915,10 @@ func TestSweepBatcherConfirmedBatchIncompleteSweeps(t *testing.T) { parentBatch, err := batcherStore.GetParentBatch(ctx, sweepOutpoint) require.NoError(t, err) - require.Equal(t, parentBatch.Confirmed, completed, - "inconsistent DB: confirmed batch vs sweep completion") + require.Equal( + t, parentBatch.Confirmed, completed, + "inconsistent DB: confirmed batch vs sweep completion", + ) } // testCustomSignMuSig2 tests the operation with custom musig2 signer. @@ -4581,9 +4934,11 @@ func testCustomSignMuSig2(t *testing.T, store testStore, require.NoError(t, err) // Use custom MuSig2 signer function. - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, - nil, testVerifySchnorrSig, lnd.ChainParams, batcherStore, - sweepStore, WithCustomSignMuSig2(testSignMuSig2func)) + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, nil, + testVerifySchnorrSig, lnd.ChainParams, batcherStore, sweepStore, + WithCustomSignMuSig2(testSignMuSig2func), + ) var wg sync.WaitGroup @@ -4597,11 +4952,18 @@ func testCustomSignMuSig2(t *testing.T, store testStore, // Create a sweep request. sweepReq := SweepRequest{ - SwapHash: lntypes.Hash{1, 1, 1}, + SwapHash: lntypes.Hash{ + 1, + 1, + 1, + }, Inputs: []Input{{ Value: 1111, Outpoint: wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, }, }}, @@ -4691,8 +5053,8 @@ func testWithMixedBatch(t *testing.T, store testStore, muSig2SignSweep := func(ctx context.Context, protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash, paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte, - prevoutMap map[wire.OutPoint]*wire.TxOut) ( - []byte, []byte, error) { + prevoutMap map[wire.OutPoint]*wire.TxOut) ([]byte, []byte, + error) { if swapHash == swapHashes[2] { return nil, nil, nil @@ -4703,9 +5065,9 @@ func testWithMixedBatch(t *testing.T, store testStore, // Use mixed batches. batcher := NewBatcher( - lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, - muSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepFetcher, + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, muSig2SignSweep, + testVerifySchnorrSig, lnd.ChainParams, batcherStore, + sweepFetcher, ) var wg sync.WaitGroup @@ -4727,7 +5089,9 @@ func testWithMixedBatch(t *testing.T, store testStore, // Create 3 swaps and 3 sweeps. for i, swapHash := range swapHashes { outpoint := wire.OutPoint{ - Hash: chainhash.Hash{byte(i + 1)}, + Hash: chainhash.Hash{ + byte(i + 1), + }, Index: uint32(i + 1), } @@ -4824,7 +5188,9 @@ func testWithMixedBatch(t *testing.T, store testStore, // Check weight. gotWeight := lntypes.WeightUnit( - blockchain.GetTransactionWeight(btcutil.NewTx(tx)), + blockchain.GetTransactionWeight( + btcutil.NewTx(tx), + ), ) require.Equal(t, weight, gotWeight, "weights don't match") @@ -4884,9 +5250,9 @@ func testWithMixedBatchCustom(t *testing.T, store testStore, // Use mixed batches. batcher := NewBatcher( - lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, - muSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepFetcher, + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, muSig2SignSweep, + testVerifySchnorrSig, lnd.ChainParams, batcherStore, + sweepFetcher, ) var wg sync.WaitGroup @@ -4902,7 +5268,9 @@ func testWithMixedBatchCustom(t *testing.T, store testStore, // Create swaps and sweeps. for i, swapHash := range swapHashes { outpoint := wire.OutPoint{ - Hash: chainhash.Hash{byte(i + 1)}, + Hash: chainhash.Hash{ + byte(i + 1), + }, Index: uint32(i + 1), } @@ -4990,7 +5358,9 @@ func testWithMixedBatchCustom(t *testing.T, store testStore, // Check weight. gotWeight := lntypes.WeightUnit( - blockchain.GetTransactionWeight(btcutil.NewTx(tx)), + blockchain.GetTransactionWeight( + btcutil.NewTx(tx), + ), ) require.Equal(t, wantWeight, gotWeight, "weights don't match") @@ -5038,8 +5408,8 @@ func testWithMixedBatchLarge(t *testing.T, store testStore, muSig2SignSweep := func(ctx context.Context, protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash, paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte, - prevoutMap map[wire.OutPoint]*wire.TxOut) ( - []byte, []byte, error) { + prevoutMap map[wire.OutPoint]*wire.TxOut) ([]byte, []byte, + error) { switch { case swapHash == preimages[2].Hash(): @@ -5080,9 +5450,11 @@ func testWithMixedBatchLarge(t *testing.T, store testStore, // Expected weight. wantWeight := lntypes.WeightUnit(3377) - testWithMixedBatchCustom(t, store, batcherStore, preimages, - muSig2SignSweep, nonCoopHints, expectSignOutputRawChannel, - wantWeight, wantWitnessSizes) + testWithMixedBatchCustom( + t, store, batcherStore, preimages, muSig2SignSweep, + nonCoopHints, expectSignOutputRawChannel, wantWeight, + wantWitnessSizes, + ) } // testWithMixedBatchCoopOnly tests mixed batches construction, @@ -5099,8 +5471,8 @@ func testWithMixedBatchCoopOnly(t *testing.T, store testStore, muSig2SignSweep := func(ctx context.Context, protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash, paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte, - prevoutMap map[wire.OutPoint]*wire.TxOut) ( - []byte, []byte, error) { + prevoutMap map[wire.OutPoint]*wire.TxOut) ([]byte, []byte, + error) { return nil, nil, nil } @@ -5118,9 +5490,11 @@ func testWithMixedBatchCoopOnly(t *testing.T, store testStore, // Expected weight. wantWeight := lntypes.WeightUnit(856) - testWithMixedBatchCustom(t, store, batcherStore, preimages, - muSig2SignSweep, nonCoopHints, expectSignOutputRawChannel, - wantWeight, wantWitnessSizes) + testWithMixedBatchCustom( + t, store, batcherStore, preimages, muSig2SignSweep, + nonCoopHints, expectSignOutputRawChannel, wantWeight, + wantWitnessSizes, + ) } // testWithMixedBatchNonCoopHintOnly tests mixed batches construction, @@ -5138,8 +5512,8 @@ func testWithMixedBatchNonCoopHintOnly(t *testing.T, store testStore, muSig2SignSweep := func(ctx context.Context, protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash, paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte, - prevoutMap map[wire.OutPoint]*wire.TxOut) ( - []byte, []byte, error) { + prevoutMap map[wire.OutPoint]*wire.TxOut) ([]byte, []byte, + error) { panic("must not be called in this test") } @@ -5157,9 +5531,11 @@ func testWithMixedBatchNonCoopHintOnly(t *testing.T, store testStore, // Expected weight. wantWeight := lntypes.WeightUnit(1345) - testWithMixedBatchCustom(t, store, batcherStore, preimages, - muSig2SignSweep, nonCoopHints, expectSignOutputRawChannel, - wantWeight, wantWitnessSizes) + testWithMixedBatchCustom( + t, store, batcherStore, preimages, muSig2SignSweep, + nonCoopHints, expectSignOutputRawChannel, wantWeight, + wantWitnessSizes, + ) } // testWithMixedBatchCoopFailedOnly tests mixed batches construction, @@ -5176,8 +5552,8 @@ func testWithMixedBatchCoopFailedOnly(t *testing.T, store testStore, muSig2SignSweep := func(ctx context.Context, protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash, paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte, - prevoutMap map[wire.OutPoint]*wire.TxOut) ( - []byte, []byte, error) { + prevoutMap map[wire.OutPoint]*wire.TxOut) ([]byte, []byte, + error) { return nil, nil, fmt.Errorf("test error") } @@ -5195,9 +5571,11 @@ func testWithMixedBatchCoopFailedOnly(t *testing.T, store testStore, // Expected weight. wantWeight := lntypes.WeightUnit(1345) - testWithMixedBatchCustom(t, store, batcherStore, preimages, - muSig2SignSweep, nonCoopHints, expectSignOutputRawChannel, - wantWeight, wantWitnessSizes) + testWithMixedBatchCustom( + t, store, batcherStore, preimages, muSig2SignSweep, + nonCoopHints, expectSignOutputRawChannel, wantWeight, + wantWitnessSizes, + ) } // testFeeRateGrows tests that fee rate of a batch does not decrease and is at @@ -5239,9 +5617,11 @@ func testFeeRateGrows(t *testing.T, store testStore, feeRateHigh = chainfee.SatPerKWeight(50_000) ) - batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, + batcher := NewBatcher( + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, - batcherStore, sweepStore, WithCustomFeeRate(customFeeRate)) + batcherStore, sweepStore, WithCustomFeeRate(customFeeRate), + ) go func() { err := batcher.Run(ctx) @@ -5256,7 +5636,10 @@ func testFeeRateGrows(t *testing.T, store testStore, Inputs: []Input{{ Value: 1_000_000, Outpoint: wire.OutPoint{ - Hash: chainhash.Hash{1, 1}, + Hash: chainhash.Hash{ + 1, + 1, + }, Index: 1, }, }}, @@ -5271,7 +5654,9 @@ func testFeeRateGrows(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{1}, + Preimage: lntypes.Preimage{ + 1, + }, }, DestAddr: destAddr, @@ -5322,7 +5707,10 @@ func testFeeRateGrows(t *testing.T, store testStore, Inputs: []Input{{ Value: 1_000_000, Outpoint: wire.OutPoint{ - Hash: chainhash.Hash{2, 2}, + Hash: chainhash.Hash{ + 2, + 2, + }, Index: 1, }, }}, @@ -5337,7 +5725,9 @@ func testFeeRateGrows(t *testing.T, store testStore, HtlcKeys: htlcKeys, // Make preimage unique to pass SQL constraints. - Preimage: lntypes.Preimage{2}, + Preimage: lntypes.Preimage{ + 2, + }, }, DestAddr: destAddr, @@ -5584,6 +5974,7 @@ func (s *loopdbBatcherStore) UpsertSweep(ctx context.Context, if err == nil { s.sweepsSet[sweep.Outpoint] = struct{}{} } + return err } @@ -5647,8 +6038,9 @@ func (s *loopdbStore) AssertLoopOutStored() { } // runTests runs a test with both mock and loopdb. -func runTests(t *testing.T, testFn func(t *testing.T, store testStore, - batcherStore testBatcherStore)) { +func runTests(t *testing.T, + testFn func(t *testing.T, store testStore, + batcherStore testBatcherStore)) { logger := btclog.NewSLogger(btclog.NewDefaultHandler(os.Stdout)) logger.SetLevel(btclog.LevelTrace) diff --git a/test/chainnotifier_mock.go b/test/chainnotifier_mock.go index 9b4623bd8..f59203168 100644 --- a/test/chainnotifier_mock.go +++ b/test/chainnotifier_mock.go @@ -24,9 +24,8 @@ type mockChainNotifier struct { var _ lndclient.ChainNotifierClient = (*mockChainNotifier)(nil) -func (c *mockChainNotifier) RawClientWithMacAuth( - ctx context.Context) (context.Context, time.Duration, - chainrpc.ChainNotifierClient) { +func (c *mockChainNotifier) RawClientWithMacAuth(ctx context.Context) ( + context.Context, time.Duration, chainrpc.ChainNotifierClient) { return ctx, 0, nil } @@ -172,12 +171,17 @@ func (c *mockChainNotifier) RegisterConfirmationsNtfn(ctx context.Context, for i := 0; i < len(c.confRegistrations); i++ { r := c.confRegistrations[i] - // Whichever conf notifier catches the confirmation - // will forward it to all matching subscribers. - if bytes.Equal(m.Tx.TxOut[0].PkScript, r.PkScript) { + // Whichever conf notifier catches the + // confirmation will forward it to all matching + // subscribers. + if bytes.Equal( + m.Tx.TxOut[0].PkScript, r.PkScript, + ) { + // Unregister the "notifier". c.confRegistrations = append( - c.confRegistrations[:i], c.confRegistrations[i+1:]..., + c.confRegistrations[:i], + c.confRegistrations[i+1:]..., ) i-- diff --git a/test/context.go b/test/context.go index a81e7557b..9849390a2 100644 --- a/test/context.go +++ b/test/context.go @@ -24,9 +24,7 @@ type Context struct { } // NewContext instanties a new common test context. -func NewContext(t *testing.T, - lnd *LndMockServices) Context { - +func NewContext(t *testing.T, lnd *LndMockServices) Context { return Context{ T: t, Lnd: lnd, @@ -42,8 +40,10 @@ func (ctx *Context) ReceiveTx() *wire.MsgTx { select { case tx := <-ctx.Lnd.TxPublishChannel: return tx + case <-time.After(Timeout): ctx.T.Fatalf("sweep not published") + return nil } } @@ -104,7 +104,6 @@ func (ctx *Context) AssertTrackPayment() TrackPaymentMessage { var msg TrackPaymentMessage select { case msg = <-ctx.Lnd.TrackPaymentChannel: - case <-time.After(Timeout): DumpGoroutines() ctx.T.Fatalf("payment not tracked") @@ -114,7 +113,9 @@ func (ctx *Context) AssertTrackPayment() TrackPaymentMessage { } // AssertRegisterConf asserts that a register for conf has been received. -func (ctx *Context) AssertRegisterConf(expectTxHash bool, confs int32) *ConfRegistration { +func (ctx *Context) AssertRegisterConf(expectTxHash bool, + confs int32) *ConfRegistration { + ctx.T.Helper() // Expect client to register for conf @@ -142,9 +143,7 @@ func (ctx *Context) AssertRegisterConf(expectTxHash bool, confs int32) *ConfRegi // AssertPaid asserts that the expected payment request has been paid. This // function returns a complete function to signal the final payment result. -func (ctx *Context) AssertPaid( - expectedMemo string) func(error) { - +func (ctx *Context) AssertPaid(expectedMemo string) func(error) { ctx.T.Helper() if done, ok := ctx.PaidInvoices[expectedMemo]; ok { @@ -161,12 +160,14 @@ func (ctx *Context) AssertPaid( expectedMemo) } - payReq := ctx.DecodeInvoice(swapPayment.SendPaymentRequest.Invoice) + payReq := ctx.DecodeInvoice( + swapPayment.SendPaymentRequest.Invoice, + ) _, ok := ctx.PaidInvoices[*payReq.Description] require.False( - ctx.T, ok, - "duplicate invoice paid: %v", *payReq.Description, + ctx.T, ok, "duplicate invoice paid: %v", + *payReq.Description, ) done := func(result error) { @@ -177,6 +178,7 @@ func (ctx *Context) AssertPaid( swapPayment.Updates <- lndclient.PaymentStatus{ State: lnrpc.Payment_FAILED, } + return } swapPayment.Updates <- lndclient.PaymentStatus{ @@ -193,9 +195,7 @@ func (ctx *Context) AssertPaid( } // AssertSettled asserts that an invoice with the given hash is settled. -func (ctx *Context) AssertSettled( - expectedHash lntypes.Hash) lntypes.Preimage { - +func (ctx *Context) AssertSettled(expectedHash lntypes.Hash) lntypes.Preimage { ctx.T.Helper() select { @@ -207,9 +207,11 @@ func (ctx *Context) AssertSettled( ) return preimage + case <-time.After(Timeout): } ctx.T.Fatalf("invoice not settled") + return lntypes.Preimage{} } @@ -228,6 +230,7 @@ func (ctx *Context) AssertFailed(expectedHash lntypes.Hash) { if expectedHash == hash { return } + case <-time.After(Timeout): ctx.T.Fatalf("invoice not failed") } @@ -245,9 +248,7 @@ func (ctx *Context) DecodeInvoice(request string) *zpay32.Invoice { } // GetOutputIndex returns the index in the tx outs of the given script hash. -func (ctx *Context) GetOutputIndex(tx *wire.MsgTx, - script []byte) int { - +func (ctx *Context) GetOutputIndex(tx *wire.MsgTx, script []byte) int { for idx, out := range tx.TxOut { if bytes.Equal(out.PkScript, script) { return idx @@ -255,6 +256,7 @@ func (ctx *Context) GetOutputIndex(tx *wire.MsgTx, } ctx.T.Fatal("the output not present in tx") + return 0 } diff --git a/test/invoices_mock.go b/test/invoices_mock.go index 7b0f309dc..8767f95db 100644 --- a/test/invoices_mock.go +++ b/test/invoices_mock.go @@ -22,9 +22,8 @@ type mockInvoices struct { var _ lndclient.InvoicesClient = (*mockInvoices)(nil) -func (s *mockInvoices) RawClientWithMacAuth( - ctx context.Context) (context.Context, time.Duration, - invoicesrpc.InvoicesClient) { +func (s *mockInvoices) RawClientWithMacAuth(ctx context.Context) ( + context.Context, time.Duration, invoicesrpc.InvoicesClient) { return ctx, 0, nil } @@ -53,8 +52,8 @@ func (s *mockInvoices) CancelInvoice(ctx context.Context, } func (s *mockInvoices) SubscribeSingleInvoice(ctx context.Context, - hash lntypes.Hash) (<-chan lndclient.InvoiceUpdate, - <-chan error, error) { + hash lntypes.Hash) (<-chan lndclient.InvoiceUpdate, <-chan error, + error) { updateChan := make(chan lndclient.InvoiceUpdate, 2) errChan := make(chan error) diff --git a/test/lightning_client_mock.go b/test/lightning_client_mock.go index fe4198a1d..d859bee5e 100644 --- a/test/lightning_client_mock.go +++ b/test/lightning_client_mock.go @@ -70,16 +70,18 @@ func (h *mockLightningClient) GetInfo(ctx context.Context) (*lndclient.Info, } var pubKey [33]byte copy(pubKey[:], pubKeyBytes) + return &lndclient.Info{ BlockHeight: 600, IdentityPubkey: pubKey, - Uris: []string{h.lnd.NodePubkey + "@127.0.0.1:9735"}, + Uris: []string{ + h.lnd.NodePubkey + "@127.0.0.1:9735", + }, }, nil } func (h *mockLightningClient) EstimateFeeToP2WSH(ctx context.Context, - amt btcutil.Amount, confTarget int32) (btcutil.Amount, - error) { + amt btcutil.Amount, confTarget int32) (btcutil.Amount, error) { return 3000, nil } @@ -94,8 +96,10 @@ func (h *mockLightningClient) AddInvoice(ctx context.Context, switch { case in.Hash != nil: hash = *in.Hash + case in.Preimage != nil: hash = (*in.Preimage).Hash() + default: if _, err := rand.Read(hash[:]); err != nil { return lntypes.Hash{}, "", err @@ -174,9 +178,9 @@ func (h *mockLightningClient) LookupInvoice(_ context.Context, } // ListTransactions returns all known transactions of the backing lnd node. -func (h *mockLightningClient) ListTransactions( - _ context.Context, _, _ int32, _ ...lndclient.ListTransactionsOption) ( - []lndclient.Transaction, error) { +func (h *mockLightningClient) ListTransactions(_ context.Context, _, _ int32, + _ ...lndclient.ListTransactionsOption) ([]lndclient.Transaction, + error) { h.lnd.lock.Lock() txs := h.lnd.Transactions @@ -188,7 +192,8 @@ func (h *mockLightningClient) ListTransactions( // GetNodeInfo retrieves info on the node, and if includeChannels is True, // will return other channels the node may have with other peers. func (h *mockLightningClient) GetNodeInfo(ctx context.Context, - pubKeyBytes route.Vertex, includeChannels bool) (*lndclient.NodeInfo, error) { + pubKeyBytes route.Vertex, includeChannels bool) (*lndclient.NodeInfo, + error) { nodeInfo := &lndclient.NodeInfo{ Node: &lndclient.Node{ @@ -213,8 +218,12 @@ func (h *mockLightningClient) GetNodeInfo(ctx context.Context, (edge.Node1 != nodePubKey || edge.Node2 != nodePubKey) { for _, channel := range h.lnd.Channels { - if channel.ChannelID == edge.ChannelID && !channel.Private { - nodeInfo.Channels = append(nodeInfo.Channels, *edge) + if channel.ChannelID == edge.ChannelID && + !channel.Private { + + nodeInfo.Channels = append( + nodeInfo.Channels, *edge, + ) } } } @@ -233,6 +242,7 @@ func (h *mockLightningClient) GetChanInfo(ctx context.Context, if channelEdge, ok := h.lnd.ChannelEdges[channelID]; ok { return channelEdge, nil } + return channelEdge, fmt.Errorf("not found") } @@ -244,16 +254,16 @@ func (h *mockLightningClient) ListChannels(ctx context.Context, _, _ bool, } // ClosedChannels returns a list of our closed channels. -func (h *mockLightningClient) ClosedChannels(_ context.Context) ([]lndclient.ClosedChannel, - error) { +func (h *mockLightningClient) ClosedChannels(_ context.Context) ( + []lndclient.ClosedChannel, error) { return h.lnd.ClosedChannels, nil } // ForwardingHistory returns the mock's set of forwarding events. func (h *mockLightningClient) ForwardingHistory(_ context.Context, - _ lndclient.ForwardingHistoryRequest) (*lndclient.ForwardingHistoryResponse, - error) { + _ lndclient.ForwardingHistoryRequest) ( + *lndclient.ForwardingHistoryResponse, error) { return &lndclient.ForwardingHistoryResponse{ LastIndexOffset: 0, @@ -299,13 +309,17 @@ func (h *mockLightningClient) ListPayments(_ context.Context, // ChannelBackup retrieves the backup for a particular channel. The // backup is returned as an encrypted chanbackup.Single payload. -func (h *mockLightningClient) ChannelBackup(context.Context, wire.OutPoint) ([]byte, error) { +func (h *mockLightningClient) ChannelBackup(context.Context, wire.OutPoint) ( + []byte, error) { + return nil, nil } // ChannelBackups retrieves backups for all existing pending open and // open channels. The backups are returned as an encrypted // chanbackup.Multi payload. -func (h *mockLightningClient) ChannelBackups(ctx context.Context) ([]byte, error) { +func (h *mockLightningClient) ChannelBackups(ctx context.Context) ([]byte, + error) { + return nil, nil } diff --git a/test/lnd_services_mock.go b/test/lnd_services_mock.go index 4fe5d9b5e..45cdfe573 100644 --- a/test/lnd_services_mock.go +++ b/test/lnd_services_mock.go @@ -48,15 +48,19 @@ func NewMockLnd() *LndMockServices { ChainParams: &chaincfg.TestNet3Params, Versioner: versioner, }, - SendPaymentChannel: make(chan PaymentChannelMessage), - ConfChannel: make(chan *chainntnfs.TxConfirmation), - RegisterConfChannel: make(chan *ConfRegistration), - RegisterSpendChannel: make(chan *SpendRegistration), - SpendChannel: make(chan *chainntnfs.SpendDetail), - TxPublishChannel: make(chan *wire.MsgTx), - SendOutputsChannel: make(chan wire.MsgTx), - SettleInvoiceChannel: make(chan lntypes.Preimage), - SingleInvoiceSubcribeChannel: make(chan *SingleInvoiceSubscription, 1), + SendPaymentChannel: make(chan PaymentChannelMessage), + ConfChannel: make( + chan *chainntnfs.TxConfirmation, + ), + RegisterConfChannel: make(chan *ConfRegistration), + RegisterSpendChannel: make(chan *SpendRegistration), + SpendChannel: make(chan *chainntnfs.SpendDetail), + TxPublishChannel: make(chan *wire.MsgTx), + SendOutputsChannel: make(chan wire.MsgTx), + SettleInvoiceChannel: make(chan lntypes.Preimage), + SingleInvoiceSubcribeChannel: make( + chan *SingleInvoiceSubscription, 1, + ), RouterSendPaymentChannel: make(chan RouterPaymentChannelMessage), TrackPaymentChannel: make(chan TrackPaymentMessage), @@ -109,7 +113,8 @@ type TrackPaymentMessage struct { Errors chan error } -// RouterPaymentChannelMessage is the data that passed through RouterSendPaymentChannel. +// RouterPaymentChannelMessage is the data that passed through +// RouterSendPaymentChannel. type RouterPaymentChannelMessage struct { lndclient.SendPaymentRequest @@ -224,48 +229,56 @@ func (s *LndMockServices) IsDone() error { select { case <-s.SendPaymentChannel: return errors.New("SendPaymentChannel not empty") + default: } select { case <-s.SpendChannel: return errors.New("SpendChannel not empty") + default: } select { case <-s.TxPublishChannel: return errors.New("TxPublishChannel not empty") + default: } select { case <-s.SendOutputsChannel: return errors.New("SendOutputsChannel not empty") + default: } select { case <-s.SettleInvoiceChannel: return errors.New("SettleInvoiceChannel not empty") + default: } select { case <-s.ConfChannel: return errors.New("ConfChannel not empty") + default: } select { case <-s.RegisterConfChannel: return errors.New("RegisterConfChannel not empty") + default: } select { case <-s.RegisterSpendChannel: return errors.New("RegisterSpendChannel not empty") + default: } @@ -283,7 +296,8 @@ func (s *LndMockServices) SetFeeEstimate(confTarget int32, feeEstimate chainfee.SatPerKWeight) { s.LndServices.WalletKit.(*mockWalletKit).setFeeEstimate( - confTarget, feeEstimate, + confTarget, + feeEstimate, ) } diff --git a/test/route_hints.go b/test/route_hints.go index bc6434157..cb8d619d5 100644 --- a/test/route_hints.go +++ b/test/route_hints.go @@ -8,9 +8,7 @@ import ( ) // RequireRouteHintsEqual asserts that two route hint sets are identical. -func RequireRouteHintsEqual(t testing.TB, expected, - actual [][]zpay32.HopHint) { - +func RequireRouteHintsEqual(t testing.TB, expected, actual [][]zpay32.HopHint) { t.Helper() require.Len(t, actual, len(expected)) @@ -23,8 +21,7 @@ func RequireRouteHintsEqual(t testing.TB, expected, actualHint := actual[i][j] require.Equal( - t, - expectedHint.NodeID.SerializeCompressed(), + t, expectedHint.NodeID.SerializeCompressed(), actualHint.NodeID.SerializeCompressed(), ) require.Equal( @@ -35,13 +32,11 @@ func RequireRouteHintsEqual(t testing.TB, expected, actualHint.FeeBaseMSat, ) require.Equal( - t, - expectedHint.FeeProportionalMillionths, + t, expectedHint.FeeProportionalMillionths, actualHint.FeeProportionalMillionths, ) require.Equal( - t, - expectedHint.CLTVExpiryDelta, + t, expectedHint.CLTVExpiryDelta, actualHint.CLTVExpiryDelta, ) } diff --git a/test/router_mock.go b/test/router_mock.go index 7eda1d7e3..3bbf66b80 100644 --- a/test/router_mock.go +++ b/test/router_mock.go @@ -31,8 +31,8 @@ func (r *mockRouter) SendPayment(ctx context.Context, return statusChan, errorChan, nil } -func (r *mockRouter) TrackPayment(ctx context.Context, - hash lntypes.Hash) (chan lndclient.PaymentStatus, chan error, error) { +func (r *mockRouter) TrackPayment(ctx context.Context, hash lntypes.Hash) ( + chan lndclient.PaymentStatus, chan error, error) { statusChan := make(chan lndclient.PaymentStatus) errorChan := make(chan error) @@ -63,11 +63,12 @@ func (r *mockRouter) ImportMissionControl(ctx context.Context, if entry.NodeFrom == current.NodeFrom && entry.NodeTo == current.NodeTo { - // Mark that the entry has been found and updated. + // Mark that the entry has been found and + // updated. found = true - // Import failure result first. We ignore failure - // relax interval here for convenience. + // Import failure result first. We ignore + // failure relax interval here for convenience. current.FailTime = entry.FailTime current.FailAmt = entry.FailAmt @@ -107,5 +108,6 @@ func (r *mockRouter) ImportMissionControl(ctx context.Context, func (r *mockRouter) ResetMissionControl(ctx context.Context) error { r.lnd.MissionControlState = []lndclient.MissionControlEntry{} + return nil } diff --git a/test/signer_mock.go b/test/signer_mock.go index 23ed69533..50222e93c 100644 --- a/test/signer_mock.go +++ b/test/signer_mock.go @@ -24,16 +24,15 @@ type mockSigner struct { var _ lndclient.SignerClient = (*mockSigner)(nil) -func (s *mockSigner) RawClientWithMacAuth( - ctx context.Context) (context.Context, time.Duration, - signrpc.SignerClient) { +func (s *mockSigner) RawClientWithMacAuth(ctx context.Context) (context.Context, + time.Duration, signrpc.SignerClient) { return ctx, 0, nil } func (s *mockSigner) SignOutputRaw(ctx context.Context, tx *wire.MsgTx, - signDescriptors []*lndclient.SignDescriptor, - _ []*wire.TxOut) ([][]byte, error) { + signDescriptors []*lndclient.SignDescriptor, _ []*wire.TxOut) ([][]byte, + error) { s.lnd.SignOutputRawChannel <- SignOutputRawRequest{ Tx: tx, @@ -123,8 +122,8 @@ func (s *mockSigner) MuSig2RegisterNonces(context.Context, [32]byte, // combining all the partial signatures, then the cleanup parameter // should be set, indicating that the session can be removed from memory // once the signature was produced. -func (s *mockSigner) MuSig2Sign(context.Context, [32]byte, [32]byte, - bool) ([]byte, error) { +func (s *mockSigner) MuSig2Sign(context.Context, [32]byte, [32]byte, bool) ( + []byte, error) { return nil, nil } @@ -133,8 +132,8 @@ func (s *mockSigner) MuSig2Sign(context.Context, [32]byte, [32]byte, // local one, if it already exists. Once a partial signature of all // participants is registered, the final signature will be combined and // returned. -func (s *mockSigner) MuSig2CombineSig(context.Context, [32]byte, - [][]byte) (bool, []byte, error) { +func (s *mockSigner) MuSig2CombineSig(context.Context, [32]byte, [][]byte) ( + bool, []byte, error) { sig := make([]byte, 64) sig[0] = 42 diff --git a/test/testutils.go b/test/testutils.go index 8176e12dc..d721b1903 100644 --- a/test/testutils.go +++ b/test/testutils.go @@ -61,13 +61,15 @@ func EncodePayReq(payReq *zpay32.Invoice) (string, error) { } // GetInvoice creates a testnet payment request with the given parameters. -func GetInvoice(hash lntypes.Hash, amt btcutil.Amount, memo string) ( - string, error) { +func GetInvoice(hash lntypes.Hash, amt btcutil.Amount, + memo string) (string, error) { req, err := zpay32.NewInvoice( &chaincfg.TestNet3Params, hash, testTime, zpay32.Description(memo), - zpay32.Amount(lnwire.NewMSatFromSatoshis(amt)), + zpay32.Amount( + lnwire.NewMSatFromSatoshis(amt), + ), ) if err != nil { return "", err diff --git a/test/timeout.go b/test/timeout.go index 4370cedde..8cc0b15e9 100644 --- a/test/timeout.go +++ b/test/timeout.go @@ -43,6 +43,7 @@ func Guard(t *testing.T, opts ...GuardOption) func() { } panic("test timeout") + case <-done: } }() diff --git a/test/versioner_mock.go b/test/versioner_mock.go index b686e7ebb..1c72ddb67 100644 --- a/test/versioner_mock.go +++ b/test/versioner_mock.go @@ -49,9 +49,8 @@ func newMockVersioner() *mockVersioner { } } -func (v *mockVersioner) RawClientWithMacAuth( - ctx context.Context) (context.Context, time.Duration, - verrpc.VersionerClient) { +func (v *mockVersioner) RawClientWithMacAuth(ctx context.Context) ( + context.Context, time.Duration, verrpc.VersionerClient) { return ctx, 0, nil } diff --git a/test/walletkit_mock.go b/test/walletkit_mock.go index ee42fa162..eb62aaf62 100644 --- a/test/walletkit_mock.go +++ b/test/walletkit_mock.go @@ -43,22 +43,21 @@ type mockWalletKit struct { var _ lndclient.WalletKitClient = (*mockWalletKit)(nil) -func (m *mockWalletKit) RawClientWithMacAuth( - ctx context.Context) (context.Context, time.Duration, - walletrpc.WalletKitClient) { +func (m *mockWalletKit) RawClientWithMacAuth(ctx context.Context) ( + context.Context, time.Duration, walletrpc.WalletKitClient) { return ctx, 0, nil } func (m *mockWalletKit) ListUnspent(ctx context.Context, minConfs, - maxConfs int32, opts ...lndclient.ListUnspentOption) ( - []*lnwallet.Utxo, error) { + maxConfs int32, opts ...lndclient.ListUnspentOption) ([]*lnwallet.Utxo, + error) { return m.listUnspent, nil } -func (m *mockWalletKit) ListLeases( - context.Context) ([]lndclient.LeaseDescriptor, error) { +func (m *mockWalletKit) ListLeases(context.Context) ( + []lndclient.LeaseDescriptor, error) { return nil, nil } @@ -69,8 +68,8 @@ func (m *mockWalletKit) LeaseOutput(ctx context.Context, lockID wtxmgr.LockID, return time.Now(), nil } -func (m *mockWalletKit) ReleaseOutput(ctx context.Context, - lockID wtxmgr.LockID, op wire.OutPoint) error { +func (m *mockWalletKit) ReleaseOutput(ctx context.Context, lockID wtxmgr.LockID, + op wire.OutPoint) error { return nil } @@ -92,8 +91,8 @@ func (m *mockWalletKit) DeriveNextKey(ctx context.Context, family int32) ( }, nil } -func (m *mockWalletKit) DeriveKey(ctx context.Context, in *keychain.KeyLocator) ( - *keychain.KeyDescriptor, error) { +func (m *mockWalletKit) DeriveKey(ctx context.Context, + in *keychain.KeyLocator) (*keychain.KeyDescriptor, error) { _, pubKey := CreateKey(int32(in.Index)) @@ -112,6 +111,7 @@ func (m *mockWalletKit) NextAddr(context.Context, string, walletrpc.AddressType, if err != nil { return nil, err } + return addr, nil } @@ -126,6 +126,7 @@ func (m *mockWalletKit) PublishTransaction(ctx context.Context, tx *wire.MsgTx, m.lnd.AddTx(tx) m.lnd.TxPublishChannel <- tx + return nil } @@ -155,15 +156,17 @@ func (m *mockWalletKit) SendOutputs(ctx context.Context, outputs []*wire.TxOut, return &tx, nil } -func (m *mockWalletKit) setFeeEstimate(confTarget int32, fee chainfee.SatPerKWeight) { +func (m *mockWalletKit) setFeeEstimate(confTarget int32, + fee chainfee.SatPerKWeight) { + m.feeEstimateLock.Lock() defer m.feeEstimateLock.Unlock() m.feeEstimates[confTarget] = fee } -func (m *mockWalletKit) EstimateFeeRate(ctx context.Context, - confTarget int32) (chainfee.SatPerKWeight, error) { +func (m *mockWalletKit) EstimateFeeRate(ctx context.Context, confTarget int32) ( + chainfee.SatPerKWeight, error) { m.feeEstimateLock.Lock() defer m.feeEstimateLock.Unlock() @@ -194,8 +197,8 @@ func (m *mockWalletKit) setListUnspent(utxos []*lnwallet.Utxo) { // MinRelayFee returns the current minimum relay fee based on our chain backend // in sat/kw. It can be set with setMinRelayFee. -func (m *mockWalletKit) MinRelayFee( - ctx context.Context) (chainfee.SatPerKWeight, error) { +func (m *mockWalletKit) MinRelayFee(ctx context.Context) ( + chainfee.SatPerKWeight, error) { m.feeEstimateLock.Lock() defer m.feeEstimateLock.Unlock() @@ -204,8 +207,8 @@ func (m *mockWalletKit) MinRelayFee( } // ListSweeps returns a list of the sweep transaction ids known to our node. -func (m *mockWalletKit) ListSweeps(_ context.Context, _ int32) ( - []string, error) { +func (m *mockWalletKit) ListSweeps(_ context.Context, _ int32) ([]string, + error) { return m.lnd.Sweeps, nil } @@ -291,8 +294,8 @@ var finalScriptWitness = func() []byte { // does not perform any other tasks (such as coin selection, UTXO // locking or input/output/fee value validation, PSBT finalization). Any // input that is incomplete will be skipped. -func (m *mockWalletKit) SignPsbt(_ context.Context, - packet *psbt.Packet) (*psbt.Packet, error) { +func (m *mockWalletKit) SignPsbt(_ context.Context, packet *psbt.Packet) ( + *psbt.Packet, error) { inputs := make([]psbt.PInput, len(packet.Inputs)) copy(inputs, packet.Inputs) diff --git a/testcontext_test.go b/testcontext_test.go index e9fe20cbc..e78799f7f 100644 --- a/testcontext_test.go +++ b/testcontext_test.go @@ -46,9 +46,7 @@ type testContext struct { // mockVerifySchnorrSigFail is used to simulate failed taproot keyspend // signature verification. If passed to the executeConfig we'll test an // uncooperative server and will fall back to scriptspend sweep. -func mockVerifySchnorrSigFail(pubKey *btcec.PublicKey, hash, - sig []byte) error { - +func mockVerifySchnorrSigFail(pubKey *btcec.PublicKey, hash, sig []byte) error { return fmt.Errorf("invalid sig") } @@ -64,8 +62,7 @@ func mockVerifySchnorrSigSuccess(pubKey *btcec.PublicKey, hash, func mockMuSig2SignSweep(ctx context.Context, protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash, paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte, - prevoutMap map[wire.OutPoint]*wire.TxOut) ( - []byte, []byte, error) { + prevoutMap map[wire.OutPoint]*wire.TxOut) ([]byte, []byte, error) { return nil, nil, nil } @@ -187,6 +184,7 @@ func (ctx *testContext) assertIsDone() { select { case <-ctx.statusChan: ctx.Context.T.Fatalf("not all status updates read") + default: } } @@ -222,6 +220,7 @@ func (ctx *testContext) assertStatus(expectedState loopdb.SwapState) { if update.State == expectedState { return } + case <-time.After(test.Timeout): ctx.Context.T.Fatalf("expected status %v not "+ "received in time", expectedState) diff --git a/tools/go.mod b/tools/go.mod index 819df12f3..08e6757de 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -3,6 +3,7 @@ module github.com/lightninglabs/loop/tools go 1.26 require ( + github.com/bhandras/llformat v0.1.1-beta // indirect // Once golangci-lint v2.4.1 update it here. github.com/golangci/golangci-lint/v2 v2.10.1 github.com/rinchsan/gosimports v0.3.8 @@ -222,3 +223,8 @@ require ( go.augendre.info/fatcontext v0.9.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect ) + +tool ( + github.com/bhandras/llformat/cmd/llformat + github.com/golangci/golangci-lint/v2/cmd/golangci-lint +) diff --git a/tools/go.sum b/tools/go.sum index 243eb20e3..7e660ee13 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -102,6 +102,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bhandras/llformat v0.1.1-beta h1:eXOyGfkvISWM7d5RUcwhvXPidEsDwLzOB1LN+d5FE9Q= +github.com/bhandras/llformat v0.1.1-beta/go.mod h1:fudtFasc8GnV1JFw+tB9tb7W0wj7IRUvUdGkOaw+q0s= github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w= github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= diff --git a/updates.go b/updates.go index 2f61b4506..8dea83813 100644 --- a/updates.go +++ b/updates.go @@ -12,12 +12,14 @@ import ( // does not survive server restarts; we will simply not have update logs if the // server restarts during swap execution. func subscribeAndLogUpdates(ctx context.Context, hash lntypes.Hash, - log *swap.PrefixLog, subscribe func(context.Context, - lntypes.Hash) (<-chan *ServerUpdate, <-chan error, error)) { + log *swap.PrefixLog, + subscribe func(context.Context, lntypes.Hash) (<-chan *ServerUpdate, + <-chan error, error)) { subscribeChan, errChan, err := subscribe(ctx, hash) if err != nil { log.Errorf("could not get swap subscription: %v", err) + return } @@ -25,8 +27,8 @@ func subscribeAndLogUpdates(ctx context.Context, hash lntypes.Hash, select { // Consume any updates and log them. case update := <-subscribeChan: - log.Infof("Server update: %v received, "+ - "timestamp: %v", update.State, update.Timestamp) + log.Infof("Server update: %v received, timestamp: %v", + update.State, update.Timestamp) // If we get an error from the server, we check whether it is // due to server exit, or restart, and log this information @@ -37,7 +39,6 @@ func subscribeAndLogUpdates(ctx context.Context, hash lntypes.Hash, log.Infof("swap subscription: %v", err) case nil: - default: log.Errorf("swap subscription error: %v", err) } diff --git a/utils.go b/utils.go index 69d79fda9..0aaf71304 100644 --- a/utils.go +++ b/utils.go @@ -89,8 +89,8 @@ func fetchChannelEdgesByID(ctx context.Context, func parseOutPoint(s string) (*wire.OutPoint, error) { split := strings.Split(s, ":") if len(split) != 2 { - return nil, fmt.Errorf("expecting outpoint to be in format "+ - "of txid:index: %s", s) + return nil, fmt.Errorf("expecting outpoint to be in format of "+ + "txid:index: %s", s) } index, err := strconv.ParseInt(split[1], 10, 32) @@ -181,15 +181,16 @@ func SelectHopHints(ctx context.Context, lndClient lndclient.LightningClient, return fetchChannelEdgesByID(ctx, lndClient, chanID) }, - GetAlias: func(id lnwire.ChannelID) ( - lnwire.ShortChannelID, error) { + GetAlias: func(id lnwire.ChannelID) (lnwire.ShortChannelID, + error) { return getAlias(aliasCache, id) }, } routeHints := invoicesrpcSelectHopHints( - lnwire.MilliSatoshi(amt*1000), cfg, openChannels, numMaxHophints, + lnwire.MilliSatoshi(amt*1000), cfg, openChannels, + numMaxHophints, ) return routeHints, nil @@ -197,8 +198,8 @@ func SelectHopHints(ctx context.Context, lndClient lndclient.LightningClient, // chanCanBeHopHint returns true if the target channel is eligible to be a hop // hint. -func chanCanBeHopHint(channel *HopHintInfo, cfg *SelectHopHintsCfg) ( - *models.ChannelEdgePolicy, bool) { +func chanCanBeHopHint(channel *HopHintInfo, + cfg *SelectHopHintsCfg) (*models.ChannelEdgePolicy, bool) { // Since we're only interested in our private channels, we'll skip // public ones. @@ -208,9 +209,9 @@ func chanCanBeHopHint(channel *HopHintInfo, cfg *SelectHopHintsCfg) ( // Make sure the channel is active. if !channel.IsActive { - log.Debugf("Skipping channel %v due to not "+ - "being eligible to forward payments", - channel.ShortChannelID) + log.Debugf("Skipping channel %v due to not being eligible to "+ + "forward payments", channel.ShortChannelID) + return nil, false } @@ -223,15 +224,16 @@ func chanCanBeHopHint(channel *HopHintInfo, cfg *SelectHopHintsCfg) ( copy(remotePub[:], channel.RemotePubkey.SerializeCompressed()) isRemoteNodePublic, err := cfg.IsPublicNode(remotePub) if err != nil { - log.Errorf("Unable to determine if node %x "+ - "is advertised: %v", remotePub, err) + log.Errorf("Unable to determine if node %x is advertised: %v", + remotePub, err) + return nil, false } if !isRemoteNodePublic { - log.Debugf("Skipping channel %v due to "+ - "counterparty %x being unadvertised", - channel.ShortChannelID, remotePub) + log.Debugf("Skipping channel %v due to counterparty %x being "+ + "unadvertised", channel.ShortChannelID, remotePub) + return nil, false } @@ -247,6 +249,7 @@ func chanCanBeHopHint(channel *HopHintInfo, cfg *SelectHopHintsCfg) ( log.Errorf("Unable to fetch the routing policies for "+ "the edges of the channel %v: %v", channel.ShortChannelID, err) + return nil, false } } @@ -265,9 +268,8 @@ func chanCanBeHopHint(channel *HopHintInfo, cfg *SelectHopHintsCfg) ( // addHopHint creates a hop hint out of the passed channel and channel policy. // The new hop hint is appended to the passed slice. -func addHopHint(hopHints *[][]zpay32.HopHint, - channel *HopHintInfo, chanPolicy *models.ChannelEdgePolicy, - aliasScid lnwire.ShortChannelID) { +func addHopHint(hopHints *[][]zpay32.HopHint, channel *HopHintInfo, + chanPolicy *models.ChannelEdgePolicy, aliasScid lnwire.ShortChannelID) { hopHint := zpay32.HopHint{ NodeID: channel.RemotePubkey, @@ -347,11 +349,12 @@ type SelectHopHintsCfg struct { // // We limit our number of hop hints like this to keep our invoice size down, // and to avoid leaking all our private channels when we don't need to. -func sufficientHints(numHints, maxHints, scalingFactor int, amount, - totalHintAmount lnwire.MilliSatoshi) bool { +func sufficientHints(numHints, maxHints, scalingFactor int, + amount, totalHintAmount lnwire.MilliSatoshi) bool { if numHints >= maxHints { log.Debug("Reached maximum number of hop hints") + return true } @@ -359,8 +362,7 @@ func sufficientHints(numHints, maxHints, scalingFactor int, amount, if totalHintAmount >= requiredAmount { log.Debugf("Total hint amount: %v has reached target hint "+ "bandwidth: %v (invoice amount: %v * factor: %v)", - totalHintAmount, requiredAmount, amount, - scalingFactor) + totalHintAmount, requiredAmount, amount, scalingFactor) return true } @@ -374,8 +376,8 @@ func sufficientHints(numHints, maxHints, scalingFactor int, amount, // // TODO(sputn1ck): remove when https://github.com/lightningnetwork/lnd/pull/7065 // is merged to a new lnd release. -func invoicesrpcSelectHopHints(amtMSat lnwire.MilliSatoshi, cfg *SelectHopHintsCfg, - openChannels []*HopHintInfo, +func invoicesrpcSelectHopHints(amtMSat lnwire.MilliSatoshi, + cfg *SelectHopHintsCfg, openChannels []*HopHintInfo, numMaxHophints int) [][]zpay32.HopHint { // We'll add our hop hints in two passes, first we'll add all channels diff --git a/utils/dust_limit.go b/utils/dust_limit.go index ba9b567a0..cd8b6f5d9 100644 --- a/utils/dust_limit.go +++ b/utils/dust_limit.go @@ -9,7 +9,11 @@ import ( // DustLimitForPkScript returns the dust limit for a given pkScript. An output // must be greater or equal to this value. func DustLimitForPkScript(pkscript []byte) btcutil.Amount { - return btcutil.Amount(mempool.GetDustThreshold(&wire.TxOut{ - PkScript: pkscript, - })) + return btcutil.Amount( + mempool.GetDustThreshold( + &wire.TxOut{ + PkScript: pkscript, + }, + ), + ) } diff --git a/utils/htlc_utils.go b/utils/htlc_utils.go index 49c201ce9..412bace30 100644 --- a/utils/htlc_utils.go +++ b/utils/htlc_utils.go @@ -19,8 +19,7 @@ func GetHtlc(hash lntypes.Hash, contract *loopdb.SwapContract, case swap.HtlcV2: return swap.NewHtlcV2( contract.CltvExpiry, contract.HtlcKeys.SenderScriptKey, - contract.HtlcKeys.ReceiverScriptKey, hash, - chainParams, + contract.HtlcKeys.ReceiverScriptKey, hash, chainParams, ) case swap.HtlcV3: @@ -32,13 +31,11 @@ func GetHtlc(hash lntypes.Hash, contract *loopdb.SwapContract, } return swap.NewHtlcV3( - muSig2Version, - contract.CltvExpiry, + muSig2Version, contract.CltvExpiry, contract.HtlcKeys.SenderInternalPubKey, contract.HtlcKeys.ReceiverInternalPubKey, contract.HtlcKeys.SenderScriptKey, - contract.HtlcKeys.ReceiverScriptKey, - hash, chainParams, + contract.HtlcKeys.ReceiverScriptKey, hash, chainParams, ) } @@ -53,7 +50,6 @@ func GetHtlcScriptVersion( // If the swap was initiated before we had our v3 script, use v2. if protocolVersion < loopdb.ProtocolVersionHtlcV3 || protocolVersion == loopdb.ProtocolVersionUnrecorded { - return swap.HtlcV2 } @@ -61,8 +57,8 @@ func GetHtlcScriptVersion( } // ObtainSwapPaymentAddr will retrieve the payment addr from the passed invoice. -func ObtainSwapPaymentAddr(swapInvoice string, chainParams *chaincfg.Params) ( - *[32]byte, error) { +func ObtainSwapPaymentAddr(swapInvoice string, + chainParams *chaincfg.Params) (*[32]byte, error) { swapPayReq, err := zpay32.Decode(swapInvoice, chainParams) if err != nil { diff --git a/utils/musig.go b/utils/musig.go index 74626c2ad..4c131e92b 100644 --- a/utils/musig.go +++ b/utils/musig.go @@ -22,8 +22,8 @@ func MuSig2Sign(version input.MuSig2Version, privKeys []*btcec.PrivateKey, version, signingKey, pubKeys, tweaks, nil, ) if err != nil { - return nil, fmt.Errorf("error creating "+ - "signing context: %v", err) + return nil, fmt.Errorf("error creating signing "+ + "context: %v", err) } sessions[i] = muSigSession @@ -42,8 +42,8 @@ func MuSig2Sign(version input.MuSig2Version, privKeys []*btcec.PrivateKey, _, err := sessions[j].RegisterPubNonce(nonce) if err != nil { - return nil, fmt.Errorf("error sharing "+ - "MuSig2 nonces: %v", err) + return nil, fmt.Errorf("error sharing MuSig2 "+ + "nonces: %v", err) } } } @@ -74,8 +74,7 @@ func MuSig2Sign(version input.MuSig2Version, privKeys []*btcec.PrivateKey, } if !haveAllSigs { - return nil, fmt.Errorf("combinging MuSig2 signatures " + - "failed") + return nil, fmt.Errorf("combinging MuSig2 signatures failed") } return sessions[0].FinalSig().Serialize(), nil diff --git a/utils/tx_sort.go b/utils/tx_sort.go index bc31e6a1b..f01ff35e3 100644 --- a/utils/tx_sort.go +++ b/utils/tx_sort.go @@ -11,5 +11,6 @@ func Bip69Less(output1, output2 *wire.TxOut) bool { if output1.Value == output2.Value { return bytes.Compare(output1.PkScript, output2.PkScript) < 0 } + return output1.Value < output2.Value } diff --git a/version.go b/version.go index 405e35c45..a50e300e5 100644 --- a/version.go +++ b/version.go @@ -58,6 +58,7 @@ var AgentName = defaultAgentName // Version returns the application version as a properly formed string per the // semantic versioning 2.0.0 spec (http://semver.org/). func Version() string { + // Append commit hash of current build to version. return semanticVersion() } @@ -66,12 +67,11 @@ func Version() string { // per the semantic versioning 2.0.0 spec (http://semver.org/), followed by the // most recent git tag and commit hash the build was built on. func RichVersion() string { + // Append the most recent git tag and commit hash of the current build // to version. - return fmt.Sprintf( - "%s commit=%s commit_hash=%s", semanticVersion(), Commit, - CommitHash, - ) + return fmt.Sprintf("%s commit=%s commit_hash=%s", semanticVersion(), + Commit, CommitHash) } // UserAgent returns the full user agent string that identifies the software @@ -95,10 +95,8 @@ func UserAgent(initiator string) string { cleanInitiator = cleanInitiator[:int(math.Min(float64(strLen), 150))] // Assemble full string, including the commit hash of current build. - return fmt.Sprintf( - "%s/v%s/commit=%s%s", AgentName, semanticVersion(), Commit, - cleanInitiator, - ) + return fmt.Sprintf("%s/v%s/commit=%s%s", AgentName, semanticVersion(), + Commit, cleanInitiator) } // semanticVersion returns the SemVer part of the version. @@ -127,5 +125,6 @@ func normalizeVerString(str, alphabet string) string { result.WriteRune(r) } } + return result.String() }