diff --git a/lntest/itest/psbt.go b/lntest/itest/psbt.go index e8c633e6..e316c45d 100644 --- a/lntest/itest/psbt.go +++ b/lntest/itest/psbt.go @@ -45,6 +45,10 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) { if err != nil { t.Fatalf("unable to connect peers: %v", err) } + err = net.EnsureConnected(ctxt, carol, net.Alice) + if err != nil { + t.Fatalf("unable to connect peers: %v", err) + } // At this point, we can begin our PSBT channel funding workflow. We'll // start by generating a pending channel ID externally that will be used @@ -54,8 +58,15 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) { t.Fatalf("unable to gen pending chan ID: %v", err) } + // We'll also test batch funding of two channels so we need another ID. + var pendingChanID2 [32]byte + if _, err := rand.Read(pendingChanID2[:]); err != nil { + t.Fatalf("unable to gen pending chan ID: %v", err) + } + // Now that we have the pending channel ID, Carol will open the channel - // by specifying a PSBT shim. + // by specifying a PSBT shim. We use the NoPublish flag here to avoid + // publishing the whole batch TX too early. ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) defer cancel() chanUpdates, psbtBytes, err := openChannelPsbt( @@ -65,23 +76,50 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) { Shim: &lnrpc.FundingShim_PsbtShim{ PsbtShim: &lnrpc.PsbtShim{ PendingChanId: pendingChanID[:], + NoPublish: true, }, }, }, }, ) if err != nil { - t.Fatalf("unable to open channel: %v", err) + t.Fatalf("unable to open channel to dave: %v", err) } packet, err := psbt.NewFromRawBytes(bytes.NewReader(psbtBytes), false) if err != nil { t.Fatalf("unable to parse returned PSBT: %v", err) } + // 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. + ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) + defer cancel() + chanUpdates2, psbtBytes2, err := openChannelPsbt( + ctxt, carol, net.Alice, lntest.OpenChannelParams{ + Amt: chanSize, + FundingShim: &lnrpc.FundingShim{ + Shim: &lnrpc.FundingShim_PsbtShim{ + PsbtShim: &lnrpc.PsbtShim{ + PendingChanId: pendingChanID2[:], + NoPublish: false, + }, + }, + }, + }, + ) + if err != nil { + t.Fatalf("unable to open channel to alice: %v", err) + } + packet2, err := psbt.NewFromRawBytes(bytes.NewReader(psbtBytes2), false) + if err != nil { + t.Fatalf("unable to parse returned PSBT: %v", err) + } + // We'll now create a fully signed transaction that sends to the outputs // 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. - tx, err := net.Miner.CreateTransaction(packet.UnsignedTx.TxOut, 5, true) + allOuts := append(packet.UnsignedTx.TxOut, packet2.UnsignedTx.TxOut...) + tx, err := net.Miner.CreateTransaction(allOuts, 5, true) if err != nil { t.Fatalf("unable to create funding transaction: %v", err) } @@ -119,7 +157,7 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) { } // We have a PSBT that has no witness data yet, which is exactly what we - // need for the next step: Verify the PSBT with the funding intent. + // need for the next step: Verify the PSBT with the funding intents. _, err = carol.FundingStateStep(ctxb, &lnrpc.FundingTransitionMsg{ Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{ PsbtVerify: &lnrpc.FundingPsbtVerify{ @@ -131,6 +169,17 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) { if err != nil { t.Fatalf("error verifying PSBT with funding intent: %v", err) } + _, err = carol.FundingStateStep(ctxb, &lnrpc.FundingTransitionMsg{ + Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{ + PsbtVerify: &lnrpc.FundingPsbtVerify{ + PendingChanId: pendingChanID2[:], + FundedPsbt: buf.Bytes(), + }, + }, + }) + if err != nil { + t.Fatalf("error verifying PSBT with funding intent 2: %v", err) + } // Now we'll add the witness data back into the PSBT to make it a // complete and signed transaction that can be finalized. We'll trick @@ -162,7 +211,7 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) { } // Consume the "channel pending" update. This waits until the funding - // transaction has been published. + // transaction was fully compiled. ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) defer cancel() updateResp, err := receiveChanUpdate(ctxt, chanUpdates) @@ -181,6 +230,49 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) { OutputIndex: upd.ChanPending.OutputIndex, } + // No transaction should have been published yet. + mempool, err := net.Miner.Node.GetRawMempool() + if err != nil { + t.Fatalf("error querying mempool: %v", err) + } + if len(mempool) != 0 { + t.Fatalf("unexpected txes in mempool: %v", mempool) + } + + // Let's progress the second channel now. + _, err = carol.FundingStateStep(ctxb, &lnrpc.FundingTransitionMsg{ + Trigger: &lnrpc.FundingTransitionMsg_PsbtFinalize{ + PsbtFinalize: &lnrpc.FundingPsbtFinalize{ + PendingChanId: pendingChanID2[:], + SignedPsbt: buf.Bytes(), + }, + }, + }) + if err != nil { + t.Fatalf("error finalizing PSBT with funding intent 2: %v", err) + } + + // Consume the "channel pending" update for the second channel. This + // waits until the funding transaction was fully compiled and in this + // case published. + ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) + defer cancel() + updateResp2, err := receiveChanUpdate(ctxt, chanUpdates2) + if err != nil { + t.Fatalf("unable to consume channel update message: %v", err) + } + upd2, ok := updateResp2.Update.(*lnrpc.OpenStatusUpdate_ChanPending) + if !ok { + t.Fatalf("expected PSBT funding update, instead got %v", + updateResp2) + } + chanPoint2 := &lnrpc.ChannelPoint{ + FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{ + FundingTxidBytes: upd2.ChanPending.Txid, + }, + OutputIndex: upd2.ChanPending.OutputIndex, + } + // 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() @@ -192,6 +284,10 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) { if err != nil { t.Fatalf("carol didn't report channel: %v", err) } + err = carol.WaitForNetworkChannelOpen(ctxt, chanPoint2) + if err != nil { + t.Fatalf("carol didn't report channel 2: %v", err) + } // With the channel open, ensure that it is counted towards Carol's // total channel balance.