lnwire: adjusted coop close messages to comply with spec

Removed close_request and close_complete and replaced with shutdown and
closing_signed.
This commit is contained in:
bryanvu 2017-03-08 15:32:11 -08:00 committed by Olaoluwa Osuntokun
parent 570c0e5508
commit d7bb600c23
8 changed files with 220 additions and 195 deletions

View File

@ -1,68 +0,0 @@
package lnwire
import (
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/wire"
"io"
)
// CloseComplete is sent by Bob signalling a fufillment and completion of
// Alice's prior CloseRequest message. After Alice receives Bob's CloseComplete
// message, she is able to broadcast the fully signed transaction executing a
// cooperative closure of the channel.
//
// NOTE: The responder is able to only send a signature without any additional
// message as all transactions are assembled observing BIP 69 which defines a
// cannonical ordering for input/outputs. Therefore, both sides are able to
// arrive at an identical closure transaction as they know the order of the
// inputs/outputs.
type CloseComplete struct {
// ChannelPoint serves to identify which channel is to be closed.
ChannelPoint wire.OutPoint
// ResponderCloseSig is the signature of the responder for the
// transaction which closes the previously active channel.
ResponderCloseSig *btcec.Signature
}
// A compile time check to ensure CloseComplete implements the lnwire.Message
// interface.
var _ Message = (*CloseComplete)(nil)
// Decode deserializes a serialized CloseComplete message stored in the passed
// io.Reader observing the specified protocol version.
//
// This is part of the lnwire.Message interface.
func (c *CloseComplete) Decode(r io.Reader, pver uint32) error {
return readElements(r,
&c.ChannelPoint,
&c.ResponderCloseSig)
}
// Encode serializes the target CloseComplete into the passed io.Writer observing
// the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (c *CloseComplete) Encode(w io.Writer, pver uint32) error {
return writeElements(w,
c.ChannelPoint,
c.ResponderCloseSig)
}
// MsgType returns the integer uniquely identifying this message type on the
// wire.
//
// This is part of the lnwire.Message interface.
func (c *CloseComplete) MsgType() MessageType {
return MsgCloseComplete
}
// MaxPayloadLength returns the maximum allowed payload size for a CloseComplete
// complete message observing the specified protocol version.
//
// This is part of the lnwire.Message interface.
func (c *CloseComplete) MaxPayloadLength(uint32) uint32 {
// 141 + 73 + 32
return 141
}

View File

@ -1,85 +0,0 @@
package lnwire
import (
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcutil"
"io"
)
// CloseRequest is sent by either side in order to initiate the cooperative
// closure of a channel. This message is rather sparse as both side implicitly
// know to craft a transaction sending the settled funds of both parties to the
// final delivery addresses negotiated during the funding workflow.
//
// NOTE: The requester is able to only send a signature to initiate the
// cooperative channel closure as all transactions are assembled observing
// BIP 69 which defines a cannonical ordering for input/outputs. Therefore,
// both sides are able to arrive at an identical closure transaction as they
// know the order of the inputs/outputs.
type CloseRequest struct {
// ChanID serves to identify which channel is to be closed.
ChanID ChannelID
// RequesterCloseSig is the signature of the requester for the fully
// assembled closing transaction.
RequesterCloseSig *btcec.Signature
// Fee is the required fee-per-KB the closing transaction must have.
// It is recommended that a "sufficient" fee be paid in order to
// achieve timely channel closure.
// TODO(roasbeef): if initiator always pays fees, then no longer needed.
Fee btcutil.Amount
}
// NewCloseRequest creates a new CloseRequest.
func NewCloseRequest(cid ChannelID, sig *btcec.Signature) *CloseRequest {
// TODO(roasbeef): update once fees aren't hardcoded
return &CloseRequest{
ChanID: cid,
RequesterCloseSig: sig,
}
}
// A compile time check to ensure CloseRequest implements the lnwire.Message
// interface.
var _ Message = (*CloseRequest)(nil)
// Decode deserializes a serialized CloseRequest stored in the passed io.Reader
// observing the specified protocol version.
//
// This is part of the lnwire.Message interface.
func (c *CloseRequest) Decode(r io.Reader, pver uint32) error {
return readElements(r,
&c.ChanID,
&c.RequesterCloseSig,
&c.Fee)
}
// Encode serializes the target CloseRequest into the passed io.Writer observing
// the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (c *CloseRequest) Encode(w io.Writer, pver uint32) error {
return writeElements(w,
c.ChanID,
c.RequesterCloseSig,
c.Fee)
}
// MsgType returns the integer uniquely identifying this message type on the
// wire.
//
// This is part of the lnwire.Message interface.
func (c *CloseRequest) MsgType() MessageType {
return MsgCloseRequest
}
// MaxPayloadLength returns the maximum allowed payload size for this message
// observing the specified protocol version.
//
// This is part of the lnwire.Message interface.
func (c *CloseRequest) MaxPayloadLength(pver uint32) uint32 {
// 36 + 73 + 8
return 117
}

88
lnwire/closing_signed.go Normal file
View File

@ -0,0 +1,88 @@
package lnwire
import (
"io"
"github.com/roasbeef/btcd/btcec"
)
// ClosingSigned is sent by both parties to a channel once the channel is clear
// of HTLCs, and is primarily concerned with negotiating fees for the close
// transaction. Each party provides a signature for a transaction with a fee
// that they believe is fair. The process terminates when both sides agree on
// the same fee, or when one side force closes the channel.
//
// NOTE: The responder is able to send a signature without any additional
// messages as all transactions are assembled observing BIP 69 which defines a
// cannonical ordering for input/outputs. Therefore, both sides are able to
// arrive at an identical closure transaction as they know the order of the
// inputs/outputs.
type ClosingSigned struct {
// ChannelID serves to identify which channel is to be closed.
ChannelID ChannelID
// FeeSatoshis is the fee that the party to the channel would like to
// propose for the close transaction.
FeeSatoshis uint64
// Signature is for the proposed channel close transaction.
Signature *btcec.Signature
}
// NewClosingSigned creates a new empty ClosingSigned message.
func NewClosingSigned(cid ChannelID, fs uint64,
sig *btcec.Signature) *ClosingSigned {
return &ClosingSigned{
ChannelID: cid,
FeeSatoshis: fs,
Signature: sig,
}
}
// A compile time check to ensure ClosingSigned implements the lnwire.Message
// interface.
var _ Message = (*ClosingSigned)(nil)
// Decode deserializes a serialized ClosingSigned message stored in the passed
// io.Reader observing the specified protocol version.
//
// This is part of the lnwire.Message interface.
func (c *ClosingSigned) Decode(r io.Reader, pver uint32) error {
return readElements(r, &c.ChannelID, &c.FeeSatoshis, &c.Signature)
}
// Encode serializes the target ClosingSigned into the passed io.Writer
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (c *ClosingSigned) Encode(w io.Writer, pver uint32) error {
return writeElements(w, c.ChannelID, c.FeeSatoshis, c.Signature)
}
// MsgType returns the integer uniquely identifying this message type on the
// wire.
//
// This is part of the lnwire.Message interface.
func (c *ClosingSigned) MsgType() MessageType {
return MsgClosingSigned
}
// MaxPayloadLength returns the maximum allowed payload size for a
// ClosingSigned complete message observing the specified protocol version.
//
// This is part of the lnwire.Message interface.
func (c *ClosingSigned) MaxPayloadLength(uint32) uint32 {
var length uint32
// ChannelID - 32 bytes
length += 32
// FeeSatoshis - 8 bytes
length += 8
// Signature - 64 bytes
length += 64
return length
}

View File

@ -267,6 +267,15 @@ func writeElement(w io.Writer, element interface{}) error {
if err := writeElements(w, e.data[:]); err != nil {
return err
}
case DeliveryAddress:
var length [2]byte
binary.BigEndian.PutUint16(length[:], uint16(len(e)))
if _, err := w.Write(length[:]); err != nil {
return err
}
if _, err := w.Write(e[:]); err != nil {
return err
}
default:
return fmt.Errorf("Unknown type in writeElement: %T", e)
@ -515,6 +524,18 @@ func readElement(r io.Reader, element interface{}) error {
}
*e = newAlias(a[:])
case *DeliveryAddress:
var addrLen [2]byte
if _, err = io.ReadFull(r, addrLen[:]); err != nil {
return err
}
length := binary.BigEndian.Uint16(addrLen[:])
var addrBytes [34]byte
if _, err = io.ReadFull(r, addrBytes[:length]); err != nil {
return err
}
*e = addrBytes[:length]
default:
return fmt.Errorf("Unknown type in readElement: %T", e)
}

View File

@ -273,31 +273,17 @@ func TestLightningWireProtocol(t *testing.T) {
v[0] = reflect.ValueOf(*req)
},
MsgCloseRequest: func(v []reflect.Value, r *rand.Rand) {
var chanID [32]byte
if _, err := r.Read(chanID[:]); err != nil {
MsgClosingSigned: func(v []reflect.Value, r *rand.Rand) {
req := ClosingSigned{
FeeSatoshis: uint64(r.Int63()),
Signature: testSig,
}
if _, err := r.Read(req.ChannelID[:]); err != nil {
t.Fatalf("unable to generate chan id: %v", err)
return
}
req := NewCloseRequest(ChannelID(chanID), testSig)
req.Fee = btcutil.Amount(rand.Int63())
req.RequesterCloseSig = testSig
v[0] = reflect.ValueOf(*req)
},
MsgCloseComplete: func(v []reflect.Value, r *rand.Rand) {
req := CloseComplete{}
if _, err := r.Read(req.ChannelPoint.Hash[:]); err != nil {
t.Fatalf("unable to generate hash: %v", err)
return
}
req.ChannelPoint.Index = uint32(r.Int31()) % math.MaxUint16
req.ResponderCloseSig = testSig
v[0] = reflect.ValueOf(req)
},
MsgCommitSig: func(v []reflect.Value, r *rand.Rand) {
@ -490,14 +476,14 @@ func TestLightningWireProtocol(t *testing.T) {
},
},
{
msgType: MsgCloseRequest,
scenario: func(m CloseRequest) bool {
msgType: MsgShutdown,
scenario: func(m Shutdown) bool {
return mainScenario(&m)
},
},
{
msgType: MsgCloseComplete,
scenario: func(m CloseComplete) bool {
msgType: MsgClosingSigned,
scenario: func(m ClosingSigned) bool {
return mainScenario(&m)
},
},

View File

@ -32,8 +32,8 @@ const (
MsgSingleFundingComplete = 34
MsgSingleFundingSignComplete = 35
MsgFundingLocked = 36
MsgCloseRequest = 39
MsgCloseComplete = 40
MsgShutdown = 39
MsgClosingSigned = 40
MsgUpdateAddHTLC = 128
MsgUpdateFufillHTLC = 130
MsgUpdateFailHTLC = 131
@ -87,10 +87,10 @@ func makeEmptyMessage(msgType MessageType) (Message, error) {
msg = &SingleFundingSignComplete{}
case MsgFundingLocked:
msg = &FundingLocked{}
case MsgCloseRequest:
msg = &CloseRequest{}
case MsgCloseComplete:
msg = &CloseComplete{}
case MsgShutdown:
msg = &Shutdown{}
case MsgClosingSigned:
msg = &ClosingSigned{}
case MsgUpdateAddHTLC:
msg = &UpdateAddHTLC{}
case MsgUpdateFailHTLC:

81
lnwire/shutdown.go Normal file
View File

@ -0,0 +1,81 @@
package lnwire
import (
"io"
)
// Shutdown is sent by either side in order to initiate the cooperative closure
// of a channel. This message is sparse as both sides implicitly have the
// information necessary to construct a transaction that will send the settled
// funds of both parties to the final delivery addresses negotiated during the
// funding workflow.
type Shutdown struct {
// ChannelID serves to identify which channel is to be closed.
ChannelID ChannelID
// Address is the script to which
Address DeliveryAddress
}
// DeliveryAddress is used to communicate the address to which funds from a
// closed channel should be sent. The address can be a p2wsh, p2pkh, p2sh or
// p2wpkh.
type DeliveryAddress []byte
// NewShutdown creates a new Shutdown message.
func NewShutdown(cid ChannelID, addr DeliveryAddress) *Shutdown {
return &Shutdown{
ChannelID: cid,
Address: addr,
}
}
// A compile-time check to ensure Shutdown implements the lnwire.Message
// interface.
var _ Message = (*Shutdown)(nil)
// Decode deserializes a serialized Shutdown stored in the passed io.Reader
// observing the specified protocol version.
//
// This is part of the lnwire.Message interface.
func (s *Shutdown) Decode(r io.Reader, pver uint32) error {
return readElements(r, &s.ChannelID, &s.Address)
}
// Encode serializes the target Shutdown into the passed io.Writer observing
// the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (s *Shutdown) Encode(w io.Writer, pver uint32) error {
return writeElements(w, s.ChannelID, s.Address)
}
// MsgType returns the integer uniquely identifying this message type on the
// wire.
//
// This is part of the lnwire.Message interface.
func (s *Shutdown) MsgType() MessageType {
return MsgShutdown
}
// MaxPayloadLength returns the maximum allowed payload size for this message
// observing the specified protocol version.
//
// This is part of the lnwire.Message interface.
func (s *Shutdown) MaxPayloadLength(pver uint32) uint32 {
var length uint32
// ChannelID - 32bytes
length += 32
// Len - 2 bytes
length += 2
// ScriptPubKey - 34 bytes for pay to witness script hash
length += 34
// NOTE: pay to pubkey hash is 25 bytes, pay to script hash is 22
// bytes, and pay to witness pubkey hash is 22 bytes in length.
return length
}

24
peer.go
View File

@ -11,6 +11,7 @@ import (
"sync/atomic"
"time"
"github.com/btcsuite/btcd/txscript"
"github.com/davecgh/go-spew/spew"
"github.com/go-errors/errors"
"github.com/lightningnetwork/lightning-onion"
@ -23,7 +24,6 @@ import (
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/connmgr"
"github.com/roasbeef/btcd/txscript"
"github.com/roasbeef/btcd/wire"
"github.com/roasbeef/btcutil"
)
@ -136,7 +136,7 @@ type peer struct {
// remoteCloseChanReqs is a channel in which any remote requests
// (initiated by the remote peer) close a particular channel are sent
// over.
remoteCloseChanReqs chan *lnwire.CloseRequest
remoteCloseChanReqs chan *lnwire.ClosingSigned
server *server
@ -183,7 +183,7 @@ func newPeer(conn net.Conn, connReq *connmgr.ConnReq, server *server,
newChannels: make(chan *newChannelMsg, 1),
localCloseChanReqs: make(chan *closeLinkReq),
remoteCloseChanReqs: make(chan *lnwire.CloseRequest),
remoteCloseChanReqs: make(chan *lnwire.ClosingSigned),
localSharedFeatures: nil,
globalSharedFeatures: nil,
@ -445,7 +445,7 @@ out:
p.server.fundingMgr.processFundingSignComplete(msg, p.addr)
case *lnwire.FundingLocked:
p.server.fundingMgr.processFundingLocked(msg, p.addr)
case *lnwire.CloseRequest:
case *lnwire.ClosingSigned:
p.remoteCloseChanReqs <- msg
case *lnwire.Error:
@ -837,7 +837,7 @@ func (p *peer) executeCooperativeClose(channel *lnwallet.LightningChannel) (*cha
}
chanID := lnwire.NewChanIDFromOutPoint(chanPoint)
closeReq := lnwire.NewCloseRequest(chanID, closeSig)
closeReq := lnwire.NewClosingSigned(chanID, 5000, closeSig)
p.queueMsg(closeReq, nil)
return txid, nil
@ -960,21 +960,23 @@ func (p *peer) handleLocalClose(req *closeLinkReq) {
// handleRemoteClose completes a request for cooperative channel closure
// initiated by the remote node.
func (p *peer) handleRemoteClose(req *lnwire.CloseRequest) {
func (p *peer) handleRemoteClose(req *lnwire.ClosingSigned) {
p.activeChanMtx.RLock()
channel, ok := p.activeChannels[req.ChanID]
channel, ok := p.activeChannels[req.ChannelID]
p.activeChanMtx.RUnlock()
if !ok {
peerLog.Errorf("unable to close channel, ChannelID(%v) is "+
"unknown", req.ChanID)
"unknown", req.ChannelID)
return
}
chanPoint := channel.ChannelPoint()
// Now that we have their signature for the closure transaction, we can
// assemble the final closure transaction, complete with our signature.
sig := req.RequesterCloseSig
// Now that we have their signature for the closure transaction, we
// can assemble the final closure transaction, complete with our
// signature.
sig := req.Signature
closeSig := append(sig.Serialize(), byte(txscript.SigHashAll))
closeTx, err := channel.CompleteCooperativeClose(closeSig)
if err != nil {