github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/cmd/lit-af/chancmds.go (about) 1 package main 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "strconv" 7 8 "github.com/fatih/color" 9 "github.com/mit-dci/lit/litrpc" 10 "github.com/mit-dci/lit/lnutil" 11 ) 12 13 var fundCommand = &Command{ 14 Format: fmt.Sprintf("%s%s%s\n", lnutil.White("fund"), 15 lnutil.ReqColor("peer", "coinType", "capacity", "initialSend"), lnutil.OptColor("data")), 16 Description: fmt.Sprintf("%s\n%s\n%s\n%s\n", 17 "Establish and fund a new lightning channel with the given peer.", 18 "The capacity is the amount of satoshi we insert into the channel,", 19 "and initialSend is the amount we initially hand over to the other party.", 20 "data is an optional field that can contain 32 bytes of hex to send as part of the channel fund", 21 ), 22 ShortDescription: "Establish and fund a new lightning channel with the given peer.\n", 23 } 24 25 var dualFundCommand = &Command{ 26 Format: fmt.Sprintf("%s%s%s\n", lnutil.White("dualfund"), 27 lnutil.ReqColor("subcommand"), lnutil.OptColor("parameters...")), 28 Description: fmt.Sprintf("%s\n%s\n", 29 "Commands for establishing and mutually funding a new lightning channel with the given peer.", 30 "Subcommands: start, accept, decline"), 31 32 ShortDescription: "Commands for dual funding\n", 33 } 34 35 var dualFundStartCommand = &Command{ 36 Format: fmt.Sprintf("%s%s\n", lnutil.White("dualfund start"), 37 lnutil.ReqColor("peer", "coinType", "ourAmount", "theirAmount")), 38 Description: fmt.Sprintf("%s\n%s\n%s\n", 39 "Establish and mutually fund a new lightning channel with the given peer.", 40 "The capacity is the sum of the amounts both peers insert into the channel,", 41 "each party will end up with their funded amount in the channel."), 42 ShortDescription: "Establish and mutually fund a new lightning channel with the given peer.\n", 43 } 44 45 var dualFundDeclineCommand = &Command{ 46 Format: fmt.Sprintf("%s\n", lnutil.White("dualfund decline")), 47 Description: "Declines the pending dual funding request received from another peer (if any)\n", 48 ShortDescription: "Declines the pending dual funding request received from another peer (if any)\n", 49 } 50 51 var dualFundAcceptCommand = &Command{ 52 Format: fmt.Sprintf("%s\n", lnutil.White("dualfund accept")), 53 Description: "Accepts the pending dual funding request received from another peer (if any)\n", 54 ShortDescription: "Accepts the pending dual funding request received from another peer (if any)\n", 55 } 56 57 var watchCommand = &Command{ 58 Format: fmt.Sprintf("%s%s\n", lnutil.White("watch"), 59 lnutil.ReqColor("channel idx", "watchPeerIdx")), 60 Description: fmt.Sprintf("%s\n%s\n", 61 "Send channel data to a watcher", 62 "The watcher can defend your channel while you're offline."), 63 ShortDescription: "Send channel watch data to watcher.\n", 64 } 65 66 var pushCommand = &Command{ 67 Format: fmt.Sprintf("%s%s%s%s\n", lnutil.White("push"), lnutil.ReqColor("channel idx", "amount"), lnutil.OptColor("times"), lnutil.OptColor("data")), 68 Description: fmt.Sprintf("%s\n%s\n%s\n", 69 "Push the given amount (in satoshis) to the other party on the given channel.", 70 "Optionally, the push operation can be associated with a 32 byte value hex encoded.", 71 "Optionally, the push operation can be repeated <times> number of times."), 72 ShortDescription: "Push the given amount (in satoshis) to the other party on the given channel.\n", 73 } 74 75 var closeCommand = &Command{ 76 Format: fmt.Sprintf("%s%s\n", lnutil.White("close"), lnutil.ReqColor("channel idx")), 77 Description: fmt.Sprintf("%s\n%s\n%s%s\n", 78 "Cooperatively close the channel with the given index by asking", 79 "the other party to finalize the channel pay-out.", 80 "See also: ", lnutil.White("break")), 81 ShortDescription: "Cooperatively close the channel with the given index by asking\n", 82 } 83 84 var breakCommand = &Command{ 85 Format: fmt.Sprintf("%s%s\n", lnutil.White("break"), lnutil.ReqColor("channel idx")), 86 Description: fmt.Sprintf("%s\n%s\n%s%s\n", 87 "Forcibly break the given channel. Note that you need to wait", 88 "a set number of blocks before you can use the money.", 89 "See also: ", lnutil.White("stop")), 90 ShortDescription: "Forcibly break the given channel.\n", 91 } 92 93 var historyCommand = &Command{ 94 Format: fmt.Sprintf("%s\n", lnutil.White("history")), 95 Description: "Show all the metadata for justice txs", 96 ShortDescription: "Show all the metadata for justice txs.\n", 97 } 98 99 var addHTLCCommand = &Command{ 100 Format: fmt.Sprintf("%s%s%s\n", lnutil.White("add"), lnutil.ReqColor("channel idx", "amount", "locktime", "RHash"), lnutil.OptColor("data")), 101 Description: fmt.Sprintf("%s\n%s\n", 102 "Add an HTLC of the given amount (in satoshis) to the given channel. Locktime specifies the number of blocks the HTLC stays active before timing out", 103 "Optionally, the push operation can be associated with a 32 byte value hex encoded."), 104 ShortDescription: "Add HTLC of the given amount (in satoshis) to the given channel.\n", 105 } 106 107 var clearHTLCCommand = &Command{ 108 Format: fmt.Sprintf("%s%s%s\n", lnutil.White("clear"), lnutil.ReqColor("channel idx", "HTLC idx", "R"), lnutil.OptColor("data")), 109 Description: fmt.Sprintf("%s\n%s\n%s\n", 110 "Clear an HTLC of the given index from the given channel.", 111 "Optionally, the push operation can be associated with a 32 byte value hex encoded.", 112 "Set R to zero to timeout the HTLC"), 113 ShortDescription: "Clear HTLC of the given index from the given channel.\n", 114 } 115 116 var claimHTLCCommand = &Command{ 117 Format: fmt.Sprintf("%s%s\n", lnutil.White("claim"), lnutil.ReqColor("R")), 118 Description: "Claim any on-chain HTLC that matches the given preimage. Use this to claim an HTLC after the channel is broken.\n", 119 ShortDescription: "Clear HTLC of the given index from the given channel.\n", 120 } 121 122 var paymultihopCommand = &Command{ 123 Format: fmt.Sprintf("%s%s\n", lnutil.White("paymultihop"), lnutil.ReqColor("dest cointype amount")), 124 Description: fmt.Sprintf("%s\n%s%s\n%s%s\n%s%s\n", 125 "Tries to pay using a multi-hop payment. Will fail if no route available", 126 lnutil.White("dest"), ": Destination address", 127 lnutil.White("cointype"), ": Coin type to pay", 128 lnutil.White("amount"), ": Amount to pay"), 129 ShortDescription: "Pay via multi-hop.\n", 130 } 131 132 func (lc *litAfClient) History(textArgs []string) error { 133 if len(textArgs) > 0 && textArgs[0] == "-h" { 134 fmt.Fprintf(color.Output, historyCommand.Format) 135 fmt.Fprintf(color.Output, historyCommand.Description) 136 return nil 137 } 138 139 args := new(litrpc.StateDumpArgs) 140 reply := new(litrpc.StateDumpReply) 141 142 err := lc.Call("LitRPC.StateDump", args, reply) 143 if err != nil { 144 return err 145 } 146 147 for _, tx := range reply.Txs { 148 fmt.Fprintf(color.Output, "Pkh: %x, Idx: %d, Sig: %x, Txid: %x, Data: %x, Amt: %d\n", tx.Pkh, tx.Idx, tx.Sig, tx.Txid, tx.Data, tx.Amt) 149 } 150 151 return nil 152 } 153 154 // CheckHelpCommand checks whether the user wants help regarding the command 155 // or passed invalid arguments. Also checks for expected length of command 156 // and returns and error if the expected length is different. 157 func CheckHelpCommand(command *Command, textArgs []string, expectedLength int) (bool, error) { 158 if len(textArgs) > 0 && textArgs[0] == "-h" { 159 fmt.Fprintf(color.Output, command.Format) 160 fmt.Fprintf(color.Output, command.Description) 161 return true, nil // stop Execution if the guy just wants help 162 } 163 if len(textArgs) < expectedLength { 164 // if number of args are less than expected, return 165 return true, fmt.Errorf(command.Format) // stop execution in case of err 166 } 167 return false, nil 168 } 169 170 func (lc *litAfClient) FundChannel(textArgs []string) error { 171 stopEx, err := CheckHelpCommand(fundCommand, textArgs, 4) 172 if err != nil || stopEx { 173 return err 174 } 175 args := new(litrpc.FundArgs) 176 reply := new(litrpc.FundReply) 177 178 peer, err := strconv.Atoi(textArgs[0]) 179 if err != nil { 180 return err 181 } 182 coinType, err := strconv.Atoi(textArgs[1]) 183 if err != nil { 184 return err 185 } 186 187 cCap, err := strconv.Atoi(textArgs[2]) 188 if err != nil { 189 return err 190 } 191 iSend, err := strconv.Atoi(textArgs[3]) 192 if err != nil { 193 return err 194 } 195 196 if len(textArgs) > 4 { 197 data, err := hex.DecodeString(textArgs[4]) 198 if err != nil { 199 // Wasn't valid hex, copy directly and truncate 200 copy(args.Data[:], textArgs[3]) 201 } else { 202 copy(args.Data[:], data[:]) 203 } 204 } 205 206 args.Peer = uint32(peer) 207 args.CoinType = uint32(coinType) 208 args.Capacity = int64(cCap) 209 args.InitialSend = int64(iSend) 210 211 err = lc.Call("LitRPC.FundChannel", args, reply) 212 if err != nil { 213 return err 214 } 215 216 fmt.Fprintf(color.Output, "funded channel %d (height: %d)\n", reply.ChanIdx, reply.FundHeight) 217 return nil 218 } 219 220 func (lc *litAfClient) DualFund(textArgs []string) error { 221 stopEx, err := CheckHelpCommand(dualFundCommand, textArgs, 2) 222 if err != nil || stopEx { 223 return err 224 } 225 return nil 226 } 227 228 // Mutually fund a channel 229 func (lc *litAfClient) DualFundChannel(textArgs []string) error { 230 stopEx, err := CheckHelpCommand(dualFundStartCommand, textArgs, 4) 231 if err != nil || stopEx { 232 return err 233 } 234 235 args := new(litrpc.DualFundArgs) 236 reply := new(litrpc.StatusReply) 237 238 peer, err := strconv.Atoi(textArgs[0]) 239 if err != nil { 240 return err 241 } 242 coinType, err := strconv.Atoi(textArgs[1]) 243 if err != nil { 244 return err 245 } 246 247 ourAmt, err := strconv.Atoi(textArgs[2]) 248 if err != nil { 249 return err 250 } 251 252 theirAmt, err := strconv.Atoi(textArgs[3]) 253 if err != nil { 254 return err 255 } 256 257 args.Peer = uint32(peer) 258 args.CoinType = uint32(coinType) 259 args.OurAmount = int64(ourAmt) 260 args.TheirAmount = int64(theirAmt) 261 262 err = lc.Call("LitRPC.DualFundChannel", args, reply) 263 if err != nil { 264 return err 265 } 266 267 fmt.Fprintf(color.Output, "%s\n", reply.Status) 268 return nil 269 } 270 271 func (lc *litAfClient) dualFundRespond(aor bool) error { 272 reply := new(litrpc.StatusReply) 273 args := new(litrpc.DualFundRespondArgs) 274 args.AcceptOrDecline = aor 275 err := lc.Call("LitRPC.DualFundRespond", args, reply) 276 if err != nil { 277 return err 278 } 279 fmt.Fprintf(color.Output, "%s\n", reply.Status) 280 return nil 281 } 282 283 // Decline mutual funding of a channel 284 func (lc *litAfClient) DualFundDecline(textArgs []string) error { 285 stopEx, err := CheckHelpCommand(dualFundDeclineCommand, textArgs, 0) 286 if err != nil || stopEx { 287 return err 288 } 289 290 return lc.dualFundRespond(false) 291 } 292 293 // Accept mutual funding of a channel 294 func (lc *litAfClient) DualFundAccept(textArgs []string) error { 295 stopEx, err := CheckHelpCommand(dualFundAcceptCommand, textArgs, 0) 296 if err != nil || stopEx { 297 return err 298 } 299 300 return lc.dualFundRespond(true) 301 } 302 303 // Request close of a channel. Need to pass in peer, channel index 304 func (lc *litAfClient) CloseChannel(textArgs []string) error { 305 stopEx, err := CheckHelpCommand(closeCommand, textArgs, 1) 306 if err != nil || stopEx { 307 return err 308 } 309 310 args := new(litrpc.ChanArgs) 311 reply := new(litrpc.StatusReply) 312 313 cIdx, err := strconv.Atoi(textArgs[0]) 314 if err != nil { 315 return err 316 } 317 318 args.ChanIdx = uint32(cIdx) 319 320 err = lc.Call("LitRPC.CloseChannel", args, reply) 321 if err != nil { 322 return err 323 } 324 325 fmt.Fprintf(color.Output, "%s\n", reply.Status) 326 return nil 327 } 328 329 // Almost exactly the same as CloseChannel. Maybe make "break" a bool...? 330 func (lc *litAfClient) BreakChannel(textArgs []string) error { 331 stopEx, err := CheckHelpCommand(breakCommand, textArgs, 1) 332 if err != nil || stopEx { 333 return err 334 } 335 336 args := new(litrpc.ChanArgs) 337 reply := new(litrpc.StatusReply) 338 339 cIdx, err := strconv.Atoi(textArgs[0]) 340 if err != nil { 341 return err 342 } 343 344 args.ChanIdx = uint32(cIdx) 345 346 err = lc.Call("LitRPC.BreakChannel", args, reply) 347 if err != nil { 348 return err 349 } 350 351 fmt.Fprintf(color.Output, "%s\n", reply.Status) 352 return nil 353 } 354 355 // Push is the shell command which calls PushChannel 356 func (lc *litAfClient) Push(textArgs []string) error { 357 stopEx, err := CheckHelpCommand(pushCommand, textArgs, 2) 358 if err != nil || stopEx { 359 return err 360 } 361 362 args := new(litrpc.PushArgs) 363 reply := new(litrpc.PushReply) 364 365 // this stuff is all the same as in cclose, should put into a function... 366 cIdx, err := strconv.Atoi(textArgs[0]) 367 if err != nil { 368 return err 369 } 370 amt, err := strconv.Atoi(textArgs[1]) 371 if err != nil { 372 return err 373 } 374 375 times := int(1) 376 if len(textArgs) > 2 { 377 times, err = strconv.Atoi(textArgs[2]) 378 if err != nil { 379 return err 380 } 381 } 382 383 if len(textArgs) > 3 { 384 data, err := hex.DecodeString(textArgs[3]) 385 if err != nil { 386 // Wasn't valid hex, copy directly and truncate 387 copy(args.Data[:], textArgs[3]) 388 } else { 389 copy(args.Data[:], data[:]) 390 } 391 } 392 393 args.ChanIdx = uint32(cIdx) 394 args.Amt = int64(amt) 395 396 for times > 0 { 397 err := lc.Call("LitRPC.Push", args, reply) 398 if err != nil { 399 return err 400 } 401 fmt.Fprintf(color.Output, "Pushed %s at state %s\n", lnutil.SatoshiColor(int64(amt)), lnutil.White(reply.StateIndex)) 402 times-- 403 } 404 405 return nil 406 } 407 408 func (lc *litAfClient) Dump(textArgs []string) error { 409 pReply := new(litrpc.DumpReply) 410 pArgs := new(litrpc.NoArgs) 411 412 err := lc.Call("LitRPC.DumpPrivs", pArgs, pReply) 413 if err != nil { 414 return err 415 } 416 fmt.Fprintf(color.Output, "Private keys for all channels and utxos:\n") 417 418 // Display DumpPriv info 419 for i, t := range pReply.Privs { 420 fmt.Fprintf(color.Output, "%d %s h:%d amt:%s %s ", 421 i, lnutil.OutPoint(t.OutPoint), t.Height, 422 lnutil.SatoshiColor(t.Amt), t.CoinType) 423 if t.Delay != 0 { 424 fmt.Fprintf(color.Output, " delay: %d", t.Delay) 425 } 426 if !t.Witty { 427 fmt.Fprintf(color.Output, " non-witness") 428 } 429 if len(t.PairKey) > 1 { 430 fmt.Fprintf( 431 color.Output, "\nPair Pubkey: %s", lnutil.Green(t.PairKey)) 432 } 433 fmt.Fprintf(color.Output, "\n\tprivkey: %s", lnutil.Red(t.WIF)) 434 fmt.Fprintf(color.Output, "\n") 435 } 436 437 return nil 438 } 439 440 func (lc *litAfClient) Watch(textArgs []string) error { 441 stopEx, err := CheckHelpCommand(watchCommand, textArgs, 2) 442 if err != nil || stopEx { 443 return err 444 } 445 446 args := new(litrpc.WatchArgs) 447 reply := new(litrpc.WatchReply) 448 449 cIdx, err := strconv.Atoi(textArgs[0]) 450 if err != nil { 451 return err 452 } 453 454 peer, err := strconv.Atoi(textArgs[1]) 455 if err != nil { 456 return err 457 } 458 459 args.ChanIdx = uint32(cIdx) 460 args.SendToPeer = uint32(peer) 461 462 err = lc.Call("LitRPC.Watch", args, reply) 463 if err != nil { 464 return err 465 } 466 467 fmt.Fprintf(color.Output, "Send channel %d data to peer %d\n", 468 args.ChanIdx, args.SendToPeer) 469 470 return nil 471 } 472 473 // Add is the shell command which calls AddHTLC 474 func (lc *litAfClient) AddHTLC(textArgs []string) error { 475 stopEx, err := CheckHelpCommand(addHTLCCommand, textArgs, 3) 476 477 if err != nil || stopEx { 478 return err 479 } 480 481 args := new(litrpc.AddHTLCArgs) 482 reply := new(litrpc.AddHTLCReply) 483 484 // this stuff is all the same as in cclose, should put into a function... 485 cIdx, err := strconv.Atoi(textArgs[0]) 486 if err != nil { 487 return err 488 } 489 amt, err := strconv.Atoi(textArgs[1]) 490 if err != nil { 491 return err 492 } 493 locktime, err := strconv.Atoi(textArgs[2]) 494 if err != nil { 495 return err 496 } 497 498 RHash, err := hex.DecodeString(textArgs[3]) 499 if err != nil { 500 return err 501 } 502 copy(args.RHash[:], RHash[:]) 503 504 if len(textArgs) > 4 { 505 data, err := hex.DecodeString(textArgs[4]) 506 if err != nil { 507 // Wasn't valid hex, copy directly and truncate 508 copy(args.Data[:], textArgs[4]) 509 } else { 510 copy(args.Data[:], data[:]) 511 } 512 } 513 514 args.ChanIdx = uint32(cIdx) 515 args.Amt = int64(amt) 516 args.LockTime = uint32(locktime) 517 518 err = lc.Call("LitRPC.AddHTLC", args, reply) 519 if err != nil { 520 return err 521 } 522 fmt.Fprintf(color.Output, "Added HTLC %s at state %s idx %s\n", lnutil.SatoshiColor(int64(amt)), lnutil.White(reply.StateIndex), lnutil.White(reply.HTLCIndex)) 523 524 return nil 525 } 526 527 // Clear is the shell command which calls ClearHTLC 528 func (lc *litAfClient) ClearHTLC(textArgs []string) error { 529 stopEx, err := CheckHelpCommand(clearHTLCCommand, textArgs, 3) 530 if err != nil || stopEx { 531 return err 532 } 533 534 args := new(litrpc.ClearHTLCArgs) 535 reply := new(litrpc.ClearHTLCReply) 536 537 // this stuff is all the same as in cclose, should put into a function... 538 cIdx, err := strconv.Atoi(textArgs[0]) 539 if err != nil { 540 return err 541 } 542 HTLCIdx, err := strconv.Atoi(textArgs[1]) 543 if err != nil { 544 return err 545 } 546 547 R, err := hex.DecodeString(textArgs[2]) 548 if err != nil { 549 return err 550 } 551 copy(args.R[:], R[:]) 552 553 if len(textArgs) > 3 { 554 data, err := hex.DecodeString(textArgs[3]) 555 if err != nil { 556 // Wasn't valid hex, copy directly and truncate 557 copy(args.Data[:], textArgs[3]) 558 } else { 559 copy(args.Data[:], data[:]) 560 } 561 } 562 563 args.ChanIdx = uint32(cIdx) 564 args.HTLCIdx = uint32(HTLCIdx) 565 566 err = lc.Call("LitRPC.ClearHTLC", args, reply) 567 if err != nil { 568 return err 569 } 570 fmt.Fprintf(color.Output, "Cleared HTLC %s at state %s\n", lnutil.White(HTLCIdx), lnutil.White(reply.StateIndex)) 571 572 return nil 573 } 574 575 // Clear is the shell command which calls ClearHTLC 576 func (lc *litAfClient) ClaimHTLC(textArgs []string) error { 577 stopEx, err := CheckHelpCommand(claimHTLCCommand, textArgs, 1) 578 if err != nil || stopEx { 579 return err 580 } 581 582 args := new(litrpc.ClaimHTLCArgs) 583 reply := new(litrpc.TxidsReply) 584 585 R, err := hex.DecodeString(textArgs[0]) 586 if err != nil { 587 return err 588 } 589 copy(args.R[:], R[:]) 590 591 err = lc.Call("LitRPC.ClaimHTLC", args, reply) 592 if err != nil { 593 return err 594 } 595 for _, txid := range reply.Txids { 596 fmt.Fprintf(color.Output, "Claimed HTLC with txid %s\n", lnutil.White(txid)) 597 } 598 599 return nil 600 } 601 602 func (lc *litAfClient) PayMultihop(textArgs []string) error { 603 if len(textArgs) > 0 && textArgs[0] == "-h" { 604 fmt.Fprintf(color.Output, paymultihopCommand.Format) 605 fmt.Fprintf(color.Output, paymultihopCommand.Description) 606 return nil 607 } 608 609 args := new(litrpc.PayMultihopArgs) 610 reply := new(litrpc.StatusReply) 611 612 if len(textArgs) < 3 { 613 return fmt.Errorf("need args: paymultihop dest destCoinType originCoinType amount") 614 } 615 616 args.DestLNAdr = textArgs[0] 617 destcointype, err := strconv.Atoi(textArgs[1]) 618 if err != nil { 619 return err 620 } 621 args.DestCoinType = uint32(destcointype) 622 623 origincointype, err := strconv.Atoi(textArgs[2]) 624 if err != nil { 625 return err 626 } 627 args.OriginCoinType = uint32(origincointype) 628 629 amount, err := strconv.Atoi(textArgs[3]) 630 if err != nil { 631 return err 632 } 633 args.Amt = int64(amount) 634 635 err = lc.Call("LitRPC.PayMultihop", args, reply) 636 if err != nil { 637 return err 638 } 639 640 fmt.Fprintf(color.Output, "%s\n", reply.Status) 641 642 return nil 643 }