2017-05-02 02:29:30 +03:00
|
|
|
package htlcswitch
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2020-05-22 01:37:39 +03:00
|
|
|
crand "crypto/rand"
|
2017-05-02 02:29:30 +03:00
|
|
|
"crypto/sha256"
|
2018-01-16 11:39:25 +03:00
|
|
|
"encoding/binary"
|
2017-11-11 02:15:19 +03:00
|
|
|
"fmt"
|
2017-05-02 02:29:30 +03:00
|
|
|
"io/ioutil"
|
2017-06-29 16:40:45 +03:00
|
|
|
"math/big"
|
2017-07-09 02:30:20 +03:00
|
|
|
"net"
|
2018-02-28 06:32:36 +03:00
|
|
|
"os"
|
multi: replace per channel sigPool with global daemon level sigPool
In this commit, we remove the per channel `sigPool` within the
`lnwallet.LightningChannel` struct. With this change, we ensure that as
the number of channels grows, the number of gouroutines idling in the
sigPool stays constant. It's the case that currently on the daemon, most
channels are likely inactive, with only a hand full actually
consistently carrying out channel updates. As a result, this change
should reduce the amount of idle CPU usage, as we have less active
goroutines in select loops.
In order to make this change, the `SigPool` itself has been publicly
exported such that outside callers can make a `SigPool` and pass it into
newly created channels. Since the sig pool now lives outside the
channel, we were also able to do away with the Stop() method on the
channel all together.
Finally, the server is the sub-system that is currently responsible for
managing the `SigPool` within lnd.
2018-12-15 03:35:07 +03:00
|
|
|
"runtime"
|
2019-04-08 14:10:32 +03:00
|
|
|
"runtime/pprof"
|
2018-01-16 11:39:25 +03:00
|
|
|
"sync/atomic"
|
2018-02-28 06:32:36 +03:00
|
|
|
"testing"
|
|
|
|
"time"
|
2017-07-09 02:30:20 +03:00
|
|
|
|
2018-07-31 10:17:17 +03:00
|
|
|
"github.com/btcsuite/btcd/btcec"
|
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
|
|
"github.com/btcsuite/btcutil"
|
2017-05-02 02:29:30 +03:00
|
|
|
"github.com/go-errors/errors"
|
2019-11-05 02:10:15 +03:00
|
|
|
sphinx "github.com/lightningnetwork/lightning-onion"
|
2017-05-02 02:29:30 +03:00
|
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
2020-01-10 05:44:27 +03:00
|
|
|
"github.com/lightningnetwork/lnd/channeldb/kvdb"
|
2018-01-17 07:18:53 +03:00
|
|
|
"github.com/lightningnetwork/lnd/contractcourt"
|
2019-08-31 00:05:53 +03:00
|
|
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
2019-01-16 17:47:43 +03:00
|
|
|
"github.com/lightningnetwork/lnd/input"
|
2018-02-18 02:29:01 +03:00
|
|
|
"github.com/lightningnetwork/lnd/keychain"
|
2018-06-08 06:18:10 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lnpeer"
|
2020-08-26 21:18:02 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lntest/mock"
|
2019-09-19 22:46:56 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
2019-01-15 13:31:22 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
2017-05-02 02:29:30 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lnwallet"
|
2019-10-31 05:43:05 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
2017-05-02 02:29:30 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
|
|
"github.com/lightningnetwork/lnd/shachain"
|
2018-08-02 00:19:29 +03:00
|
|
|
"github.com/lightningnetwork/lnd/ticker"
|
2017-05-02 02:29:30 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
alicePrivKey = []byte("alice priv key")
|
|
|
|
bobPrivKey = []byte("bob priv key")
|
|
|
|
carolPrivKey = []byte("carol priv key")
|
2017-06-29 16:40:45 +03:00
|
|
|
|
2019-09-10 13:27:39 +03:00
|
|
|
testSig = &btcec.Signature{
|
2017-06-29 16:40:45 +03:00
|
|
|
R: new(big.Int),
|
|
|
|
S: new(big.Int),
|
|
|
|
}
|
2018-01-31 07:11:01 +03:00
|
|
|
wireSig, _ = lnwire.NewSigFromSignature(testSig)
|
2017-06-29 16:40:45 +03:00
|
|
|
|
|
|
|
_, _ = testSig.R.SetString("6372440660162918006277497454296753625158993"+
|
|
|
|
"5445068131219452686511677818569431", 10)
|
|
|
|
_, _ = testSig.S.SetString("1880105606924982582529128710493133386286603"+
|
|
|
|
"3135609736119018462340006816851118", 10)
|
2018-03-11 04:27:51 +03:00
|
|
|
|
|
|
|
// testTx is used as the default funding txn for single-funder channels.
|
|
|
|
testTx = &wire.MsgTx{
|
|
|
|
Version: 1,
|
|
|
|
TxIn: []*wire.TxIn{
|
|
|
|
{
|
|
|
|
PreviousOutPoint: wire.OutPoint{
|
|
|
|
Hash: chainhash.Hash{},
|
|
|
|
Index: 0xffffffff,
|
|
|
|
},
|
|
|
|
SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62},
|
|
|
|
Sequence: 0xffffffff,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
TxOut: []*wire.TxOut{
|
|
|
|
{
|
|
|
|
Value: 5000000000,
|
|
|
|
PkScript: []byte{
|
|
|
|
0x41, // OP_DATA_65
|
|
|
|
0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
|
|
|
|
0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
|
|
|
|
0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
|
|
|
|
0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
|
|
|
|
0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
|
|
|
|
0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
|
|
|
|
0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
|
|
|
|
0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
|
|
|
|
0xa6, // 65-byte signature
|
|
|
|
0xac, // OP_CHECKSIG
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
LockTime: 5,
|
|
|
|
}
|
2019-04-08 14:10:32 +03:00
|
|
|
|
|
|
|
testBatchTimeout = 50 * time.Millisecond
|
2017-05-02 02:29:30 +03:00
|
|
|
)
|
|
|
|
|
2018-01-16 11:39:25 +03:00
|
|
|
var idSeqNum uint64
|
|
|
|
|
2019-10-09 17:11:06 +03:00
|
|
|
// genID generates a unique tuple to identify a test channel.
|
|
|
|
func genID() (lnwire.ChannelID, lnwire.ShortChannelID) {
|
|
|
|
id := atomic.AddUint64(&idSeqNum, 1)
|
2018-01-16 11:39:25 +03:00
|
|
|
|
|
|
|
var scratch [8]byte
|
|
|
|
|
|
|
|
binary.BigEndian.PutUint64(scratch[:], id)
|
|
|
|
hash1, _ := chainhash.NewHash(bytes.Repeat(scratch[:], 4))
|
|
|
|
|
|
|
|
chanPoint1 := wire.NewOutPoint(hash1, uint32(id))
|
|
|
|
chanID1 := lnwire.NewChanIDFromOutPoint(chanPoint1)
|
|
|
|
aliceChanID := lnwire.NewShortChanIDFromInt(id)
|
2019-10-09 17:11:06 +03:00
|
|
|
|
|
|
|
return chanID1, aliceChanID
|
|
|
|
}
|
|
|
|
|
|
|
|
// genIDs generates ids for two test channels.
|
|
|
|
func genIDs() (lnwire.ChannelID, lnwire.ChannelID, lnwire.ShortChannelID,
|
|
|
|
lnwire.ShortChannelID) {
|
|
|
|
|
|
|
|
chanID1, aliceChanID := genID()
|
|
|
|
chanID2, bobChanID := genID()
|
2018-01-16 11:39:25 +03:00
|
|
|
|
|
|
|
return chanID1, chanID2, aliceChanID, bobChanID
|
|
|
|
}
|
|
|
|
|
2018-04-04 06:09:51 +03:00
|
|
|
// mockGetChanUpdateMessage helper function which returns topology update of
|
|
|
|
// the channel
|
|
|
|
func mockGetChanUpdateMessage(cid lnwire.ShortChannelID) (*lnwire.ChannelUpdate, error) {
|
2017-06-29 16:40:45 +03:00
|
|
|
return &lnwire.ChannelUpdate{
|
2018-01-31 07:11:01 +03:00
|
|
|
Signature: wireSig,
|
2017-06-29 16:40:45 +03:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2017-05-02 02:29:30 +03:00
|
|
|
// generateRandomBytes returns securely generated random bytes.
|
|
|
|
// It will return an error if the system's secure random
|
|
|
|
// number generator fails to function correctly, in which
|
|
|
|
// case the caller should not continue.
|
|
|
|
func generateRandomBytes(n int) ([]byte, error) {
|
|
|
|
b := make([]byte, n)
|
|
|
|
|
2017-08-01 22:53:07 +03:00
|
|
|
// TODO(roasbeef): should use counter in tests (atomic) rather than
|
|
|
|
// this
|
|
|
|
|
2020-05-22 01:37:39 +03:00
|
|
|
_, err := crand.Read(b)
|
2017-05-02 02:29:30 +03:00
|
|
|
// Note that Err == nil only if we read len(b) bytes.
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return b, nil
|
|
|
|
}
|
|
|
|
|
2019-04-08 12:29:18 +03:00
|
|
|
type testLightningChannel struct {
|
|
|
|
channel *lnwallet.LightningChannel
|
|
|
|
restore func() (*lnwallet.LightningChannel, error)
|
|
|
|
}
|
|
|
|
|
2017-05-02 02:29:30 +03:00
|
|
|
// createTestChannel creates the channel and returns our and remote channels
|
|
|
|
// representations.
|
2017-07-31 00:09:10 +03:00
|
|
|
//
|
|
|
|
// TODO(roasbeef): need to factor out, similar func re-used in many parts of codebase
|
|
|
|
func createTestChannel(alicePrivKey, bobPrivKey []byte,
|
2018-02-08 03:45:19 +03:00
|
|
|
aliceAmount, bobAmount, aliceReserve, bobReserve btcutil.Amount,
|
2019-04-08 12:29:18 +03:00
|
|
|
chanID lnwire.ShortChannelID) (*testLightningChannel,
|
|
|
|
*testLightningChannel, func(), error) {
|
2017-05-02 02:29:30 +03:00
|
|
|
|
|
|
|
aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), alicePrivKey)
|
|
|
|
bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), bobPrivKey)
|
|
|
|
|
|
|
|
channelCapacity := aliceAmount + bobAmount
|
|
|
|
csvTimeoutAlice := uint32(5)
|
|
|
|
csvTimeoutBob := uint32(4)
|
|
|
|
|
2017-11-29 16:26:48 +03:00
|
|
|
aliceConstraints := &channeldb.ChannelConstraints{
|
|
|
|
DustLimit: btcutil.Amount(200),
|
|
|
|
MaxPendingAmount: lnwire.NewMSatFromSatoshis(
|
|
|
|
channelCapacity),
|
2018-02-08 03:45:19 +03:00
|
|
|
ChanReserve: aliceReserve,
|
2017-11-29 16:26:48 +03:00
|
|
|
MinHTLC: 0,
|
2019-01-16 17:47:43 +03:00
|
|
|
MaxAcceptedHtlcs: input.MaxHTLCNumber / 2,
|
2018-12-11 00:56:41 +03:00
|
|
|
CsvDelay: uint16(csvTimeoutAlice),
|
2017-11-29 16:26:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bobConstraints := &channeldb.ChannelConstraints{
|
|
|
|
DustLimit: btcutil.Amount(800),
|
|
|
|
MaxPendingAmount: lnwire.NewMSatFromSatoshis(
|
|
|
|
channelCapacity),
|
2018-02-08 03:45:19 +03:00
|
|
|
ChanReserve: bobReserve,
|
2017-11-29 16:26:48 +03:00
|
|
|
MinHTLC: 0,
|
2019-01-16 17:47:43 +03:00
|
|
|
MaxAcceptedHtlcs: input.MaxHTLCNumber / 2,
|
2018-12-11 00:56:41 +03:00
|
|
|
CsvDelay: uint16(csvTimeoutBob),
|
2017-11-29 16:26:48 +03:00
|
|
|
}
|
|
|
|
|
2017-05-02 02:29:30 +03:00
|
|
|
var hash [sha256.Size]byte
|
|
|
|
randomSeed, err := generateRandomBytes(sha256.Size)
|
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
copy(hash[:], randomSeed)
|
|
|
|
|
|
|
|
prevOut := &wire.OutPoint{
|
|
|
|
Hash: chainhash.Hash(hash),
|
|
|
|
Index: 0,
|
|
|
|
}
|
|
|
|
fundingTxIn := wire.NewTxIn(prevOut, nil, nil)
|
|
|
|
|
2017-07-31 00:09:10 +03:00
|
|
|
aliceCfg := channeldb.ChannelConfig{
|
2018-02-18 02:29:01 +03:00
|
|
|
ChannelConstraints: *aliceConstraints,
|
|
|
|
MultiSigKey: keychain.KeyDescriptor{
|
|
|
|
PubKey: aliceKeyPub,
|
|
|
|
},
|
|
|
|
RevocationBasePoint: keychain.KeyDescriptor{
|
|
|
|
PubKey: aliceKeyPub,
|
|
|
|
},
|
|
|
|
PaymentBasePoint: keychain.KeyDescriptor{
|
|
|
|
PubKey: aliceKeyPub,
|
|
|
|
},
|
|
|
|
DelayBasePoint: keychain.KeyDescriptor{
|
|
|
|
PubKey: aliceKeyPub,
|
|
|
|
},
|
|
|
|
HtlcBasePoint: keychain.KeyDescriptor{
|
|
|
|
PubKey: aliceKeyPub,
|
|
|
|
},
|
2017-07-31 00:09:10 +03:00
|
|
|
}
|
|
|
|
bobCfg := channeldb.ChannelConfig{
|
2018-02-18 02:29:01 +03:00
|
|
|
ChannelConstraints: *bobConstraints,
|
|
|
|
MultiSigKey: keychain.KeyDescriptor{
|
|
|
|
PubKey: bobKeyPub,
|
|
|
|
},
|
|
|
|
RevocationBasePoint: keychain.KeyDescriptor{
|
|
|
|
PubKey: bobKeyPub,
|
|
|
|
},
|
|
|
|
PaymentBasePoint: keychain.KeyDescriptor{
|
|
|
|
PubKey: bobKeyPub,
|
|
|
|
},
|
|
|
|
DelayBasePoint: keychain.KeyDescriptor{
|
|
|
|
PubKey: bobKeyPub,
|
|
|
|
},
|
|
|
|
HtlcBasePoint: keychain.KeyDescriptor{
|
|
|
|
PubKey: bobKeyPub,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
bobRoot, err := chainhash.NewHash(bobKeyPriv.Serialize())
|
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2017-07-31 00:09:10 +03:00
|
|
|
}
|
2018-02-18 02:29:01 +03:00
|
|
|
bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot)
|
2017-05-02 02:29:30 +03:00
|
|
|
bobFirstRevoke, err := bobPreimageProducer.AtIndex(0)
|
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
2019-01-16 17:47:43 +03:00
|
|
|
bobCommitPoint := input.ComputeCommitmentPoint(bobFirstRevoke[:])
|
2017-05-02 02:29:30 +03:00
|
|
|
|
2018-02-18 02:29:01 +03:00
|
|
|
aliceRoot, err := chainhash.NewHash(aliceKeyPriv.Serialize())
|
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2018-02-18 02:29:01 +03:00
|
|
|
}
|
|
|
|
alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot)
|
2017-05-02 02:29:30 +03:00
|
|
|
aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0)
|
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
2019-01-16 17:47:43 +03:00
|
|
|
aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:])
|
2017-07-31 00:09:10 +03:00
|
|
|
|
2019-08-01 06:10:45 +03:00
|
|
|
aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(
|
|
|
|
aliceAmount, bobAmount, &aliceCfg, &bobCfg, aliceCommitPoint,
|
2020-01-06 13:42:04 +03:00
|
|
|
bobCommitPoint, *fundingTxIn, channeldb.SingleFunderTweaklessBit,
|
2019-08-01 06:10:45 +03:00
|
|
|
)
|
2017-05-02 02:29:30 +03:00
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
alicePath, err := ioutil.TempDir("", "alicedb")
|
2019-09-13 05:59:07 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, err
|
|
|
|
}
|
|
|
|
|
2017-05-02 02:29:30 +03:00
|
|
|
dbAlice, err := channeldb.Open(alicePath)
|
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bobPath, err := ioutil.TempDir("", "bobdb")
|
2019-09-13 05:59:07 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, err
|
|
|
|
}
|
|
|
|
|
2017-05-02 02:29:30 +03:00
|
|
|
dbBob, err := channeldb.Open(bobPath)
|
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
2019-10-31 05:43:05 +03:00
|
|
|
estimator := chainfee.NewStaticEstimator(6000, 0)
|
2018-07-28 04:20:58 +03:00
|
|
|
feePerKw, err := estimator.EstimateFeePerKW(1)
|
2017-11-23 10:12:53 +03:00
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2017-11-23 10:12:53 +03:00
|
|
|
}
|
2018-02-13 16:57:47 +03:00
|
|
|
commitFee := feePerKw.FeeForWeight(724)
|
2017-07-31 00:09:10 +03:00
|
|
|
|
2017-07-09 02:30:20 +03:00
|
|
|
const broadcastHeight = 1
|
|
|
|
bobAddr := &net.TCPAddr{
|
|
|
|
IP: net.ParseIP("127.0.0.1"),
|
|
|
|
Port: 18555,
|
|
|
|
}
|
|
|
|
|
|
|
|
aliceAddr := &net.TCPAddr{
|
|
|
|
IP: net.ParseIP("127.0.0.1"),
|
|
|
|
Port: 18556,
|
|
|
|
}
|
|
|
|
|
2017-11-11 02:15:19 +03:00
|
|
|
aliceCommit := channeldb.ChannelCommitment{
|
|
|
|
CommitHeight: 0,
|
|
|
|
LocalBalance: lnwire.NewMSatFromSatoshis(aliceAmount - commitFee),
|
|
|
|
RemoteBalance: lnwire.NewMSatFromSatoshis(bobAmount),
|
|
|
|
CommitFee: commitFee,
|
2018-02-13 16:57:47 +03:00
|
|
|
FeePerKw: btcutil.Amount(feePerKw),
|
2017-11-11 02:15:19 +03:00
|
|
|
CommitTx: aliceCommitTx,
|
|
|
|
CommitSig: bytes.Repeat([]byte{1}, 71),
|
|
|
|
}
|
|
|
|
bobCommit := channeldb.ChannelCommitment{
|
|
|
|
CommitHeight: 0,
|
|
|
|
LocalBalance: lnwire.NewMSatFromSatoshis(bobAmount),
|
|
|
|
RemoteBalance: lnwire.NewMSatFromSatoshis(aliceAmount - commitFee),
|
|
|
|
CommitFee: commitFee,
|
2018-02-13 16:57:47 +03:00
|
|
|
FeePerKw: btcutil.Amount(feePerKw),
|
2017-11-11 02:15:19 +03:00
|
|
|
CommitTx: bobCommitTx,
|
|
|
|
CommitSig: bytes.Repeat([]byte{1}, 71),
|
|
|
|
}
|
|
|
|
|
2017-05-02 02:29:30 +03:00
|
|
|
aliceChannelState := &channeldb.OpenChannel{
|
2017-07-31 00:09:10 +03:00
|
|
|
LocalChanCfg: aliceCfg,
|
|
|
|
RemoteChanCfg: bobCfg,
|
|
|
|
IdentityPub: aliceKeyPub,
|
|
|
|
FundingOutpoint: *prevOut,
|
2019-10-31 05:24:49 +03:00
|
|
|
ChanType: channeldb.SingleFunderTweaklessBit,
|
2017-07-31 00:09:10 +03:00
|
|
|
IsInitiator: true,
|
|
|
|
Capacity: channelCapacity,
|
|
|
|
RemoteCurrentRevocation: bobCommitPoint,
|
|
|
|
RevocationProducer: alicePreimageProducer,
|
|
|
|
RevocationStore: shachain.NewRevocationStore(),
|
2017-11-11 02:15:19 +03:00
|
|
|
LocalCommitment: aliceCommit,
|
|
|
|
RemoteCommitment: aliceCommit,
|
2018-05-02 02:27:20 +03:00
|
|
|
ShortChannelID: chanID,
|
2017-07-31 00:09:10 +03:00
|
|
|
Db: dbAlice,
|
2018-02-28 06:32:36 +03:00
|
|
|
Packager: channeldb.NewChannelPackager(chanID),
|
2018-03-11 04:27:51 +03:00
|
|
|
FundingTxn: testTx,
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
2017-07-09 02:30:20 +03:00
|
|
|
|
2017-05-02 02:29:30 +03:00
|
|
|
bobChannelState := &channeldb.OpenChannel{
|
2017-07-31 00:09:10 +03:00
|
|
|
LocalChanCfg: bobCfg,
|
|
|
|
RemoteChanCfg: aliceCfg,
|
|
|
|
IdentityPub: bobKeyPub,
|
|
|
|
FundingOutpoint: *prevOut,
|
2019-10-31 05:24:49 +03:00
|
|
|
ChanType: channeldb.SingleFunderTweaklessBit,
|
2017-07-31 00:09:10 +03:00
|
|
|
IsInitiator: false,
|
|
|
|
Capacity: channelCapacity,
|
|
|
|
RemoteCurrentRevocation: aliceCommitPoint,
|
|
|
|
RevocationProducer: bobPreimageProducer,
|
|
|
|
RevocationStore: shachain.NewRevocationStore(),
|
2017-11-11 02:15:19 +03:00
|
|
|
LocalCommitment: bobCommit,
|
|
|
|
RemoteCommitment: bobCommit,
|
2018-05-02 02:27:20 +03:00
|
|
|
ShortChannelID: chanID,
|
2017-07-31 00:09:10 +03:00
|
|
|
Db: dbBob,
|
2018-02-28 06:32:36 +03:00
|
|
|
Packager: channeldb.NewChannelPackager(chanID),
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
2017-11-11 02:15:19 +03:00
|
|
|
if err := aliceChannelState.SyncPending(bobAddr, broadcastHeight); err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2017-11-11 02:15:19 +03:00
|
|
|
}
|
|
|
|
|
2017-07-09 02:30:20 +03:00
|
|
|
if err := bobChannelState.SyncPending(aliceAddr, broadcastHeight); err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2017-07-09 02:30:20 +03:00
|
|
|
}
|
|
|
|
|
2017-05-02 02:29:30 +03:00
|
|
|
cleanUpFunc := func() {
|
2018-01-16 11:39:25 +03:00
|
|
|
dbAlice.Close()
|
|
|
|
dbBob.Close()
|
2017-05-02 02:29:30 +03:00
|
|
|
os.RemoveAll(bobPath)
|
|
|
|
os.RemoveAll(alicePath)
|
|
|
|
}
|
|
|
|
|
2020-08-26 21:18:02 +03:00
|
|
|
aliceSigner := &mock.SingleSigner{Privkey: aliceKeyPriv}
|
|
|
|
bobSigner := &mock.SingleSigner{Privkey: bobKeyPriv}
|
2017-05-02 02:29:30 +03:00
|
|
|
|
multi: replace per channel sigPool with global daemon level sigPool
In this commit, we remove the per channel `sigPool` within the
`lnwallet.LightningChannel` struct. With this change, we ensure that as
the number of channels grows, the number of gouroutines idling in the
sigPool stays constant. It's the case that currently on the daemon, most
channels are likely inactive, with only a hand full actually
consistently carrying out channel updates. As a result, this change
should reduce the amount of idle CPU usage, as we have less active
goroutines in select loops.
In order to make this change, the `SigPool` itself has been publicly
exported such that outside callers can make a `SigPool` and pass it into
newly created channels. Since the sig pool now lives outside the
channel, we were also able to do away with the Stop() method on the
channel all together.
Finally, the server is the sub-system that is currently responsible for
managing the `SigPool` within lnd.
2018-12-15 03:35:07 +03:00
|
|
|
alicePool := lnwallet.NewSigPool(runtime.NumCPU(), aliceSigner)
|
2018-01-17 07:18:53 +03:00
|
|
|
channelAlice, err := lnwallet.NewLightningChannel(
|
2019-04-15 15:24:43 +03:00
|
|
|
aliceSigner, aliceChannelState, alicePool,
|
2018-01-17 07:18:53 +03:00
|
|
|
)
|
2017-05-02 02:29:30 +03:00
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
multi: replace per channel sigPool with global daemon level sigPool
In this commit, we remove the per channel `sigPool` within the
`lnwallet.LightningChannel` struct. With this change, we ensure that as
the number of channels grows, the number of gouroutines idling in the
sigPool stays constant. It's the case that currently on the daemon, most
channels are likely inactive, with only a hand full actually
consistently carrying out channel updates. As a result, this change
should reduce the amount of idle CPU usage, as we have less active
goroutines in select loops.
In order to make this change, the `SigPool` itself has been publicly
exported such that outside callers can make a `SigPool` and pass it into
newly created channels. Since the sig pool now lives outside the
channel, we were also able to do away with the Stop() method on the
channel all together.
Finally, the server is the sub-system that is currently responsible for
managing the `SigPool` within lnd.
2018-12-15 03:35:07 +03:00
|
|
|
alicePool.Start()
|
|
|
|
|
|
|
|
bobPool := lnwallet.NewSigPool(runtime.NumCPU(), bobSigner)
|
2018-01-17 07:18:53 +03:00
|
|
|
channelBob, err := lnwallet.NewLightningChannel(
|
2019-04-15 15:24:43 +03:00
|
|
|
bobSigner, bobChannelState, bobPool,
|
2018-01-17 07:18:53 +03:00
|
|
|
)
|
2017-05-02 02:29:30 +03:00
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
multi: replace per channel sigPool with global daemon level sigPool
In this commit, we remove the per channel `sigPool` within the
`lnwallet.LightningChannel` struct. With this change, we ensure that as
the number of channels grows, the number of gouroutines idling in the
sigPool stays constant. It's the case that currently on the daemon, most
channels are likely inactive, with only a hand full actually
consistently carrying out channel updates. As a result, this change
should reduce the amount of idle CPU usage, as we have less active
goroutines in select loops.
In order to make this change, the `SigPool` itself has been publicly
exported such that outside callers can make a `SigPool` and pass it into
newly created channels. Since the sig pool now lives outside the
channel, we were also able to do away with the Stop() method on the
channel all together.
Finally, the server is the sub-system that is currently responsible for
managing the `SigPool` within lnd.
2018-12-15 03:35:07 +03:00
|
|
|
bobPool.Start()
|
2017-05-02 02:29:30 +03:00
|
|
|
|
2017-07-31 00:09:10 +03:00
|
|
|
// Now that the channel are open, simulate the start of a session by
|
|
|
|
// having Alice and Bob extend their revocation windows to each other.
|
2017-07-31 04:22:38 +03:00
|
|
|
aliceNextRevoke, err := channelAlice.NextRevocationKey()
|
2017-07-31 00:09:10 +03:00
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2017-07-31 00:09:10 +03:00
|
|
|
}
|
|
|
|
if err := channelBob.InitNextRevocation(aliceNextRevoke); err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2017-07-31 00:09:10 +03:00
|
|
|
}
|
|
|
|
|
2017-07-31 04:22:38 +03:00
|
|
|
bobNextRevoke, err := channelBob.NextRevocationKey()
|
2017-07-31 00:09:10 +03:00
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2017-07-31 00:09:10 +03:00
|
|
|
}
|
|
|
|
if err := channelAlice.InitNextRevocation(bobNextRevoke); err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, nil, nil, err
|
2017-07-09 02:30:20 +03:00
|
|
|
}
|
|
|
|
|
2019-04-08 12:29:18 +03:00
|
|
|
restoreAlice := func() (*lnwallet.LightningChannel, error) {
|
2017-07-09 02:30:20 +03:00
|
|
|
aliceStoredChannels, err := dbAlice.FetchOpenChannels(aliceKeyPub)
|
2018-01-16 11:39:25 +03:00
|
|
|
switch err {
|
|
|
|
case nil:
|
2020-01-10 05:44:27 +03:00
|
|
|
case kvdb.ErrDatabaseNotOpen:
|
2018-01-16 11:39:25 +03:00
|
|
|
dbAlice, err = channeldb.Open(dbAlice.Path())
|
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, errors.Errorf("unable to reopen alice "+
|
2018-01-16 11:39:25 +03:00
|
|
|
"db: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
aliceStoredChannels, err = dbAlice.FetchOpenChannels(aliceKeyPub)
|
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, errors.Errorf("unable to fetch alice "+
|
2018-01-16 11:39:25 +03:00
|
|
|
"channel: %v", err)
|
|
|
|
}
|
|
|
|
default:
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, errors.Errorf("unable to fetch alice channel: "+
|
2017-07-09 02:30:20 +03:00
|
|
|
"%v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var aliceStoredChannel *channeldb.OpenChannel
|
|
|
|
for _, channel := range aliceStoredChannels {
|
|
|
|
if channel.FundingOutpoint.String() == prevOut.String() {
|
|
|
|
aliceStoredChannel = channel
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if aliceStoredChannel == nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, errors.New("unable to find stored alice channel")
|
2017-07-09 02:30:20 +03:00
|
|
|
}
|
|
|
|
|
multi: replace per channel sigPool with global daemon level sigPool
In this commit, we remove the per channel `sigPool` within the
`lnwallet.LightningChannel` struct. With this change, we ensure that as
the number of channels grows, the number of gouroutines idling in the
sigPool stays constant. It's the case that currently on the daemon, most
channels are likely inactive, with only a hand full actually
consistently carrying out channel updates. As a result, this change
should reduce the amount of idle CPU usage, as we have less active
goroutines in select loops.
In order to make this change, the `SigPool` itself has been publicly
exported such that outside callers can make a `SigPool` and pass it into
newly created channels. Since the sig pool now lives outside the
channel, we were also able to do away with the Stop() method on the
channel all together.
Finally, the server is the sub-system that is currently responsible for
managing the `SigPool` within lnd.
2018-12-15 03:35:07 +03:00
|
|
|
newAliceChannel, err := lnwallet.NewLightningChannel(
|
2019-04-15 15:24:43 +03:00
|
|
|
aliceSigner, aliceStoredChannel, alicePool,
|
multi: replace per channel sigPool with global daemon level sigPool
In this commit, we remove the per channel `sigPool` within the
`lnwallet.LightningChannel` struct. With this change, we ensure that as
the number of channels grows, the number of gouroutines idling in the
sigPool stays constant. It's the case that currently on the daemon, most
channels are likely inactive, with only a hand full actually
consistently carrying out channel updates. As a result, this change
should reduce the amount of idle CPU usage, as we have less active
goroutines in select loops.
In order to make this change, the `SigPool` itself has been publicly
exported such that outside callers can make a `SigPool` and pass it into
newly created channels. Since the sig pool now lives outside the
channel, we were also able to do away with the Stop() method on the
channel all together.
Finally, the server is the sub-system that is currently responsible for
managing the `SigPool` within lnd.
2018-12-15 03:35:07 +03:00
|
|
|
)
|
2017-07-09 02:30:20 +03:00
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, errors.Errorf("unable to create new channel: %v",
|
2017-07-09 02:30:20 +03:00
|
|
|
err)
|
|
|
|
}
|
|
|
|
|
2019-04-08 12:29:18 +03:00
|
|
|
return newAliceChannel, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
restoreBob := func() (*lnwallet.LightningChannel, error) {
|
2017-07-09 02:30:20 +03:00
|
|
|
bobStoredChannels, err := dbBob.FetchOpenChannels(bobKeyPub)
|
2018-01-16 11:39:25 +03:00
|
|
|
switch err {
|
|
|
|
case nil:
|
2020-01-10 05:44:27 +03:00
|
|
|
case kvdb.ErrDatabaseNotOpen:
|
2018-01-16 11:39:25 +03:00
|
|
|
dbBob, err = channeldb.Open(dbBob.Path())
|
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, errors.Errorf("unable to reopen bob "+
|
2018-01-16 11:39:25 +03:00
|
|
|
"db: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
bobStoredChannels, err = dbBob.FetchOpenChannels(bobKeyPub)
|
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, errors.Errorf("unable to fetch bob "+
|
2018-01-16 11:39:25 +03:00
|
|
|
"channel: %v", err)
|
|
|
|
}
|
|
|
|
default:
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, errors.Errorf("unable to fetch bob channel: "+
|
2017-07-09 02:30:20 +03:00
|
|
|
"%v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var bobStoredChannel *channeldb.OpenChannel
|
|
|
|
for _, channel := range bobStoredChannels {
|
|
|
|
if channel.FundingOutpoint.String() == prevOut.String() {
|
|
|
|
bobStoredChannel = channel
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if bobStoredChannel == nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, errors.New("unable to find stored bob channel")
|
2017-07-09 02:30:20 +03:00
|
|
|
}
|
|
|
|
|
multi: replace per channel sigPool with global daemon level sigPool
In this commit, we remove the per channel `sigPool` within the
`lnwallet.LightningChannel` struct. With this change, we ensure that as
the number of channels grows, the number of gouroutines idling in the
sigPool stays constant. It's the case that currently on the daemon, most
channels are likely inactive, with only a hand full actually
consistently carrying out channel updates. As a result, this change
should reduce the amount of idle CPU usage, as we have less active
goroutines in select loops.
In order to make this change, the `SigPool` itself has been publicly
exported such that outside callers can make a `SigPool` and pass it into
newly created channels. Since the sig pool now lives outside the
channel, we were also able to do away with the Stop() method on the
channel all together.
Finally, the server is the sub-system that is currently responsible for
managing the `SigPool` within lnd.
2018-12-15 03:35:07 +03:00
|
|
|
newBobChannel, err := lnwallet.NewLightningChannel(
|
2019-04-15 15:24:43 +03:00
|
|
|
bobSigner, bobStoredChannel, bobPool,
|
multi: replace per channel sigPool with global daemon level sigPool
In this commit, we remove the per channel `sigPool` within the
`lnwallet.LightningChannel` struct. With this change, we ensure that as
the number of channels grows, the number of gouroutines idling in the
sigPool stays constant. It's the case that currently on the daemon, most
channels are likely inactive, with only a hand full actually
consistently carrying out channel updates. As a result, this change
should reduce the amount of idle CPU usage, as we have less active
goroutines in select loops.
In order to make this change, the `SigPool` itself has been publicly
exported such that outside callers can make a `SigPool` and pass it into
newly created channels. Since the sig pool now lives outside the
channel, we were also able to do away with the Stop() method on the
channel all together.
Finally, the server is the sub-system that is currently responsible for
managing the `SigPool` within lnd.
2018-12-15 03:35:07 +03:00
|
|
|
)
|
2017-07-09 02:30:20 +03:00
|
|
|
if err != nil {
|
2019-04-08 12:29:18 +03:00
|
|
|
return nil, errors.Errorf("unable to create new channel: %v",
|
2017-07-09 02:30:20 +03:00
|
|
|
err)
|
|
|
|
}
|
2019-04-08 12:29:18 +03:00
|
|
|
return newBobChannel, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
testLightningChannelAlice := &testLightningChannel{
|
|
|
|
channel: channelAlice,
|
|
|
|
restore: restoreAlice,
|
|
|
|
}
|
|
|
|
|
|
|
|
testLightningChannelBob := &testLightningChannel{
|
|
|
|
channel: channelBob,
|
|
|
|
restore: restoreBob,
|
2017-07-31 00:09:10 +03:00
|
|
|
}
|
|
|
|
|
2019-04-08 12:29:18 +03:00
|
|
|
return testLightningChannelAlice, testLightningChannelBob, cleanUpFunc,
|
|
|
|
nil
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
multi: replace per channel sigPool with global daemon level sigPool
In this commit, we remove the per channel `sigPool` within the
`lnwallet.LightningChannel` struct. With this change, we ensure that as
the number of channels grows, the number of gouroutines idling in the
sigPool stays constant. It's the case that currently on the daemon, most
channels are likely inactive, with only a hand full actually
consistently carrying out channel updates. As a result, this change
should reduce the amount of idle CPU usage, as we have less active
goroutines in select loops.
In order to make this change, the `SigPool` itself has been publicly
exported such that outside callers can make a `SigPool` and pass it into
newly created channels. Since the sig pool now lives outside the
channel, we were also able to do away with the Stop() method on the
channel all together.
Finally, the server is the sub-system that is currently responsible for
managing the `SigPool` within lnd.
2018-12-15 03:35:07 +03:00
|
|
|
// getChanID retrieves the channel point from an lnnwire message.
|
2017-07-09 01:46:27 +03:00
|
|
|
func getChanID(msg lnwire.Message) (lnwire.ChannelID, error) {
|
|
|
|
var chanID lnwire.ChannelID
|
2017-05-02 02:29:30 +03:00
|
|
|
switch msg := msg.(type) {
|
|
|
|
case *lnwire.UpdateAddHTLC:
|
2017-07-09 01:46:27 +03:00
|
|
|
chanID = msg.ChanID
|
2018-02-07 06:11:11 +03:00
|
|
|
case *lnwire.UpdateFulfillHTLC:
|
2017-07-09 01:46:27 +03:00
|
|
|
chanID = msg.ChanID
|
2017-05-02 02:29:30 +03:00
|
|
|
case *lnwire.UpdateFailHTLC:
|
2017-07-09 01:46:27 +03:00
|
|
|
chanID = msg.ChanID
|
2017-05-02 02:29:30 +03:00
|
|
|
case *lnwire.RevokeAndAck:
|
2017-07-09 01:46:27 +03:00
|
|
|
chanID = msg.ChanID
|
2017-05-02 02:29:30 +03:00
|
|
|
case *lnwire.CommitSig:
|
2017-07-09 01:46:27 +03:00
|
|
|
chanID = msg.ChanID
|
|
|
|
case *lnwire.ChannelReestablish:
|
|
|
|
chanID = msg.ChanID
|
2017-11-11 02:17:33 +03:00
|
|
|
case *lnwire.FundingLocked:
|
|
|
|
chanID = msg.ChanID
|
2017-11-24 07:31:45 +03:00
|
|
|
case *lnwire.UpdateFee:
|
|
|
|
chanID = msg.ChanID
|
2017-07-09 01:46:27 +03:00
|
|
|
default:
|
2017-11-11 02:17:33 +03:00
|
|
|
return chanID, fmt.Errorf("unknown type: %T", msg)
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
2017-07-09 01:46:27 +03:00
|
|
|
return chanID, nil
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
2019-02-10 23:03:23 +03:00
|
|
|
// generateHoldPayment generates the htlc add request by given path blob and
|
2017-05-02 02:29:30 +03:00
|
|
|
// invoice which should be added by destination peer.
|
2019-02-10 23:03:23 +03:00
|
|
|
func generatePaymentWithPreimage(invoiceAmt, htlcAmt lnwire.MilliSatoshi,
|
|
|
|
timelock uint32, blob [lnwire.OnionPacketSize]byte,
|
2020-04-08 14:47:10 +03:00
|
|
|
preimage *lntypes.Preimage, rhash, payAddr [32]byte) (
|
|
|
|
*channeldb.Invoice, *lnwire.UpdateAddHTLC, uint64, error) {
|
2017-05-02 02:29:30 +03:00
|
|
|
|
2019-04-05 12:13:52 +03:00
|
|
|
// Create the db invoice. Normally the payment requests needs to be set,
|
|
|
|
// because it is decoded in InvoiceRegistry to obtain the cltv expiry.
|
|
|
|
// But because the mock registry used in tests is mocking the decode
|
|
|
|
// step and always returning the value of testInvoiceCltvExpiry, we
|
|
|
|
// don't need to bother here with creating and signing a payment
|
|
|
|
// request.
|
2020-04-08 14:47:10 +03:00
|
|
|
|
2017-06-17 01:03:30 +03:00
|
|
|
invoice := &channeldb.Invoice{
|
|
|
|
CreationDate: time.Now(),
|
|
|
|
Terms: channeldb.ContractTerm{
|
2019-11-22 13:25:02 +03:00
|
|
|
FinalCltvDelta: testInvoiceCltvExpiry,
|
2017-06-17 01:03:30 +03:00
|
|
|
Value: invoiceAmt,
|
|
|
|
PaymentPreimage: preimage,
|
2020-05-22 01:37:39 +03:00
|
|
|
PaymentAddr: payAddr,
|
2019-11-22 13:24:28 +03:00
|
|
|
Features: lnwire.NewFeatureVector(
|
|
|
|
nil, lnwire.Features,
|
|
|
|
),
|
2017-05-02 02:29:30 +03:00
|
|
|
},
|
2020-04-08 14:47:10 +03:00
|
|
|
HodlInvoice: preimage == nil,
|
2017-06-17 01:03:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
htlc := &lnwire.UpdateAddHTLC{
|
|
|
|
PaymentHash: rhash,
|
|
|
|
Amount: htlcAmt,
|
|
|
|
Expiry: timelock,
|
|
|
|
OnionBlob: blob,
|
|
|
|
}
|
|
|
|
|
2019-05-16 16:27:28 +03:00
|
|
|
pid, err := generateRandomBytes(8)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, 0, err
|
|
|
|
}
|
|
|
|
paymentID := binary.BigEndian.Uint64(pid)
|
|
|
|
|
|
|
|
return invoice, htlc, paymentID, nil
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
2019-02-10 23:03:23 +03:00
|
|
|
// generatePayment generates the htlc add request by given path blob and
|
|
|
|
// invoice which should be added by destination peer.
|
|
|
|
func generatePayment(invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32,
|
2019-05-16 16:27:28 +03:00
|
|
|
blob [lnwire.OnionPacketSize]byte) (*channeldb.Invoice,
|
|
|
|
*lnwire.UpdateAddHTLC, uint64, error) {
|
2019-02-10 23:03:23 +03:00
|
|
|
|
2020-04-08 14:47:10 +03:00
|
|
|
var preimage lntypes.Preimage
|
2019-02-10 23:03:23 +03:00
|
|
|
r, err := generateRandomBytes(sha256.Size)
|
|
|
|
if err != nil {
|
2019-05-16 16:27:28 +03:00
|
|
|
return nil, nil, 0, err
|
2019-02-10 23:03:23 +03:00
|
|
|
}
|
|
|
|
copy(preimage[:], r)
|
|
|
|
|
2020-05-20 12:07:38 +03:00
|
|
|
rhash := sha256.Sum256(preimage[:])
|
2020-05-22 01:37:39 +03:00
|
|
|
|
|
|
|
var payAddr [sha256.Size]byte
|
|
|
|
r, err = generateRandomBytes(sha256.Size)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, 0, err
|
|
|
|
}
|
|
|
|
copy(payAddr[:], r)
|
|
|
|
|
2019-02-10 23:03:23 +03:00
|
|
|
return generatePaymentWithPreimage(
|
2020-04-08 14:47:10 +03:00
|
|
|
invoiceAmt, htlcAmt, timelock, blob, &preimage, rhash, payAddr,
|
2019-02-10 23:03:23 +03:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2017-05-02 02:29:30 +03:00
|
|
|
// generateRoute generates the path blob by given array of peers.
|
2019-11-05 02:10:15 +03:00
|
|
|
func generateRoute(hops ...*hop.Payload) (
|
2019-08-31 00:11:20 +03:00
|
|
|
[lnwire.OnionPacketSize]byte, error) {
|
|
|
|
|
2017-05-02 02:29:30 +03:00
|
|
|
var blob [lnwire.OnionPacketSize]byte
|
2017-06-17 01:03:30 +03:00
|
|
|
if len(hops) == 0 {
|
|
|
|
return blob, errors.New("empty path")
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
iterator := newMockHopIterator(hops...)
|
2017-06-17 01:03:30 +03:00
|
|
|
|
|
|
|
w := bytes.NewBuffer(blob[0:0])
|
|
|
|
if err := iterator.EncodeNextHop(w); err != nil {
|
|
|
|
return blob, err
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
2017-06-17 01:03:30 +03:00
|
|
|
return blob, nil
|
2017-05-02 02:29:30 +03:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// threeHopNetwork is used for managing the created cluster of 3 hops.
|
|
|
|
type threeHopNetwork struct {
|
2019-05-01 04:28:39 +03:00
|
|
|
aliceServer *mockServer
|
|
|
|
aliceChannelLink *channelLink
|
|
|
|
aliceOnionDecoder *mockIteratorDecoder
|
2017-05-02 02:29:30 +03:00
|
|
|
|
|
|
|
bobServer *mockServer
|
2018-06-01 06:31:40 +03:00
|
|
|
firstBobChannelLink *channelLink
|
2017-05-02 02:29:30 +03:00
|
|
|
secondBobChannelLink *channelLink
|
2019-05-01 04:28:39 +03:00
|
|
|
bobOnionDecoder *mockIteratorDecoder
|
2017-05-02 02:29:30 +03:00
|
|
|
|
2019-05-01 04:28:39 +03:00
|
|
|
carolServer *mockServer
|
|
|
|
carolChannelLink *channelLink
|
|
|
|
carolOnionDecoder *mockIteratorDecoder
|
2017-11-24 07:31:45 +03:00
|
|
|
|
2019-01-29 16:45:18 +03:00
|
|
|
hopNetwork
|
2017-06-17 01:03:30 +03:00
|
|
|
}
|
|
|
|
|
2017-06-17 01:41:42 +03:00
|
|
|
// generateHops creates the per hop payload, the total amount to be sent, and
|
2018-04-18 05:02:04 +03:00
|
|
|
// also the time lock value needed to route an HTLC with the target amount over
|
2017-06-17 01:41:42 +03:00
|
|
|
// the specified path.
|
2017-08-22 09:36:43 +03:00
|
|
|
func generateHops(payAmt lnwire.MilliSatoshi, startingHeight uint32,
|
2019-11-05 02:10:15 +03:00
|
|
|
path ...*channelLink) (lnwire.MilliSatoshi, uint32, []*hop.Payload) {
|
2017-06-17 01:03:30 +03:00
|
|
|
|
2017-08-03 07:11:31 +03:00
|
|
|
totalTimelock := startingHeight
|
2017-06-17 02:10:03 +03:00
|
|
|
runningAmt := payAmt
|
2017-06-17 01:03:30 +03:00
|
|
|
|
2019-11-05 02:10:15 +03:00
|
|
|
hops := make([]*hop.Payload, len(path))
|
2017-06-17 01:03:30 +03:00
|
|
|
for i := len(path) - 1; i >= 0; i-- {
|
2017-06-17 01:41:42 +03:00
|
|
|
// If this is the last hop, then the next hop is the special
|
|
|
|
// "exit node". Otherwise, we look to the "prior" hop.
|
2019-08-31 00:11:38 +03:00
|
|
|
nextHop := hop.Exit
|
2017-06-17 01:03:30 +03:00
|
|
|
if i != len(path)-1 {
|
|
|
|
nextHop = path[i+1].channel.ShortChanID()
|
|
|
|
}
|
|
|
|
|
2018-06-26 06:12:07 +03:00
|
|
|
var timeLock uint32
|
2017-06-17 01:41:42 +03:00
|
|
|
// If this is the last, hop, then the time lock will be their
|
2017-09-12 22:31:55 +03:00
|
|
|
// specified delta policy plus our starting height.
|
2018-06-26 06:12:07 +03:00
|
|
|
if i == len(path)-1 {
|
2019-04-05 12:13:52 +03:00
|
|
|
totalTimelock += testInvoiceCltvExpiry
|
2018-06-26 06:12:07 +03:00
|
|
|
timeLock = totalTimelock
|
|
|
|
} else {
|
|
|
|
// Otherwise, the outgoing time lock should be the
|
|
|
|
// incoming timelock minus their specified delta.
|
|
|
|
delta := path[i+1].cfg.FwrdingPolicy.TimeLockDelta
|
|
|
|
totalTimelock += delta
|
2017-06-17 01:03:30 +03:00
|
|
|
timeLock = totalTimelock - delta
|
|
|
|
}
|
|
|
|
|
2017-06-17 01:41:42 +03:00
|
|
|
// Finally, we'll need to calculate the amount to forward. For
|
|
|
|
// the last hop, it's just the payment amount.
|
2017-06-17 01:03:30 +03:00
|
|
|
amount := payAmt
|
|
|
|
if i != len(path)-1 {
|
|
|
|
prevHop := hops[i+1]
|
2019-11-05 02:10:15 +03:00
|
|
|
prevAmount := prevHop.ForwardingInfo().AmountToForward
|
2017-06-17 01:03:30 +03:00
|
|
|
|
|
|
|
fee := ExpectedFee(path[i].cfg.FwrdingPolicy, prevAmount)
|
|
|
|
runningAmt += fee
|
|
|
|
|
2017-10-25 04:31:39 +03:00
|
|
|
// Otherwise, for a node to forward an HTLC, then
|
|
|
|
// following inequality most hold true:
|
|
|
|
// * amt_in - fee >= amt_to_forward
|
|
|
|
amount = runningAmt - fee
|
2017-06-17 01:03:30 +03:00
|
|
|
}
|
|
|
|
|
2019-11-05 02:10:15 +03:00
|
|
|
var nextHopBytes [8]byte
|
|
|
|
binary.BigEndian.PutUint64(nextHopBytes[:], nextHop.ToUint64())
|
|
|
|
|
|
|
|
hops[i] = hop.NewLegacyPayload(&sphinx.HopData{
|
|
|
|
Realm: [1]byte{}, // hop.BitcoinNetwork
|
|
|
|
NextAddress: nextHopBytes,
|
|
|
|
ForwardAmount: uint64(amount),
|
|
|
|
OutgoingCltv: timeLock,
|
|
|
|
})
|
2017-06-17 01:03:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return runningAmt, totalTimelock, hops
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
2017-07-09 02:04:49 +03:00
|
|
|
type paymentResponse struct {
|
2019-01-15 13:31:22 +03:00
|
|
|
rhash lntypes.Hash
|
2017-07-09 02:30:20 +03:00
|
|
|
err chan error
|
2017-07-09 02:04:49 +03:00
|
|
|
}
|
|
|
|
|
2019-01-15 13:31:22 +03:00
|
|
|
func (r *paymentResponse) Wait(d time.Duration) (lntypes.Hash, error) {
|
2019-01-29 16:13:02 +03:00
|
|
|
return r.rhash, waitForPaymentResult(r.err, d)
|
|
|
|
}
|
|
|
|
|
|
|
|
// waitForPaymentResult waits for either an error to be received on c or a
|
|
|
|
// timeout.
|
|
|
|
func waitForPaymentResult(c chan error, d time.Duration) error {
|
2017-07-09 02:04:49 +03:00
|
|
|
select {
|
2019-01-29 16:13:02 +03:00
|
|
|
case err := <-c:
|
|
|
|
close(c)
|
|
|
|
return err
|
2017-07-09 02:04:49 +03:00
|
|
|
case <-time.After(d):
|
2019-01-29 16:13:02 +03:00
|
|
|
return errors.New("htlc was not settled in time")
|
2017-07-09 02:04:49 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-29 16:54:08 +03:00
|
|
|
// waitForPayFuncResult executes the given function and waits for a result with
|
|
|
|
// a timeout.
|
|
|
|
func waitForPayFuncResult(payFunc func() error, d time.Duration) error {
|
|
|
|
errChan := make(chan error)
|
|
|
|
go func() {
|
|
|
|
errChan <- payFunc()
|
|
|
|
}()
|
|
|
|
|
|
|
|
return waitForPaymentResult(errChan, d)
|
|
|
|
}
|
|
|
|
|
2017-05-02 02:29:30 +03:00
|
|
|
// makePayment takes the destination node and amount as input, sends the
|
|
|
|
// payment and returns the error channel to wait for error to be received and
|
|
|
|
// invoice in order to check its status after the payment finished.
|
|
|
|
//
|
|
|
|
// With this function you can send payments:
|
|
|
|
// * from Alice to Bob
|
|
|
|
// * from Alice to Carol through the Bob
|
|
|
|
// * from Alice to some another peer through the Bob
|
2019-01-29 15:28:36 +03:00
|
|
|
func makePayment(sendingPeer, receivingPeer lnpeer.Peer,
|
2019-11-05 02:10:15 +03:00
|
|
|
firstHop lnwire.ShortChannelID, hops []*hop.Payload,
|
2017-08-22 09:36:43 +03:00
|
|
|
invoiceAmt, htlcAmt lnwire.MilliSatoshi,
|
2017-07-09 02:04:49 +03:00
|
|
|
timelock uint32) *paymentResponse {
|
|
|
|
|
|
|
|
paymentErr := make(chan error, 1)
|
2019-01-15 13:31:22 +03:00
|
|
|
var rhash lntypes.Hash
|
2017-07-09 02:30:20 +03:00
|
|
|
|
2019-01-29 16:15:07 +03:00
|
|
|
invoice, payFunc, err := preparePayment(sendingPeer, receivingPeer,
|
|
|
|
firstHop, hops, invoiceAmt, htlcAmt, timelock,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
paymentErr <- err
|
|
|
|
return &paymentResponse{
|
|
|
|
rhash: rhash,
|
|
|
|
err: paymentErr,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rhash = invoice.Terms.PaymentPreimage.Hash()
|
|
|
|
|
|
|
|
// Send payment and expose err channel.
|
|
|
|
go func() {
|
|
|
|
paymentErr <- payFunc()
|
|
|
|
}()
|
|
|
|
|
|
|
|
return &paymentResponse{
|
|
|
|
rhash: rhash,
|
|
|
|
err: paymentErr,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// preparePayment creates an invoice at the receivingPeer and returns a function
|
|
|
|
// that, when called, launches the payment from the sendingPeer.
|
|
|
|
func preparePayment(sendingPeer, receivingPeer lnpeer.Peer,
|
2019-11-05 02:10:15 +03:00
|
|
|
firstHop lnwire.ShortChannelID, hops []*hop.Payload,
|
2019-01-29 16:15:07 +03:00
|
|
|
invoiceAmt, htlcAmt lnwire.MilliSatoshi,
|
|
|
|
timelock uint32) (*channeldb.Invoice, func() error, error) {
|
|
|
|
|
2017-06-17 01:03:30 +03:00
|
|
|
sender := sendingPeer.(*mockServer)
|
|
|
|
receiver := receivingPeer.(*mockServer)
|
2017-05-02 02:29:30 +03:00
|
|
|
|
|
|
|
// Generate route convert it to blob, and return next destination for
|
|
|
|
// htlc add request.
|
2017-06-17 01:03:30 +03:00
|
|
|
blob, err := generateRoute(hops...)
|
2017-05-02 02:29:30 +03:00
|
|
|
if err != nil {
|
2019-01-29 16:15:07 +03:00
|
|
|
return nil, nil, err
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate payment: invoice and htlc.
|
2019-05-16 16:27:28 +03:00
|
|
|
invoice, htlc, pid, err := generatePayment(
|
|
|
|
invoiceAmt, htlcAmt, timelock, blob,
|
|
|
|
)
|
2017-05-02 02:29:30 +03:00
|
|
|
if err != nil {
|
2019-01-29 16:15:07 +03:00
|
|
|
return nil, nil, err
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check who is last in the route and add invoice to server registry.
|
2019-02-10 23:03:23 +03:00
|
|
|
hash := invoice.Terms.PaymentPreimage.Hash()
|
|
|
|
if err := receiver.registry.AddInvoice(*invoice, hash); err != nil {
|
2019-01-29 16:15:07 +03:00
|
|
|
return nil, nil, err
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Send payment and expose err channel.
|
2019-01-29 16:15:07 +03:00
|
|
|
return invoice, func() error {
|
2019-05-16 16:27:29 +03:00
|
|
|
err := sender.htlcSwitch.SendHTLC(
|
2019-05-16 16:27:29 +03:00
|
|
|
firstHop, pid, htlc,
|
2018-07-10 04:11:25 +03:00
|
|
|
)
|
2019-05-16 16:27:29 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-05-16 16:27:29 +03:00
|
|
|
resultChan, err := sender.htlcSwitch.GetPaymentResult(
|
2019-06-07 17:42:25 +03:00
|
|
|
pid, hash, newMockDeobfuscator(),
|
2019-05-16 16:27:29 +03:00
|
|
|
)
|
2019-05-16 16:27:29 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-05-16 16:27:29 +03:00
|
|
|
result, ok := <-resultChan
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("shutting down")
|
|
|
|
}
|
|
|
|
|
2019-05-16 16:27:29 +03:00
|
|
|
if result.Error != nil {
|
|
|
|
return result.Error
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2019-01-29 16:15:07 +03:00
|
|
|
}, nil
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// start starts the three hop network alice,bob,carol servers.
|
|
|
|
func (n *threeHopNetwork) start() error {
|
|
|
|
if err := n.aliceServer.Start(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := n.bobServer.Start(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := n.carolServer.Start(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-09-19 22:46:56 +03:00
|
|
|
return waitLinksEligible(map[string]*channelLink{
|
|
|
|
"alice": n.aliceChannelLink,
|
|
|
|
"bob first": n.firstBobChannelLink,
|
|
|
|
"bob second": n.secondBobChannelLink,
|
|
|
|
"carol": n.carolChannelLink,
|
|
|
|
})
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// stop stops nodes and cleanup its databases.
|
|
|
|
func (n *threeHopNetwork) stop() {
|
|
|
|
done := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
n.aliceServer.Stop()
|
|
|
|
done <- struct{}{}
|
|
|
|
}()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
n.bobServer.Stop()
|
|
|
|
done <- struct{}{}
|
|
|
|
}()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
n.carolServer.Stop()
|
|
|
|
done <- struct{}{}
|
|
|
|
}()
|
|
|
|
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
<-done
|
|
|
|
}
|
2017-07-09 02:30:20 +03:00
|
|
|
}
|
2017-05-02 02:29:30 +03:00
|
|
|
|
2017-07-09 02:30:20 +03:00
|
|
|
type clusterChannels struct {
|
|
|
|
aliceToBob *lnwallet.LightningChannel
|
|
|
|
bobToAlice *lnwallet.LightningChannel
|
|
|
|
bobToCarol *lnwallet.LightningChannel
|
|
|
|
carolToBob *lnwallet.LightningChannel
|
|
|
|
}
|
|
|
|
|
|
|
|
// createClusterChannels creates lightning channels which are needed for
|
|
|
|
// network cluster to be initialized.
|
|
|
|
func createClusterChannels(aliceToBob, bobToCarol btcutil.Amount) (
|
|
|
|
*clusterChannels, func(), func() (*clusterChannels, error), error) {
|
|
|
|
|
2018-01-16 11:39:25 +03:00
|
|
|
_, _, firstChanID, secondChanID := genIDs()
|
2017-07-09 02:30:20 +03:00
|
|
|
|
|
|
|
// Create lightning channels between Alice<->Bob and Bob<->Carol
|
2019-04-08 12:29:18 +03:00
|
|
|
aliceChannel, firstBobChannel, cleanAliceBob, err :=
|
2018-02-08 03:45:19 +03:00
|
|
|
createTestChannel(alicePrivKey, bobPrivKey, aliceToBob,
|
|
|
|
aliceToBob, 0, 0, firstChanID)
|
2017-07-09 02:30:20 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, errors.Errorf("unable to create "+
|
|
|
|
"alice<->bob channel: %v", err)
|
|
|
|
}
|
|
|
|
|
2019-04-08 12:29:18 +03:00
|
|
|
secondBobChannel, carolChannel, cleanBobCarol, err :=
|
2018-02-08 03:45:19 +03:00
|
|
|
createTestChannel(bobPrivKey, carolPrivKey, bobToCarol,
|
|
|
|
bobToCarol, 0, 0, secondChanID)
|
2017-07-09 02:30:20 +03:00
|
|
|
if err != nil {
|
|
|
|
cleanAliceBob()
|
|
|
|
return nil, nil, nil, errors.Errorf("unable to create "+
|
|
|
|
"bob<->carol channel: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanUp := func() {
|
|
|
|
cleanAliceBob()
|
|
|
|
cleanBobCarol()
|
|
|
|
}
|
|
|
|
|
|
|
|
restoreFromDb := func() (*clusterChannels, error) {
|
2019-04-08 12:29:18 +03:00
|
|
|
|
|
|
|
a2b, err := aliceChannel.restore()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
b2a, err := firstBobChannel.restore()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
b2c, err := secondBobChannel.restore()
|
2017-07-09 02:30:20 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-04-08 12:29:18 +03:00
|
|
|
c2b, err := carolChannel.restore()
|
2017-07-09 02:30:20 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &clusterChannels{
|
|
|
|
aliceToBob: a2b,
|
|
|
|
bobToAlice: b2a,
|
|
|
|
bobToCarol: b2c,
|
|
|
|
carolToBob: c2b,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return &clusterChannels{
|
2019-04-08 12:29:18 +03:00
|
|
|
aliceToBob: aliceChannel.channel,
|
|
|
|
bobToAlice: firstBobChannel.channel,
|
|
|
|
bobToCarol: secondBobChannel.channel,
|
|
|
|
carolToBob: carolChannel.channel,
|
2017-07-09 02:30:20 +03:00
|
|
|
}, cleanUp, restoreFromDb, nil
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// newThreeHopNetwork function creates the following topology and returns the
|
|
|
|
// control object to manage this cluster:
|
|
|
|
//
|
|
|
|
// alice bob carol
|
|
|
|
// server - <-connection-> - server - - <-connection-> - - - server
|
|
|
|
// | | |
|
|
|
|
// alice htlc bob htlc carol htlc
|
|
|
|
// switch switch \ switch
|
|
|
|
// | | \ |
|
|
|
|
// | | \ |
|
|
|
|
// alice first bob second bob carol
|
|
|
|
// channel link channel link channel link channel link
|
|
|
|
//
|
2020-02-19 18:34:48 +03:00
|
|
|
// This function takes server options which can be used to apply custom
|
|
|
|
// settings to alice, bob and carol.
|
2017-11-12 02:05:09 +03:00
|
|
|
func newThreeHopNetwork(t testing.TB, aliceChannel, firstBobChannel,
|
2017-07-09 02:30:20 +03:00
|
|
|
secondBobChannel, carolChannel *lnwallet.LightningChannel,
|
2020-02-19 18:34:48 +03:00
|
|
|
startingHeight uint32, opts ...serverOption) *threeHopNetwork {
|
2017-05-02 02:29:30 +03:00
|
|
|
|
2018-01-16 11:39:25 +03:00
|
|
|
aliceDb := aliceChannel.State().Db
|
|
|
|
bobDb := firstBobChannel.State().Db
|
|
|
|
carolDb := carolChannel.State().Db
|
|
|
|
|
2019-01-29 16:45:18 +03:00
|
|
|
hopNetwork := newHopNetwork()
|
2018-06-30 02:02:59 +03:00
|
|
|
|
2017-05-02 02:29:30 +03:00
|
|
|
// Create three peers/servers.
|
2018-06-30 02:02:59 +03:00
|
|
|
aliceServer, err := newMockServer(
|
2019-01-29 16:45:18 +03:00
|
|
|
t, "alice", startingHeight, aliceDb, hopNetwork.defaultDelta,
|
2018-06-30 02:02:59 +03:00
|
|
|
)
|
2018-01-16 11:39:25 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create alice server: %v", err)
|
|
|
|
}
|
2018-06-30 02:02:59 +03:00
|
|
|
bobServer, err := newMockServer(
|
2019-01-29 16:45:18 +03:00
|
|
|
t, "bob", startingHeight, bobDb, hopNetwork.defaultDelta,
|
2018-06-30 02:02:59 +03:00
|
|
|
)
|
2018-01-16 11:39:25 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create bob server: %v", err)
|
|
|
|
}
|
2018-06-30 02:02:59 +03:00
|
|
|
carolServer, err := newMockServer(
|
2019-01-29 16:45:18 +03:00
|
|
|
t, "carol", startingHeight, carolDb, hopNetwork.defaultDelta,
|
2018-06-30 02:02:59 +03:00
|
|
|
)
|
2018-01-16 11:39:25 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create carol server: %v", err)
|
|
|
|
}
|
2017-05-02 02:29:30 +03:00
|
|
|
|
2020-02-19 18:34:48 +03:00
|
|
|
// Apply all additional functional options to the servers before
|
|
|
|
// creating any links.
|
|
|
|
for _, option := range opts {
|
|
|
|
option(aliceServer, bobServer, carolServer)
|
|
|
|
}
|
|
|
|
|
2017-11-24 07:31:45 +03:00
|
|
|
// Create mock decoder instead of sphinx one in order to mock the route
|
|
|
|
// which htlc should follow.
|
2018-01-16 11:39:25 +03:00
|
|
|
aliceDecoder := newMockIteratorDecoder()
|
|
|
|
bobDecoder := newMockIteratorDecoder()
|
|
|
|
carolDecoder := newMockIteratorDecoder()
|
2017-05-02 02:29:30 +03:00
|
|
|
|
2019-01-29 16:45:18 +03:00
|
|
|
aliceChannelLink, err := hopNetwork.createChannelLink(aliceServer,
|
|
|
|
bobServer, aliceChannel, aliceDecoder,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
2017-08-03 07:11:31 +03:00
|
|
|
}
|
2017-11-24 07:31:45 +03:00
|
|
|
|
2019-01-29 16:45:18 +03:00
|
|
|
firstBobChannelLink, err := hopNetwork.createChannelLink(bobServer,
|
|
|
|
aliceServer, firstBobChannel, bobDecoder)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
secondBobChannelLink, err := hopNetwork.createChannelLink(bobServer,
|
|
|
|
carolServer, secondBobChannel, bobDecoder)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
carolChannelLink, err := hopNetwork.createChannelLink(carolServer,
|
|
|
|
bobServer, carolChannel, carolDecoder)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &threeHopNetwork{
|
2019-05-01 04:28:39 +03:00
|
|
|
aliceServer: aliceServer,
|
|
|
|
aliceChannelLink: aliceChannelLink.(*channelLink),
|
|
|
|
aliceOnionDecoder: aliceDecoder,
|
2019-01-29 16:45:18 +03:00
|
|
|
|
|
|
|
bobServer: bobServer,
|
|
|
|
firstBobChannelLink: firstBobChannelLink.(*channelLink),
|
|
|
|
secondBobChannelLink: secondBobChannelLink.(*channelLink),
|
2019-05-01 04:28:39 +03:00
|
|
|
bobOnionDecoder: bobDecoder,
|
2019-01-29 16:45:18 +03:00
|
|
|
|
2019-05-01 04:28:39 +03:00
|
|
|
carolServer: carolServer,
|
|
|
|
carolChannelLink: carolChannelLink.(*channelLink),
|
|
|
|
carolOnionDecoder: carolDecoder,
|
2019-01-29 16:45:18 +03:00
|
|
|
|
|
|
|
hopNetwork: *hopNetwork,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-19 18:34:48 +03:00
|
|
|
// serverOption is a function which alters the three servers created for
|
|
|
|
// a three hop network to allow custom settings on each server.
|
|
|
|
type serverOption func(aliceServer, bobServer, carolServer *mockServer)
|
|
|
|
|
|
|
|
// serverOptionWithHtlcNotifier is a functional option for the creation of
|
|
|
|
// three hop network servers which allows setting of htlc notifiers.
|
|
|
|
// Note that these notifiers should be started and stopped by the calling
|
|
|
|
// function.
|
|
|
|
func serverOptionWithHtlcNotifier(alice, bob,
|
|
|
|
carol *HtlcNotifier) serverOption {
|
|
|
|
|
|
|
|
return func(aliceServer, bobServer, carolServer *mockServer) {
|
|
|
|
aliceServer.htlcSwitch.cfg.HtlcNotifier = alice
|
|
|
|
bobServer.htlcSwitch.cfg.HtlcNotifier = bob
|
|
|
|
carolServer.htlcSwitch.cfg.HtlcNotifier = carol
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// serverOptionRejectHtlc is the functional option for setting the reject
|
|
|
|
// htlc config option in each server's switch.
|
|
|
|
func serverOptionRejectHtlc(alice, bob, carol bool) serverOption {
|
|
|
|
return func(aliceServer, bobServer, carolServer *mockServer) {
|
|
|
|
aliceServer.htlcSwitch.cfg.RejectHTLC = alice
|
|
|
|
bobServer.htlcSwitch.cfg.RejectHTLC = bob
|
|
|
|
carolServer.htlcSwitch.cfg.RejectHTLC = carol
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-29 16:45:18 +03:00
|
|
|
// createTwoClusterChannels creates lightning channels which are needed for
|
|
|
|
// a 2 hop network cluster to be initialized.
|
|
|
|
func createTwoClusterChannels(aliceToBob, bobToCarol btcutil.Amount) (
|
2019-04-08 12:29:18 +03:00
|
|
|
*testLightningChannel, *testLightningChannel,
|
2019-01-29 16:45:18 +03:00
|
|
|
func(), error) {
|
|
|
|
|
|
|
|
_, _, firstChanID, _ := genIDs()
|
|
|
|
|
|
|
|
// Create lightning channels between Alice<->Bob and Bob<->Carol
|
2019-04-08 12:29:18 +03:00
|
|
|
alice, bob, cleanAliceBob, err :=
|
2019-01-29 16:45:18 +03:00
|
|
|
createTestChannel(alicePrivKey, bobPrivKey, aliceToBob,
|
|
|
|
aliceToBob, 0, 0, firstChanID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, errors.Errorf("unable to create "+
|
|
|
|
"alice<->bob channel: %v", err)
|
|
|
|
}
|
|
|
|
|
2019-04-08 12:29:18 +03:00
|
|
|
return alice, bob, cleanAliceBob, nil
|
2019-01-29 16:45:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// hopNetwork is the base struct for two and three hop networks
|
|
|
|
type hopNetwork struct {
|
|
|
|
feeEstimator *mockFeeEstimator
|
|
|
|
globalPolicy ForwardingPolicy
|
2019-09-05 14:35:39 +03:00
|
|
|
obfuscator hop.ErrorEncrypter
|
2019-01-29 16:45:18 +03:00
|
|
|
|
|
|
|
defaultDelta uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
func newHopNetwork() *hopNetwork {
|
|
|
|
defaultDelta := uint32(6)
|
2018-05-11 00:40:29 +03:00
|
|
|
|
2017-06-17 01:03:30 +03:00
|
|
|
globalPolicy := ForwardingPolicy{
|
2019-11-15 12:09:27 +03:00
|
|
|
MinHTLCOut: lnwire.NewMSatFromSatoshis(5),
|
2017-08-22 09:36:43 +03:00
|
|
|
BaseFee: lnwire.NewMSatFromSatoshis(1),
|
2018-06-30 02:02:59 +03:00
|
|
|
TimeLockDelta: defaultDelta,
|
2017-06-17 01:03:30 +03:00
|
|
|
}
|
2018-01-16 11:39:25 +03:00
|
|
|
obfuscator := NewMockObfuscator()
|
2017-11-24 07:31:45 +03:00
|
|
|
|
2019-01-29 16:45:18 +03:00
|
|
|
feeEstimator := &mockFeeEstimator{
|
2019-10-31 05:43:05 +03:00
|
|
|
byteFeeIn: make(chan chainfee.SatPerKWeight),
|
2019-01-29 16:45:18 +03:00
|
|
|
quit: make(chan struct{}),
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
|
|
|
|
2019-01-29 16:45:18 +03:00
|
|
|
return &hopNetwork{
|
|
|
|
feeEstimator: feeEstimator,
|
|
|
|
globalPolicy: globalPolicy,
|
|
|
|
obfuscator: obfuscator,
|
|
|
|
defaultDelta: defaultDelta,
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
2019-01-29 16:45:18 +03:00
|
|
|
}
|
2017-05-02 02:29:30 +03:00
|
|
|
|
2019-01-29 16:45:18 +03:00
|
|
|
func (h *hopNetwork) createChannelLink(server, peer *mockServer,
|
|
|
|
channel *lnwallet.LightningChannel,
|
|
|
|
decoder *mockIteratorDecoder) (ChannelLink, error) {
|
|
|
|
|
|
|
|
const (
|
|
|
|
fwdPkgTimeout = 15 * time.Second
|
|
|
|
minFeeUpdateTimeout = 30 * time.Minute
|
|
|
|
maxFeeUpdateTimeout = 40 * time.Minute
|
2017-06-17 01:03:30 +03:00
|
|
|
)
|
2017-05-02 02:29:30 +03:00
|
|
|
|
2019-01-29 16:45:18 +03:00
|
|
|
link := NewChannelLink(
|
2017-06-17 01:03:30 +03:00
|
|
|
ChannelLinkConfig{
|
2019-01-29 16:45:18 +03:00
|
|
|
Switch: server.htlcSwitch,
|
|
|
|
FwrdingPolicy: h.globalPolicy,
|
|
|
|
Peer: peer,
|
|
|
|
Circuits: server.htlcSwitch.CircuitModifier(),
|
|
|
|
ForwardPackets: server.htlcSwitch.ForwardPackets,
|
|
|
|
DecodeHopIterators: decoder.DecodeHopIterators,
|
2018-03-12 22:40:14 +03:00
|
|
|
ExtractErrorEncrypter: func(*btcec.PublicKey) (
|
2019-09-05 14:35:39 +03:00
|
|
|
hop.ErrorEncrypter, lnwire.FailCode) {
|
2019-01-29 16:45:18 +03:00
|
|
|
return h.obfuscator, lnwire.CodeNone
|
2017-06-29 16:40:45 +03:00
|
|
|
},
|
2018-04-04 06:09:51 +03:00
|
|
|
FetchLastChannelUpdate: mockGetChanUpdateMessage,
|
2019-01-29 16:45:18 +03:00
|
|
|
Registry: server.registry,
|
|
|
|
FeeEstimator: h.feeEstimator,
|
2019-02-09 19:22:32 +03:00
|
|
|
PreimageCache: server.pCache,
|
2018-01-17 07:18:53 +03:00
|
|
|
UpdateContractSignals: func(*contractcourt.ContractSignals) error {
|
|
|
|
return nil
|
|
|
|
},
|
2019-04-03 13:18:19 +03:00
|
|
|
ChainEvents: &contractcourt.ChainEventSubscription{},
|
|
|
|
SyncStates: true,
|
|
|
|
BatchSize: 10,
|
2019-04-08 14:10:32 +03:00
|
|
|
BatchTicker: ticker.NewForce(testBatchTimeout),
|
2019-04-03 13:18:19 +03:00
|
|
|
FwdPkgGCTicker: ticker.NewForce(fwdPkgTimeout),
|
2020-04-15 08:34:16 +03:00
|
|
|
PendingCommitTicker: ticker.NewForce(2 * time.Minute),
|
2019-04-03 13:18:19 +03:00
|
|
|
MinFeeUpdateTimeout: minFeeUpdateTimeout,
|
|
|
|
MaxFeeUpdateTimeout: maxFeeUpdateTimeout,
|
|
|
|
OnChannelFailure: func(lnwire.ChannelID, lnwire.ShortChannelID, LinkFailureError) {},
|
|
|
|
OutgoingCltvRejectDelta: 3,
|
2019-07-27 04:05:58 +03:00
|
|
|
MaxOutgoingCltvExpiry: DefaultMaxOutgoingCltvExpiry,
|
2019-08-24 02:04:59 +03:00
|
|
|
MaxFeeAllocation: DefaultMaxLinkFeeAllocation,
|
2020-03-07 06:42:58 +03:00
|
|
|
NotifyActiveLink: func(wire.OutPoint) {},
|
2019-09-19 22:46:44 +03:00
|
|
|
NotifyActiveChannel: func(wire.OutPoint) {},
|
|
|
|
NotifyInactiveChannel: func(wire.OutPoint) {},
|
2020-02-19 18:34:47 +03:00
|
|
|
HtlcNotifier: server.htlcSwitch.cfg.HtlcNotifier,
|
2017-06-17 01:03:30 +03:00
|
|
|
},
|
2019-01-29 16:45:18 +03:00
|
|
|
channel,
|
2017-06-17 01:03:30 +03:00
|
|
|
)
|
2019-01-29 16:45:18 +03:00
|
|
|
if err := server.htlcSwitch.AddLink(link); err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to add channel link: %v", err)
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
2019-09-19 22:46:56 +03:00
|
|
|
|
2018-01-17 07:18:53 +03:00
|
|
|
go func() {
|
|
|
|
for {
|
2018-01-16 23:23:56 +03:00
|
|
|
select {
|
2019-01-29 16:45:18 +03:00
|
|
|
case <-link.(*channelLink).htlcUpdates:
|
|
|
|
case <-link.(*channelLink).quit:
|
2018-01-16 23:23:56 +03:00
|
|
|
return
|
|
|
|
}
|
2018-01-17 07:18:53 +03:00
|
|
|
}
|
|
|
|
}()
|
2017-05-02 02:29:30 +03:00
|
|
|
|
2019-01-29 16:45:18 +03:00
|
|
|
return link, nil
|
2017-05-02 02:29:30 +03:00
|
|
|
}
|
2019-01-29 16:46:59 +03:00
|
|
|
|
|
|
|
// twoHopNetwork is used for managing the created cluster of 2 hops.
|
|
|
|
type twoHopNetwork struct {
|
|
|
|
hopNetwork
|
|
|
|
|
|
|
|
aliceServer *mockServer
|
|
|
|
aliceChannelLink *channelLink
|
|
|
|
|
|
|
|
bobServer *mockServer
|
|
|
|
bobChannelLink *channelLink
|
|
|
|
}
|
|
|
|
|
|
|
|
// newTwoHopNetwork function creates the following topology and returns the
|
|
|
|
// control object to manage this cluster:
|
|
|
|
//
|
|
|
|
// alice bob
|
|
|
|
// server - <-connection-> - server
|
|
|
|
// | |
|
|
|
|
// alice htlc bob htlc
|
|
|
|
// switch switch
|
|
|
|
// | |
|
|
|
|
// | |
|
|
|
|
// alice bob
|
|
|
|
// channel link channel link
|
|
|
|
//
|
|
|
|
func newTwoHopNetwork(t testing.TB,
|
|
|
|
aliceChannel, bobChannel *lnwallet.LightningChannel,
|
|
|
|
startingHeight uint32) *twoHopNetwork {
|
|
|
|
|
|
|
|
aliceDb := aliceChannel.State().Db
|
|
|
|
bobDb := bobChannel.State().Db
|
|
|
|
|
|
|
|
hopNetwork := newHopNetwork()
|
|
|
|
|
|
|
|
// Create two peers/servers.
|
|
|
|
aliceServer, err := newMockServer(
|
|
|
|
t, "alice", startingHeight, aliceDb, hopNetwork.defaultDelta,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create alice server: %v", err)
|
|
|
|
}
|
|
|
|
bobServer, err := newMockServer(
|
|
|
|
t, "bob", startingHeight, bobDb, hopNetwork.defaultDelta,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create bob server: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create mock decoder instead of sphinx one in order to mock the route
|
|
|
|
// which htlc should follow.
|
|
|
|
aliceDecoder := newMockIteratorDecoder()
|
|
|
|
bobDecoder := newMockIteratorDecoder()
|
|
|
|
|
|
|
|
aliceChannelLink, err := hopNetwork.createChannelLink(
|
|
|
|
aliceServer, bobServer, aliceChannel, aliceDecoder,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
bobChannelLink, err := hopNetwork.createChannelLink(
|
|
|
|
bobServer, aliceServer, bobChannel, bobDecoder,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &twoHopNetwork{
|
|
|
|
aliceServer: aliceServer,
|
|
|
|
aliceChannelLink: aliceChannelLink.(*channelLink),
|
|
|
|
|
|
|
|
bobServer: bobServer,
|
|
|
|
bobChannelLink: bobChannelLink.(*channelLink),
|
|
|
|
|
|
|
|
hopNetwork: *hopNetwork,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-10 23:03:23 +03:00
|
|
|
// start starts the two hop network alice,bob servers.
|
2019-01-29 16:46:59 +03:00
|
|
|
func (n *twoHopNetwork) start() error {
|
|
|
|
if err := n.aliceServer.Start(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := n.bobServer.Start(); err != nil {
|
|
|
|
n.aliceServer.Stop()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-09-19 22:46:56 +03:00
|
|
|
return waitLinksEligible(map[string]*channelLink{
|
|
|
|
"alice": n.aliceChannelLink,
|
|
|
|
"bob": n.bobChannelLink,
|
|
|
|
})
|
2019-01-29 16:46:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// stop stops nodes and cleanup its databases.
|
|
|
|
func (n *twoHopNetwork) stop() {
|
|
|
|
done := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
n.aliceServer.Stop()
|
|
|
|
done <- struct{}{}
|
|
|
|
}()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
n.bobServer.Stop()
|
|
|
|
done <- struct{}{}
|
|
|
|
}()
|
|
|
|
|
|
|
|
for i := 0; i < 2; i++ {
|
|
|
|
<-done
|
|
|
|
}
|
|
|
|
}
|
2019-02-10 23:03:23 +03:00
|
|
|
|
|
|
|
func (n *twoHopNetwork) makeHoldPayment(sendingPeer, receivingPeer lnpeer.Peer,
|
2019-11-05 02:10:15 +03:00
|
|
|
firstHop lnwire.ShortChannelID, hops []*hop.Payload,
|
2019-02-10 23:03:23 +03:00
|
|
|
invoiceAmt, htlcAmt lnwire.MilliSatoshi,
|
|
|
|
timelock uint32, preimage lntypes.Preimage) chan error {
|
|
|
|
|
|
|
|
paymentErr := make(chan error, 1)
|
|
|
|
|
|
|
|
sender := sendingPeer.(*mockServer)
|
|
|
|
receiver := receivingPeer.(*mockServer)
|
|
|
|
|
|
|
|
// Generate route convert it to blob, and return next destination for
|
|
|
|
// htlc add request.
|
|
|
|
blob, err := generateRoute(hops...)
|
|
|
|
if err != nil {
|
|
|
|
paymentErr <- err
|
|
|
|
return paymentErr
|
|
|
|
}
|
|
|
|
|
|
|
|
rhash := preimage.Hash()
|
|
|
|
|
2020-05-22 01:37:39 +03:00
|
|
|
var payAddr [32]byte
|
|
|
|
if _, err := crand.Read(payAddr[:]); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2019-02-10 23:03:23 +03:00
|
|
|
// Generate payment: invoice and htlc.
|
2019-05-16 16:27:28 +03:00
|
|
|
invoice, htlc, pid, err := generatePaymentWithPreimage(
|
|
|
|
invoiceAmt, htlcAmt, timelock, blob,
|
2020-04-08 14:47:10 +03:00
|
|
|
nil, rhash, payAddr,
|
2019-05-16 16:27:28 +03:00
|
|
|
)
|
2019-02-10 23:03:23 +03:00
|
|
|
if err != nil {
|
|
|
|
paymentErr <- err
|
|
|
|
return paymentErr
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check who is last in the route and add invoice to server registry.
|
|
|
|
if err := receiver.registry.AddInvoice(*invoice, rhash); err != nil {
|
|
|
|
paymentErr <- err
|
|
|
|
return paymentErr
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send payment and expose err channel.
|
2020-04-07 21:56:22 +03:00
|
|
|
err = sender.htlcSwitch.SendHTLC(firstHop, pid, htlc)
|
|
|
|
if err != nil {
|
|
|
|
paymentErr <- err
|
|
|
|
return paymentErr
|
|
|
|
}
|
2019-05-16 16:27:29 +03:00
|
|
|
|
2020-04-07 21:56:22 +03:00
|
|
|
go func() {
|
2019-05-16 16:27:29 +03:00
|
|
|
resultChan, err := sender.htlcSwitch.GetPaymentResult(
|
2019-06-07 17:42:25 +03:00
|
|
|
pid, rhash, newMockDeobfuscator(),
|
2019-05-16 16:27:29 +03:00
|
|
|
)
|
2019-05-16 16:27:29 +03:00
|
|
|
if err != nil {
|
|
|
|
paymentErr <- err
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-05-16 16:27:29 +03:00
|
|
|
result, ok := <-resultChan
|
|
|
|
if !ok {
|
|
|
|
paymentErr <- fmt.Errorf("shutting down")
|
2020-04-07 21:54:56 +03:00
|
|
|
return
|
2019-05-16 16:27:29 +03:00
|
|
|
}
|
|
|
|
|
2019-05-16 16:27:29 +03:00
|
|
|
if result.Error != nil {
|
|
|
|
paymentErr <- result.Error
|
|
|
|
return
|
|
|
|
}
|
|
|
|
paymentErr <- nil
|
2019-02-10 23:03:23 +03:00
|
|
|
}()
|
|
|
|
|
|
|
|
return paymentErr
|
|
|
|
}
|
2019-04-08 14:10:32 +03:00
|
|
|
|
2019-09-19 22:46:56 +03:00
|
|
|
// waitLinksEligible blocks until all links the provided name-to-link map are
|
|
|
|
// eligible to forward HTLCs.
|
|
|
|
func waitLinksEligible(links map[string]*channelLink) error {
|
|
|
|
return wait.NoError(func() error {
|
|
|
|
for name, link := range links {
|
|
|
|
if link.EligibleToForward() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return fmt.Errorf("%s channel link not eligible", name)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}, 3*time.Second)
|
|
|
|
}
|
|
|
|
|
2019-04-08 14:10:32 +03:00
|
|
|
// timeout implements a test level timeout.
|
|
|
|
func timeout(t *testing.T) func() {
|
|
|
|
done := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
select {
|
2020-12-04 12:50:19 +03:00
|
|
|
case <-time.After(20 * time.Second):
|
2019-04-08 14:10:32 +03:00
|
|
|
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
|
|
|
|
|
|
|
|
panic("test timeout")
|
|
|
|
case <-done:
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
return func() {
|
|
|
|
close(done)
|
|
|
|
}
|
|
|
|
}
|