mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
multi: finalize rename from uncharge to loop out
This commit is contained in:
parent
e299dc696c
commit
94f347e673
26 changed files with 656 additions and 572 deletions
57
client.go
57
client.go
|
|
@ -11,8 +11,9 @@ import (
|
|||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/loop/lndclient"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/swap"
|
||||
"github.com/lightninglabs/loop/sweep"
|
||||
"github.com/lightninglabs/loop/utils"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
)
|
||||
|
||||
|
|
@ -66,7 +67,7 @@ type Client struct {
|
|||
func NewClient(dbDir string, serverAddress string, insecure bool,
|
||||
lnd *lndclient.LndServices) (*Client, func(), error) {
|
||||
|
||||
store, err := newBoltSwapClientStore(dbDir)
|
||||
store, err := loopdb.NewBoltSwapStore(dbDir)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
@ -112,9 +113,9 @@ func NewClient(dbDir string, serverAddress string, insecure bool,
|
|||
return client, cleanup, nil
|
||||
}
|
||||
|
||||
// GetUnchargeSwaps returns a list of all swaps currently in the database.
|
||||
func (s *Client) GetUnchargeSwaps() ([]*PersistentUncharge, error) {
|
||||
return s.Store.getUnchargeSwaps()
|
||||
// FetchLoopOutSwaps returns a list of all swaps currently in the database.
|
||||
func (s *Client) FetchLoopOutSwaps() ([]*loopdb.LoopOut, error) {
|
||||
return s.Store.FetchLoopOutSwaps()
|
||||
}
|
||||
|
||||
// Run is a blocking call that executes all swaps. Any pending swaps are
|
||||
|
|
@ -143,7 +144,7 @@ func (s *Client) Run(ctx context.Context,
|
|||
|
||||
// Query store before starting event loop to prevent new swaps from
|
||||
// being treated as swaps that need to be resumed.
|
||||
pendingSwaps, err := s.Store.getUnchargeSwaps()
|
||||
pendingSwaps, err := s.Store.FetchLoopOutSwaps()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -193,17 +194,17 @@ func (s *Client) Run(ctx context.Context,
|
|||
|
||||
// resumeSwaps restarts all pending swaps from the provided list.
|
||||
func (s *Client) resumeSwaps(ctx context.Context,
|
||||
swaps []*PersistentUncharge) {
|
||||
swaps []*loopdb.LoopOut) {
|
||||
|
||||
for _, pend := range swaps {
|
||||
if pend.State().Type() != StateTypePending {
|
||||
if pend.State().Type() != loopdb.StateTypePending {
|
||||
continue
|
||||
}
|
||||
swapCfg := &swapConfig{
|
||||
lnd: s.lndServices,
|
||||
store: s.Store,
|
||||
}
|
||||
swap, err := resumeUnchargeSwap(ctx, swapCfg, pend)
|
||||
swap, err := resumeLoopOutSwap(ctx, swapCfg, pend)
|
||||
if err != nil {
|
||||
logger.Errorf("resuming swap: %v", err)
|
||||
continue
|
||||
|
|
@ -213,21 +214,21 @@ func (s *Client) resumeSwaps(ctx context.Context,
|
|||
}
|
||||
}
|
||||
|
||||
// Uncharge initiates a uncharge swap. It blocks until the swap is initiation
|
||||
// LoopOut initiates a loop out swap. It blocks until the swap is initiation
|
||||
// with the swap server is completed (typically this takes only a short amount
|
||||
// of time). From there on further status information can be acquired through
|
||||
// the status channel returned from the Run call.
|
||||
//
|
||||
// When the call returns, the swap has been persisted and will be
|
||||
// resumed automatically after restarts.
|
||||
// When the call returns, the swap has been persisted and will be resumed
|
||||
// automatically after restarts.
|
||||
//
|
||||
// The return value is a hash that uniquely identifies the new swap.
|
||||
func (s *Client) Uncharge(globalCtx context.Context,
|
||||
request *UnchargeRequest) (*lntypes.Hash, error) {
|
||||
func (s *Client) LoopOut(globalCtx context.Context,
|
||||
request *OutRequest) (*lntypes.Hash, error) {
|
||||
|
||||
logger.Infof("Uncharge %v to %v (channel: %v)",
|
||||
logger.Infof("LoopOut %v to %v (channel: %v)",
|
||||
request.Amount, request.DestAddr,
|
||||
request.UnchargeChannel,
|
||||
request.LoopOutChannel,
|
||||
)
|
||||
|
||||
if err := s.waitForInitialized(globalCtx); err != nil {
|
||||
|
|
@ -241,7 +242,7 @@ func (s *Client) Uncharge(globalCtx context.Context,
|
|||
store: s.Store,
|
||||
server: s.Server,
|
||||
}
|
||||
swap, err := newUnchargeSwap(
|
||||
swap, err := newLoopOutSwap(
|
||||
globalCtx, swapCfg, initiationHeight, request,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -256,13 +257,13 @@ func (s *Client) Uncharge(globalCtx context.Context,
|
|||
return &swap.hash, nil
|
||||
}
|
||||
|
||||
// UnchargeQuote takes a Uncharge amount and returns a break down of estimated
|
||||
// LoopOutQuote takes a LoopOut amount and returns a break down 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) UnchargeQuote(ctx context.Context,
|
||||
request *UnchargeQuoteRequest) (*UnchargeQuote, error) {
|
||||
func (s *Client) LoopOutQuote(ctx context.Context,
|
||||
request *LoopOutQuoteRequest) (*LoopOutQuote, error) {
|
||||
|
||||
terms, err := s.Server.GetUnchargeTerms(ctx)
|
||||
terms, err := s.Server.GetLoopOutTerms(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -277,30 +278,30 @@ func (s *Client) UnchargeQuote(ctx context.Context,
|
|||
|
||||
logger.Infof("Offchain swap destination: %x", terms.SwapPaymentDest)
|
||||
|
||||
swapFee := utils.CalcFee(
|
||||
swapFee := swap.CalcFee(
|
||||
request.Amount, terms.SwapFeeBase, terms.SwapFeeRate,
|
||||
)
|
||||
|
||||
minerFee, err := s.sweeper.GetSweepFee(
|
||||
ctx, utils.QuoteHtlc.MaxSuccessWitnessSize,
|
||||
ctx, swap.QuoteHtlc.MaxSuccessWitnessSize,
|
||||
request.SweepConfTarget,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &UnchargeQuote{
|
||||
return &LoopOutQuote{
|
||||
SwapFee: swapFee,
|
||||
MinerFee: minerFee,
|
||||
PrepayAmount: btcutil.Amount(terms.PrepayAmt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UnchargeTerms returns the terms on which the server executes swaps.
|
||||
func (s *Client) UnchargeTerms(ctx context.Context) (
|
||||
*UnchargeTerms, error) {
|
||||
// LoopOutTerms returns the terms on which the server executes swaps.
|
||||
func (s *Client) LoopOutTerms(ctx context.Context) (
|
||||
*LoopOutTerms, error) {
|
||||
|
||||
return s.Server.GetUnchargeTerms(ctx)
|
||||
return s.Server.GetLoopOutTerms(ctx)
|
||||
}
|
||||
|
||||
// waitForInitialized for swaps to be resumed and executor ready.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/loop/lndclient"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/test"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
)
|
||||
|
|
@ -17,7 +18,7 @@ var (
|
|||
testAddr, _ = btcutil.DecodeAddress(
|
||||
"rbsHiPKwAgxeo1EQYiyzJTkA8XEmWSVAKx", nil)
|
||||
|
||||
testRequest = &UnchargeRequest{
|
||||
testRequest = &OutRequest{
|
||||
Amount: btcutil.Amount(50000),
|
||||
DestAddr: testAddr,
|
||||
MaxMinerFee: 50000,
|
||||
|
|
@ -40,13 +41,13 @@ func TestSuccess(t *testing.T) {
|
|||
|
||||
// Initiate uncharge.
|
||||
|
||||
hash, err := ctx.swapClient.Uncharge(context.Background(), testRequest)
|
||||
hash, err := ctx.swapClient.LoopOut(context.Background(), testRequest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ctx.assertStored()
|
||||
ctx.assertStatus(StateInitiated)
|
||||
ctx.assertStatus(loopdb.StateInitiated)
|
||||
|
||||
signalSwapPaymentResult := ctx.AssertPaid(swapInvoiceDesc)
|
||||
signalPrepaymentResult := ctx.AssertPaid(prepayInvoiceDesc)
|
||||
|
|
@ -67,13 +68,13 @@ func TestFailOffchain(t *testing.T) {
|
|||
|
||||
ctx := createClientTestContext(t, nil)
|
||||
|
||||
_, err := ctx.swapClient.Uncharge(context.Background(), testRequest)
|
||||
_, err := ctx.swapClient.LoopOut(context.Background(), testRequest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ctx.assertStored()
|
||||
ctx.assertStatus(StateInitiated)
|
||||
ctx.assertStatus(loopdb.StateInitiated)
|
||||
|
||||
signalSwapPaymentResult := ctx.AssertPaid(swapInvoiceDesc)
|
||||
signalPrepaymentResult := ctx.AssertPaid(prepayInvoiceDesc)
|
||||
|
|
@ -86,9 +87,9 @@ func TestFailOffchain(t *testing.T) {
|
|||
signalPrepaymentResult(
|
||||
errors.New(lndclient.PaymentResultUnknownPaymentHash),
|
||||
)
|
||||
ctx.assertStatus(StateFailOffchainPayments)
|
||||
ctx.assertStatus(loopdb.StateFailOffchainPayments)
|
||||
|
||||
ctx.assertStoreFinished(StateFailOffchainPayments)
|
||||
ctx.assertStoreFinished(loopdb.StateFailOffchainPayments)
|
||||
|
||||
ctx.finish()
|
||||
}
|
||||
|
|
@ -105,7 +106,7 @@ func TestFailWrongAmount(t *testing.T) {
|
|||
// Modify mock for this subtest.
|
||||
modifier(ctx.serverMock)
|
||||
|
||||
_, err := ctx.swapClient.Uncharge(
|
||||
_, err := ctx.swapClient.LoopOut(
|
||||
context.Background(), testRequest,
|
||||
)
|
||||
if err != expectedErr {
|
||||
|
|
@ -175,17 +176,17 @@ func testResume(t *testing.T, expired, preimageRevealed, expectSuccess bool) {
|
|||
var receiverKey [33]byte
|
||||
copy(receiverKey[:], receiverPubKey.SerializeCompressed())
|
||||
|
||||
state := StateInitiated
|
||||
state := loopdb.StateInitiated
|
||||
if preimageRevealed {
|
||||
state = StatePreimageRevealed
|
||||
state = loopdb.StatePreimageRevealed
|
||||
}
|
||||
pendingSwap := &PersistentUncharge{
|
||||
Contract: &UnchargeContract{
|
||||
pendingSwap := &loopdb.LoopOut{
|
||||
Contract: &loopdb.LoopOutContract{
|
||||
DestAddr: dest,
|
||||
SwapInvoice: swapPayReq,
|
||||
SweepConfTarget: 2,
|
||||
MaxSwapRoutingFee: 70000,
|
||||
SwapContract: SwapContract{
|
||||
SwapContract: loopdb.SwapContract{
|
||||
Preimage: preimage,
|
||||
AmountRequested: amt,
|
||||
CltvExpiry: 744,
|
||||
|
|
@ -196,7 +197,7 @@ func testResume(t *testing.T, expired, preimageRevealed, expectSuccess bool) {
|
|||
MaxMinerFee: 50000,
|
||||
},
|
||||
},
|
||||
Events: []*PersistentUnchargeEvent{
|
||||
Events: []*loopdb.LoopOutEvent{
|
||||
{
|
||||
State: state,
|
||||
},
|
||||
|
|
@ -210,12 +211,12 @@ func testResume(t *testing.T, expired, preimageRevealed, expectSuccess bool) {
|
|||
pendingSwap.Contract.CltvExpiry = 610
|
||||
}
|
||||
|
||||
ctx := createClientTestContext(t, []*PersistentUncharge{pendingSwap})
|
||||
ctx := createClientTestContext(t, []*loopdb.LoopOut{pendingSwap})
|
||||
|
||||
if preimageRevealed {
|
||||
ctx.assertStatus(StatePreimageRevealed)
|
||||
ctx.assertStatus(loopdb.StatePreimageRevealed)
|
||||
} else {
|
||||
ctx.assertStatus(StateInitiated)
|
||||
ctx.assertStatus(loopdb.StateInitiated)
|
||||
}
|
||||
|
||||
signalSwapPaymentResult := ctx.AssertPaid(swapInvoiceDesc)
|
||||
|
|
@ -228,8 +229,8 @@ func testResume(t *testing.T, expired, preimageRevealed, expectSuccess bool) {
|
|||
signalPrepaymentResult(nil)
|
||||
|
||||
if !expectSuccess {
|
||||
ctx.assertStatus(StateFailTimeout)
|
||||
ctx.assertStoreFinished(StateFailTimeout)
|
||||
ctx.assertStatus(loopdb.StateFailTimeout)
|
||||
ctx.assertStoreFinished(loopdb.StateFailTimeout)
|
||||
ctx.finish()
|
||||
return
|
||||
}
|
||||
|
|
@ -259,7 +260,7 @@ func testSuccess(ctx *testContext, amt btcutil.Amount, hash lntypes.Hash,
|
|||
ctx.expiryChan <- testTime
|
||||
|
||||
if !preimageRevealed {
|
||||
ctx.assertStatus(StatePreimageRevealed)
|
||||
ctx.assertStatus(loopdb.StatePreimageRevealed)
|
||||
ctx.assertStorePreimageReveal()
|
||||
}
|
||||
|
||||
|
|
@ -283,9 +284,9 @@ func testSuccess(ctx *testContext, amt btcutil.Amount, hash lntypes.Hash,
|
|||
|
||||
ctx.NotifySpend(sweepTx, 0)
|
||||
|
||||
ctx.assertStatus(StateSuccess)
|
||||
ctx.assertStatus(loopdb.StateSuccess)
|
||||
|
||||
ctx.assertStoreFinished(StateSuccess)
|
||||
ctx.assertStoreFinished(loopdb.StateSuccess)
|
||||
|
||||
ctx.finish()
|
||||
}
|
||||
|
|
|
|||
177
cmd/loop/commands.go
Normal file
177
cmd/loop/commands.go
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/loop/looprpc"
|
||||
"github.com/lightninglabs/loop/swap"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var loopOutCommand = cli.Command{
|
||||
Name: "out",
|
||||
Usage: "perform an off-chain to on-chain swap (looping out)",
|
||||
ArgsUsage: "amt [addr]",
|
||||
Description: `
|
||||
Attempts loop out the target amount into either the backing lnd's
|
||||
wallet, or a targeted address.
|
||||
|
||||
The amount is to be specified in satoshis.
|
||||
|
||||
Optionally a BASE58/bech32 encoded bitcoin destination address may be
|
||||
specified. If not specified, a new wallet address will be generated.`,
|
||||
Flags: []cli.Flag{
|
||||
cli.Uint64Flag{
|
||||
Name: "channel",
|
||||
Usage: "the 8-byte compact channel ID of the channel to loop out",
|
||||
},
|
||||
},
|
||||
Action: loopOut,
|
||||
}
|
||||
|
||||
func loopOut(ctx *cli.Context) error {
|
||||
// Show command help if no arguments and flags were provided.
|
||||
if ctx.NArg() < 1 {
|
||||
cli.ShowCommandHelp(ctx, "out")
|
||||
return nil
|
||||
}
|
||||
|
||||
args := ctx.Args()
|
||||
|
||||
amt, err := parseAmt(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var destAddr string
|
||||
args = args.Tail()
|
||||
if args.Present() {
|
||||
destAddr = args.First()
|
||||
}
|
||||
|
||||
client, cleanup, err := getClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
quote, err := client.GetLoopOutQuote(
|
||||
context.Background(),
|
||||
&looprpc.QuoteRequest{
|
||||
Amt: int64(amt),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
limits := getLimits(amt, quote)
|
||||
|
||||
if err := displayLimits(amt, limits); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var unchargeChannel uint64
|
||||
if ctx.IsSet("channel") {
|
||||
unchargeChannel = ctx.Uint64("channel")
|
||||
}
|
||||
|
||||
resp, err := client.LoopOut(context.Background(), &looprpc.LoopOutRequest{
|
||||
Amt: int64(amt),
|
||||
Dest: destAddr,
|
||||
MaxMinerFee: int64(limits.maxMinerFee),
|
||||
MaxPrepayAmt: int64(limits.maxPrepayAmt),
|
||||
MaxSwapFee: int64(limits.maxSwapFee),
|
||||
MaxPrepayRoutingFee: int64(limits.maxPrepayRoutingFee),
|
||||
MaxSwapRoutingFee: int64(limits.maxSwapRoutingFee),
|
||||
LoopOutChannel: unchargeChannel,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Swap initiated with id: %v\n", resp.Id[:8])
|
||||
fmt.Printf("Run `loop monitor` to monitor progress.\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var termsCommand = cli.Command{
|
||||
Name: "terms",
|
||||
Usage: "show current server swap terms",
|
||||
Action: terms,
|
||||
}
|
||||
|
||||
func terms(ctx *cli.Context) error {
|
||||
client, cleanup, err := getClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
terms, err := client.GetLoopOutTerms(
|
||||
context.Background(), &looprpc.TermsRequest{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Amount: %d - %d\n",
|
||||
btcutil.Amount(terms.MinSwapAmount),
|
||||
btcutil.Amount(terms.MaxSwapAmount),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printTerms := func(terms *looprpc.TermsResponse) {
|
||||
fmt.Printf("Amount: %d - %d\n",
|
||||
btcutil.Amount(terms.MinSwapAmount),
|
||||
btcutil.Amount(terms.MaxSwapAmount),
|
||||
)
|
||||
fmt.Printf("Fee: %d + %.4f %% (%d prepaid)\n",
|
||||
btcutil.Amount(terms.SwapFeeBase),
|
||||
swap.FeeRateAsPercentage(terms.SwapFeeRate),
|
||||
btcutil.Amount(terms.PrepayAmt),
|
||||
)
|
||||
|
||||
fmt.Printf("Cltv delta: %v blocks\n", terms.CltvDelta)
|
||||
}
|
||||
|
||||
fmt.Println("Loop Out")
|
||||
fmt.Println("--------")
|
||||
printTerms(terms)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var monitorCommand = cli.Command{
|
||||
Name: "monitor",
|
||||
Usage: "monitor progress of any active swaps",
|
||||
Description: "Allows the user to monitor progress of any active swaps",
|
||||
Action: monitor,
|
||||
}
|
||||
|
||||
func monitor(ctx *cli.Context) error {
|
||||
client, cleanup, err := getClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
stream, err := client.Monitor(
|
||||
context.Background(), &looprpc.MonitorRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
swap, err := stream.Recv()
|
||||
if err != nil {
|
||||
return fmt.Errorf("recv: %v", err)
|
||||
}
|
||||
logSwap(swap)
|
||||
}
|
||||
}
|
||||
196
cmd/loop/main.go
196
cmd/loop/main.go
|
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
|
@ -9,7 +8,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/lightninglabs/loop/looprpc"
|
||||
"github.com/lightninglabs/loop/utils"
|
||||
"github.com/lightninglabs/loop/swap"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
|
||||
|
|
@ -18,131 +17,50 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
swapdAddress = "localhost:11010"
|
||||
loopdAddress = "localhost:11010"
|
||||
|
||||
// Define route independent max routing fees. We have currently no way
|
||||
// to get a reliable estimate of the routing fees. Best we can do is the
|
||||
// minimum routing fees, which is not very indicative.
|
||||
// to get a reliable estimate of the routing fees. Best we can do is
|
||||
// the minimum routing fees, which is not very indicative.
|
||||
maxRoutingFeeBase = btcutil.Amount(10)
|
||||
|
||||
maxRoutingFeeRate = int64(50000)
|
||||
)
|
||||
|
||||
var unchargeCommand = cli.Command{
|
||||
Name: "uncharge",
|
||||
Usage: "perform an off-chain to on-chain swap",
|
||||
ArgsUsage: "amt [addr]",
|
||||
Description: `
|
||||
Send the amount in satoshis specified by the amt argument on-chain.
|
||||
|
||||
Optionally a BASE58 encoded bitcoin destination address may be
|
||||
specified. If not specified, a new wallet address will be generated.`,
|
||||
Flags: []cli.Flag{
|
||||
cli.Uint64Flag{
|
||||
Name: "channel",
|
||||
Usage: "the 8-byte compact channel ID of the channel to uncharge",
|
||||
},
|
||||
},
|
||||
Action: uncharge,
|
||||
}
|
||||
|
||||
var termsCommand = cli.Command{
|
||||
Name: "terms",
|
||||
Usage: "show current server swap terms",
|
||||
Action: terms,
|
||||
func fatal(err error) {
|
||||
fmt.Fprintf(os.Stderr, "[loop] %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Version = "0.0.1"
|
||||
app.Usage = "command line interface to swapd"
|
||||
app.Commands = []cli.Command{unchargeCommand, termsCommand}
|
||||
app.Action = monitor
|
||||
app.Name = "loop"
|
||||
app.Usage = "control plane for your loopd"
|
||||
app.Commands = []cli.Command{
|
||||
loopOutCommand, termsCommand, monitorCommand,
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func terms(ctx *cli.Context) error {
|
||||
client, cleanup, err := getClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
terms, err := client.GetUnchargeTerms(
|
||||
context.Background(), &looprpc.TermsRequest{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Amount: %d - %d\n",
|
||||
btcutil.Amount(terms.MinSwapAmount),
|
||||
btcutil.Amount(terms.MaxSwapAmount),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printTerms := func(terms *looprpc.TermsResponse) {
|
||||
fmt.Printf("Amount: %d - %d\n",
|
||||
btcutil.Amount(terms.MinSwapAmount),
|
||||
btcutil.Amount(terms.MaxSwapAmount),
|
||||
)
|
||||
fmt.Printf("Fee: %d + %.4f %% (%d prepaid)\n",
|
||||
btcutil.Amount(terms.SwapFeeBase),
|
||||
utils.FeeRateAsPercentage(terms.SwapFeeRate),
|
||||
btcutil.Amount(terms.PrepayAmt),
|
||||
)
|
||||
|
||||
fmt.Printf("Cltv delta: %v blocks\n", terms.CltvDelta)
|
||||
}
|
||||
|
||||
fmt.Println("Uncharge")
|
||||
fmt.Println("--------")
|
||||
printTerms(terms)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func monitor(ctx *cli.Context) error {
|
||||
client, cleanup, err := getClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
stream, err := client.Monitor(
|
||||
context.Background(), &looprpc.MonitorRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
swap, err := stream.Recv()
|
||||
if err != nil {
|
||||
return fmt.Errorf("recv: %v", err)
|
||||
}
|
||||
logSwap(swap)
|
||||
fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func getClient(ctx *cli.Context) (looprpc.SwapClientClient, func(), error) {
|
||||
conn, err := getSwapCliConn(swapdAddress)
|
||||
conn, err := getClientConn(loopdAddress)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
cleanup := func() { conn.Close() }
|
||||
|
||||
swapCliClient := looprpc.NewSwapClientClient(conn)
|
||||
return swapCliClient, cleanup, nil
|
||||
loopClient := looprpc.NewSwapClientClient(conn)
|
||||
return loopClient, cleanup, nil
|
||||
}
|
||||
|
||||
func getMaxRoutingFee(amt btcutil.Amount) btcutil.Amount {
|
||||
return utils.CalcFee(amt, maxRoutingFeeBase, maxRoutingFeeRate)
|
||||
return swap.CalcFee(amt, maxRoutingFeeBase, maxRoutingFeeRate)
|
||||
}
|
||||
|
||||
type limits struct {
|
||||
|
|
@ -160,8 +78,8 @@ func getLimits(amt btcutil.Amount, quote *looprpc.QuoteResponse) *limits {
|
|||
quote.PrepayAmt,
|
||||
)),
|
||||
|
||||
// Apply a multiplier to the estimated miner fee, to not get the swap
|
||||
// canceled because fees increased in the mean time.
|
||||
// Apply a multiplier to the estimated miner fee, to not get
|
||||
// the swap canceled because fees increased in the mean time.
|
||||
maxMinerFee: btcutil.Amount(quote.MinerFee) * 3,
|
||||
|
||||
maxSwapFee: btcutil.Amount(quote.SwapFee),
|
||||
|
|
@ -173,12 +91,15 @@ func displayLimits(amt btcutil.Amount, l *limits) error {
|
|||
totalSuccessMax := l.maxSwapRoutingFee + l.maxPrepayRoutingFee +
|
||||
l.maxMinerFee + l.maxSwapFee
|
||||
|
||||
fmt.Printf("Max swap fees for %d uncharge: %d\n",
|
||||
fmt.Printf("Max swap fees for %d loop out: %d\n",
|
||||
btcutil.Amount(amt), totalSuccessMax,
|
||||
)
|
||||
|
||||
fmt.Printf("CONTINUE SWAP? (y/n), expand fee detail (x): ")
|
||||
|
||||
var answer string
|
||||
fmt.Scanln(&answer)
|
||||
|
||||
switch answer {
|
||||
case "y":
|
||||
return nil
|
||||
|
|
@ -211,73 +132,6 @@ func parseAmt(text string) (btcutil.Amount, error) {
|
|||
return btcutil.Amount(amtInt64), nil
|
||||
}
|
||||
|
||||
func uncharge(ctx *cli.Context) error {
|
||||
// Show command help if no arguments and flags were provided.
|
||||
if ctx.NArg() < 1 {
|
||||
cli.ShowCommandHelp(ctx, "uncharge")
|
||||
return nil
|
||||
}
|
||||
|
||||
args := ctx.Args()
|
||||
|
||||
amt, err := parseAmt(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var destAddr string
|
||||
args = args.Tail()
|
||||
if args.Present() {
|
||||
destAddr = args.First()
|
||||
}
|
||||
|
||||
client, cleanup, err := getClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
quote, err := client.GetUnchargeQuote(
|
||||
context.Background(),
|
||||
&looprpc.QuoteRequest{
|
||||
Amt: int64(amt),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
limits := getLimits(amt, quote)
|
||||
|
||||
if err := displayLimits(amt, limits); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var unchargeChannel uint64
|
||||
if ctx.IsSet("channel") {
|
||||
unchargeChannel = ctx.Uint64("channel")
|
||||
}
|
||||
|
||||
resp, err := client.Uncharge(context.Background(), &looprpc.UnchargeRequest{
|
||||
Amt: int64(amt),
|
||||
Dest: destAddr,
|
||||
MaxMinerFee: int64(limits.maxMinerFee),
|
||||
MaxPrepayAmt: int64(limits.maxPrepayAmt),
|
||||
MaxSwapFee: int64(limits.maxSwapFee),
|
||||
MaxPrepayRoutingFee: int64(limits.maxPrepayRoutingFee),
|
||||
MaxSwapRoutingFee: int64(limits.maxSwapRoutingFee),
|
||||
UnchargeChannel: unchargeChannel,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Swap initiated with id: %v\n", resp.Id[:8])
|
||||
fmt.Printf("Run swapcli without a command to monitor progress.\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func logSwap(swap *looprpc.SwapStatus) {
|
||||
fmt.Printf("%v %v %v %v - %v\n",
|
||||
time.Unix(0, swap.LastUpdateTime).Format(time.RFC3339),
|
||||
|
|
@ -286,7 +140,7 @@ func logSwap(swap *looprpc.SwapStatus) {
|
|||
)
|
||||
}
|
||||
|
||||
func getSwapCliConn(address string) (*grpc.ClientConn, error) {
|
||||
func getClientConn(address string) (*grpc.ClientConn, error) {
|
||||
opts := []grpc.DialOption{
|
||||
grpc.WithInsecure(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lightninglabs/loop/client"
|
||||
"github.com/lightninglabs/loop"
|
||||
"github.com/lightninglabs/loop/looprpc"
|
||||
"github.com/urfave/cli"
|
||||
"google.golang.org/grpc"
|
||||
|
|
@ -34,13 +34,13 @@ func daemon(ctx *cli.Context) error {
|
|||
// Before starting the client, build an in-memory view of all swaps.
|
||||
// This view is used to update newly connected clients with the most
|
||||
// recent swaps.
|
||||
storedSwaps, err := swapClient.GetUnchargeSwaps()
|
||||
storedSwaps, err := swapClient.FetchLoopOutSwaps()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, swap := range storedSwaps {
|
||||
swaps[swap.Hash] = client.SwapInfo{
|
||||
SwapType: client.SwapTypeUncharge,
|
||||
swaps[swap.Hash] = loop.SwapInfo{
|
||||
SwapType: loop.TypeOut,
|
||||
SwapContract: swap.Contract.SwapContract,
|
||||
State: swap.State(),
|
||||
SwapHash: swap.Hash,
|
||||
|
|
@ -68,7 +68,7 @@ func daemon(ctx *cli.Context) error {
|
|||
}
|
||||
defer lis.Close()
|
||||
|
||||
statusChan := make(chan client.SwapInfo)
|
||||
statusChan := make(chan loop.SwapInfo)
|
||||
|
||||
mainCtx, cancel := context.WithCancel(context.Background())
|
||||
var wg sync.WaitGroup
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import (
|
|||
"github.com/btcsuite/btclog"
|
||||
)
|
||||
|
||||
// log is a logger that is initialized with no output filters. This
|
||||
// means the package will not perform any logging by default until the caller
|
||||
// requests it.
|
||||
// log is a logger that is initialized with no output filters. This means the
|
||||
// package will not perform any logging by default until the caller requests
|
||||
// it.
|
||||
var (
|
||||
backendLog = btclog.NewBackend(logWriter{})
|
||||
logger = backendLog.Logger("SWAPD")
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/loop/client"
|
||||
"github.com/lightninglabs/loop"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
|
@ -20,7 +20,7 @@ var (
|
|||
defaultListenAddr = fmt.Sprintf("localhost:%d", defaultListenPort)
|
||||
defaultSwapletDir = btcutil.AppDataDir("swaplet", false)
|
||||
|
||||
swaps = make(map[lntypes.Hash]client.SwapInfo)
|
||||
swaps = make(map[lntypes.Hash]loop.SwapInfo)
|
||||
subscribers = make(map[int]chan<- interface{})
|
||||
nextSubscriberID int
|
||||
swapsLock sync.Mutex
|
||||
|
|
@ -58,9 +58,12 @@ func main() {
|
|||
Usage: "disable tls",
|
||||
},
|
||||
}
|
||||
app.Name = "loopd"
|
||||
app.Version = "0.0.1"
|
||||
app.Usage = "swaps execution daemon"
|
||||
app.Commands = []cli.Command{viewCommand}
|
||||
app.Usage = "Lightning Loop Client Daemon"
|
||||
app.Commands = []cli.Command{
|
||||
viewCommand,
|
||||
}
|
||||
app.Action = daemon
|
||||
|
||||
err := app.Run(os.Args)
|
||||
|
|
|
|||
|
|
@ -7,31 +7,32 @@ import (
|
|||
|
||||
"github.com/lightningnetwork/lnd/queue"
|
||||
|
||||
"github.com/lightninglabs/loop"
|
||||
"github.com/lightninglabs/loop/lndclient"
|
||||
"github.com/lightninglabs/loop/utils"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/swap"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/loop/client"
|
||||
"github.com/lightninglabs/loop/looprpc"
|
||||
)
|
||||
|
||||
const completedSwapsCount = 5
|
||||
|
||||
// swapClientServer implements the grpc service exposed by swapd.
|
||||
// swapClientServer implements the grpc service exposed by loopd.
|
||||
type swapClientServer struct {
|
||||
impl *client.Client
|
||||
impl *loop.Client
|
||||
lnd *lndclient.LndServices
|
||||
}
|
||||
|
||||
// Uncharge initiates an uncharge swap with the given parameters. The call
|
||||
// LoopOut initiates an loop out swap with the given parameters. The call
|
||||
// returns after the swap has been set up with the swap server. From that point
|
||||
// onwards, progress can be tracked via the UnchargeStatus stream that is
|
||||
// onwards, progress can be tracked via the LoopOutStatus stream that is
|
||||
// returned from Monitor().
|
||||
func (s *swapClientServer) Uncharge(ctx context.Context,
|
||||
in *looprpc.UnchargeRequest) (
|
||||
func (s *swapClientServer) LoopOut(ctx context.Context,
|
||||
in *looprpc.LoopOutRequest) (
|
||||
*looprpc.SwapResponse, error) {
|
||||
|
||||
logger.Infof("Uncharge request received")
|
||||
logger.Infof("LoopOut request received")
|
||||
|
||||
var sweepAddr btcutil.Address
|
||||
if in.Dest == "" {
|
||||
|
|
@ -49,7 +50,7 @@ func (s *swapClientServer) Uncharge(ctx context.Context,
|
|||
}
|
||||
}
|
||||
|
||||
req := &client.UnchargeRequest{
|
||||
req := &loop.OutRequest{
|
||||
Amount: btcutil.Amount(in.Amt),
|
||||
DestAddr: sweepAddr,
|
||||
MaxMinerFee: btcutil.Amount(in.MaxMinerFee),
|
||||
|
|
@ -59,12 +60,12 @@ func (s *swapClientServer) Uncharge(ctx context.Context,
|
|||
MaxSwapFee: btcutil.Amount(in.MaxSwapFee),
|
||||
SweepConfTarget: defaultConfTarget,
|
||||
}
|
||||
if in.UnchargeChannel != 0 {
|
||||
req.UnchargeChannel = &in.UnchargeChannel
|
||||
if in.LoopOutChannel != 0 {
|
||||
req.LoopOutChannel = &in.LoopOutChannel
|
||||
}
|
||||
hash, err := s.impl.Uncharge(ctx, req)
|
||||
hash, err := s.impl.LoopOut(ctx, req)
|
||||
if err != nil {
|
||||
logger.Errorf("Uncharge: %v", err)
|
||||
logger.Errorf("LoopOut: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -73,24 +74,25 @@ func (s *swapClientServer) Uncharge(ctx context.Context,
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (s *swapClientServer) marshallSwap(swap *client.SwapInfo) (
|
||||
func (s *swapClientServer) marshallSwap(loopSwap *loop.SwapInfo) (
|
||||
*looprpc.SwapStatus, error) {
|
||||
|
||||
var state looprpc.SwapState
|
||||
switch swap.State {
|
||||
case client.StateInitiated:
|
||||
switch loopSwap.State {
|
||||
case loopdb.StateInitiated:
|
||||
state = looprpc.SwapState_INITIATED
|
||||
case client.StatePreimageRevealed:
|
||||
case loopdb.StatePreimageRevealed:
|
||||
state = looprpc.SwapState_PREIMAGE_REVEALED
|
||||
case client.StateSuccess:
|
||||
case loopdb.StateSuccess:
|
||||
state = looprpc.SwapState_SUCCESS
|
||||
default:
|
||||
// Return less granular status over rpc.
|
||||
state = looprpc.SwapState_FAILED
|
||||
}
|
||||
|
||||
htlc, err := utils.NewHtlc(swap.CltvExpiry, swap.SenderKey,
|
||||
swap.ReceiverKey, swap.SwapHash,
|
||||
htlc, err := swap.NewHtlc(
|
||||
loopSwap.CltvExpiry, loopSwap.SenderKey, loopSwap.ReceiverKey,
|
||||
loopSwap.SwapHash,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -102,13 +104,13 @@ func (s *swapClientServer) marshallSwap(swap *client.SwapInfo) (
|
|||
}
|
||||
|
||||
return &looprpc.SwapStatus{
|
||||
Amt: int64(swap.AmountRequested),
|
||||
Id: swap.SwapHash.String(),
|
||||
Amt: int64(loopSwap.AmountRequested),
|
||||
Id: loopSwap.SwapHash.String(),
|
||||
State: state,
|
||||
InitiationTime: swap.InitiationTime.UnixNano(),
|
||||
LastUpdateTime: swap.LastUpdate.UnixNano(),
|
||||
InitiationTime: loopSwap.InitiationTime.UnixNano(),
|
||||
LastUpdateTime: loopSwap.LastUpdate.UnixNano(),
|
||||
HtlcAddress: address.EncodeAddress(),
|
||||
Type: looprpc.SwapType_UNCHARGE,
|
||||
Type: looprpc.SwapType_LOOP_OUT,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +120,7 @@ func (s *swapClientServer) Monitor(in *looprpc.MonitorRequest,
|
|||
|
||||
logger.Infof("Monitor request received")
|
||||
|
||||
send := func(info client.SwapInfo) error {
|
||||
send := func(info loop.SwapInfo) error {
|
||||
rpcSwap, err := s.marshallSwap(&info)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -140,9 +142,9 @@ func (s *swapClientServer) Monitor(in *looprpc.MonitorRequest,
|
|||
nextSubscriberID++
|
||||
subscribers[id] = queue.ChanIn()
|
||||
|
||||
var pendingSwaps, completedSwaps []client.SwapInfo
|
||||
var pendingSwaps, completedSwaps []loop.SwapInfo
|
||||
for _, swap := range swaps {
|
||||
if swap.State.Type() == client.StateTypePending {
|
||||
if swap.State.Type() == loopdb.StateTypePending {
|
||||
pendingSwaps = append(pendingSwaps, swap)
|
||||
} else {
|
||||
completedSwaps = append(completedSwaps, swap)
|
||||
|
|
@ -196,7 +198,7 @@ func (s *swapClientServer) Monitor(in *looprpc.MonitorRequest,
|
|||
return nil
|
||||
}
|
||||
|
||||
swap := queueItem.(client.SwapInfo)
|
||||
swap := queueItem.(loop.SwapInfo)
|
||||
if err := send(swap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -207,12 +209,12 @@ func (s *swapClientServer) Monitor(in *looprpc.MonitorRequest,
|
|||
}
|
||||
|
||||
// GetTerms returns the terms that the server enforces for swaps.
|
||||
func (s *swapClientServer) GetUnchargeTerms(ctx context.Context, req *looprpc.TermsRequest) (
|
||||
*looprpc.TermsResponse, error) {
|
||||
func (s *swapClientServer) GetLoopOutTerms(ctx context.Context,
|
||||
req *looprpc.TermsRequest) (*looprpc.TermsResponse, error) {
|
||||
|
||||
logger.Infof("Terms request received")
|
||||
|
||||
terms, err := s.impl.UnchargeTerms(ctx)
|
||||
terms, err := s.impl.LoopOutTerms(ctx)
|
||||
if err != nil {
|
||||
logger.Errorf("Terms request: %v", err)
|
||||
return nil, err
|
||||
|
|
@ -229,10 +231,10 @@ func (s *swapClientServer) GetUnchargeTerms(ctx context.Context, req *looprpc.Te
|
|||
}
|
||||
|
||||
// GetQuote returns a quote for a swap with the provided parameters.
|
||||
func (s *swapClientServer) GetUnchargeQuote(ctx context.Context,
|
||||
func (s *swapClientServer) GetLoopOutQuote(ctx context.Context,
|
||||
req *looprpc.QuoteRequest) (*looprpc.QuoteResponse, error) {
|
||||
|
||||
quote, err := s.impl.UnchargeQuote(ctx, &client.UnchargeQuoteRequest{
|
||||
quote, err := s.impl.LoopOutQuote(ctx, &loop.LoopOutQuoteRequest{
|
||||
Amount: btcutil.Amount(req.Amt),
|
||||
SweepConfTarget: defaultConfTarget,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/lightninglabs/loop/client"
|
||||
"github.com/lightninglabs/loop"
|
||||
"github.com/lightninglabs/loop/lndclient"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
|
@ -20,7 +20,9 @@ func getLnd(ctx *cli.Context) (*lndclient.GrpcLndServices, error) {
|
|||
}
|
||||
|
||||
// getClient returns an instance of the swap client.
|
||||
func getClient(ctx *cli.Context, lnd *lndclient.LndServices) (*client.Client, func(), error) {
|
||||
func getClient(ctx *cli.Context,
|
||||
lnd *lndclient.LndServices) (*loop.Client, func(), error) {
|
||||
|
||||
network := ctx.GlobalString("network")
|
||||
|
||||
storeDir, err := getStoreDir(network)
|
||||
|
|
@ -28,7 +30,7 @@ func getClient(ctx *cli.Context, lnd *lndclient.LndServices) (*client.Client, fu
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
swapClient, cleanUp, err := client.NewClient(
|
||||
swapClient, cleanUp, err := loop.NewClient(
|
||||
storeDir, ctx.GlobalString("swapserver"),
|
||||
ctx.GlobalBool("insecure"), lnd,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/lightninglabs/loop/utils"
|
||||
"github.com/lightninglabs/loop/swap"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ var viewCommand = cli.Command{
|
|||
func view(ctx *cli.Context) error {
|
||||
network := ctx.GlobalString("network")
|
||||
|
||||
chainParams, err := utils.ChainParamsFromNetwork(network)
|
||||
chainParams, err := swap.ChainParamsFromNetwork(network)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -38,13 +38,13 @@ func view(ctx *cli.Context) error {
|
|||
}
|
||||
defer cleanup()
|
||||
|
||||
swaps, err := swapClient.GetUnchargeSwaps()
|
||||
swaps, err := swapClient.FetchLoopOutSwaps()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, s := range swaps {
|
||||
htlc, err := utils.NewHtlc(
|
||||
htlc, err := swap.NewHtlc(
|
||||
s.Contract.CltvExpiry,
|
||||
s.Contract.SenderKey,
|
||||
s.Contract.ReceiverKey,
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/lightninglabs/loop/lndclient"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
)
|
||||
|
||||
// clientConfig contains config items for the swap client.
|
||||
type clientConfig struct {
|
||||
LndServices *lndclient.LndServices
|
||||
Server swapServerClient
|
||||
Store swapClientStore
|
||||
Store loopdb.SwapStore
|
||||
CreateExpiryTimer func(expiry time.Duration) <-chan time.Time
|
||||
}
|
||||
|
|
|
|||
10
executor.go
10
executor.go
|
|
@ -8,15 +8,19 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/lightninglabs/loop/lndclient"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/sweep"
|
||||
"github.com/lightningnetwork/lnd/queue"
|
||||
)
|
||||
|
||||
// executorConfig contains executor configuration data.
|
||||
type executorConfig struct {
|
||||
lnd *lndclient.LndServices
|
||||
sweeper *sweep.Sweeper
|
||||
store swapClientStore
|
||||
lnd *lndclient.LndServices
|
||||
|
||||
sweeper *sweep.Sweeper
|
||||
|
||||
store loopdb.SwapStore
|
||||
|
||||
createExpiryTimer func(expiry time.Duration) <-chan time.Time
|
||||
}
|
||||
|
||||
|
|
|
|||
72
interface.go
72
interface.go
|
|
@ -4,11 +4,12 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
)
|
||||
|
||||
// UnchargeRequest contains the required parameters for the swap.
|
||||
type UnchargeRequest struct {
|
||||
// OutRequest contains the required parameters for a loop out swap.
|
||||
type OutRequest struct {
|
||||
// Amount specifies the requested swap amount in sat. This does not
|
||||
// include the swap and miner fee.
|
||||
Amount btcutil.Amount
|
||||
|
|
@ -19,19 +20,19 @@ type UnchargeRequest struct {
|
|||
// MaxSwapRoutingFee is the maximum off-chain fee in msat that may be
|
||||
// paid for payment to the server. This limit is applied during path
|
||||
// finding. Typically this value is taken from the response of the
|
||||
// UnchargeQuote call.
|
||||
// LoopOutQuote call.
|
||||
MaxSwapRoutingFee btcutil.Amount
|
||||
|
||||
// MaxPrepayRoutingFee is the maximum off-chain fee in msat that may be
|
||||
// paid for payment to the server. This limit is applied during path
|
||||
// finding. Typically this value is taken from the response of the
|
||||
// UnchargeQuote call.
|
||||
// LoopOutQuote call.
|
||||
MaxPrepayRoutingFee btcutil.Amount
|
||||
|
||||
// MaxSwapFee is the maximum we are willing to pay the server for the
|
||||
// swap. This value is not disclosed in the swap initiation call, but
|
||||
// if the server asks for a higher fee, we abort the swap. Typically
|
||||
// this value is taken from the response of the UnchargeQuote call. It
|
||||
// this value is taken from the response of the LoopOutQuote call. It
|
||||
// includes the prepay amount.
|
||||
MaxSwapFee btcutil.Amount
|
||||
|
||||
|
|
@ -54,26 +55,32 @@ type UnchargeRequest struct {
|
|||
// revocation.
|
||||
//
|
||||
// MaxMinerFee is typically taken from the response of the
|
||||
// UnchargeQuote call.
|
||||
// LoopOutQuote call.
|
||||
MaxMinerFee btcutil.Amount
|
||||
|
||||
// SweepConfTarget specifies the targeted confirmation target for the
|
||||
// client sweep tx.
|
||||
SweepConfTarget int32
|
||||
|
||||
// UnchargeChannel optionally specifies the short channel id of the
|
||||
// LoopOutChannel optionally specifies the short channel id of the
|
||||
// channel to uncharge.
|
||||
UnchargeChannel *uint64
|
||||
LoopOutChannel *uint64
|
||||
}
|
||||
|
||||
// UnchargeSwapInfo contains status information for a uncharge swap.
|
||||
type UnchargeSwapInfo struct {
|
||||
UnchargeContract
|
||||
// Out contains the full details of a loop out request. This includes things
|
||||
// like the payment hash, the total value, and the final CTLV delay of the
|
||||
// swap. We'll use this to track an active swap throughout that various swap
|
||||
// stages.
|
||||
type Out struct {
|
||||
// LoopOutContract describes the details of this loop.Out. Using these
|
||||
// details,the full swap can be executed.
|
||||
loopdb.LoopOutContract
|
||||
|
||||
// State is the current state of the target swap.
|
||||
State loopdb.SwapState
|
||||
|
||||
// SwapInfoKit contains shared data amongst all swap types.
|
||||
SwapInfoKit
|
||||
|
||||
// State where the swap is in.
|
||||
State SwapState
|
||||
}
|
||||
|
||||
// SwapCost is a breakdown of the final swap costs.
|
||||
|
|
@ -85,9 +92,9 @@ type SwapCost struct {
|
|||
Onchain btcutil.Amount
|
||||
}
|
||||
|
||||
// UnchargeQuoteRequest specifies the swap parameters for which a quote is
|
||||
// LoopOutQuoteRequest specifies the swap parameters for which a quote is
|
||||
// requested.
|
||||
type UnchargeQuoteRequest struct {
|
||||
type LoopOutQuoteRequest struct {
|
||||
// Amount specifies the requested swap amount in sat. This does not
|
||||
// include the swap and miner fee.
|
||||
Amount btcutil.Amount
|
||||
|
|
@ -107,9 +114,9 @@ type UnchargeQuoteRequest struct {
|
|||
// final cltv delta values for the off-chain payments.
|
||||
}
|
||||
|
||||
// UnchargeQuote contains estimates for the fees making up the total swap cost
|
||||
// LoopOutQuote contains estimates for the fees making up the total swap cost
|
||||
// for the client.
|
||||
type UnchargeQuote struct {
|
||||
type LoopOutQuote struct {
|
||||
// SwapFee is the fee that the swap server is charging for the swap.
|
||||
SwapFee btcutil.Amount
|
||||
|
||||
|
|
@ -122,8 +129,8 @@ type UnchargeQuote struct {
|
|||
MinerFee btcutil.Amount
|
||||
}
|
||||
|
||||
// UnchargeTerms are the server terms on which it executes swaps.
|
||||
type UnchargeTerms struct {
|
||||
// LoopOutTerms are the server terms on which it executes swaps.
|
||||
type LoopOutTerms struct {
|
||||
// SwapFeeBase is the fixed per-swap base fee.
|
||||
SwapFeeBase btcutil.Amount
|
||||
|
||||
|
|
@ -161,23 +168,26 @@ type SwapInfoKit struct {
|
|||
LastUpdateTime time.Time
|
||||
}
|
||||
|
||||
// SwapType indicates the type of swap.
|
||||
type SwapType uint8
|
||||
// Type indicates the type of swap.
|
||||
type Type uint8
|
||||
|
||||
const (
|
||||
// SwapTypeCharge is a charge swap.
|
||||
SwapTypeCharge SwapType = iota
|
||||
// TypeIn is a loop in swap.
|
||||
TypeIn Type = iota
|
||||
|
||||
// SwapTypeUncharge is an uncharge swap.
|
||||
SwapTypeUncharge
|
||||
// TypeOut is a loop out swap.
|
||||
TypeOut
|
||||
)
|
||||
|
||||
// SwapInfo exposes common info fields for charge and uncharge swaps.
|
||||
// SwapInfo exposes common info fields for loop in and loop out swaps.
|
||||
type SwapInfo struct {
|
||||
LastUpdate time.Time
|
||||
SwapHash lntypes.Hash
|
||||
State SwapState
|
||||
SwapType SwapType
|
||||
|
||||
SwapContract
|
||||
SwapHash lntypes.Hash
|
||||
|
||||
State loopdb.SwapState
|
||||
|
||||
SwapType Type
|
||||
|
||||
loopdb.SwapContract
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,19 +6,19 @@ import (
|
|||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
)
|
||||
|
||||
// SwapStore is the priamry database interface used by the loopd system. It
|
||||
// houses informatino for all pending completed/failed swaps.
|
||||
// SwapStore is the primary database interface used by the loopd system. It
|
||||
// houses information for all pending completed/failed swaps.
|
||||
type SwapStore interface {
|
||||
// FetchUnchargeSwaps returns all swaps currently in the store.
|
||||
FetchUnchargeSwaps() ([]*PersistentUncharge, error)
|
||||
// FetchLoopOutSwaps returns all swaps currently in the store.
|
||||
FetchLoopOutSwaps() ([]*LoopOut, error)
|
||||
|
||||
// CreateUncharge adds an initiated swap to the store.
|
||||
CreateUncharge(hash lntypes.Hash, swap *UnchargeContract) error
|
||||
// CreateLoopOut adds an initiated swap to the store.
|
||||
CreateLoopOut(hash lntypes.Hash, swap *LoopOutContract) error
|
||||
|
||||
// UpdateUncharge stores a swap updateUncharge. This appends to the
|
||||
// event log for a particular swap as it goes through the various
|
||||
// stages in its lifetime.
|
||||
UpdateUncharge(hash lntypes.Hash, time time.Time, state SwapState) error
|
||||
// UpdateLoopOut stores a new event for a target loop out swap. This
|
||||
// appends to the event log for a particular swap as it goes through
|
||||
// the various stages in its lifetime.
|
||||
UpdateLoopOut(hash lntypes.Hash, time time.Time, state SwapState) error
|
||||
|
||||
// Close closes the underlying database.
|
||||
Close() error
|
||||
|
|
|
|||
|
|
@ -12,13 +12,63 @@ import (
|
|||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
)
|
||||
|
||||
// UnchargeContract contains the data that is serialized to persistent storage
|
||||
// SwapContract contains the base data that is serialized to persistent storage
|
||||
// for pending swaps.
|
||||
type UnchargeContract struct {
|
||||
type SwapContract struct {
|
||||
// Preimage is the preimage for the swap.
|
||||
Preimage lntypes.Preimage
|
||||
|
||||
// AmountRequested is the total amount of the swap.
|
||||
AmountRequested btcutil.Amount
|
||||
|
||||
// PrepayInvoice is the invoice that the client should pay to the
|
||||
// server that will be returned if the swap is complete.
|
||||
PrepayInvoice string
|
||||
|
||||
// SenderKey is the key of the sender that will be used in the on-chain
|
||||
// HTLC.
|
||||
SenderKey [33]byte
|
||||
|
||||
// ReceiverKey is the of the receiver that will be used in the on-chain
|
||||
// HTLC.
|
||||
ReceiverKey [33]byte
|
||||
|
||||
// CltvExpiry is the total absolute CLTV expiry of the swap.
|
||||
CltvExpiry int32
|
||||
|
||||
// MaxPrepayRoutingFee is the maximum off-chain fee in msat that may be
|
||||
// paid for the prepayment to the server.
|
||||
MaxPrepayRoutingFee btcutil.Amount
|
||||
|
||||
// MaxSwapFee is the maximum we are willing to pay the server for the
|
||||
// swap.
|
||||
MaxSwapFee btcutil.Amount
|
||||
|
||||
// MaxMinerFee is the maximum in on-chain fees that we are willing to
|
||||
// spend.
|
||||
MaxMinerFee btcutil.Amount
|
||||
|
||||
// InitiationHeight is the block height at which the swap was
|
||||
// initiated.
|
||||
InitiationHeight int32
|
||||
|
||||
// InitiationTime is the time at which the swap was initiated.
|
||||
InitiationTime time.Time
|
||||
}
|
||||
|
||||
// LoopOutContract contains the data that is serialized to persistent storage
|
||||
// for pending swaps.
|
||||
type LoopOutContract struct {
|
||||
// SwapContract contains basic information pertaining to this swap.
|
||||
// Each swap type has a base contract, then swap specific information
|
||||
// on top of it.
|
||||
SwapContract
|
||||
|
||||
// DestAddr is the destination address of the loop out swap.
|
||||
DestAddr btcutil.Address
|
||||
|
||||
// SwapInvoice is the invoice that is to be paid by the client to
|
||||
// initiate the loop out swap.
|
||||
SwapInvoice string
|
||||
|
||||
// MaxSwapRoutingFee is the maximum off-chain fee in msat that may be
|
||||
|
|
@ -29,13 +79,13 @@ type UnchargeContract struct {
|
|||
// client sweep tx.
|
||||
SweepConfTarget int32
|
||||
|
||||
// UnchargeChannel is the channel to uncharge. If zero, any channel may
|
||||
// TargetChannel is the channel to loop out. If zero, any channel may
|
||||
// be used.
|
||||
UnchargeChannel *uint64
|
||||
}
|
||||
|
||||
// PersistentUnchargeEvent contains the dynamic data of a swap.
|
||||
type PersistentUnchargeEvent struct {
|
||||
// LoopOutEvent contains the dynamic data of a swap.
|
||||
type LoopOutEvent struct {
|
||||
// State is the new state for this swap as a result of this event.
|
||||
State SwapState
|
||||
|
||||
|
|
@ -43,22 +93,22 @@ type PersistentUnchargeEvent struct {
|
|||
Time time.Time
|
||||
}
|
||||
|
||||
// PersistentUncharge is a combination of the contract and the updates.
|
||||
type PersistentUncharge struct {
|
||||
// LoopOut is a combination of the contract and the updates.
|
||||
type LoopOut struct {
|
||||
// Hash is the hash that uniquely identifies this swap.
|
||||
Hash lntypes.Hash
|
||||
|
||||
// Contract is the active contract for this swap. It describes the
|
||||
// precise details of the swap including the final fee, CLTV value,
|
||||
// etc.
|
||||
Contract *UnchargeContract
|
||||
Contract *LoopOutContract
|
||||
|
||||
// Events are each of the state transitions that this swap underwent.
|
||||
Events []*PersistentUnchargeEvent
|
||||
Events []*LoopOutEvent
|
||||
}
|
||||
|
||||
// State returns the most recent state of this swap.
|
||||
func (s *PersistentUncharge) State() SwapState {
|
||||
func (s *LoopOut) State() SwapState {
|
||||
lastUpdate := s.LastUpdate()
|
||||
if lastUpdate == nil {
|
||||
return StateInitiated
|
||||
|
|
@ -68,7 +118,7 @@ func (s *PersistentUncharge) State() SwapState {
|
|||
}
|
||||
|
||||
// LastUpdate returns the most recent update of this swap.
|
||||
func (s *PersistentUncharge) LastUpdate() *PersistentUnchargeEvent {
|
||||
func (s *LoopOut) LastUpdate() *LoopOutEvent {
|
||||
eventCount := len(s.Events)
|
||||
|
||||
if eventCount == 0 {
|
||||
|
|
@ -80,7 +130,7 @@ func (s *PersistentUncharge) LastUpdate() *PersistentUnchargeEvent {
|
|||
}
|
||||
|
||||
// LastUpdateTime returns the last update time of this swap.
|
||||
func (s *PersistentUncharge) LastUpdateTime() time.Time {
|
||||
func (s *LoopOut) LastUpdateTime() time.Time {
|
||||
lastUpdate := s.LastUpdate()
|
||||
if lastUpdate == nil {
|
||||
return s.Contract.InitiationTime
|
||||
|
|
@ -89,7 +139,7 @@ func (s *PersistentUncharge) LastUpdateTime() time.Time {
|
|||
return lastUpdate.Time
|
||||
}
|
||||
|
||||
func deserializeUnchargeContract(value []byte) (*UnchargeContract, error) {
|
||||
func deserializeLoopOutContract(value []byte) (*LoopOutContract, error) {
|
||||
r := bytes.NewReader(value)
|
||||
|
||||
contract, err := deserializeContract(r)
|
||||
|
|
@ -97,7 +147,7 @@ func deserializeUnchargeContract(value []byte) (*UnchargeContract, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
swap := UnchargeContract{
|
||||
swap := LoopOutContract{
|
||||
SwapContract: *contract,
|
||||
}
|
||||
|
||||
|
|
@ -134,7 +184,7 @@ func deserializeUnchargeContract(value []byte) (*UnchargeContract, error) {
|
|||
return &swap, nil
|
||||
}
|
||||
|
||||
func serializeUnchargeContract(swap *UnchargeContract) (
|
||||
func serializeLoopOutContract(swap *LoopOutContract) (
|
||||
[]byte, error) {
|
||||
|
||||
var b bytes.Buffer
|
||||
|
|
@ -282,7 +332,7 @@ func serializeContract(swap *SwapContract, b *bytes.Buffer) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func serializeUnchargeUpdate(time time.Time, state SwapState) (
|
||||
func serializeLoopOutEvent(time time.Time, state SwapState) (
|
||||
[]byte, error) {
|
||||
|
||||
var b bytes.Buffer
|
||||
|
|
@ -298,8 +348,8 @@ func serializeUnchargeUpdate(time time.Time, state SwapState) (
|
|||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func deserializeUnchargeUpdate(value []byte) (*PersistentUnchargeEvent, error) {
|
||||
update := &PersistentUnchargeEvent{}
|
||||
func deserializeLoopOutEvent(value []byte) (*LoopOutEvent, error) {
|
||||
update := &LoopOutEvent{}
|
||||
|
||||
r := bytes.NewReader(value)
|
||||
|
||||
|
|
@ -67,9 +67,8 @@ type boltSwapStore struct {
|
|||
// interface.
|
||||
var _ = (*boltSwapStore)(nil)
|
||||
|
||||
// newBoltSwapStore creates a new client swap store.
|
||||
func newBoltSwapStore(dbPath string) (*boltSwapStore, error) {
|
||||
|
||||
// NewBoltSwapStore creates a new client swap store.
|
||||
func NewBoltSwapStore(dbPath string) (*boltSwapStore, error) {
|
||||
// If the target path for the swap store doesn't exist, then we'll
|
||||
// create it now before we proceed.
|
||||
if !fileExists(dbPath) {
|
||||
|
|
@ -119,11 +118,11 @@ func newBoltSwapStore(dbPath string) (*boltSwapStore, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// FetchUnchargeSwaps returns all swaps currently in the store.
|
||||
// FetchLoopOutSwaps returns all swaps currently in the store.
|
||||
//
|
||||
// NOTE: Part of the loopdb.SwapStore interface.
|
||||
func (s *boltSwapStore) FetchUnchargeSwaps() ([]*PersistentUncharge, error) {
|
||||
var swaps []*PersistentUncharge
|
||||
func (s *boltSwapStore) FetchLoopOutSwaps() ([]*LoopOut, error) {
|
||||
var swaps []*LoopOut
|
||||
|
||||
err := s.db.View(func(tx *bbolt.Tx) error {
|
||||
// First, we'll grab our main loop out swap bucket key.
|
||||
|
|
@ -155,7 +154,7 @@ func (s *boltSwapStore) FetchUnchargeSwaps() ([]*PersistentUncharge, error) {
|
|||
if contractBytes == nil {
|
||||
return errors.New("contract not found")
|
||||
}
|
||||
contract, err := deserializeUnchargeContract(
|
||||
contract, err := deserializeLoopOutContract(
|
||||
contractBytes,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -171,9 +170,9 @@ func (s *boltSwapStore) FetchUnchargeSwaps() ([]*PersistentUncharge, error) {
|
|||
|
||||
// De serialize and collect each swap update into our
|
||||
// slice of swap events.
|
||||
var updates []*PersistentUnchargeEvent
|
||||
var updates []*LoopOutEvent
|
||||
err = stateBucket.ForEach(func(k, v []byte) error {
|
||||
event, err := deserializeUnchargeUpdate(v)
|
||||
event, err := deserializeLoopOutEvent(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -188,7 +187,7 @@ func (s *boltSwapStore) FetchUnchargeSwaps() ([]*PersistentUncharge, error) {
|
|||
var hash lntypes.Hash
|
||||
copy(hash[:], swapHash)
|
||||
|
||||
swap := PersistentUncharge{
|
||||
swap := LoopOut{
|
||||
Contract: contract,
|
||||
Hash: hash,
|
||||
Events: updates,
|
||||
|
|
@ -205,11 +204,11 @@ func (s *boltSwapStore) FetchUnchargeSwaps() ([]*PersistentUncharge, error) {
|
|||
return swaps, nil
|
||||
}
|
||||
|
||||
// CreateUncharge adds an initiated swap to the store.
|
||||
// CreateLoopOut adds an initiated swap to the store.
|
||||
//
|
||||
// NOTE: Part of the loopdb.SwapStore interface.
|
||||
func (s *boltSwapStore) CreateUncharge(hash lntypes.Hash,
|
||||
swap *UnchargeContract) error {
|
||||
func (s *boltSwapStore) CreateLoopOut(hash lntypes.Hash,
|
||||
swap *LoopOutContract) error {
|
||||
|
||||
// If the hash doesn't match the pre-image, then this is an invalid
|
||||
// swap so we'll bail out early.
|
||||
|
|
@ -244,7 +243,7 @@ func (s *boltSwapStore) CreateUncharge(hash lntypes.Hash,
|
|||
|
||||
// With out swap bucket created, we'll serialize and store the
|
||||
// swap itself.
|
||||
contract, err := serializeUnchargeContract(swap)
|
||||
contract, err := serializeLoopOutContract(swap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -259,11 +258,11 @@ func (s *boltSwapStore) CreateUncharge(hash lntypes.Hash,
|
|||
})
|
||||
}
|
||||
|
||||
// UpdateUncharge stores a swap updateUncharge. This appends to the event log
|
||||
// for a particular swap as it goes through the various stages in its lifetime.
|
||||
// UpdateLoopOut stores a swap updateLoopOut. This appends to the event log for
|
||||
// a particular swap as it goes through the various stages in its lifetime.
|
||||
//
|
||||
// NOTE: Part of the loopdb.SwapStore interface.
|
||||
func (s *boltSwapStore) UpdateUncharge(hash lntypes.Hash, time time.Time,
|
||||
func (s *boltSwapStore) UpdateLoopOut(hash lntypes.Hash, time time.Time,
|
||||
state SwapState) error {
|
||||
|
||||
return s.db.Update(func(tx *bbolt.Tx) error {
|
||||
|
|
@ -291,7 +290,7 @@ func (s *boltSwapStore) UpdateUncharge(hash lntypes.Hash, time time.Time,
|
|||
}
|
||||
|
||||
// With the ID obtained, we'll write out this new update value.
|
||||
updateValue, err := serializeUnchargeUpdate(time, state)
|
||||
updateValue, err := serializeLoopOutEvent(time, state)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,13 +42,13 @@ func TestBoltSwapStore(t *testing.T) {
|
|||
}
|
||||
defer os.RemoveAll(tempDirName)
|
||||
|
||||
store, err := newBoltSwapStore(tempDirName)
|
||||
store, err := NewBoltSwapStore(tempDirName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// First, verify that an empty database has no active swaps.
|
||||
swaps, err := store.FetchUnchargeSwaps()
|
||||
swaps, err := store.FetchLoopOutSwaps()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ func TestBoltSwapStore(t *testing.T) {
|
|||
|
||||
// Next, we'll make a new pending swap that we'll insert into the
|
||||
// database shortly.
|
||||
pendingSwap := UnchargeContract{
|
||||
pendingSwap := LoopOutContract{
|
||||
SwapContract: SwapContract{
|
||||
AmountRequested: 100,
|
||||
Preimage: testPreimage,
|
||||
|
|
@ -90,7 +90,7 @@ func TestBoltSwapStore(t *testing.T) {
|
|||
checkSwap := func(expectedState SwapState) {
|
||||
t.Helper()
|
||||
|
||||
swaps, err := store.FetchUnchargeSwaps()
|
||||
swaps, err := store.FetchLoopOutSwaps()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -113,20 +113,20 @@ func TestBoltSwapStore(t *testing.T) {
|
|||
|
||||
// If we create a new swap, then it should show up as being initialized
|
||||
// right after.
|
||||
if err := store.CreateUncharge(hash, &pendingSwap); err != nil {
|
||||
if err := store.CreateLoopOut(hash, &pendingSwap); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkSwap(StateInitiated)
|
||||
|
||||
// Trying to make the same swap again should result in an error.
|
||||
if err := store.CreateUncharge(hash, &pendingSwap); err == nil {
|
||||
if err := store.CreateLoopOut(hash, &pendingSwap); err == nil {
|
||||
t.Fatal("expected error on storing duplicate")
|
||||
}
|
||||
checkSwap(StateInitiated)
|
||||
|
||||
// Next, we'll update to the next state of the pre-image being
|
||||
// revealed. The state should be reflected here again.
|
||||
err = store.UpdateUncharge(
|
||||
err = store.UpdateLoopOut(
|
||||
hash, testTime, StatePreimageRevealed,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -136,7 +136,7 @@ func TestBoltSwapStore(t *testing.T) {
|
|||
|
||||
// Next, we'll update to the final state to ensure that the state is
|
||||
// properly updated.
|
||||
err = store.UpdateUncharge(
|
||||
err = store.UpdateLoopOut(
|
||||
hash, testTime, StateFailInsufficientValue,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -150,7 +150,7 @@ func TestBoltSwapStore(t *testing.T) {
|
|||
|
||||
// If we re-open the same store, then the state of the current swap
|
||||
// should be the same.
|
||||
store, err = newBoltSwapStore(tempDirName)
|
||||
store, err = NewBoltSwapStore(tempDirName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
package loopdb
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
)
|
||||
|
||||
// SwapContract contains the base data that is serialized to persistent storage
|
||||
// for pending swaps.
|
||||
type SwapContract struct {
|
||||
Preimage lntypes.Preimage
|
||||
AmountRequested btcutil.Amount
|
||||
|
||||
PrepayInvoice string
|
||||
|
||||
SenderKey [33]byte
|
||||
ReceiverKey [33]byte
|
||||
|
||||
CltvExpiry int32
|
||||
|
||||
// MaxPrepayRoutingFee is the maximum off-chain fee in msat that may be
|
||||
// paid for the prepayment to the server.
|
||||
MaxPrepayRoutingFee btcutil.Amount
|
||||
|
||||
// MaxSwapFee is the maximum we are willing to pay the server for the
|
||||
// swap.
|
||||
MaxSwapFee btcutil.Amount
|
||||
|
||||
// MaxMinerFee is the maximum in on-chain fees that we are willing to
|
||||
// spend.
|
||||
MaxMinerFee btcutil.Amount
|
||||
|
||||
// InitiationHeight is the block height at which the swap was
|
||||
// initiated.
|
||||
InitiationHeight int32
|
||||
|
||||
// InitiationTime is the time at which the swap was initiated.
|
||||
InitiationTime time.Time
|
||||
}
|
||||
|
|
@ -10,24 +10,25 @@ import (
|
|||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/loop/lndclient"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/swap"
|
||||
"github.com/lightninglabs/loop/sweep"
|
||||
"github.com/lightninglabs/loop/utils"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
)
|
||||
|
||||
var (
|
||||
// MinUnchargePreimageRevealDelta configures the minimum number of remaining
|
||||
// blocks before htlc expiry required to reveal preimage.
|
||||
MinUnchargePreimageRevealDelta = int32(20)
|
||||
// MinLoopOutPreimageRevealDelta configures the minimum number of
|
||||
// remaining blocks before htlc expiry required to reveal preimage.
|
||||
MinLoopOutPreimageRevealDelta = int32(20)
|
||||
)
|
||||
|
||||
// unchargeSwap contains all the in-memory state related to a pending uncharge
|
||||
// loopOutSwap contains all the in-memory state related to a pending loop out
|
||||
// swap.
|
||||
type unchargeSwap struct {
|
||||
type loopOutSwap struct {
|
||||
swapKit
|
||||
|
||||
UnchargeContract
|
||||
loopdb.LoopOutContract
|
||||
|
||||
swapPaymentChan chan lndclient.PaymentResult
|
||||
prePaymentChan chan lndclient.PaymentResult
|
||||
|
|
@ -41,10 +42,10 @@ type executeConfig struct {
|
|||
timerFactory func(d time.Duration) <-chan time.Time
|
||||
}
|
||||
|
||||
// newUnchargeSwap initiates a new swap with the server and returns a
|
||||
// newLoopOutSwap initiates a new swap with the server and returns a
|
||||
// corresponding swap object.
|
||||
func newUnchargeSwap(globalCtx context.Context, cfg *swapConfig,
|
||||
currentHeight int32, request *UnchargeRequest) (*unchargeSwap, error) {
|
||||
func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig,
|
||||
currentHeight int32, request *OutRequest) (*loopOutSwap, error) {
|
||||
|
||||
// Generate random preimage.
|
||||
var swapPreimage [32]byte
|
||||
|
|
@ -55,7 +56,7 @@ func newUnchargeSwap(globalCtx context.Context, cfg *swapConfig,
|
|||
|
||||
// Derive a receiver key for this swap.
|
||||
keyDesc, err := cfg.lnd.WalletKit.DeriveNextKey(
|
||||
globalCtx, utils.SwapKeyFamily,
|
||||
globalCtx, swap.KeyFamily,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -67,14 +68,14 @@ func newUnchargeSwap(globalCtx context.Context, cfg *swapConfig,
|
|||
// the server revocation key and the swap and prepay invoices.
|
||||
logger.Infof("Initiating swap request at height %v", currentHeight)
|
||||
|
||||
swapResp, err := cfg.server.NewUnchargeSwap(globalCtx, swapHash,
|
||||
swapResp, err := cfg.server.NewLoopOutSwap(globalCtx, swapHash,
|
||||
request.Amount, receiverKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot initiate swap: %v", err)
|
||||
}
|
||||
|
||||
err = validateUnchargeContract(cfg.lnd, currentHeight, request, swapResp)
|
||||
err = validateLoopOutContract(cfg.lnd, currentHeight, request, swapResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -82,13 +83,13 @@ func newUnchargeSwap(globalCtx context.Context, cfg *swapConfig,
|
|||
// Instantie a struct that contains all required data to start the swap.
|
||||
initiationTime := time.Now()
|
||||
|
||||
contract := UnchargeContract{
|
||||
contract := loopdb.LoopOutContract{
|
||||
SwapInvoice: swapResp.swapInvoice,
|
||||
DestAddr: request.DestAddr,
|
||||
MaxSwapRoutingFee: request.MaxSwapRoutingFee,
|
||||
SweepConfTarget: request.SweepConfTarget,
|
||||
UnchargeChannel: request.UnchargeChannel,
|
||||
SwapContract: SwapContract{
|
||||
UnchargeChannel: request.LoopOutChannel,
|
||||
SwapContract: loopdb.SwapContract{
|
||||
InitiationHeight: currentHeight,
|
||||
InitiationTime: initiationTime,
|
||||
PrepayInvoice: swapResp.prepayInvoice,
|
||||
|
|
@ -104,7 +105,7 @@ func newUnchargeSwap(globalCtx context.Context, cfg *swapConfig,
|
|||
}
|
||||
|
||||
swapKit, err := newSwapKit(
|
||||
swapHash, SwapTypeUncharge, cfg, &contract.SwapContract,
|
||||
swapHash, TypeOut, cfg, &contract.SwapContract,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -112,14 +113,14 @@ func newUnchargeSwap(globalCtx context.Context, cfg *swapConfig,
|
|||
|
||||
swapKit.lastUpdateTime = initiationTime
|
||||
|
||||
swap := &unchargeSwap{
|
||||
UnchargeContract: contract,
|
||||
swapKit: *swapKit,
|
||||
swap := &loopOutSwap{
|
||||
LoopOutContract: contract,
|
||||
swapKit: *swapKit,
|
||||
}
|
||||
|
||||
// Persist the data before exiting this function, so that the caller can
|
||||
// trust that this swap will be resumed on restart.
|
||||
err = cfg.store.createUncharge(swapHash, &swap.UnchargeContract)
|
||||
// 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(swapHash, &swap.LoopOutContract)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot store swap: %v", err)
|
||||
}
|
||||
|
|
@ -127,25 +128,25 @@ func newUnchargeSwap(globalCtx context.Context, cfg *swapConfig,
|
|||
return swap, nil
|
||||
}
|
||||
|
||||
// resumeUnchargeSwap returns a swap object representing a pending swap that has
|
||||
// resumeLoopOutSwap returns a swap object representing a pending swap that has
|
||||
// been restored from the database.
|
||||
func resumeUnchargeSwap(reqContext context.Context, cfg *swapConfig,
|
||||
pend *PersistentUncharge) (*unchargeSwap, error) {
|
||||
func resumeLoopOutSwap(reqContext context.Context, cfg *swapConfig,
|
||||
pend *loopdb.LoopOut) (*loopOutSwap, error) {
|
||||
|
||||
hash := lntypes.Hash(sha256.Sum256(pend.Contract.Preimage[:]))
|
||||
|
||||
logger.Infof("Resuming swap %v", hash)
|
||||
|
||||
swapKit, err := newSwapKit(
|
||||
hash, SwapTypeUncharge, cfg, &pend.Contract.SwapContract,
|
||||
hash, TypeOut, cfg, &pend.Contract.SwapContract,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
swap := &unchargeSwap{
|
||||
UnchargeContract: *pend.Contract,
|
||||
swapKit: *swapKit,
|
||||
swap := &loopOutSwap{
|
||||
LoopOutContract: *pend.Contract,
|
||||
swapKit: *swapKit,
|
||||
}
|
||||
|
||||
lastUpdate := pend.LastUpdate()
|
||||
|
|
@ -161,7 +162,7 @@ func resumeUnchargeSwap(reqContext context.Context, cfg *swapConfig,
|
|||
|
||||
// execute starts/resumes the swap. It is a thin wrapper around
|
||||
// executeAndFinalize to conveniently handle the error case.
|
||||
func (s *unchargeSwap) execute(mainCtx context.Context,
|
||||
func (s *loopOutSwap) execute(mainCtx context.Context,
|
||||
cfg *executeConfig, height int32) error {
|
||||
|
||||
s.executeConfig = *cfg
|
||||
|
|
@ -170,13 +171,15 @@ func (s *unchargeSwap) execute(mainCtx context.Context,
|
|||
err := s.executeAndFinalize(mainCtx)
|
||||
|
||||
// If an unexpected error happened, report a temporary failure.
|
||||
// Otherwise for example a connection error could lead to abandoning the
|
||||
// swap permanently and losing funds.
|
||||
// Otherwise for example a connection error could lead to abandoning
|
||||
// the swap permanently and losing funds.
|
||||
if err != nil {
|
||||
s.log.Errorf("Swap error: %v", err)
|
||||
s.state = StateFailTemporary
|
||||
|
||||
// If we cannot send out this update, there is nothing we can do.
|
||||
s.state = loopdb.StateFailTemporary
|
||||
|
||||
// If we cannot send out this update, there is nothing we can
|
||||
// do.
|
||||
_ = s.sendUpdate(mainCtx)
|
||||
}
|
||||
|
||||
|
|
@ -185,7 +188,7 @@ func (s *unchargeSwap) execute(mainCtx context.Context,
|
|||
|
||||
// executeAndFinalize executes a swap and awaits the definitive outcome of the
|
||||
// offchain payments. When this method returns, the swap outcome is final.
|
||||
func (s *unchargeSwap) executeAndFinalize(globalCtx context.Context) error {
|
||||
func (s *loopOutSwap) executeAndFinalize(globalCtx context.Context) error {
|
||||
// Announce swap by sending out an initial update.
|
||||
err := s.sendUpdate(globalCtx)
|
||||
if err != nil {
|
||||
|
|
@ -200,7 +203,7 @@ func (s *unchargeSwap) executeAndFinalize(globalCtx context.Context) error {
|
|||
}
|
||||
|
||||
// Sanity check.
|
||||
if s.state.Type() == StateTypePending {
|
||||
if s.state.Type() == loopdb.StateTypePending {
|
||||
return fmt.Errorf("swap in non-final state %v", s.state)
|
||||
}
|
||||
|
||||
|
|
@ -249,7 +252,7 @@ func (s *unchargeSwap) executeAndFinalize(globalCtx context.Context) error {
|
|||
|
||||
// executeSwap executes the swap, but returns as soon as the swap outcome is
|
||||
// final. At that point, there may still be pending off-chain payment(s).
|
||||
func (s *unchargeSwap) executeSwap(globalCtx context.Context) error {
|
||||
func (s *loopOutSwap) executeSwap(globalCtx context.Context) error {
|
||||
// We always pay both invoices (again). This is currently the only way
|
||||
// to sort of resume payments.
|
||||
//
|
||||
|
|
@ -277,7 +280,7 @@ func (s *unchargeSwap) executeSwap(globalCtx context.Context) error {
|
|||
// attempt.
|
||||
|
||||
// Retrieve outpoint for sweep.
|
||||
htlcOutpoint, htlcValue, err := utils.GetScriptOutput(
|
||||
htlcOutpoint, htlcValue, err := swap.GetScriptOutput(
|
||||
txConf.Tx, s.htlc.ScriptHash,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -287,11 +290,11 @@ func (s *unchargeSwap) executeSwap(globalCtx context.Context) error {
|
|||
s.log.Infof("Htlc value: %v", htlcValue)
|
||||
|
||||
// Verify amount if preimage hasn't been revealed yet.
|
||||
if s.state != StatePreimageRevealed && htlcValue < s.AmountRequested {
|
||||
if s.state != loopdb.StatePreimageRevealed && htlcValue < s.AmountRequested {
|
||||
logger.Warnf("Swap amount too low, expected %v but received %v",
|
||||
s.AmountRequested, htlcValue)
|
||||
|
||||
s.state = StateFailInsufficientValue
|
||||
s.state = loopdb.StateFailInsufficientValue
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -308,7 +311,7 @@ func (s *unchargeSwap) executeSwap(globalCtx context.Context) error {
|
|||
// Inspect witness stack to see if it is a success transaction. We
|
||||
// don't just try to match with the hash of our sweep tx, because it
|
||||
// may be swept by a different (fee) sweep tx from a previous run.
|
||||
htlcInput, err := getTxInputByOutpoint(
|
||||
htlcInput, err := swap.GetTxInputByOutpoint(
|
||||
spendDetails.SpendingTx, htlcOutpoint,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -322,22 +325,22 @@ func (s *unchargeSwap) executeSwap(globalCtx context.Context) error {
|
|||
s.cost.Onchain = htlcValue -
|
||||
btcutil.Amount(spendDetails.SpendingTx.TxOut[0].Value)
|
||||
|
||||
s.state = StateSuccess
|
||||
s.state = loopdb.StateSuccess
|
||||
} else {
|
||||
s.state = StateFailSweepTimeout
|
||||
s.state = loopdb.StateFailSweepTimeout
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// persistState updates the swap state and sends out an update notification.
|
||||
func (s *unchargeSwap) persistState(ctx context.Context) error {
|
||||
func (s *loopOutSwap) persistState(ctx context.Context) error {
|
||||
updateTime := time.Now()
|
||||
|
||||
s.lastUpdateTime = updateTime
|
||||
|
||||
// Update state in store.
|
||||
err := s.store.updateUncharge(s.hash, updateTime, s.state)
|
||||
err := s.store.UpdateLoopOut(s.hash, updateTime, s.state)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -347,12 +350,12 @@ func (s *unchargeSwap) persistState(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// payInvoices pays both swap invoices.
|
||||
func (s *unchargeSwap) payInvoices(ctx context.Context) {
|
||||
func (s *loopOutSwap) payInvoices(ctx context.Context) {
|
||||
// Pay the swap invoice.
|
||||
s.log.Infof("Sending swap payment %v", s.SwapInvoice)
|
||||
s.swapPaymentChan = s.lnd.Client.PayInvoice(
|
||||
ctx, s.SwapInvoice, s.MaxSwapRoutingFee,
|
||||
s.UnchargeContract.UnchargeChannel,
|
||||
s.LoopOutContract.UnchargeChannel,
|
||||
)
|
||||
|
||||
// Pay the prepay invoice.
|
||||
|
|
@ -366,7 +369,7 @@ func (s *unchargeSwap) payInvoices(ctx context.Context) {
|
|||
// waitForConfirmedHtlc waits for a confirmed htlc to appear on the chain. In
|
||||
// case we haven't revealed the preimage yet, it also monitors block height and
|
||||
// off-chain payment failure.
|
||||
func (s *unchargeSwap) waitForConfirmedHtlc(globalCtx context.Context) (
|
||||
func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) (
|
||||
*chainntnfs.TxConfirmation, error) {
|
||||
|
||||
// Wait for confirmation of the on-chain htlc by watching for a tx
|
||||
|
|
@ -388,12 +391,12 @@ func (s *unchargeSwap) waitForConfirmedHtlc(globalCtx context.Context) (
|
|||
}
|
||||
|
||||
var txConf *chainntnfs.TxConfirmation
|
||||
if s.state == StateInitiated {
|
||||
if s.state == loopdb.StateInitiated {
|
||||
// Check if it is already too late to start this swap. If we
|
||||
// already revealed the preimage, this check is irrelevant and
|
||||
// we need to sweep in any case.
|
||||
maxPreimageRevealHeight := s.CltvExpiry -
|
||||
MinUnchargePreimageRevealDelta
|
||||
MinLoopOutPreimageRevealDelta
|
||||
|
||||
checkMaxRevealHeightExceeded := func() bool {
|
||||
s.log.Infof("Checking preimage reveal height %v "+
|
||||
|
|
@ -408,7 +411,7 @@ func (s *unchargeSwap) waitForConfirmedHtlc(globalCtx context.Context) (
|
|||
"exceeded (height %v)",
|
||||
maxPreimageRevealHeight, s.height)
|
||||
|
||||
s.state = StateFailTimeout
|
||||
s.state = loopdb.StateFailTimeout
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
@ -429,7 +432,7 @@ func (s *unchargeSwap) waitForConfirmedHtlc(globalCtx context.Context) (
|
|||
case result := <-s.swapPaymentChan:
|
||||
s.swapPaymentChan = nil
|
||||
if result.Err != nil {
|
||||
s.state = StateFailOffchainPayments
|
||||
s.state = loopdb.StateFailOffchainPayments
|
||||
s.log.Infof("Failed swap payment: %v",
|
||||
result.Err)
|
||||
|
||||
|
|
@ -443,7 +446,7 @@ func (s *unchargeSwap) waitForConfirmedHtlc(globalCtx context.Context) (
|
|||
case result := <-s.prePaymentChan:
|
||||
s.prePaymentChan = nil
|
||||
if result.Err != nil {
|
||||
s.state = StateFailOffchainPayments
|
||||
s.state = loopdb.StateFailOffchainPayments
|
||||
s.log.Infof("Failed prepayment: %v",
|
||||
result.Err)
|
||||
|
||||
|
|
@ -504,7 +507,7 @@ func (s *unchargeSwap) waitForConfirmedHtlc(globalCtx context.Context) (
|
|||
// TODO: Improve retry/fee increase mechanism. Once in the mempool, server can
|
||||
// sweep offchain. So we must make sure we sweep successfully before on-chain
|
||||
// timeout.
|
||||
func (s *unchargeSwap) waitForHtlcSpendConfirmed(globalCtx context.Context,
|
||||
func (s *loopOutSwap) waitForHtlcSpendConfirmed(globalCtx context.Context,
|
||||
spendFunc func() error) (*chainntnfs.SpendDetail, error) {
|
||||
|
||||
// Register the htlc spend notification.
|
||||
|
|
@ -556,7 +559,7 @@ func (s *unchargeSwap) waitForHtlcSpendConfirmed(globalCtx context.Context,
|
|||
// published the tx.
|
||||
//
|
||||
// TODO: Use lnd sweeper?
|
||||
func (s *unchargeSwap) sweep(ctx context.Context,
|
||||
func (s *loopOutSwap) sweep(ctx context.Context,
|
||||
htlcOutpoint wire.OutPoint,
|
||||
htlcValue btcutil.Amount) error {
|
||||
|
||||
|
|
@ -579,7 +582,7 @@ func (s *unchargeSwap) sweep(ctx context.Context,
|
|||
s.log.Warnf("Required miner fee %v exceeds max of %v",
|
||||
fee, s.MaxMinerFee)
|
||||
|
||||
if s.state == StatePreimageRevealed {
|
||||
if s.state == loopdb.StatePreimageRevealed {
|
||||
// The currently required fee exceeds the max, but we
|
||||
// already revealed the preimage. The best we can do now
|
||||
// is to republish with the max fee.
|
||||
|
|
@ -603,8 +606,8 @@ func (s *unchargeSwap) sweep(ctx context.Context,
|
|||
// Before publishing the tx, already mark the preimage as revealed. This
|
||||
// is a precaution in case the publish call never returns and would
|
||||
// leave us thinking we didn't reveal yet.
|
||||
if s.state != StatePreimageRevealed {
|
||||
s.state = StatePreimageRevealed
|
||||
if s.state != loopdb.StatePreimageRevealed {
|
||||
s.state = loopdb.StatePreimageRevealed
|
||||
|
||||
err := s.persistState(ctx)
|
||||
if err != nil {
|
||||
|
|
@ -624,24 +627,24 @@ func (s *unchargeSwap) sweep(ctx context.Context,
|
|||
return nil
|
||||
}
|
||||
|
||||
// validateUnchargeContract validates the contract parameters against our
|
||||
// validateLoopOutContract validates the contract parameters against our
|
||||
// request.
|
||||
func validateUnchargeContract(lnd *lndclient.LndServices,
|
||||
func validateLoopOutContract(lnd *lndclient.LndServices,
|
||||
height int32,
|
||||
request *UnchargeRequest,
|
||||
response *newUnchargeResponse) error {
|
||||
request *OutRequest,
|
||||
response *newLoopOutResponse) error {
|
||||
|
||||
// Check invoice amounts.
|
||||
chainParams := lnd.ChainParams
|
||||
|
||||
swapInvoiceAmt, err := utils.GetInvoiceAmt(
|
||||
swapInvoiceAmt, err := swap.GetInvoiceAmt(
|
||||
chainParams, response.swapInvoice,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prepayInvoiceAmt, err := utils.GetInvoiceAmt(
|
||||
prepayInvoiceAmt, err := swap.GetInvoiceAmt(
|
||||
chainParams, response.prepayInvoice,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -663,7 +666,7 @@ func validateUnchargeContract(lnd *lndclient.LndServices,
|
|||
return ErrPrepayAmountTooHigh
|
||||
}
|
||||
|
||||
if response.expiry-height < MinUnchargePreimageRevealDelta {
|
||||
if response.expiry-height < MinLoopOutPreimageRevealDelta {
|
||||
logger.Warnf("Proposed expiry %v (delta %v) too soon",
|
||||
response.expiry, response.expiry-height)
|
||||
|
||||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/lightninglabs/loop/lndclient"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/sweep"
|
||||
"github.com/lightninglabs/loop/test"
|
||||
)
|
||||
|
|
@ -37,7 +38,7 @@ func TestLateHtlcPublish(t *testing.T) {
|
|||
server: server,
|
||||
}
|
||||
|
||||
swap, err := newUnchargeSwap(
|
||||
swap, err := newLoopOutSwap(
|
||||
context.Background(), cfg, height, testRequest,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -63,10 +64,10 @@ func TestLateHtlcPublish(t *testing.T) {
|
|||
errChan <- err
|
||||
}()
|
||||
|
||||
store.assertUnchargeStored()
|
||||
store.assertLoopOutStored()
|
||||
|
||||
state := <-statusChan
|
||||
if state.State != StateInitiated {
|
||||
if state.State != loopdb.StateInitiated {
|
||||
t.Fatal("unexpected state")
|
||||
}
|
||||
|
||||
|
|
@ -86,10 +87,10 @@ func TestLateHtlcPublish(t *testing.T) {
|
|||
errors.New(lndclient.PaymentResultUnknownPaymentHash),
|
||||
)
|
||||
|
||||
store.assertStoreFinished(StateFailTimeout)
|
||||
store.assertStoreFinished(loopdb.StateFailTimeout)
|
||||
|
||||
status := <-statusChan
|
||||
if status.State != StateFailTimeout {
|
||||
if status.State != loopdb.StateFailTimeout {
|
||||
t.Fatal("unexpected state")
|
||||
}
|
||||
|
||||
|
|
@ -18,16 +18,16 @@ import (
|
|||
var (
|
||||
testTime = time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
|
||||
|
||||
testUnchargeOnChainCltvDelta = int32(30)
|
||||
testCltvDelta = 50
|
||||
testSwapFeeBase = btcutil.Amount(21)
|
||||
testSwapFeeRate = int64(100)
|
||||
testInvoiceExpiry = 180 * time.Second
|
||||
testFixedPrepayAmount = btcutil.Amount(100)
|
||||
testMinSwapAmount = btcutil.Amount(10000)
|
||||
testMaxSwapAmount = btcutil.Amount(1000000)
|
||||
testTxConfTarget = 2
|
||||
testRepublishDelay = 10 * time.Second
|
||||
testLoopOutOnChainCltvDelta = int32(30)
|
||||
testCltvDelta = 50
|
||||
testSwapFeeBase = btcutil.Amount(21)
|
||||
testSwapFeeRate = int64(100)
|
||||
testInvoiceExpiry = 180 * time.Second
|
||||
testFixedPrepayAmount = btcutil.Amount(100)
|
||||
testMinSwapAmount = btcutil.Amount(10000)
|
||||
testMaxSwapAmount = btcutil.Amount(1000000)
|
||||
testTxConfTarget = 2
|
||||
testRepublishDelay = 10 * time.Second
|
||||
)
|
||||
|
||||
// serverMock is used in client unit tests to simulate swap server behaviour.
|
||||
|
|
@ -56,10 +56,10 @@ func newServerMock() *serverMock {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *serverMock) NewUnchargeSwap(ctx context.Context,
|
||||
func (s *serverMock) NewLoopOutSwap(ctx context.Context,
|
||||
swapHash lntypes.Hash, amount btcutil.Amount,
|
||||
receiverKey [33]byte) (
|
||||
*newUnchargeResponse, error) {
|
||||
*newLoopOutResponse, error) {
|
||||
|
||||
_, senderKey := test.CreateKey(100)
|
||||
|
||||
|
|
@ -82,24 +82,24 @@ func (s *serverMock) NewUnchargeSwap(ctx context.Context,
|
|||
var senderKeyArray [33]byte
|
||||
copy(senderKeyArray[:], senderKey.SerializeCompressed())
|
||||
|
||||
return &newUnchargeResponse{
|
||||
return &newLoopOutResponse{
|
||||
senderKey: senderKeyArray,
|
||||
swapInvoice: swapPayReqString,
|
||||
prepayInvoice: prePayReqString,
|
||||
expiry: s.height + testUnchargeOnChainCltvDelta,
|
||||
expiry: s.height + testLoopOutOnChainCltvDelta,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *serverMock) GetUnchargeTerms(ctx context.Context) (
|
||||
*UnchargeTerms, error) {
|
||||
func (s *serverMock) GetLoopOutTerms(ctx context.Context) (
|
||||
*LoopOutTerms, error) {
|
||||
|
||||
dest := [33]byte{1, 2, 3}
|
||||
|
||||
return &UnchargeTerms{
|
||||
return &LoopOutTerms{
|
||||
SwapFeeBase: testSwapFeeBase,
|
||||
SwapFeeRate: testSwapFeeRate,
|
||||
SwapPaymentDest: dest,
|
||||
CltvDelta: testUnchargeOnChainCltvDelta,
|
||||
CltvDelta: testLoopOutOnChainCltvDelta,
|
||||
MinSwapAmount: testMinSwapAmount,
|
||||
MaxSwapAmount: testMaxSwapAmount,
|
||||
PrepayAmt: testFixedPrepayAmount,
|
||||
|
|
|
|||
|
|
@ -5,51 +5,54 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/test"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
)
|
||||
|
||||
// storeMock implements a mock client swap store.
|
||||
type storeMock struct {
|
||||
unchargeSwaps map[lntypes.Hash]*UnchargeContract
|
||||
unchargeUpdates map[lntypes.Hash][]SwapState
|
||||
unchargeStoreChan chan UnchargeContract
|
||||
unchargeUpdateChan chan SwapState
|
||||
loopOutSwaps map[lntypes.Hash]*loopdb.LoopOutContract
|
||||
loopOutUpdates map[lntypes.Hash][]loopdb.SwapState
|
||||
loopOutStoreChan chan loopdb.LoopOutContract
|
||||
loopOutUpdateChan chan loopdb.SwapState
|
||||
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
type finishData struct {
|
||||
preimage lntypes.Hash
|
||||
result SwapState
|
||||
result loopdb.SwapState
|
||||
}
|
||||
|
||||
// NewStoreMock instantiates a new mock store.
|
||||
func newStoreMock(t *testing.T) *storeMock {
|
||||
return &storeMock{
|
||||
unchargeStoreChan: make(chan UnchargeContract, 1),
|
||||
unchargeUpdateChan: make(chan SwapState, 1),
|
||||
unchargeSwaps: make(map[lntypes.Hash]*UnchargeContract),
|
||||
unchargeUpdates: make(map[lntypes.Hash][]SwapState),
|
||||
loopOutStoreChan: make(chan loopdb.LoopOutContract, 1),
|
||||
loopOutUpdateChan: make(chan loopdb.SwapState, 1),
|
||||
loopOutSwaps: make(map[lntypes.Hash]*loopdb.LoopOutContract),
|
||||
loopOutUpdates: make(map[lntypes.Hash][]loopdb.SwapState),
|
||||
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
// getUnchargeSwaps returns all swaps currently in the store.
|
||||
func (s *storeMock) getUnchargeSwaps() ([]*PersistentUncharge, error) {
|
||||
result := []*PersistentUncharge{}
|
||||
// FetchLoopOutSwaps returns all swaps currently in the store.
|
||||
//
|
||||
// NOTE: Part of the loopdb.SwapStore interface.
|
||||
func (s *storeMock) FetchLoopOutSwaps() ([]*loopdb.LoopOut, error) {
|
||||
result := []*loopdb.LoopOut{}
|
||||
|
||||
for hash, contract := range s.unchargeSwaps {
|
||||
updates := s.unchargeUpdates[hash]
|
||||
events := make([]*PersistentUnchargeEvent, len(updates))
|
||||
for hash, contract := range s.loopOutSwaps {
|
||||
updates := s.loopOutUpdates[hash]
|
||||
events := make([]*loopdb.LoopOutEvent, len(updates))
|
||||
for i, u := range updates {
|
||||
events[i] = &PersistentUnchargeEvent{
|
||||
events[i] = &loopdb.LoopOutEvent{
|
||||
State: u,
|
||||
}
|
||||
}
|
||||
|
||||
swap := &PersistentUncharge{
|
||||
swap := &loopdb.LoopOut{
|
||||
Hash: hash,
|
||||
Contract: contract,
|
||||
Events: events,
|
||||
|
|
@ -60,58 +63,68 @@ func (s *storeMock) getUnchargeSwaps() ([]*PersistentUncharge, error) {
|
|||
return result, nil
|
||||
}
|
||||
|
||||
// createUncharge adds an initiated swap to the store.
|
||||
func (s *storeMock) createUncharge(hash lntypes.Hash,
|
||||
swap *UnchargeContract) error {
|
||||
// CreateLoopOut adds an initiated swap to the store.
|
||||
//
|
||||
// NOTE: Part of the loopdb.SwapStore interface.
|
||||
func (s *storeMock) CreateLoopOut(hash lntypes.Hash,
|
||||
swap *loopdb.LoopOutContract) error {
|
||||
|
||||
_, ok := s.unchargeSwaps[hash]
|
||||
_, ok := s.loopOutSwaps[hash]
|
||||
if ok {
|
||||
return errors.New("swap already exists")
|
||||
}
|
||||
|
||||
s.unchargeSwaps[hash] = swap
|
||||
s.unchargeUpdates[hash] = []SwapState{}
|
||||
s.unchargeStoreChan <- *swap
|
||||
s.loopOutSwaps[hash] = swap
|
||||
s.loopOutUpdates[hash] = []loopdb.SwapState{}
|
||||
s.loopOutStoreChan <- *swap
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Finalize stores the final swap result.
|
||||
func (s *storeMock) updateUncharge(hash lntypes.Hash, time time.Time,
|
||||
state SwapState) error {
|
||||
// UpdateLoopOut stores a new event for a target loop out swap. This appends to
|
||||
// the event log for a particular swap as it goes through the various stages in
|
||||
// its lifetime.
|
||||
//
|
||||
// NOTE: Part of the loopdb.SwapStore interface.
|
||||
func (s *storeMock) UpdateLoopOut(hash lntypes.Hash, time time.Time,
|
||||
state loopdb.SwapState) error {
|
||||
|
||||
updates, ok := s.unchargeUpdates[hash]
|
||||
updates, ok := s.loopOutUpdates[hash]
|
||||
if !ok {
|
||||
return errors.New("swap does not exists")
|
||||
}
|
||||
|
||||
updates = append(updates, state)
|
||||
s.unchargeUpdates[hash] = updates
|
||||
s.unchargeUpdateChan <- state
|
||||
s.loopOutUpdates[hash] = updates
|
||||
s.loopOutUpdateChan <- state
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *storeMock) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *storeMock) isDone() error {
|
||||
select {
|
||||
case <-s.unchargeStoreChan:
|
||||
case <-s.loopOutStoreChan:
|
||||
return errors.New("storeChan not empty")
|
||||
default:
|
||||
}
|
||||
|
||||
select {
|
||||
case <-s.unchargeUpdateChan:
|
||||
case <-s.loopOutUpdateChan:
|
||||
return errors.New("updateChan not empty")
|
||||
default:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *storeMock) assertUnchargeStored() {
|
||||
func (s *storeMock) assertLoopOutStored() {
|
||||
s.t.Helper()
|
||||
|
||||
select {
|
||||
case <-s.unchargeStoreChan:
|
||||
case <-s.loopOutStoreChan:
|
||||
case <-time.After(test.Timeout):
|
||||
s.t.Fatalf("expected swap to be stored")
|
||||
}
|
||||
|
|
@ -122,8 +135,8 @@ func (s *storeMock) assertStorePreimageReveal() {
|
|||
s.t.Helper()
|
||||
|
||||
select {
|
||||
case state := <-s.unchargeUpdateChan:
|
||||
if state != StatePreimageRevealed {
|
||||
case state := <-s.loopOutUpdateChan:
|
||||
if state != loopdb.StatePreimageRevealed {
|
||||
s.t.Fatalf("unexpected state")
|
||||
}
|
||||
case <-time.After(test.Timeout):
|
||||
|
|
@ -131,11 +144,11 @@ func (s *storeMock) assertStorePreimageReveal() {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *storeMock) assertStoreFinished(expectedResult SwapState) {
|
||||
func (s *storeMock) assertStoreFinished(expectedResult loopdb.SwapState) {
|
||||
s.t.Helper()
|
||||
|
||||
select {
|
||||
case state := <-s.unchargeUpdateChan:
|
||||
case state := <-s.loopOutUpdateChan:
|
||||
if state != expectedResult {
|
||||
s.t.Fatalf("expected result %v, but got %v",
|
||||
expectedResult, state)
|
||||
|
|
|
|||
25
swap.go
25
swap.go
|
|
@ -5,33 +5,34 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/lightninglabs/loop/lndclient"
|
||||
"github.com/lightninglabs/loop/utils"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/swap"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
)
|
||||
|
||||
type swapKit struct {
|
||||
htlc *utils.Htlc
|
||||
htlc *swap.Htlc
|
||||
hash lntypes.Hash
|
||||
|
||||
height int32
|
||||
|
||||
log *utils.SwapLog
|
||||
log *SwapLog
|
||||
|
||||
lastUpdateTime time.Time
|
||||
cost SwapCost
|
||||
state SwapState
|
||||
state loopdb.SwapState
|
||||
executeConfig
|
||||
swapConfig
|
||||
|
||||
contract *SwapContract
|
||||
swapType SwapType
|
||||
contract *loopdb.SwapContract
|
||||
swapType Type
|
||||
}
|
||||
|
||||
func newSwapKit(hash lntypes.Hash, swapType SwapType, cfg *swapConfig,
|
||||
contract *SwapContract) (*swapKit, error) {
|
||||
func newSwapKit(hash lntypes.Hash, swapType Type, cfg *swapConfig,
|
||||
contract *loopdb.SwapContract) (*swapKit, error) {
|
||||
|
||||
// Compose expected on-chain swap script
|
||||
htlc, err := utils.NewHtlc(
|
||||
htlc, err := swap.NewHtlc(
|
||||
contract.CltvExpiry, contract.SenderKey,
|
||||
contract.ReceiverKey, hash,
|
||||
)
|
||||
|
|
@ -45,7 +46,7 @@ func newSwapKit(hash lntypes.Hash, swapType SwapType, cfg *swapConfig,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
log := &utils.SwapLog{
|
||||
log := &SwapLog{
|
||||
Hash: hash,
|
||||
Logger: logger,
|
||||
}
|
||||
|
|
@ -57,7 +58,7 @@ func newSwapKit(hash lntypes.Hash, swapType SwapType, cfg *swapConfig,
|
|||
hash: hash,
|
||||
log: log,
|
||||
htlc: htlc,
|
||||
state: StateInitiated,
|
||||
state: loopdb.StateInitiated,
|
||||
contract: contract,
|
||||
swapType: swapType,
|
||||
}, nil
|
||||
|
|
@ -91,6 +92,6 @@ type genericSwap interface {
|
|||
|
||||
type swapConfig struct {
|
||||
lnd *lndclient.LndServices
|
||||
store swapClientStore
|
||||
store loopdb.SwapStore
|
||||
server swapServerClient
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ import (
|
|||
)
|
||||
|
||||
type swapServerClient interface {
|
||||
GetUnchargeTerms(ctx context.Context) (
|
||||
*UnchargeTerms, error)
|
||||
GetLoopOutTerms(ctx context.Context) (
|
||||
*LoopOutTerms, error)
|
||||
|
||||
NewUnchargeSwap(ctx context.Context,
|
||||
NewLoopOutSwap(ctx context.Context,
|
||||
swapHash lntypes.Hash, amount btcutil.Amount,
|
||||
receiverKey [33]byte) (
|
||||
*newUnchargeResponse, error)
|
||||
*newLoopOutResponse, error)
|
||||
}
|
||||
|
||||
type grpcSwapServerClient struct {
|
||||
|
|
@ -31,7 +31,9 @@ type grpcSwapServerClient struct {
|
|||
conn *grpc.ClientConn
|
||||
}
|
||||
|
||||
func newSwapServerClient(address string, insecure bool) (*grpcSwapServerClient, error) {
|
||||
func newSwapServerClient(address string,
|
||||
insecure bool) (*grpcSwapServerClient, error) {
|
||||
|
||||
serverConn, err := getSwapServerConn(address, insecure)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -45,13 +47,13 @@ func newSwapServerClient(address string, insecure bool) (*grpcSwapServerClient,
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (s *grpcSwapServerClient) GetUnchargeTerms(ctx context.Context) (
|
||||
*UnchargeTerms, error) {
|
||||
func (s *grpcSwapServerClient) GetLoopOutTerms(ctx context.Context) (
|
||||
*LoopOutTerms, error) {
|
||||
|
||||
rpcCtx, rpcCancel := context.WithTimeout(ctx, serverRPCTimeout)
|
||||
defer rpcCancel()
|
||||
quoteResp, err := s.server.UnchargeQuote(rpcCtx,
|
||||
&looprpc.ServerUnchargeQuoteRequest{},
|
||||
quoteResp, err := s.server.LoopOutQuote(rpcCtx,
|
||||
&looprpc.ServerLoopOutQuoteRequest{},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -67,7 +69,7 @@ func (s *grpcSwapServerClient) GetUnchargeTerms(ctx context.Context) (
|
|||
var destArray [33]byte
|
||||
copy(destArray[:], dest)
|
||||
|
||||
return &UnchargeTerms{
|
||||
return &LoopOutTerms{
|
||||
MinSwapAmount: btcutil.Amount(quoteResp.MinSwapAmount),
|
||||
MaxSwapAmount: btcutil.Amount(quoteResp.MaxSwapAmount),
|
||||
PrepayAmt: btcutil.Amount(quoteResp.PrepayAmt),
|
||||
|
|
@ -78,14 +80,14 @@ func (s *grpcSwapServerClient) GetUnchargeTerms(ctx context.Context) (
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (s *grpcSwapServerClient) NewUnchargeSwap(ctx context.Context,
|
||||
swapHash lntypes.Hash, amount btcutil.Amount, receiverKey [33]byte) (
|
||||
*newUnchargeResponse, error) {
|
||||
func (s *grpcSwapServerClient) NewLoopOutSwap(ctx context.Context,
|
||||
swapHash lntypes.Hash, amount btcutil.Amount,
|
||||
receiverKey [33]byte) (*newLoopOutResponse, error) {
|
||||
|
||||
rpcCtx, rpcCancel := context.WithTimeout(ctx, serverRPCTimeout)
|
||||
defer rpcCancel()
|
||||
swapResp, err := s.server.NewUnchargeSwap(rpcCtx,
|
||||
&looprpc.ServerUnchargeSwapRequest{
|
||||
swapResp, err := s.server.NewLoopOutSwap(rpcCtx,
|
||||
&looprpc.ServerLoopOutRequest{
|
||||
SwapHash: swapHash[:],
|
||||
Amt: uint64(amount),
|
||||
ReceiverKey: receiverKey[:],
|
||||
|
|
@ -104,7 +106,7 @@ func (s *grpcSwapServerClient) NewUnchargeSwap(ctx context.Context,
|
|||
return nil, fmt.Errorf("invalid sender key: %v", err)
|
||||
}
|
||||
|
||||
return &newUnchargeResponse{
|
||||
return &newLoopOutResponse{
|
||||
swapInvoice: swapResp.SwapInvoice,
|
||||
prepayInvoice: swapResp.PrepayInvoice,
|
||||
senderKey: senderKey,
|
||||
|
|
@ -135,7 +137,7 @@ func getSwapServerConn(address string, insecure bool) (*grpc.ClientConn, error)
|
|||
return conn, nil
|
||||
}
|
||||
|
||||
type newUnchargeResponse struct {
|
||||
type newLoopOutResponse struct {
|
||||
swapInvoice string
|
||||
prepayInvoice string
|
||||
senderKey [33]byte
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/loop/lndclient"
|
||||
"github.com/lightninglabs/loop/utils"
|
||||
"github.com/lightninglabs/loop/swap"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
)
|
||||
|
|
@ -22,7 +22,7 @@ type Sweeper struct {
|
|||
// CreateSweepTx creates an htlc sweep tx.
|
||||
func (s *Sweeper) CreateSweepTx(
|
||||
globalCtx context.Context, height int32,
|
||||
htlc *utils.Htlc, htlcOutpoint wire.OutPoint,
|
||||
htlc *swap.Htlc, htlcOutpoint wire.OutPoint,
|
||||
keyBytes [33]byte,
|
||||
witnessFunc func(sig []byte) (wire.TxWitness, error),
|
||||
amount, fee btcutil.Amount,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/sweep"
|
||||
"github.com/lightninglabs/loop/test"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
|
|
@ -68,7 +69,7 @@ func newSwapClient(config *clientConfig) *Client {
|
|||
}
|
||||
|
||||
func createClientTestContext(t *testing.T,
|
||||
pendingSwaps []*PersistentUncharge) *testContext {
|
||||
pendingSwaps []*loopdb.LoopOut) *testContext {
|
||||
|
||||
serverMock := newServerMock()
|
||||
|
||||
|
|
@ -76,13 +77,13 @@ func createClientTestContext(t *testing.T,
|
|||
|
||||
store := newStoreMock(t)
|
||||
for _, s := range pendingSwaps {
|
||||
store.unchargeSwaps[s.Hash] = s.Contract
|
||||
store.loopOutSwaps[s.Hash] = s.Contract
|
||||
|
||||
updates := []SwapState{}
|
||||
updates := []loopdb.SwapState{}
|
||||
for _, e := range s.Events {
|
||||
updates = append(updates, e.State)
|
||||
}
|
||||
store.unchargeUpdates[s.Hash] = updates
|
||||
store.loopOutUpdates[s.Hash] = updates
|
||||
}
|
||||
|
||||
expiryChan := make(chan time.Time)
|
||||
|
|
@ -169,7 +170,7 @@ func (ctx *testContext) assertIsDone() {
|
|||
func (ctx *testContext) assertStored() {
|
||||
ctx.T.Helper()
|
||||
|
||||
ctx.store.assertUnchargeStored()
|
||||
ctx.store.assertLoopOutStored()
|
||||
}
|
||||
|
||||
func (ctx *testContext) assertStorePreimageReveal() {
|
||||
|
|
@ -178,21 +179,21 @@ func (ctx *testContext) assertStorePreimageReveal() {
|
|||
ctx.store.assertStorePreimageReveal()
|
||||
}
|
||||
|
||||
func (ctx *testContext) assertStoreFinished(expectedResult SwapState) {
|
||||
func (ctx *testContext) assertStoreFinished(expectedResult loopdb.SwapState) {
|
||||
ctx.T.Helper()
|
||||
|
||||
ctx.store.assertStoreFinished(expectedResult)
|
||||
|
||||
}
|
||||
|
||||
func (ctx *testContext) assertStatus(expectedState SwapState) {
|
||||
func (ctx *testContext) assertStatus(expectedState loopdb.SwapState) {
|
||||
|
||||
ctx.T.Helper()
|
||||
|
||||
for {
|
||||
select {
|
||||
case update := <-ctx.statusChan:
|
||||
if update.SwapType != SwapTypeUncharge {
|
||||
if update.SwapType != TypeOut {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue