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:
parent
570c0e5508
commit
d7bb600c23
@ -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
|
||||
}
|
@ -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
88
lnwire/closing_signed.go
Normal 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
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
},
|
||||
},
|
||||
|
@ -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
81
lnwire/shutdown.go
Normal 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
24
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 {
|
||||
|
Loading…
Reference in New Issue
Block a user