diff --git a/cmd/loop/main.go b/cmd/loop/main.go index e711705..42ffba3 100644 --- a/cmd/loop/main.go +++ b/cmd/loop/main.go @@ -147,7 +147,7 @@ func main() { monitorCommand, quoteCommand, listAuthCommand, listSwapsCommand, swapInfoCommand, getLiquidityParamsCommand, setLiquidityRuleCommand, suggestSwapCommand, setParamsCommand, - getInfoCommand, + getInfoCommand, abandonSwapCommand, } err := app.Run(os.Args) diff --git a/cmd/loop/swaps.go b/cmd/loop/swaps.go index dfab471..8c8dcc6 100644 --- a/cmd/loop/swaps.go +++ b/cmd/loop/swaps.go @@ -90,3 +90,73 @@ func swapInfo(ctx *cli.Context) error { printRespJSON(resp) return nil } + +var abandonSwapCommand = cli.Command{ + Name: "abandonswap", + Usage: "abandon a swap with a given swap hash", + Description: "This command overrides the database and abandons a " + + "swap with a given swap hash.\n\n" + + "!!! This command might potentially lead to loss of funds if " + + "it is applied to swaps that are still waiting for pending " + + "user funds. Before executing this command make sure that " + + "no funds are locked by the swap.", + ArgsUsage: "ID", + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "i_know_what_i_am_doing", + Usage: "Specify this flag if you made sure that you " + + "read and understood the following " + + "consequence of applying this command.", + }, + }, + Action: abandonSwap, +} + +func abandonSwap(ctx *cli.Context) error { + args := ctx.Args() + + var id string + switch { + case ctx.IsSet("id"): + id = ctx.String("id") + + case ctx.NArg() > 0: + id = args[0] + args = args.Tail() // nolint:wastedassign + + default: + // Show command help if no arguments and flags were provided. + return cli.ShowCommandHelp(ctx, "abandonswap") + } + + if len(id) != hex.EncodedLen(lntypes.HashSize) { + return fmt.Errorf("invalid swap ID") + } + idBytes, err := hex.DecodeString(id) + if err != nil { + return fmt.Errorf("cannot hex decode id: %v", err) + } + + client, cleanup, err := getClient(ctx) + if err != nil { + return err + } + defer cleanup() + + if !ctx.Bool("i_know_what_i_am_doing") { + return cli.ShowCommandHelp(ctx, "abandonswap") + } + + resp, err := client.AbandonSwap( + context.Background(), &looprpc.AbandonSwapRequest{ + Id: idBytes, + IKnowWhatIAmDoing: ctx.Bool("i_know_what_i_am_doing"), + }, + ) + if err != nil { + return err + } + + printRespJSON(resp) + return nil +} diff --git a/loopd/perms/perms.go b/loopd/perms/perms.go index 0179e30..90b7aa2 100644 --- a/loopd/perms/perms.go +++ b/loopd/perms/perms.go @@ -31,6 +31,16 @@ var RequiredPermissions = map[string][]bakery.Op{ Entity: "swap", Action: "read", }}, + "/looprpc.SwapClient/AbandonSwap": {{ + Entity: "swap", + Action: "execute", + }, { + Entity: "loop", + Action: "in", + }, { + Entity: "loop", + Action: "out", + }}, "/looprpc.SwapClient/LoopOutTerms": {{ Entity: "terms", Action: "read",