diff --git a/lnwire/message.go b/lnwire/message.go index 727a2385..eca3159b 100644 --- a/lnwire/message.go +++ b/lnwire/message.go @@ -54,6 +54,10 @@ const ( // Commands for reporting protocol errors. CmdErrorGeneric = uint32(4000) + + // Commands for connection keep-alive. + CmdPing = uint32(5000) + CmdPong = uint32(5010) ) // Message is an interface that defines a lightning wire protocol message. The @@ -65,7 +69,6 @@ type Message interface { Command() uint32 MaxPayloadLength(uint32) uint32 Validate() error - String() string } // makeEmptyMessage creates a new empty message of the proper concrete type @@ -114,6 +117,10 @@ func makeEmptyMessage(command uint32) (Message, error) { msg = &RoutingTableRequestMessage{} case CmdRoutingTableTransferMessage: msg = &RoutingTableTransferMessage{} + case CmdPing: + msg = &Ping{} + case CmdPong: + msg = &Pong{} default: return nil, fmt.Errorf("unhandled command [%d]", command) } diff --git a/lnwire/ping.go b/lnwire/ping.go new file mode 100644 index 00000000..a8b03a6e --- /dev/null +++ b/lnwire/ping.go @@ -0,0 +1,77 @@ +package lnwire + +import "io" + +// Pong defines a message which is the direct response to a received Ping +// message. A Pong reply indicates that a connection is still active. The Pong +// reply to a Ping message should contain the nonce carried in the original +// Pong message. +type Pong struct { + // Nonce is the unique nonce that was associated with the Ping message + // that this Pong is replying to. + Nonce uint64 +} + +// NewPong returns a new Pong message binded to the specified nonce. +func NewPong(nonce uint64) *Pong { + return &Pong{ + Nonce: nonce, + } +} + +// A compile time check to ensure Pong implements the lnwire.Message interface. +var _ Message = (*Pong)(nil) + +// Decode deserializes a serialized Pong message stored in the passed io.Reader +// observing the specified protocol version. +// +// This is part of the lnwire.Message interface. +func (p *Pong) Decode(r io.Reader, pver uint32) error { + err := readElements(r, + &p.Nonce, + ) + if err != nil { + return err + } + + return nil +} + +// Encode serializes the target Pong into the passed io.Writer observing the +// protocol version specified. +// +// This is part of the lnwire.Message interface. +func (p *Pong) Encode(w io.Writer, pver uint32) error { + err := writeElements(w, + p.Nonce, + ) + if err != nil { + return err + } + + return nil +} + +// Command returns the integer uniquely identifying this message type on the +// wire. +// +// This is part of the lnwire.Message interface. +func (p *Pong) Command() uint32 { + return CmdPong +} + +// MaxPayloadLength returns the maximum allowed payload size for a Pong +// complete message observing the specified protocol version. +// +// This is part of the lnwire.Message interface. +func (p *Pong) MaxPayloadLength(uint32) uint32 { + return 8 +} + +// Validate performs any necessary sanity checks to ensure all fields present +// on the Pong are valid. +// +// This is part of the lnwire.Message interface. +func (p *Pong) Validate() error { + return nil +} diff --git a/lnwire/ping_test.go b/lnwire/ping_test.go new file mode 100644 index 00000000..c0b5a2f1 --- /dev/null +++ b/lnwire/ping_test.go @@ -0,0 +1,31 @@ +package lnwire + +import ( + "bytes" + "reflect" + "testing" +) + +func TestPongEncodeDecode(t *testing.T) { + pong := &Pong{ + Nonce: 9, + } + + // Next encode the pong message into an empty bytes buffer. + var b bytes.Buffer + if err := pong.Encode(&b, 0); err != nil { + t.Fatalf("unable to encode pong: %v", err) + } + + // Deserialize the encoded pong message into a new empty struct. + pong2 := &Pong{} + if err := pong2.Decode(&b, 0); err != nil { + t.Fatalf("unable to decode ping: %v", err) + } + + // Assert equality of the two instances. + if !reflect.DeepEqual(pong, pong2) { + t.Fatalf("encode/decode pong messages don't match %#v vs %#v", + pong, pong2) + } +} diff --git a/lnwire/pong.go b/lnwire/pong.go new file mode 100644 index 00000000..acfb89e9 --- /dev/null +++ b/lnwire/pong.go @@ -0,0 +1,76 @@ +package lnwire + +import "io" + +// Ping defines a message which is sent by peers periodically to determine if +// the connection is still valid. Each ping message should carry a unique nonce +// which is to be echoed back within the Pong response. +type Ping struct { + // Nonce is a unique value associated with this ping message. The pong + // message that responds to this ping should reference the same value. + Nonce uint64 +} + +// NewPing returns a new Ping message binded to the specified nonce. +func NewPing(nonce uint64) *Ping { + return &Ping{ + Nonce: nonce, + } +} + +// A compile time check to ensure Ping implements the lnwire.Message interface. +var _ Message = (*Ping)(nil) + +// Decode deserializes a serialized Ping message stored in the passed io.Reader +// observing the specified protocol version. +// +// This is part of the lnwire.Message interface. +func (p *Ping) Decode(r io.Reader, pver uint32) error { + err := readElements(r, + &p.Nonce, + ) + if err != nil { + return err + } + + return nil +} + +// Encode serializes the target Ping into the passed io.Writer observing the +// protocol version specified. +// +// This is part of the lnwire.Message interface. +func (p *Ping) Encode(w io.Writer, pver uint32) error { + err := writeElements(w, + p.Nonce, + ) + if err != nil { + return err + } + + return nil +} + +// Command returns the integer uniquely identifying this message type on the +// wire. +// +// This is part of the lnwire.Message interface. +func (p *Ping) Command() uint32 { + return CmdPing +} + +// MaxPayloadLength returns the maximum allowed payload size for a Ping +// complete message observing the specified protocol version. +// +// This is part of the lnwire.Message interface. +func (p Ping) MaxPayloadLength(uint32) uint32 { + return 8 +} + +// Validate performs any necessary sanity checks to ensure all fields present +// on the Ping are valid. +// +// This is part of the lnwire.Message interface. +func (p *Ping) Validate() error { + return nil +} diff --git a/lnwire/pong_test.go b/lnwire/pong_test.go new file mode 100644 index 00000000..896a543d --- /dev/null +++ b/lnwire/pong_test.go @@ -0,0 +1,31 @@ +package lnwire + +import ( + "bytes" + "reflect" + "testing" +) + +func TestPingEncodeDecode(t *testing.T) { + ping := &Ping{ + Nonce: 9, + } + + // Next encode the ping message into an empty bytes buffer. + var b bytes.Buffer + if err := ping.Encode(&b, 0); err != nil { + t.Fatalf("unable to encode ping: %v", err) + } + + // Deserialize the encoded ping message into a new empty struct. + ping2 := &Ping{} + if err := ping2.Decode(&b, 0); err != nil { + t.Fatalf("unable to decode ping: %v", err) + } + + // Assert equality of the two instances. + if !reflect.DeepEqual(ping, ping2) { + t.Fatalf("encode/decode ping messages don't match %#v vs %#v", + ping, ping2) + } +}