lnwire+peer: implement new ping/pong messages and behavior
This commit implements the new ping/pong messages along with their new behavior. The new set of ping/pong messages allow clients to generate fake cover traffic as the ping messages tells the pong message how many bytes to included and can also be padded itself.
This commit is contained in:
parent
83c72eb082
commit
feec611531
@ -8,7 +8,7 @@ import (
|
||||
|
||||
func TestAnnounceSignatureEncodeDecode(t *testing.T) {
|
||||
ac := &AnnounceSignatures{
|
||||
ChannelID: *outpoint1,
|
||||
ChannelID: ChannelID(revHash),
|
||||
ShortChannelID: NewShortChanIDFromInt(1),
|
||||
NodeSignature: someSig,
|
||||
BitcoinSignature: someSig,
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
|
||||
func TestCloseRequestEncodeDecode(t *testing.T) {
|
||||
cr := &CloseRequest{
|
||||
ChannelPoint: *outpoint1,
|
||||
ChanID: ChannelID(revHash),
|
||||
RequesterCloseSig: commitSig,
|
||||
Fee: btcutil.Amount(10000),
|
||||
}
|
||||
|
@ -174,6 +174,26 @@ func writeElement(w io.Writer, element interface{}) error {
|
||||
if _, err := w.Write(b[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
case PingPayload:
|
||||
var l [2]byte
|
||||
binary.BigEndian.PutUint16(l[:], uint16(len(e)))
|
||||
if _, err := w.Write(l[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(e[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
case PongPayload:
|
||||
var l [2]byte
|
||||
binary.BigEndian.PutUint16(l[:], uint16(len(e)))
|
||||
if _, err := w.Write(l[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(e[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
case ErrorData:
|
||||
var l [2]byte
|
||||
binary.BigEndian.PutUint16(l[:], uint16(len(e)))
|
||||
@ -535,6 +555,28 @@ func readElement(r io.Reader, element interface{}) error {
|
||||
if _, err := io.ReadFull(r, *e); err != nil {
|
||||
return err
|
||||
}
|
||||
case *PingPayload:
|
||||
var l [2]byte
|
||||
if _, err := io.ReadFull(r, l[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
pingLen := binary.BigEndian.Uint16(l[:])
|
||||
|
||||
*e = PingPayload(make([]byte, pingLen))
|
||||
if _, err := io.ReadFull(r, *e); err != nil {
|
||||
return err
|
||||
}
|
||||
case *PongPayload:
|
||||
var l [2]byte
|
||||
if _, err := io.ReadFull(r, l[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
pongLen := binary.BigEndian.Uint16(l[:])
|
||||
|
||||
*e = PongPayload(make([]byte, pongLen))
|
||||
if _, err := io.ReadFull(r, *e); err != nil {
|
||||
return err
|
||||
}
|
||||
case *[6]byte:
|
||||
if _, err := io.ReadFull(r, e[:]); err != nil {
|
||||
return err
|
||||
|
@ -2,19 +2,28 @@ package lnwire
|
||||
|
||||
import "io"
|
||||
|
||||
// PingPayload is a set of opaque bytes used to pad out a ping message.
|
||||
type PingPayload []byte
|
||||
|
||||
// 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.
|
||||
// the connection is still valid. Each ping message carries the number of bytes
|
||||
// to pad the pong response with, and also a number of bytes to be ignored at
|
||||
// the end of the ping message (which is padding).
|
||||
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
|
||||
// NumPongBytes is the number of bytes the pong response to this
|
||||
// message should carry.
|
||||
NumPongBytes uint16
|
||||
|
||||
// PaddingBytes is a set of opaque bytes used to pad out this ping
|
||||
// message. Using this field in conjunction to the one above, it's
|
||||
// possible for node to generate fake cover traffic.
|
||||
PaddingBytes PingPayload
|
||||
}
|
||||
|
||||
// NewPing returns a new Ping message binded to the specified nonce.
|
||||
func NewPing(nonce uint64) *Ping {
|
||||
// NewPing returns a new Ping message.
|
||||
func NewPing(numBytes uint16) *Ping {
|
||||
return &Ping{
|
||||
Nonce: nonce,
|
||||
NumPongBytes: numBytes,
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,8 +36,8 @@ var _ Message = (*Ping)(nil)
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (p *Ping) Decode(r io.Reader, pver uint32) error {
|
||||
return readElements(r,
|
||||
&p.Nonce,
|
||||
)
|
||||
&p.NumPongBytes,
|
||||
&p.PaddingBytes)
|
||||
}
|
||||
|
||||
// Encode serializes the target Ping into the passed io.Writer observing the
|
||||
@ -37,8 +46,8 @@ func (p *Ping) Decode(r io.Reader, pver uint32) error {
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (p *Ping) Encode(w io.Writer, pver uint32) error {
|
||||
return writeElements(w,
|
||||
p.Nonce,
|
||||
)
|
||||
p.NumPongBytes,
|
||||
p.PaddingBytes)
|
||||
}
|
||||
|
||||
// Command returns the integer uniquely identifying this message type on the
|
||||
@ -54,7 +63,7 @@ func (p *Ping) Command() uint32 {
|
||||
//
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (p Ping) MaxPayloadLength(uint32) uint32 {
|
||||
return 8
|
||||
return 65532
|
||||
}
|
||||
|
||||
// Validate performs any necessary sanity checks to ensure all fields present
|
||||
|
@ -6,26 +6,27 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPongEncodeDecode(t *testing.T) {
|
||||
pong := &Pong{
|
||||
Nonce: 9,
|
||||
func TestPingEncodeDecode(t *testing.T) {
|
||||
ping := &Ping{
|
||||
NumPongBytes: 10,
|
||||
PaddingBytes: bytes.Repeat([]byte("A"), 100),
|
||||
}
|
||||
|
||||
// Next encode the pong message into an empty bytes buffer.
|
||||
// Next encode the ping 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)
|
||||
if err := ping.Encode(&b, 0); err != nil {
|
||||
t.Fatalf("unable to encode ping: %v", err)
|
||||
}
|
||||
|
||||
// Deserialize the encoded pong message into a new empty struct.
|
||||
pong2 := &Pong{}
|
||||
if err := pong2.Decode(&b, 0); err != nil {
|
||||
// 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(pong, pong2) {
|
||||
t.Fatalf("encode/decode pong messages don't match %#v vs %#v",
|
||||
pong, pong2)
|
||||
if !reflect.DeepEqual(ping, ping2) {
|
||||
t.Fatalf("encode/decode ping messages don't match %#v vs %#v",
|
||||
ping, ping2)
|
||||
}
|
||||
}
|
||||
|
@ -2,20 +2,24 @@ package lnwire
|
||||
|
||||
import "io"
|
||||
|
||||
// PingPayload is a set of opaque bytes sent in response to a ping message.
|
||||
type PongPayload []byte
|
||||
|
||||
// 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
|
||||
// PongBytes is a set of opaque bytes that corresponds to the
|
||||
// NumPongBytes defined in the ping message that this this pong is
|
||||
// replying to.
|
||||
PongBytes PongPayload
|
||||
}
|
||||
|
||||
// NewPong returns a new Pong message binded to the specified nonce.
|
||||
func NewPong(nonce uint64) *Pong {
|
||||
// NewPong returns a new Pong message.
|
||||
func NewPong(pongBytes []byte) *Pong {
|
||||
return &Pong{
|
||||
Nonce: nonce,
|
||||
PongBytes: pongBytes,
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +32,7 @@ var _ Message = (*Pong)(nil)
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (p *Pong) Decode(r io.Reader, pver uint32) error {
|
||||
return readElements(r,
|
||||
&p.Nonce,
|
||||
&p.PongBytes,
|
||||
)
|
||||
}
|
||||
|
||||
@ -38,7 +42,7 @@ func (p *Pong) Decode(r io.Reader, pver uint32) error {
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (p *Pong) Encode(w io.Writer, pver uint32) error {
|
||||
return writeElements(w,
|
||||
p.Nonce,
|
||||
p.PongBytes,
|
||||
)
|
||||
}
|
||||
|
||||
@ -55,7 +59,7 @@ func (p *Pong) Command() uint32 {
|
||||
//
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (p *Pong) MaxPayloadLength(uint32) uint32 {
|
||||
return 8
|
||||
return 65532
|
||||
}
|
||||
|
||||
// Validate performs any necessary sanity checks to ensure all fields present
|
||||
|
@ -6,26 +6,26 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPingEncodeDecode(t *testing.T) {
|
||||
ping := &Ping{
|
||||
Nonce: 9,
|
||||
func TestPongEncodeDecode(t *testing.T) {
|
||||
pong := &Pong{
|
||||
PongBytes: bytes.Repeat([]byte("A"), 100),
|
||||
}
|
||||
|
||||
// Next encode the ping message into an empty bytes buffer.
|
||||
// Next encode the pong 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)
|
||||
if err := pong.Encode(&b, 0); err != nil {
|
||||
t.Fatalf("unable to encode pong: %v", err)
|
||||
}
|
||||
|
||||
// Deserialize the encoded ping message into a new empty struct.
|
||||
ping2 := &Ping{}
|
||||
if err := ping2.Decode(&b, 0); err != nil {
|
||||
// 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(ping, ping2) {
|
||||
t.Fatalf("encode/decode ping messages don't match %#v vs %#v",
|
||||
ping, ping2)
|
||||
if !reflect.DeepEqual(pong, pong2) {
|
||||
t.Fatalf("encode/decode pong messages don't match %#v vs %#v",
|
||||
pong, pong2)
|
||||
}
|
||||
}
|
||||
|
22
peer.go
22
peer.go
@ -3,9 +3,7 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
@ -436,7 +434,8 @@ out:
|
||||
atomic.StoreInt64(&p.pingTime, delay)
|
||||
|
||||
case *lnwire.Ping:
|
||||
p.queueMsg(lnwire.NewPong(msg.Nonce), nil)
|
||||
pongBytes := make([]byte, msg.NumPongBytes)
|
||||
p.queueMsg(lnwire.NewPong(pongBytes), nil)
|
||||
|
||||
case *lnwire.SingleFundingRequest:
|
||||
p.server.fundingMgr.processFundingRequest(msg, p.addr)
|
||||
@ -694,25 +693,14 @@ func (p *peer) pingHandler() {
|
||||
pingTicker := time.NewTicker(pingInterval)
|
||||
defer pingTicker.Stop()
|
||||
|
||||
var pingBuf [8]byte
|
||||
// TODO(roasbeef): make dynamic in order to create fake cover traffic
|
||||
const numPingBytes = 16
|
||||
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-pingTicker.C:
|
||||
// Fill the ping buffer with fresh randomness. If we're
|
||||
// unable to read enough bytes, then we simply defer
|
||||
// sending the ping to the next interval.
|
||||
if _, err := rand.Read(pingBuf[:]); err != nil {
|
||||
peerLog.Errorf("unable to send ping to %v: %v", p,
|
||||
err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Convert the bytes read into a uint64, and queue the
|
||||
// message for sending.
|
||||
nonce := binary.BigEndian.Uint64(pingBuf[:])
|
||||
p.queueMsg(lnwire.NewPing(nonce), nil)
|
||||
p.queueMsg(lnwire.NewPing(numPingBytes), nil)
|
||||
case <-p.quit:
|
||||
break out
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user