mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-09-06 17:47:01 +02:00
htlcswitch: add channel link tests
Step #5 in making htlcManager (aka channelLink) testable: Combine all that have been done so far and add test framework for channel links which allow unit test: * message ordering * detect redundant messages * single hop payment * multihop payment * several cancel payment scenarios
This commit is contained in:
committed by
Olaoluwa Osuntokun
parent
3897343db1
commit
882aec704d
@@ -2,15 +2,24 @@ package htlcswitch
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"io"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/btcsuite/fastsha256"
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/roasbeef/btcutil"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||
"github.com/roasbeef/btcd/txscript"
|
||||
"github.com/roasbeef/btcd/wire"
|
||||
"github.com/roasbeef/btcutil"
|
||||
)
|
||||
|
||||
type mockServer struct {
|
||||
@@ -28,6 +37,7 @@ type mockServer struct {
|
||||
id []byte
|
||||
htlcSwitch *Switch
|
||||
|
||||
registry *mockInvoiceRegistry
|
||||
recordFuncs []func(lnwire.Message)
|
||||
}
|
||||
|
||||
@@ -35,12 +45,12 @@ var _ Peer = (*mockServer)(nil)
|
||||
|
||||
func newMockServer(t *testing.T, name string) *mockServer {
|
||||
return &mockServer{
|
||||
t: t,
|
||||
id: []byte(name),
|
||||
name: name,
|
||||
messages: make(chan lnwire.Message, 3000),
|
||||
|
||||
t: t,
|
||||
id: []byte(name),
|
||||
name: name,
|
||||
messages: make(chan lnwire.Message, 3000),
|
||||
quit: make(chan bool),
|
||||
registry: newMockRegistry(),
|
||||
htlcSwitch: New(Config{}),
|
||||
recordFuncs: make([]func(lnwire.Message), 0),
|
||||
}
|
||||
@@ -78,6 +88,74 @@ func (s *mockServer) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// mockHopIterator represents the test version of hop iterator which instead
|
||||
// of encrypting the path in onion blob just stores the path as a list of hops.
|
||||
type mockHopIterator struct {
|
||||
hops []HopID
|
||||
}
|
||||
|
||||
func newMockHopIterator(hops ...HopID) HopIterator {
|
||||
return &mockHopIterator{hops: hops}
|
||||
}
|
||||
|
||||
func (r *mockHopIterator) Next() *HopID {
|
||||
if len(r.hops) != 0 {
|
||||
next := r.hops[0]
|
||||
r.hops = r.hops[1:]
|
||||
return &next
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *mockHopIterator) Encode(w io.Writer) error {
|
||||
var hopLength [4]byte
|
||||
binary.BigEndian.PutUint32(hopLength[:], uint32(len(r.hops)))
|
||||
|
||||
if _, err := w.Write(hopLength[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, hop := range r.hops {
|
||||
if _, err := w.Write(hop[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ HopIterator = (*mockHopIterator)(nil)
|
||||
|
||||
// mockIteratorDecoder test version of hop iterator decoder which decodes the
|
||||
// encoded array of hops.
|
||||
type mockIteratorDecoder struct{}
|
||||
|
||||
func (p *mockIteratorDecoder) Decode(r io.Reader, meta []byte) (
|
||||
HopIterator, error) {
|
||||
|
||||
var b [4]byte
|
||||
_, err := r.Read(b[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hopLength := binary.BigEndian.Uint32(b[:])
|
||||
|
||||
hops := make([]HopID, hopLength)
|
||||
for i := uint32(0); i < hopLength; i++ {
|
||||
var hop HopID
|
||||
|
||||
_, err := r.Read(hop[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hops[i] = hop
|
||||
}
|
||||
|
||||
return newMockHopIterator(hops...), nil
|
||||
}
|
||||
|
||||
// messageInterceptor is function that handles the incoming peer messages and
|
||||
// may decide should we handle it or not.
|
||||
type messageInterceptor func(m lnwire.Message)
|
||||
@@ -205,3 +283,105 @@ func (f *mockChannelLink) Start() error { return nil }
|
||||
func (f *mockChannelLink) Stop() {}
|
||||
|
||||
var _ ChannelLink = (*mockChannelLink)(nil)
|
||||
|
||||
type mockInvoiceRegistry struct {
|
||||
sync.Mutex
|
||||
invoices map[chainhash.Hash]*channeldb.Invoice
|
||||
}
|
||||
|
||||
func newMockRegistry() *mockInvoiceRegistry {
|
||||
return &mockInvoiceRegistry{
|
||||
invoices: make(map[chainhash.Hash]*channeldb.Invoice),
|
||||
}
|
||||
}
|
||||
|
||||
func (i *mockInvoiceRegistry) LookupInvoice(rHash chainhash.Hash) (*channeldb.Invoice, error) {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
|
||||
invoice, ok := i.invoices[rHash]
|
||||
if !ok {
|
||||
return nil, errors.New("can't find mock invoice")
|
||||
}
|
||||
|
||||
return invoice, nil
|
||||
}
|
||||
|
||||
func (i *mockInvoiceRegistry) SettleInvoice(rhash chainhash.Hash) error {
|
||||
|
||||
invoice, err := i.LookupInvoice(rhash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.Lock()
|
||||
invoice.Terms.Settled = true
|
||||
i.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *mockInvoiceRegistry) AddInvoice(invoice *channeldb.Invoice) error {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
|
||||
rhash := fastsha256.Sum256(invoice.Terms.PaymentPreimage[:])
|
||||
i.invoices[chainhash.Hash(rhash)] = invoice
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ InvoiceDatabase = (*mockInvoiceRegistry)(nil)
|
||||
|
||||
type mockSigner struct {
|
||||
key *btcec.PrivateKey
|
||||
}
|
||||
|
||||
func (m *mockSigner) SignOutputRaw(tx *wire.MsgTx, signDesc *lnwallet.SignDescriptor) ([]byte, error) {
|
||||
amt := signDesc.Output.Value
|
||||
witnessScript := signDesc.WitnessScript
|
||||
privKey := m.key
|
||||
|
||||
sig, err := txscript.RawTxInWitnessSignature(tx, signDesc.SigHashes,
|
||||
signDesc.InputIndex, amt, witnessScript, txscript.SigHashAll, privKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sig[:len(sig)-1], nil
|
||||
}
|
||||
func (m *mockSigner) ComputeInputScript(tx *wire.MsgTx, signDesc *lnwallet.SignDescriptor) (*lnwallet.InputScript, error) {
|
||||
|
||||
witnessScript, err := txscript.WitnessScript(tx, signDesc.SigHashes,
|
||||
signDesc.InputIndex, signDesc.Output.Value, signDesc.Output.PkScript,
|
||||
txscript.SigHashAll, m.key, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lnwallet.InputScript{
|
||||
Witness: witnessScript,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type mockNotifier struct {
|
||||
}
|
||||
|
||||
func (m *mockNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, numConfs uint32) (*chainntnfs.ConfirmationEvent, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *mockNotifier) RegisterBlockEpochNtfn() (*chainntnfs.BlockEpochEvent, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *mockNotifier) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockNotifier) Stop() error {
|
||||
return nil
|
||||
}
|
||||
func (m *mockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint) (*chainntnfs.SpendEvent, error) {
|
||||
return &chainntnfs.SpendEvent{
|
||||
Spend: make(chan *chainntnfs.SpendDetail),
|
||||
}, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user