lncli: allow PSBT to be read from file
Fixes #5080. The N_TTY_BUF_SIZE kernel parameter dictates how many characters can be pasted into a terminal window. This cannot be circumvented by reading the input in a different manner. To avoid the problem fully, we instead allso allow the user to type in a path to a text file that is read if it exists. That way the PSBT can be as long as needed.
This commit is contained in:
parent
4bab68a808
commit
b3dfe5d1af
@ -8,6 +8,8 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -39,14 +41,23 @@ DO NOT PUBLISH the finished transaction by yourself or with another tool.
|
|||||||
lnd MUST publish it in the proper funding flow order OR THE FUNDS CAN BE LOST!
|
lnd MUST publish it in the proper funding flow order OR THE FUNDS CAN BE LOST!
|
||||||
|
|
||||||
Paste the funded PSBT here to continue the funding flow.
|
Paste the funded PSBT here to continue the funding flow.
|
||||||
Base64 encoded PSBT: `
|
If your PSBT is very long (specifically, more than 4096 characters), please save
|
||||||
|
it to a file and paste the full file path here instead as some terminals will
|
||||||
|
truncate the pasted text if it's too long.
|
||||||
|
Base64 encoded PSBT (or path to text file): `
|
||||||
|
|
||||||
userMsgSign = `
|
userMsgSign = `
|
||||||
PSBT verified by lnd, please continue the funding flow by signing the PSBT by
|
PSBT verified by lnd, please continue the funding flow by signing the PSBT by
|
||||||
all required parties/devices. Once the transaction is fully signed, paste it
|
all required parties/devices. Once the transaction is fully signed, paste it
|
||||||
again here either in base64 PSBT or hex encoded raw wire TX format.
|
again here either in base64 PSBT or hex encoded raw wire TX format.
|
||||||
|
|
||||||
Signed base64 encoded PSBT or hex encoded raw wire TX: `
|
Signed base64 encoded PSBT or hex encoded raw wire TX (or path to text file): `
|
||||||
|
|
||||||
|
// psbtMaxFileSize is the maximum file size we allow a PSBT file to be
|
||||||
|
// in case we want to read a PSBT from a file. This is mainly to protect
|
||||||
|
// the user from choosing a large file by accident and running into out
|
||||||
|
// of memory issues or other weird errors.
|
||||||
|
psbtMaxFileSize = 1024 * 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(roasbeef): change default number of confirmations
|
// TODO(roasbeef): change default number of confirmations
|
||||||
@ -506,13 +517,13 @@ func openChannelPsbt(ctx *cli.Context, client lnrpc.LightningClient,
|
|||||||
// Read the user's response and send it to the server to
|
// Read the user's response and send it to the server to
|
||||||
// verify everything's correct before anything is
|
// verify everything's correct before anything is
|
||||||
// signed.
|
// signed.
|
||||||
psbtBase64, err := readLine(quit)
|
psbtBase64, err := readTerminalOrFile(quit)
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("reading from console "+
|
return fmt.Errorf("reading from terminal or "+
|
||||||
"failed: %v", err)
|
"file failed: %v", err)
|
||||||
}
|
}
|
||||||
fundedPsbt, err := base64.StdEncoding.DecodeString(
|
fundedPsbt, err := base64.StdEncoding.DecodeString(
|
||||||
strings.TrimSpace(psbtBase64),
|
strings.TrimSpace(psbtBase64),
|
||||||
@ -540,13 +551,13 @@ func openChannelPsbt(ctx *cli.Context, client lnrpc.LightningClient,
|
|||||||
fmt.Print(userMsgSign)
|
fmt.Print(userMsgSign)
|
||||||
|
|
||||||
// Read the signed PSBT and send it to lnd.
|
// Read the signed PSBT and send it to lnd.
|
||||||
finalTxStr, err := readLine(quit)
|
finalTxStr, err := readTerminalOrFile(quit)
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("reading from console "+
|
return fmt.Errorf("reading from terminal or "+
|
||||||
"failed: %v", err)
|
"file failed: %v", err)
|
||||||
}
|
}
|
||||||
finalizeMsg, err := finalizeMsgFromString(
|
finalizeMsg, err := finalizeMsgFromString(
|
||||||
finalTxStr, pendingChanID[:],
|
finalTxStr, pendingChanID[:],
|
||||||
@ -635,6 +646,60 @@ func printChanPending(update *lnrpc.OpenStatusUpdate_ChanPending) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readTerminalOrFile reads a single line from the terminal. If the line read is
|
||||||
|
// short enough to be a file and a file with that exact name exists, the content
|
||||||
|
// of that file is read and returned as a string. If the content is longer or no
|
||||||
|
// file exists, the string read from the terminal is returned directly. This
|
||||||
|
// function can be used to circumvent the N_TTY_BUF_SIZE kernel parameter that
|
||||||
|
// prevents pasting more than 4096 characters (on most systems) into a terminal.
|
||||||
|
func readTerminalOrFile(quit chan struct{}) (string, error) {
|
||||||
|
maybeFile, err := readLine(quit)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Absolute file paths normally can't be longer than 255 characters so
|
||||||
|
// we don't even check if it's a file in that case.
|
||||||
|
if len(maybeFile) > 255 {
|
||||||
|
return maybeFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// It might be a file since the length is small enough. Calling os.Stat
|
||||||
|
// should be safe with any arbitrary input as it will only query info
|
||||||
|
// about the file, not open or execute it directly.
|
||||||
|
stat, err := os.Stat(maybeFile)
|
||||||
|
|
||||||
|
// The file doesn't exist, we must assume this wasn't a file path after
|
||||||
|
// all.
|
||||||
|
if err != nil && os.IsNotExist(err) {
|
||||||
|
return maybeFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some other error, perhaps access denied or something similar, let's
|
||||||
|
// surface that to the user.
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we don't read a huge file by accident which might lead to
|
||||||
|
// undesired side effects. Even very large PSBTs should still only be a
|
||||||
|
// few hundred kilobytes so it makes sense to put a cap here.
|
||||||
|
if stat.Size() > psbtMaxFileSize {
|
||||||
|
return "", fmt.Errorf("error reading file %s: size of %d "+
|
||||||
|
"bytes exceeds max PSBT file size of %d", maybeFile,
|
||||||
|
stat.Size(), psbtMaxFileSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's a path to an existing file and it's small enough, let's try
|
||||||
|
// to read its content now.
|
||||||
|
content, err := ioutil.ReadFile(maybeFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(content), nil
|
||||||
|
}
|
||||||
|
|
||||||
// readLine reads a line from standard in but does not block in case of a
|
// readLine reads a line from standard in but does not block in case of a
|
||||||
// system interrupt like syscall.SIGINT (Ctrl+C).
|
// system interrupt like syscall.SIGINT (Ctrl+C).
|
||||||
func readLine(quit chan struct{}) (string, error) {
|
func readLine(quit chan struct{}) (string, error) {
|
||||||
|
Loading…
Reference in New Issue
Block a user