Merge pull request #7122 from antonilol/read-binary-psbt

lncli: Allow lncli to read binary PSBTs from a file
This commit is contained in:
Oliver Gugger 2022-11-17 13:13:58 +01:00 committed by GitHub
commit 00006adeda
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 24 deletions

View file

@ -44,14 +44,14 @@ Paste the funded PSBT here to continue the funding flow.
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): `
Base64 encoded PSBT (or path to file): `
userMsgSign = `
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
again here either in base64 PSBT or hex encoded raw wire TX format.
Signed base64 encoded PSBT or hex encoded raw wire TX (or path to text file): `
Signed base64 encoded PSBT or hex encoded raw wire TX (or path to 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
@ -595,7 +595,7 @@ func openChannelPsbt(rpcCtx context.Context, ctx *cli.Context,
// Read the user's response and send it to the server to
// verify everything's correct before anything is
// signed.
psbtBase64, err := readTerminalOrFile(quit)
inputPsbt, err := readTerminalOrFile(quit)
if err == io.EOF {
return nil
}
@ -603,11 +603,9 @@ func openChannelPsbt(rpcCtx context.Context, ctx *cli.Context,
return fmt.Errorf("reading from terminal or "+
"file failed: %v", err)
}
fundedPsbt, err := base64.StdEncoding.DecodeString(
strings.TrimSpace(psbtBase64),
)
fundedPsbt, err := decodePsbt(inputPsbt)
if err != nil {
return fmt.Errorf("base64 decode failed: %v",
return fmt.Errorf("psbt decode failed: %v",
err)
}
verifyMsg := &lnrpc.FundingTransitionMsg{
@ -995,17 +993,29 @@ func sendFundingState(cancelCtx context.Context, cliCtx *cli.Context,
}
// finalizeMsgFromString creates the final message for the PsbtFinalize step
// from either a hex encoded raw wire transaction or a base64 encoded PSBT
// packet.
// from either a hex encoded raw wire transaction or a base64/binary encoded
// PSBT packet.
func finalizeMsgFromString(tx string,
pendingChanID []byte) (*lnrpc.FundingTransitionMsg_PsbtFinalize, error) {
rawTx, err := hex.DecodeString(strings.TrimSpace(tx))
psbtBytes, err := decodePsbt(tx)
if err == nil {
// Hex decoding succeeded so we assume we have a raw wire format
// transaction. Let's submit that instead of a PSBT packet.
tx := &wire.MsgTx{}
err := tx.Deserialize(bytes.NewReader(rawTx))
return &lnrpc.FundingTransitionMsg_PsbtFinalize{
PsbtFinalize: &lnrpc.FundingPsbtFinalize{
SignedPsbt: psbtBytes,
PendingChanId: pendingChanID,
},
}, nil
}
// PSBT decode failed, try to parse it as a hex encoded Bitcoin
// transaction
rawTx, err := hex.DecodeString(strings.TrimSpace(tx))
if err != nil {
return nil, fmt.Errorf("hex decode failed: %w", err)
}
msgtx := &wire.MsgTx{}
err = msgtx.Deserialize(bytes.NewReader(rawTx))
if err != nil {
return nil, fmt.Errorf("deserializing as raw wire "+
"transaction failed: %v", err)
@ -1016,18 +1026,31 @@ func finalizeMsgFromString(tx string,
PendingChanId: pendingChanID,
},
}, nil
}
// decodePsbt tries to decode the input as a binary or base64 PSBT. If this
// succeeded, the PSBT bytes are returned, an error otherwise.
func decodePsbt(psbt string) ([]byte, error) {
switch {
case strings.HasPrefix(psbt, "psbt\xff"):
// A binary PSBT (read from a file) always starts with the PSBT
// magic "psbt\xff" according to BIP 174
return []byte(psbt), nil
case strings.HasPrefix(strings.TrimSpace(psbt), "cHNidP"):
// A base64 PSBT always starts with "cHNidP". This is the
// longest base64 representation of the PSBT magic that is not
// dependent on the byte after it.
psbtBytes, err := base64.StdEncoding.DecodeString(
strings.TrimSpace(psbt),
)
if err != nil {
return nil, fmt.Errorf("base64 decode failed: %w", err)
}
// If the string isn't a hex encoded transaction, we assume it must be
// a base64 encoded PSBT packet.
psbtBytes, err := base64.StdEncoding.DecodeString(strings.TrimSpace(tx))
if err != nil {
return nil, fmt.Errorf("base64 decode failed: %v", err)
return psbtBytes, nil
default:
return nil, fmt.Errorf("not a PSBT")
}
return &lnrpc.FundingTransitionMsg_PsbtFinalize{
PsbtFinalize: &lnrpc.FundingPsbtFinalize{
SignedPsbt: psbtBytes,
PendingChanId: pendingChanID,
},
}, nil
}

View file

@ -181,6 +181,9 @@ certain large transactions](https://github.com/lightningnetwork/lnd/pull/7100).
the invoice payment request together with the prefix, which throws checksum
error when pasting it to the CLI.
* [Allow lncli to read binary PSBTs](https://github.com/lightningnetwork/lnd/pull/7122)
from a file during PSBT channel funding flow to comply with [BIP 174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#specification)
## Code Health
* [test: use `T.TempDir` to create temporary test
@ -251,6 +254,7 @@ to refactor the itest for code health and maintenance.
* Alejandro Pedraza
* andreihod
* Antoni Spaanderman
* Carla Kirk-Cohen
* Conner Babinchak
* cutiful