Skip to content

Commit 6499181

Browse files
committed
test: spend tests for the new v2 htlc
1 parent 71a7dec commit 6499181

File tree

1 file changed

+315
-0
lines changed

1 file changed

+315
-0
lines changed

swap/htlc_test.go

Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
package swap
2+
3+
import (
4+
"bytes"
5+
"crypto/sha256"
6+
"fmt"
7+
"testing"
8+
9+
"github.com/btcsuite/btcd/btcec"
10+
"github.com/btcsuite/btcd/chaincfg"
11+
"github.com/btcsuite/btcd/chaincfg/chainhash"
12+
"github.com/btcsuite/btcd/txscript"
13+
"github.com/btcsuite/btcd/wire"
14+
"github.com/btcsuite/btcutil"
15+
"github.com/lightninglabs/loop/test"
16+
"github.com/lightningnetwork/lnd/input"
17+
"github.com/lightningnetwork/lnd/keychain"
18+
"github.com/lightningnetwork/lnd/lntypes"
19+
"github.com/stretchr/testify/require"
20+
)
21+
22+
// assertEngineExecution executes the VM returned by the newEngine closure,
23+
// asserting the result matches the validity expectation. In the case where it
24+
// doesn't match the expectation, it executes the script step-by-step and
25+
// prints debug information to stdout.
26+
// This code is adopted from: lnd/input/script_utils_test.go
27+
func assertEngineExecution(t *testing.T, valid bool,
28+
newEngine func() (*txscript.Engine, error)) {
29+
30+
t.Helper()
31+
32+
// Get a new VM to execute.
33+
vm, err := newEngine()
34+
require.NoError(t, err, "unable to create engine")
35+
36+
// Execute the VM, only go on to the step-by-step execution if it
37+
// doesn't validate as expected.
38+
vmErr := vm.Execute()
39+
executionValid := vmErr == nil
40+
if valid == executionValid {
41+
return
42+
}
43+
44+
// Now that the execution didn't match what we expected, fetch a new VM
45+
// to step through.
46+
vm, err = newEngine()
47+
require.NoError(t, err, "unable to create engine")
48+
49+
// This buffer will trace execution of the Script, dumping out to
50+
// stdout.
51+
var debugBuf bytes.Buffer
52+
53+
done := false
54+
for !done {
55+
dis, err := vm.DisasmPC()
56+
if err != nil {
57+
t.Fatalf("stepping (%v)\n", err)
58+
}
59+
debugBuf.WriteString(fmt.Sprintf("stepping %v\n", dis))
60+
61+
done, err = vm.Step()
62+
if err != nil && valid {
63+
fmt.Println(debugBuf.String())
64+
t.Fatalf("spend test case failed, spend "+
65+
"should be valid: %v", err)
66+
} else if err == nil && !valid && done {
67+
fmt.Println(debugBuf.String())
68+
t.Fatalf("spend test case succeed, spend "+
69+
"should be invalid: %v", err)
70+
}
71+
72+
debugBuf.WriteString(
73+
fmt.Sprintf("Stack: %v", vm.GetStack()),
74+
)
75+
debugBuf.WriteString(
76+
fmt.Sprintf("AltStack: %v", vm.GetAltStack()),
77+
)
78+
}
79+
80+
// If we get to this point the unexpected case was not reached
81+
// during step execution, which happens for some checks, like
82+
// the clean-stack rule.
83+
validity := "invalid"
84+
if valid {
85+
validity = "valid"
86+
}
87+
88+
fmt.Println(debugBuf.String())
89+
t.Fatalf(
90+
"%v spend test case execution ended with: %v", validity, vmErr,
91+
)
92+
}
93+
94+
// TestHtlcV2 tests the HTLC V2 script success and timeout spend cases.
95+
func TestHtlcV2(t *testing.T) {
96+
const (
97+
htlcValue = btcutil.Amount(1 * 10e8)
98+
testCltvExpiry = 24
99+
)
100+
101+
var (
102+
testPreimage = lntypes.Preimage([32]byte{1, 2, 3})
103+
err error
104+
)
105+
106+
// We generate a fake output, and the corresponding txin. This output
107+
// doesn't need to exist, as we'll only be validating spending from the
108+
// transaction that references this.
109+
fundingOut := &wire.OutPoint{
110+
Hash: chainhash.Hash(sha256.Sum256([]byte{1, 2, 3})),
111+
Index: 50,
112+
}
113+
fakeFundingTxIn := wire.NewTxIn(fundingOut, nil, nil)
114+
115+
sweepTx := wire.NewMsgTx(2)
116+
sweepTx.AddTxIn(fakeFundingTxIn)
117+
sweepTx.AddTxOut(
118+
&wire.TxOut{
119+
PkScript: []byte("doesn't matter"),
120+
Value: int64(htlcValue),
121+
},
122+
)
123+
124+
// Create sender and receiver keys.
125+
senderPrivKey, senderPubKey := test.CreateKey(1)
126+
receiverPrivKey, receiverPubKey := test.CreateKey(2)
127+
128+
var (
129+
senderKey [33]byte
130+
receiverKey [33]byte
131+
)
132+
copy(senderKey[:], senderPubKey.SerializeCompressed())
133+
copy(receiverKey[:], receiverPubKey.SerializeCompressed())
134+
135+
hash := sha256.Sum256(testPreimage[:])
136+
137+
// Create the htlc.
138+
htlc, err := NewHtlc(
139+
HtlcV2, testCltvExpiry,
140+
senderKey, receiverKey, hash,
141+
HtlcP2WSH, &chaincfg.MainNetParams,
142+
)
143+
require.NoError(t, err)
144+
145+
// Create the htlc output we'll try to spend.
146+
htlcOutput := &wire.TxOut{
147+
Value: int64(htlcValue),
148+
PkScript: htlc.PkScript,
149+
}
150+
151+
// Create signers for sender and receiver.
152+
senderSigner := &input.MockSigner{
153+
Privkeys: []*btcec.PrivateKey{senderPrivKey},
154+
}
155+
receiverSigner := &input.MockSigner{
156+
Privkeys: []*btcec.PrivateKey{receiverPrivKey},
157+
}
158+
159+
signTx := func(tx *wire.MsgTx, pubkey *btcec.PublicKey,
160+
signer *input.MockSigner) (input.Signature, error) {
161+
162+
signDesc := &input.SignDescriptor{
163+
KeyDesc: keychain.KeyDescriptor{
164+
PubKey: pubkey,
165+
},
166+
167+
WitnessScript: htlc.Script(),
168+
Output: htlcOutput,
169+
HashType: txscript.SigHashAll,
170+
SigHashes: txscript.NewTxSigHashes(tx),
171+
InputIndex: 0,
172+
}
173+
174+
return signer.SignOutputRaw(tx, signDesc)
175+
}
176+
177+
testCases := []struct {
178+
name string
179+
witness func(*testing.T) wire.TxWitness
180+
valid bool
181+
}{
182+
{
183+
// Receiver can spend with valid preimage.
184+
"success case spend with valid preimage",
185+
func(t *testing.T) wire.TxWitness {
186+
sweepTx.TxIn[0].Sequence = htlc.SuccessSequence()
187+
sweepSig, err := signTx(
188+
sweepTx, receiverPubKey, receiverSigner,
189+
)
190+
require.NoError(t, err)
191+
192+
witness, err := htlc.GenSuccessWitness(
193+
sweepSig.Serialize(), testPreimage,
194+
)
195+
require.NoError(t, err)
196+
197+
return witness
198+
199+
}, true,
200+
},
201+
{
202+
// Receiver can't spend with the valid preimage and with
203+
// zero sequence.
204+
"success case no spend with valid preimage and zero sequence",
205+
func(t *testing.T) wire.TxWitness {
206+
sweepTx.TxIn[0].Sequence = 0
207+
sweepSig, err := signTx(
208+
sweepTx, receiverPubKey, receiverSigner,
209+
)
210+
require.NoError(t, err)
211+
212+
witness, err := htlc.GenSuccessWitness(
213+
sweepSig.Serialize(), testPreimage,
214+
)
215+
require.NoError(t, err)
216+
217+
return witness
218+
}, false,
219+
},
220+
{
221+
// Sender can't spend when haven't yet timed out.
222+
"timeout case no spend before timeout",
223+
func(t *testing.T) wire.TxWitness {
224+
sweepTx.LockTime = testCltvExpiry - 1
225+
sweepSig, err := signTx(
226+
sweepTx, senderPubKey, senderSigner,
227+
)
228+
require.NoError(t, err)
229+
230+
return htlc.GenTimeoutWitness(
231+
sweepSig.Serialize(),
232+
)
233+
}, false,
234+
},
235+
{
236+
// Sender can spend after timeout.
237+
"timeout case spend after timeout",
238+
func(t *testing.T) wire.TxWitness {
239+
sweepTx.LockTime = testCltvExpiry
240+
sweepSig, err := signTx(
241+
sweepTx, senderPubKey, senderSigner,
242+
)
243+
require.NoError(t, err)
244+
245+
return htlc.GenTimeoutWitness(
246+
sweepSig.Serialize(),
247+
)
248+
}, true,
249+
},
250+
{
251+
// Receiver can't spend after timeout.
252+
"timeout case receiver cannot spend",
253+
func(t *testing.T) wire.TxWitness {
254+
sweepTx.LockTime = testCltvExpiry
255+
sweepSig, err := signTx(
256+
sweepTx, receiverPubKey, receiverSigner,
257+
)
258+
require.NoError(t, err)
259+
260+
return htlc.GenTimeoutWitness(
261+
sweepSig.Serialize(),
262+
)
263+
}, false,
264+
},
265+
{
266+
// Sender can't spend after timeout with wrong sender
267+
// key.
268+
"timeout case cannot spend with wrong key",
269+
func(t *testing.T) wire.TxWitness {
270+
bogusKey := [33]byte{0xb, 0xa, 0xd}
271+
272+
// Create the htlc with the bogus key.
273+
htlc, err = NewHtlc(
274+
HtlcV2, testCltvExpiry,
275+
bogusKey, receiverKey, hash,
276+
HtlcP2WSH, &chaincfg.MainNetParams,
277+
)
278+
require.NoError(t, err)
279+
280+
// Create the htlc output we'll try to spend.
281+
htlcOutput = &wire.TxOut{
282+
Value: int64(htlcValue),
283+
PkScript: htlc.PkScript,
284+
}
285+
286+
sweepTx.LockTime = testCltvExpiry
287+
sweepSig, err := signTx(
288+
sweepTx, senderPubKey, senderSigner,
289+
)
290+
require.NoError(t, err)
291+
292+
return htlc.GenTimeoutWitness(
293+
sweepSig.Serialize(),
294+
)
295+
}, false,
296+
},
297+
}
298+
299+
for _, testCase := range testCases {
300+
testCase := testCase
301+
302+
t.Run(testCase.name, func(t *testing.T) {
303+
sweepTx.TxIn[0].Witness = testCase.witness(t)
304+
305+
newEngine := func() (*txscript.Engine, error) {
306+
return txscript.NewEngine(
307+
htlc.PkScript, sweepTx, 0,
308+
txscript.StandardVerifyFlags, nil,
309+
nil, int64(htlcValue))
310+
}
311+
312+
assertEngineExecution(t, testCase.valid, newEngine)
313+
})
314+
}
315+
}

0 commit comments

Comments
 (0)