lnwire: add ChannelAnnoucement,NodeAnnoucement,ChannelUpdateAnnoucement messages
This commit is contained in:
parent
d5423f007d
commit
b440005219
232
lnwire/channel_announcement.go
Normal file
232
lnwire/channel_announcement.go
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
package lnwire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-errors/errors"
|
||||||
|
"github.com/roasbeef/btcd/btcec"
|
||||||
|
"github.com/roasbeef/btcd/wire"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChannelID represent the set of data which is needed to retrieve all
|
||||||
|
// necessary data to validate the channel existance.
|
||||||
|
type ChannelID struct {
|
||||||
|
// BlockHeight is the height of the block where funding
|
||||||
|
// transaction located.
|
||||||
|
// NOTE: This field is limited to 3 bytes.
|
||||||
|
BlockHeight uint32
|
||||||
|
|
||||||
|
// TxIndex is a position of funding transaction within a block.
|
||||||
|
// NOTE: This field is limited to 3 bytes.
|
||||||
|
TxIndex uint32
|
||||||
|
|
||||||
|
// TxPosition indicating transaction output which pays to the
|
||||||
|
// channel.
|
||||||
|
TxPosition uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChannelID) String() string {
|
||||||
|
return fmt.Sprintf("BlockHeight:\t\t\t%v\n", c.BlockHeight) +
|
||||||
|
fmt.Sprintf("TxIndex:\t\t\t%v\n", c.TxIndex) +
|
||||||
|
fmt.Sprintf("TxPosition:\t\t\t%v\n", c.TxPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelAnnouncement message is used to announce the existence of a channel
|
||||||
|
// between two peers in the overlay, which is propagated by the discovery
|
||||||
|
// service over broadcast handler.
|
||||||
|
type ChannelAnnouncement struct {
|
||||||
|
// This signatures are used by nodes in order to create cross
|
||||||
|
// references between node's channel and node. Requiring both nodes
|
||||||
|
// to sign indicates they are both willing to route other payments via
|
||||||
|
// this node.
|
||||||
|
FirstNodeSig *btcec.Signature
|
||||||
|
SecondNodeSig *btcec.Signature
|
||||||
|
|
||||||
|
// ChannelID is the unique description of the funding transaction.
|
||||||
|
ChannelID *ChannelID
|
||||||
|
|
||||||
|
// This signatures are used by nodes in order to create cross
|
||||||
|
// references between node's channel and node. Requiring the bitcoin
|
||||||
|
// signatures proves they control the channel.
|
||||||
|
FirstBitcoinSig *btcec.Signature
|
||||||
|
SecondBitcoinSig *btcec.Signature
|
||||||
|
|
||||||
|
// The public keys of the two nodes who are operating the channel, such
|
||||||
|
// that is FirstNodeID the numerically-lesser of the two DER encoded
|
||||||
|
// keys (ascending numerical order).
|
||||||
|
FirstNodeID *btcec.PublicKey
|
||||||
|
SecondNodeID *btcec.PublicKey
|
||||||
|
|
||||||
|
// Public keys which corresponds to the keys which was declared in
|
||||||
|
// multisig funding transaction output.
|
||||||
|
FirstBitcoinKey *btcec.PublicKey
|
||||||
|
SecondBitcoinKey *btcec.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// A compile time check to ensure ChannelAnnouncement implements the
|
||||||
|
// lnwire.Message interface.
|
||||||
|
var _ Message = (*ChannelAnnouncement)(nil)
|
||||||
|
|
||||||
|
// Validate performs any necessary sanity checks to ensure all fields present
|
||||||
|
// on the ChannelAnnouncement are valid.
|
||||||
|
//
|
||||||
|
// This is part of the lnwire.Message interface.
|
||||||
|
func (a *ChannelAnnouncement) Validate() error {
|
||||||
|
var sigHash []byte
|
||||||
|
|
||||||
|
sigHash = wire.DoubleSha256(a.FirstNodeID.SerializeCompressed())
|
||||||
|
if !a.FirstBitcoinSig.Verify(sigHash, a.FirstBitcoinKey) {
|
||||||
|
return errors.New("can't verify first bitcoin signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
sigHash = wire.DoubleSha256(a.SecondNodeID.SerializeCompressed())
|
||||||
|
if !a.SecondBitcoinSig.Verify(sigHash, a.SecondBitcoinKey) {
|
||||||
|
return errors.New("can't verify second bitcoin signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := a.DataToSign()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dataHash := wire.DoubleSha256(data)
|
||||||
|
|
||||||
|
if !a.FirstNodeSig.Verify(dataHash, a.FirstNodeID) {
|
||||||
|
return errors.New("can't verify data in first node signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !a.SecondNodeSig.Verify(dataHash, a.SecondNodeID) {
|
||||||
|
return errors.New("can't verify data in second node signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode deserializes a serialized ChannelAnnouncement stored in the passed
|
||||||
|
// io.Reader observing the specified protocol version.
|
||||||
|
//
|
||||||
|
// This is part of the lnwire.Message interface.
|
||||||
|
func (c *ChannelAnnouncement) Decode(r io.Reader, pver uint32) error {
|
||||||
|
err := readElements(r,
|
||||||
|
&c.FirstNodeSig,
|
||||||
|
&c.SecondNodeSig,
|
||||||
|
&c.ChannelID,
|
||||||
|
&c.FirstBitcoinSig,
|
||||||
|
&c.SecondBitcoinSig,
|
||||||
|
&c.FirstNodeID,
|
||||||
|
&c.SecondNodeID,
|
||||||
|
&c.FirstBitcoinKey,
|
||||||
|
&c.SecondBitcoinKey,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode serializes the target ChannelAnnouncement into the passed io.Writer
|
||||||
|
// observing the protocol version specified.
|
||||||
|
//
|
||||||
|
// This is part of the lnwire.Message interface.
|
||||||
|
func (c *ChannelAnnouncement) Encode(w io.Writer, pver uint32) error {
|
||||||
|
err := writeElements(w,
|
||||||
|
c.FirstNodeSig,
|
||||||
|
c.SecondNodeSig,
|
||||||
|
c.ChannelID,
|
||||||
|
c.FirstBitcoinSig,
|
||||||
|
c.SecondBitcoinSig,
|
||||||
|
c.FirstNodeID,
|
||||||
|
c.SecondNodeID,
|
||||||
|
c.FirstBitcoinKey,
|
||||||
|
c.SecondBitcoinKey,
|
||||||
|
)
|
||||||
|
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 (c *ChannelAnnouncement) Command() uint32 {
|
||||||
|
return CmdChannelAnnoucmentMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 *ChannelAnnouncement) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
var length uint32
|
||||||
|
|
||||||
|
// FirstNodeSig - 64 bytes
|
||||||
|
length += 64
|
||||||
|
|
||||||
|
// SecondNodeSig - 64 bytes
|
||||||
|
length += 64
|
||||||
|
|
||||||
|
// ChannelID - 8 bytes
|
||||||
|
length += 8
|
||||||
|
|
||||||
|
// FirstBitcoinSig - 64 bytes
|
||||||
|
length += 64
|
||||||
|
|
||||||
|
// SecondBitcoinSig - 64 bytes
|
||||||
|
length += 64
|
||||||
|
|
||||||
|
// FirstNodeID - 33 bytes
|
||||||
|
length += 33
|
||||||
|
|
||||||
|
// SecondNodeID - 33 bytes
|
||||||
|
length += 33
|
||||||
|
|
||||||
|
// FirstBitcoinKey - 33 bytes
|
||||||
|
length += 33
|
||||||
|
|
||||||
|
// SecondBitcoinKey - 33 bytes
|
||||||
|
length += 33
|
||||||
|
|
||||||
|
return length
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the target ChannelAnnouncement.
|
||||||
|
//
|
||||||
|
// This is part of the lnwire.Message interface.
|
||||||
|
func (c *ChannelAnnouncement) String() string {
|
||||||
|
return fmt.Sprintf("\n--- Begin ChannelAnnouncement ---\n") +
|
||||||
|
fmt.Sprintf("FirstNodeSig:\t\t%v\n", c.FirstNodeSig) +
|
||||||
|
fmt.Sprintf("SecondNodeSig:\t\t%v\n", c.SecondNodeSig) +
|
||||||
|
fmt.Sprintf("ChannelID:\t\t%v\n", c.ChannelID.String()) +
|
||||||
|
fmt.Sprintf("FirstBitcoinSig:\t\t%v\n", c.FirstBitcoinSig) +
|
||||||
|
fmt.Sprintf("SecondBitcoinSig:\t\t%v\n", c.SecondBitcoinSig) +
|
||||||
|
fmt.Sprintf("FirstNodeSig:\t\t%v\n", c.FirstNodeSig) +
|
||||||
|
fmt.Sprintf("SecondNodeID:\t\t%v\n", c.SecondNodeID) +
|
||||||
|
fmt.Sprintf("FirstBitcoinKey:\t\t%v\n", c.FirstBitcoinKey) +
|
||||||
|
fmt.Sprintf("SecondBitcoinKey:\t\t%v\n", c.SecondBitcoinKey) +
|
||||||
|
fmt.Sprintf("--- End ChannelAnnouncement ---\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataToSign is used to retrieve part of the announcement message which
|
||||||
|
// should be signed.
|
||||||
|
func (c *ChannelAnnouncement) DataToSign() ([]byte, error) {
|
||||||
|
// We should not include the signatures itself.
|
||||||
|
var w bytes.Buffer
|
||||||
|
err := writeElements(&w,
|
||||||
|
c.ChannelID,
|
||||||
|
c.FirstBitcoinSig,
|
||||||
|
c.SecondBitcoinSig,
|
||||||
|
c.FirstNodeID,
|
||||||
|
c.SecondNodeID,
|
||||||
|
c.FirstBitcoinKey,
|
||||||
|
c.SecondBitcoinKey,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return w.Bytes(), nil
|
||||||
|
}
|
125
lnwire/channel_announcement_test.go
Normal file
125
lnwire/channel_announcement_test.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
package lnwire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/roasbeef/btcd/btcec"
|
||||||
|
"github.com/roasbeef/btcd/wire"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestChannelAnnoucementEncodeDecode(t *testing.T) {
|
||||||
|
ca := &ChannelAnnouncement{
|
||||||
|
FirstNodeSig: someSig,
|
||||||
|
SecondNodeSig: someSig,
|
||||||
|
ChannelID: someChannelID,
|
||||||
|
FirstBitcoinSig: someSig,
|
||||||
|
SecondBitcoinSig: someSig,
|
||||||
|
FirstNodeID: pubKey,
|
||||||
|
SecondNodeID: pubKey,
|
||||||
|
FirstBitcoinKey: pubKey,
|
||||||
|
SecondBitcoinKey: pubKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next encode the CA message into an empty bytes buffer.
|
||||||
|
var b bytes.Buffer
|
||||||
|
if err := ca.Encode(&b, 0); err != nil {
|
||||||
|
t.Fatalf("unable to encode ChannelAnnouncement: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the encoded CA message into a new empty struct.
|
||||||
|
ca2 := &ChannelAnnouncement{}
|
||||||
|
if err := ca2.Decode(&b, 0); err != nil {
|
||||||
|
t.Fatalf("unable to decode ChannelAnnouncement: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert equality of the two instances.
|
||||||
|
if !reflect.DeepEqual(ca, ca2) {
|
||||||
|
t.Fatalf("encode/decode error messages don't match %#v vs %#v",
|
||||||
|
ca, ca2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChannelAnnoucementValidation(t *testing.T) {
|
||||||
|
getKeys := func(s string) (*btcec.PrivateKey, *btcec.PublicKey) {
|
||||||
|
return btcec.PrivKeyFromBytes(btcec.S256(), []byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
firstNodePrivKey, firstNodePubKey := getKeys("node-id-1")
|
||||||
|
secondNodePrivKey, secondNodePubKey := getKeys("node-id-2")
|
||||||
|
firstBitcoinPrivKey, firstBitcoinPubKey := getKeys("bitcoin-key-1")
|
||||||
|
secondBitcoinPrivKey, secondBitcoinPubKey := getKeys("bitcoin-key-2")
|
||||||
|
|
||||||
|
var hash []byte
|
||||||
|
|
||||||
|
hash = wire.DoubleSha256(firstNodePubKey.SerializeCompressed())
|
||||||
|
firstBitcoinSig, _ := firstBitcoinPrivKey.Sign(hash)
|
||||||
|
|
||||||
|
hash = wire.DoubleSha256(secondNodePubKey.SerializeCompressed())
|
||||||
|
secondBitcoinSig, _ := secondBitcoinPrivKey.Sign(hash)
|
||||||
|
|
||||||
|
ca := &ChannelAnnouncement{
|
||||||
|
ChannelID: someChannelID,
|
||||||
|
FirstBitcoinSig: firstBitcoinSig,
|
||||||
|
SecondBitcoinSig: secondBitcoinSig,
|
||||||
|
FirstNodeID: firstNodePubKey,
|
||||||
|
SecondNodeID: secondNodePubKey,
|
||||||
|
FirstBitcoinKey: firstBitcoinPubKey,
|
||||||
|
SecondBitcoinKey: secondBitcoinPubKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
dataToSign, _ := ca.DataToSign()
|
||||||
|
hash = wire.DoubleSha256(dataToSign)
|
||||||
|
|
||||||
|
firstNodeSign, _ := firstNodePrivKey.Sign(hash)
|
||||||
|
ca.FirstNodeSig = firstNodeSign
|
||||||
|
|
||||||
|
secondNodeSign, _ := secondNodePrivKey.Sign(hash)
|
||||||
|
ca.SecondNodeSig = secondNodeSign
|
||||||
|
|
||||||
|
if err := ca.Validate(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChannelAnnoucementBadValidation(t *testing.T) {
|
||||||
|
getKeys := func(s string) (*btcec.PrivateKey, *btcec.PublicKey) {
|
||||||
|
return btcec.PrivKeyFromBytes(btcec.S256(), []byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
firstNodePrivKey, firstNodePubKey := getKeys("node-id-1")
|
||||||
|
secondNodePrivKey, secondNodePubKey := getKeys("node-id-2")
|
||||||
|
firstBitcoinPrivKey, _ := getKeys("bitcoin-key-1")
|
||||||
|
secondBitcoinPrivKey, _ := getKeys("bitcoin-key-2")
|
||||||
|
|
||||||
|
var hash []byte
|
||||||
|
|
||||||
|
hash = wire.DoubleSha256(firstNodePubKey.SerializeCompressed())
|
||||||
|
firstBitcoinSig, _ := firstBitcoinPrivKey.Sign(hash)
|
||||||
|
|
||||||
|
hash = wire.DoubleSha256(secondNodePubKey.SerializeCompressed())
|
||||||
|
secondBitcoinSig, _ := secondBitcoinPrivKey.Sign(hash)
|
||||||
|
|
||||||
|
ca := &ChannelAnnouncement{
|
||||||
|
ChannelID: someChannelID,
|
||||||
|
FirstBitcoinSig: firstBitcoinSig,
|
||||||
|
SecondBitcoinSig: secondBitcoinSig,
|
||||||
|
FirstNodeID: pubKey, // wrong pubkey
|
||||||
|
SecondNodeID: pubKey, // wrong pubkey
|
||||||
|
FirstBitcoinKey: pubKey, // wrong pubkey
|
||||||
|
SecondBitcoinKey: pubKey, // wrong pubkey
|
||||||
|
}
|
||||||
|
|
||||||
|
dataToSign, _ := ca.DataToSign()
|
||||||
|
hash = wire.DoubleSha256(dataToSign)
|
||||||
|
|
||||||
|
firstNodeSign, _ := firstNodePrivKey.Sign(hash)
|
||||||
|
ca.FirstNodeSig = firstNodeSign
|
||||||
|
|
||||||
|
secondNodeSign, _ := secondNodePrivKey.Sign(hash)
|
||||||
|
ca.SecondNodeSig = secondNodeSign
|
||||||
|
|
||||||
|
if err := ca.Validate(); err == nil {
|
||||||
|
t.Fatal("error should be raised")
|
||||||
|
}
|
||||||
|
}
|
190
lnwire/channel_update_announcement.go
Normal file
190
lnwire/channel_update_announcement.go
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
package lnwire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/roasbeef/btcd/btcec"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChannelUpdateAnnouncement message is used after channel has been initially
|
||||||
|
// announced. Each side independently announces its fees and minimum expiry for
|
||||||
|
// HTLCs and other parameters. Also this message is used to redeclare initially
|
||||||
|
// setted channel parameters.
|
||||||
|
type ChannelUpdateAnnouncement struct {
|
||||||
|
// Signature is used to validate the announced data and prove the
|
||||||
|
// ownership of node id.
|
||||||
|
Signature *btcec.Signature
|
||||||
|
|
||||||
|
// ChannelID is the unique description of the funding transaction.
|
||||||
|
ChannelID *ChannelID
|
||||||
|
|
||||||
|
// Timestamp allows ordering in the case of multiple announcements.
|
||||||
|
// We should ignore the message if timestamp is not greater than
|
||||||
|
// the last-received.
|
||||||
|
Timestamp uint32
|
||||||
|
|
||||||
|
// Flags least-significant bit must be set to 0 if the creating node
|
||||||
|
// corresponds to the first node in previously sent channel
|
||||||
|
// announcement and 1 otherwise.
|
||||||
|
Flags uint16
|
||||||
|
|
||||||
|
// Expiry is the minimum number of blocks this node requires to be
|
||||||
|
// added to the expiry of HTLCs. This is a security parameter determined
|
||||||
|
// by the node operator.
|
||||||
|
Expiry uint16
|
||||||
|
|
||||||
|
// HtlcMinimumMstat is the minimum HTLC value which will be accepted.
|
||||||
|
HtlcMinimumMstat uint32
|
||||||
|
|
||||||
|
// FeeBaseMstat...
|
||||||
|
FeeBaseMstat uint32
|
||||||
|
|
||||||
|
// FeeProportionalMillionths...
|
||||||
|
FeeProportionalMillionths uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// A compile time check to ensure ChannelUpdateAnnouncement implements the
|
||||||
|
// lnwire.Message interface.
|
||||||
|
var _ Message = (*ChannelUpdateAnnouncement)(nil)
|
||||||
|
|
||||||
|
// Validate performs any necessary sanity checks to ensure all fields present
|
||||||
|
// on the ChannelUpdateAnnouncement are valid.
|
||||||
|
//
|
||||||
|
// This is part of the lnwire.Message interface.
|
||||||
|
func (a *ChannelUpdateAnnouncement) Validate() error {
|
||||||
|
// NOTE: As far as we don't have the node id (public key) in this
|
||||||
|
// message, we can't validate the signature on this stage, it should
|
||||||
|
// be validated latter - in discovery service handler.
|
||||||
|
|
||||||
|
if a.Expiry == 0 {
|
||||||
|
return errors.New("expiry should be greater then zero")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode deserializes a serialized ChannelUpdateAnnouncement stored in the
|
||||||
|
// passed io.Reader observing the specified protocol version.
|
||||||
|
//
|
||||||
|
// This is part of the lnwire.Message interface.
|
||||||
|
func (c *ChannelUpdateAnnouncement) Decode(r io.Reader, pver uint32) error {
|
||||||
|
err := readElements(r,
|
||||||
|
&c.Signature,
|
||||||
|
&c.ChannelID,
|
||||||
|
&c.Timestamp,
|
||||||
|
&c.Flags,
|
||||||
|
&c.Expiry,
|
||||||
|
&c.HtlcMinimumMstat,
|
||||||
|
&c.FeeBaseMstat,
|
||||||
|
&c.FeeProportionalMillionths,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode serializes the target ChannelUpdateAnnouncement into the passed
|
||||||
|
// io.Writer observing the protocol version specified.
|
||||||
|
//
|
||||||
|
// This is part of the lnwire.Message interface.
|
||||||
|
func (c *ChannelUpdateAnnouncement) Encode(w io.Writer, pver uint32) error {
|
||||||
|
err := writeElements(w,
|
||||||
|
c.Signature,
|
||||||
|
c.ChannelID,
|
||||||
|
c.Timestamp,
|
||||||
|
c.Flags,
|
||||||
|
c.Expiry,
|
||||||
|
c.HtlcMinimumMstat,
|
||||||
|
c.FeeBaseMstat,
|
||||||
|
c.FeeProportionalMillionths,
|
||||||
|
)
|
||||||
|
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 (c *ChannelUpdateAnnouncement) Command() uint32 {
|
||||||
|
return CmdChannelUpdateAnnoucmentMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 *ChannelUpdateAnnouncement) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
var length uint32
|
||||||
|
|
||||||
|
// Signature - 64 bytes
|
||||||
|
length += 64
|
||||||
|
|
||||||
|
// ChannelID - 8 bytes
|
||||||
|
length += 8
|
||||||
|
|
||||||
|
// Timestamp - 4 bytes
|
||||||
|
length += 4
|
||||||
|
|
||||||
|
// Flags - 2 bytes
|
||||||
|
length += 2
|
||||||
|
|
||||||
|
// Expiry - 2 bytes
|
||||||
|
length += 2
|
||||||
|
|
||||||
|
// HtlcMinimumMstat - 4 bytes
|
||||||
|
length += 4
|
||||||
|
|
||||||
|
// FeeBaseMstat - 4 bytes
|
||||||
|
length += 4
|
||||||
|
|
||||||
|
// FeeProportionalMillionths - 4 bytes
|
||||||
|
length += 4
|
||||||
|
|
||||||
|
return length
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the target ChannelUpdateAnnouncement.
|
||||||
|
//
|
||||||
|
// This is part of the lnwire.Message interface.
|
||||||
|
func (c *ChannelUpdateAnnouncement) String() string {
|
||||||
|
return fmt.Sprintf("\n--- Begin ChannelUpdateAnnouncement ---\n") +
|
||||||
|
fmt.Sprintf("Signature:\t\t%v\n", c.Signature) +
|
||||||
|
fmt.Sprintf("ChannelID:\t\t%v\n", c.ChannelID.String()) +
|
||||||
|
fmt.Sprintf("Timestamp:\t\t%v\n", c.Timestamp) +
|
||||||
|
fmt.Sprintf("Flags:\t\t%v\n", c.Flags) +
|
||||||
|
fmt.Sprintf("Expiry:\t\t%v\n", c.Expiry) +
|
||||||
|
fmt.Sprintf("HtlcMinimumMstat:\t\t%v\n", c.HtlcMinimumMstat) +
|
||||||
|
fmt.Sprintf("FeeBaseMstat:\t\t%v\n", c.FeeBaseMstat) +
|
||||||
|
fmt.Sprintf("FeeProportionalMillionths:\t\t%v\n", c.FeeProportionalMillionths) +
|
||||||
|
fmt.Sprintf("--- End ChannelUpdateAnnouncement ---\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataToSign is used to retrieve part of the announcement message which
|
||||||
|
// should be signed.
|
||||||
|
func (c *ChannelUpdateAnnouncement) DataToSign() ([]byte, error) {
|
||||||
|
|
||||||
|
// We should not include the signatures itself.
|
||||||
|
var w bytes.Buffer
|
||||||
|
err := writeElements(&w,
|
||||||
|
c.ChannelID,
|
||||||
|
c.Timestamp,
|
||||||
|
c.Flags,
|
||||||
|
c.Expiry,
|
||||||
|
c.HtlcMinimumMstat,
|
||||||
|
c.FeeBaseMstat,
|
||||||
|
c.FeeProportionalMillionths,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return w.Bytes(), nil
|
||||||
|
}
|
38
lnwire/channel_update_announcement_test.go
Normal file
38
lnwire/channel_update_announcement_test.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package lnwire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestChannelUpdateAnnouncementEncodeDecode(t *testing.T) {
|
||||||
|
cua := &ChannelUpdateAnnouncement{
|
||||||
|
Signature: someSig,
|
||||||
|
ChannelID: someChannelID,
|
||||||
|
Timestamp: maxUint32,
|
||||||
|
Flags: maxUint16,
|
||||||
|
Expiry: maxUint16,
|
||||||
|
HtlcMinimumMstat: maxUint32,
|
||||||
|
FeeBaseMstat: maxUint32,
|
||||||
|
FeeProportionalMillionths: maxUint32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next encode the CUA message into an empty bytes buffer.
|
||||||
|
var b bytes.Buffer
|
||||||
|
if err := cua.Encode(&b, 0); err != nil {
|
||||||
|
t.Fatalf("unable to encode ChannelUpdateAnnouncement: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the encoded CUA message into a new empty struct.
|
||||||
|
cua2 := &ChannelUpdateAnnouncement{}
|
||||||
|
if err := cua2.Decode(&b, 0); err != nil {
|
||||||
|
t.Fatalf("unable to decode ChannelUpdateAnnouncement: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert equality of the two instances.
|
||||||
|
if !reflect.DeepEqual(cua, cua2) {
|
||||||
|
t.Fatalf("encode/decode error messages don't match %#v vs %#v",
|
||||||
|
cua, cua2)
|
||||||
|
}
|
||||||
|
}
|
117
lnwire/lnwire.go
117
lnwire/lnwire.go
@ -6,10 +6,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/go-errors/errors"
|
||||||
"github.com/roasbeef/btcd/btcec"
|
"github.com/roasbeef/btcd/btcec"
|
||||||
"github.com/roasbeef/btcd/txscript"
|
"github.com/roasbeef/btcd/txscript"
|
||||||
"github.com/roasbeef/btcd/wire"
|
"github.com/roasbeef/btcd/wire"
|
||||||
"github.com/roasbeef/btcutil"
|
"github.com/roasbeef/btcutil"
|
||||||
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MaxSliceLength is the maximum allowed lenth for any opaque byte slices in
|
// MaxSliceLength is the maximum allowed lenth for any opaque byte slices in
|
||||||
@ -324,6 +326,63 @@ func writeElement(w io.Writer, element interface{}) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case *ChannelID:
|
||||||
|
// Check that field fit in 3 bytes and write the blockHeight
|
||||||
|
if e.BlockHeight > ((1 << 24) - 1) {
|
||||||
|
return errors.New("block height should fit in 3 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
var blockHeight [4]byte
|
||||||
|
binary.BigEndian.PutUint32(blockHeight[:], e.BlockHeight)
|
||||||
|
|
||||||
|
if _, err := w.Write(blockHeight[1:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that field fit in 3 bytes and write the txIndex
|
||||||
|
if e.TxIndex > ((1 << 24) - 1) {
|
||||||
|
return errors.New("tx index should fit in 3 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
var txIndex [4]byte
|
||||||
|
binary.BigEndian.PutUint32(txIndex[:], e.TxIndex)
|
||||||
|
if _, err := w.Write(txIndex[1:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the txPosition
|
||||||
|
var txPosition [2]byte
|
||||||
|
binary.BigEndian.PutUint16(txPosition[:], e.TxPosition)
|
||||||
|
if _, err := w.Write(txPosition[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case *net.TCPAddr:
|
||||||
|
var ip [16]byte
|
||||||
|
copy(ip[:], e.IP.To16())
|
||||||
|
if _, err := w.Write(ip[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var port [4]byte
|
||||||
|
binary.BigEndian.PutUint32(port[:], uint32(e.Port))
|
||||||
|
if _, err := w.Write(port[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case RGB:
|
||||||
|
err := writeElements(w,
|
||||||
|
e.red,
|
||||||
|
e.green,
|
||||||
|
e.blue,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case Alias:
|
||||||
|
if err := writeElements(w, ([32]byte)(e)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Unknown type in writeElement: %T", e)
|
return fmt.Errorf("Unknown type in writeElement: %T", e)
|
||||||
}
|
}
|
||||||
@ -403,7 +462,7 @@ func readElement(r io.Reader, element interface{}) error {
|
|||||||
}
|
}
|
||||||
*e = &b
|
*e = &b
|
||||||
case **btcec.PublicKey:
|
case **btcec.PublicKey:
|
||||||
var b [33]byte
|
var b [btcec.PubKeyBytesLenCompressed]byte
|
||||||
if _, err = io.ReadFull(r, b[:]); err != nil {
|
if _, err = io.ReadFull(r, b[:]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -503,11 +562,11 @@ func readElement(r io.Reader, element interface{}) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case *[]byte:
|
case *[]byte:
|
||||||
bytes, err := wire.ReadVarBytes(r, 0, MaxSliceLength, "byte slice")
|
b, err := wire.ReadVarBytes(r, 0, MaxSliceLength, "byte slice")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*e = bytes
|
*e = b
|
||||||
case *PkScript:
|
case *PkScript:
|
||||||
pkScript, err := wire.ReadVarBytes(r, 0, 25, "pkscript")
|
pkScript, err := wire.ReadVarBytes(r, 0, 25, "pkscript")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -610,6 +669,58 @@ func readElement(r io.Reader, element interface{}) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case **ChannelID:
|
||||||
|
var blockHeight [4]byte
|
||||||
|
if _, err = io.ReadFull(r, blockHeight[1:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var txIndex [4]byte
|
||||||
|
if _, err = io.ReadFull(r, txIndex[1:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var txPosition [2]byte
|
||||||
|
if _, err = io.ReadFull(r, txPosition[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*e = &ChannelID{
|
||||||
|
BlockHeight: binary.BigEndian.Uint32(blockHeight[:]),
|
||||||
|
TxIndex: binary.BigEndian.Uint32(txIndex[:]),
|
||||||
|
TxPosition: binary.BigEndian.Uint16(txPosition[:]),
|
||||||
|
}
|
||||||
|
|
||||||
|
case **net.TCPAddr:
|
||||||
|
var ip [16]byte
|
||||||
|
if _, err = io.ReadFull(r, ip[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var port [4]byte
|
||||||
|
if _, err = io.ReadFull(r, port[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*e = &net.TCPAddr{
|
||||||
|
IP: (net.IP)(ip[:]),
|
||||||
|
Port: int(binary.BigEndian.Uint32(port[:])),
|
||||||
|
}
|
||||||
|
case *RGB:
|
||||||
|
err := readElements(r,
|
||||||
|
&e.red,
|
||||||
|
&e.green,
|
||||||
|
&e.blue,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *Alias:
|
||||||
|
var a [32]byte
|
||||||
|
if err := readElements(r, &a); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*e = (Alias)(a)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Unknown type in readElement: %T", e)
|
return fmt.Errorf("Unknown type in readElement: %T", e)
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/roasbeef/btcd/btcec"
|
"github.com/roasbeef/btcd/btcec"
|
||||||
"github.com/roasbeef/btcd/txscript"
|
"github.com/roasbeef/btcd/txscript"
|
||||||
"github.com/roasbeef/btcd/wire"
|
"github.com/roasbeef/btcd/wire"
|
||||||
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Common variables and functions for the message tests
|
// Common variables and functions for the message tests
|
||||||
@ -22,6 +23,10 @@ var (
|
|||||||
0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53,
|
0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maxUint32 uint32 = (1 << 32) - 1
|
||||||
|
maxUint24 uint32 = (1 << 24) - 1
|
||||||
|
maxUint16 uint16 = (1 << 16) - 1
|
||||||
|
|
||||||
// For debugging, writes to /dev/shm/
|
// For debugging, writes to /dev/shm/
|
||||||
// Maybe in the future do it if you do "go test -v"
|
// Maybe in the future do it if you do "go test -v"
|
||||||
WRITE_FILE = false
|
WRITE_FILE = false
|
||||||
@ -91,6 +96,24 @@ var (
|
|||||||
// Reversed when displayed
|
// Reversed when displayed
|
||||||
txidBytes, _ = hex.DecodeString("fd95c6e5c9d5bcf9cfc7231b6a438e46c518c724d0b04b75cc8fddf84a254e3a")
|
txidBytes, _ = hex.DecodeString("fd95c6e5c9d5bcf9cfc7231b6a438e46c518c724d0b04b75cc8fddf84a254e3a")
|
||||||
_ = copy(txid[:], txidBytes)
|
_ = copy(txid[:], txidBytes)
|
||||||
|
|
||||||
|
someAlias, _ = NewAlias("012345678901234567890")
|
||||||
|
someSig, _ = btcec.ParseSignature(sigStr, btcec.S256())
|
||||||
|
someSigBytes = someSig.Serialize()
|
||||||
|
|
||||||
|
someAddress = &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333}
|
||||||
|
|
||||||
|
someChannelID = &ChannelID{
|
||||||
|
BlockHeight: maxUint24,
|
||||||
|
TxIndex: maxUint24,
|
||||||
|
TxPosition: maxUint16,
|
||||||
|
}
|
||||||
|
|
||||||
|
someRGB = RGB{
|
||||||
|
red: 255,
|
||||||
|
green: 255,
|
||||||
|
blue: 255,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func SerializeTest(t *testing.T, message Message, expectedString string, filename string) *bytes.Buffer {
|
func SerializeTest(t *testing.T, message Message, expectedString string, filename string) *bytes.Buffer {
|
||||||
|
@ -55,9 +55,14 @@ const (
|
|||||||
// Commands for reporting protocol errors.
|
// Commands for reporting protocol errors.
|
||||||
CmdErrorGeneric = uint32(4000)
|
CmdErrorGeneric = uint32(4000)
|
||||||
|
|
||||||
|
// Commands for discovery service.
|
||||||
|
CmdChannelAnnoucmentMessage = uint32(5000)
|
||||||
|
CmdChannelUpdateAnnoucmentMessage = uint32(5010)
|
||||||
|
CmdNodeAnnoucmentMessage = uint32(5020)
|
||||||
|
|
||||||
// Commands for connection keep-alive.
|
// Commands for connection keep-alive.
|
||||||
CmdPing = uint32(5000)
|
CmdPing = uint32(6000)
|
||||||
CmdPong = uint32(5010)
|
CmdPong = uint32(6010)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Message is an interface that defines a lightning wire protocol message. The
|
// Message is an interface that defines a lightning wire protocol message. The
|
||||||
@ -114,6 +119,12 @@ func makeEmptyMessage(command uint32) (Message, error) {
|
|||||||
msg = &NeighborAckMessage{}
|
msg = &NeighborAckMessage{}
|
||||||
case CmdNeighborRstMessage:
|
case CmdNeighborRstMessage:
|
||||||
msg = &NeighborRstMessage{}
|
msg = &NeighborRstMessage{}
|
||||||
|
case CmdChannelAnnoucmentMessage:
|
||||||
|
msg = &ChannelAnnouncement{}
|
||||||
|
case CmdChannelUpdateAnnoucmentMessage:
|
||||||
|
msg = &ChannelUpdateAnnouncement{}
|
||||||
|
case CmdNodeAnnoucmentMessage:
|
||||||
|
msg = &NodeAnnouncement{}
|
||||||
case CmdPing:
|
case CmdPing:
|
||||||
msg = &Ping{}
|
msg = &Ping{}
|
||||||
case CmdPong:
|
case CmdPong:
|
||||||
|
230
lnwire/node_announcement.go
Normal file
230
lnwire/node_announcement.go
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
package lnwire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-errors/errors"
|
||||||
|
"github.com/roasbeef/btcd/btcec"
|
||||||
|
"github.com/roasbeef/btcd/wire"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
startPort uint16 = 1024
|
||||||
|
endPort uint16 = 49151
|
||||||
|
aliasSpecLen = 21
|
||||||
|
)
|
||||||
|
|
||||||
|
// RGB is used to represent the color.
|
||||||
|
type RGB struct {
|
||||||
|
red uint8
|
||||||
|
green uint8
|
||||||
|
blue uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alias a hex encoded UTF-8 string that may be displayed
|
||||||
|
// as an alternative to the node's ID. Notice that aliases are not
|
||||||
|
// unique and may be freely chosen by the node operators.
|
||||||
|
type Alias [32]byte
|
||||||
|
|
||||||
|
// NewAlias create the alias from string and also checks spec requirements.
|
||||||
|
func NewAlias(s string) (Alias, error) {
|
||||||
|
var a Alias
|
||||||
|
|
||||||
|
data := []byte(s)
|
||||||
|
if len(data) > aliasSpecLen {
|
||||||
|
return a, errors.Errorf("alias too long the size "+
|
||||||
|
"must be less than %v", aliasSpecLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(a[:], data)
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Alias) String() string {
|
||||||
|
return string(a[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate check that alias data lenght is lower than spec size.
|
||||||
|
func (a *Alias) Validate() error {
|
||||||
|
nonzero := len(a)
|
||||||
|
for a[nonzero-1] == 0 && nonzero > 0 {
|
||||||
|
nonzero--
|
||||||
|
}
|
||||||
|
|
||||||
|
if nonzero > aliasSpecLen {
|
||||||
|
return errors.New("alias should be less then 21 bytes")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeAnnouncement message is used to announce the presence of a lightning node
|
||||||
|
// and signal that the node is accepting incoming connections.
|
||||||
|
type NodeAnnouncement struct {
|
||||||
|
// Signature is used to prove the ownership of node id.
|
||||||
|
Signature *btcec.Signature
|
||||||
|
|
||||||
|
// Timestamp allows ordering in the case of multiple announcements.
|
||||||
|
Timestamp uint32
|
||||||
|
|
||||||
|
// Address includes two specification fields: 'ipv6' and 'port' on which
|
||||||
|
// the node is accepting incoming connections.
|
||||||
|
Address *net.TCPAddr
|
||||||
|
|
||||||
|
// NodeID is a public key which is used as node identificator.
|
||||||
|
NodeID *btcec.PublicKey
|
||||||
|
|
||||||
|
// RGBColor is used to customize their node's appearance in maps and graphs
|
||||||
|
RGBColor RGB
|
||||||
|
|
||||||
|
// pad is used to reserve to additional bytes for future usage.
|
||||||
|
pad uint16
|
||||||
|
|
||||||
|
// Alias is used to customize their node's appearance in maps and graphs
|
||||||
|
Alias Alias
|
||||||
|
}
|
||||||
|
|
||||||
|
// A compile time check to ensure NodeAnnouncement implements the
|
||||||
|
// lnwire.Message interface.
|
||||||
|
var _ Message = (*NodeAnnouncement)(nil)
|
||||||
|
|
||||||
|
// Validate performs any necessary sanity checks to ensure all fields present
|
||||||
|
// on the NodeAnnouncement are valid.
|
||||||
|
//
|
||||||
|
// This is part of the lnwire.Message interface.
|
||||||
|
func (a *NodeAnnouncement) Validate() error {
|
||||||
|
if err := a.Alias.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := a.DataToSign()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dataHash := wire.DoubleSha256(data)
|
||||||
|
if !a.Signature.Verify(dataHash, a.NodeID) {
|
||||||
|
return errors.New("can't check the node annoucement signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode deserializes a serialized NodeAnnouncement stored in the
|
||||||
|
// passed io.Reader observing the specified protocol version.
|
||||||
|
//
|
||||||
|
// This is part of the lnwire.Message interface.
|
||||||
|
func (c *NodeAnnouncement) Decode(r io.Reader, pver uint32) error {
|
||||||
|
err := readElements(r,
|
||||||
|
&c.Signature,
|
||||||
|
&c.Timestamp,
|
||||||
|
&c.Address,
|
||||||
|
&c.NodeID,
|
||||||
|
&c.RGBColor,
|
||||||
|
&c.pad,
|
||||||
|
&c.Alias,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode serializes the target NodeAnnouncement into the passed
|
||||||
|
// io.Writer observing the protocol version specified.
|
||||||
|
//
|
||||||
|
// This is part of the lnwire.Message interface.
|
||||||
|
func (c *NodeAnnouncement) Encode(w io.Writer, pver uint32) error {
|
||||||
|
err := writeElements(w,
|
||||||
|
c.Signature,
|
||||||
|
c.Timestamp,
|
||||||
|
c.Address,
|
||||||
|
c.NodeID,
|
||||||
|
c.RGBColor,
|
||||||
|
c.pad,
|
||||||
|
c.Alias,
|
||||||
|
)
|
||||||
|
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 (c *NodeAnnouncement) Command() uint32 {
|
||||||
|
return CmdNodeAnnoucmentMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 *NodeAnnouncement) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
var length uint32
|
||||||
|
|
||||||
|
// Signature - 64 bytes
|
||||||
|
length += 64
|
||||||
|
|
||||||
|
// Timestamp - 4 bytes
|
||||||
|
length += 4
|
||||||
|
|
||||||
|
// Ipv6 - 16 bytes
|
||||||
|
length += 16
|
||||||
|
|
||||||
|
// Port - 2 bytes
|
||||||
|
length += 2
|
||||||
|
|
||||||
|
// NodeID - 32 bytes
|
||||||
|
length += 32
|
||||||
|
|
||||||
|
// RGBColor - 3 bytes
|
||||||
|
length += 3
|
||||||
|
|
||||||
|
// pad - 2 bytes
|
||||||
|
length += 2
|
||||||
|
|
||||||
|
// Alias - 32 bytes
|
||||||
|
length += 32
|
||||||
|
|
||||||
|
return length
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the target NodeAnnouncement.
|
||||||
|
//
|
||||||
|
// This is part of the lnwire.Message interface.
|
||||||
|
func (c *NodeAnnouncement) String() string {
|
||||||
|
return fmt.Sprintf("\n--- Begin NodeAnnouncement ---\n") +
|
||||||
|
fmt.Sprintf("Signature:\t\t%v\n", c.Signature) +
|
||||||
|
fmt.Sprintf("Timestamp:\t\t%v\n", c.Timestamp) +
|
||||||
|
fmt.Sprintf("Address:\t\t%v\n", c.Address.String()) +
|
||||||
|
fmt.Sprintf("NodeID:\t\t%v\n", c.NodeID) +
|
||||||
|
fmt.Sprintf("RGBColor:\t\t%v\n", c.RGBColor) +
|
||||||
|
fmt.Sprintf("Alias:\t\t%v\n", c.Alias) +
|
||||||
|
fmt.Sprintf("--- End NodeAnnouncement ---\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// dataToSign...
|
||||||
|
func (c *NodeAnnouncement) DataToSign() ([]byte, error) {
|
||||||
|
|
||||||
|
// We should not include the signatures itself.
|
||||||
|
var w bytes.Buffer
|
||||||
|
err := writeElements(&w,
|
||||||
|
c.Timestamp,
|
||||||
|
c.Address,
|
||||||
|
c.NodeID,
|
||||||
|
c.RGBColor,
|
||||||
|
c.pad,
|
||||||
|
c.Alias,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return w.Bytes(), nil
|
||||||
|
}
|
94
lnwire/node_announcement_test.go
Normal file
94
lnwire/node_announcement_test.go
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package lnwire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/roasbeef/btcd/btcec"
|
||||||
|
"github.com/roasbeef/btcd/wire"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNodeAnnouncementEncodeDecode(t *testing.T) {
|
||||||
|
cua := &NodeAnnouncement{
|
||||||
|
Signature: someSig,
|
||||||
|
Timestamp: maxUint32,
|
||||||
|
Address: someAddress,
|
||||||
|
NodeID: pubKey,
|
||||||
|
RGBColor: someRGB,
|
||||||
|
pad: maxUint16,
|
||||||
|
Alias: someAlias,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next encode the NA message into an empty bytes buffer.
|
||||||
|
var b bytes.Buffer
|
||||||
|
if err := cua.Encode(&b, 0); err != nil {
|
||||||
|
t.Fatalf("unable to encode NodeAnnouncement: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the encoded NA message into a new empty struct.
|
||||||
|
cua2 := &NodeAnnouncement{}
|
||||||
|
if err := cua2.Decode(&b, 0); err != nil {
|
||||||
|
t.Fatalf("unable to decode NodeAnnouncement: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert equality of the two instances.
|
||||||
|
if !reflect.DeepEqual(cua, cua2) {
|
||||||
|
t.Fatalf("encode/decode error messages don't match %#v vs %#v",
|
||||||
|
cua, cua2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNodeAnnoucementValidation(t *testing.T) {
|
||||||
|
getKeys := func(s string) (*btcec.PrivateKey, *btcec.PublicKey) {
|
||||||
|
return btcec.PrivKeyFromBytes(btcec.S256(), []byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
nodePrivKey, nodePubKey := getKeys("node-id-1")
|
||||||
|
|
||||||
|
var hash []byte
|
||||||
|
|
||||||
|
na := &NodeAnnouncement{
|
||||||
|
Timestamp: maxUint32,
|
||||||
|
Address: someAddress,
|
||||||
|
NodeID: nodePubKey,
|
||||||
|
RGBColor: someRGB,
|
||||||
|
pad: maxUint16,
|
||||||
|
Alias: someAlias,
|
||||||
|
}
|
||||||
|
|
||||||
|
dataToSign, _ := na.DataToSign()
|
||||||
|
hash = wire.DoubleSha256(dataToSign)
|
||||||
|
|
||||||
|
signature, _ := nodePrivKey.Sign(hash)
|
||||||
|
na.Signature = signature
|
||||||
|
|
||||||
|
if err := na.Validate(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNodeAnnoucementBadValidation(t *testing.T) {
|
||||||
|
getKeys := func(s string) (*btcec.PrivateKey, *btcec.PublicKey) {
|
||||||
|
return btcec.PrivKeyFromBytes(btcec.S256(), []byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
na := &NodeAnnouncement{
|
||||||
|
Timestamp: maxUint32,
|
||||||
|
Address: someAddress,
|
||||||
|
NodeID: pubKey, // wrong pubkey
|
||||||
|
RGBColor: someRGB,
|
||||||
|
pad: maxUint16,
|
||||||
|
Alias: someAlias,
|
||||||
|
}
|
||||||
|
|
||||||
|
nodePrivKey, _ := getKeys("node-id-1")
|
||||||
|
dataToSign, _ := na.DataToSign()
|
||||||
|
hash := wire.DoubleSha256(dataToSign)
|
||||||
|
|
||||||
|
signature, _ := nodePrivKey.Sign(hash)
|
||||||
|
na.Signature = signature
|
||||||
|
|
||||||
|
if err := na.Validate(); err == nil {
|
||||||
|
t.Fatal("error wasn't raised")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user