Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add swaps 2.0 #810

Open
wants to merge 14 commits into
base: hyperloop
Choose a base branch
from
78 changes: 78 additions & 0 deletions cmd/loop/hyperloop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package main

import (
"context"

"github.com/lightninglabs/loop/looprpc"
"github.com/urfave/cli"
)

var hyperloopCommand = cli.Command{
Name: "hyperloop",
Usage: "perform a fee optimized off-chain to on-chain swap (hyperloop)",
Description: `

`,
ArgsUsage: "amt",
Flags: []cli.Flag{
cli.Uint64Flag{
Name: "amt",
Usage: "the amount in satoshis to loop out. To check " +
"for the minimum and maximum amounts to loop " +
"out please consult \"loop terms\"",
},
cli.StringFlag{
Name: "addr",
Usage: "the optional address that the looped out funds " +
"should be sent to, if let blank the funds " +
"will go to lnd's wallet",
},
},

Action: hyperloop,
}

func hyperloop(ctx *cli.Context) error {
args := ctx.Args()

var amtStr string
switch {
case ctx.IsSet("amt"):
amtStr = ctx.String("amt")

case ctx.NArg() > 0:
amtStr = args[0]

default:
// Show command help if no arguments and flags were provided.
return cli.ShowCommandHelp(ctx, "hyperloop")
}

amt, err := parseAmt(amtStr)
if err != nil {
return err
}

// First set up the swap client itself.
client, cleanup, err := getClient(ctx)
if err != nil {
return err
}
defer cleanup()
ctxb := context.Background()

hyperloopRes, err := client.HyperLoopOut(
ctxb,
&looprpc.HyperLoopOutRequest{
Amt: uint64(amt),
CustomSweepAddr: ctx.String("addr"),
},
)
if err != nil {
return err
}

printJSON(hyperloopRes)

return nil
}
2 changes: 1 addition & 1 deletion cmd/loop/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func main() {
listSwapsCommand, swapInfoCommand, getLiquidityParamsCommand,
setLiquidityRuleCommand, suggestSwapCommand, setParamsCommand,
getInfoCommand, abandonSwapCommand, reservationsCommands,
instantOutCommand, listInstantOutsCommand,
instantOutCommand, listInstantOutsCommand, hyperloopCommand,
}

err := app.Run(os.Args)
Expand Down
21 changes: 21 additions & 0 deletions fsm/fsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,27 @@ func (s *StateMachine) SendEvent(ctx context.Context, event EventType,
}
}

// GetStateMachineLock locks the state machine mutex and returns a function that
// unlocks it.
func (s *StateMachine) GetStateMachineLock(ctx context.Context) (func(), error) {
gotLock := make(chan struct{})
// Try to get the lock.
go func() {
s.mutex.Lock()
close(gotLock)
}()

for {
select {
case <-ctx.Done():
return nil, errors.New("context canceled")

case <-gotLock:
return s.mutex.Unlock, nil
}
}
}

// RegisterObserver registers an observer with the state machine.
func (s *StateMachine) RegisterObserver(observer Observer) {
s.observerMutex.Lock()
Expand Down
72 changes: 72 additions & 0 deletions fsm/generic_fsm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package fsm

import "sync"

type GenericFSM[T any] struct {
*StateMachine

Val *T
ValLock sync.RWMutex
}

// NewGenericFSM creates a new generic FSM with the given initial state and
// value.
func NewGenericFSM[T any](fsm *StateMachine, val *T) *GenericFSM[T] {
return &GenericFSM[T]{
StateMachine: fsm,
Val: val,
}
}

type callOptions struct {
withMainMutex bool
}

// CallOption is a functional option that can be used to modify the runFuncs.
type CallOption func(*callOptions)

// WithMainMutex is an option that can be used to run the function with the main
// mutex locked. This requires the FSM to not be currently running an action.
func WithMainMutex() CallOption {
return func(o *callOptions) {
o.withMainMutex = true
}
}

// RunFunc runs the given function in the FSM. It locks the FSM value lock
// before running the function and unlocks it after the function is done.
func (fsm *GenericFSM[T]) RunFunc(fn func(val *T) error, options ...CallOption,
) error {

opts := &callOptions{}
for _, option := range options {
option(opts)
}

fsm.ValLock.Lock()
defer fsm.ValLock.Unlock()
if opts.withMainMutex {
fsm.mutex.Lock()
defer fsm.mutex.Unlock()
}

return fn(fsm.Val)
}

// GetVal returns the value of the FSM.
func (fsm *GenericFSM[T]) GetVal(options ...CallOption) *T {
opts := &callOptions{}
for _, option := range options {
option(opts)
}

fsm.ValLock.RLock()
defer fsm.ValLock.RUnlock()

if opts.withMainMutex {
fsm.mutex.Lock()
defer fsm.mutex.Unlock()
}

return fsm.Val
}
8 changes: 8 additions & 0 deletions fsm/stateparser/stateparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"sort"

"github.com/lightninglabs/loop/fsm"
"github.com/lightninglabs/loop/hyperloop"
"github.com/lightninglabs/loop/instantout"
"github.com/lightninglabs/loop/instantout/reservation"
)
Expand Down Expand Up @@ -57,6 +58,13 @@ func run() error {
return err
}

case "hyperloop":
hyperloop := hyperloop.FSM{}
err = writeMermaidFile(fp, hyperloop.GetStateMap())
if err != nil {
return err
}

default:
fmt.Println("Missing or wrong argument: fsm must be one of:")
fmt.Println("\treservations")
Expand Down
Loading
Loading