mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-22 22:25:24 +01:00
Merge pull request #4601 from guggero/psbt-final-tx-raw
PSBT funding: allow final TX to be specified as raw wire format transaction
This commit is contained in:
commit
904055cf4f
11 changed files with 996 additions and 815 deletions
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
|
@ -11,6 +12,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chanfunding"
|
||||
|
@ -43,9 +45,9 @@ Base64 encoded PSBT: `
|
|||
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.
|
||||
again here either in base64 PSBT or hex encoded raw wire TX format.
|
||||
|
||||
Base64 encoded signed PSBT: `
|
||||
Signed base64 encoded PSBT or hex encoded raw wire TX: `
|
||||
)
|
||||
|
||||
// TODO(roasbeef): change default number of confirmations
|
||||
|
@ -505,7 +507,7 @@ func openChannelPsbt(ctx *cli.Context, client lnrpc.LightningClient,
|
|||
return fmt.Errorf("reading from console "+
|
||||
"failed: %v", err)
|
||||
}
|
||||
psbt, err := base64.StdEncoding.DecodeString(
|
||||
fundedPsbt, err := base64.StdEncoding.DecodeString(
|
||||
strings.TrimSpace(psbtBase64),
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -515,7 +517,7 @@ func openChannelPsbt(ctx *cli.Context, client lnrpc.LightningClient,
|
|||
verifyMsg := &lnrpc.FundingTransitionMsg{
|
||||
Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{
|
||||
PsbtVerify: &lnrpc.FundingPsbtVerify{
|
||||
FundedPsbt: psbt,
|
||||
FundedPsbt: fundedPsbt,
|
||||
PendingChanId: pendingChanID[:],
|
||||
},
|
||||
},
|
||||
|
@ -531,7 +533,7 @@ func openChannelPsbt(ctx *cli.Context, client lnrpc.LightningClient,
|
|||
fmt.Print(userMsgSign)
|
||||
|
||||
// Read the signed PSBT and send it to lnd.
|
||||
psbtBase64, err = readLine(quit)
|
||||
finalTxStr, err := readLine(quit)
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
|
@ -539,22 +541,16 @@ func openChannelPsbt(ctx *cli.Context, client lnrpc.LightningClient,
|
|||
return fmt.Errorf("reading from console "+
|
||||
"failed: %v", err)
|
||||
}
|
||||
psbt, err = base64.StdEncoding.DecodeString(
|
||||
strings.TrimSpace(psbtBase64),
|
||||
finalizeMsg, err := finalizeMsgFromString(
|
||||
finalTxStr, pendingChanID[:],
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("base64 decode failed: %v",
|
||||
err)
|
||||
return err
|
||||
}
|
||||
finalizeMsg := &lnrpc.FundingTransitionMsg{
|
||||
Trigger: &lnrpc.FundingTransitionMsg_PsbtFinalize{
|
||||
PsbtFinalize: &lnrpc.FundingPsbtFinalize{
|
||||
SignedPsbt: psbt,
|
||||
PendingChanId: pendingChanID[:],
|
||||
},
|
||||
},
|
||||
transitionMsg := &lnrpc.FundingTransitionMsg{
|
||||
Trigger: finalizeMsg,
|
||||
}
|
||||
err = sendFundingState(ctxc, ctx, finalizeMsg)
|
||||
err = sendFundingState(ctxc, ctx, transitionMsg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("finalizing PSBT funding "+
|
||||
"flow failed: %v", err)
|
||||
|
@ -686,3 +682,41 @@ func sendFundingState(cancelCtx context.Context, cliCtx *cli.Context,
|
|||
_, err := client.FundingStateStep(cancelCtx, msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// finalizeMsgFromString creates the final message for the PsbtFinalize step
|
||||
// from either a hex encoded raw wire transaction or a base64 encoded PSBT
|
||||
// packet.
|
||||
func finalizeMsgFromString(tx string,
|
||||
pendingChanID []byte) (*lnrpc.FundingTransitionMsg_PsbtFinalize, error) {
|
||||
|
||||
rawTx, err := hex.DecodeString(strings.TrimSpace(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))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("deserializing as raw wire "+
|
||||
"transaction failed: %v", err)
|
||||
}
|
||||
return &lnrpc.FundingTransitionMsg_PsbtFinalize{
|
||||
PsbtFinalize: &lnrpc.FundingPsbtFinalize{
|
||||
FinalRawTx: rawTx,
|
||||
PendingChanId: pendingChanID,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 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 &lnrpc.FundingTransitionMsg_PsbtFinalize{
|
||||
PsbtFinalize: &lnrpc.FundingPsbtFinalize{
|
||||
SignedPsbt: psbtBytes,
|
||||
PendingChanId: pendingChanID,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
4
go.mod
4
go.mod
|
@ -8,8 +8,8 @@ require (
|
|||
github.com/btcsuite/btcd v0.20.1-beta.0.20200730232343-1db1b6f8217f
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
|
||||
github.com/btcsuite/btcutil v1.0.2
|
||||
github.com/btcsuite/btcutil/psbt v1.0.2
|
||||
github.com/btcsuite/btcwallet v0.11.1-0.20200814001439-1d31f4ea6fc5
|
||||
github.com/btcsuite/btcutil/psbt v1.0.3-0.20200826194809-5f93e33af2b0
|
||||
github.com/btcsuite/btcwallet v0.11.1-0.20200904022754-2c5947a45222
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.0.0
|
||||
github.com/btcsuite/btcwallet/walletdb v1.3.3
|
||||
|
|
8
go.sum
8
go.sum
|
@ -35,10 +35,10 @@ github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6
|
|||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts=
|
||||
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
|
||||
github.com/btcsuite/btcutil/psbt v1.0.2 h1:gCVY3KxdoEVU7Q6TjusPO+GANIwVgr9yTLqM+a6CZr8=
|
||||
github.com/btcsuite/btcutil/psbt v1.0.2/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ=
|
||||
github.com/btcsuite/btcwallet v0.11.1-0.20200814001439-1d31f4ea6fc5 h1:1We7EuizBnX/17Q6O2dkeToyehxzUHo62Wv1c0ncr7c=
|
||||
github.com/btcsuite/btcwallet v0.11.1-0.20200814001439-1d31f4ea6fc5/go.mod h1:YkEbJaCyN6yncq5gEp2xG0OKDwus2QxGCEXTNF27w5I=
|
||||
github.com/btcsuite/btcutil/psbt v1.0.3-0.20200826194809-5f93e33af2b0 h1:3Zumkyl6PWyHuVJ04me0xeD9CnPOhNgeGpapFbzy7O4=
|
||||
github.com/btcsuite/btcutil/psbt v1.0.3-0.20200826194809-5f93e33af2b0/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ=
|
||||
github.com/btcsuite/btcwallet v0.11.1-0.20200904022754-2c5947a45222 h1:rh1FQAhh+BeR29twIFDM0RLOFpDK62tsABtUkWctTXw=
|
||||
github.com/btcsuite/btcwallet v0.11.1-0.20200904022754-2c5947a45222/go.mod h1:owv9oZqM0HnUW+ByF7VqOgfs2eb0ooiePW/+Tl/i/Nk=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0 h1:KGHMW5sd7yDdDMkCZ/JpP0KltolFsQcB973brBnfj4c=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU=
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.0.0 h1:2VsfS0sBedcM5KmDzRMT3+b6xobqWveZGvjb+jFez5w=
|
||||
|
|
1517
lnrpc/rpc.pb.go
1517
lnrpc/rpc.pb.go
File diff suppressed because it is too large
Load diff
|
@ -1859,12 +1859,19 @@ message FundingPsbtFinalize {
|
|||
/*
|
||||
The funded PSBT that contains all witness data to send the exact channel
|
||||
capacity amount to the PK script returned in the open channel message in a
|
||||
previous step.
|
||||
previous step. Cannot be set at the same time as final_raw_tx.
|
||||
*/
|
||||
bytes signed_psbt = 1;
|
||||
|
||||
// The pending channel ID of the channel to get the PSBT for.
|
||||
bytes pending_chan_id = 2;
|
||||
|
||||
/*
|
||||
As an alternative to the signed PSBT with all witness data, the final raw
|
||||
wire format transaction can also be specified directly. Cannot be set at the
|
||||
same time as signed_psbt.
|
||||
*/
|
||||
bytes final_raw_tx = 3;
|
||||
}
|
||||
|
||||
message FundingTransitionMsg {
|
||||
|
|
|
@ -3537,12 +3537,17 @@
|
|||
"signed_psbt": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "The funded PSBT that contains all witness data to send the exact channel\ncapacity amount to the PK script returned in the open channel message in a\nprevious step."
|
||||
"description": "The funded PSBT that contains all witness data to send the exact channel\ncapacity amount to the PK script returned in the open channel message in a\nprevious step. Cannot be set at the same time as final_raw_tx."
|
||||
},
|
||||
"pending_chan_id": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "The pending channel ID of the channel to get the PSBT for."
|
||||
},
|
||||
"final_raw_tx": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "As an alternative to the signed PSBT with all witness data, the final raw\nwire format transaction can also be specified directly. Cannot be set at the\nsame time as signed_psbt."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/lightningnetwork/lnd"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lntest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testPsbtChanFunding makes sure a channel can be opened between carol and dave
|
||||
|
@ -119,14 +120,14 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
|
|||
// encoded in the PSBT. We'll let the miner do it and convert the final
|
||||
// TX into a PSBT, that's way easier than assembling a PSBT manually.
|
||||
allOuts := append(packet.UnsignedTx.TxOut, packet2.UnsignedTx.TxOut...)
|
||||
tx, err := net.Miner.CreateTransaction(allOuts, 5, true)
|
||||
finalTx, err := net.Miner.CreateTransaction(allOuts, 5, true)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create funding transaction: %v", err)
|
||||
}
|
||||
|
||||
// The helper function splits the final TX into the non-witness data
|
||||
// encoded in a PSBT and the witness data returned separately.
|
||||
unsignedPsbt, scripts, witnesses, err := createPsbtFromSignedTx(tx)
|
||||
unsignedPsbt, scripts, witnesses, err := createPsbtFromSignedTx(finalTx)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to convert funding transaction into PSBT: %v",
|
||||
err)
|
||||
|
@ -185,7 +186,7 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
|
|||
// complete and signed transaction that can be finalized. We'll trick
|
||||
// a bit by putting the script sig back directly, because we know we
|
||||
// will only get non-witness outputs from the miner wallet.
|
||||
for idx := range tx.TxIn {
|
||||
for idx := range finalTx.TxIn {
|
||||
if len(witnesses[idx]) > 0 {
|
||||
t.Fatalf("unexpected witness inputs in wallet TX")
|
||||
}
|
||||
|
@ -239,12 +240,16 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
|
|||
t.Fatalf("unexpected txes in mempool: %v", mempool)
|
||||
}
|
||||
|
||||
// Let's progress the second channel now.
|
||||
// Let's progress the second channel now. This time we'll use the raw
|
||||
// wire format transaction directly.
|
||||
buf.Reset()
|
||||
err = finalTx.Serialize(&buf)
|
||||
require.NoError(t.t, err)
|
||||
_, err = carol.FundingStateStep(ctxb, &lnrpc.FundingTransitionMsg{
|
||||
Trigger: &lnrpc.FundingTransitionMsg_PsbtFinalize{
|
||||
PsbtFinalize: &lnrpc.FundingPsbtFinalize{
|
||||
PendingChanId: pendingChanID2[:],
|
||||
SignedPsbt: buf.Bytes(),
|
||||
FinalRawTx: buf.Bytes(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -275,7 +280,7 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
|
|||
|
||||
// Great, now we can mine a block to get the transaction confirmed, then
|
||||
// wait for the new channel to be propagated through the network.
|
||||
txHash := tx.TxHash()
|
||||
txHash := finalTx.TxHash()
|
||||
block := mineBlocks(t, net, 6, 1)[0]
|
||||
assertTxInBlock(t, block, &txHash)
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
|
|
|
@ -125,6 +125,11 @@ type PsbtIntent struct {
|
|||
// Only witness data should be added after the verification process.
|
||||
PendingPsbt *psbt.Packet
|
||||
|
||||
// FinalTX is the final, signed and ready to be published wire format
|
||||
// transaction. This is only set after the PsbtFinalize step was
|
||||
// completed successfully.
|
||||
FinalTX *wire.MsgTx
|
||||
|
||||
// PsbtReady is an error channel the funding manager will listen for
|
||||
// a signal about the PSBT being ready to continue the funding flow. In
|
||||
// the normal, happy flow, this channel is only ever closed. If a
|
||||
|
@ -270,12 +275,29 @@ func (i *PsbtIntent) Finalize(packet *psbt.Packet) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("error finalizing PSBT: %v", err)
|
||||
}
|
||||
_, err = psbt.Extract(packet)
|
||||
rawTx, err := psbt.Extract(packet)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to extract funding TX: %v", err)
|
||||
}
|
||||
|
||||
// Do a basic check that this is still the same PSBT that we verified in
|
||||
return i.FinalizeRawTX(rawTx)
|
||||
}
|
||||
|
||||
// FinalizeRawTX makes sure the final raw transaction that is given to the
|
||||
// intent is fully valid and signed but still contains the same UTXOs and
|
||||
// outputs as the pending transaction we previously verified. If everything
|
||||
// checks out, the funding manager is informed that the channel can now be
|
||||
// opened and the funding transaction be broadcast.
|
||||
func (i *PsbtIntent) FinalizeRawTX(rawTx *wire.MsgTx) error {
|
||||
if rawTx == nil {
|
||||
return fmt.Errorf("raw transaction is nil")
|
||||
}
|
||||
if i.State != PsbtVerified {
|
||||
return fmt.Errorf("invalid state. got %v expected %v", i.State,
|
||||
PsbtVerified)
|
||||
}
|
||||
|
||||
// Do a basic check that this is still the same TX that we verified in
|
||||
// the previous step. This is to protect the user from unwanted
|
||||
// modifications. We only check the outputs and previous outpoints of
|
||||
// the inputs of the wire transaction because the fields in the PSBT
|
||||
|
@ -283,23 +305,29 @@ func (i *PsbtIntent) Finalize(packet *psbt.Packet) error {
|
|||
if i.PendingPsbt == nil {
|
||||
return fmt.Errorf("PSBT was not verified first")
|
||||
}
|
||||
err = verifyOutputsEqual(
|
||||
packet.UnsignedTx.TxOut, i.PendingPsbt.UnsignedTx.TxOut,
|
||||
)
|
||||
err := verifyOutputsEqual(rawTx.TxOut, i.PendingPsbt.UnsignedTx.TxOut)
|
||||
if err != nil {
|
||||
return fmt.Errorf("outputs differ from verified PSBT: %v", err)
|
||||
}
|
||||
err = verifyInputPrevOutpointsEqual(
|
||||
packet.UnsignedTx.TxIn, i.PendingPsbt.UnsignedTx.TxIn,
|
||||
rawTx.TxIn, i.PendingPsbt.UnsignedTx.TxIn,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inputs differ from verified PSBT: %v", err)
|
||||
}
|
||||
|
||||
// As far as we can tell, this PSBT is ok to be used as a funding
|
||||
// We also check that we have a signed TX. This is only necessary if the
|
||||
// FinalizeRawTX is called directly with a wire format TX instead of
|
||||
// extracting the TX from a PSBT.
|
||||
err = verifyInputsSigned(rawTx.TxIn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inputs not signed: %v", err)
|
||||
}
|
||||
|
||||
// As far as we can tell, this TX is ok to be used as a funding
|
||||
// transaction.
|
||||
i.PendingPsbt = packet
|
||||
i.State = PsbtFinalized
|
||||
i.FinalTX = rawTx
|
||||
|
||||
// Signal the funding manager that it can now finally continue with its
|
||||
// funding flow as the PSBT is now ready to be converted into a real
|
||||
|
@ -319,32 +347,22 @@ func (i *PsbtIntent) CompileFundingTx() (*wire.MsgTx, error) {
|
|||
i.State, PsbtFinalized)
|
||||
}
|
||||
|
||||
// Make sure the PSBT can be finalized and extracted.
|
||||
err := psbt.MaybeFinalizeAll(i.PendingPsbt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error finalizing PSBT: %v", err)
|
||||
}
|
||||
fundingTx, err := psbt.Extract(i.PendingPsbt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to extract funding TX: %v", err)
|
||||
}
|
||||
|
||||
// Identify our funding outpoint now that we know everything's ready.
|
||||
_, txOut, err := i.FundingOutput()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot get funding output: %v", err)
|
||||
}
|
||||
ok, idx := input.FindScriptOutputIndex(fundingTx, txOut.PkScript)
|
||||
ok, idx := input.FindScriptOutputIndex(i.FinalTX, txOut.PkScript)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("funding output not found in PSBT")
|
||||
}
|
||||
i.chanPoint = &wire.OutPoint{
|
||||
Hash: fundingTx.TxHash(),
|
||||
Hash: i.FinalTX.TxHash(),
|
||||
Index: idx,
|
||||
}
|
||||
i.State = PsbtFundingTxCompiled
|
||||
|
||||
return fundingTx, nil
|
||||
return i.FinalTX, nil
|
||||
}
|
||||
|
||||
// RemoteCanceled informs the listener of the PSBT ready channel that the
|
||||
|
@ -529,3 +547,18 @@ func verifyInputPrevOutpointsEqual(ins1, ins2 []*wire.TxIn) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyInputsSigned verifies that the given list of inputs is non-empty and
|
||||
// that all the inputs either contain a script signature or a witness stack.
|
||||
func verifyInputsSigned(ins []*wire.TxIn) error {
|
||||
if len(ins) == 0 {
|
||||
return fmt.Errorf("no inputs in transaction")
|
||||
}
|
||||
for idx, in := range ins {
|
||||
if len(in.SignatureScript) == 0 && len(in.Witness) == 0 {
|
||||
return fmt.Errorf("input %d has no signature data "+
|
||||
"attached", idx)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -496,6 +497,44 @@ func TestPsbtFinalize(t *testing.T) {
|
|||
return i.Finalize(p)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "raw tx - nil transaction",
|
||||
expectedErr: "raw transaction is nil",
|
||||
doFinalize: func(amt int64, p *psbt.Packet,
|
||||
i *PsbtIntent) error {
|
||||
|
||||
return i.FinalizeRawTX(nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "raw tx - no witness data in raw tx",
|
||||
expectedErr: "inputs not signed: input 0 has no " +
|
||||
"signature data attached",
|
||||
doFinalize: func(amt int64, p *psbt.Packet,
|
||||
i *PsbtIntent) error {
|
||||
|
||||
rawTx, err := psbt.Extract(p)
|
||||
require.NoError(t, err)
|
||||
rawTx.TxIn[0].Witness = nil
|
||||
|
||||
return i.FinalizeRawTX(rawTx)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path",
|
||||
expectedErr: "",
|
||||
doFinalize: func(amt int64, p *psbt.Packet,
|
||||
i *PsbtIntent) error {
|
||||
|
||||
err := i.Finalize(p)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, PsbtFinalized, i.State)
|
||||
require.NotNil(t, i.FinalTX)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Create a simple assembler and ask it to provision a channel to get
|
||||
|
|
|
@ -536,8 +536,8 @@ func (l *LightningWallet) PsbtFundingVerify(pid [32]byte,
|
|||
// PsbtFundingFinalize looks up a previously registered funding intent by its
|
||||
// pending channel ID and tries to advance the state machine by finalizing the
|
||||
// passed PSBT.
|
||||
func (l *LightningWallet) PsbtFundingFinalize(pid [32]byte,
|
||||
packet *psbt.Packet) error {
|
||||
func (l *LightningWallet) PsbtFundingFinalize(pid [32]byte, packet *psbt.Packet,
|
||||
rawTx *wire.MsgTx) error {
|
||||
|
||||
l.intentMtx.Lock()
|
||||
defer l.intentMtx.Unlock()
|
||||
|
@ -551,9 +551,23 @@ func (l *LightningWallet) PsbtFundingFinalize(pid [32]byte,
|
|||
if !ok {
|
||||
return fmt.Errorf("incompatible funding intent")
|
||||
}
|
||||
err := psbtIntent.Finalize(packet)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finalizing PSBT: %v", err)
|
||||
|
||||
// Either the PSBT or the raw TX must be set.
|
||||
switch {
|
||||
case packet != nil && rawTx == nil:
|
||||
err := psbtIntent.Finalize(packet)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finalizing PSBT: %v", err)
|
||||
}
|
||||
|
||||
case rawTx != nil && packet == nil:
|
||||
err := psbtIntent.FinalizeRawTX(rawTx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finalizing raw TX: %v", err)
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("either a PSBT or raw TX must be specified")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
43
rpcserver.go
43
rpcserver.go
|
@ -6738,19 +6738,50 @@ func (r *rpcServer) FundingStateStep(ctx context.Context,
|
|||
// the final PSBT to the previously verified one and if nothing
|
||||
// unexpected was changed, continue the channel opening process.
|
||||
case in.GetPsbtFinalize() != nil:
|
||||
msg := in.GetPsbtFinalize()
|
||||
rpcsLog.Debugf("Finalizing PSBT for pending_id=%x",
|
||||
in.GetPsbtFinalize().PendingChanId)
|
||||
msg.PendingChanId)
|
||||
|
||||
copy(pendingChanID[:], in.GetPsbtFinalize().PendingChanId)
|
||||
packet, err := psbt.NewFromRawBytes(
|
||||
bytes.NewReader(in.GetPsbtFinalize().SignedPsbt), false,
|
||||
|
||||
var (
|
||||
packet *psbt.Packet
|
||||
rawTx *wire.MsgTx
|
||||
err error
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing psbt: %v", err)
|
||||
|
||||
// Either the signed PSBT or the raw transaction need to be set
|
||||
// but not both at the same time.
|
||||
switch {
|
||||
case len(msg.SignedPsbt) > 0 && len(msg.FinalRawTx) > 0:
|
||||
return nil, fmt.Errorf("cannot set both signed PSBT " +
|
||||
"and final raw TX at the same time")
|
||||
|
||||
case len(msg.SignedPsbt) > 0:
|
||||
packet, err = psbt.NewFromRawBytes(
|
||||
bytes.NewReader(in.GetPsbtFinalize().SignedPsbt),
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing psbt: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
case len(msg.FinalRawTx) > 0:
|
||||
rawTx = &wire.MsgTx{}
|
||||
err = rawTx.Deserialize(bytes.NewReader(msg.FinalRawTx))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing final "+
|
||||
"raw TX: %v", err)
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("PSBT or raw transaction to " +
|
||||
"finalize missing")
|
||||
}
|
||||
|
||||
err = r.server.cc.wallet.PsbtFundingFinalize(
|
||||
pendingChanID, packet,
|
||||
pendingChanID, packet, rawTx,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
Loading…
Add table
Reference in a new issue