itest: use new PSBT functions for funding flow

Now that we have all functions that we need to complete the whole
PSBT channel funding flow, we change the itest to use Dave's wallet
to fund the channel from Carol to Dave through a PSBT.
This commit is contained in:
Oliver Gugger 2020-10-01 16:21:51 +02:00
parent f114fb3c8d
commit eb280fd248
No known key found for this signature in database
GPG Key ID: 8E4256593F177720

@ -8,9 +8,9 @@ import (
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/psbt"
"github.com/lightningnetwork/lnd" "github.com/lightningnetwork/lnd"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -22,8 +22,9 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
ctxb := context.Background() ctxb := context.Background()
const chanSize = lnd.MaxBtcFundingAmount const chanSize = lnd.MaxBtcFundingAmount
// First, we'll create two new nodes that we'll use to open channel // First, we'll create two new nodes that we'll use to open channels
// between for this test. // between for this test. Dave gets some coins that will be used to
// fund the PSBT, just to make sure that Carol has an empty wallet.
carol, err := net.NewNode("carol", nil) carol, err := net.NewNode("carol", nil)
require.NoError(t.t, err) require.NoError(t.t, err)
defer shutdownAndAssert(net, t, carol) defer shutdownAndAssert(net, t, carol)
@ -31,6 +32,10 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
dave, err := net.NewNode("dave", nil) dave, err := net.NewNode("dave", nil)
require.NoError(t.t, err) require.NoError(t.t, err)
defer shutdownAndAssert(net, t, dave) defer shutdownAndAssert(net, t, dave)
err = net.SendCoins(ctxb, btcutil.SatoshiPerBitcoin, dave)
if err != nil {
t.Fatalf("unable to send coins to dave: %v", err)
}
// Before we start the test, we'll ensure both sides are connected so // Before we start the test, we'll ensure both sides are connected so
// the funding flow can be properly executed. // the funding flow can be properly executed.
@ -58,7 +63,7 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
// publishing the whole batch TX too early. // publishing the whole batch TX too early.
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
defer cancel() defer cancel()
chanUpdates, psbtBytes, err := openChannelPsbt( chanUpdates, tempPsbt, err := openChannelPsbt(
ctxt, carol, dave, lntest.OpenChannelParams{ ctxt, carol, dave, lntest.OpenChannelParams{
Amt: chanSize, Amt: chanSize,
FundingShim: &lnrpc.FundingShim{ FundingShim: &lnrpc.FundingShim{
@ -72,11 +77,10 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
}, },
) )
require.NoError(t.t, err) require.NoError(t.t, err)
packet, err := psbt.NewFromRawBytes(bytes.NewReader(psbtBytes), false)
require.NoError(t.t, err)
// Let's add a second channel to the batch. This time between carol and // Let's add a second channel to the batch. This time between Carol and
// alice. We will the batch TX once this channel funding is complete. // Alice. We will publish the batch TX once this channel funding is
// complete.
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
defer cancel() defer cancel()
chanUpdates2, psbtBytes2, err := openChannelPsbt( chanUpdates2, psbtBytes2, err := openChannelPsbt(
@ -87,47 +91,28 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
PsbtShim: &lnrpc.PsbtShim{ PsbtShim: &lnrpc.PsbtShim{
PendingChanId: pendingChanID2[:], PendingChanId: pendingChanID2[:],
NoPublish: false, NoPublish: false,
BasePsbt: tempPsbt,
}, },
}, },
}, },
}, },
) )
require.NoError(t.t, err) require.NoError(t.t, err)
packet2, err := psbt.NewFromRawBytes(bytes.NewReader(psbtBytes2), false)
require.NoError(t.t, err)
// We'll now create a fully signed transaction that sends to the outputs // We'll now ask Dave's wallet to fund the PSBT for us. This will return
// encoded in the PSBT. We'll let the miner do it and convert the final // a packet with inputs and outputs set but without any witness data.
// TX into a PSBT, that's way easier than assembling a PSBT manually. // This is exactly what we need for the next step.
allOuts := append(packet.UnsignedTx.TxOut, packet2.UnsignedTx.TxOut...) ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
finalTx, err := net.Miner.CreateTransaction(allOuts, 5, true) defer cancel()
require.NoError(t.t, err) fundReq := &walletrpc.FundPsbtRequest{
Template: &walletrpc.FundPsbtRequest_Psbt{
// The helper function splits the final TX into the non-witness data Psbt: psbtBytes2,
// encoded in a PSBT and the witness data returned separately. },
unsignedPsbt, scripts, witnesses, err := createPsbtFromSignedTx(finalTx) Fees: &walletrpc.FundPsbtRequest_SatPerVbyte{
require.NoError(t.t, err) SatPerVbyte: 2,
},
// The PSBT will also be checked if there are large enough inputs
// present. We need to add some fake UTXO information to the PSBT to
// tell it what size of inputs we have.
for idx, txIn := range unsignedPsbt.UnsignedTx.TxIn {
utxPrevOut := txIn.PreviousOutPoint.Index
fakeUtxo := &wire.MsgTx{
Version: 2,
TxIn: []*wire.TxIn{{}},
TxOut: make([]*wire.TxOut, utxPrevOut+1),
}
for idx := range fakeUtxo.TxOut {
fakeUtxo.TxOut[idx] = &wire.TxOut{}
}
fakeUtxo.TxOut[utxPrevOut].Value = 10000000000
unsignedPsbt.Inputs[idx].NonWitnessUtxo = fakeUtxo
} }
fundResp, err := dave.WalletKitClient.FundPsbt(ctxt, fundReq)
// Serialize the PSBT with the faked UTXO information.
var buf bytes.Buffer
err = unsignedPsbt.Serialize(&buf)
require.NoError(t.t, err) require.NoError(t.t, err)
// We have a PSBT that has no witness data yet, which is exactly what we // We have a PSBT that has no witness data yet, which is exactly what we
@ -136,7 +121,7 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{ Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{
PsbtVerify: &lnrpc.FundingPsbtVerify{ PsbtVerify: &lnrpc.FundingPsbtVerify{
PendingChanId: pendingChanID[:], PendingChanId: pendingChanID[:],
FundedPsbt: buf.Bytes(), FundedPsbt: fundResp.FundedPsbt,
}, },
}, },
}) })
@ -145,30 +130,28 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{ Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{
PsbtVerify: &lnrpc.FundingPsbtVerify{ PsbtVerify: &lnrpc.FundingPsbtVerify{
PendingChanId: pendingChanID2[:], PendingChanId: pendingChanID2[:],
FundedPsbt: buf.Bytes(), FundedPsbt: fundResp.FundedPsbt,
}, },
}, },
}) })
require.NoError(t.t, err) require.NoError(t.t, err)
// Now we'll add the witness data back into the PSBT to make it a // Now we'll ask Dave's wallet to sign the PSBT so we can finish the
// complete and signed transaction that can be finalized. We'll trick // funding flow.
// a bit by putting the script sig back directly, because we know we ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
// will only get non-witness outputs from the miner wallet. defer cancel()
for idx := range finalTx.TxIn { finalizeReq := &walletrpc.FinalizePsbtRequest{
require.Greater(t.t, len(witnesses[idx]), 0) FundedPsbt: fundResp.FundedPsbt,
unsignedPsbt.Inputs[idx].FinalScriptSig = scripts[idx]
} }
finalizeRes, err := dave.WalletKitClient.FinalizePsbt(ctxt, finalizeReq)
require.NoError(t.t, err)
// We've signed our PSBT now, let's pass it to the intent again. // We've signed our PSBT now, let's pass it to the intent again.
buf.Reset()
err = unsignedPsbt.Serialize(&buf)
require.NoError(t.t, err)
_, err = carol.FundingStateStep(ctxb, &lnrpc.FundingTransitionMsg{ _, err = carol.FundingStateStep(ctxb, &lnrpc.FundingTransitionMsg{
Trigger: &lnrpc.FundingTransitionMsg_PsbtFinalize{ Trigger: &lnrpc.FundingTransitionMsg_PsbtFinalize{
PsbtFinalize: &lnrpc.FundingPsbtFinalize{ PsbtFinalize: &lnrpc.FundingPsbtFinalize{
PendingChanId: pendingChanID[:], PendingChanId: pendingChanID[:],
SignedPsbt: buf.Bytes(), SignedPsbt: finalizeRes.SignedPsbt,
}, },
}, },
}) })
@ -196,14 +179,12 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
// Let's progress the second channel now. This time we'll use the raw // Let's progress the second channel now. This time we'll use the raw
// wire format transaction directly. // wire format transaction directly.
buf.Reset()
err = finalTx.Serialize(&buf)
require.NoError(t.t, err) require.NoError(t.t, err)
_, err = carol.FundingStateStep(ctxb, &lnrpc.FundingTransitionMsg{ _, err = carol.FundingStateStep(ctxb, &lnrpc.FundingTransitionMsg{
Trigger: &lnrpc.FundingTransitionMsg_PsbtFinalize{ Trigger: &lnrpc.FundingTransitionMsg_PsbtFinalize{
PsbtFinalize: &lnrpc.FundingPsbtFinalize{ PsbtFinalize: &lnrpc.FundingPsbtFinalize{
PendingChanId: pendingChanID2[:], PendingChanId: pendingChanID2[:],
FinalRawTx: buf.Bytes(), FinalRawTx: finalizeRes.RawFinalTx,
}, },
}, },
}) })
@ -227,6 +208,10 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
// Great, now we can mine a block to get the transaction confirmed, then // Great, now we can mine a block to get the transaction confirmed, then
// wait for the new channel to be propagated through the network. // wait for the new channel to be propagated through the network.
var finalTx wire.MsgTx
err = finalTx.Deserialize(bytes.NewReader(finalizeRes.RawFinalTx))
require.NoError(t.t, err)
txHash := finalTx.TxHash() txHash := finalTx.TxHash()
block := mineBlocks(t, net, 6, 1)[0] block := mineBlocks(t, net, 6, 1)[0]
assertTxInBlock(t, block, &txHash) assertTxInBlock(t, block, &txHash)
@ -358,31 +343,3 @@ func receiveChanUpdate(ctx context.Context,
return updateMsg, nil return updateMsg, nil
} }
} }
// createPsbtFromSignedTx is a utility function to create a PSBT from an
// already-signed transaction, so we can test reconstructing, signing and
// extracting it. Returned are: an unsigned transaction serialization, a list
// of scriptSigs, one per input, and a list of witnesses, one per input.
func createPsbtFromSignedTx(tx *wire.MsgTx) (*psbt.Packet, [][]byte,
[]wire.TxWitness, error) {
scriptSigs := make([][]byte, 0, len(tx.TxIn))
witnesses := make([]wire.TxWitness, 0, len(tx.TxIn))
tx2 := tx.Copy()
// Blank out signature info in inputs
for i, tin := range tx2.TxIn {
tin.SignatureScript = nil
scriptSigs = append(scriptSigs, tx.TxIn[i].SignatureScript)
tin.Witness = nil
witnesses = append(witnesses, tx.TxIn[i].Witness)
}
// Outputs always contain: (value, scriptPubkey) so don't need
// amending. Now tx2 is tx with all signing data stripped out
unsignedPsbt, err := psbt.NewFromUnsignedTx(tx2)
if err != nil {
return nil, nil, nil, err
}
return unsignedPsbt, scriptSigs, witnesses, nil
}