Merge pull request #4884 from yyforyongyu/3004-buffer-pool

lnwire: fix heap escapes to reduce gc pressure
This commit is contained in:
Olaoluwa Osuntokun authored and GitHub committed 2021-08-10 19:13:08 -07:00
commit b0e9442ba5
44 files changed
+2936 -391

No files matched your search

+1 -1
View File
@@ -7,7 +7,7 @@ import (
// ReadSize represents the size of the maximum message that can be read off the
// wire by brontide. The buffer is used to hold the ciphertext while the
// brontide state machine decrypts the message.
const ReadSize = lnwire.MaxMessagePayload + 16
const ReadSize = lnwire.MaxSliceLength + 16
// Read is a static byte array sized to the maximum-allowed Lightning message
// size, plus 16 bytes for the MAC.
+1 -1
View File
@@ -7,7 +7,7 @@ import (
// WriteSize represents the size of the maximum plaintext message than can be
// sent using brontide. The buffer does not include extra space for the MAC, as
// that is applied by the Noise protocol after encrypting the plaintext.
const WriteSize = lnwire.MaxMessagePayload
const WriteSize = lnwire.MaxSliceLength
// Write is static byte array occupying to maximum-allowed plaintext-message
// size.
+8 -1
View File
@@ -265,8 +265,15 @@ func (s *Single) Serialize(w io.Writer) error {
return err
}
// TODO(yy): remove the type assertion when we finished refactoring db
// into using write buffer.
buf, ok := w.(*bytes.Buffer)
if !ok {
return fmt.Errorf("expect io.Writer to be *bytes.Buffer")
}
return lnwire.WriteElements(
w,
buf,
byte(s.Version),
uint16(len(singleBytes.Bytes())),
singleBytes.Bytes(),
+9 -1
View File
@@ -2,6 +2,7 @@ package channeldb
import (
"encoding/binary"
"fmt"
"sync"
"io"
@@ -232,7 +233,14 @@ func (p *WaitingProof) Encode(w io.Writer) error {
return err
}
if err := p.AnnounceSignatures.Encode(w, 0); err != nil {
// TODO(yy): remove the type assertion when we finished refactoring db
// into using write buffer.
buf, ok := w.(*bytes.Buffer)
if !ok {
return fmt.Errorf("expect io.Writer to be *bytes.Buffer")
}
if err := p.AnnounceSignatures.Encode(buf, 0); err != nil {
return err
}
@@ -121,6 +121,10 @@ you.
* [Update MC store in blocks](https://github.com/lightningnetwork/lnd/pull/5515)
to make payment throughput better when using etcd.
* [The `lnwire` package now uses a write buffer pool](https://github.com/lightningnetwork/lnd/pull/4884)
when encoding/decoding messages. Such that most of the heap escapes are fixed,
resulting in less memory being used when running `lnd`.
## Bug Fixes
A bug has been fixed that would cause `lnd` to [try to bootstrap using the
+59 -18
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"fmt"
"io"
@@ -115,7 +116,7 @@ var _ Message = (*AcceptChannel)(nil)
// protocol version.
//
// This is part of the lnwire.Message interface.
func (a *AcceptChannel) Encode(w io.Writer, pver uint32) error {
func (a *AcceptChannel) Encode(w *bytes.Buffer, pver uint32) error {
// Since the upfront script is encoded as a TLV record, concatenate it
// with the ExtraData, and write them as one.
tlvRecords, err := packShutdownScript(
@@ -125,23 +126,63 @@ func (a *AcceptChannel) Encode(w io.Writer, pver uint32) error {
return err
}
return WriteElements(w,
a.PendingChannelID[:],
a.DustLimit,
a.MaxValueInFlight,
a.ChannelReserve,
a.HtlcMinimum,
a.MinAcceptDepth,
a.CsvDelay,
a.MaxAcceptedHTLCs,
a.FundingKey,
a.RevocationPoint,
a.PaymentPoint,
a.DelayedPaymentPoint,
a.HtlcPoint,
a.FirstCommitmentPoint,
tlvRecords,
)
if err := WriteBytes(w, a.PendingChannelID[:]); err != nil {
return err
}
if err := WriteSatoshi(w, a.DustLimit); err != nil {
return err
}
if err := WriteMilliSatoshi(w, a.MaxValueInFlight); err != nil {
return err
}
if err := WriteSatoshi(w, a.ChannelReserve); err != nil {
return err
}
if err := WriteMilliSatoshi(w, a.HtlcMinimum); err != nil {
return err
}
if err := WriteUint32(w, a.MinAcceptDepth); err != nil {
return err
}
if err := WriteUint16(w, a.CsvDelay); err != nil {
return err
}
if err := WriteUint16(w, a.MaxAcceptedHTLCs); err != nil {
return err
}
if err := WritePublicKey(w, a.FundingKey); err != nil {
return err
}
if err := WritePublicKey(w, a.RevocationPoint); err != nil {
return err
}
if err := WritePublicKey(w, a.PaymentPoint); err != nil {
return err
}
if err := WritePublicKey(w, a.DelayedPaymentPoint); err != nil {
return err
}
if err := WritePublicKey(w, a.HtlcPoint); err != nil {
return err
}
if err := WritePublicKey(w, a.FirstCommitmentPoint); err != nil {
return err
}
return WriteBytes(w, tlvRecords)
}
// Decode deserializes the serialized AcceptChannel stored in the passed
+19 -8
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
)
@@ -64,14 +65,24 @@ func (a *AnnounceSignatures) Decode(r io.Reader, pver uint32) error {
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (a *AnnounceSignatures) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
a.ChannelID,
a.ShortChannelID,
a.NodeSignature,
a.BitcoinSignature,
a.ExtraOpaqueData,
)
func (a *AnnounceSignatures) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteChannelID(w, a.ChannelID); err != nil {
return err
}
if err := WriteShortChannelID(w, a.ShortChannelID); err != nil {
return err
}
if err := WriteSig(w, a.NodeSignature); err != nil {
return err
}
if err := WriteSig(w, a.BitcoinSignature); err != nil {
return err
}
return WriteBytes(w, a.ExtraOpaqueData)
}
// MsgType returns the integer uniquely identifying this message type on the
+79 -28
View File
@@ -87,21 +87,52 @@ func (a *ChannelAnnouncement) Decode(r io.Reader, pver uint32) error {
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (a *ChannelAnnouncement) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
a.NodeSig1,
a.NodeSig2,
a.BitcoinSig1,
a.BitcoinSig2,
a.Features,
a.ChainHash[:],
a.ShortChannelID,
a.NodeID1,
a.NodeID2,
a.BitcoinKey1,
a.BitcoinKey2,
a.ExtraOpaqueData,
)
func (a *ChannelAnnouncement) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteSig(w, a.NodeSig1); err != nil {
return err
}
if err := WriteSig(w, a.NodeSig2); err != nil {
return err
}
if err := WriteSig(w, a.BitcoinSig1); err != nil {
return err
}
if err := WriteSig(w, a.BitcoinSig2); err != nil {
return err
}
if err := WriteRawFeatureVector(w, a.Features); err != nil {
return err
}
if err := WriteBytes(w, a.ChainHash[:]); err != nil {
return err
}
if err := WriteShortChannelID(w, a.ShortChannelID); err != nil {
return err
}
if err := WriteBytes(w, a.NodeID1[:]); err != nil {
return err
}
if err := WriteBytes(w, a.NodeID2[:]); err != nil {
return err
}
if err := WriteBytes(w, a.BitcoinKey1[:]); err != nil {
return err
}
if err := WriteBytes(w, a.BitcoinKey2[:]); err != nil {
return err
}
return WriteBytes(w, a.ExtraOpaqueData)
}
// MsgType returns the integer uniquely identifying this message type on the
@@ -116,20 +147,40 @@ func (a *ChannelAnnouncement) MsgType() MessageType {
// be signed.
func (a *ChannelAnnouncement) DataToSign() ([]byte, error) {
// We should not include the signatures itself.
var w bytes.Buffer
err := WriteElements(&w,
a.Features,
a.ChainHash[:],
a.ShortChannelID,
a.NodeID1,
a.NodeID2,
a.BitcoinKey1,
a.BitcoinKey2,
a.ExtraOpaqueData,
)
if err != nil {
b := make([]byte, 0, MaxMsgBody)
buf := bytes.NewBuffer(b)
if err := WriteRawFeatureVector(buf, a.Features); err != nil {
return nil, err
}
return w.Bytes(), nil
if err := WriteBytes(buf, a.ChainHash[:]); err != nil {
return nil, err
}
if err := WriteShortChannelID(buf, a.ShortChannelID); err != nil {
return nil, err
}
if err := WriteBytes(buf, a.NodeID1[:]); err != nil {
return nil, err
}
if err := WriteBytes(buf, a.NodeID2[:]); err != nil {
return nil, err
}
if err := WriteBytes(buf, a.BitcoinKey1[:]); err != nil {
return nil, err
}
if err := WriteBytes(buf, a.BitcoinKey2[:]); err != nil {
return nil, err
}
if err := WriteBytes(buf, a.ExtraOpaqueData); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
+20 -13
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
"github.com/btcsuite/btcd/btcec"
@@ -75,13 +76,16 @@ var _ Message = (*ChannelReestablish)(nil)
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (a *ChannelReestablish) Encode(w io.Writer, pver uint32) error {
err := WriteElements(w,
a.ChanID,
a.NextLocalCommitHeight,
a.RemoteCommitTailHeight,
)
if err != nil {
func (a *ChannelReestablish) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteChannelID(w, a.ChanID); err != nil {
return err
}
if err := WriteUint64(w, a.NextLocalCommitHeight); err != nil {
return err
}
if err := WriteUint64(w, a.RemoteCommitTailHeight); err != nil {
return err
}
@@ -93,15 +97,18 @@ func (a *ChannelReestablish) Encode(w io.Writer, pver uint32) error {
//
// NOTE: This is here primarily for the quickcheck tests, in
// practice, we'll always populate this field.
return WriteElements(w, a.ExtraData)
return WriteBytes(w, a.ExtraData)
}
// Otherwise, we'll write out the remaining elements.
return WriteElements(w,
a.LastRemoteCommitSecret[:],
a.LocalUnrevokedCommitPoint,
a.ExtraData,
)
if err := WriteBytes(w, a.LastRemoteCommitSecret[:]); err != nil {
return err
}
if err := WritePublicKey(w, a.LocalUnrevokedCommitPoint); err != nil {
return err
}
return WriteBytes(w, a.ExtraData)
}
// Decode deserializes a serialized ChannelReestablish stored in the passed
+80 -33
View File
@@ -159,33 +159,58 @@ func (a *ChannelUpdate) Decode(r io.Reader, pver uint32) error {
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (a *ChannelUpdate) Encode(w io.Writer, pver uint32) error {
err := WriteElements(w,
a.Signature,
a.ChainHash[:],
a.ShortChannelID,
a.Timestamp,
a.MessageFlags,
a.ChannelFlags,
a.TimeLockDelta,
a.HtlcMinimumMsat,
a.BaseFee,
a.FeeRate,
)
if err != nil {
func (a *ChannelUpdate) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteSig(w, a.Signature); err != nil {
return err
}
if err := WriteBytes(w, a.ChainHash[:]); err != nil {
return err
}
if err := WriteShortChannelID(w, a.ShortChannelID); err != nil {
return err
}
if err := WriteUint32(w, a.Timestamp); err != nil {
return err
}
if err := WriteChanUpdateMsgFlags(w, a.MessageFlags); err != nil {
return err
}
if err := WriteChanUpdateChanFlags(w, a.ChannelFlags); err != nil {
return err
}
if err := WriteUint16(w, a.TimeLockDelta); err != nil {
return err
}
if err := WriteMilliSatoshi(w, a.HtlcMinimumMsat); err != nil {
return err
}
if err := WriteUint32(w, a.BaseFee); err != nil {
return err
}
if err := WriteUint32(w, a.FeeRate); err != nil {
return err
}
// Now append optional fields if they are set. Currently, the only
// optional field is max HTLC.
if a.MessageFlags.HasMaxHtlc() {
if err := WriteElements(w, a.HtlcMaximumMsat); err != nil {
err := WriteMilliSatoshi(w, a.HtlcMaximumMsat)
if err != nil {
return err
}
}
// Finally, append any extra opaque data.
return a.ExtraOpaqueData.Encode(w)
return WriteBytes(w, a.ExtraOpaqueData)
}
// MsgType returns the integer uniquely identifying this message type on the
@@ -199,36 +224,58 @@ func (a *ChannelUpdate) MsgType() MessageType {
// DataToSign is used to retrieve part of the announcement message which should
// be signed.
func (a *ChannelUpdate) DataToSign() ([]byte, error) {
// We should not include the signatures itself.
var w bytes.Buffer
err := WriteElements(&w,
a.ChainHash[:],
a.ShortChannelID,
a.Timestamp,
a.MessageFlags,
a.ChannelFlags,
a.TimeLockDelta,
a.HtlcMinimumMsat,
a.BaseFee,
a.FeeRate,
)
if err != nil {
b := make([]byte, 0, MaxMsgBody)
buf := bytes.NewBuffer(b)
if err := WriteBytes(buf, a.ChainHash[:]); err != nil {
return nil, err
}
if err := WriteShortChannelID(buf, a.ShortChannelID); err != nil {
return nil, err
}
if err := WriteUint32(buf, a.Timestamp); err != nil {
return nil, err
}
if err := WriteChanUpdateMsgFlags(buf, a.MessageFlags); err != nil {
return nil, err
}
if err := WriteChanUpdateChanFlags(buf, a.ChannelFlags); err != nil {
return nil, err
}
if err := WriteUint16(buf, a.TimeLockDelta); err != nil {
return nil, err
}
if err := WriteMilliSatoshi(buf, a.HtlcMinimumMsat); err != nil {
return nil, err
}
if err := WriteUint32(buf, a.BaseFee); err != nil {
return nil, err
}
if err := WriteUint32(buf, a.FeeRate); err != nil {
return nil, err
}
// Now append optional fields if they are set. Currently, the only
// optional field is max HTLC.
if a.MessageFlags.HasMaxHtlc() {
if err := WriteElements(&w, a.HtlcMaximumMsat); err != nil {
err := WriteMilliSatoshi(buf, a.HtlcMaximumMsat)
if err != nil {
return nil, err
}
}
// Finally, append any extra opaque data.
if err := a.ExtraOpaqueData.Encode(&w); err != nil {
if err := WriteBytes(buf, a.ExtraOpaqueData); err != nil {
return nil, err
}
return w.Bytes(), nil
return buf.Bytes(), nil
}
+15 -4
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
"github.com/btcsuite/btcutil"
@@ -63,10 +64,20 @@ func (c *ClosingSigned) Decode(r io.Reader, pver uint32) error {
// 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, c.ExtraData,
)
func (c *ClosingSigned) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteChannelID(w, c.ChannelID); err != nil {
return err
}
if err := WriteSatoshi(w, c.FeeSatoshis); err != nil {
return err
}
if err := WriteSig(w, c.Signature); err != nil {
return err
}
return WriteBytes(w, c.ExtraData)
}
// MsgType returns the integer uniquely identifying this message type on the
+15 -7
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
)
@@ -69,13 +70,20 @@ func (c *CommitSig) Decode(r io.Reader, pver uint32) error {
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (c *CommitSig) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
c.ChanID,
c.CommitSig,
c.HtlcSigs,
c.ExtraData,
)
func (c *CommitSig) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteChannelID(w, c.ChanID); err != nil {
return err
}
if err := WriteSig(w, c.CommitSig); err != nil {
return err
}
if err := WriteSigs(w, c.HtlcSigs); err != nil {
return err
}
return WriteBytes(w, c.ExtraData)
}
// MsgType returns the integer uniquely identifying this message type on the
+7 -5
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"fmt"
"io"
)
@@ -103,11 +104,12 @@ func (c *Error) Decode(r io.Reader, pver uint32) error {
// protocol version specified.
//
// This is part of the lnwire.Message interface.
func (c *Error) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
c.ChanID,
c.Data,
)
func (c *Error) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteBytes(w, c.ChanID[:]); err != nil {
return err
}
return WriteErrorData(w, c.Data)
}
// MsgType returns the integer uniquely identifying an Error message on the
+2 -2
View File
@@ -16,9 +16,9 @@ import (
type ExtraOpaqueData []byte
// Encode attempts to encode the raw extra bytes into the passed io.Writer.
func (e *ExtraOpaqueData) Encode(w io.Writer) error {
func (e *ExtraOpaqueData) Encode(w *bytes.Buffer) error {
eBytes := []byte((*e)[:])
if err := WriteElements(w, eBytes); err != nil {
if err := WriteBytes(w, eBytes); err != nil {
return err
}
+2 -2
View File
@@ -91,7 +91,7 @@ const (
// attacks on the receiver of a payment.
PaymentAddrOptional FeatureBit = 15
// MPPOptional is a required feature bit that signals that the receiver
// MPPRequired is a required feature bit that signals that the receiver
// of a payment requires settlement of an invoice with more than one
// HTLC.
MPPRequired FeatureBit = 16
@@ -124,7 +124,7 @@ const (
// transactions, which also imply anchor commitments.
AnchorsZeroFeeHtlcTxRequired FeatureBit = 22
// AnchorsZeroFeeHtlcTxRequired is an optional feature bit that signals
// AnchorsZeroFeeHtlcTxOptional is an optional feature bit that signals
// that the node supports channels having zero-fee second-level HTLC
// transactions, which also imply anchor commitments.
AnchorsZeroFeeHtlcTxOptional FeatureBit = 23
+15 -5
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
"github.com/btcsuite/btcd/wire"
@@ -40,11 +41,20 @@ var _ Message = (*FundingCreated)(nil)
// protocol version.
//
// This is part of the lnwire.Message interface.
func (f *FundingCreated) Encode(w io.Writer, pver uint32) error {
return WriteElements(
w, f.PendingChannelID[:], f.FundingPoint, f.CommitSig,
f.ExtraData,
)
func (f *FundingCreated) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteBytes(w, f.PendingChannelID[:]); err != nil {
return err
}
if err := WriteOutPoint(w, f.FundingPoint); err != nil {
return err
}
if err := WriteSig(w, f.CommitSig); err != nil {
return err
}
return WriteBytes(w, f.ExtraData)
}
// Decode deserializes the serialized FundingCreated stored in the passed
+11 -6
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
"github.com/btcsuite/btcd/btcec"
@@ -58,12 +59,16 @@ func (c *FundingLocked) Decode(r io.Reader, pver uint32) error {
// protocol version.
//
// This is part of the lnwire.Message interface.
func (c *FundingLocked) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
c.ChanID,
c.NextPerCommitmentPoint,
c.ExtraData,
)
func (c *FundingLocked) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteChannelID(w, c.ChanID); err != nil {
return err
}
if err := WritePublicKey(w, c.NextPerCommitmentPoint); err != nil {
return err
}
return WriteBytes(w, c.ExtraData)
}
// MsgType returns the uint32 code which uniquely identifies this message as a
+14 -3
View File
@@ -1,6 +1,9 @@
package lnwire
import "io"
import (
"bytes"
"io"
)
// FundingSigned is sent from Bob (the responder) to Alice (the initiator)
// after receiving the funding outpoint and her signature for Bob's version of
@@ -29,8 +32,16 @@ var _ Message = (*FundingSigned)(nil)
// protocol version.
//
// This is part of the lnwire.Message interface.
func (f *FundingSigned) Encode(w io.Writer, pver uint32) error {
return WriteElements(w, f.ChanID, f.CommitSig, f.ExtraData)
func (f *FundingSigned) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteChannelID(w, f.ChanID); err != nil {
return err
}
if err := WriteSig(w, f.CommitSig); err != nil {
return err
}
return WriteBytes(w, f.ExtraData)
}
// Decode deserializes the serialized FundingSigned stored in the passed
+15 -7
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
"github.com/btcsuite/btcd/chaincfg/chainhash"
@@ -57,13 +58,20 @@ func (g *GossipTimestampRange) Decode(r io.Reader, pver uint32) error {
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (g *GossipTimestampRange) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
g.ChainHash[:],
g.FirstTimestamp,
g.TimestampRange,
g.ExtraData,
)
func (g *GossipTimestampRange) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteBytes(w, g.ChainHash[:]); err != nil {
return err
}
if err := WriteUint32(w, g.FirstTimestamp); err != nil {
return err
}
if err := WriteUint32(w, g.TimestampRange); err != nil {
return err
}
return WriteBytes(w, g.ExtraData)
}
// MsgType returns the integer uniquely identifying this message type on the
+14 -7
View File
@@ -1,6 +1,9 @@
package lnwire
import "io"
import (
"bytes"
"io"
)
// Init is the first message reveals the features supported or required by this
// node. Nodes wait for receipt of the other's features to simplify error
@@ -56,12 +59,16 @@ func (msg *Init) Decode(r io.Reader, pver uint32) error {
// the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (msg *Init) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
msg.GlobalFeatures,
msg.Features,
msg.ExtraData,
)
func (msg *Init) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteRawFeatureVector(w, msg.GlobalFeatures); err != nil {
return err
}
if err := WriteRawFeatureVector(w, msg.Features); err != nil {
return err
}
return WriteBytes(w, msg.ExtraData)
}
// MsgType returns the integer uniquely identifying this message type on the
+14 -10
View File
@@ -75,13 +75,11 @@ func (a addressType) AddrLen() uint16 {
}
// WriteElement is a one-stop shop to write the big endian representation of
// any element which is to be serialized for the wire protocol. The passed
// io.Writer should be backed by an appropriately sized byte slice, or be able
// to dynamically expand to accommodate additional data.
// any element which is to be serialized for the wire protocol.
//
// TODO(roasbeef): this should eventually draw from a buffer pool for
// serialization.
func WriteElement(w io.Writer, element interface{}) error {
// TODO(yy): rm this method once we finish dereferencing it from other
// packages.
func WriteElement(w *bytes.Buffer, element interface{}) error {
switch e := element.(type) {
case NodeAlias:
if _, err := w.Write(e[:]); err != nil {
@@ -437,10 +435,13 @@ func WriteElement(w io.Writer, element interface{}) error {
}
// WriteElements is writes each element in the elements slice to the passed
// io.Writer using WriteElement.
func WriteElements(w io.Writer, elements ...interface{}) error {
// buffer using WriteElement.
//
// TODO(yy): rm this method once we finish dereferencing it from other
// packages.
func WriteElements(buf *bytes.Buffer, elements ...interface{}) error {
for _, element := range elements {
err := WriteElement(w, element)
err := WriteElement(buf, element)
if err != nil {
return err
}
@@ -828,8 +829,11 @@ func ReadElement(r io.Reader, element interface{}) error {
length := binary.BigEndian.Uint16(addrLen[:])
var addrBytes [deliveryAddressMaxSize]byte
if length > deliveryAddressMaxSize {
return fmt.Errorf("cannot read %d bytes into addrBytes", length)
return fmt.Errorf(
"cannot read %d bytes into addrBytes", length,
)
}
if _, err = io.ReadFull(r, addrBytes[:length]); err != nil {
return err
+1 -1
View File
@@ -232,7 +232,7 @@ func TestMaxOutPointIndex(t *testing.T) {
}
var b bytes.Buffer
if err := WriteElement(&b, op); err == nil {
if err := WriteOutPoint(&b, op); err == nil {
t.Fatalf("write of outPoint should fail, index exceeds 16-bits")
}
}
+57 -35
View File
@@ -12,10 +12,6 @@ import (
"io"
)
// MaxMessagePayload is the maximum bytes a message can be regardless of other
// individual limits imposed by messages themselves.
const MaxMessagePayload = 65535 // 65KB
// MessageType is the unique 2 byte big-endian integer that indicates the type
// of message on the wire. All messages have a very simple header which
// consists simply of 2-byte message type. We omit a length field, and checksum
@@ -56,6 +52,27 @@ const (
MsgGossipTimestampRange = 265
)
// ErrorEncodeMessage is used when failed to encode the message payload.
func ErrorEncodeMessage(err error) error {
return fmt.Errorf("failed to encode message to buffer, got %w", err)
}
// ErrorWriteMessageType is used when failed to write the message type.
func ErrorWriteMessageType(err error) error {
return fmt.Errorf("failed to write message type, got %w", err)
}
// ErrorPayloadTooLarge is used when the payload size exceeds the
// MaxMsgBody.
func ErrorPayloadTooLarge(size int) error {
return fmt.Errorf(
"message payload is too large - encoded %d bytes, "+
"but maximum message payload is %d bytes",
size, MaxMsgBody,
)
}
// String return the string representation of message type.
func (t MessageType) String() string {
switch t {
@@ -141,8 +158,8 @@ type Serializable interface {
Decode(io.Reader, uint32) error
// Encode converts object to the bytes stream and write it into the
// writer.
Encode(io.Writer, uint32) error
// write buffer.
Encode(*bytes.Buffer, uint32) error
}
// Message is an interface that defines a lightning wire protocol message. The
@@ -222,44 +239,49 @@ func makeEmptyMessage(msgType MessageType) (Message, error) {
return msg, nil
}
// WriteMessage writes a lightning Message to w including the necessary header
// information and returns the number of bytes written.
func WriteMessage(w io.Writer, msg Message, pver uint32) (int, error) {
totalBytes := 0
// WriteMessage writes a lightning Message to a buffer including the necessary
// header information and returns the number of bytes written. If any error is
// encountered, the buffer passed will be reset to its original state since we
// don't want any broken bytes left. In other words, no bytes will be written
// if there's an error. Either all or none of the message bytes will be written
// to the buffer.
//
// NOTE: this method is not concurrent safe.
func WriteMessage(buf *bytes.Buffer, msg Message, pver uint32) (int, error) {
// Record the size of the bytes already written in buffer.
oldByteSize := buf.Len()
// Encode the message payload itself into a temporary buffer.
// TODO(roasbeef): create buffer pool
var bw bytes.Buffer
if err := msg.Encode(&bw, pver); err != nil {
return totalBytes, err
}
payload := bw.Bytes()
lenp := len(payload)
// Enforce maximum message payload, which means the body cannot be
// greater than MaxMsgBody.
if lenp > MaxMsgBody {
return totalBytes, fmt.Errorf("message payload is too large - "+
"encoded %d bytes, but maximum message body is %d bytes",
lenp, MaxMsgBody)
// cleanBrokenBytes is a helper closure that helps reset the buffer to
// its original state. It truncates all the bytes written in current
// scope.
var cleanBrokenBytes = func(b *bytes.Buffer) int {
b.Truncate(oldByteSize)
return 0
}
// With the initial sanity checks complete, we'll now write out the
// message type itself.
// Write the message type.
var mType [2]byte
binary.BigEndian.PutUint16(mType[:], uint16(msg.MsgType()))
n, err := w.Write(mType[:])
totalBytes += n
msgTypeBytes, err := buf.Write(mType[:])
if err != nil {
return totalBytes, err
return cleanBrokenBytes(buf), ErrorWriteMessageType(err)
}
// With the message type written, we'll now write out the raw payload
// itself.
n, err = w.Write(payload)
totalBytes += n
// Use the write buffer to encode our message.
if err := msg.Encode(buf, pver); err != nil {
return cleanBrokenBytes(buf), ErrorEncodeMessage(err)
}
return totalBytes, err
// Enforce maximum overall message payload. The write buffer now has
// the size of len(originalBytes) + len(payload) + len(type). We want
// to enforce the payload here, so we subtract it by the length of the
// type and old bytes.
lenp := buf.Len() - oldByteSize - msgTypeBytes
if lenp > MaxMsgBody {
return cleanBrokenBytes(buf), ErrorPayloadTooLarge(lenp)
}
return buf.Len() - oldByteSize, nil
}
// ReadMessage reads, validates, and parses the next Lightning message from r
File diff suppressed because it is too large. Load diff
+60 -23
View File
@@ -124,17 +124,37 @@ func (a *NodeAnnouncement) Decode(r io.Reader, pver uint32) error {
// Encode serializes the target NodeAnnouncement into the passed io.Writer
// observing the protocol version specified.
//
func (a *NodeAnnouncement) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
a.Signature,
a.Features,
a.Timestamp,
a.NodeID,
a.RGBColor,
a.Alias,
a.Addresses,
a.ExtraOpaqueData,
)
// This is part of the lnwire.Message interface.
func (a *NodeAnnouncement) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteSig(w, a.Signature); err != nil {
return err
}
if err := WriteRawFeatureVector(w, a.Features); err != nil {
return err
}
if err := WriteUint32(w, a.Timestamp); err != nil {
return err
}
if err := WriteBytes(w, a.NodeID[:]); err != nil {
return err
}
if err := WriteColorRGBA(w, a.RGBColor); err != nil {
return err
}
if err := WriteNodeAlias(w, a.Alias); err != nil {
return err
}
if err := WriteNetAddrs(w, a.Addresses); err != nil {
return err
}
return WriteBytes(w, a.ExtraOpaqueData)
}
// MsgType returns the integer uniquely identifying this message type on the
@@ -149,19 +169,36 @@ func (a *NodeAnnouncement) MsgType() MessageType {
func (a *NodeAnnouncement) DataToSign() ([]byte, error) {
// We should not include the signatures itself.
var w bytes.Buffer
err := WriteElements(&w,
a.Features,
a.Timestamp,
a.NodeID,
a.RGBColor,
a.Alias[:],
a.Addresses,
a.ExtraOpaqueData,
)
if err != nil {
buffer := make([]byte, 0, MaxMsgBody)
buf := bytes.NewBuffer(buffer)
if err := WriteRawFeatureVector(buf, a.Features); err != nil {
return nil, err
}
return w.Bytes(), nil
if err := WriteUint32(buf, a.Timestamp); err != nil {
return nil, err
}
if err := WriteBytes(buf, a.NodeID[:]); err != nil {
return nil, err
}
if err := WriteColorRGBA(buf, a.RGBColor); err != nil {
return nil, err
}
if err := WriteNodeAlias(buf, a.Alias); err != nil {
return nil, err
}
if err := WriteNetAddrs(buf, a.Addresses); err != nil {
return nil, err
}
if err := WriteBytes(buf, a.ExtraOpaqueData); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
+53 -35
View File
@@ -419,8 +419,12 @@ func (f *FailIncorrectDetails) Decode(r io.Reader, pver uint32) error {
// Encode writes the failure in bytes stream.
//
// NOTE: Part of the Serializable interface.
func (f *FailIncorrectDetails) Encode(w io.Writer, pver uint32) error {
return WriteElements(w, f.amount, f.height)
func (f *FailIncorrectDetails) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteMilliSatoshi(w, f.amount); err != nil {
return err
}
return WriteUint32(w, f.height)
}
// FailFinalExpiryTooSoon is returned if the cltv_expiry is too low, the final
@@ -485,8 +489,8 @@ func (f *FailInvalidOnionVersion) Decode(r io.Reader, pver uint32) error {
// Encode writes the failure in bytes stream.
//
// NOTE: Part of the Serializable interface.
func (f *FailInvalidOnionVersion) Encode(w io.Writer, pver uint32) error {
return WriteElement(w, f.OnionSHA256[:])
func (f *FailInvalidOnionVersion) Encode(w *bytes.Buffer, pver uint32) error {
return WriteBytes(w, f.OnionSHA256[:])
}
// FailInvalidOnionHmac is return if the onion HMAC is incorrect.
@@ -519,8 +523,8 @@ func (f *FailInvalidOnionHmac) Decode(r io.Reader, pver uint32) error {
// Encode writes the failure in bytes stream.
//
// NOTE: Part of the Serializable interface.
func (f *FailInvalidOnionHmac) Encode(w io.Writer, pver uint32) error {
return WriteElement(w, f.OnionSHA256[:])
func (f *FailInvalidOnionHmac) Encode(w *bytes.Buffer, pver uint32) error {
return WriteBytes(w, f.OnionSHA256[:])
}
// Returns a human readable string describing the target FailureMessage.
@@ -561,8 +565,8 @@ func (f *FailInvalidOnionKey) Decode(r io.Reader, pver uint32) error {
// Encode writes the failure in bytes stream.
//
// NOTE: Part of the Serializable interface.
func (f *FailInvalidOnionKey) Encode(w io.Writer, pver uint32) error {
return WriteElement(w, f.OnionSHA256[:])
func (f *FailInvalidOnionKey) Encode(w *bytes.Buffer, pver uint32) error {
return WriteBytes(w, f.OnionSHA256[:])
}
// Returns a human readable string describing the target FailureMessage.
@@ -670,7 +674,9 @@ func (f *FailTemporaryChannelFailure) Decode(r io.Reader, pver uint32) error {
// Encode writes the failure in bytes stream.
//
// NOTE: Part of the Serializable interface.
func (f *FailTemporaryChannelFailure) Encode(w io.Writer, pver uint32) error {
func (f *FailTemporaryChannelFailure) Encode(w *bytes.Buffer,
pver uint32) error {
var payload []byte
if f.Update != nil {
var bw bytes.Buffer
@@ -680,7 +686,7 @@ func (f *FailTemporaryChannelFailure) Encode(w io.Writer, pver uint32) error {
payload = bw.Bytes()
}
if err := WriteElement(w, uint16(len(payload))); err != nil {
if err := WriteUint16(w, uint16(len(payload))); err != nil {
return err
}
@@ -749,8 +755,8 @@ func (f *FailAmountBelowMinimum) Decode(r io.Reader, pver uint32) error {
// Encode writes the failure in bytes stream.
//
// NOTE: Part of the Serializable interface.
func (f *FailAmountBelowMinimum) Encode(w io.Writer, pver uint32) error {
if err := WriteElement(w, f.HtlcMsat); err != nil {
func (f *FailAmountBelowMinimum) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteMilliSatoshi(w, f.HtlcMsat); err != nil {
return err
}
@@ -817,8 +823,8 @@ func (f *FailFeeInsufficient) Decode(r io.Reader, pver uint32) error {
// Encode writes the failure in bytes stream.
//
// NOTE: Part of the Serializable interface.
func (f *FailFeeInsufficient) Encode(w io.Writer, pver uint32) error {
if err := WriteElement(w, f.HtlcMsat); err != nil {
func (f *FailFeeInsufficient) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteMilliSatoshi(w, f.HtlcMsat); err != nil {
return err
}
@@ -885,8 +891,8 @@ func (f *FailIncorrectCltvExpiry) Decode(r io.Reader, pver uint32) error {
// Encode writes the failure in bytes stream.
//
// NOTE: Part of the Serializable interface.
func (f *FailIncorrectCltvExpiry) Encode(w io.Writer, pver uint32) error {
if err := WriteElement(w, f.CltvExpiry); err != nil {
func (f *FailIncorrectCltvExpiry) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteUint32(w, f.CltvExpiry); err != nil {
return err
}
@@ -942,7 +948,7 @@ func (f *FailExpiryTooSoon) Decode(r io.Reader, pver uint32) error {
// Encode writes the failure in bytes stream.
//
// NOTE: Part of the Serializable interface.
func (f *FailExpiryTooSoon) Encode(w io.Writer, pver uint32) error {
func (f *FailExpiryTooSoon) Encode(w *bytes.Buffer, pver uint32) error {
return writeOnionErrorChanUpdate(w, &f.Update, pver)
}
@@ -1006,8 +1012,8 @@ func (f *FailChannelDisabled) Decode(r io.Reader, pver uint32) error {
// Encode writes the failure in bytes stream.
//
// NOTE: Part of the Serializable interface.
func (f *FailChannelDisabled) Encode(w io.Writer, pver uint32) error {
if err := WriteElement(w, f.Flags); err != nil {
func (f *FailChannelDisabled) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteUint16(w, f.Flags); err != nil {
return err
}
@@ -1056,8 +1062,10 @@ func (f *FailFinalIncorrectCltvExpiry) Decode(r io.Reader, pver uint32) error {
// Encode writes the failure in bytes stream.
//
// NOTE: Part of the Serializable interface.
func (f *FailFinalIncorrectCltvExpiry) Encode(w io.Writer, pver uint32) error {
return WriteElement(w, f.CltvExpiry)
func (f *FailFinalIncorrectCltvExpiry) Encode(w *bytes.Buffer,
pver uint32) error {
return WriteUint32(w, f.CltvExpiry)
}
// FailFinalIncorrectHtlcAmount is returned if the amt_to_forward is higher
@@ -1102,8 +1110,10 @@ func (f *FailFinalIncorrectHtlcAmount) Decode(r io.Reader, pver uint32) error {
// Encode writes the failure in bytes stream.
//
// NOTE: Part of the Serializable interface.
func (f *FailFinalIncorrectHtlcAmount) Encode(w io.Writer, pver uint32) error {
return WriteElement(w, f.IncomingHTLCAmount)
func (f *FailFinalIncorrectHtlcAmount) Encode(w *bytes.Buffer,
pver uint32) error {
return WriteMilliSatoshi(w, f.IncomingHTLCAmount)
}
// FailExpiryTooFar is returned if the CLTV expiry in the HTLC is too far in the
@@ -1177,13 +1187,13 @@ func (f *InvalidOnionPayload) Decode(r io.Reader, pver uint32) error {
// Encode writes the failure in bytes stream.
//
// NOTE: Part of the Serializable interface.
func (f *InvalidOnionPayload) Encode(w io.Writer, pver uint32) error {
func (f *InvalidOnionPayload) Encode(w *bytes.Buffer, pver uint32) error {
var buf [8]byte
if err := tlv.WriteVarInt(w, f.Type, &buf); err != nil {
return err
}
return WriteElements(w, f.Offset)
return WriteUint16(w, f.Offset)
}
// FailMPPTimeout is returned if the complete amount for a multi part payment
@@ -1263,7 +1273,7 @@ func DecodeFailureMessage(r io.Reader, pver uint32) (FailureMessage, error) {
// EncodeFailure encodes, including the necessary onion failure header
// information.
func EncodeFailure(w io.Writer, failure FailureMessage, pver uint32) error {
func EncodeFailure(w *bytes.Buffer, failure FailureMessage, pver uint32) error {
var failureMessageBuffer bytes.Buffer
err := EncodeFailureMessage(&failureMessageBuffer, failure, pver)
@@ -1283,17 +1293,25 @@ func EncodeFailure(w io.Writer, failure FailureMessage, pver uint32) error {
// messages are fixed size.
pad := make([]byte, FailureMessageLength-len(failureMessage))
return WriteElements(w,
uint16(len(failureMessage)),
failureMessage,
uint16(len(pad)),
pad,
)
if err := WriteUint16(w, uint16(len(failureMessage))); err != nil {
return err
}
if err := WriteBytes(w, failureMessage); err != nil {
return err
}
if err := WriteUint16(w, uint16(len(pad))); err != nil {
return err
}
return WriteBytes(w, pad)
}
// EncodeFailureMessage encodes just the failure message without adding a length
// and padding the message for the onion protocol.
func EncodeFailureMessage(w io.Writer, failure FailureMessage, pver uint32) error {
func EncodeFailureMessage(w *bytes.Buffer,
failure FailureMessage, pver uint32) error {
// First, we'll write out the error code itself into the failure
// buffer.
var codeBytes [2]byte
@@ -1401,7 +1419,7 @@ func makeEmptyOnionError(code FailCode) (FailureMessage, error) {
// writeOnionErrorChanUpdate writes out a ChannelUpdate using the onion error
// format. The format is that we first write out the true serialized length of
// the channel update, followed by the serialized channel update itself.
func writeOnionErrorChanUpdate(w io.Writer, chanUpdate *ChannelUpdate,
func writeOnionErrorChanUpdate(w *bytes.Buffer, chanUpdate *ChannelUpdate,
pver uint32) error {
// First, we encode the channel update in a temporary buffer in order
@@ -1414,7 +1432,7 @@ func writeOnionErrorChanUpdate(w io.Writer, chanUpdate *ChannelUpdate,
// Now that we know the size, we can write the length out in the main
// writer.
updateLen := b.Len()
if err := WriteElement(w, uint16(updateLen)); err != nil {
if err := WriteUint16(w, uint16(updateLen)); err != nil {
return err
}
+4 -2
View File
@@ -275,6 +275,8 @@ func (f *mockFailIncorrectDetailsNoHeight) Decode(r io.Reader, pver uint32) erro
return nil
}
func (f *mockFailIncorrectDetailsNoHeight) Encode(w io.Writer, pver uint32) error {
return WriteElement(w, f.amount)
func (f *mockFailIncorrectDetailsNoHeight) Encode(w *bytes.Buffer,
pver uint32) error {
return WriteUint64(w, f.amount)
}
+76 -22
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
"github.com/btcsuite/btcd/btcec"
@@ -150,7 +151,7 @@ var _ Message = (*OpenChannel)(nil)
// protocol version.
//
// This is part of the lnwire.Message interface.
func (o *OpenChannel) Encode(w io.Writer, pver uint32) error {
func (o *OpenChannel) Encode(w *bytes.Buffer, pver uint32) error {
// Since the upfront script is encoded as a TLV record, concatenate it
// with the ExtraData, and write them as one.
tlvRecords, err := packShutdownScript(
@@ -160,27 +161,80 @@ func (o *OpenChannel) Encode(w io.Writer, pver uint32) error {
return err
}
return WriteElements(w,
o.ChainHash[:],
o.PendingChannelID[:],
o.FundingAmount,
o.PushAmount,
o.DustLimit,
o.MaxValueInFlight,
o.ChannelReserve,
o.HtlcMinimum,
o.FeePerKiloWeight,
o.CsvDelay,
o.MaxAcceptedHTLCs,
o.FundingKey,
o.RevocationPoint,
o.PaymentPoint,
o.DelayedPaymentPoint,
o.HtlcPoint,
o.FirstCommitmentPoint,
o.ChannelFlags,
tlvRecords,
)
if err := WriteBytes(w, o.ChainHash[:]); err != nil {
return err
}
if err := WriteBytes(w, o.PendingChannelID[:]); err != nil {
return err
}
if err := WriteSatoshi(w, o.FundingAmount); err != nil {
return err
}
if err := WriteMilliSatoshi(w, o.PushAmount); err != nil {
return err
}
if err := WriteSatoshi(w, o.DustLimit); err != nil {
return err
}
if err := WriteMilliSatoshi(w, o.MaxValueInFlight); err != nil {
return err
}
if err := WriteSatoshi(w, o.ChannelReserve); err != nil {
return err
}
if err := WriteMilliSatoshi(w, o.HtlcMinimum); err != nil {
return err
}
if err := WriteUint32(w, o.FeePerKiloWeight); err != nil {
return err
}
if err := WriteUint16(w, o.CsvDelay); err != nil {
return err
}
if err := WriteUint16(w, o.MaxAcceptedHTLCs); err != nil {
return err
}
if err := WritePublicKey(w, o.FundingKey); err != nil {
return err
}
if err := WritePublicKey(w, o.RevocationPoint); err != nil {
return err
}
if err := WritePublicKey(w, o.PaymentPoint); err != nil {
return err
}
if err := WritePublicKey(w, o.DelayedPaymentPoint); err != nil {
return err
}
if err := WritePublicKey(w, o.HtlcPoint); err != nil {
return err
}
if err := WritePublicKey(w, o.FirstCommitmentPoint); err != nil {
return err
}
if err := WriteFundingFlag(w, o.ChannelFlags); err != nil {
return err
}
return WriteBytes(w, tlvRecords)
}
// Decode deserializes the serialized OpenChannel stored in the passed
+10 -5
View File
@@ -1,6 +1,9 @@
package lnwire
import "io"
import (
"bytes"
"io"
)
// PingPayload is a set of opaque bytes used to pad out a ping message.
type PingPayload []byte
@@ -44,10 +47,12 @@ func (p *Ping) Decode(r io.Reader, pver uint32) error {
// protocol version specified.
//
// This is part of the lnwire.Message interface.
func (p *Ping) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
p.NumPongBytes,
p.PaddingBytes)
func (p *Ping) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteUint16(w, p.NumPongBytes); err != nil {
return err
}
return WritePingPayload(w, p.PaddingBytes)
}
// MsgType returns the integer uniquely identifying this message type on the
+6 -5
View File
@@ -1,6 +1,9 @@
package lnwire
import "io"
import (
"bytes"
"io"
)
// PongPayload is a set of opaque bytes sent in response to a ping message.
type PongPayload []byte
@@ -40,10 +43,8 @@ func (p *Pong) Decode(r io.Reader, pver uint32) error {
// protocol version specified.
//
// This is part of the lnwire.Message interface.
func (p *Pong) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
p.PongBytes,
)
func (p *Pong) Encode(w *bytes.Buffer, pver uint32) error {
return WritePongPayload(w, p.PongBytes)
}
// MsgType returns the integer uniquely identifying this message type on the
+15 -7
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
"math"
@@ -58,13 +59,20 @@ func (q *QueryChannelRange) Decode(r io.Reader, pver uint32) error {
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (q *QueryChannelRange) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
q.ChainHash[:],
q.FirstBlockHeight,
q.NumBlocks,
q.ExtraData,
)
func (q *QueryChannelRange) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteBytes(w, q.ChainHash[:]); err != nil {
return err
}
if err := WriteUint32(w, q.FirstBlockHeight); err != nil {
return err
}
if err := WriteUint32(w, q.NumBlocks); err != nil {
return err
}
return WriteBytes(w, q.ExtraData)
}
// MsgType returns the integer uniquely identifying this message type on the
+34 -23
View File
@@ -291,26 +291,25 @@ func decodeShortChanIDs(r io.Reader) (ShortChanIDEncoding, []ShortChannelID, err
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (q *QueryShortChanIDs) Encode(w io.Writer, pver uint32) error {
func (q *QueryShortChanIDs) Encode(w *bytes.Buffer, pver uint32) error {
// First, we'll write out the chain hash.
err := WriteElements(w, q.ChainHash[:])
if err != nil {
if err := WriteBytes(w, q.ChainHash[:]); err != nil {
return err
}
// Base on our encoding type, we'll write out the set of short channel
// ID's.
err = encodeShortChanIDs(w, q.EncodingType, q.ShortChanIDs, q.noSort)
err := encodeShortChanIDs(w, q.EncodingType, q.ShortChanIDs, q.noSort)
if err != nil {
return err
}
return q.ExtraData.Encode(w)
return WriteBytes(w, q.ExtraData)
}
// encodeShortChanIDs encodes the passed short channel ID's into the passed
// io.Writer, respecting the specified encoding type.
func encodeShortChanIDs(w io.Writer, encodingType ShortChanIDEncoding,
func encodeShortChanIDs(w *bytes.Buffer, encodingType ShortChanIDEncoding,
shortChanIDs []ShortChannelID, noSort bool) error {
// For both of the current encoding types, the channel ID's are to be
@@ -332,20 +331,21 @@ func encodeShortChanIDs(w io.Writer, encodingType ShortChanIDEncoding,
// body. We add 1 as the response will have the encoding type
// prepended to it.
numBytesBody := uint16(len(shortChanIDs)*8) + 1
if err := WriteElements(w, numBytesBody); err != nil {
if err := WriteUint16(w, numBytesBody); err != nil {
return err
}
// We'll then write out the encoding that that follows the
// actual encoded short channel ID's.
if err := WriteElements(w, encodingType); err != nil {
err := WriteShortChanIDEncoding(w, encodingType)
if err != nil {
return err
}
// Now that we know they're sorted, we can write out each short
// channel ID to the buffer.
for _, chanID := range shortChanIDs {
if err := WriteElements(w, chanID); err != nil {
if err := WriteShortChannelID(w, chanID); err != nil {
return fmt.Errorf("unable to write short chan "+
"ID: %v", err)
}
@@ -360,29 +360,40 @@ func encodeShortChanIDs(w io.Writer, encodingType ShortChanIDEncoding,
// TODO(roasbeef): assumes the caller knows the proper chunk size to
// pass to avoid bin-packing here
case EncodingSortedZlib:
// We'll make a new buffer, then wrap that with a zlib writer
// so we can write directly to the buffer and encode in a
// streaming manner.
var buf bytes.Buffer
zlibWriter := zlib.NewWriter(&buf)
// If we don't have anything at all to write, then we'll write
// an empty payload so we don't include things like the zlib
// header when the remote party is expecting no actual short
// channel IDs.
var compressedPayload []byte
if len(shortChanIDs) > 0 {
// We'll make a new write buffer to hold the bytes of
// shortChanIDs.
var wb bytes.Buffer
// Next, we'll write out all the channel ID's directly
// into the zlib writer, which will do compressing on
// the fly.
for _, chanID := range shortChanIDs {
err := WriteElements(zlibWriter, chanID)
err := WriteShortChannelID(&wb, chanID)
if err != nil {
return fmt.Errorf("unable to write short chan "+
"ID: %v", err)
return fmt.Errorf(
"unable to write short chan "+
"ID: %v", err,
)
}
}
// With shortChanIDs written into wb, we'll create a
// zlib writer and write all the compressed bytes.
var zlibBuffer bytes.Buffer
zlibWriter := zlib.NewWriter(&zlibBuffer)
if _, err := zlibWriter.Write(wb.Bytes()); err != nil {
return fmt.Errorf(
"unable to write compressed short chan"+
"ID: %w", err)
}
// Now that we've written all the elements, we'll
// ensure the compressed stream is written to the
// underlying buffer.
@@ -391,7 +402,7 @@ func encodeShortChanIDs(w io.Writer, encodingType ShortChanIDEncoding,
"compression: %v", err)
}
compressedPayload = buf.Bytes()
compressedPayload = zlibBuffer.Bytes()
}
// Now that we have all the items compressed, we can compute
@@ -407,15 +418,15 @@ func encodeShortChanIDs(w io.Writer, encodingType ShortChanIDEncoding,
// Finally, we can write out the number of bytes, the
// compression type, and finally the buffer itself.
if err := WriteElements(w, uint16(numBytesBody)); err != nil {
if err := WriteUint16(w, uint16(numBytesBody)); err != nil {
return err
}
if err := WriteElements(w, encodingType); err != nil {
err := WriteShortChanIDEncoding(w, encodingType)
if err != nil {
return err
}
_, err := w.Write(compressedPayload)
return err
return WriteBytes(w, compressedPayload)
default:
// If we're trying to encode with an encoding type that we
+20 -13
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
"math"
@@ -85,23 +86,29 @@ func (c *ReplyChannelRange) Decode(r io.Reader, pver uint32) error {
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (c *ReplyChannelRange) Encode(w io.Writer, pver uint32) error {
err := WriteElements(w,
c.ChainHash[:],
c.FirstBlockHeight,
c.NumBlocks,
c.Complete,
)
func (c *ReplyChannelRange) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteBytes(w, c.ChainHash[:]); err != nil {
return err
}
if err := WriteUint32(w, c.FirstBlockHeight); err != nil {
return err
}
if err := WriteUint32(w, c.NumBlocks); err != nil {
return err
}
if err := WriteUint8(w, c.Complete); err != nil {
return err
}
err := encodeShortChanIDs(w, c.EncodingType, c.ShortChanIDs, c.noSort)
if err != nil {
return err
}
err = encodeShortChanIDs(w, c.EncodingType, c.ShortChanIDs, c.noSort)
if err != nil {
return err
}
return c.ExtraData.Encode(w)
return WriteBytes(w, c.ExtraData)
}
// MsgType returns the integer uniquely identifying this message type on the
+11 -6
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
"github.com/btcsuite/btcd/chaincfg/chainhash"
@@ -54,12 +55,16 @@ func (c *ReplyShortChanIDsEnd) Decode(r io.Reader, pver uint32) error {
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (c *ReplyShortChanIDsEnd) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
c.ChainHash[:],
c.Complete,
c.ExtraData,
)
func (c *ReplyShortChanIDsEnd) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteBytes(w, c.ChainHash[:]); err != nil {
return err
}
if err := WriteUint8(w, c.Complete); err != nil {
return err
}
return WriteBytes(w, c.ExtraData)
}
// MsgType returns the integer uniquely identifying this message type on the
+15 -7
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
"github.com/btcsuite/btcd/btcec"
@@ -65,13 +66,20 @@ func (c *RevokeAndAck) Decode(r io.Reader, pver uint32) error {
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (c *RevokeAndAck) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
c.ChanID,
c.Revocation[:],
c.NextRevocationKey,
c.ExtraData,
)
func (c *RevokeAndAck) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteChannelID(w, c.ChanID); err != nil {
return err
}
if err := WriteBytes(w, c.Revocation[:]); err != nil {
return err
}
if err := WritePublicKey(w, c.NextRevocationKey); err != nil {
return err
}
return WriteBytes(w, c.ExtraData)
}
// MsgType returns the integer uniquely identifying this message type on the
+11 -2
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
)
@@ -46,8 +47,16 @@ func (s *Shutdown) Decode(r io.Reader, pver uint32) error {
// 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, s.ExtraData)
func (s *Shutdown) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteChannelID(w, s.ChannelID); err != nil {
return err
}
if err := WriteDeliveryAddress(w, s.Address); err != nil {
return err
}
return WriteBytes(w, s.ExtraData)
}
// MsgType returns the integer uniquely identifying this message type on the
+29 -12
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
)
@@ -84,20 +85,36 @@ func (c *UpdateAddHTLC) Decode(r io.Reader, pver uint32) error {
)
}
// Encode serializes the target UpdateAddHTLC into the passed io.Writer observing
// the protocol version specified.
// Encode serializes the target UpdateAddHTLC into the passed io.Writer
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (c *UpdateAddHTLC) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
c.ChanID,
c.ID,
c.Amount,
c.PaymentHash[:],
c.Expiry,
c.OnionBlob[:],
c.ExtraData,
)
func (c *UpdateAddHTLC) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteChannelID(w, c.ChanID); err != nil {
return err
}
if err := WriteUint64(w, c.ID); err != nil {
return err
}
if err := WriteMilliSatoshi(w, c.Amount); err != nil {
return err
}
if err := WriteBytes(w, c.PaymentHash[:]); err != nil {
return err
}
if err := WriteUint32(w, c.Expiry); err != nil {
return err
}
if err := WriteBytes(w, c.OnionBlob[:]); err != nil {
return err
}
return WriteBytes(w, c.ExtraData)
}
// MsgType returns the integer uniquely identifying this message type on the
+15 -7
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
)
@@ -54,13 +55,20 @@ func (c *UpdateFailHTLC) Decode(r io.Reader, pver uint32) error {
// the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (c *UpdateFailHTLC) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
c.ChanID,
c.ID,
c.Reason,
c.ExtraData,
)
func (c *UpdateFailHTLC) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteChannelID(w, c.ChanID); err != nil {
return err
}
if err := WriteUint64(w, c.ID); err != nil {
return err
}
if err := WriteOpaqueReason(w, c.Reason); err != nil {
return err
}
return WriteBytes(w, c.ExtraData)
}
// MsgType returns the integer uniquely identifying this message type on the
+21 -8
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"crypto/sha256"
"io"
)
@@ -53,14 +54,26 @@ func (c *UpdateFailMalformedHTLC) Decode(r io.Reader, pver uint32) error {
// io.Writer observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (c *UpdateFailMalformedHTLC) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
c.ChanID,
c.ID,
c.ShaOnionBlob[:],
c.FailureCode,
c.ExtraData,
)
func (c *UpdateFailMalformedHTLC) Encode(w *bytes.Buffer,
pver uint32) error {
if err := WriteChannelID(w, c.ChanID); err != nil {
return err
}
if err := WriteUint64(w, c.ID); err != nil {
return err
}
if err := WriteBytes(w, c.ShaOnionBlob[:]); err != nil {
return err
}
if err := WriteFailCode(w, c.FailureCode); err != nil {
return err
}
return WriteBytes(w, c.ExtraData)
}
// MsgType returns the integer uniquely identifying this message type on the
+11 -6
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
)
@@ -51,12 +52,16 @@ func (c *UpdateFee) Decode(r io.Reader, pver uint32) error {
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (c *UpdateFee) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
c.ChanID,
c.FeePerKw,
c.ExtraData,
)
func (c *UpdateFee) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteChannelID(w, c.ChanID); err != nil {
return err
}
if err := WriteUint32(w, c.FeePerKw); err != nil {
return err
}
return WriteBytes(w, c.ExtraData)
}
// MsgType returns the integer uniquely identifying this message type on the
+15 -7
View File
@@ -1,6 +1,7 @@
package lnwire
import (
"bytes"
"io"
)
@@ -60,13 +61,20 @@ func (c *UpdateFulfillHTLC) Decode(r io.Reader, pver uint32) error {
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (c *UpdateFulfillHTLC) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
c.ChanID,
c.ID,
c.PaymentPreimage[:],
c.ExtraData,
)
func (c *UpdateFulfillHTLC) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteChannelID(w, c.ChanID); err != nil {
return err
}
if err := WriteUint64(w, c.ID); err != nil {
return err
}
if err := WriteBytes(w, c.PaymentPreimage[:]); err != nil {
return err
}
return WriteBytes(w, c.ExtraData)
}
// MsgType returns the integer uniquely identifying this message type on the
+431
View File
@@ -0,0 +1,431 @@
package lnwire
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"image/color"
"math"
"net"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/tor"
)
var (
// ErrNilFeatureVector is returned when the supplied feature is nil.
ErrNilFeatureVector = errors.New("cannot write nil feature vector")
// ErrPkScriptTooLong is returned when the length of the provided
// script exceeds 34.
ErrPkScriptTooLong = errors.New("'PkScript' too long")
// ErrNilTCPAddress is returned when the supplied address is nil.
ErrNilTCPAddress = errors.New("cannot write nil TCPAddr")
// ErrNilOnionAddress is returned when the supplied address is nil.
ErrNilOnionAddress = errors.New("cannot write nil onion address")
// ErrNilNetAddress is returned when a nil value is used in []net.Addr.
ErrNilNetAddress = errors.New("cannot write nil address")
// ErrNilPublicKey is returned when a nil pubkey is used.
ErrNilPublicKey = errors.New("cannot write nil pubkey")
// ErrUnknownServiceLength is returned when the onion service length is
// unknown.
ErrUnknownServiceLength = errors.New("unknown onion service length")
)
// ErrOutpointIndexTooBig is used when the outpoint index exceeds the max value
// of uint16.
func ErrOutpointIndexTooBig(index uint32) error {
return fmt.Errorf(
"index for outpoint (%v) is greater than "+
"max index of %v", index, math.MaxUint16,
)
}
// WriteBytes appends the given bytes to the provided buffer.
//
// Note: We intentionally skip the interfacer linter check here because we want
// to have concrete type (bytes.Buffer) rather than interface type (io.Write)
// due to performance concern.
func WriteBytes(buf *bytes.Buffer, b []byte) error { // nolint: interfacer
_, err := buf.Write(b)
return err
}
// WriteUint8 appends the uint8 to the provided buffer.
//
// Note: We intentionally skip the interfacer linter check here because we want
// to have concrete type (bytes.Buffer) rather than interface type (io.Write)
// due to performance concern.
func WriteUint8(buf *bytes.Buffer, n uint8) error { // nolint: interfacer
_, err := buf.Write([]byte{n})
return err
}
// WriteUint16 appends the uint16 to the provided buffer. It encodes the
// integer using big endian byte order.
//
// Note: We intentionally skip the interfacer linter check here because we want
// to have concrete type (bytes.Buffer) rather than interface type (io.Write)
// due to performance concern.
func WriteUint16(buf *bytes.Buffer, n uint16) error { // nolint: interfacer
var b [2]byte
binary.BigEndian.PutUint16(b[:], n)
_, err := buf.Write(b[:])
return err
}
// WriteUint32 appends the uint32 to the provided buffer. It encodes the
// integer using big endian byte order.
func WriteUint32(buf *bytes.Buffer, n uint32) error {
var b [4]byte
binary.BigEndian.PutUint32(b[:], n)
_, err := buf.Write(b[:])
return err
}
// WriteUint64 appends the uint64 to the provided buffer. It encodes the
// integer using big endian byte order.
//
// Note: We intentionally skip the interfacer linter check here because we want
// to have concrete type (bytes.Buffer) rather than interface type (io.Write)
// due to performance concern.
func WriteUint64(buf *bytes.Buffer, n uint64) error { // nolint: interfacer
var b [8]byte
binary.BigEndian.PutUint64(b[:], n)
_, err := buf.Write(b[:])
return err
}
// WriteSatoshi appends the Satoshi value to the provided buffer.
func WriteSatoshi(buf *bytes.Buffer, amount btcutil.Amount) error {
return WriteUint64(buf, uint64(amount))
}
// WriteMilliSatoshi appends the MilliSatoshi value to the provided buffer.
func WriteMilliSatoshi(buf *bytes.Buffer, amount MilliSatoshi) error {
return WriteUint64(buf, uint64(amount))
}
// WritePublicKey appends the compressed public key to the provided buffer.
func WritePublicKey(buf *bytes.Buffer, pub *btcec.PublicKey) error {
if pub == nil {
return ErrNilPublicKey
}
serializedPubkey := pub.SerializeCompressed()
return WriteBytes(buf, serializedPubkey)
}
// WriteChannelID appends the ChannelID to the provided buffer.
func WriteChannelID(buf *bytes.Buffer, channelID ChannelID) error {
return WriteBytes(buf, channelID[:])
}
// WriteNodeAlias appends the alias to the provided buffer.
func WriteNodeAlias(buf *bytes.Buffer, alias NodeAlias) error {
return WriteBytes(buf, alias[:])
}
// WriteShortChannelID appends the ShortChannelID to the provided buffer. It
// encodes the BlockHeight and TxIndex each using 3 bytes with big endian byte
// order, and encodes txPosition using 2 bytes with big endian byte order.
func WriteShortChannelID(buf *bytes.Buffer, shortChanID ShortChannelID) error {
// Check that field fit in 3 bytes and write the blockHeight
if shortChanID.BlockHeight > ((1 << 24) - 1) {
return errors.New("block height should fit in 3 bytes")
}
var blockHeight [4]byte
binary.BigEndian.PutUint32(blockHeight[:], shortChanID.BlockHeight)
if _, err := buf.Write(blockHeight[1:]); err != nil {
return err
}
// Check that field fit in 3 bytes and write the txIndex
if shortChanID.TxIndex > ((1 << 24) - 1) {
return errors.New("tx index should fit in 3 bytes")
}
var txIndex [4]byte
binary.BigEndian.PutUint32(txIndex[:], shortChanID.TxIndex)
if _, err := buf.Write(txIndex[1:]); err != nil {
return err
}
// Write the TxPosition
return WriteUint16(buf, shortChanID.TxPosition)
}
// WriteSig appends the signature to the provided buffer.
func WriteSig(buf *bytes.Buffer, sig Sig) error {
return WriteBytes(buf, sig[:])
}
// WriteSigs appends the slice of signatures to the provided buffer with its
// length.
func WriteSigs(buf *bytes.Buffer, sigs []Sig) error {
// Write the length of the sigs.
if err := WriteUint16(buf, uint16(len(sigs))); err != nil {
return err
}
for _, sig := range sigs {
if err := WriteSig(buf, sig); err != nil {
return err
}
}
return nil
}
// WriteFailCode appends the FailCode to the provided buffer.
func WriteFailCode(buf *bytes.Buffer, e FailCode) error {
return WriteUint16(buf, uint16(e))
}
// WriteRawFeatureVector encodes the feature using the feature's Encode method
// and appends the data to the provided buffer. An error will return if the
// passed feature is nil.
//
// Note: We intentionally skip the interfacer linter check here because we want
// to have concrete type (bytes.Buffer) rather than interface type (io.Write)
// due to performance concern.
func WriteRawFeatureVector(buf *bytes.Buffer, // nolint: interfacer
feature *RawFeatureVector) error {
if feature == nil {
return ErrNilFeatureVector
}
return feature.Encode(buf)
}
// WriteColorRGBA appends the RGBA color using three bytes.
func WriteColorRGBA(buf *bytes.Buffer, e color.RGBA) error {
// Write R
if err := WriteUint8(buf, e.R); err != nil {
return err
}
// Write G
if err := WriteUint8(buf, e.G); err != nil {
return err
}
// Write B
return WriteUint8(buf, e.B)
}
// WriteShortChanIDEncoding appends the ShortChanIDEncoding to the provided
// buffer.
func WriteShortChanIDEncoding(buf *bytes.Buffer, e ShortChanIDEncoding) error {
return WriteUint8(buf, uint8(e))
}
// WriteFundingFlag appends the FundingFlag to the provided buffer.
func WriteFundingFlag(buf *bytes.Buffer, flag FundingFlag) error {
return WriteUint8(buf, uint8(flag))
}
// WriteChanUpdateMsgFlags appends the update flag to the provided buffer.
func WriteChanUpdateMsgFlags(buf *bytes.Buffer, f ChanUpdateMsgFlags) error {
return WriteUint8(buf, uint8(f))
}
// WriteChanUpdateChanFlags appends the update flag to the provided buffer.
func WriteChanUpdateChanFlags(buf *bytes.Buffer, f ChanUpdateChanFlags) error {
return WriteUint8(buf, uint8(f))
}
// WriteDeliveryAddress appends the address to the provided buffer.
func WriteDeliveryAddress(buf *bytes.Buffer, addr DeliveryAddress) error {
return writeDataWithLength(buf, addr)
}
// WritePingPayload appends the payload to the provided buffer.
func WritePingPayload(buf *bytes.Buffer, payload PingPayload) error {
return writeDataWithLength(buf, payload)
}
// WritePongPayload appends the payload to the provided buffer.
func WritePongPayload(buf *bytes.Buffer, payload PongPayload) error {
return writeDataWithLength(buf, payload)
}
// WriteErrorData appends the data to the provided buffer.
func WriteErrorData(buf *bytes.Buffer, data ErrorData) error {
return writeDataWithLength(buf, data)
}
// WriteOpaqueReason appends the reason to the provided buffer.
func WriteOpaqueReason(buf *bytes.Buffer, reason OpaqueReason) error {
return writeDataWithLength(buf, reason)
}
// WriteBool appends the boolean to the provided buffer.
func WriteBool(buf *bytes.Buffer, b bool) error {
if b {
return WriteBytes(buf, []byte{1})
}
return WriteBytes(buf, []byte{0})
}
// WritePkScript appends the script to the provided buffer. Returns an error if
// the provided script exceeds 34 bytes.
//
// Note: We intentionally skip the interfacer linter check here because we want
// to have concrete type (bytes.Buffer) rather than interface type (io.Write)
// due to performance concern.
func WritePkScript(buf *bytes.Buffer, s PkScript) error { // nolint: interfacer
// The largest script we'll accept is a p2wsh which is exactly
// 34 bytes long.
scriptLength := len(s)
if scriptLength > 34 {
return ErrPkScriptTooLong
}
return wire.WriteVarBytes(buf, 0, s)
}
// WriteOutPoint appends the outpoint to the provided buffer.
func WriteOutPoint(buf *bytes.Buffer, p wire.OutPoint) error {
// Before we write anything to the buffer, check the Index is sane.
if p.Index > math.MaxUint16 {
return ErrOutpointIndexTooBig(p.Index)
}
var h [32]byte
copy(h[:], p.Hash[:])
if _, err := buf.Write(h[:]); err != nil {
return err
}
// Write the index using two bytes.
return WriteUint16(buf, uint16(p.Index))
}
// WriteTCPAddr appends the TCP address to the provided buffer, either a IPv4
// or a IPv6.
func WriteTCPAddr(buf *bytes.Buffer, addr *net.TCPAddr) error {
if addr == nil {
return ErrNilTCPAddress
}
// Make a slice of bytes to hold the data of descriptor and ip. At
// most, we need 17 bytes - 1 byte for the descriptor, 16 bytes for
// IPv6.
data := make([]byte, 0, 17)
if addr.IP.To4() != nil {
data = append(data, uint8(tcp4Addr))
data = append(data, addr.IP.To4()...)
} else {
data = append(data, uint8(tcp6Addr))
data = append(data, addr.IP.To16()...)
}
if _, err := buf.Write(data); err != nil {
return err
}
return WriteUint16(buf, uint16(addr.Port))
}
// WriteOnionAddr appends the onion address to the provided buffer.
func WriteOnionAddr(buf *bytes.Buffer, addr *tor.OnionAddr) error {
if addr == nil {
return ErrNilOnionAddress
}
var (
suffixIndex int
descriptor []byte
)
// Decide the suffixIndex and descriptor.
switch len(addr.OnionService) {
case tor.V2Len:
descriptor = []byte{byte(v2OnionAddr)}
suffixIndex = tor.V2Len - tor.OnionSuffixLen
case tor.V3Len:
descriptor = []byte{byte(v3OnionAddr)}
suffixIndex = tor.V3Len - tor.OnionSuffixLen
default:
return ErrUnknownServiceLength
}
// Decode the address.
host, err := tor.Base32Encoding.DecodeString(
addr.OnionService[:suffixIndex],
)
if err != nil {
return err
}
// Perform the actual write when the above checks passed.
if _, err := buf.Write(descriptor); err != nil {
return err
}
if _, err := buf.Write(host); err != nil {
return err
}
return WriteUint16(buf, uint16(addr.Port))
}
// WriteNetAddrs appends a slice of addresses to the provided buffer with the
// length info.
func WriteNetAddrs(buf *bytes.Buffer, addresses []net.Addr) error {
// First, we'll encode all the addresses into an intermediate
// buffer. We need to do this in order to compute the total
// length of the addresses.
buffer := make([]byte, 0, MaxMsgBody)
addrBuf := bytes.NewBuffer(buffer)
for _, address := range addresses {
switch a := address.(type) {
case *net.TCPAddr:
if err := WriteTCPAddr(addrBuf, a); err != nil {
return err
}
case *tor.OnionAddr:
if err := WriteOnionAddr(addrBuf, a); err != nil {
return err
}
default:
return ErrNilNetAddress
}
}
// With the addresses fully encoded, we can now write out data.
return writeDataWithLength(buf, addrBuf.Bytes())
}
// writeDataWithLength writes the data and its length to the buffer.
//
// Note: We intentionally skip the interfacer linter check here because we want
// to have concrete type (bytes.Buffer) rather than interface type (io.Write)
// due to performance concern.
func writeDataWithLength(buf *bytes.Buffer, // nolint: interfacer
data []byte) error {
var l [2]byte
binary.BigEndian.PutUint16(l[:], uint16(len(data)))
if _, err := buf.Write(l[:]); err != nil {
return err
}
_, err := buf.Write(data)
return err
}
+618
View File
@@ -0,0 +1,618 @@
package lnwire
import (
"bytes"
"encoding/base32"
"image/color"
"math"
"net"
"testing"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/tor"
"github.com/stretchr/testify/require"
)
func TestWriteBytes(t *testing.T) {
buf := new(bytes.Buffer)
data := []byte{1, 2, 3}
err := WriteBytes(buf, data)
require.NoError(t, err)
require.Equal(t, data, buf.Bytes())
}
func TestWriteUint8(t *testing.T) {
buf := new(bytes.Buffer)
data := uint8(1)
expectedBytes := []byte{1}
err := WriteUint8(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteUint16(t *testing.T) {
buf := new(bytes.Buffer)
data := uint16(1)
expectedBytes := []byte{0, 1}
err := WriteUint16(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteUint32(t *testing.T) {
buf := new(bytes.Buffer)
data := uint32(1)
expectedBytes := []byte{0, 0, 0, 1}
err := WriteUint32(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteUint64(t *testing.T) {
buf := new(bytes.Buffer)
data := uint64(1)
expectedBytes := []byte{0, 0, 0, 0, 0, 0, 0, 1}
err := WriteUint64(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteSatoshi(t *testing.T) {
buf := new(bytes.Buffer)
data := btcutil.Amount(1)
expectedBytes := []byte{0, 0, 0, 0, 0, 0, 0, 1}
err := WriteSatoshi(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteMilliSatoshi(t *testing.T) {
buf := new(bytes.Buffer)
data := MilliSatoshi(1)
expectedBytes := []byte{0, 0, 0, 0, 0, 0, 0, 1}
err := WriteMilliSatoshi(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWritePublicKey(t *testing.T) {
buf := new(bytes.Buffer)
// Check that when nil pubkey is used, an error will return.
err := WritePublicKey(buf, nil)
require.Equal(t, ErrNilPublicKey, err)
pub, err := randPubKey()
require.NoError(t, err)
expectedBytes := pub.SerializeCompressed()
err = WritePublicKey(buf, pub)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteChannelID(t *testing.T) {
buf := new(bytes.Buffer)
data := ChannelID{1}
expectedBytes := [32]byte{1}
err := WriteChannelID(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes[:], buf.Bytes())
}
func TestWriteNodeAlias(t *testing.T) {
buf := new(bytes.Buffer)
data := NodeAlias{1}
expectedBytes := [32]byte{1}
err := WriteNodeAlias(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes[:], buf.Bytes())
}
func TestWriteShortChannelID(t *testing.T) {
buf := new(bytes.Buffer)
data := ShortChannelID{BlockHeight: 1, TxIndex: 2, TxPosition: 3}
expectedBytes := []byte{
0, 0, 1, // First three bytes encodes BlockHeight.
0, 0, 2, // Second three bytes encodes TxIndex.
0, 3, // Final two bytes encodes TxPosition.
}
err := WriteShortChannelID(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteSig(t *testing.T) {
buf := new(bytes.Buffer)
data := Sig{1, 2, 3}
expectedBytes := [64]byte{1, 2, 3}
err := WriteSig(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes[:], buf.Bytes())
}
func TestWriteSigs(t *testing.T) {
buf := new(bytes.Buffer)
sig1, sig2, sig3 := Sig{1}, Sig{2}, Sig{3}
data := []Sig{sig1, sig2, sig3}
// First two bytes encode the length of the slice.
expectedBytes := []byte{0, 3}
expectedBytes = append(expectedBytes, sig1[:]...)
expectedBytes = append(expectedBytes, sig2[:]...)
expectedBytes = append(expectedBytes, sig3[:]...)
err := WriteSigs(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteFailCode(t *testing.T) {
buf := new(bytes.Buffer)
data := FailCode(1)
expectedBytes := []byte{0, 1}
err := WriteFailCode(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
// TODO(yy): expand the test to cover more encoding scenarios.
func TestWriteRawFeatureVector(t *testing.T) {
buf := new(bytes.Buffer)
// Check that when nil feature is used, an error will return.
err := WriteRawFeatureVector(buf, nil)
require.Equal(t, ErrNilFeatureVector, err)
// Create a raw feature vector.
feature := &RawFeatureVector{features: map[FeatureBit]bool{
InitialRoutingSync: true, // FeatureBit 3.
}}
expectedBytes := []byte{
0, 1, // First two bytes encode the length.
8, // Last byte encodes the feature bit (1 << 3).
}
err = WriteRawFeatureVector(buf, feature)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteColorRGBA(t *testing.T) {
buf := new(bytes.Buffer)
data := color.RGBA{R: 1, G: 2, B: 3}
expectedBytes := []byte{1, 2, 3}
err := WriteColorRGBA(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteShortChanIDEncoding(t *testing.T) {
buf := new(bytes.Buffer)
data := ShortChanIDEncoding(1)
expectedBytes := []byte{1}
err := WriteShortChanIDEncoding(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteFundingFlag(t *testing.T) {
buf := new(bytes.Buffer)
data := FundingFlag(1)
expectedBytes := []byte{1}
err := WriteFundingFlag(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteChanUpdateMsgFlags(t *testing.T) {
buf := new(bytes.Buffer)
data := ChanUpdateMsgFlags(1)
expectedBytes := []byte{1}
err := WriteChanUpdateMsgFlags(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteChanUpdateChanFlags(t *testing.T) {
buf := new(bytes.Buffer)
data := ChanUpdateChanFlags(1)
expectedBytes := []byte{1}
err := WriteChanUpdateChanFlags(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteDeliveryAddress(t *testing.T) {
buf := new(bytes.Buffer)
data := DeliveryAddress{1, 1, 1}
expectedBytes := []byte{
0, 3, // First two bytes encode the length.
1, 1, 1, // The actual data.
}
err := WriteDeliveryAddress(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWritePingPayload(t *testing.T) {
buf := new(bytes.Buffer)
data := PingPayload{1, 1, 1}
expectedBytes := []byte{
0, 3, // First two bytes encode the length.
1, 1, 1, // The actual data.
}
err := WritePingPayload(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWritePongPayload(t *testing.T) {
buf := new(bytes.Buffer)
data := PongPayload{1, 1, 1}
expectedBytes := []byte{
0, 3, // First two bytes encode the length.
1, 1, 1, // The actual data.
}
err := WritePongPayload(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteErrorData(t *testing.T) {
buf := new(bytes.Buffer)
data := ErrorData{1, 1, 1}
expectedBytes := []byte{
0, 3, // First two bytes encode the length.
1, 1, 1, // The actual data.
}
err := WriteErrorData(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteOpaqueReason(t *testing.T) {
buf := new(bytes.Buffer)
data := OpaqueReason{1, 1, 1}
expectedBytes := []byte{
0, 3, // First two bytes encode the length.
1, 1, 1, // The actual data.
}
err := WriteOpaqueReason(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteBool(t *testing.T) {
buf := new(bytes.Buffer)
// Test write true.
data := true
expectedBytes := []byte{1}
err := WriteBool(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
// Test write false.
data = false
expectedBytes = append(expectedBytes, 0)
err = WriteBool(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWritePkScript(t *testing.T) {
buf := new(bytes.Buffer)
// Write a very long script to check the error is returned as expected.
script := PkScript{}
zeros := [35]byte{}
script = append(script, zeros[:]...)
err := WritePkScript(buf, script)
require.Equal(t, ErrPkScriptTooLong, err)
data := PkScript{1, 1, 1}
expectedBytes := []byte{
3, // First byte encodes the length.
1, 1, 1, // The actual data.
}
err = WritePkScript(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteOutPoint(t *testing.T) {
buf := new(bytes.Buffer)
// Create an outpoint with very large index to check the error is
// returned as expected.
outpointWrong := wire.OutPoint{Index: math.MaxUint16 + 1}
err := WriteOutPoint(buf, outpointWrong)
require.Equal(t, ErrOutpointIndexTooBig(outpointWrong.Index), err)
// Now check the normal write succeeds.
hash := chainhash.Hash{1}
data := wire.OutPoint{Index: 2, Hash: hash}
expectedBytes := []byte{}
expectedBytes = append(expectedBytes, hash[:]...)
expectedBytes = append(expectedBytes, []byte{0, 2}...)
err = WriteOutPoint(buf, data)
require.NoError(t, err)
require.Equal(t, expectedBytes, buf.Bytes())
}
func TestWriteTCPAddr(t *testing.T) {
buf := new(bytes.Buffer)
testCases := []struct {
name string
addr *net.TCPAddr
expectedErr error
expectedBytes []byte
}{
{
// Check that the error is returned when nil address is
// used.
name: "nil address err",
addr: nil,
expectedErr: ErrNilTCPAddress,
expectedBytes: nil,
},
{
// Check write IPv4.
name: "write ipv4",
addr: &net.TCPAddr{
IP: net.IP{127, 0, 0, 1},
Port: 8080,
},
expectedErr: nil,
expectedBytes: []byte{
0x1, // The addressType.
0x7f, 0x0, 0x0, 0x1, // The IP.
0x1f, 0x90, // The port (31 * 256 + 144).
},
},
{
// Check write IPv6.
name: "write ipv6",
addr: &net.TCPAddr{
IP: net.IP{
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
},
Port: 8080,
},
expectedErr: nil,
expectedBytes: []byte{
0x2, // The addressType.
// The IP.
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
0x1f, 0x90, // The port (31 * 256 + 144).
},
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
oldLen := buf.Len()
err := WriteTCPAddr(buf, tc.addr)
require.Equal(t, tc.expectedErr, err)
bytesWritten := buf.Bytes()[oldLen:buf.Len()]
require.Equal(t, tc.expectedBytes, bytesWritten)
})
}
}
func TestWriteOnionAddr(t *testing.T) {
buf := new(bytes.Buffer)
testCases := []struct {
name string
addr *tor.OnionAddr
expectedErr error
expectedBytes []byte
}{
{
// Check that the error is returned when nil address is
// used.
name: "nil address err",
addr: nil,
expectedErr: ErrNilOnionAddress,
expectedBytes: nil,
},
{
// Check the error is returned when an invalid onion
// address is used.
name: "wrong onion service length",
addr: &tor.OnionAddr{OnionService: "wrong"},
expectedErr: ErrUnknownServiceLength,
expectedBytes: nil,
},
{
// Check when the address has invalid base32 encoding,
// the error is returned.
name: "invalid base32 encoding",
addr: &tor.OnionAddr{
OnionService: "1234567890123456.onion",
},
expectedErr: base32.CorruptInputError(0),
expectedBytes: nil,
},
{
// Check write onion v2.
name: "onion address v2",
addr: &tor.OnionAddr{
OnionService: "abcdefghijklmnop.onion",
Port: 9065,
},
expectedErr: nil,
expectedBytes: []byte{
0x3, // The descriptor.
0x0, 0x44, 0x32, 0x14, 0xc7, // The host.
0x42, 0x54, 0xb6, 0x35, 0xcf,
0x23, 0x69, // The port.
},
},
{
// Check write onion v3.
name: "onion address v3",
addr: &tor.OnionAddr{
OnionService: "abcdefghij" +
"abcdefghijabcdefghij" +
"abcdefghijabcdefghij" +
"234567.onion",
Port: 9065,
},
expectedErr: nil,
expectedBytes: []byte{
0x4, // The descriptor.
0x0, 0x44, 0x32, 0x14, 0xc7, 0x42, 0x40,
0x11, 0xc, 0x85, 0x31, 0xd0, 0x90, 0x4,
0x43, 0x21, 0x4c, 0x74, 0x24, 0x1, 0x10,
0xc8, 0x53, 0x1d, 0x9, 0x0, 0x44, 0x32,
0x14, 0xc7, 0x42, 0x75, 0xbe, 0x77, 0xdf,
0x23, 0x69, // The port.
},
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
oldLen := buf.Len()
err := WriteOnionAddr(buf, tc.addr)
require.Equal(t, tc.expectedErr, err)
bytesWritten := buf.Bytes()[oldLen:buf.Len()]
require.Equal(t, tc.expectedBytes, bytesWritten)
})
}
}
func TestWriteNetAddrs(t *testing.T) {
buf := new(bytes.Buffer)
tcpAddr := &net.TCPAddr{
IP: net.IP{127, 0, 0, 1},
Port: 8080,
}
onionAddr := &tor.OnionAddr{
OnionService: "abcdefghijklmnop.onion",
Port: 9065,
}
testCases := []struct {
name string
addr []net.Addr
expectedErr error
expectedBytes []byte
}{
{
// Check that the error is returned when nil address is
// used.
name: "nil address err",
addr: []net.Addr{nil, tcpAddr, onionAddr},
expectedErr: ErrNilNetAddress,
expectedBytes: nil,
},
{
// Check empty address slice.
name: "empty address slice",
addr: []net.Addr{},
expectedErr: nil,
// Use two bytes to encode the address size.
expectedBytes: []byte{0, 0},
},
{
// Check a successful writes of a slice of addresses.
name: "two addresses",
addr: []net.Addr{tcpAddr, onionAddr},
expectedErr: nil,
expectedBytes: []byte{
// 7 bytes for TCP and 13 bytes for onion.
0x0, 0x14,
// TCP address.
0x1, 0x7f, 0x0, 0x0, 0x1, 0x1f, 0x90,
// Onion address.
0x3, 0x0, 0x44, 0x32, 0x14, 0xc7, 0x42,
0x54, 0xb6, 0x35, 0xcf, 0x23, 0x69,
},
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
oldLen := buf.Len()
err := WriteNetAddrs(buf, tc.addr)
require.Equal(t, tc.expectedErr, err)
bytesWritten := buf.Bytes()[oldLen:buf.Len()]
require.Equal(t, tc.expectedBytes, bytesWritten)
})
}
}
+8 -3
View File
@@ -5,8 +5,6 @@ import (
"encoding/binary"
"fmt"
"io"
"github.com/lightningnetwork/lnd/lnwire"
)
// MaxMessagePayload is the maximum bytes a message can be regardless of other
@@ -75,7 +73,14 @@ func (m MessageType) String() string {
// Serializable is an interface which defines a lightning wire serializable
// object.
type Serializable = lnwire.Serializable
type Serializable interface {
// Decode reads the bytes stream and converts it to the object.
Decode(io.Reader, uint32) error
// Encode converts object to the bytes stream and write it into the
// write buffer.
Encode(io.Writer, uint32) error
}
// Message is an interface that defines a lightning wire protocol message. The
// interface is general in order to allow implementing types full control over