github.com/decred/dcrlnd@v0.7.6/cmd/dcrlncli/cmd_open_channel.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/rand" 7 "encoding/base64" 8 "encoding/hex" 9 "encoding/json" 10 "fmt" 11 "io" 12 "io/ioutil" 13 "os" 14 "strconv" 15 "strings" 16 17 "github.com/decred/dcrd/chaincfg/chainhash" 18 "github.com/decred/dcrd/dcrutil/v4" 19 "github.com/decred/dcrd/wire" 20 "github.com/decred/dcrlnd/lnrpc" 21 "github.com/decred/dcrlnd/lnwallet/chanfunding" 22 "github.com/urfave/cli" 23 ) 24 25 const ( 26 userMsgFund = `PSBT funding initiated with peer %x. 27 Please create a PSBT that sends %v (%d satoshi) to the funding address %s. 28 29 Note: The whole process should be completed within 10 minutes, otherwise there 30 is a risk of the remote node timing out and canceling the funding process. 31 32 Example with bitcoind: 33 bitcoin-cli walletcreatefundedpsbt [] '[{"%s":%.8f}]' 34 35 If you are using a wallet that can fund a PSBT directly (currently not possible 36 with bitcoind), you can use this PSBT that contains the same address and amount: 37 %s 38 39 !!! WARNING !!! 40 DO NOT PUBLISH the finished transaction by yourself or with another tool. 41 lnd MUST publish it in the proper funding flow order OR THE FUNDS CAN BE LOST! 42 43 Paste the funded PSBT here to continue the funding flow. 44 If your PSBT is very long (specifically, more than 4096 characters), please save 45 it to a file and paste the full file path here instead as some terminals will 46 truncate the pasted text if it's too long. 47 Base64 encoded PSBT (or path to text file): ` 48 49 userMsgSign = ` 50 PSBT verified by lnd, please continue the funding flow by signing the PSBT by 51 all required parties/devices. Once the transaction is fully signed, paste it 52 again here either in base64 PSBT or hex encoded raw wire TX format. 53 54 Signed base64 encoded PSBT or hex encoded raw wire TX (or path to text file): ` 55 56 // psbtMaxFileSize is the maximum file size we allow a PSBT file to be 57 // in case we want to read a PSBT from a file. This is mainly to protect 58 // the user from choosing a large file by accident and running into out 59 // of memory issues or other weird errors. 60 psbtMaxFileSize = 1024 * 1024 61 62 channelTypeTweakless = "tweakless" 63 channelTypeAnchors = "anchors" 64 ) 65 66 // TODO(roasbeef): change default number of confirmations 67 var openChannelCommand = cli.Command{ 68 Name: "openchannel", 69 Category: "Channels", 70 Usage: "Open a channel to a node or an existing peer.", 71 Description: ` 72 Attempt to open a new channel to an existing peer with the key node-key 73 optionally blocking until the channel is 'open'. 74 75 One can also connect to a node before opening a new channel to it by 76 setting its host:port via the '--connect' argument. For this to work, 77 the 'node_key' must be provided, rather than the 'peer_id'. This is optional. 78 79 The channel will be initialized with 'local-amt' atoms locally and 'push-amt' 80 atoms for the remote node. Note that specifying push-amt means you give that 81 amount to the remote node as part of the channel opening. Once the channel is open, 82 a 'channelPoint' ('txid:vout') of the funding output is returned. 83 84 If the remote peer supports the option upfront shutdown feature bit (query 85 'listpeers' to see their supported feature bits), an address to enforce 86 payout of funds on cooperative close can optionally be provided. Note that 87 if you set this value, you will not be able to cooperatively close out to 88 another address. 89 90 One can manually set the fee to be used for the funding transaction via either 91 the '--conf_target' or '--atoms_per_byte' arguments. This is optional.`, 92 ArgsUsage: "node-key local-amt push-amt", 93 Flags: []cli.Flag{ 94 cli.StringFlag{ 95 Name: "node_key", 96 Usage: "The identity public key of the target node/peer " + 97 "serialized in compressed format", 98 }, 99 cli.StringFlag{ 100 Name: "connect", 101 Usage: "The host:port of the target node (optional)", 102 }, 103 cli.IntFlag{ 104 Name: "local_amt", 105 Usage: "The number of atoms the wallet should commit to the channel", 106 }, 107 cli.IntFlag{ 108 Name: "push_amt", 109 Usage: "The number of atoms to give the remote side " + 110 "as part of the initial commitment state, " + 111 "this is equivalent to first opening a " + 112 "channel and sending the remote party funds, " + 113 "but done all in one step", 114 }, 115 cli.BoolFlag{ 116 Name: "block", 117 Usage: "Block and wait until the channel is fully open", 118 }, 119 cli.Int64Flag{ 120 Name: "conf_target", 121 Usage: "The number of blocks that the " + 122 "transaction *should* confirm in, will be " + 123 "used for fee estimation (optional)", 124 }, 125 cli.Int64Flag{ 126 Name: "atoms_per_byte", 127 Usage: "A manual fee expressed in " + 128 "atom/byte that should be used when crafting " + 129 "the transaction (optional)", 130 }, 131 cli.BoolFlag{ 132 Name: "private", 133 Usage: "Make the channel private, such that it won't " + 134 "be announced to the greater network, and " + 135 "nodes other than the two channel endpoints " + 136 "must be explicitly told about it to be able " + 137 "to route through it", 138 }, 139 cli.Int64Flag{ 140 Name: "min_htlc_m_atoms", 141 Usage: "The minimum value we will require " + 142 "for incoming HTLCs on the channel (optional)", 143 }, 144 cli.Uint64Flag{ 145 Name: "remote_csv_delay", 146 Usage: "The number of blocks we will require " + 147 "our channel counterparty to wait before accessing " + 148 "its funds in case of unilateral close. If this is " + 149 "not set, we will scale the value according to the " + 150 "channel size (optional)", 151 }, 152 cli.Uint64Flag{ 153 Name: "max_local_csv", 154 Usage: "(optional) the maximum number of blocks that " + 155 "we will allow the remote peer to require we " + 156 "wait before accessing our funds in the case " + 157 "of a unilateral close.", 158 }, 159 cli.Uint64Flag{ 160 Name: "min_confs", 161 Usage: "The minimum number of confirmations " + 162 "each one of your outputs used for the funding " + 163 "transaction must satisfy (optional)", 164 Value: defaultUtxoMinConf, 165 }, 166 cli.StringFlag{ 167 Name: "close_address", 168 Usage: "An address to enforce payout of our " + 169 "funds to on cooperative close. Note that if this " + 170 "value is set on channel open, you will *not* be " + 171 "able to cooperatively close to a different address (optional)", 172 }, 173 cli.BoolFlag{ 174 Name: "psbt", 175 Usage: "start an interactive mode that initiates " + 176 "funding through a partially signed bitcoin " + 177 "transaction (PSBT), allowing the channel " + 178 "funds to be added and signed from a hardware " + 179 "or other offline device.", 180 }, 181 cli.StringFlag{ 182 Name: "base_psbt", 183 Usage: "when using the interactive PSBT mode to open " + 184 "a new channel, use this base64 encoded PSBT " + 185 "as a base and add the new channel output to " + 186 "it instead of creating a new, empty one.", 187 }, 188 cli.BoolFlag{ 189 Name: "no_publish", 190 Usage: "when using the interactive PSBT mode to open " + 191 "multiple channels in a batch, this flag " + 192 "instructs lnd to not publish the full batch " + 193 "transaction just yet. For safety reasons " + 194 "this flag should be set for each of the " + 195 "batch's transactions except the very last", 196 }, 197 cli.Uint64Flag{ 198 Name: "remote_max_value_in_flight_m_atoms", 199 Usage: "The maximum value in matoms that " + 200 "can be pending within the channel at any " + 201 "given time (optional).", 202 }, 203 cli.StringFlag{ 204 Name: "channel_type", 205 Usage: fmt.Sprintf("(optional) the type of channel to "+ 206 "propose to the remote peer (%q, %q)", 207 channelTypeTweakless, channelTypeAnchors), 208 }, 209 }, 210 Action: actionDecorator(openChannel), 211 } 212 213 func openChannel(ctx *cli.Context) error { 214 // TODO(roasbeef): add deadline to context 215 ctxc := getContext() 216 client, cleanUp := getClient(ctx) 217 defer cleanUp() 218 219 args := ctx.Args() 220 var err error 221 222 // Show command help if no arguments provided 223 if ctx.NArg() == 0 && ctx.NumFlags() == 0 { 224 _ = cli.ShowCommandHelp(ctx, "openchannel") 225 return nil 226 } 227 228 minConfs := int32(ctx.Uint64("min_confs")) 229 req := &lnrpc.OpenChannelRequest{ 230 TargetConf: int32(ctx.Int64("conf_target")), 231 AtomsPerByte: ctx.Int64("atoms_per_byte"), 232 MinHtlcMAtoms: ctx.Int64("min_htlc_m_atoms"), 233 RemoteCsvDelay: uint32(ctx.Uint64("remote_csv_delay")), 234 MinConfs: minConfs, 235 SpendUnconfirmed: minConfs == 0, 236 CloseAddress: ctx.String("close_address"), 237 RemoteMaxValueInFlightMAtoms: ctx.Uint64("remote_max_value_in_flight_m_atoms"), 238 MaxLocalCsv: uint32(ctx.Uint64("max_local_csv")), 239 } 240 241 switch { 242 case ctx.IsSet("node_key"): 243 nodePubHex, err := hex.DecodeString(ctx.String("node_key")) 244 if err != nil { 245 return fmt.Errorf("unable to decode node public key: %v", err) 246 } 247 req.NodePubkey = nodePubHex 248 249 case args.Present(): 250 nodePubHex, err := hex.DecodeString(args.First()) 251 if err != nil { 252 return fmt.Errorf("unable to decode node public key: %v", err) 253 } 254 args = args.Tail() 255 req.NodePubkey = nodePubHex 256 default: 257 return fmt.Errorf("node id argument missing") 258 } 259 260 // As soon as we can confirm that the node's node_key was set, rather 261 // than the peer_id, we can check if the host:port was also set to 262 // connect to it before opening the channel. 263 if req.NodePubkey != nil && ctx.IsSet("connect") { 264 addr := &lnrpc.LightningAddress{ 265 Pubkey: hex.EncodeToString(req.NodePubkey), 266 Host: ctx.String("connect"), 267 } 268 269 req := &lnrpc.ConnectPeerRequest{ 270 Addr: addr, 271 Perm: false, 272 } 273 274 // Check if connecting to the node was successful. 275 // We discard the peer id returned as it is not needed. 276 _, err := client.ConnectPeer(ctxc, req) 277 if err != nil && 278 !strings.Contains(err.Error(), "already connected") { 279 return err 280 } 281 } 282 283 switch { 284 case ctx.IsSet("local_amt"): 285 req.LocalFundingAmount = int64(ctx.Int("local_amt")) 286 case args.Present(): 287 req.LocalFundingAmount, err = strconv.ParseInt(args.First(), 10, 64) 288 if err != nil { 289 return fmt.Errorf("unable to decode local amt: %v", err) 290 } 291 args = args.Tail() 292 default: 293 return fmt.Errorf("local amt argument missing") 294 } 295 296 if ctx.IsSet("push_amt") { 297 req.PushAtoms = int64(ctx.Int("push_amt")) 298 } else if args.Present() { 299 req.PushAtoms, err = strconv.ParseInt(args.First(), 10, 64) 300 if err != nil { 301 return fmt.Errorf("unable to decode push amt: %v", err) 302 } 303 } 304 305 req.Private = ctx.Bool("private") 306 307 // Parse the channel type and map it to its RPC representation. 308 channelType := ctx.String("channel_type") 309 switch channelType { 310 case "": 311 break 312 case channelTypeTweakless: 313 req.CommitmentType = lnrpc.CommitmentType_STATIC_REMOTE_KEY 314 case channelTypeAnchors: 315 req.CommitmentType = lnrpc.CommitmentType_ANCHORS 316 default: 317 return fmt.Errorf("unsupported channel type %v", channelType) 318 } 319 320 // PSBT funding is a more involved, interactive process that is too 321 // large to also fit into this already long function. 322 if ctx.Bool("psbt") { 323 return openChannelPsbt(ctxc, ctx, client, req) 324 } 325 if !ctx.Bool("psbt") && ctx.Bool("no_publish") { 326 return fmt.Errorf("the --no_publish flag can only be used in " + 327 "combination with the --psbt flag") 328 } 329 330 stream, err := client.OpenChannel(ctxc, req) 331 if err != nil { 332 return err 333 } 334 335 for { 336 resp, err := stream.Recv() 337 if err == io.EOF { 338 return nil 339 } else if err != nil { 340 return err 341 } 342 343 switch update := resp.Update.(type) { 344 case *lnrpc.OpenStatusUpdate_ChanPending: 345 err := printChanPending(update) 346 if err != nil { 347 return err 348 } 349 350 if !ctx.Bool("block") { 351 return nil 352 } 353 354 case *lnrpc.OpenStatusUpdate_ChanOpen: 355 return printChanOpen(update) 356 } 357 } 358 } 359 360 // openChannelPsbt starts an interactive channel open protocol that uses a 361 // partially signed bitcoin transaction (PSBT) to fund the channel output. The 362 // protocol involves several steps between the RPC server and the CLI client: 363 // 364 // RPC server CLI client 365 // 366 // | | 367 // | |<------open channel (stream)-----| 368 // | |-------ready for funding----->| | 369 // | |<------PSBT verify------------| | 370 // | |-------ready for signing----->| | 371 // | |<------PSBT finalize----------| | 372 // | |-------channel pending------->| | 373 // | |-------channel open------------->| 374 // | | 375 // 376 //nolint:govet 377 func openChannelPsbt(rpcCtx context.Context, ctx *cli.Context, 378 client lnrpc.LightningClient, 379 req *lnrpc.OpenChannelRequest) error { 380 381 return fmt.Errorf("psbt channel opening is not supported in dcrlnd yet") 382 383 var ( 384 pendingChanID [32]byte 385 shimPending = true 386 basePsbtBytes []byte 387 quit = make(chan struct{}) 388 srvMsg = make(chan *lnrpc.OpenStatusUpdate, 1) 389 srvErr = make(chan error, 1) 390 ctxc, cancel = context.WithCancel(rpcCtx) 391 ) 392 defer cancel() 393 394 // Make sure the user didn't supply any command line flags that are 395 // incompatible with PSBT funding. 396 err := checkPsbtFlags(req) 397 if err != nil { 398 return err 399 } 400 401 // If the user supplied a base PSBT, only make sure it's valid base64. 402 // The RPC server will make sure it's also a valid PSBT. 403 basePsbt := ctx.String("base_psbt") 404 if basePsbt != "" { 405 basePsbtBytes, err = base64.StdEncoding.DecodeString(basePsbt) 406 if err != nil { 407 return fmt.Errorf("error parsing base PSBT: %v", err) 408 } 409 } 410 411 // Generate a new, random pending channel ID that we'll use as the main 412 // identifier when sending update messages to the RPC server. 413 if _, err := rand.Read(pendingChanID[:]); err != nil { 414 return fmt.Errorf("unable to generate random chan ID: %v", err) 415 } 416 fmt.Printf("Starting PSBT funding flow with pending channel ID %x.\n", 417 pendingChanID) 418 419 // maybeCancelShim is a helper function that cancels the funding shim 420 // with the RPC server in case we end up aborting early. 421 maybeCancelShim := func() { 422 // If the user canceled while there was still a shim registered 423 // with the wallet, release the resources now. 424 if shimPending { 425 fmt.Printf("Canceling PSBT funding flow for pending "+ 426 "channel ID %x.\n", pendingChanID) 427 cancelMsg := &lnrpc.FundingTransitionMsg{ 428 Trigger: &lnrpc.FundingTransitionMsg_ShimCancel{ 429 ShimCancel: &lnrpc.FundingShimCancel{ 430 PendingChanId: pendingChanID[:], 431 }, 432 }, 433 } 434 err := sendFundingState(ctxc, ctx, cancelMsg) 435 if err != nil { 436 fmt.Printf("Error canceling shim: %v\n", err) 437 } 438 shimPending = false 439 } 440 441 // Abort the stream connection to the server. 442 cancel() 443 } 444 defer maybeCancelShim() 445 446 // Create the PSBT funding shim that will tell the funding manager we 447 // want to use a PSBT. 448 req.FundingShim = &lnrpc.FundingShim{ 449 Shim: &lnrpc.FundingShim_PsbtShim{ 450 PsbtShim: &lnrpc.PsbtShim{ 451 PendingChanId: pendingChanID[:], 452 BasePsbt: basePsbtBytes, 453 NoPublish: ctx.Bool("no_publish"), 454 }, 455 }, 456 } 457 458 // Start the interactive process by opening the stream connection to the 459 // daemon. If the user cancels by pressing <Ctrl+C> we need to cancel 460 // the shim. To not just kill the process on interrupt, we need to 461 // explicitly capture the signal. 462 stream, err := client.OpenChannel(ctxc, req) 463 if err != nil { 464 return fmt.Errorf("opening stream to server failed: %v", err) 465 } 466 467 // We also need to spawn a goroutine that reads from the server. This 468 // will copy the messages to the channel as long as they come in or add 469 // exactly one error to the error stream and then bail out. 470 go func() { 471 for { 472 // Recv blocks until a message or error arrives. 473 resp, err := stream.Recv() 474 if err == io.EOF { 475 srvErr <- fmt.Errorf("lnd shutting down: %v", 476 err) 477 return 478 } else if err != nil { 479 srvErr <- fmt.Errorf("got error from server: "+ 480 "%v", err) 481 return 482 } 483 484 // Don't block on sending in case of shutting down. 485 select { 486 case srvMsg <- resp: 487 case <-quit: 488 return 489 } 490 } 491 }() 492 493 // Spawn another goroutine that only handles abort from user or errors 494 // from the server. Both will trigger an attempt to cancel the shim with 495 // the server. 496 go func() { 497 select { 498 case <-rpcCtx.Done(): 499 fmt.Printf("\nInterrupt signal received.\n") 500 close(quit) 501 502 case err := <-srvErr: 503 fmt.Printf("\nError received: %v\n", err) 504 505 // If the remote peer canceled on us, the reservation 506 // has already been deleted. We don't need to try to 507 // remove it again, this would just produce another 508 // error. 509 cancelErr := chanfunding.ErrRemoteCanceled.Error() 510 if err != nil && strings.Contains(err.Error(), cancelErr) { 511 shimPending = false 512 } 513 close(quit) 514 515 case <-quit: 516 } 517 }() 518 519 // Our main event loop where we wait for triggers 520 for { 521 var srvResponse *lnrpc.OpenStatusUpdate 522 select { 523 case srvResponse = <-srvMsg: 524 case <-quit: 525 return nil 526 } 527 528 switch update := srvResponse.Update.(type) { 529 case *lnrpc.OpenStatusUpdate_PsbtFund: 530 // First tell the user how to create the PSBT with the 531 // address and amount we now know. 532 amt := dcrutil.Amount(update.PsbtFund.FundingAmount) 533 addr := update.PsbtFund.FundingAddress 534 fmt.Printf( 535 userMsgFund, req.NodePubkey, amt, amt, addr, 536 addr, amt.ToCoin(), 537 base64.StdEncoding.EncodeToString( 538 update.PsbtFund.Psbt, 539 ), 540 ) 541 542 // Read the user's response and send it to the server to 543 // verify everything's correct before anything is 544 // signed. 545 psbtBase64, err := readTerminalOrFile(quit) 546 if err == io.EOF { 547 return nil 548 } 549 if err != nil { 550 return fmt.Errorf("reading from terminal or "+ 551 "file failed: %v", err) 552 } 553 fundedPsbt, err := base64.StdEncoding.DecodeString( 554 strings.TrimSpace(psbtBase64), 555 ) 556 if err != nil { 557 return fmt.Errorf("base64 decode failed: %v", 558 err) 559 } 560 verifyMsg := &lnrpc.FundingTransitionMsg{ 561 Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{ 562 PsbtVerify: &lnrpc.FundingPsbtVerify{ 563 FundedPsbt: fundedPsbt, 564 PendingChanId: pendingChanID[:], 565 }, 566 }, 567 } 568 err = sendFundingState(ctxc, ctx, verifyMsg) 569 if err != nil { 570 return fmt.Errorf("verifying PSBT by lnd "+ 571 "failed: %v", err) 572 } 573 574 // Now that we know the PSBT looks good, we can let it 575 // be signed by the user. 576 fmt.Print(userMsgSign) 577 578 // Read the signed PSBT and send it to lnd. 579 finalTxStr, err := readTerminalOrFile(quit) 580 if err == io.EOF { 581 return nil 582 } 583 if err != nil { 584 return fmt.Errorf("reading from terminal or "+ 585 "file failed: %v", err) 586 } 587 finalizeMsg, err := finalizeMsgFromString( 588 finalTxStr, pendingChanID[:], 589 ) 590 if err != nil { 591 return err 592 } 593 transitionMsg := &lnrpc.FundingTransitionMsg{ 594 Trigger: finalizeMsg, 595 } 596 err = sendFundingState(ctxc, ctx, transitionMsg) 597 if err != nil { 598 return fmt.Errorf("finalizing PSBT funding "+ 599 "flow failed: %v", err) 600 } 601 602 case *lnrpc.OpenStatusUpdate_ChanPending: 603 // As soon as the channel is pending, there is no more 604 // shim that needs to be canceled. If the user 605 // interrupts now, we don't need to clean up anything. 606 shimPending = false 607 608 err := printChanPending(update) 609 if err != nil { 610 return err 611 } 612 613 if !ctx.Bool("block") { 614 return nil 615 } 616 617 case *lnrpc.OpenStatusUpdate_ChanOpen: 618 return printChanOpen(update) 619 } 620 } 621 } 622 623 var batchOpenChannelCommand = cli.Command{ 624 Name: "batchopenchannel", 625 Category: "Channels", 626 Usage: "Open multiple channels to existing peers in a single " + 627 "transaction.", 628 Description: ` 629 Attempt to open one or more new channels to an existing peer with the 630 given node-keys. 631 632 Example: 633 lncli batchopenchannel --sat_per_vbyte=5 '[{ 634 "node_pubkey": "02abcdef...", 635 "local_funding_amount": 500000, 636 "private": true, 637 "close_address": "bc1qxxx..." 638 }, { 639 "node_pubkey": "03fedcba...", 640 "local_funding_amount": 200000, 641 "remote_csv_delay": 288 642 }]' 643 644 All nodes listed must already be connected peers, otherwise funding will 645 fail. 646 647 The channel will be initialized with local-amt satoshis local and 648 push-amt satoshis for the remote node. Note that specifying push-amt 649 means you give that amount to the remote node as part of the channel 650 opening. Once the channel is open, a channelPoint (txid:vout) of the 651 funding output is returned. 652 653 If the remote peer supports the option upfront shutdown feature bit 654 (query listpeers to see their supported feature bits), an address to 655 enforce payout of funds on cooperative close can optionally be provided. 656 Note that if you set this value, you will not be able to cooperatively 657 close out to another address. 658 659 One can manually set the fee to be used for the funding transaction via 660 either the --conf_target or --sat_per_vbyte arguments. This is optional. 661 `, 662 ArgsUsage: "channels-json", 663 Flags: []cli.Flag{ 664 cli.Int64Flag{ 665 Name: "conf_target", 666 Usage: "(optional) the number of blocks that the " + 667 "transaction *should* confirm in, will be " + 668 "used for fee estimation", 669 }, 670 cli.Int64Flag{ 671 Name: "sat_per_vbyte", 672 Usage: "(optional) a manual fee expressed in " + 673 "sat/vByte that should be used when crafting " + 674 "the transaction", 675 }, 676 cli.Uint64Flag{ 677 Name: "min_confs", 678 Usage: "(optional) the minimum number of " + 679 "confirmations each one of your outputs used " + 680 "for the funding transaction must satisfy", 681 Value: defaultUtxoMinConf, 682 }, 683 cli.StringFlag{ 684 Name: "label", 685 Usage: "(optional) a label to attach to the batch " + 686 "transaction when storing it to the local " + 687 "wallet after publishing it", 688 }, 689 }, 690 Action: actionDecorator(batchOpenChannel), 691 } 692 693 type batchChannelJSON struct { 694 NodePubkey string `json:"node_pubkey,omitempty"` 695 LocalFundingAmount int64 `json:"local_funding_amount,omitempty"` 696 PushAtoms int64 `json:"push_sat,omitempty"` 697 Private bool `json:"private,omitempty"` 698 MinHtlcMAtoms int64 `json:"min_htlc_msat,omitempty"` 699 RemoteCsvDelay uint32 `json:"remote_csv_delay,omitempty"` 700 CloseAddress string `json:"close_address,omitempty"` 701 PendingChanID string `json:"pending_chan_id,omitempty"` 702 } 703 704 func batchOpenChannel(ctx *cli.Context) error { 705 ctxc := getContext() 706 client, cleanUp := getClient(ctx) 707 defer cleanUp() 708 709 args := ctx.Args() 710 711 // Show command help if no arguments provided 712 if ctx.NArg() == 0 { 713 _ = cli.ShowCommandHelp(ctx, "batchopenchannel") 714 return nil 715 } 716 717 minConfs := int32(ctx.Uint64("min_confs")) 718 req := &lnrpc.BatchOpenChannelRequest{ 719 TargetConf: int32(ctx.Int64("conf_target")), 720 AtomsPerByte: int64(ctx.Uint64("sat_per_vbyte")), 721 MinConfs: minConfs, 722 SpendUnconfirmed: minConfs == 0, 723 Label: ctx.String("label"), 724 } 725 726 // Let's try and parse the JSON part of the CLI now. Fortunately we can 727 // parse it directly into the RPC struct if we use the correct 728 // marshaler that keeps the original snake case. 729 var jsonChannels []*batchChannelJSON 730 if err := json.Unmarshal([]byte(args.First()), &jsonChannels); err != nil { 731 return fmt.Errorf("error parsing channels JSON: %v", err) 732 } 733 734 req.Channels = make([]*lnrpc.BatchOpenChannel, len(jsonChannels)) 735 for idx, jsonChannel := range jsonChannels { 736 pubKeyBytes, err := hex.DecodeString(jsonChannel.NodePubkey) 737 if err != nil { 738 return fmt.Errorf("error parsing node pubkey hex: %v", 739 err) 740 } 741 pendingChanBytes, err := hex.DecodeString( 742 jsonChannel.PendingChanID, 743 ) 744 if err != nil { 745 return fmt.Errorf("error parsing pending chan ID: %v", 746 err) 747 } 748 749 req.Channels[idx] = &lnrpc.BatchOpenChannel{ 750 NodePubkey: pubKeyBytes, 751 LocalFundingAmount: jsonChannel.LocalFundingAmount, 752 PushAtoms: jsonChannel.PushAtoms, 753 Private: jsonChannel.Private, 754 MinHtlcMAtoms: jsonChannel.MinHtlcMAtoms, 755 RemoteCsvDelay: jsonChannel.RemoteCsvDelay, 756 CloseAddress: jsonChannel.CloseAddress, 757 PendingChanId: pendingChanBytes, 758 } 759 } 760 761 resp, err := client.BatchOpenChannel(ctxc, req) 762 if err != nil { 763 return err 764 } 765 766 for _, pending := range resp.PendingChannels { 767 txid, err := chainhash.NewHash(pending.Txid) 768 if err != nil { 769 return err 770 } 771 772 printJSON(struct { 773 FundingTxid string `json:"funding_txid"` 774 FundingOutputIndex uint32 `json:"funding_output_index"` 775 }{ 776 FundingTxid: txid.String(), 777 FundingOutputIndex: pending.OutputIndex, 778 }) 779 } 780 781 return nil 782 } 783 784 // printChanOpen prints the channel point of the channel open message. 785 func printChanOpen(update *lnrpc.OpenStatusUpdate_ChanOpen) error { 786 channelPoint := update.ChanOpen.ChannelPoint 787 788 // A channel point's funding txid can be get/set as a 789 // byte slice or a string. In the case it is a string, 790 // decode it. 791 var txidHash []byte 792 switch channelPoint.GetFundingTxid().(type) { 793 case *lnrpc.ChannelPoint_FundingTxidBytes: 794 txidHash = channelPoint.GetFundingTxidBytes() 795 case *lnrpc.ChannelPoint_FundingTxidStr: 796 s := channelPoint.GetFundingTxidStr() 797 h, err := chainhash.NewHashFromStr(s) 798 if err != nil { 799 return err 800 } 801 802 txidHash = h[:] 803 } 804 805 txid, err := chainhash.NewHash(txidHash) 806 if err != nil { 807 return err 808 } 809 810 index := channelPoint.OutputIndex 811 printJSON(struct { 812 ChannelPoint string `json:"channel_point"` 813 }{ 814 ChannelPoint: fmt.Sprintf("%v:%v", txid, index), 815 }) 816 return nil 817 } 818 819 // printChanPending prints the funding transaction ID of the channel pending 820 // message. 821 func printChanPending(update *lnrpc.OpenStatusUpdate_ChanPending) error { 822 txid, err := chainhash.NewHash(update.ChanPending.Txid) 823 if err != nil { 824 return err 825 } 826 827 printJSON(struct { 828 FundingTxid string `json:"funding_txid"` 829 }{ 830 FundingTxid: txid.String(), 831 }) 832 return nil 833 } 834 835 // readTerminalOrFile reads a single line from the terminal. If the line read is 836 // short enough to be a file and a file with that exact name exists, the content 837 // of that file is read and returned as a string. If the content is longer or no 838 // file exists, the string read from the terminal is returned directly. This 839 // function can be used to circumvent the N_TTY_BUF_SIZE kernel parameter that 840 // prevents pasting more than 4096 characters (on most systems) into a terminal. 841 func readTerminalOrFile(quit chan struct{}) (string, error) { 842 maybeFile, err := readLine(quit) 843 if err != nil { 844 return "", err 845 } 846 847 // Absolute file paths normally can't be longer than 255 characters so 848 // we don't even check if it's a file in that case. 849 if len(maybeFile) > 255 { 850 return maybeFile, nil 851 } 852 853 // It might be a file since the length is small enough. Calling os.Stat 854 // should be safe with any arbitrary input as it will only query info 855 // about the file, not open or execute it directly. 856 stat, err := os.Stat(maybeFile) 857 858 // The file doesn't exist, we must assume this wasn't a file path after 859 // all. 860 if err != nil && os.IsNotExist(err) { 861 return maybeFile, nil 862 } 863 864 // Some other error, perhaps access denied or something similar, let's 865 // surface that to the user. 866 if err != nil { 867 return "", err 868 } 869 870 // Make sure we don't read a huge file by accident which might lead to 871 // undesired side effects. Even very large PSBTs should still only be a 872 // few hundred kilobytes so it makes sense to put a cap here. 873 if stat.Size() > psbtMaxFileSize { 874 return "", fmt.Errorf("error reading file %s: size of %d "+ 875 "bytes exceeds max PSBT file size of %d", maybeFile, 876 stat.Size(), psbtMaxFileSize) 877 } 878 879 // If it's a path to an existing file and it's small enough, let's try 880 // to read its content now. 881 content, err := ioutil.ReadFile(maybeFile) 882 if err != nil { 883 return "", err 884 } 885 886 return string(content), nil 887 } 888 889 // readLine reads a line from standard in but does not block in case of a 890 // system interrupt like syscall.SIGINT (Ctrl+C). 891 func readLine(quit chan struct{}) (string, error) { // nolint: unused 892 msg := make(chan string, 1) 893 894 // In a normal console, reading from stdin won't signal EOF when the 895 // user presses Ctrl+C. That's why we need to put this in a separate 896 // goroutine so it doesn't block. 897 go func() { 898 for { 899 var str string 900 _, _ = fmt.Scan(&str) 901 msg <- str 902 return 903 } 904 }() 905 for { 906 select { 907 case <-quit: 908 return "", io.EOF 909 910 case str := <-msg: 911 return str, nil 912 } 913 } 914 } 915 916 // checkPsbtFlags make sure a request to open a channel doesn't set any 917 // parameters that are incompatible with the PSBT funding flow. 918 func checkPsbtFlags(req *lnrpc.OpenChannelRequest) error { // nolint: unused 919 if req.MinConfs != defaultUtxoMinConf || req.SpendUnconfirmed { 920 return fmt.Errorf("specifying minimum confirmations for PSBT " + 921 "funding is not supported") 922 } 923 if req.TargetConf != 0 || req.AtomsPerByte != 0 { 924 return fmt.Errorf("setting fee estimation parameters not " + 925 "supported for PSBT funding") 926 } 927 return nil 928 } 929 930 // sendFundingState sends a single funding state step message by using a new 931 // client connection. This is necessary if the whole funding flow takes longer 932 // than the default macaroon timeout, then we cannot use a single client 933 // connection. 934 func sendFundingState(cancelCtx context.Context, cliCtx *cli.Context, 935 msg *lnrpc.FundingTransitionMsg) error { 936 937 client, cleanUp := getClient(cliCtx) 938 defer cleanUp() 939 940 _, err := client.FundingStateStep(cancelCtx, msg) 941 return err 942 } 943 944 // finalizeMsgFromString creates the final message for the PsbtFinalize step 945 // from either a hex encoded raw wire transaction or a base64 encoded PSBT 946 // packet. 947 // 948 // nolint: unused 949 func finalizeMsgFromString(tx string, 950 pendingChanID []byte) (*lnrpc.FundingTransitionMsg_PsbtFinalize, error) { 951 952 rawTx, err := hex.DecodeString(strings.TrimSpace(tx)) 953 if err == nil { 954 // Hex decoding succeeded so we assume we have a raw wire format 955 // transaction. Let's submit that instead of a PSBT packet. 956 tx := &wire.MsgTx{} 957 err := tx.Deserialize(bytes.NewReader(rawTx)) 958 if err != nil { 959 return nil, fmt.Errorf("deserializing as raw wire "+ 960 "transaction failed: %v", err) 961 } 962 return &lnrpc.FundingTransitionMsg_PsbtFinalize{ 963 PsbtFinalize: &lnrpc.FundingPsbtFinalize{ 964 FinalRawTx: rawTx, 965 PendingChanId: pendingChanID, 966 }, 967 }, nil 968 } 969 970 // If the string isn't a hex encoded transaction, we assume it must be 971 // a base64 encoded PSBT packet. 972 psbtBytes, err := base64.StdEncoding.DecodeString(strings.TrimSpace(tx)) 973 if err != nil { 974 return nil, fmt.Errorf("base64 decode failed: %v", err) 975 } 976 return &lnrpc.FundingTransitionMsg_PsbtFinalize{ 977 PsbtFinalize: &lnrpc.FundingPsbtFinalize{ 978 SignedPsbt: psbtBytes, 979 PendingChanId: pendingChanID, 980 }, 981 }, nil 982 }