You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
430 lines
12 KiB
430 lines
12 KiB
package lnd |
|
|
|
import ( |
|
"bytes" |
|
crand "crypto/rand" |
|
"encoding/binary" |
|
"io" |
|
"io/ioutil" |
|
"math/rand" |
|
"net" |
|
"os" |
|
"time" |
|
|
|
"github.com/btcsuite/btcd/btcec" |
|
"github.com/btcsuite/btcd/chaincfg/chainhash" |
|
"github.com/btcsuite/btcd/wire" |
|
"github.com/btcsuite/btcutil" |
|
"github.com/lightningnetwork/lnd/chainntnfs" |
|
"github.com/lightningnetwork/lnd/channeldb" |
|
"github.com/lightningnetwork/lnd/contractcourt" |
|
"github.com/lightningnetwork/lnd/htlcswitch" |
|
"github.com/lightningnetwork/lnd/input" |
|
"github.com/lightningnetwork/lnd/keychain" |
|
"github.com/lightningnetwork/lnd/lnwallet" |
|
"github.com/lightningnetwork/lnd/lnwire" |
|
"github.com/lightningnetwork/lnd/netann" |
|
"github.com/lightningnetwork/lnd/shachain" |
|
"github.com/lightningnetwork/lnd/ticker" |
|
) |
|
|
|
var ( |
|
alicesPrivKey = []byte{ |
|
0x2b, 0xd8, 0x06, 0xc9, 0x7f, 0x0e, 0x00, 0xaf, |
|
0x1a, 0x1f, 0xc3, 0x32, 0x8f, 0xa7, 0x63, 0xa9, |
|
0x26, 0x97, 0x23, 0xc8, 0xdb, 0x8f, 0xac, 0x4f, |
|
0x93, 0xaf, 0x71, 0xdb, 0x18, 0x6d, 0x6e, 0x90, |
|
} |
|
|
|
bobsPrivKey = []byte{ |
|
0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda, |
|
0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17, |
|
0xd, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d, |
|
0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9, |
|
} |
|
|
|
// Use a hard-coded HD seed. |
|
testHdSeed = [32]byte{ |
|
0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab, |
|
0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4, |
|
0x4f, 0x2f, 0x6f, 0x25, 0x88, 0xa3, 0xef, 0xb9, |
|
0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53, |
|
} |
|
|
|
// Just use some arbitrary bytes as delivery script. |
|
dummyDeliveryScript = alicesPrivKey[:] |
|
|
|
// 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, |
|
} |
|
) |
|
|
|
// createTestPeer creates a channel between two nodes, and returns a peer for |
|
// one of the nodes, together with the channel seen from both nodes. |
|
func createTestPeer(notifier chainntnfs.ChainNotifier, |
|
publTx chan *wire.MsgTx) (*peer, *lnwallet.LightningChannel, |
|
*lnwallet.LightningChannel, func(), error) { |
|
|
|
aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), |
|
alicesPrivKey) |
|
bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), |
|
bobsPrivKey) |
|
|
|
channelCapacity := btcutil.Amount(10 * 1e8) |
|
channelBal := channelCapacity / 2 |
|
aliceDustLimit := btcutil.Amount(200) |
|
bobDustLimit := btcutil.Amount(1300) |
|
csvTimeoutAlice := uint32(5) |
|
csvTimeoutBob := uint32(4) |
|
|
|
prevOut := &wire.OutPoint{ |
|
Hash: chainhash.Hash(testHdSeed), |
|
Index: 0, |
|
} |
|
fundingTxIn := wire.NewTxIn(prevOut, nil, nil) |
|
|
|
aliceCfg := channeldb.ChannelConfig{ |
|
ChannelConstraints: channeldb.ChannelConstraints{ |
|
DustLimit: aliceDustLimit, |
|
MaxPendingAmount: lnwire.MilliSatoshi(rand.Int63()), |
|
ChanReserve: btcutil.Amount(rand.Int63()), |
|
MinHTLC: lnwire.MilliSatoshi(rand.Int63()), |
|
MaxAcceptedHtlcs: uint16(rand.Int31()), |
|
CsvDelay: uint16(csvTimeoutAlice), |
|
}, |
|
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, |
|
}, |
|
} |
|
bobCfg := channeldb.ChannelConfig{ |
|
ChannelConstraints: channeldb.ChannelConstraints{ |
|
DustLimit: bobDustLimit, |
|
MaxPendingAmount: lnwire.MilliSatoshi(rand.Int63()), |
|
ChanReserve: btcutil.Amount(rand.Int63()), |
|
MinHTLC: lnwire.MilliSatoshi(rand.Int63()), |
|
MaxAcceptedHtlcs: uint16(rand.Int31()), |
|
CsvDelay: uint16(csvTimeoutBob), |
|
}, |
|
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 { |
|
return nil, nil, nil, nil, err |
|
} |
|
bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot) |
|
bobFirstRevoke, err := bobPreimageProducer.AtIndex(0) |
|
if err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
bobCommitPoint := input.ComputeCommitmentPoint(bobFirstRevoke[:]) |
|
|
|
aliceRoot, err := chainhash.NewHash(aliceKeyPriv.Serialize()) |
|
if err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot) |
|
aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0) |
|
if err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:]) |
|
|
|
aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(channelBal, |
|
channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, bobCommitPoint, |
|
*fundingTxIn) |
|
if err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
|
|
alicePath, err := ioutil.TempDir("", "alicedb") |
|
dbAlice, err := channeldb.Open(alicePath) |
|
if err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
|
|
bobPath, err := ioutil.TempDir("", "bobdb") |
|
dbBob, err := channeldb.Open(bobPath) |
|
if err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
|
|
estimator := lnwallet.NewStaticFeeEstimator(12500, 0) |
|
feePerKw, err := estimator.EstimateFeePerKW(1) |
|
if err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
|
|
// TODO(roasbeef): need to factor in commit fee? |
|
aliceCommit := channeldb.ChannelCommitment{ |
|
CommitHeight: 0, |
|
LocalBalance: lnwire.NewMSatFromSatoshis(channelBal), |
|
RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal), |
|
FeePerKw: btcutil.Amount(feePerKw), |
|
CommitFee: feePerKw.FeeForWeight(input.CommitWeight), |
|
CommitTx: aliceCommitTx, |
|
CommitSig: bytes.Repeat([]byte{1}, 71), |
|
} |
|
bobCommit := channeldb.ChannelCommitment{ |
|
CommitHeight: 0, |
|
LocalBalance: lnwire.NewMSatFromSatoshis(channelBal), |
|
RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal), |
|
FeePerKw: btcutil.Amount(feePerKw), |
|
CommitFee: feePerKw.FeeForWeight(input.CommitWeight), |
|
CommitTx: bobCommitTx, |
|
CommitSig: bytes.Repeat([]byte{1}, 71), |
|
} |
|
|
|
var chanIDBytes [8]byte |
|
if _, err := io.ReadFull(crand.Reader, chanIDBytes[:]); err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
|
|
shortChanID := lnwire.NewShortChanIDFromInt( |
|
binary.BigEndian.Uint64(chanIDBytes[:]), |
|
) |
|
|
|
aliceChannelState := &channeldb.OpenChannel{ |
|
LocalChanCfg: aliceCfg, |
|
RemoteChanCfg: bobCfg, |
|
IdentityPub: aliceKeyPub, |
|
FundingOutpoint: *prevOut, |
|
ShortChannelID: shortChanID, |
|
ChanType: channeldb.SingleFunder, |
|
IsInitiator: true, |
|
Capacity: channelCapacity, |
|
RemoteCurrentRevocation: bobCommitPoint, |
|
RevocationProducer: alicePreimageProducer, |
|
RevocationStore: shachain.NewRevocationStore(), |
|
LocalCommitment: aliceCommit, |
|
RemoteCommitment: aliceCommit, |
|
Db: dbAlice, |
|
Packager: channeldb.NewChannelPackager(shortChanID), |
|
FundingTxn: testTx, |
|
} |
|
bobChannelState := &channeldb.OpenChannel{ |
|
LocalChanCfg: bobCfg, |
|
RemoteChanCfg: aliceCfg, |
|
IdentityPub: bobKeyPub, |
|
FundingOutpoint: *prevOut, |
|
ChanType: channeldb.SingleFunder, |
|
IsInitiator: false, |
|
Capacity: channelCapacity, |
|
RemoteCurrentRevocation: aliceCommitPoint, |
|
RevocationProducer: bobPreimageProducer, |
|
RevocationStore: shachain.NewRevocationStore(), |
|
LocalCommitment: bobCommit, |
|
RemoteCommitment: bobCommit, |
|
Db: dbBob, |
|
Packager: channeldb.NewChannelPackager(shortChanID), |
|
} |
|
|
|
aliceAddr := &net.TCPAddr{ |
|
IP: net.ParseIP("127.0.0.1"), |
|
Port: 18555, |
|
} |
|
|
|
if err := aliceChannelState.SyncPending(aliceAddr, 0); err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
|
|
bobAddr := &net.TCPAddr{ |
|
IP: net.ParseIP("127.0.0.1"), |
|
Port: 18556, |
|
} |
|
|
|
if err := bobChannelState.SyncPending(bobAddr, 0); err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
|
|
cleanUpFunc := func() { |
|
os.RemoveAll(bobPath) |
|
os.RemoveAll(alicePath) |
|
} |
|
|
|
aliceSigner := &mockSigner{aliceKeyPriv} |
|
bobSigner := &mockSigner{bobKeyPriv} |
|
|
|
alicePool := lnwallet.NewSigPool(1, aliceSigner) |
|
channelAlice, err := lnwallet.NewLightningChannel( |
|
aliceSigner, nil, aliceChannelState, alicePool, |
|
) |
|
if err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
alicePool.Start() |
|
|
|
bobPool := lnwallet.NewSigPool(1, bobSigner) |
|
channelBob, err := lnwallet.NewLightningChannel( |
|
bobSigner, nil, bobChannelState, bobPool, |
|
) |
|
if err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
bobPool.Start() |
|
|
|
chainIO := &mockChainIO{ |
|
bestHeight: fundingBroadcastHeight, |
|
} |
|
wallet := &lnwallet.LightningWallet{ |
|
WalletController: &mockWalletController{ |
|
rootKey: aliceKeyPriv, |
|
publishedTransactions: publTx, |
|
}, |
|
} |
|
cc := &chainControl{ |
|
feeEstimator: estimator, |
|
chainIO: chainIO, |
|
chainNotifier: notifier, |
|
wallet: wallet, |
|
} |
|
|
|
breachArbiter := &breachArbiter{} |
|
|
|
chainArb := contractcourt.NewChainArbitrator( |
|
contractcourt.ChainArbitratorConfig{ |
|
Notifier: notifier, |
|
ChainIO: chainIO, |
|
}, dbAlice, |
|
) |
|
chainArb.WatchNewChannel(aliceChannelState) |
|
|
|
s := &server{ |
|
chanDB: dbAlice, |
|
cc: cc, |
|
breachArbiter: breachArbiter, |
|
chainArb: chainArb, |
|
} |
|
|
|
_, currentHeight, err := s.cc.chainIO.GetBestBlock() |
|
if err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
|
|
htlcSwitch, err := htlcswitch.New(htlcswitch.Config{ |
|
DB: dbAlice, |
|
SwitchPackager: channeldb.NewSwitchPackager(), |
|
Notifier: notifier, |
|
FwdEventTicker: ticker.New( |
|
htlcswitch.DefaultFwdEventInterval), |
|
LogEventTicker: ticker.New( |
|
htlcswitch.DefaultLogInterval), |
|
}, uint32(currentHeight)) |
|
if err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
if err = htlcSwitch.Start(); err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
s.htlcSwitch = htlcSwitch |
|
|
|
nodeSignerAlice := netann.NewNodeSigner(aliceKeyPriv) |
|
|
|
const chanActiveTimeout = time.Minute |
|
|
|
chanStatusMgr, err := netann.NewChanStatusManager(&netann.ChanStatusConfig{ |
|
ChanStatusSampleInterval: 30 * time.Second, |
|
ChanEnableTimeout: chanActiveTimeout, |
|
ChanDisableTimeout: 2 * time.Minute, |
|
DB: dbAlice, |
|
Graph: dbAlice.ChannelGraph(), |
|
MessageSigner: nodeSignerAlice, |
|
OurPubKey: aliceKeyPub, |
|
IsChannelActive: s.htlcSwitch.HasActiveLink, |
|
ApplyChannelUpdate: func(*lnwire.ChannelUpdate) error { return nil }, |
|
}) |
|
if err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
if err = chanStatusMgr.Start(); err != nil { |
|
return nil, nil, nil, nil, err |
|
} |
|
s.chanStatusMgr = chanStatusMgr |
|
|
|
alicePeer := &peer{ |
|
addr: &lnwire.NetAddress{ |
|
IdentityKey: aliceKeyPub, |
|
Address: aliceAddr, |
|
}, |
|
|
|
server: s, |
|
sendQueue: make(chan outgoingMsg, 1), |
|
outgoingQueue: make(chan outgoingMsg, outgoingQueueLen), |
|
|
|
activeChannels: make(map[lnwire.ChannelID]*lnwallet.LightningChannel), |
|
newChannels: make(chan *newChannelMsg, 1), |
|
|
|
activeChanCloses: make(map[lnwire.ChannelID]*channelCloser), |
|
localCloseChanReqs: make(chan *htlcswitch.ChanClose), |
|
chanCloseMsgs: make(chan *closeMsg), |
|
|
|
chanActiveTimeout: chanActiveTimeout, |
|
|
|
queueQuit: make(chan struct{}), |
|
quit: make(chan struct{}), |
|
} |
|
|
|
chanID := lnwire.NewChanIDFromOutPoint(channelAlice.ChannelPoint()) |
|
alicePeer.activeChannels[chanID] = channelAlice |
|
|
|
alicePeer.wg.Add(1) |
|
go alicePeer.channelManager() |
|
|
|
return alicePeer, channelAlice, channelBob, cleanUpFunc, nil |
|
}
|
|
|