mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-09-18 19:41:21 +02:00
multi: remove peer.go, change all references to point to peer pkg
This commit is contained in:
2839
peer/brontide.go
Normal file
2839
peer/brontide.go
Normal file
File diff suppressed because it is too large
Load Diff
872
peer/brontide_test.go
Normal file
872
peer/brontide_test.go
Normal file
@@ -0,0 +1,872 @@
|
||||
package peer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chancloser"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
)
|
||||
|
||||
var (
|
||||
// p2SHAddress is a valid pay to script hash address.
|
||||
p2SHAddress = "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n"
|
||||
|
||||
// p2wshAddress is a valid pay to witness script hash address.
|
||||
p2wshAddress = "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3"
|
||||
|
||||
// timeout is a timeout value to use for tests which need ot wait for
|
||||
// a return value on a channel.
|
||||
timeout = time.Second * 5
|
||||
)
|
||||
|
||||
// TestPeerChannelClosureAcceptFeeResponder tests the shutdown responder's
|
||||
// behavior if we can agree on the fee immediately.
|
||||
func TestPeerChannelClosureAcceptFeeResponder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
notifier := &mockNotifier{
|
||||
confChannel: make(chan *chainntnfs.TxConfirmation),
|
||||
}
|
||||
broadcastTxChan := make(chan *wire.MsgTx)
|
||||
|
||||
alicePeer, bobChan, cleanUp, err := createTestPeer(
|
||||
notifier, broadcastTxChan, noUpdate,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create test channels: %v", err)
|
||||
}
|
||||
defer cleanUp()
|
||||
|
||||
chanID := lnwire.NewChanIDFromOutPoint(bobChan.ChannelPoint())
|
||||
|
||||
// We send a shutdown request to Alice. She will now be the responding
|
||||
// node in this shutdown procedure. We first expect Alice to answer
|
||||
// this shutdown request with a Shutdown message.
|
||||
alicePeer.chanCloseMsgs <- &closeMsg{
|
||||
cid: chanID,
|
||||
msg: lnwire.NewShutdown(chanID, dummyDeliveryScript),
|
||||
}
|
||||
|
||||
var msg lnwire.Message
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive shutdown message")
|
||||
}
|
||||
|
||||
shutdownMsg, ok := msg.(*lnwire.Shutdown)
|
||||
if !ok {
|
||||
t.Fatalf("expected Shutdown message, got %T", msg)
|
||||
}
|
||||
|
||||
respDeliveryScript := shutdownMsg.Address
|
||||
|
||||
// Alice will then send a ClosingSigned message, indicating her proposed
|
||||
// closing transaction fee. Alice sends the ClosingSigned message as she is
|
||||
// the initiator of the channel.
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive ClosingSigned message")
|
||||
}
|
||||
|
||||
respClosingSigned, ok := msg.(*lnwire.ClosingSigned)
|
||||
if !ok {
|
||||
t.Fatalf("expected ClosingSigned message, got %T", msg)
|
||||
}
|
||||
|
||||
// We accept the fee, and send a ClosingSigned with the same fee back,
|
||||
// so she knows we agreed.
|
||||
aliceFee := respClosingSigned.FeeSatoshis
|
||||
bobSig, _, _, err := bobChan.CreateCloseProposal(
|
||||
aliceFee, dummyDeliveryScript, respDeliveryScript,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating close proposal: %v", err)
|
||||
}
|
||||
|
||||
parsedSig, err := lnwire.NewSigFromSignature(bobSig)
|
||||
if err != nil {
|
||||
t.Fatalf("error parsing signature: %v", err)
|
||||
}
|
||||
closingSigned := lnwire.NewClosingSigned(chanID, aliceFee, parsedSig)
|
||||
alicePeer.chanCloseMsgs <- &closeMsg{
|
||||
cid: chanID,
|
||||
msg: closingSigned,
|
||||
}
|
||||
|
||||
// Alice should now see that we agreed on the fee, and should broadcast the
|
||||
// closing transaction.
|
||||
select {
|
||||
case <-broadcastTxChan:
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("closing tx not broadcast")
|
||||
}
|
||||
|
||||
// Need to pull the remaining message off of Alice's outgoing queue.
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive ClosingSigned message")
|
||||
}
|
||||
if _, ok := msg.(*lnwire.ClosingSigned); !ok {
|
||||
t.Fatalf("expected ClosingSigned message, got %T", msg)
|
||||
}
|
||||
|
||||
// Alice should be waiting in a goroutine for a confirmation.
|
||||
notifier.confChannel <- &chainntnfs.TxConfirmation{}
|
||||
}
|
||||
|
||||
// TestPeerChannelClosureAcceptFeeInitiator tests the shutdown initiator's
|
||||
// behavior if we can agree on the fee immediately.
|
||||
func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
notifier := &mockNotifier{
|
||||
confChannel: make(chan *chainntnfs.TxConfirmation),
|
||||
}
|
||||
broadcastTxChan := make(chan *wire.MsgTx)
|
||||
|
||||
alicePeer, bobChan, cleanUp, err := createTestPeer(
|
||||
notifier, broadcastTxChan, noUpdate,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create test channels: %v", err)
|
||||
}
|
||||
defer cleanUp()
|
||||
|
||||
// We make Alice send a shutdown request.
|
||||
updateChan := make(chan interface{}, 1)
|
||||
errChan := make(chan error, 1)
|
||||
closeCommand := &htlcswitch.ChanClose{
|
||||
CloseType: htlcswitch.CloseRegular,
|
||||
ChanPoint: bobChan.ChannelPoint(),
|
||||
Updates: updateChan,
|
||||
TargetFeePerKw: 12500,
|
||||
Err: errChan,
|
||||
}
|
||||
alicePeer.localCloseChanReqs <- closeCommand
|
||||
|
||||
// We can now pull a Shutdown message off of Alice's outgoingQueue.
|
||||
var msg lnwire.Message
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive shutdown request")
|
||||
}
|
||||
|
||||
shutdownMsg, ok := msg.(*lnwire.Shutdown)
|
||||
if !ok {
|
||||
t.Fatalf("expected Shutdown message, got %T", msg)
|
||||
}
|
||||
|
||||
aliceDeliveryScript := shutdownMsg.Address
|
||||
|
||||
// Bob will respond with his own Shutdown message.
|
||||
chanID := shutdownMsg.ChannelID
|
||||
alicePeer.chanCloseMsgs <- &closeMsg{
|
||||
cid: chanID,
|
||||
msg: lnwire.NewShutdown(chanID,
|
||||
dummyDeliveryScript),
|
||||
}
|
||||
|
||||
// Alice will reply with a ClosingSigned here.
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive closing signed message")
|
||||
}
|
||||
closingSignedMsg, ok := msg.(*lnwire.ClosingSigned)
|
||||
if !ok {
|
||||
t.Fatalf("expected to receive closing signed message, got %T", msg)
|
||||
}
|
||||
|
||||
// Bob should reply with the exact same fee in his next ClosingSigned
|
||||
// message.
|
||||
bobFee := closingSignedMsg.FeeSatoshis
|
||||
bobSig, _, _, err := bobChan.CreateCloseProposal(
|
||||
bobFee, dummyDeliveryScript, aliceDeliveryScript,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create close proposal: %v", err)
|
||||
}
|
||||
parsedSig, err := lnwire.NewSigFromSignature(bobSig)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse signature: %v", err)
|
||||
}
|
||||
|
||||
closingSigned := lnwire.NewClosingSigned(shutdownMsg.ChannelID,
|
||||
bobFee, parsedSig)
|
||||
alicePeer.chanCloseMsgs <- &closeMsg{
|
||||
cid: chanID,
|
||||
msg: closingSigned,
|
||||
}
|
||||
|
||||
// Alice should accept Bob's fee, broadcast the cooperative close tx, and
|
||||
// send a ClosingSigned message back to Bob.
|
||||
|
||||
// Alice should now broadcast the closing transaction.
|
||||
select {
|
||||
case <-broadcastTxChan:
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("closing tx not broadcast")
|
||||
}
|
||||
|
||||
// Alice should respond with the ClosingSigned they both agreed upon.
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive closing signed message")
|
||||
}
|
||||
|
||||
closingSignedMsg, ok = msg.(*lnwire.ClosingSigned)
|
||||
if !ok {
|
||||
t.Fatalf("expected ClosingSigned message, got %T", msg)
|
||||
}
|
||||
|
||||
if closingSignedMsg.FeeSatoshis != bobFee {
|
||||
t.Fatalf("expected ClosingSigned fee to be %v, instead got %v",
|
||||
bobFee, closingSignedMsg.FeeSatoshis)
|
||||
}
|
||||
|
||||
// Alice should be waiting on a single confirmation for the coop close tx.
|
||||
notifier.confChannel <- &chainntnfs.TxConfirmation{}
|
||||
}
|
||||
|
||||
// TestPeerChannelClosureFeeNegotiationsResponder tests the shutdown
|
||||
// responder's behavior in the case where we must do several rounds of fee
|
||||
// negotiation before we agree on a fee.
|
||||
func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
notifier := &mockNotifier{
|
||||
confChannel: make(chan *chainntnfs.TxConfirmation),
|
||||
}
|
||||
broadcastTxChan := make(chan *wire.MsgTx)
|
||||
|
||||
alicePeer, bobChan, cleanUp, err := createTestPeer(
|
||||
notifier, broadcastTxChan, noUpdate,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create test channels: %v", err)
|
||||
}
|
||||
defer cleanUp()
|
||||
|
||||
chanID := lnwire.NewChanIDFromOutPoint(bobChan.ChannelPoint())
|
||||
|
||||
// Bob sends a shutdown request to Alice. She will now be the responding
|
||||
// node in this shutdown procedure. We first expect Alice to answer this
|
||||
// Shutdown request with a Shutdown message.
|
||||
alicePeer.chanCloseMsgs <- &closeMsg{
|
||||
cid: chanID,
|
||||
msg: lnwire.NewShutdown(chanID,
|
||||
dummyDeliveryScript),
|
||||
}
|
||||
|
||||
var msg lnwire.Message
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive shutdown message")
|
||||
}
|
||||
|
||||
shutdownMsg, ok := msg.(*lnwire.Shutdown)
|
||||
if !ok {
|
||||
t.Fatalf("expected Shutdown message, got %T", msg)
|
||||
}
|
||||
|
||||
aliceDeliveryScript := shutdownMsg.Address
|
||||
|
||||
// As Alice is the channel initiator, she will send her ClosingSigned
|
||||
// message.
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive closing signed message")
|
||||
}
|
||||
|
||||
aliceClosingSigned, ok := msg.(*lnwire.ClosingSigned)
|
||||
if !ok {
|
||||
t.Fatalf("expected ClosingSigned message, got %T", msg)
|
||||
}
|
||||
|
||||
// Bob doesn't agree with the fee and will send one back that's 2.5x.
|
||||
preferredRespFee := aliceClosingSigned.FeeSatoshis
|
||||
increasedFee := btcutil.Amount(float64(preferredRespFee) * 2.5)
|
||||
bobSig, _, _, err := bobChan.CreateCloseProposal(
|
||||
increasedFee, dummyDeliveryScript, aliceDeliveryScript,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating close proposal: %v", err)
|
||||
}
|
||||
|
||||
parsedSig, err := lnwire.NewSigFromSignature(bobSig)
|
||||
if err != nil {
|
||||
t.Fatalf("error parsing signature: %v", err)
|
||||
}
|
||||
closingSigned := lnwire.NewClosingSigned(chanID, increasedFee, parsedSig)
|
||||
alicePeer.chanCloseMsgs <- &closeMsg{
|
||||
cid: chanID,
|
||||
msg: closingSigned,
|
||||
}
|
||||
|
||||
// Alice will now see the new fee we propose, but with current settings it
|
||||
// won't accept it immediately as it differs too much by its ideal fee. We
|
||||
// should get a new proposal back, which should have the average fee rate
|
||||
// proposed.
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive closing signed message")
|
||||
}
|
||||
|
||||
aliceClosingSigned, ok = msg.(*lnwire.ClosingSigned)
|
||||
if !ok {
|
||||
t.Fatalf("expected ClosingSigned message, got %T", msg)
|
||||
}
|
||||
|
||||
// The fee sent by Alice should be less than the fee Bob just sent as Alice
|
||||
// should attempt to compromise.
|
||||
aliceFee := aliceClosingSigned.FeeSatoshis
|
||||
if aliceFee > increasedFee {
|
||||
t.Fatalf("new fee should be less than our fee: new=%v, "+
|
||||
"prior=%v", aliceFee, increasedFee)
|
||||
}
|
||||
lastFeeResponder := aliceFee
|
||||
|
||||
// We try negotiating a 2.1x fee, which should also be rejected.
|
||||
increasedFee = btcutil.Amount(float64(preferredRespFee) * 2.1)
|
||||
bobSig, _, _, err = bobChan.CreateCloseProposal(
|
||||
increasedFee, dummyDeliveryScript, aliceDeliveryScript,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating close proposal: %v", err)
|
||||
}
|
||||
|
||||
parsedSig, err = lnwire.NewSigFromSignature(bobSig)
|
||||
if err != nil {
|
||||
t.Fatalf("error parsing signature: %v", err)
|
||||
}
|
||||
closingSigned = lnwire.NewClosingSigned(chanID, increasedFee, parsedSig)
|
||||
alicePeer.chanCloseMsgs <- &closeMsg{
|
||||
cid: chanID,
|
||||
msg: closingSigned,
|
||||
}
|
||||
|
||||
// Bob's latest proposal still won't be accepted and Alice should send over
|
||||
// a new ClosingSigned message. It should be the average of what Bob and
|
||||
// Alice each proposed last time.
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive closing signed message")
|
||||
}
|
||||
|
||||
aliceClosingSigned, ok = msg.(*lnwire.ClosingSigned)
|
||||
if !ok {
|
||||
t.Fatalf("expected ClosingSigned message, got %T", msg)
|
||||
}
|
||||
|
||||
// Alice should inch towards Bob's fee, in order to compromise.
|
||||
// Additionally, this fee should be less than the fee Bob sent before.
|
||||
aliceFee = aliceClosingSigned.FeeSatoshis
|
||||
if aliceFee < lastFeeResponder {
|
||||
t.Fatalf("new fee should be greater than prior: new=%v, "+
|
||||
"prior=%v", aliceFee, lastFeeResponder)
|
||||
}
|
||||
if aliceFee > increasedFee {
|
||||
t.Fatalf("new fee should be less than Bob's fee: new=%v, "+
|
||||
"prior=%v", aliceFee, increasedFee)
|
||||
}
|
||||
|
||||
// Finally, Bob will accept the fee by echoing back the same fee that Alice
|
||||
// just sent over.
|
||||
bobSig, _, _, err = bobChan.CreateCloseProposal(
|
||||
aliceFee, dummyDeliveryScript, aliceDeliveryScript,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating close proposal: %v", err)
|
||||
}
|
||||
|
||||
parsedSig, err = lnwire.NewSigFromSignature(bobSig)
|
||||
if err != nil {
|
||||
t.Fatalf("error parsing signature: %v", err)
|
||||
}
|
||||
closingSigned = lnwire.NewClosingSigned(chanID, aliceFee, parsedSig)
|
||||
alicePeer.chanCloseMsgs <- &closeMsg{
|
||||
cid: chanID,
|
||||
msg: closingSigned,
|
||||
}
|
||||
|
||||
// Alice will now see that Bob agreed on the fee, and broadcast the coop
|
||||
// close transaction.
|
||||
select {
|
||||
case <-broadcastTxChan:
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("closing tx not broadcast")
|
||||
}
|
||||
|
||||
// Alice should respond with the ClosingSigned they both agreed upon.
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive closing signed message")
|
||||
}
|
||||
if _, ok := msg.(*lnwire.ClosingSigned); !ok {
|
||||
t.Fatalf("expected to receive closing signed message, got %T", msg)
|
||||
}
|
||||
|
||||
// Alice should be waiting on a single confirmation for the coop close tx.
|
||||
notifier.confChannel <- &chainntnfs.TxConfirmation{}
|
||||
}
|
||||
|
||||
// TestPeerChannelClosureFeeNegotiationsInitiator tests the shutdown
|
||||
// initiator's behavior in the case where we must do several rounds of fee
|
||||
// negotiation before we agree on a fee.
|
||||
func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
notifier := &mockNotifier{
|
||||
confChannel: make(chan *chainntnfs.TxConfirmation),
|
||||
}
|
||||
broadcastTxChan := make(chan *wire.MsgTx)
|
||||
|
||||
alicePeer, bobChan, cleanUp, err := createTestPeer(
|
||||
notifier, broadcastTxChan, noUpdate,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create test channels: %v", err)
|
||||
}
|
||||
defer cleanUp()
|
||||
|
||||
// We make the initiator send a shutdown request.
|
||||
updateChan := make(chan interface{}, 1)
|
||||
errChan := make(chan error, 1)
|
||||
closeCommand := &htlcswitch.ChanClose{
|
||||
CloseType: htlcswitch.CloseRegular,
|
||||
ChanPoint: bobChan.ChannelPoint(),
|
||||
Updates: updateChan,
|
||||
TargetFeePerKw: 12500,
|
||||
Err: errChan,
|
||||
}
|
||||
|
||||
alicePeer.localCloseChanReqs <- closeCommand
|
||||
|
||||
// Alice should now send a Shutdown request to Bob.
|
||||
var msg lnwire.Message
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive shutdown request")
|
||||
}
|
||||
|
||||
shutdownMsg, ok := msg.(*lnwire.Shutdown)
|
||||
if !ok {
|
||||
t.Fatalf("expected Shutdown message, got %T", msg)
|
||||
}
|
||||
|
||||
aliceDeliveryScript := shutdownMsg.Address
|
||||
|
||||
// Bob will answer the Shutdown message with his own Shutdown.
|
||||
chanID := lnwire.NewChanIDFromOutPoint(bobChan.ChannelPoint())
|
||||
respShutdown := lnwire.NewShutdown(chanID, dummyDeliveryScript)
|
||||
alicePeer.chanCloseMsgs <- &closeMsg{
|
||||
cid: chanID,
|
||||
msg: respShutdown,
|
||||
}
|
||||
|
||||
// Alice should now respond with a ClosingSigned message with her ideal
|
||||
// fee rate.
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive closing signed")
|
||||
}
|
||||
closingSignedMsg, ok := msg.(*lnwire.ClosingSigned)
|
||||
if !ok {
|
||||
t.Fatalf("expected ClosingSigned message, got %T", msg)
|
||||
}
|
||||
|
||||
idealFeeRate := closingSignedMsg.FeeSatoshis
|
||||
lastReceivedFee := idealFeeRate
|
||||
|
||||
increasedFee := btcutil.Amount(float64(idealFeeRate) * 2.1)
|
||||
lastSentFee := increasedFee
|
||||
|
||||
bobSig, _, _, err := bobChan.CreateCloseProposal(
|
||||
increasedFee, dummyDeliveryScript, aliceDeliveryScript,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating close proposal: %v", err)
|
||||
}
|
||||
|
||||
parsedSig, err := lnwire.NewSigFromSignature(bobSig)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse signature: %v", err)
|
||||
}
|
||||
|
||||
closingSigned := lnwire.NewClosingSigned(chanID, increasedFee, parsedSig)
|
||||
alicePeer.chanCloseMsgs <- &closeMsg{
|
||||
cid: chanID,
|
||||
msg: closingSigned,
|
||||
}
|
||||
|
||||
// It still won't be accepted, and we should get a new proposal, the
|
||||
// average of what we proposed, and what they proposed last time.
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive closing signed")
|
||||
}
|
||||
closingSignedMsg, ok = msg.(*lnwire.ClosingSigned)
|
||||
if !ok {
|
||||
t.Fatalf("expected ClosingSigned message, got %T", msg)
|
||||
}
|
||||
|
||||
aliceFee := closingSignedMsg.FeeSatoshis
|
||||
if aliceFee < lastReceivedFee {
|
||||
t.Fatalf("new fee should be greater than prior: new=%v, old=%v",
|
||||
aliceFee, lastReceivedFee)
|
||||
}
|
||||
if aliceFee > lastSentFee {
|
||||
t.Fatalf("new fee should be less than our fee: new=%v, old=%v",
|
||||
aliceFee, lastSentFee)
|
||||
}
|
||||
|
||||
lastReceivedFee = aliceFee
|
||||
|
||||
// We'll try negotiating a 1.5x fee, which should also be rejected.
|
||||
increasedFee = btcutil.Amount(float64(idealFeeRate) * 1.5)
|
||||
lastSentFee = increasedFee
|
||||
|
||||
bobSig, _, _, err = bobChan.CreateCloseProposal(
|
||||
increasedFee, dummyDeliveryScript, aliceDeliveryScript,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating close proposal: %v", err)
|
||||
}
|
||||
|
||||
parsedSig, err = lnwire.NewSigFromSignature(bobSig)
|
||||
if err != nil {
|
||||
t.Fatalf("error parsing signature: %v", err)
|
||||
}
|
||||
|
||||
closingSigned = lnwire.NewClosingSigned(chanID, increasedFee, parsedSig)
|
||||
alicePeer.chanCloseMsgs <- &closeMsg{
|
||||
cid: chanID,
|
||||
msg: closingSigned,
|
||||
}
|
||||
|
||||
// Alice won't accept Bob's new proposal, and Bob should receive a new
|
||||
// proposal which is the average of what Bob proposed and Alice proposed
|
||||
// last time.
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive closing signed")
|
||||
}
|
||||
closingSignedMsg, ok = msg.(*lnwire.ClosingSigned)
|
||||
if !ok {
|
||||
t.Fatalf("expected ClosingSigned message, got %T", msg)
|
||||
}
|
||||
|
||||
aliceFee = closingSignedMsg.FeeSatoshis
|
||||
if aliceFee < lastReceivedFee {
|
||||
t.Fatalf("new fee should be greater than prior: new=%v, old=%v",
|
||||
aliceFee, lastReceivedFee)
|
||||
}
|
||||
if aliceFee > lastSentFee {
|
||||
t.Fatalf("new fee should be less than Bob's fee: new=%v, old=%v",
|
||||
aliceFee, lastSentFee)
|
||||
}
|
||||
|
||||
// Bob will now accept their fee by sending back a ClosingSigned message
|
||||
// with an identical fee.
|
||||
bobSig, _, _, err = bobChan.CreateCloseProposal(
|
||||
aliceFee, dummyDeliveryScript, aliceDeliveryScript,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating close proposal: %v", err)
|
||||
}
|
||||
|
||||
parsedSig, err = lnwire.NewSigFromSignature(bobSig)
|
||||
if err != nil {
|
||||
t.Fatalf("error parsing signature: %v", err)
|
||||
}
|
||||
closingSigned = lnwire.NewClosingSigned(chanID, aliceFee, parsedSig)
|
||||
alicePeer.chanCloseMsgs <- &closeMsg{
|
||||
cid: chanID,
|
||||
msg: closingSigned,
|
||||
}
|
||||
|
||||
// Wait for closing tx to be broadcasted.
|
||||
select {
|
||||
case <-broadcastTxChan:
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("closing tx not broadcast")
|
||||
}
|
||||
|
||||
// Alice should respond with the ClosingSigned they both agreed upon.
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive closing signed message")
|
||||
}
|
||||
if _, ok := msg.(*lnwire.ClosingSigned); !ok {
|
||||
t.Fatalf("expected to receive closing signed message, got %T", msg)
|
||||
}
|
||||
|
||||
// Alice should be waiting on a single confirmation for the coop close tx.
|
||||
notifier.confChannel <- &chainntnfs.TxConfirmation{}
|
||||
}
|
||||
|
||||
// TestChooseDeliveryScript tests that chooseDeliveryScript correctly errors
|
||||
// when upfront and user set scripts that do not match are provided, allows
|
||||
// matching values and returns appropriate values in the case where one or none
|
||||
// are set.
|
||||
func TestChooseDeliveryScript(t *testing.T) {
|
||||
// generate non-zero scripts for testing.
|
||||
script1 := genScript(t, p2SHAddress)
|
||||
script2 := genScript(t, p2wshAddress)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
userScript lnwire.DeliveryAddress
|
||||
shutdownScript lnwire.DeliveryAddress
|
||||
expectedScript lnwire.DeliveryAddress
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "Neither set",
|
||||
userScript: nil,
|
||||
shutdownScript: nil,
|
||||
expectedScript: nil,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Both set and equal",
|
||||
userScript: script1,
|
||||
shutdownScript: script1,
|
||||
expectedScript: script1,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Both set and not equal",
|
||||
userScript: script1,
|
||||
shutdownScript: script2,
|
||||
expectedScript: nil,
|
||||
expectedError: chancloser.ErrUpfrontShutdownScriptMismatch,
|
||||
},
|
||||
{
|
||||
name: "Only upfront script",
|
||||
userScript: nil,
|
||||
shutdownScript: script1,
|
||||
expectedScript: script1,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Only user script",
|
||||
userScript: script2,
|
||||
shutdownScript: nil,
|
||||
expectedScript: script2,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
script, err := chooseDeliveryScript(
|
||||
test.shutdownScript, test.userScript,
|
||||
)
|
||||
if err != test.expectedError {
|
||||
t.Fatalf("Expected: %v, got: %v", test.expectedError, err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(script, test.expectedScript) {
|
||||
t.Fatalf("Expected: %x, got: %x", test.expectedScript, script)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestCustomShutdownScript tests that the delivery script of a shutdown
|
||||
// message can be set to a specified address. It checks that setting a close
|
||||
// script fails for channels which have an upfront shutdown script already set.
|
||||
func TestCustomShutdownScript(t *testing.T) {
|
||||
script := genScript(t, p2SHAddress)
|
||||
|
||||
// setShutdown is a function which sets the upfront shutdown address for
|
||||
// the local channel.
|
||||
setShutdown := func(a, b *channeldb.OpenChannel) {
|
||||
a.LocalShutdownScript = script
|
||||
b.RemoteShutdownScript = script
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
// update is a function used to set values on the channel set up for the
|
||||
// test. It is used to set values for upfront shutdown addresses.
|
||||
update func(a, b *channeldb.OpenChannel)
|
||||
|
||||
// userCloseScript is the address specified by the user.
|
||||
userCloseScript lnwire.DeliveryAddress
|
||||
|
||||
// expectedScript is the address we expect to be set on the shutdown
|
||||
// message.
|
||||
expectedScript lnwire.DeliveryAddress
|
||||
|
||||
// expectedError is the error we expect, if any.
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "User set script",
|
||||
update: noUpdate,
|
||||
userCloseScript: script,
|
||||
expectedScript: script,
|
||||
},
|
||||
{
|
||||
name: "No user set script",
|
||||
update: noUpdate,
|
||||
},
|
||||
{
|
||||
name: "Shutdown set, no user script",
|
||||
update: setShutdown,
|
||||
expectedScript: script,
|
||||
},
|
||||
{
|
||||
name: "Shutdown set, user script matches",
|
||||
update: setShutdown,
|
||||
userCloseScript: script,
|
||||
expectedScript: script,
|
||||
},
|
||||
{
|
||||
name: "Shutdown set, user script different",
|
||||
update: setShutdown,
|
||||
userCloseScript: []byte("different addr"),
|
||||
expectedError: chancloser.ErrUpfrontShutdownScriptMismatch,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
notifier := &mockNotifier{
|
||||
confChannel: make(chan *chainntnfs.TxConfirmation),
|
||||
}
|
||||
broadcastTxChan := make(chan *wire.MsgTx)
|
||||
|
||||
// Open a channel.
|
||||
alicePeer, bobChan, cleanUp, err := createTestPeer(
|
||||
notifier, broadcastTxChan, test.update,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create test channels: %v", err)
|
||||
}
|
||||
defer cleanUp()
|
||||
|
||||
// Request initiator to cooperatively close the channel, with
|
||||
// a specified delivery address.
|
||||
updateChan := make(chan interface{}, 1)
|
||||
errChan := make(chan error, 1)
|
||||
chanPoint := bobChan.ChannelPoint()
|
||||
closeCommand := htlcswitch.ChanClose{
|
||||
CloseType: htlcswitch.CloseRegular,
|
||||
ChanPoint: chanPoint,
|
||||
Updates: updateChan,
|
||||
TargetFeePerKw: 12500,
|
||||
DeliveryScript: test.userCloseScript,
|
||||
Err: errChan,
|
||||
}
|
||||
|
||||
// Send the close command for the correct channel and check that a
|
||||
// shutdown message is sent.
|
||||
alicePeer.localCloseChanReqs <- &closeCommand
|
||||
|
||||
var msg lnwire.Message
|
||||
select {
|
||||
case outMsg := <-alicePeer.outgoingQueue:
|
||||
msg = outMsg.msg
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("did not receive shutdown message")
|
||||
case err := <-errChan:
|
||||
// Fail if we do not expect an error.
|
||||
if err != test.expectedError {
|
||||
t.Fatalf("error closing channel: %v", err)
|
||||
}
|
||||
|
||||
// Terminate the test early if have received an error, no
|
||||
// further action is expected.
|
||||
return
|
||||
}
|
||||
|
||||
// Check that we have received a shutdown message.
|
||||
shutdownMsg, ok := msg.(*lnwire.Shutdown)
|
||||
if !ok {
|
||||
t.Fatalf("expected shutdown message, got %T", msg)
|
||||
}
|
||||
|
||||
// If the test has not specified an expected address, do not check
|
||||
// whether the shutdown address matches. This covers the case where
|
||||
// we epect shutdown to a random address and cannot match it.
|
||||
if len(test.expectedScript) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Check that the Shutdown message includes the expected delivery
|
||||
// script.
|
||||
if !bytes.Equal(test.expectedScript, shutdownMsg.Address) {
|
||||
t.Fatalf("expected delivery script: %x, got: %x",
|
||||
test.expectedScript, shutdownMsg.Address)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// genScript creates a script paying out to the address provided, which must
|
||||
// be a valid address.
|
||||
func genScript(t *testing.T, address string) lnwire.DeliveryAddress {
|
||||
// Generate an address which can be used for testing.
|
||||
deliveryAddr, err := btcutil.DecodeAddress(
|
||||
address,
|
||||
&chaincfg.TestNet3Params,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid delivery address: %v", err)
|
||||
}
|
||||
|
||||
script, err := txscript.PayToAddrScript(deliveryAddr)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot create script: %v", err)
|
||||
}
|
||||
|
||||
return script
|
||||
}
|
40
peer/log.go
Normal file
40
peer/log.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package peer
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btclog"
|
||||
"github.com/lightningnetwork/lnd/build"
|
||||
)
|
||||
|
||||
// peerLog is a logger that is initialized with the btclog.Disabled logger.
|
||||
var peerLog btclog.Logger
|
||||
|
||||
// The default amount of logging is none.
|
||||
func init() {
|
||||
UseLogger(build.NewSubLogger("PEER", nil))
|
||||
}
|
||||
|
||||
// DisableLog disables all logging output.
|
||||
func DisableLog() {
|
||||
UseLogger(btclog.Disabled)
|
||||
}
|
||||
|
||||
// UseLogger uses a specified Logger to output package logging info.
|
||||
func UseLogger(logger btclog.Logger) {
|
||||
peerLog = logger
|
||||
}
|
||||
|
||||
// logClosure is used to provide a closure over expensive logging operations
|
||||
// so they aren't performed when the logging level doesn't warrant it.
|
||||
type logClosure func() string
|
||||
|
||||
// String invokes the underlying function and returns the result.
|
||||
func (c logClosure) String() string {
|
||||
return c()
|
||||
}
|
||||
|
||||
// newLogClosure returns a new closure over a function that returns a string
|
||||
// which itself provides a Stringer interface so that it can be used with the
|
||||
// logging system.
|
||||
func newLogClosure(c func() string) logClosure {
|
||||
return logClosure(c)
|
||||
}
|
695
peer/test_utils.go
Normal file
695
peer/test_utils.go
Normal file
@@ -0,0 +1,695 @@
|
||||
package peer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
crand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcwallet/wallet/txauthor"
|
||||
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/netann"
|
||||
"github.com/lightningnetwork/lnd/queue"
|
||||
"github.com/lightningnetwork/lnd/shachain"
|
||||
"github.com/lightningnetwork/lnd/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
broadcastHeight = 100
|
||||
)
|
||||
|
||||
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,
|
||||
}
|
||||
)
|
||||
|
||||
// noUpdate is a function which can be used as a parameter in createTestPeer to
|
||||
// call the setup code with no custom values on the channels set up.
|
||||
var noUpdate = func(a, b *channeldb.OpenChannel) {}
|
||||
|
||||
type mockSigner struct {
|
||||
key *btcec.PrivateKey
|
||||
}
|
||||
|
||||
func (m *mockSigner) SignOutputRaw(tx *wire.MsgTx,
|
||||
signDesc *input.SignDescriptor) (input.Signature, error) {
|
||||
amt := signDesc.Output.Value
|
||||
witnessScript := signDesc.WitnessScript
|
||||
privKey := m.key
|
||||
|
||||
if !privKey.PubKey().IsEqual(signDesc.KeyDesc.PubKey) {
|
||||
return nil, fmt.Errorf("incorrect key passed")
|
||||
}
|
||||
|
||||
switch {
|
||||
case signDesc.SingleTweak != nil:
|
||||
privKey = input.TweakPrivKey(privKey,
|
||||
signDesc.SingleTweak)
|
||||
case signDesc.DoubleTweak != nil:
|
||||
privKey = input.DeriveRevocationPrivKey(privKey,
|
||||
signDesc.DoubleTweak)
|
||||
}
|
||||
|
||||
sig, err := txscript.RawTxInWitnessSignature(tx, signDesc.SigHashes,
|
||||
signDesc.InputIndex, amt, witnessScript, signDesc.HashType,
|
||||
privKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return btcec.ParseDERSignature(sig[:len(sig)-1], btcec.S256())
|
||||
}
|
||||
|
||||
func (m *mockSigner) ComputeInputScript(tx *wire.MsgTx,
|
||||
signDesc *input.SignDescriptor) (*input.Script, error) {
|
||||
|
||||
// TODO(roasbeef): expose tweaked signer from lnwallet so don't need to
|
||||
// duplicate this code?
|
||||
|
||||
privKey := m.key
|
||||
|
||||
switch {
|
||||
case signDesc.SingleTweak != nil:
|
||||
privKey = input.TweakPrivKey(privKey,
|
||||
signDesc.SingleTweak)
|
||||
case signDesc.DoubleTweak != nil:
|
||||
privKey = input.DeriveRevocationPrivKey(privKey,
|
||||
signDesc.DoubleTweak)
|
||||
}
|
||||
|
||||
witnessScript, err := txscript.WitnessSignature(tx, signDesc.SigHashes,
|
||||
signDesc.InputIndex, signDesc.Output.Value, signDesc.Output.PkScript,
|
||||
signDesc.HashType, privKey, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &input.Script{
|
||||
Witness: witnessScript,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ input.Signer = (*mockSigner)(nil)
|
||||
|
||||
type mockChainIO struct {
|
||||
bestHeight int32
|
||||
}
|
||||
|
||||
func (m *mockChainIO) GetBestBlock() (*chainhash.Hash, int32, error) {
|
||||
return nil, m.bestHeight, nil
|
||||
}
|
||||
|
||||
func (*mockChainIO) GetUtxo(op *wire.OutPoint, _ []byte,
|
||||
heightHint uint32, _ <-chan struct{}) (*wire.TxOut, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (*mockChainIO) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (*mockChainIO) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var _ lnwallet.BlockChainIO = (*mockChainIO)(nil)
|
||||
|
||||
type mockWalletController struct {
|
||||
rootKey *btcec.PrivateKey
|
||||
publishedTxns chan *wire.MsgTx
|
||||
}
|
||||
|
||||
func (*mockWalletController) FetchInputInfo(prevOut *wire.OutPoint) (
|
||||
*lnwallet.Utxo, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (*mockWalletController) ConfirmedBalance(confs int32) (btcutil.Amount,
|
||||
error) {
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (m *mockWalletController) NewAddress(addrType lnwallet.AddressType,
|
||||
change bool) (btcutil.Address, error) {
|
||||
|
||||
addr, _ := btcutil.NewAddressPubKey(
|
||||
m.rootKey.PubKey().SerializeCompressed(), &chaincfg.MainNetParams,
|
||||
)
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
func (*mockWalletController) LastUnusedAddress(addrType lnwallet.AddressType) (
|
||||
btcutil.Address, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (*mockWalletController) IsOurAddress(a btcutil.Address) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (*mockWalletController) SendOutputs(outputs []*wire.TxOut,
|
||||
feeRate chainfee.SatPerKWeight, label string) (*wire.MsgTx, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (*mockWalletController) CreateSimpleTx(outputs []*wire.TxOut,
|
||||
feeRate chainfee.SatPerKWeight, dryRun bool) (*txauthor.AuthoredTx, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (*mockWalletController) ListUnspentWitness(minconfirms,
|
||||
maxconfirms int32) ([]*lnwallet.Utxo, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (*mockWalletController) ListTransactionDetails(startHeight,
|
||||
endHeight int32) ([]*lnwallet.TransactionDetail, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (*mockWalletController) LockOutpoint(o wire.OutPoint) {}
|
||||
|
||||
func (*mockWalletController) UnlockOutpoint(o wire.OutPoint) {}
|
||||
|
||||
func (m *mockWalletController) PublishTransaction(tx *wire.MsgTx,
|
||||
label string) error {
|
||||
m.publishedTxns <- tx
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*mockWalletController) LabelTransaction(hash chainhash.Hash,
|
||||
label string, overwrite bool) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*mockWalletController) SubscribeTransactions() (
|
||||
lnwallet.TransactionSubscription, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (*mockWalletController) IsSynced() (bool, int64, error) {
|
||||
return false, 0, nil
|
||||
}
|
||||
|
||||
func (*mockWalletController) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*mockWalletController) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*mockWalletController) BackEnd() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (*mockWalletController) LeaseOutput(wtxmgr.LockID,
|
||||
wire.OutPoint) (time.Time, error) {
|
||||
|
||||
return time.Now(), nil
|
||||
}
|
||||
|
||||
func (*mockWalletController) ReleaseOutput(wtxmgr.LockID, wire.OutPoint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*mockWalletController) GetRecoveryInfo() (bool, float64, error) {
|
||||
return false, 0, nil
|
||||
}
|
||||
|
||||
var _ lnwallet.WalletController = (*mockWalletController)(nil)
|
||||
|
||||
type mockNotifier struct {
|
||||
confChannel chan *chainntnfs.TxConfirmation
|
||||
}
|
||||
|
||||
func (m *mockNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
|
||||
_ []byte, numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent,
|
||||
error) {
|
||||
|
||||
return &chainntnfs.ConfirmationEvent{
|
||||
Confirmed: m.confChannel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *mockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, _ []byte,
|
||||
heightHint uint32) (*chainntnfs.SpendEvent, error) {
|
||||
|
||||
return &chainntnfs.SpendEvent{
|
||||
Spend: make(chan *chainntnfs.SpendDetail),
|
||||
Cancel: func() {},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *mockNotifier) RegisterBlockEpochNtfn(
|
||||
bestBlock *chainntnfs.BlockEpoch) (*chainntnfs.BlockEpochEvent, error) {
|
||||
|
||||
return &chainntnfs.BlockEpochEvent{
|
||||
Epochs: make(chan *chainntnfs.BlockEpoch),
|
||||
Cancel: func() {},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *mockNotifier) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockNotifier) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockNotifier) Started() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
var _ chainntnfs.ChainNotifier = (*mockNotifier)(nil)
|
||||
|
||||
// createTestPeer creates a channel between two nodes, and returns a peer for
|
||||
// one of the nodes, together with the channel seen from both nodes. It takes
|
||||
// an updateChan function which can be used to modify the default values on
|
||||
// the channel states for each peer.
|
||||
func createTestPeer(notifier chainntnfs.ChainNotifier,
|
||||
publTx chan *wire.MsgTx, updateChan func(a, b *channeldb.OpenChannel)) (
|
||||
*Brontide, *lnwallet.LightningChannel, func(), error) {
|
||||
|
||||
aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(
|
||||
btcec.S256(), alicesPrivKey,
|
||||
)
|
||||
aliceKeySigner := &keychain.PrivKeyDigestSigner{PrivKey: aliceKeyPriv}
|
||||
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, err
|
||||
}
|
||||
bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot)
|
||||
bobFirstRevoke, err := bobPreimageProducer.AtIndex(0)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
bobCommitPoint := input.ComputeCommitmentPoint(bobFirstRevoke[:])
|
||||
|
||||
aliceRoot, err := chainhash.NewHash(aliceKeyPriv.Serialize())
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot)
|
||||
aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:])
|
||||
|
||||
aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(
|
||||
channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint,
|
||||
bobCommitPoint, *fundingTxIn, channeldb.SingleFunderTweaklessBit,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
alicePath, err := ioutil.TempDir("", "alicedb")
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
dbAlice, err := channeldb.Open(alicePath)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
bobPath, err := ioutil.TempDir("", "bobdb")
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
dbBob, err := channeldb.Open(bobPath)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
estimator := chainfee.NewStaticEstimator(12500, 0)
|
||||
feePerKw, err := estimator.EstimateFeePerKW(1)
|
||||
if err != nil {
|
||||
return 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, err
|
||||
}
|
||||
|
||||
shortChanID := lnwire.NewShortChanIDFromInt(
|
||||
binary.BigEndian.Uint64(chanIDBytes[:]),
|
||||
)
|
||||
|
||||
aliceChannelState := &channeldb.OpenChannel{
|
||||
LocalChanCfg: aliceCfg,
|
||||
RemoteChanCfg: bobCfg,
|
||||
IdentityPub: aliceKeyPub,
|
||||
FundingOutpoint: *prevOut,
|
||||
ShortChannelID: shortChanID,
|
||||
ChanType: channeldb.SingleFunderTweaklessBit,
|
||||
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.SingleFunderTweaklessBit,
|
||||
IsInitiator: false,
|
||||
Capacity: channelCapacity,
|
||||
RemoteCurrentRevocation: aliceCommitPoint,
|
||||
RevocationProducer: bobPreimageProducer,
|
||||
RevocationStore: shachain.NewRevocationStore(),
|
||||
LocalCommitment: bobCommit,
|
||||
RemoteCommitment: bobCommit,
|
||||
Db: dbBob,
|
||||
Packager: channeldb.NewChannelPackager(shortChanID),
|
||||
}
|
||||
|
||||
// Set custom values on the channel states.
|
||||
updateChan(aliceChannelState, bobChannelState)
|
||||
|
||||
aliceAddr := &net.TCPAddr{
|
||||
IP: net.ParseIP("127.0.0.1"),
|
||||
Port: 18555,
|
||||
}
|
||||
|
||||
if err := aliceChannelState.SyncPending(aliceAddr, 0); err != nil {
|
||||
return 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, 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, aliceChannelState, alicePool,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
_ = alicePool.Start()
|
||||
|
||||
bobPool := lnwallet.NewSigPool(1, bobSigner)
|
||||
channelBob, err := lnwallet.NewLightningChannel(
|
||||
bobSigner, bobChannelState, bobPool,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
_ = bobPool.Start()
|
||||
|
||||
chainIO := &mockChainIO{
|
||||
bestHeight: broadcastHeight,
|
||||
}
|
||||
wallet := &lnwallet.LightningWallet{
|
||||
WalletController: &mockWalletController{
|
||||
rootKey: aliceKeyPriv,
|
||||
publishedTxns: publTx,
|
||||
},
|
||||
}
|
||||
|
||||
_, currentHeight, err := chainIO.GetBestBlock()
|
||||
if err != nil {
|
||||
return 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),
|
||||
AckEventTicker: ticker.New(
|
||||
htlcswitch.DefaultAckInterval),
|
||||
}, uint32(currentHeight))
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if err = htlcSwitch.Start(); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
nodeSignerAlice := netann.NewNodeSigner(aliceKeySigner)
|
||||
|
||||
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: htlcSwitch.HasActiveLink,
|
||||
ApplyChannelUpdate: func(*lnwire.ChannelUpdate) error { return nil },
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if err = chanStatusMgr.Start(); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
errBuffer, err := queue.NewCircularBuffer(ErrorBufferSize)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
var pubKey [33]byte
|
||||
copy(pubKey[:], aliceKeyPub.SerializeCompressed())
|
||||
|
||||
cfgAddr := &lnwire.NetAddress{
|
||||
IdentityKey: aliceKeyPub,
|
||||
Address: aliceAddr,
|
||||
ChainNet: wire.SimNet,
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Addr: cfgAddr,
|
||||
PubKeyBytes: pubKey,
|
||||
ErrorBuffer: errBuffer,
|
||||
ChainIO: chainIO,
|
||||
Switch: htlcSwitch,
|
||||
|
||||
ChanActiveTimeout: chanActiveTimeout,
|
||||
InterceptSwitch: htlcswitch.NewInterceptableSwitch(htlcSwitch),
|
||||
|
||||
ChannelDB: dbAlice,
|
||||
FeeEstimator: estimator,
|
||||
Wallet: wallet,
|
||||
ChainNotifier: notifier,
|
||||
ChanStatusMgr: chanStatusMgr,
|
||||
DisconnectPeer: func(b *btcec.PublicKey) error { return nil },
|
||||
}
|
||||
|
||||
alicePeer := NewBrontide(*cfg)
|
||||
|
||||
chanID := lnwire.NewChanIDFromOutPoint(channelAlice.ChannelPoint())
|
||||
alicePeer.activeChannels[chanID] = channelAlice
|
||||
|
||||
alicePeer.wg.Add(1)
|
||||
go alicePeer.channelManager()
|
||||
|
||||
return alicePeer, channelBob, cleanUpFunc, nil
|
||||
}
|
Reference in New Issue
Block a user