Skip to content

Commit 5812d5a

Browse files
committed
[wip]itest: add itest
1 parent 2253bd3 commit 5812d5a

File tree

3 files changed

+369
-0
lines changed

3 files changed

+369
-0
lines changed

itest/lnd_wallet.go

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
package itest
22

33
import (
4+
"bytes"
5+
46
"github.com/btcsuite/btcd/btcutil"
7+
"github.com/btcsuite/btcd/chaincfg"
8+
"github.com/btcsuite/btcd/txscript"
59
"github.com/btcsuite/btcd/wire"
10+
"github.com/davecgh/go-spew/spew"
11+
"github.com/lightningnetwork/lnd/input"
12+
"github.com/lightningnetwork/lnd/lnrpc"
13+
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
14+
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
615
"github.com/lightningnetwork/lnd/lntest"
716
"github.com/lightningnetwork/lnd/lntest/node"
817
"github.com/lightningnetwork/lnd/lnwallet"
18+
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
919
"github.com/stretchr/testify/require"
1020
)
1121

@@ -48,6 +58,12 @@ var walletTestCases = []*lntest.TestCase{
4858
runTestListUnspentRestart(ht, ht.FundCoinsP2TR)
4959
},
5060
},
61+
{
62+
Name: "submit package basic",
63+
TestFunc: func(ht *lntest.HarnessTest) {
64+
runTestSubmitPackageBasicCPFP(ht)
65+
},
66+
},
5167
}
5268

5369
type fundMethod func(amt btcutil.Amount, hn *node.HarnessNode) *wire.MsgTx
@@ -118,3 +134,231 @@ func runTestListUnspentRestart(ht *lntest.HarnessTest, fundCoins fundMethod) {
118134
// Alice should have no UTXO.
119135
ht.AssertNumUTXOs(alice, 0)
120136
}
137+
138+
// Helper to convert walletrpc.KeyDescriptor to a P2WPKH address.
139+
func keyDescToAddrP2WPKH(ht *lntest.HarnessTest,
140+
keyDesc *signrpc.KeyDescriptor) btcutil.Address {
141+
142+
harnessNetParams := &chaincfg.RegressionNetParams
143+
144+
pubKeyBytes := keyDesc.RawKeyBytes
145+
require.NotEmpty(ht, pubKeyBytes, "KeyDescriptor has empty RawKeyBytes")
146+
147+
addr, err := btcutil.NewAddressWitnessPubKeyHash(
148+
btcutil.Hash160(pubKeyBytes),
149+
harnessNetParams,
150+
)
151+
require.NoError(ht, err)
152+
153+
return addr
154+
}
155+
156+
// runTestSubmitPackageBasicCPFP tests the SubmitPackage RPC for a simple
157+
// Child-Pays-For-Parent (CPFP) scenario.
158+
func runTestSubmitPackageBasicCPFP(ht *lntest.HarnessTest) {
159+
const (
160+
fundingAmount = btcutil.Amount(10_000_000)
161+
childFeeRate = 100
162+
testKeyFamily = 111
163+
)
164+
165+
// Create a new node (Alice).
166+
alice := ht.NewNode("Alice", nil)
167+
defer ht.Shutdown(alice)
168+
169+
// Fund Alice with a UTXO.
170+
ht.FundCoins(fundingAmount*2, alice)
171+
172+
// Derive a key and address for the initial funding.
173+
fundingKey := alice.RPC.DeriveNextKey(
174+
&walletrpc.KeyReq{
175+
KeyFamily: testKeyFamily,
176+
},
177+
)
178+
179+
fundingAddr := keyDescToAddrP2WPKH(ht, fundingKey)
180+
ht.Logf("Funding address: %s", fundingAddr)
181+
182+
// Send funds to this specific address.
183+
ht.SendCoinsToAddr(alice, fundingAddr, fundingAmount)
184+
block := ht.MineBlocksAndAssertNumTxes(1, 1)
185+
186+
// Now find the funding tx in the mined block so we can use the output
187+
// as an input to the parent tx.
188+
fundingPkScript := ht.PayToAddrScript(fundingAddr)
189+
var (
190+
outputIndex uint32
191+
fundingOut *wire.TxOut
192+
fundTx *wire.MsgTx
193+
)
194+
195+
for _, tx := range block[0].Transactions {
196+
for i, txOut := range tx.TxOut {
197+
if bytes.Equal(txOut.PkScript, fundingPkScript) {
198+
outputIndex = uint32(i)
199+
fundingOut = txOut
200+
fundTx = tx
201+
202+
break
203+
}
204+
}
205+
}
206+
require.NotNil(ht, fundTx, "Funding output not found")
207+
208+
fundOutPoint := wire.OutPoint{
209+
Hash: fundTx.TxHash(),
210+
Index: outputIndex,
211+
}
212+
213+
parentOutValue := int64(fundingAmount)
214+
215+
// Create a V3 "parent" transaction spending the funding output.
216+
parentTx := wire.NewMsgTx(3)
217+
parentTx.AddTxIn(wire.NewTxIn(&fundOutPoint, nil, nil))
218+
219+
// Derive a key and address for the parent's output.
220+
parentOutKey := alice.RPC.DeriveNextKey(
221+
&walletrpc.KeyReq{
222+
KeyFamily: testKeyFamily,
223+
},
224+
)
225+
226+
parentOutAddr := keyDescToAddrP2WPKH(ht, parentOutKey)
227+
parentOutPkScript := ht.PayToAddrScript(parentOutAddr)
228+
parentTx.AddTxOut(wire.NewTxOut(parentOutValue, parentOutPkScript))
229+
230+
// Serialize the parent tx.
231+
var parentBuf bytes.Buffer
232+
require.NoError(ht, parentTx.Serialize(&parentBuf))
233+
234+
// Create a sign descriptor for the parent tx.
235+
parentSignDesc := &signrpc.SignDescriptor{
236+
KeyDesc: fundingKey,
237+
Output: &signrpc.TxOut{
238+
Value: fundingOut.Value,
239+
PkScript: fundingOut.PkScript,
240+
},
241+
Sighash: uint32(txscript.SigHashAll),
242+
WitnessScript: fundingOut.PkScript,
243+
SignMethod: signrpc.SignMethod_SIGN_METHOD_WITNESS_V0,
244+
}
245+
246+
// Sign the parent tx.
247+
parentSignResp, err := alice.RPC.Signer.SignOutputRaw(
248+
ht.Context(),
249+
&signrpc.SignReq{
250+
RawTxBytes: parentBuf.Bytes(),
251+
SignDescs: []*signrpc.SignDescriptor{
252+
parentSignDesc,
253+
},
254+
},
255+
)
256+
require.NoError(ht, err)
257+
258+
// Set the parent tx's witness to the signature.
259+
parentTx.TxIn[0].Witness = makeP2WPKHWitness(
260+
parentSignResp.RawSigs[0],
261+
fundingKey,
262+
)
263+
264+
parentOut := &wire.OutPoint{
265+
Hash: parentTx.TxHash(),
266+
Index: 0,
267+
}
268+
269+
// Create a new P2WPKH address for the child's output.
270+
childOutAddrResp := alice.RPC.NewAddress(
271+
&lnrpc.NewAddressRequest{
272+
Account: lnwallet.DefaultAccountName,
273+
Type: lnrpc.AddressType_WITNESS_PUBKEY_HASH,
274+
},
275+
)
276+
require.NoError(ht, err)
277+
278+
// Estimate child's weight and fee.
279+
childEstimator := input.TxWeightEstimator{}
280+
childEstimator.AddP2WKHInput()
281+
childEstimator.AddP2WKHOutput()
282+
childWeight := childEstimator.Weight()
283+
childFee := chainfee.SatPerVByte(
284+
childFeeRate,
285+
).FeePerKWeight().FeeForWeight(
286+
childWeight,
287+
)
288+
289+
childOutValue := btcutil.Amount(parentOutValue) - childFee
290+
291+
// Create the unsigned child transaction spending the parent's output.
292+
childTx := wire.NewMsgTx(3)
293+
childTx.AddTxIn(wire.NewTxIn(parentOut, nil, nil))
294+
295+
childOutAddr := ht.DecodeAddress(childOutAddrResp.Address)
296+
childOutPkScript := ht.PayToAddrScript(childOutAddr)
297+
298+
childTx.AddTxOut(
299+
wire.NewTxOut(int64(childOutValue), childOutPkScript),
300+
)
301+
302+
// Serialize the child tx.
303+
var childBuf bytes.Buffer
304+
require.NoError(ht, childTx.Serialize(&childBuf))
305+
306+
// Create a sign descriptor for the child tx.
307+
childSignDesc := &signrpc.SignDescriptor{
308+
KeyDesc: parentOutKey,
309+
Output: &signrpc.TxOut{
310+
Value: parentOutValue,
311+
PkScript: parentOutPkScript,
312+
},
313+
Sighash: uint32(txscript.SigHashAll),
314+
WitnessScript: parentOutPkScript,
315+
SignMethod: signrpc.SignMethod_SIGN_METHOD_WITNESS_V0,
316+
}
317+
318+
// Sign the child tx.
319+
childSignResp, err := alice.RPC.Signer.SignOutputRaw(
320+
ht.Context(),
321+
&signrpc.SignReq{
322+
RawTxBytes: childBuf.Bytes(),
323+
SignDescs: []*signrpc.SignDescriptor{
324+
childSignDesc,
325+
},
326+
},
327+
)
328+
require.NoError(ht, err)
329+
330+
// Set the child tx's witness to the signature.
331+
childTx.TxIn[0].Witness = makeP2WPKHWitness(
332+
childSignResp.RawSigs[0], parentOutKey,
333+
)
334+
335+
submitPackageResp, err := alice.RPC.WalletKit.SubmitPackage(
336+
ht.Context(), &walletrpc.SubmitPackageRequest{
337+
ParentTxs: [][]byte{parentBuf.Bytes()},
338+
ChildTx: childBuf.Bytes(),
339+
},
340+
)
341+
require.NoError(ht, err)
342+
ht.Logf("SubmitPackage response: %v", spew.Sdump(submitPackageResp))
343+
344+
ht.AssertTxInMempool(parentTx.TxHash())
345+
ht.AssertTxInMempool(childTx.TxHash())
346+
347+
ht.Log("Mining block with package transactions...")
348+
block = ht.MineBlocksAndAssertNumTxes(1, 2)
349+
ht.AssertTxInBlock(block[0], parentTx.TxHash())
350+
ht.AssertTxInBlock(block[0], childTx.TxHash())
351+
}
352+
353+
// makeP2WPKHWitness creates a P2WPKH witness stack for the given signature and
354+
// key descriptor.
355+
func makeP2WPKHWitness(sig []byte,
356+
keyDesc *signrpc.KeyDescriptor) wire.TxWitness {
357+
358+
sigWithHash := append(sig, byte(txscript.SigHashAll))
359+
360+
return wire.TxWitness{
361+
sigWithHash, // element 0: 70-73-byte signature.
362+
keyDesc.RawKeyBytes, // element 1: 33-byte compressed pubkey.
363+
}
364+
}

0 commit comments

Comments
 (0)