Skip to content

Commit 13bfef0

Browse files
committed
itest: add hold invoice auto-generate test
Add integration test covering the two AddHoldInvoice input modes: auto-generate (no hash provided) and hash-only (backward compat). For the auto-generate case, the test also verifies via LookupInvoice that the generated preimage is not persisted in the invoice DB. Each scenario exercises the full payment lifecycle including settle.
1 parent cc29e55 commit 13bfef0

2 files changed

Lines changed: 108 additions & 0 deletions

File tree

itest/list_on_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ var allTestCases = []*lntest.TestCase{
250250
Name: "hold invoice sender persistence",
251251
TestFunc: testHoldInvoicePersistence,
252252
},
253+
{
254+
Name: "hold invoice auto generate",
255+
TestFunc: testHoldInvoiceAutoGenerate,
256+
},
253257
{
254258
Name: "maximum channel size",
255259
TestFunc: testMaxChannelSize,
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package itest
2+
3+
import (
4+
"github.com/btcsuite/btcd/btcutil"
5+
"github.com/lightningnetwork/lnd/lnrpc"
6+
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
7+
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
8+
"github.com/lightningnetwork/lnd/lntest"
9+
"github.com/lightningnetwork/lnd/lntypes"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
// testHoldInvoiceAutoGenerate tests that hold invoices can be created without
14+
// providing a hash. The server auto-generates a preimage, derives the hash,
15+
// and returns the preimage in the response without persisting it. It also
16+
// verifies the hash-only flow still works unchanged.
17+
func testHoldInvoiceAutoGenerate(ht *lntest.HarnessTest) {
18+
// Open a channel between Alice and Bob.
19+
_, nodes := ht.CreateSimpleNetwork(
20+
[][]string{nil, nil}, lntest.OpenChannelParams{
21+
Amt: btcutil.Amount(1_000_000),
22+
},
23+
)
24+
alice, bob := nodes[0], nodes[1]
25+
26+
// Test 1: Auto-generate preimage and hash (no hash provided).
27+
autoReq := &invoicesrpc.AddHoldInvoiceRequest{
28+
Memo: "auto-generated",
29+
Value: 10_000,
30+
}
31+
autoResp := bob.RPC.AddHoldInvoice(autoReq)
32+
33+
// The response must contain both a preimage and hash.
34+
require.Len(ht, autoResp.PaymentPreimage, 32,
35+
"expected 32-byte preimage")
36+
require.Len(ht, autoResp.PaymentHash, 32,
37+
"expected 32-byte payment hash")
38+
39+
// Verify the hash matches SHA256 of the preimage.
40+
var autoPreimage lntypes.Preimage
41+
copy(autoPreimage[:], autoResp.PaymentPreimage)
42+
autoHash := autoPreimage.Hash()
43+
require.Equal(ht, autoHash[:], autoResp.PaymentHash,
44+
"hash should be SHA256 of preimage")
45+
46+
// Verify the generated preimage is not persisted: a LookupInvoice
47+
// before settlement must report an empty r_preimage. The server
48+
// only learns the preimage again when the caller passes it to
49+
// SettleInvoice.
50+
lookup := bob.RPC.LookupInvoice(autoResp.PaymentHash)
51+
require.Empty(ht, lookup.RPreimage,
52+
"auto-generated preimage must not be stored in the DB")
53+
54+
// Subscribe, pay, and settle.
55+
autoStream := bob.RPC.SubscribeSingleInvoice(autoResp.PaymentHash)
56+
57+
ht.SendPaymentAndAssertStatus(alice, &routerrpc.SendPaymentRequest{
58+
PaymentRequest: autoResp.PaymentRequest,
59+
FeeLimitSat: 1_000_000,
60+
}, lnrpc.Payment_IN_FLIGHT)
61+
62+
ht.AssertInvoiceState(autoStream, lnrpc.Invoice_ACCEPTED)
63+
64+
bob.RPC.SettleInvoice(autoResp.PaymentPreimage)
65+
ht.AssertInvoiceState(autoStream, lnrpc.Invoice_SETTLED)
66+
ht.AssertPaymentStatus(
67+
alice, autoHash, lnrpc.Payment_SUCCEEDED,
68+
)
69+
70+
// Test 2: Traditional hash-only flow still works. The response
71+
// preimage must be empty because the server does not know it.
72+
var hashPreimage lntypes.Preimage
73+
copy(hashPreimage[:], ht.Random32Bytes())
74+
payHash := hashPreimage.Hash()
75+
76+
hashResp := bob.RPC.AddHoldInvoice(
77+
&invoicesrpc.AddHoldInvoiceRequest{
78+
Memo: "hash-only",
79+
Value: 10_000,
80+
Hash: payHash[:],
81+
},
82+
)
83+
84+
require.Empty(ht, hashResp.PaymentPreimage,
85+
"preimage should be empty for hash-only")
86+
require.Equal(ht, payHash[:], hashResp.PaymentHash,
87+
"returned hash should match provided hash")
88+
89+
// Subscribe, pay, and settle.
90+
hashStream := bob.RPC.SubscribeSingleInvoice(payHash[:])
91+
92+
ht.SendPaymentAndAssertStatus(alice, &routerrpc.SendPaymentRequest{
93+
PaymentRequest: hashResp.PaymentRequest,
94+
FeeLimitSat: 1_000_000,
95+
}, lnrpc.Payment_IN_FLIGHT)
96+
97+
ht.AssertInvoiceState(hashStream, lnrpc.Invoice_ACCEPTED)
98+
99+
bob.RPC.SettleInvoice(hashPreimage[:])
100+
ht.AssertInvoiceState(hashStream, lnrpc.Invoice_SETTLED)
101+
ht.AssertPaymentStatus(
102+
alice, payHash, lnrpc.Payment_SUCCEEDED,
103+
)
104+
}

0 commit comments

Comments
 (0)