Merge pull request #3655 from carlaKC/fundingmgr-optionupfrontshutdown
Add Option Upfront Shutdown
This commit is contained in:
21 files changed
+826
-81
No files matched your search
+84
-2
@@ -43,6 +43,16 @@ var (
|
||||
// funding flow.
|
||||
chanInfoKey = []byte("chan-info-key")
|
||||
|
||||
// localUpfrontShutdownKey can be accessed within the bucket for a channel
|
||||
// (identified by its chanPoint). This key stores an optional upfront
|
||||
// shutdown script for the local peer.
|
||||
localUpfrontShutdownKey = []byte("local-upfront-shutdown-key")
|
||||
|
||||
// remoteUpfrontShutdownKey can be accessed within the bucket for a channel
|
||||
// (identified by its chanPoint). This key stores an optional upfront
|
||||
// shutdown script for the remote peer.
|
||||
remoteUpfrontShutdownKey = []byte("remote-upfront-shutdown-key")
|
||||
|
||||
// chanCommitmentKey can be accessed within the sub-bucket for a
|
||||
// particular channel. This key stores the up to date commitment state
|
||||
// for a particular channel party. Appending a 0 to the end of this key
|
||||
@@ -551,6 +561,16 @@ type OpenChannel struct {
|
||||
// method on the ChanType field.
|
||||
FundingTxn *wire.MsgTx
|
||||
|
||||
// LocalShutdownScript is set to a pre-set script if the channel was opened
|
||||
// by the local node with option_upfront_shutdown_script set. If the option
|
||||
// was not set, the field is empty.
|
||||
LocalShutdownScript lnwire.DeliveryAddress
|
||||
|
||||
// RemoteShutdownScript is set to a pre-set script if the channel was opened
|
||||
// by the remote node with option_upfront_shutdown_script set. If the option
|
||||
// was not set, the field is empty.
|
||||
RemoteShutdownScript lnwire.DeliveryAddress
|
||||
|
||||
// TODO(roasbeef): eww
|
||||
Db *DB
|
||||
|
||||
@@ -2573,7 +2593,60 @@ func putChanInfo(chanBucket *bbolt.Bucket, channel *OpenChannel) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return chanBucket.Put(chanInfoKey, w.Bytes())
|
||||
if err := chanBucket.Put(chanInfoKey, w.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Finally, add optional shutdown scripts for the local and remote peer if
|
||||
// they are present.
|
||||
if err := putOptionalUpfrontShutdownScript(
|
||||
chanBucket, localUpfrontShutdownKey, channel.LocalShutdownScript,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return putOptionalUpfrontShutdownScript(
|
||||
chanBucket, remoteUpfrontShutdownKey, channel.RemoteShutdownScript,
|
||||
)
|
||||
}
|
||||
|
||||
// putOptionalUpfrontShutdownScript adds a shutdown script under the key
|
||||
// provided if it has a non-zero length.
|
||||
func putOptionalUpfrontShutdownScript(chanBucket *bbolt.Bucket, key []byte,
|
||||
script []byte) error {
|
||||
// If the script is empty, we do not need to add anything.
|
||||
if len(script) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var w bytes.Buffer
|
||||
if err := WriteElement(&w, script); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return chanBucket.Put(key, w.Bytes())
|
||||
}
|
||||
|
||||
// getOptionalUpfrontShutdownScript reads the shutdown script stored under the
|
||||
// key provided if it is present. Upfront shutdown scripts are optional, so the
|
||||
// function returns with no error if the key is not present.
|
||||
func getOptionalUpfrontShutdownScript(chanBucket *bbolt.Bucket, key []byte,
|
||||
script *lnwire.DeliveryAddress) error {
|
||||
|
||||
// Return early if the bucket does not exit, a shutdown script was not set.
|
||||
bs := chanBucket.Get(key)
|
||||
if bs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var tempScript []byte
|
||||
r := bytes.NewReader(bs)
|
||||
if err := ReadElement(r, &tempScript); err != nil {
|
||||
return err
|
||||
}
|
||||
*script = tempScript
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func serializeChanCommit(w io.Writer, c *ChannelCommitment) error {
|
||||
@@ -2696,7 +2769,16 @@ func fetchChanInfo(chanBucket *bbolt.Bucket, channel *OpenChannel) error {
|
||||
|
||||
channel.Packager = NewChannelPackager(channel.ShortChannelID)
|
||||
|
||||
return nil
|
||||
// Finally, read the optional shutdown scripts.
|
||||
if err := getOptionalUpfrontShutdownScript(
|
||||
chanBucket, localUpfrontShutdownKey, &channel.LocalShutdownScript,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return getOptionalUpfrontShutdownScript(
|
||||
chanBucket, remoteUpfrontShutdownKey, &channel.RemoteShutdownScript,
|
||||
)
|
||||
}
|
||||
|
||||
func deserializeChanCommit(r io.Reader) (ChannelCommitment, error) {
|
||||
|
||||
@@ -345,6 +345,101 @@ func TestOpenChannelPutGetDelete(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestOptionalShutdown tests the reading and writing of channels with and
|
||||
// without optional shutdown script fields.
|
||||
func TestOptionalShutdown(t *testing.T) {
|
||||
local := lnwire.DeliveryAddress([]byte("local shutdown script"))
|
||||
remote := lnwire.DeliveryAddress([]byte("remote shutdown script"))
|
||||
|
||||
if _, err := rand.Read(remote); err != nil {
|
||||
t.Fatalf("Could not create random script: %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
modifyChannel func(channel *OpenChannel)
|
||||
expectedLocal lnwire.DeliveryAddress
|
||||
expectedRemote lnwire.DeliveryAddress
|
||||
}{
|
||||
{
|
||||
name: "no shutdown scripts",
|
||||
modifyChannel: func(channel *OpenChannel) {},
|
||||
},
|
||||
{
|
||||
name: "local shutdown script",
|
||||
modifyChannel: func(channel *OpenChannel) {
|
||||
channel.LocalShutdownScript = local
|
||||
},
|
||||
expectedLocal: local,
|
||||
},
|
||||
{
|
||||
name: "remote shutdown script",
|
||||
modifyChannel: func(channel *OpenChannel) {
|
||||
channel.RemoteShutdownScript = remote
|
||||
},
|
||||
expectedRemote: remote,
|
||||
},
|
||||
{
|
||||
name: "both scripts set",
|
||||
modifyChannel: func(channel *OpenChannel) {
|
||||
channel.LocalShutdownScript = local
|
||||
channel.RemoteShutdownScript = remote
|
||||
},
|
||||
expectedLocal: local,
|
||||
expectedRemote: remote,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
cdb, cleanUp, err := makeTestDB()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to make test database: %v", err)
|
||||
}
|
||||
defer cleanUp()
|
||||
|
||||
// Create the test channel state, then add an additional fake HTLC
|
||||
// before syncing to disk.
|
||||
state, err := createTestChannelState(cdb)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create channel state: %v", err)
|
||||
}
|
||||
|
||||
test.modifyChannel(state)
|
||||
|
||||
// Write channels to Db.
|
||||
addr := &net.TCPAddr{
|
||||
IP: net.ParseIP("127.0.0.1"),
|
||||
Port: 18556,
|
||||
}
|
||||
if err := state.SyncPending(addr, 101); err != nil {
|
||||
t.Fatalf("unable to save and serialize channel state: %v", err)
|
||||
}
|
||||
|
||||
openChannels, err := cdb.FetchOpenChannels(state.IdentityPub)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to fetch open channel: %v", err)
|
||||
}
|
||||
|
||||
if len(openChannels) != 1 {
|
||||
t.Fatalf("Expected one channel open, got: %v", len(openChannels))
|
||||
}
|
||||
|
||||
if !bytes.Equal(openChannels[0].LocalShutdownScript, test.expectedLocal) {
|
||||
t.Fatalf("Expected local: %x, got: %x", test.expectedLocal,
|
||||
openChannels[0].LocalShutdownScript)
|
||||
}
|
||||
|
||||
if !bytes.Equal(openChannels[0].RemoteShutdownScript, test.expectedRemote) {
|
||||
t.Fatalf("Expected remote: %x, got: %x", test.expectedRemote,
|
||||
openChannels[0].RemoteShutdownScript)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func assertCommitmentEqual(t *testing.T, a, b *ChannelCommitment) {
|
||||
if !reflect.DeepEqual(a, b) {
|
||||
_, _, line, _ := runtime.Caller(1)
|
||||
|
||||
Reference in new issue
Block a user