multi: Use user provided close address for cooperative closes

This commit is adapted from @Bluetegu's original
pull request #1462.

This commit reads an optional address to pay funds out to
from a user iniitiated close channel address. If the channel
already has a shutdown script set, the request will fail if
an address is provided. Otherwise, the cooperative close will
pay out to the address provided.
This commit is contained in:
carla committed 2019-12-10 09:13:01 +02:00
1 parent ef4d9334b7
commit 94d3eb60a4
7 files changed
+367 -41

No files matched your search

+264 -20
View File
@@ -3,17 +3,32 @@
package lnd
import (
"bytes"
"testing"
"time"
"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/chainfee"
"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) {
@@ -25,7 +40,8 @@ func TestPeerChannelClosureAcceptFeeResponder(t *testing.T) {
broadcastTxChan := make(chan *wire.MsgTx)
responder, responderChan, initiatorChan, cleanUp, err := createTestPeer(
notifier, broadcastTxChan)
notifier, broadcastTxChan, noUpdate,
)
if err != nil {
t.Fatalf("unable to create test channels: %v", err)
}
@@ -45,7 +61,7 @@ func TestPeerChannelClosureAcceptFeeResponder(t *testing.T) {
select {
case outMsg := <-responder.outgoingQueue:
msg = outMsg.msg
case <-time.After(time.Second * 5):
case <-time.After(timeout):
t.Fatalf("did not receive shutdown message")
}
@@ -61,7 +77,7 @@ func TestPeerChannelClosureAcceptFeeResponder(t *testing.T) {
select {
case outMsg := <-responder.outgoingQueue:
msg = outMsg.msg
case <-time.After(time.Second * 5):
case <-time.After(timeout):
t.Fatalf("did not receive ClosingSigned message")
}
@@ -94,7 +110,7 @@ func TestPeerChannelClosureAcceptFeeResponder(t *testing.T) {
// the closing transaction.
select {
case <-broadcastTxChan:
case <-time.After(time.Second * 5):
case <-time.After(timeout):
t.Fatalf("closing tx not broadcast")
}
@@ -113,7 +129,8 @@ func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) {
broadcastTxChan := make(chan *wire.MsgTx)
initiator, initiatorChan, responderChan, cleanUp, err := createTestPeer(
notifier, broadcastTxChan)
notifier, broadcastTxChan, noUpdate,
)
if err != nil {
t.Fatalf("unable to create test channels: %v", err)
}
@@ -136,7 +153,7 @@ func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) {
select {
case outMsg := <-initiator.outgoingQueue:
msg = outMsg.msg
case <-time.After(time.Second * 5):
case <-time.After(timeout):
t.Fatalf("did not receive shutdown request")
}
@@ -184,7 +201,7 @@ func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) {
select {
case outMsg := <-initiator.outgoingQueue:
msg = outMsg.msg
case <-time.After(time.Second * 5):
case <-time.After(timeout):
t.Fatalf("did not receive closing signed message")
}
@@ -202,7 +219,7 @@ func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) {
// the closing transaction.
select {
case <-broadcastTxChan:
case <-time.After(time.Second * 5):
case <-time.After(timeout):
t.Fatalf("closing tx not broadcast")
}
@@ -222,7 +239,7 @@ func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) {
broadcastTxChan := make(chan *wire.MsgTx)
responder, responderChan, initiatorChan, cleanUp, err := createTestPeer(
notifier, broadcastTxChan,
notifier, broadcastTxChan, noUpdate,
)
if err != nil {
t.Fatalf("unable to create test channels: %v", err)
@@ -244,7 +261,7 @@ func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) {
select {
case outMsg := <-responder.outgoingQueue:
msg = outMsg.msg
case <-time.After(time.Second * 5):
case <-time.After(timeout):
t.Fatalf("did not receive shutdown message")
}
@@ -260,7 +277,7 @@ func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) {
select {
case outMsg := <-responder.outgoingQueue:
msg = outMsg.msg
case <-time.After(time.Second * 5):
case <-time.After(timeout):
t.Fatalf("did not receive closing signed message")
}
@@ -296,7 +313,7 @@ func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) {
select {
case outMsg := <-responder.outgoingQueue:
msg = outMsg.msg
case <-time.After(time.Second * 5):
case <-time.After(timeout):
t.Fatalf("did not receive closing signed message")
}
@@ -338,7 +355,7 @@ func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) {
select {
case outMsg := <-responder.outgoingQueue:
msg = outMsg.msg
case <-time.After(time.Second * 5):
case <-time.After(timeout):
t.Fatalf("did not receive closing signed message")
}
@@ -382,7 +399,7 @@ func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) {
// the closing transaction.
select {
case <-broadcastTxChan:
case <-time.After(time.Second * 5):
case <-time.After(timeout):
t.Fatalf("closing tx not broadcast")
}
@@ -402,7 +419,8 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) {
broadcastTxChan := make(chan *wire.MsgTx)
initiator, initiatorChan, responderChan, cleanUp, err := createTestPeer(
notifier, broadcastTxChan)
notifier, broadcastTxChan, noUpdate,
)
if err != nil {
t.Fatalf("unable to create test channels: %v", err)
}
@@ -426,7 +444,7 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) {
select {
case outMsg := <-initiator.outgoingQueue:
msg = outMsg.msg
case <-time.After(time.Second * 5):
case <-time.After(timeout):
t.Fatalf("did not receive shutdown request")
}
@@ -477,7 +495,7 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) {
select {
case outMsg := <-initiator.outgoingQueue:
msg = outMsg.msg
case <-time.After(time.Second * 5):
case <-time.After(timeout):
t.Fatalf("did not receive closing signed")
}
closingSignedMsg, ok := msg.(*lnwire.ClosingSigned)
@@ -495,7 +513,7 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) {
select {
case outMsg := <-initiator.outgoingQueue:
msg = outMsg.msg
case <-time.After(time.Second * 5):
case <-time.After(timeout):
t.Fatalf("did not receive closing signed")
}
closingSignedMsg, ok = msg.(*lnwire.ClosingSigned)
@@ -541,7 +559,7 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) {
select {
case outMsg := <-initiator.outgoingQueue:
msg = outMsg.msg
case <-time.After(time.Second * 5):
case <-time.After(timeout):
t.Fatalf("did not receive closing signed")
}
@@ -584,7 +602,233 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) {
// Wait for closing tx to be broadcasted.
select {
case <-broadcastTxChan:
case <-time.After(time.Second * 5):
case <-time.After(timeout):
t.Fatalf("closing tx not broadcast")
}
}
// 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: 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: errUpfrontShutdownScriptMismatch,
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
notifier := &mockNotfier{
confChannel: make(chan *chainntnfs.TxConfirmation),
}
broadcastTxChan := make(chan *wire.MsgTx)
// Open a channel.
initiator, initiatorChan, _, 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 := initiatorChan.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.
initiator.localCloseChanReqs <- &closeCommand
var msg lnwire.Message
select {
case outMsg := <-initiator.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,
activeNetParams.Params,
)
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
}