From d7bb600c230a7a1ee228c003ad22e5ba7786cac3 Mon Sep 17 00:00:00 2001 From: bryanvu Date: Wed, 8 Mar 2017 15:32:11 -0800 Subject: [PATCH] lnwire: adjusted coop close messages to comply with spec Removed close_request and close_complete and replaced with shutdown and closing_signed. --- lnwire/close_complete.go | 68 ------------------------------- lnwire/close_request.go | 85 -------------------------------------- lnwire/closing_signed.go | 88 ++++++++++++++++++++++++++++++++++++++++ lnwire/lnwire.go | 21 ++++++++++ lnwire/lnwire_test.go | 36 +++++----------- lnwire/message.go | 12 +++--- lnwire/shutdown.go | 81 ++++++++++++++++++++++++++++++++++++ peer.go | 24 ++++++----- 8 files changed, 220 insertions(+), 195 deletions(-) delete mode 100644 lnwire/close_complete.go delete mode 100644 lnwire/close_request.go create mode 100644 lnwire/closing_signed.go create mode 100644 lnwire/shutdown.go diff --git a/lnwire/close_complete.go b/lnwire/close_complete.go deleted file mode 100644 index 2841767c..00000000 --- a/lnwire/close_complete.go +++ /dev/null @@ -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 -} diff --git a/lnwire/close_request.go b/lnwire/close_request.go deleted file mode 100644 index 6510ff42..00000000 --- a/lnwire/close_request.go +++ /dev/null @@ -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 -} diff --git a/lnwire/closing_signed.go b/lnwire/closing_signed.go new file mode 100644 index 00000000..df1c1ade --- /dev/null +++ b/lnwire/closing_signed.go @@ -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 +} diff --git a/lnwire/lnwire.go b/lnwire/lnwire.go index c9b9738f..427a9c2a 100644 --- a/lnwire/lnwire.go +++ b/lnwire/lnwire.go @@ -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) } diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index 216ac5e1..76e99ed1 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -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) }, }, diff --git a/lnwire/message.go b/lnwire/message.go index 4d9e5918..c6166262 100644 --- a/lnwire/message.go +++ b/lnwire/message.go @@ -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: diff --git a/lnwire/shutdown.go b/lnwire/shutdown.go new file mode 100644 index 00000000..c357f3e3 --- /dev/null +++ b/lnwire/shutdown.go @@ -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 +} diff --git a/peer.go b/peer.go index d6e57122..41787d22 100644 --- a/peer.go +++ b/peer.go @@ -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 {