From 0d7b8be11b009456ef7f62b8e6a9ef0aeb3595df Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2018 19:40:30 -0800 Subject: [PATCH 01/16] lnwire: add new Sig type to handle conversion to/from btcec.Signature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit, we add a new signature type. We’ll use this type to avoid fully decoding a signature on the wire into a btcec.Signature. This type is only really needed when we need to do signature validation, as a result, always encoding it is a waste. Several helper methods have been added to the new struct in order to ensure that we can use it in the existing codebase without substantial issues. --- lnwire/signature.go | 64 +++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/lnwire/signature.go b/lnwire/signature.go index b753ef21..0bc2b832 100644 --- a/lnwire/signature.go +++ b/lnwire/signature.go @@ -6,12 +6,16 @@ import ( "github.com/roasbeef/btcd/btcec" ) -// SerializeSigToWire serializes a *Signature to [64]byte in the format -// specified by the Lightning RFC. -func SerializeSigToWire(b *[64]byte, e *btcec.Signature) error { +// Sig is a fixed-sized ECDSA signature. Unlike Bitcoin, we use fixed sized +// signatures on the wire, instead of DER encoded signatures. This type +// provides several methods to convert to/from a regular Bitcoin DER encoded +// signature (raw bytes and *btcec.Signature). +type Sig [64]byte - // Serialize the signature with all the checks that entails. - sig := e.Serialize() +// NewSigFromRawSignature returns a Sig from a Bitcoin raw signature encoded in +// the cannonical DER encoding. +func NewSigFromRawSignature(sig []byte) (Sig, error) { + var b Sig // Extract lengths of R and S. The DER representation is laid out as // 0x30 0x02 r 0x02 s @@ -23,14 +27,14 @@ func SerializeSigToWire(b *[64]byte, e *btcec.Signature) error { sLen := sig[5+rLen] // Check to make sure R and S can both fit into their intended buffers. - // We check S first because these code blocks decrement sLen and - // rLen in the case of a 33-byte 0-padded integer returned from - // Serialize() and rLen is used in calculating array indices for - // S. We can track this with additional variables, but it's more - // efficient to just check S first. + // We check S first because these code blocks decrement sLen and rLen + // in the case of a 33-byte 0-padded integer returned from Serialize() + // and rLen is used in calculating array indices for S. We can track + // this with additional variables, but it's more efficient to just + // check S first. if sLen > 32 { if (sLen > 33) || (sig[6+rLen] != 0x00) { - return fmt.Errorf("S is over 32 bytes long " + + return b, fmt.Errorf("S is over 32 bytes long " + "without padding") } sLen-- @@ -42,7 +46,7 @@ func SerializeSigToWire(b *[64]byte, e *btcec.Signature) error { // Do the same for R as we did for S if rLen > 32 { if (rLen > 33) || (sig[4] != 0x00) { - return fmt.Errorf("R is over 32 bytes long " + + return b, fmt.Errorf("R is over 32 bytes long " + "without padding") } rLen-- @@ -50,13 +54,33 @@ func SerializeSigToWire(b *[64]byte, e *btcec.Signature) error { } else { copy(b[32-rLen:], sig[4:4+rLen]) } - return nil + + return b, nil } -// DeserializeSigFromWire deserializes a *Signature from [64]byte in the format -// specified by the Lightning RFC. -func DeserializeSigFromWire(e **btcec.Signature, b [64]byte) error { +// NewSigFromSignature creates a new signature as used on the wire, from an +// existing btcec.Signature. +func NewSigFromSignature(e *btcec.Signature) (Sig, error) { + // Serialize the signature with all the checks that entails. + return NewSigFromRawSignature(e.Serialize()) +} +// ToSignature converts the fixed-sized signature to a btcec.Signature objects +// which can be used for signature validation checks. +func (b *Sig) ToSignature() (*btcec.Signature, error) { + // Parse the signature with strict checks. + sigBytes := b.ToSignatureBytes() + sig, err := btcec.ParseDERSignature(sigBytes, btcec.S256()) + if err != nil { + return nil, err + } + + return sig, nil +} + +// ToSignatureBytes serializes the target fixed-sized signature into the raw +// bytes of a DER encoding. +func (b *Sig) ToSignatureBytes() []byte { // Extract canonically-padded bigint representations from buffer r := extractCanonicalPadding(b[0:32]) s := extractCanonicalPadding(b[32:64]) @@ -75,13 +99,7 @@ func DeserializeSigFromWire(e **btcec.Signature, b [64]byte) error { copy(sigBytes[4:], r) // Copy R copy(sigBytes[rLen+6:], s) // Copy S - // Parse the signature with strict checks. - sig, err := btcec.ParseDERSignature(sigBytes, btcec.S256()) - if err != nil { - return err - } - *e = sig - return nil + return sigBytes } // extractCanonicalPadding is a utility function to extract the canonical From 4dd108c8270fc66d135e28c28bf62df9128234c0 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2018 19:41:52 -0800 Subject: [PATCH 02/16] lnwire: replace usage of btcec.Signature with the new lnwire.Sig type --- lnwire/announcement_signatures.go | 10 +++------- lnwire/channel_announcement.go | 9 ++++----- lnwire/channel_update.go | 3 +-- lnwire/closing_signed.go | 5 ++--- lnwire/commit_sig.go | 10 +++------- lnwire/funding_created.go | 3 +-- lnwire/funding_signed.go | 8 ++------ lnwire/lnwire.go | 30 ++++++++---------------------- lnwire/node_announcement.go | 2 +- lnwire/onion_error_test.go | 3 ++- lnwire/signature_test.go | 8 ++++---- 11 files changed, 31 insertions(+), 60 deletions(-) diff --git a/lnwire/announcement_signatures.go b/lnwire/announcement_signatures.go index fabefadd..748f29b1 100644 --- a/lnwire/announcement_signatures.go +++ b/lnwire/announcement_signatures.go @@ -1,10 +1,6 @@ package lnwire -import ( - "io" - - "github.com/roasbeef/btcd/btcec" -) +import "io" // AnnounceSignatures this is a direct message between two endpoints of a // channel and serves as an opt-in mechanism to allow the announcement of @@ -27,13 +23,13 @@ type AnnounceSignatures struct { // NodeSignature is the signature which contains the signed announce // channel message, by this signature we proof that we possess of the // node pub key and creating the reference node_key -> bitcoin_key. - NodeSignature *btcec.Signature + NodeSignature Sig // BitcoinSignature is the signature which contains the signed node // public key, by this signature we proof that we possess of the // bitcoin key and and creating the reverse reference bitcoin_key -> // node_key. - BitcoinSignature *btcec.Signature + BitcoinSignature Sig } // A compile time check to ensure AnnounceSignatures implements the diff --git a/lnwire/channel_announcement.go b/lnwire/channel_announcement.go index 29dbb39a..ed776ca7 100644 --- a/lnwire/channel_announcement.go +++ b/lnwire/channel_announcement.go @@ -4,7 +4,6 @@ import ( "bytes" "io" - "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/chaincfg/chainhash" ) @@ -16,14 +15,14 @@ type ChannelAnnouncement struct { // references between node's channel and node. Requiring both nodes // to sign indicates they are both willing to route other payments via // this node. - NodeSig1 *btcec.Signature - NodeSig2 *btcec.Signature + NodeSig1 Sig + NodeSig2 Sig // 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. - BitcoinSig1 *btcec.Signature - BitcoinSig2 *btcec.Signature + BitcoinSig1 Sig + BitcoinSig2 Sig // Features is the feature vector that encodes the features supported // by the target node. This field can be used to signal the type of the diff --git a/lnwire/channel_update.go b/lnwire/channel_update.go index 0243cc68..cf6482a2 100644 --- a/lnwire/channel_update.go +++ b/lnwire/channel_update.go @@ -4,7 +4,6 @@ import ( "bytes" "io" - "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/chaincfg/chainhash" ) @@ -32,7 +31,7 @@ const ( type ChannelUpdate struct { // Signature is used to validate the announced data and prove the // ownership of node id. - Signature *btcec.Signature + Signature Sig // ChainHash denotes the target chain that this channel was opened // within. This value should be the genesis hash of the target chain. diff --git a/lnwire/closing_signed.go b/lnwire/closing_signed.go index 087f879b..81a7d1b5 100644 --- a/lnwire/closing_signed.go +++ b/lnwire/closing_signed.go @@ -3,7 +3,6 @@ package lnwire import ( "io" - "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcutil" ) @@ -27,12 +26,12 @@ type ClosingSigned struct { FeeSatoshis btcutil.Amount // Signature is for the proposed channel close transaction. - Signature *btcec.Signature + Signature Sig } // NewClosingSigned creates a new empty ClosingSigned message. func NewClosingSigned(cid ChannelID, fs btcutil.Amount, - sig *btcec.Signature) *ClosingSigned { + sig Sig) *ClosingSigned { return &ClosingSigned{ ChannelID: cid, diff --git a/lnwire/commit_sig.go b/lnwire/commit_sig.go index 0d966e53..a66a0dae 100644 --- a/lnwire/commit_sig.go +++ b/lnwire/commit_sig.go @@ -1,10 +1,6 @@ package lnwire -import ( - "io" - - "github.com/roasbeef/btcd/btcec" -) +import "io" // CommitSig is sent by either side to stage any pending HTLC's in the // receiver's pending set into a new commitment state. Implicitly, the new @@ -26,7 +22,7 @@ type CommitSig struct { // If initiating a new commitment state, this signature should ONLY // cover all of the sending party's pending log updates, and the log // updates of the remote party that have been ACK'd. - CommitSig *btcec.Signature + CommitSig Sig // HtlcSigs is a signature for each relevant HTLC output within the // created commitment. The order of the signatures is expected to be @@ -35,7 +31,7 @@ type CommitSig struct { // sender of this message), a signature for a HTLC timeout transaction // should be signed, for each incoming HTLC the HTLC timeout // transaction should be signed. - HtlcSigs []*btcec.Signature + HtlcSigs []Sig } // NewCommitSig creates a new empty CommitSig message. diff --git a/lnwire/funding_created.go b/lnwire/funding_created.go index b9481ebf..66f36fc0 100644 --- a/lnwire/funding_created.go +++ b/lnwire/funding_created.go @@ -3,7 +3,6 @@ package lnwire import ( "io" - "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/wire" ) @@ -24,7 +23,7 @@ type FundingCreated struct { // CommitSig is Alice's signature from Bob's version of the commitment // transaction. - CommitSig *btcec.Signature + CommitSig Sig } // A compile time check to ensure FundingCreated implements the lnwire.Message diff --git a/lnwire/funding_signed.go b/lnwire/funding_signed.go index 7e795986..19f3ba8f 100644 --- a/lnwire/funding_signed.go +++ b/lnwire/funding_signed.go @@ -1,10 +1,6 @@ package lnwire -import ( - "io" - - "github.com/roasbeef/btcd/btcec" -) +import "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 @@ -16,7 +12,7 @@ type FundingSigned struct { // CommitSig is Bob's signature for Alice's version of the commitment // transaction. - CommitSig *btcec.Signature + CommitSig Sig } // A compile time check to ensure FundingSigned implements the lnwire.Message diff --git a/lnwire/lnwire.go b/lnwire/lnwire.go index 450b8b21..5e486582 100644 --- a/lnwire/lnwire.go +++ b/lnwire/lnwire.go @@ -146,7 +146,7 @@ func writeElement(w io.Writer, element interface{}) error { if _, err := w.Write(b[:]); err != nil { return err } - case []*btcec.Signature: + case []Sig: var b [2]byte numSigs := uint16(len(e)) binary.BigEndian.PutUint16(b[:], numSigs) @@ -159,18 +159,9 @@ func writeElement(w io.Writer, element interface{}) error { return err } } - case *btcec.Signature: - if e == nil { - return fmt.Errorf("cannot write nil signature") - } - - var b [64]byte - err := SerializeSigToWire(&b, e) - if err != nil { - return err - } + case Sig: // Write buffer - if _, err = w.Write(b[:]); err != nil { + if _, err := w.Write(e[:]); err != nil { return err } case PingPayload: @@ -469,16 +460,16 @@ func readElement(r io.Reader, element interface{}) error { *e = f - case *[]*btcec.Signature: + case *[]Sig: var l [2]byte if _, err := io.ReadFull(r, l[:]); err != nil { return err } numSigs := binary.BigEndian.Uint16(l[:]) - var sigs []*btcec.Signature + var sigs []Sig if numSigs > 0 { - sigs = make([]*btcec.Signature, numSigs) + sigs = make([]Sig, numSigs) for i := 0; i < int(numSigs); i++ { if err := readElement(r, &sigs[i]); err != nil { return err @@ -488,13 +479,8 @@ func readElement(r io.Reader, element interface{}) error { *e = sigs - case **btcec.Signature: - var b [64]byte - if _, err := io.ReadFull(r, b[:]); err != nil { - return err - } - err = DeserializeSigFromWire(e, b) - if err != nil { + case *Sig: + if _, err := io.ReadFull(r, e[:]); err != nil { return err } case *OpaqueReason: diff --git a/lnwire/node_announcement.go b/lnwire/node_announcement.go index 1eaeade3..3f6988f7 100644 --- a/lnwire/node_announcement.go +++ b/lnwire/node_announcement.go @@ -50,7 +50,7 @@ func (n NodeAlias) String() string { // announcement via a signature using the advertised node pubkey. type NodeAnnouncement struct { // Signature is used to prove the ownership of node id. - Signature *btcec.Signature + Signature Sig // Features is the list of protocol features this node supports. Features *RawFeatureVector diff --git a/lnwire/onion_error_test.go b/lnwire/onion_error_test.go index 7817f677..6d68991b 100644 --- a/lnwire/onion_error_test.go +++ b/lnwire/onion_error_test.go @@ -11,8 +11,9 @@ var ( testAmount = MilliSatoshi(1) testCtlvExpiry = uint32(2) testFlags = uint16(2) + sig, _ = NewSigFromSignature(testSig) testChannelUpdate = ChannelUpdate{ - Signature: testSig, + Signature: sig, ShortChannelID: NewShortChanIDFromInt(1), Timestamp: 1, Flags: 1, diff --git a/lnwire/signature_test.go b/lnwire/signature_test.go index 0b5b3775..2e27e135 100644 --- a/lnwire/signature_test.go +++ b/lnwire/signature_test.go @@ -14,16 +14,16 @@ func TestSignatureSerializeDeserialize(t *testing.T) { // Local-scoped closure to serialize and deserialize a Signature and // check for errors as well as check if the results are correct. signatureSerializeDeserialize := func(e btcec.Signature) error { - var b [64]byte - err := SerializeSigToWire(&b, &e) + sig, err := NewSigFromSignature(&e) if err != nil { return err } - var e2 *btcec.Signature - err = DeserializeSigFromWire(&e2, b) + + e2, err := sig.ToSignature() if err != nil { return err } + if e.R.Cmp(e2.R) != 0 { return fmt.Errorf("Pre/post-serialize Rs don't match"+ ": %s, %s", e.R, e2.R) From aa2e91f7c498370ca3080e525315157c334fec8a Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2018 19:53:49 -0800 Subject: [PATCH 03/16] lnwire: replace instances of *btcec.PublicKey with [33]byte in ann messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit, we replace all instances of *btcec.PublicKey within the announcement messages with a simple [33]byte. We do this as usually we don’t need to immediately validate an announcement, therefore we can avoid the scalar multiplications during decoding. --- lnwire/channel_announcement.go | 8 +-- lnwire/lnwire.go | 8 +++ lnwire/lnwire_test.go | 124 ++++++++++++++++++++++++++------- lnwire/node_announcement.go | 4 +- 4 files changed, 113 insertions(+), 31 deletions(-) diff --git a/lnwire/channel_announcement.go b/lnwire/channel_announcement.go index ed776ca7..93eaa990 100644 --- a/lnwire/channel_announcement.go +++ b/lnwire/channel_announcement.go @@ -41,13 +41,13 @@ type ChannelAnnouncement struct { // The public keys of the two nodes who are operating the channel, such // that is NodeID1 the numerically-lesser than NodeID2 (ascending // numerical order). - NodeID1 *btcec.PublicKey - NodeID2 *btcec.PublicKey + NodeID1 [33]byte + NodeID2 [33]byte // Public keys which corresponds to the keys which was declared in // multisig funding transaction output. - BitcoinKey1 *btcec.PublicKey - BitcoinKey2 *btcec.PublicKey + BitcoinKey1 [33]byte + BitcoinKey2 [33]byte } // A compile time check to ensure ChannelAnnouncement implements the diff --git a/lnwire/lnwire.go b/lnwire/lnwire.go index 5e486582..10c565ae 100644 --- a/lnwire/lnwire.go +++ b/lnwire/lnwire.go @@ -201,6 +201,10 @@ func writeElement(w io.Writer, element interface{}) error { return err } + if _, err := w.Write(e[:]); err != nil { + return err + } + case [33]byte: if _, err := w.Write(e[:]); err != nil { return err } @@ -527,6 +531,10 @@ func readElement(r io.Reader, element interface{}) error { if _, err := io.ReadFull(r, *e); err != nil { return err } + case *[33]byte: + if _, err := io.ReadFull(r, e[:]); err != nil { + return err + } case []byte: if _, err := io.ReadFull(r, e); err != nil { return err diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index fb3677bf..e8cd720c 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -52,6 +52,19 @@ func randPubKey() (*btcec.PublicKey, error) { return priv.PubKey(), nil } +func randRawKey() ([33]byte, error) { + var n [33]byte + + priv, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + return n, err + } + + copy(n[:], priv.PubKey().SerializeCompressed()) + + return n, nil +} + func randRawFeatureVector(r *rand.Rand) *RawFeatureVector { featureVec := NewRawFeatureVector() for i := 0; i < 10000; i++ { @@ -266,20 +279,30 @@ func TestLightningWireProtocol(t *testing.T) { } req.FundingPoint.Index = uint32(r.Int31()) % math.MaxUint16 - req.CommitSig = testSig + var err error + req.CommitSig, err = NewSigFromSignature(testSig) + if err != nil { + t.Fatalf("unable to parse sig: %v", err) + return + } v[0] = reflect.ValueOf(req) }, MsgFundingSigned: func(v []reflect.Value, r *rand.Rand) { var c [32]byte - if _, err := r.Read(c[:]); err != nil { + _, err := r.Read(c[:]) + if err != nil { t.Fatalf("unable to generate chan id: %v", err) return } req := FundingSigned{ - ChanID: ChannelID(c), - CommitSig: testSig, + ChanID: ChannelID(c), + } + req.CommitSig, err = NewSigFromSignature(testSig) + if err != nil { + t.Fatalf("unable to parse sig: %v", err) + return } v[0] = reflect.ValueOf(req) @@ -305,7 +328,12 @@ func TestLightningWireProtocol(t *testing.T) { MsgClosingSigned: func(v []reflect.Value, r *rand.Rand) { req := ClosingSigned{ FeeSatoshis: btcutil.Amount(r.Int63()), - Signature: testSig, + } + var err error + req.Signature, err = NewSigFromSignature(testSig) + if err != nil { + t.Fatalf("unable to parse sig: %v", err) + return } if _, err := r.Read(req.ChannelID[:]); err != nil { @@ -321,17 +349,27 @@ func TestLightningWireProtocol(t *testing.T) { t.Fatalf("unable to generate chan id: %v", err) return } - req.CommitSig = testSig + + var err error + req.CommitSig, err = NewSigFromSignature(testSig) + if err != nil { + t.Fatalf("unable to parse sig: %v", err) + return + } // Only create the slice if there will be any signatures // in it to prevent false positive test failures due to // an empty slice versus a nil slice. numSigs := uint16(r.Int31n(1020)) if numSigs > 0 { - req.HtlcSigs = make([]*btcec.Signature, numSigs) + req.HtlcSigs = make([]Sig, numSigs) } for i := 0; i < int(numSigs); i++ { - req.HtlcSigs[i] = testSig + req.HtlcSigs[i], err = NewSigFromSignature(testSig) + if err != nil { + t.Fatalf("unable to parse sig: %v", err) + return + } } v[0] = reflect.ValueOf(*req) @@ -356,32 +394,48 @@ func TestLightningWireProtocol(t *testing.T) { v[0] = reflect.ValueOf(*req) }, MsgChannelAnnouncement: func(v []reflect.Value, r *rand.Rand) { + var err error req := ChannelAnnouncement{ ShortChannelID: NewShortChanIDFromInt(uint64(r.Int63())), Features: randRawFeatureVector(r), } - req.NodeSig1 = testSig - req.NodeSig2 = testSig - req.BitcoinSig1 = testSig - req.BitcoinSig2 = testSig + req.NodeSig1, err = NewSigFromSignature(testSig) + if err != nil { + t.Fatalf("unable to parse sig: %v", err) + return + } + req.NodeSig2, err = NewSigFromSignature(testSig) + if err != nil { + t.Fatalf("unable to parse sig: %v", err) + return + } + req.BitcoinSig1, err = NewSigFromSignature(testSig) + if err != nil { + t.Fatalf("unable to parse sig: %v", err) + return + } + req.BitcoinSig2, err = NewSigFromSignature(testSig) + if err != nil { + t.Fatalf("unable to parse sig: %v", err) + return + } - var err error - req.NodeID1, err = randPubKey() + req.NodeID1, err = randRawKey() if err != nil { t.Fatalf("unable to generate key: %v", err) return } - req.NodeID2, err = randPubKey() + req.NodeID2, err = randRawKey() if err != nil { t.Fatalf("unable to generate key: %v", err) return } - req.BitcoinKey1, err = randPubKey() + req.BitcoinKey1, err = randRawKey() if err != nil { t.Fatalf("unable to generate key: %v", err) return } - req.BitcoinKey2, err = randPubKey() + req.BitcoinKey2, err = randRawKey() if err != nil { t.Fatalf("unable to generate key: %v", err) return @@ -400,8 +454,8 @@ func TestLightningWireProtocol(t *testing.T) { return } + var err error req := NodeAnnouncement{ - Signature: testSig, Features: randRawFeatureVector(r), Timestamp: uint32(r.Int31()), Alias: a, @@ -413,9 +467,13 @@ func TestLightningWireProtocol(t *testing.T) { // TODO(roasbeef): proper gen rand addrs Addresses: testAddrs, } + req.Signature, err = NewSigFromSignature(testSig) + if err != nil { + t.Fatalf("unable to parse sig: %v", err) + return + } - var err error - req.NodeID, err = randPubKey() + req.NodeID, err = randRawKey() if err != nil { t.Fatalf("unable to generate key: %v", err) return @@ -424,8 +482,8 @@ func TestLightningWireProtocol(t *testing.T) { v[0] = reflect.ValueOf(req) }, MsgChannelUpdate: func(v []reflect.Value, r *rand.Rand) { + var err error req := ChannelUpdate{ - Signature: testSig, ShortChannelID: NewShortChanIDFromInt(uint64(r.Int63())), Timestamp: uint32(r.Int31()), Flags: ChanUpdateFlag(r.Int31()), @@ -434,6 +492,12 @@ func TestLightningWireProtocol(t *testing.T) { BaseFee: uint32(r.Int31()), FeeRate: uint32(r.Int31()), } + req.Signature, err = NewSigFromSignature(testSig) + if err != nil { + t.Fatalf("unable to parse sig: %v", err) + return + } + if _, err := r.Read(req.ChainHash[:]); err != nil { t.Fatalf("unable to generate chain hash: %v", err) return @@ -442,11 +506,23 @@ func TestLightningWireProtocol(t *testing.T) { v[0] = reflect.ValueOf(req) }, MsgAnnounceSignatures: func(v []reflect.Value, r *rand.Rand) { + var err error req := AnnounceSignatures{ - ShortChannelID: NewShortChanIDFromInt(uint64(r.Int63())), - NodeSignature: testSig, - BitcoinSignature: testSig, + ShortChannelID: NewShortChanIDFromInt(uint64(r.Int63())), } + + req.NodeSignature, err = NewSigFromSignature(testSig) + if err != nil { + t.Fatalf("unable to parse sig: %v", err) + return + } + + req.BitcoinSignature, err = NewSigFromSignature(testSig) + if err != nil { + t.Fatalf("unable to parse sig: %v", err) + return + } + if _, err := r.Read(req.ChannelID[:]); err != nil { t.Fatalf("unable to generate chan id: %v", err) return diff --git a/lnwire/node_announcement.go b/lnwire/node_announcement.go index 3f6988f7..7a3b1f1d 100644 --- a/lnwire/node_announcement.go +++ b/lnwire/node_announcement.go @@ -7,8 +7,6 @@ import ( "io" "net" "unicode/utf8" - - "github.com/roasbeef/btcd/btcec" ) var ( @@ -59,7 +57,7 @@ type NodeAnnouncement struct { Timestamp uint32 // NodeID is a public key which is used as node identification. - NodeID *btcec.PublicKey + NodeID [33]byte // RGBColor is used to customize their node's appearance in maps and // graphs From 9c483c38b18d2b21658df5ebd75603bcf3a628ac Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2018 19:55:39 -0800 Subject: [PATCH 04/16] lnwallet: update state machine to use new lnwire.Sig everywhere --- lnwallet/channel.go | 87 ++++++++++++++++++++++++++-------------- lnwallet/channel_test.go | 42 +++++++++---------- lnwallet/sigpool.go | 7 ++-- 3 files changed, 80 insertions(+), 56 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index cab7ccd2..36859952 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2557,8 +2557,8 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, // new commitment to the remote party. The commit diff returned contains all // information necessary for retransmission. func (lc *LightningChannel) createCommitDiff( - newCommit *commitment, commitSig *btcec.Signature, - htlcSigs []*btcec.Signature) (*channeldb.CommitDiff, error) { + newCommit *commitment, commitSig lnwire.Sig, + htlcSigs []lnwire.Sig) (*channeldb.CommitDiff, error) { // First, we need to convert the funding outpoint into the ID that's // used on the wire to identify this channel. We'll use this shortly @@ -2673,10 +2673,15 @@ func (lc *LightningChannel) createCommitDiff( // itself, while the second parameter is a slice of all HTLC signatures (if // any). The HTLC signatures are sorted according to the BIP 69 order of the // HTLC's on the commitment transaction. -func (lc *LightningChannel) SignNextCommitment() (*btcec.Signature, []*btcec.Signature, error) { +func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, error) { lc.Lock() defer lc.Unlock() + var ( + sig lnwire.Sig + htlcSigs []lnwire.Sig + ) + // If we're awaiting for an ACK to a commitment signature, or if we // don't yet have the initial next revocation point of the remote // party, then we're unable to create new states. Each time we create a @@ -2684,7 +2689,7 @@ func (lc *LightningChannel) SignNextCommitment() (*btcec.Signature, []*btcec.Sig commitPoint := lc.channelState.RemoteNextRevocation if lc.remoteCommitChain.hasUnackedCommitment() || commitPoint == nil { - return nil, nil, ErrNoWindow + return sig, htlcSigs, ErrNoWindow } // Determine the last update on the remote log that has been locked in. @@ -2698,7 +2703,7 @@ func (lc *LightningChannel) SignNextCommitment() (*btcec.Signature, []*btcec.Sig err := lc.validateCommitmentSanity(remoteACKedIndex, lc.localUpdateLog.logIndex, false, true, true) if err != nil { - return nil, nil, err + return sig, htlcSigs, err } // Grab the next commitment point for the remote party. This will be @@ -2719,7 +2724,7 @@ func (lc *LightningChannel) SignNextCommitment() (*btcec.Signature, []*btcec.Sig remoteACKedIndex, remoteHtlcIndex, keyRing, ) if err != nil { - return nil, nil, err + return sig, htlcSigs, err } walletLog.Tracef("ChannelPoint(%v): extending remote chain to height %v, "+ @@ -2744,7 +2749,7 @@ func (lc *LightningChannel) SignNextCommitment() (*btcec.Signature, []*btcec.Sig lc.localChanCfg, lc.remoteChanCfg, newCommitView, ) if err != nil { - return nil, nil, err + return sig, htlcSigs, err } lc.sigPool.SubmitSignBatch(sigBatch) @@ -2755,12 +2760,12 @@ func (lc *LightningChannel) SignNextCommitment() (*btcec.Signature, []*btcec.Sig rawSig, err := lc.signer.SignOutputRaw(newCommitView.txn, lc.signDesc) if err != nil { close(cancelChan) - return nil, nil, err + return sig, htlcSigs, err } - sig, err := btcec.ParseSignature(rawSig, btcec.S256()) + sig, err = lnwire.NewSigFromRawSignature(rawSig) if err != nil { close(cancelChan) - return nil, nil, err + return sig, htlcSigs, err } // We'll need to send over the signatures to the remote party in the @@ -2772,7 +2777,7 @@ func (lc *LightningChannel) SignNextCommitment() (*btcec.Signature, []*btcec.Sig // With the jobs sorted, we'll now iterate through all the responses to // gather each of the signatures in order. - htlcSigs := make([]*btcec.Signature, 0, len(sigBatch)) + htlcSigs = make([]lnwire.Sig, 0, len(sigBatch)) for _, htlcSigJob := range sigBatch { select { case jobResp := <-htlcSigJob.resp: @@ -2780,12 +2785,12 @@ func (lc *LightningChannel) SignNextCommitment() (*btcec.Signature, []*btcec.Sig // active jobs. if jobResp.err != nil { close(cancelChan) - return nil, nil, err + return sig, htlcSigs, err } htlcSigs = append(htlcSigs, jobResp.sig) case <-lc.quit: - return nil, nil, fmt.Errorf("channel shutting down") + return sig, htlcSigs, fmt.Errorf("channel shutting down") } } @@ -2794,10 +2799,10 @@ func (lc *LightningChannel) SignNextCommitment() (*btcec.Signature, []*btcec.Sig // can retransmit it if necessary. commitDiff, err := lc.createCommitDiff(newCommitView, sig, htlcSigs) if err != nil { - return nil, nil, err + return sig, htlcSigs, err } if lc.channelState.AppendRemoteCommitChain(commitDiff); err != nil { - return nil, nil, err + return sig, htlcSigs, err } // TODO(roasbeef): check that one eclair bug @@ -3128,13 +3133,13 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, // commitment state. The jobs generated are fully populated, and can be sent // directly into the pool of workers. func genHtlcSigValidationJobs(localCommitmentView *commitment, - keyRing *CommitmentKeyRing, htlcSigs []*btcec.Signature, - localChanCfg, remoteChanCfg *channeldb.ChannelConfig) []verifyJob { + keyRing *CommitmentKeyRing, htlcSigs []lnwire.Sig, + localChanCfg, remoteChanCfg *channeldb.ChannelConfig) ([]verifyJob, error) { // If this new commitment state doesn't have any HTLC's that are to be // signed, then we'll return a nil slice. if len(htlcSigs) == 0 { - return nil + return nil, nil } txHash := localCommitmentView.txn.TxHash() @@ -3154,7 +3159,11 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment, // to validate each signature within the worker pool. i := 0 for index := range localCommitmentView.txn.TxOut { - var sigHash func() ([]byte, error) + var ( + sigHash func() ([]byte, error) + sig *btcec.Signature + err error + ) outputIndex := int32(index) switch { @@ -3197,7 +3206,11 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment, // With the sighash generated, we'll also store the // signature so it can be written to disk if this state // is valid. - htlc.sig = htlcSigs[i] + sig, err = htlcSigs[i].ToSignature() + if err != nil { + return nil, err + } + htlc.sig = sig // Otherwise, if this is an outgoing HTLC, then we'll need to // generate a timeout transaction so we can verify the @@ -3239,7 +3252,11 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment, // With the sighash generated, we'll also store the // signature so it can be written to disk if this state // is valid. - htlc.sig = htlcSigs[i] + sig, err = htlcSigs[i].ToSignature() + if err != nil { + return nil, err + } + htlc.sig = sig default: continue @@ -3247,14 +3264,14 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment, verifyJobs = append(verifyJobs, verifyJob{ pubKey: keyRing.RemoteHtlcKey, - sig: htlcSigs[i], + sig: sig, sigHash: sigHash, }) i++ } - return verifyJobs + return verifyJobs, nil } // InvalidCommitSigError is a struct that implements the error interface to @@ -3292,8 +3309,8 @@ var _ error = (*InvalidCommitSigError)(nil) // to our local commitment chain. Once we send a revocation for our prior // state, then this newly added commitment becomes our current accepted channel // state. -func (lc *LightningChannel) ReceiveNewCommitment(commitSig *btcec.Signature, - htlcSigs []*btcec.Signature) error { +func (lc *LightningChannel) ReceiveNewCommitment(commitSig lnwire.Sig, + htlcSigs []lnwire.Sig) error { lc.Lock() defer lc.Unlock() @@ -3366,8 +3383,14 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig *btcec.Signature, // As an optimization, we'll generate a series of jobs for the worker // pool to verify each of the HTLc signatures presented. Once // generated, we'll submit these jobs to the worker pool. - verifyJobs := genHtlcSigValidationJobs(localCommitmentView, - keyRing, htlcSigs, lc.localChanCfg, lc.remoteChanCfg) + verifyJobs, err := genHtlcSigValidationJobs( + localCommitmentView, keyRing, htlcSigs, lc.localChanCfg, + lc.remoteChanCfg, + ) + if err != nil { + return err + } + cancelChan := make(chan struct{}) verifyResps := lc.sigPool.SubmitVerifyBatch(verifyJobs, cancelChan) @@ -3379,7 +3402,11 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig *btcec.Signature, Y: lc.remoteChanCfg.MultiSigKey.Y, Curve: btcec.S256(), } - if !commitSig.Verify(sigHash, &verifyKey) { + cSig, err := commitSig.ToSignature() + if err != nil { + return err + } + if !cSig.Verify(sigHash, &verifyKey) { close(cancelChan) // If we fail to validate their commitment signature, we'll @@ -3390,7 +3417,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig *btcec.Signature, localCommitTx.Serialize(&txBytes) return &InvalidCommitSigError{ commitHeight: nextHeight, - commitSig: commitSig.Serialize(), + commitSig: commitSig.ToSignatureBytes(), sigHash: sigHash, commitTx: txBytes.Bytes(), } @@ -3414,7 +3441,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig *btcec.Signature, // The signature checks out, so we can now add the new commitment to // our local commitment chain. - localCommitmentView.sig = commitSig.Serialize() + localCommitmentView.sig = commitSig.ToSignatureBytes() lc.localCommitChain.addCommitment(localCommitmentView) // If we are not channel initiator, then the commitment just received diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index cf37de6e..be8407ee 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -4,7 +4,6 @@ import ( "bytes" "crypto/sha256" "io/ioutil" - "math/big" "math/rand" "os" "reflect" @@ -2768,20 +2767,20 @@ func TestChanSyncOweCommitment(t *testing.T) { t.Fatalf("expected a CommitSig message, instead have %v", spew.Sdump(aliceMsgsToSend[4])) } - if !commitSigMsg.CommitSig.IsEqual(aliceSig) { + if commitSigMsg.CommitSig != aliceSig { t.Fatalf("commit sig msgs don't match: expected %x got %x", - aliceSig.Serialize(), commitSigMsg.CommitSig.Serialize()) + aliceSig, commitSigMsg.CommitSig) } if len(commitSigMsg.HtlcSigs) != len(aliceHtlcSigs) { t.Fatalf("wrong number of htlc sigs: expected %v, got %v", len(aliceHtlcSigs), len(commitSigMsg.HtlcSigs)) } for i, htlcSig := range commitSigMsg.HtlcSigs { - if !htlcSig.IsEqual(aliceHtlcSigs[i]) { + if htlcSig != aliceHtlcSigs[i] { t.Fatalf("htlc sig msgs don't match: "+ "expected %x got %x", - aliceHtlcSigs[i].Serialize(), - htlcSig.Serialize()) + aliceHtlcSigs[i], + htlcSig) } } } @@ -3228,20 +3227,19 @@ func TestChanSyncOweRevocationAndCommit(t *testing.T) { t.Fatalf("expected bob to re-send commit sig, instead sending: %v", spew.Sdump(bobMsgsToSend[1])) } - if !bobReCommitSigMsg.CommitSig.IsEqual(bobSig) { + if bobReCommitSigMsg.CommitSig != bobSig { t.Fatalf("commit sig msgs don't match: expected %x got %x", - bobSig.Serialize(), bobReCommitSigMsg.CommitSig.Serialize()) + bobSig, bobReCommitSigMsg.CommitSig) } if len(bobReCommitSigMsg.HtlcSigs) != len(bobHtlcSigs) { t.Fatalf("wrong number of htlc sigs: expected %v, got %v", len(bobHtlcSigs), len(bobReCommitSigMsg.HtlcSigs)) } for i, htlcSig := range bobReCommitSigMsg.HtlcSigs { - if !htlcSig.IsEqual(aliceHtlcSigs[i]) { + if htlcSig != aliceHtlcSigs[i] { t.Fatalf("htlc sig msgs don't match: "+ "expected %x got %x", - bobHtlcSigs[i].Serialize(), - htlcSig.Serialize()) + bobHtlcSigs[i], htlcSig) } } } @@ -3426,21 +3424,20 @@ func TestChanSyncOweRevocationAndCommitForceTransition(t *testing.T) { t.Fatalf("revocation msgs don't match: expected %v, got %v", bobRevocation, bobReRevoke) } - if !bobReCommitSigMsg.CommitSig.IsEqual(bobSigMsg.CommitSig) { + if bobReCommitSigMsg.CommitSig != bobSigMsg.CommitSig { t.Fatalf("commit sig msgs don't match: expected %x got %x", - bobSigMsg.CommitSig.Serialize(), - bobReCommitSigMsg.CommitSig.Serialize()) + bobSigMsg.CommitSig, + bobReCommitSigMsg.CommitSig) } if len(bobReCommitSigMsg.HtlcSigs) != len(bobSigMsg.HtlcSigs) { t.Fatalf("wrong number of htlc sigs: expected %v, got %v", len(bobSigMsg.HtlcSigs), len(bobReCommitSigMsg.HtlcSigs)) } for i, htlcSig := range bobReCommitSigMsg.HtlcSigs { - if htlcSig.IsEqual(bobSigMsg.HtlcSigs[i]) { + if htlcSig != bobSigMsg.HtlcSigs[i] { t.Fatalf("htlc sig msgs don't match: "+ "expected %x got %x", - bobSigMsg.HtlcSigs[i].Serialize(), - htlcSig.Serialize()) + bobSigMsg.HtlcSigs[i], htlcSig) } } @@ -3598,20 +3595,19 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { t.Fatalf("expected a CommitSig message, instead have %v", spew.Sdump(aliceMsgsToSend[1])) } - if !commitSigMsg.CommitSig.IsEqual(aliceSig) { + if commitSigMsg.CommitSig != aliceSig { t.Fatalf("commit sig msgs don't match: expected %x got %x", - aliceSig.Serialize(), commitSigMsg.CommitSig.Serialize()) + aliceSig, commitSigMsg.CommitSig) } if len(commitSigMsg.HtlcSigs) != len(aliceHtlcSigs) { t.Fatalf("wrong number of htlc sigs: expected %v, got %v", len(aliceHtlcSigs), len(commitSigMsg.HtlcSigs)) } for i, htlcSig := range commitSigMsg.HtlcSigs { - if !htlcSig.IsEqual(aliceHtlcSigs[i]) { + if htlcSig != aliceHtlcSigs[i] { t.Fatalf("htlc sig msgs don't match: "+ "expected %x got %x", - aliceHtlcSigs[i].Serialize(), - htlcSig.Serialize()) + aliceHtlcSigs[i], htlcSig) } } @@ -4127,7 +4123,7 @@ func TestInvalidCommitSigError(t *testing.T) { // Before the signature gets to Bob, we'll mutate it, such that the // signature is now actually invalid. - aliceSig.R.Add(aliceSig.R, new(big.Int).SetInt64(1)) + aliceSig[0] ^= 88 // Bob should reject this new state, and return the proper error. err = bobChannel.ReceiveNewCommitment(aliceSig, aliceHtlcSigs) diff --git a/lnwallet/sigpool.go b/lnwallet/sigpool.go index 4ee58f5e..a93c9630 100644 --- a/lnwallet/sigpool.go +++ b/lnwallet/sigpool.go @@ -5,6 +5,7 @@ import ( "sync" "sync/atomic" + "github.com/lightningnetwork/lnd/lnwire" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/wire" ) @@ -90,7 +91,7 @@ type signJobResp struct { // sig is the generated signature for a particular signJob In the case // of an error during signature generation, then this value sent will // be nil. - sig *btcec.Signature + sig lnwire.Sig // err is the error that occurred when executing the specified // signature job. In the case that no error occurred, this value will @@ -185,7 +186,7 @@ func (s *sigPool) poolWorker() { if err != nil { select { case sigMsg.resp <- signJobResp{ - sig: nil, + sig: lnwire.Sig{}, err: err, }: continue @@ -196,7 +197,7 @@ func (s *sigPool) poolWorker() { } } - sig, err := btcec.ParseSignature(rawSig, btcec.S256()) + sig, err := lnwire.NewSigFromRawSignature(rawSig) select { case sigMsg.resp <- signJobResp{ sig: sig, From 850abbbeb541e43652169d091473707ca6adab59 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2018 20:11:01 -0800 Subject: [PATCH 05/16] htlcswitch: update tests to respect recent API changes --- htlcswitch/link_test.go | 7 ------- htlcswitch/test_utils.go | 7 ++++--- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index 72623afc..17ee42d4 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -57,13 +57,6 @@ func messageToString(msg lnwire.Message) string { switch m := msg.(type) { case *lnwire.RevokeAndAck: m.NextRevocationKey.Curve = nil - case *lnwire.NodeAnnouncement: - m.NodeID.Curve = nil - case *lnwire.ChannelAnnouncement: - m.NodeID1.Curve = nil - m.NodeID2.Curve = nil - m.BitcoinKey1.Curve = nil - m.BitcoinKey2.Curve = nil case *lnwire.AcceptChannel: m.FundingKey.Curve = nil m.RevocationPoint.Curve = nil diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index 1ce3f554..7595a6d7 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -48,6 +48,7 @@ var ( R: new(big.Int), S: new(big.Int), } + wireSig, _ = lnwire.NewSigFromSignature(testSig) _, _ = testSig.R.SetString("6372440660162918006277497454296753625158993"+ "5445068131219452686511677818569431", 10) @@ -55,11 +56,11 @@ var ( "3135609736119018462340006816851118", 10) ) -// mockGetChanUpdateMessage helper function which returns topology update -// of the channel +// mockGetChanUpdateMessage helper function which returns topology update of +// the channel func mockGetChanUpdateMessage() (*lnwire.ChannelUpdate, error) { return &lnwire.ChannelUpdate{ - Signature: testSig, + Signature: wireSig, }, nil } From 5e9166e478bf7741babbfae28f50821664d33955 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2018 20:19:40 -0800 Subject: [PATCH 06/16] channeldb: use raw pub keys and signatures directly in vertex/edge structs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit, we make an API change that’s meant to reduce the amount of garbage we generate when doing pathfinding or syncing nodes with our latest graph state. Before this commit, we would always have to fully decode the public key and signatures when reading a edge or vertex struct. For the edges, we may need several EC operations to fully decode all the pubkeys. This has been seen to generate a ton of garbage, as well as slow down path finding a good bit. To remedy this, we’ll now only ever read the *raw* bytes from disk. In the event that we actually need to verify a signature (or w/e), only *then* will we fully decode everything. --- channeldb/channel_test.go | 10 +- channeldb/graph.go | 469 ++++++++++++++++++++++++--------- channeldb/graph_test.go | 197 +++++++------- channeldb/waitingproof_test.go | 4 +- 4 files changed, 461 insertions(+), 219 deletions(-) diff --git a/channeldb/channel_test.go b/channeldb/channel_test.go index c4bf7802..b2643a93 100644 --- a/channeldb/channel_test.go +++ b/channeldb/channel_test.go @@ -81,6 +81,8 @@ var ( Index: 0, } privKey, pubKey = btcec.PrivKeyFromBytes(btcec.S256(), key[:]) + + wireSig, _ = lnwire.NewSigFromSignature(testSig) ) // makeTestDB creates a new instance of the ChannelDB for testing purposes. A @@ -428,10 +430,10 @@ func TestChannelStateTransition(t *testing.T) { Commitment: remoteCommit, CommitSig: &lnwire.CommitSig{ ChanID: lnwire.ChannelID(key), - CommitSig: testSig, - HtlcSigs: []*btcec.Signature{ - testSig, - testSig, + CommitSig: wireSig, + HtlcSigs: []lnwire.Sig{ + wireSig, + wireSig, }, }, LogUpdates: []LogUpdate{ diff --git a/channeldb/graph.go b/channeldb/graph.go index 2127ac00..7c518cbe 100644 --- a/channeldb/graph.go +++ b/channeldb/graph.go @@ -309,7 +309,8 @@ func (c *ChannelGraph) SourceNode() (*LightningNode, error) { // node is to be used as the center of a star-graph within path finding // algorithms. func (c *ChannelGraph) SetSourceNode(node *LightningNode) error { - nodePub := node.PubKey.SerializeCompressed() + nodePubBytes := node.PubKeyBytes[:] + return c.db.Update(func(tx *bolt.Tx) error { // First grab the nodes bucket which stores the mapping from // pubKey to node information. @@ -320,7 +321,7 @@ func (c *ChannelGraph) SetSourceNode(node *LightningNode) error { // Next we create the mapping from source to the targeted // public key. - if err := nodes.Put(sourceKey, nodePub); err != nil { + if err := nodes.Put(sourceKey, nodePubBytes); err != nil { return err } @@ -976,13 +977,13 @@ func (c *ChannelGraph) UpdateEdgePolicy(edge *ChannelEdgePolicy) error { // from it. As the graph is directed, a node will also have an incoming edge // attached to it for each outgoing edge. type LightningNode struct { - // PubKey is the node's long-term identity public key. This key will be - // used to authenticated any advertisements/updates sent by the node. - PubKey *btcec.PublicKey + // PubKeyBytes is the raw bytes of the public key of the target node. + PubKeyBytes [33]byte + pubKey *btcec.PublicKey - // HaveNodeAnnouncement indicates whether we received a node announcement - // for this particular node. If true, the remaining fields will be set, - // if false only the PubKey is known for this node. + // HaveNodeAnnouncement indicates whether we received a node + // announcement for this particular node. If true, the remaining fields + // will be set, if false only the PubKey is known for this node. HaveNodeAnnouncement bool // LastUpdate is the last time the vertex information for this node has @@ -999,11 +1000,9 @@ type LightningNode struct { // a node's identity or to serve as a short ID for an address book. Alias string - // AuthSig is a signature under the advertised public key which serves - // to authenticate the attributes announced by this node. - // - // TODO(roasbeef): hook into serialization once full verification is in - AuthSig *btcec.Signature + // AuthSigBytes is the raw signature under the advertised public key + // which serves to authenticate the attributes announced by this node. + AuthSigBytes []byte // Features is the list of protocol features supported by this node. Features *lnwire.FeatureVector @@ -1016,6 +1015,42 @@ type LightningNode struct { // TODO(roasbeef): add update method and fetch? } +// PubKey is the node's long-term identity public key. This key will be used to +// authenticated any advertisements/updates sent by the node. +// +// NOTE: By having this method to access an attribute, we ensure we only need +// to fully deserialize the pubkey if absolutely necessary. +func (c *LightningNode) PubKey() (*btcec.PublicKey, error) { + if c.pubKey != nil { + return c.pubKey, nil + } + + key, err := btcec.ParsePubKey(c.PubKeyBytes[:], btcec.S256()) + if err != nil { + return nil, err + } + c.pubKey = key + c.pubKey.Curve = nil + + return key, nil +} + +// AuthSig is a signature under the advertised public key which serves to +// authenticate the attributes announced by this node. +// +// NOTE: By having this method to access an attribute, we ensure we only need +// to fully deserialize the signature if absolutely necessary. +func (c *LightningNode) AuthSig() (*btcec.Signature, error) { + return btcec.ParseSignature(c.AuthSigBytes, btcec.S256()) +} + +// AddPubKey is a setter-link method that can be used to swap out the public +// key for a node. +func (c *LightningNode) AddPubKey(key *btcec.PublicKey) { + c.pubKey = key + copy(c.PubKeyBytes[:], key.SerializeCompressed()) +} + // FetchLightningNode attempts to look up a target node by its identity public // key. If the node isn't found in the database, then ErrGraphNodeNotFound is // returned. @@ -1062,13 +1097,12 @@ func (c *ChannelGraph) FetchLightningNode(pub *btcec.PublicKey) (*LightningNode, // timestamp of when the data for the node was lasted updated is returned along // with a true boolean. Otherwise, an empty time.Time is returned with a false // boolean. -func (c *ChannelGraph) HasLightningNode(pub *btcec.PublicKey) (time.Time, bool, error) { +func (c *ChannelGraph) HasLightningNode(nodePub [33]byte) (time.Time, bool, error) { var ( updateTime time.Time exists bool ) - nodePub := pub.SerializeCompressed() err := c.db.View(func(tx *bolt.Tx) error { // First grab the nodes bucket which stores the mapping from // pubKey to node information. @@ -1079,7 +1113,7 @@ func (c *ChannelGraph) HasLightningNode(pub *btcec.PublicKey) (time.Time, bool, // If a key for this serialized public key isn't found, we can // exit early. - nodeBytes := nodes.Get(nodePub) + nodeBytes := nodes.Get(nodePub[:]) if nodeBytes == nil { exists = false return nil @@ -1119,7 +1153,11 @@ func (c *ChannelGraph) HasLightningNode(pub *btcec.PublicKey) (time.Time, bool, func (l *LightningNode) ForEachChannel(tx *bolt.Tx, cb func(*bolt.Tx, *ChannelEdgeInfo, *ChannelEdgePolicy, *ChannelEdgePolicy) error) error { - nodePub := l.PubKey.SerializeCompressed() + pub, err := l.PubKey() + if err != nil { + return err + } + nodePub := pub.SerializeCompressed() traversal := func(tx *bolt.Tx) error { nodes := tx.Bucket(nodeBucket) @@ -1172,7 +1210,12 @@ func (l *LightningNode) ForEachChannel(tx *bolt.Tx, // We'll also fetch the incoming edge so this // information can be available to the caller. - incomingNode := toEdgePolicy.Node.PubKey.SerializeCompressed() + incomingNodeKey, err := toEdgePolicy.Node.PubKey() + if err != nil { + return err + } + incomingNode := incomingNodeKey.SerializeCompressed() + fromEdgePolicy, err := fetchChanEdgePolicy( edges, chanID, incomingNode, nodes, ) @@ -1227,29 +1270,21 @@ type ChannelEdgeInfo struct { // * must add chain hash to prefix as well ChainHash chainhash.Hash - // NodeKey1 is the identity public key of the "first" node that was - // involved in the creation of this channel. A node is considered - // "first" if the lexicographical ordering the its serialized public - // key is "smaller" than that of the other node involved in channel - // creation. - NodeKey1 *btcec.PublicKey + // NodeKey1Bytes is the raw public key of the first node. + NodeKey1Bytes [33]byte + nodeKey1 *btcec.PublicKey - // NodeKey2 is the identity public key of the "second" node that was - // involved in the creation of this channel. A node is considered - // "second" if the lexicographical ordering the its serialized public - // key is "larger" than that of the other node involved in channel - // creation. - NodeKey2 *btcec.PublicKey + // NodeKey2Bytes is the raw public key of the first node. + NodeKey2Bytes [33]byte + nodeKey2 *btcec.PublicKey - // BitcoinKey1 is the Bitcoin multi-sig key belonging to the first - // node, that was involved in the funding transaction that originally - // created the channel that this struct represents. - BitcoinKey1 *btcec.PublicKey + // BitcoinKey1Bytes is the raw public key of the first node. + BitcoinKey1Bytes [33]byte + bitcoinKey1 *btcec.PublicKey - // BitcoinKey2 is the Bitcoin multi-sig key belonging to the second - // node, that was involved in the funding transaction that originally - // created the channel that this struct represents. - BitcoinKey2 *btcec.PublicKey + // BitcoinKey2Bytes is the raw public key of the first node. + BitcoinKey2Bytes [33]byte + bitcoinKey2 *btcec.PublicKey // Features is an opaque byte slice that encodes the set of channel // specific features that this channel edge supports. @@ -1269,6 +1304,107 @@ type ChannelEdgeInfo struct { Capacity btcutil.Amount } +// AddNodeKeys is a setter-like method that can be used to replace the set of +// keys for the target ChannelEdgeInfo. +func (c *ChannelEdgeInfo) AddNodeKeys(nodeKey1, nodeKey2, bitcoinKey1, + bitcoinKey2 *btcec.PublicKey) { + + c.nodeKey1 = nodeKey1 + copy(c.NodeKey1Bytes[:], c.nodeKey1.SerializeCompressed()) + + c.nodeKey2 = nodeKey2 + copy(c.NodeKey2Bytes[:], nodeKey2.SerializeCompressed()) + + c.bitcoinKey1 = bitcoinKey1 + copy(c.BitcoinKey1Bytes[:], c.bitcoinKey1.SerializeCompressed()) + + c.bitcoinKey2 = bitcoinKey2 + copy(c.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) +} + +// NodeKey1 is the identity public key of the "first" node that was involved in +// the creation of this channel. A node is considered "first" if the +// lexicographical ordering the its serialized public key is "smaller" than +// that of the other node involved in channel creation. +// +// NOTE: By having this method to access an attribute, we ensure we only need +// to fully deserialize the pubkey if absolutely necessary. +func (c *ChannelEdgeInfo) NodeKey1() (*btcec.PublicKey, error) { + if c.nodeKey1 != nil { + return c.nodeKey1, nil + } + + key, err := btcec.ParsePubKey(c.NodeKey1Bytes[:], btcec.S256()) + if err != nil { + return nil, err + } + c.nodeKey1 = key + + return key, nil +} + +// NodeKey2 is the identity public key of the "second" node that was +// involved in the creation of this channel. A node is considered +// "second" if the lexicographical ordering the its serialized public +// key is "larger" than that of the other node involved in channel +// creation. +// +// NOTE: By having this method to access an attribute, we ensure we only need +// to fully deserialize the pubkey if absolutely necessary. +func (c *ChannelEdgeInfo) NodeKey2() (*btcec.PublicKey, error) { + if c.nodeKey2 != nil { + return c.nodeKey2, nil + } + + key, err := btcec.ParsePubKey(c.NodeKey2Bytes[:], btcec.S256()) + if err != nil { + return nil, err + } + c.nodeKey2 = key + + return key, nil +} + +// BitcoinKey1 is the Bitcoin multi-sig key belonging to the first +// node, that was involved in the funding transaction that originally +// created the channel that this struct represents. +// +// NOTE: By having this method to access an attribute, we ensure we only need +// to fully deserialize the pubkey if absolutely necessary. +func (c *ChannelEdgeInfo) BitcoinKey1() (*btcec.PublicKey, error) { + if c.bitcoinKey1 != nil { + return c.bitcoinKey1, nil + } + + key, err := btcec.ParsePubKey(c.BitcoinKey1Bytes[:], btcec.S256()) + if err != nil { + return nil, err + } + c.bitcoinKey1 = key + + return key, nil +} + +// BitcoinKey2 is the Bitcoin multi-sig key belonging to the second +// node, that was involved in the funding transaction that originally +// created the channel that this struct represents. +// +// NOTE: By having this method to access an attribute, we ensure we only need +// to fully deserialize the pubkey if absolutely necessary. +func (c *ChannelEdgeInfo) BitcoinKey2() (*btcec.PublicKey, error) { + if c.bitcoinKey2 != nil { + return c.bitcoinKey2, nil + } + + key, err := btcec.ParsePubKey(c.BitcoinKey2Bytes[:], btcec.S256()) + if err != nil { + return nil, err + } + c.bitcoinKey2 = key + + return key, nil +} + // ChannelAuthProof is the authentication proof (the signature portion) for a // channel. Using the four signatures contained in the struct, and some // auxillary knowledge (the funding script, node identities, and outpoint) nodes @@ -1277,32 +1413,124 @@ type ChannelEdgeInfo struct { // nodeID1 || nodeID2 || bitcoinKey1|| bitcoinKey2 || 2-byte-feature-len || // features. type ChannelAuthProof struct { - // NodeSig1 is the signature using the identity key of the node that is - // first in a lexicographical ordering of the serialized public keys of - // the two nodes that created the channel. - NodeSig1 *btcec.Signature + // nodeSig1 is a cached instance of the first node signature. + nodeSig1 *btcec.Signature - // NodeSig2 is the signature using the identity key of the node that is - // second in a lexicographical ordering of the serialized public keys - // of the two nodes that created the channel. - NodeSig2 *btcec.Signature + // NodeSig1Bytes are the raw bytes of the first node signature encoded + // in DER format. + NodeSig1Bytes []byte - // BitcoinSig1 is the signature using the public key of the first node - // that was used in the channel's multi-sig output. - BitcoinSig1 *btcec.Signature + // nodeSig2 is a cached instance of the second node signature. + nodeSig2 *btcec.Signature - // BitcoinSig2 is the signature using the public key of the second node - // that was used in the channel's multi-sig output. - BitcoinSig2 *btcec.Signature + // NodeSig2Bytes are the raw bytes of the second node signature + // encoded in DER format. + NodeSig2Bytes []byte + + // bitcoinSig1 is a cached instance of the first bitcoin signature. + bitcoinSig1 *btcec.Signature + + // BitcoinSig1Bytes are the raw bytes of the first bitcoin signature + // encoded in DER format. + BitcoinSig1Bytes []byte + + // bitcoinSig2 is a cached instance of the second bitcoin signature. + bitcoinSig2 *btcec.Signature + + // BitcoinSig2Bytes are the raw bytes of the second bitcoin signature + // encoded in DER format. + BitcoinSig2Bytes []byte +} + +// NodeSig1 is the signature using the identity key of the node that is first +// in a lexicographical ordering of the serialized public keys of the two nodes +// that created the channel. +// +// NOTE: By having this method to access an attribute, we ensure we only need +// to fully deserialize the signature if absolutely necessary. +func (c *ChannelAuthProof) Node1Sig() (*btcec.Signature, error) { + if c.nodeSig1 != nil { + return c.nodeSig1, nil + } + + sig, err := btcec.ParseSignature(c.NodeSig1Bytes, btcec.S256()) + if err != nil { + return nil, err + } + + c.nodeSig1 = sig + + return sig, nil +} + +// NodeSig2 is the signature using the identity key of the node that is second +// in a lexicographical ordering of the serialized public keys of the two nodes +// that created the channel. +// +// NOTE: By having this method to access an attribute, we ensure we only need +// to fully deserialize the signature if absolutely necessary. +func (c *ChannelAuthProof) Node2Sig() (*btcec.Signature, error) { + if c.nodeSig2 != nil { + return c.nodeSig2, nil + } + + sig, err := btcec.ParseSignature(c.NodeSig2Bytes, btcec.S256()) + if err != nil { + return nil, err + } + + c.nodeSig2 = sig + + return sig, nil +} + +// BitcoinSig1 is the signature using the public key of the first node that was +// used in the channel's multi-sig output. +// +// NOTE: By having this method to access an attribute, we ensure we only need +// to fully deserialize the signature if absolutely necessary. +func (c *ChannelAuthProof) BitcoinSig1() (*btcec.Signature, error) { + if c.bitcoinSig1 != nil { + return c.bitcoinSig1, nil + } + + sig, err := btcec.ParseSignature(c.BitcoinSig1Bytes, btcec.S256()) + if err != nil { + return nil, err + } + + c.bitcoinSig1 = sig + + return sig, nil +} + +// BitcoinSig2 is the signature using the public key of the second node that +// was used in the channel's multi-sig output. +// +// NOTE: By having this method to access an attribute, we ensure we only need +// to fully deserialize the signature if absolutely necessary. +func (c *ChannelAuthProof) BitcoinSig2() (*btcec.Signature, error) { + if c.bitcoinSig2 != nil { + return c.bitcoinSig2, nil + } + + sig, err := btcec.ParseSignature(c.BitcoinSig2Bytes, btcec.S256()) + if err != nil { + return nil, err + } + + c.bitcoinSig2 = sig + + return sig, nil } // IsEmpty check is the authentication proof is empty Proof is empty if at // least one of the signatures are equal to nil. func (p *ChannelAuthProof) IsEmpty() bool { - return p.NodeSig1 == nil || - p.NodeSig2 == nil || - p.BitcoinSig1 == nil || - p.BitcoinSig2 == nil + return len(p.NodeSig1Bytes) == 0 || + len(p.NodeSig2Bytes) == 0 || + len(p.BitcoinSig1Bytes) == 0 || + len(p.BitcoinSig2Bytes) == 0 } // ChannelEdgePolicy represents a *directed* edge within the channel graph. For @@ -1311,9 +1539,13 @@ func (p *ChannelAuthProof) IsEmpty() bool { // information concerning fees, and minimum time-lock information which is // utilized during path finding. type ChannelEdgePolicy struct { - // Signature is a channel announcement signature, which is needed for - // proper edge policy announcement. - Signature *btcec.Signature + // SigBytes is the raw bytes of the signature of the channel edge + // policy. We'll only parse these if the caller needs to access the + // signature for validation purposes. + SigBytes []byte + + // sig is a cached fully parsed signature. + sig *btcec.Signature // ChannelID is the unique channel ID for the channel. The first 3 // bytes are the block height, the next 3 the index within the block, @@ -1352,6 +1584,26 @@ type ChannelEdgePolicy struct { db *DB } +// Signature is a channel announcement signature, which is needed for proper +// edge policy announcement. +// +// NOTE: By having this method to access an attribute, we ensure we only need +// to fully deserialize the signature if absolutely necessary. +func (c *ChannelEdgePolicy) Signature() (*btcec.Signature, error) { + if c.sig != nil { + return c.sig, nil + } + + sig, err := btcec.ParseSignature(c.SigBytes, btcec.S256()) + if err != nil { + return nil, err + } + + c.sig = sig + + return sig, nil +} + // FetchChannelEdgesByOutpoint attempts to lookup the two directed edges for // the channel identified by the funding outpoint. If the channel can't be // found, then ErrEdgeNotFound is returned. A struct which houses the general @@ -1539,7 +1791,11 @@ func putLightningNode(nodeBucket *bolt.Bucket, aliasBucket *bolt.Bucket, node *L b bytes.Buffer ) - nodePub := node.PubKey.SerializeCompressed() + pub, err := node.PubKey() + if err != nil { + return err + } + nodePub := pub.SerializeCompressed() // If the node has the update time set, write it, else write 0. updateUnix := uint64(0) @@ -1604,7 +1860,7 @@ func putLightningNode(nodeBucket *bolt.Bucket, aliasBucket *bolt.Bucket, node *L } } - err := wire.WriteVarBytes(&b, 0, node.AuthSig.Serialize()) + err = wire.WriteVarBytes(&b, 0, node.AuthSigBytes) if err != nil { return err } @@ -1630,8 +1886,12 @@ func fetchLightningNode(nodeBucket *bolt.Bucket, } func deserializeLightningNode(r io.Reader) (*LightningNode, error) { + var ( + scratch [8]byte + err error + ) + node := &LightningNode{} - var scratch [8]byte if _, err := r.Read(scratch[:]); err != nil { return nil, err @@ -1640,13 +1900,7 @@ func deserializeLightningNode(r io.Reader) (*LightningNode, error) { unix := int64(byteOrder.Uint64(scratch[:])) node.LastUpdate = time.Unix(unix, 0) - var pub [33]byte - if _, err := r.Read(pub[:]); err != nil { - return nil, err - } - var err error - node.PubKey, err = btcec.ParsePubKey(pub[:], btcec.S256()) - if err != nil { + if _, err := io.ReadFull(r, node.PubKeyBytes[:]); err != nil { return nil, err } @@ -1706,12 +1960,7 @@ func deserializeLightningNode(r io.Reader) (*LightningNode, error) { } node.Addresses = addresses - sigBytes, err := wire.ReadVarBytes(r, 0, 80, "sig") - if err != nil { - return nil, err - } - - node.AuthSig, err = btcec.ParseSignature(sigBytes, btcec.S256()) + node.AuthSigBytes, err = wire.ReadVarBytes(r, 0, 80, "sig") if err != nil { return nil, err } @@ -1722,16 +1971,16 @@ func deserializeLightningNode(r io.Reader) (*LightningNode, error) { func putChanEdgeInfo(edgeIndex *bolt.Bucket, edgeInfo *ChannelEdgeInfo, chanID [8]byte) error { var b bytes.Buffer - if _, err := b.Write(edgeInfo.NodeKey1.SerializeCompressed()); err != nil { + if _, err := b.Write(edgeInfo.NodeKey1Bytes[:]); err != nil { return err } - if _, err := b.Write(edgeInfo.NodeKey2.SerializeCompressed()); err != nil { + if _, err := b.Write(edgeInfo.NodeKey2Bytes[:]); err != nil { return err } - if _, err := b.Write(edgeInfo.BitcoinKey1.SerializeCompressed()); err != nil { + if _, err := b.Write(edgeInfo.BitcoinKey1Bytes[:]); err != nil { return err } - if _, err := b.Write(edgeInfo.BitcoinKey2.SerializeCompressed()); err != nil { + if _, err := b.Write(edgeInfo.BitcoinKey2Bytes[:]); err != nil { return err } @@ -1742,10 +1991,10 @@ func putChanEdgeInfo(edgeIndex *bolt.Bucket, edgeInfo *ChannelEdgeInfo, chanID [ authProof := edgeInfo.AuthProof var nodeSig1, nodeSig2, bitcoinSig1, bitcoinSig2 []byte if authProof != nil { - nodeSig1 = authProof.NodeSig1.Serialize() - nodeSig2 = authProof.NodeSig2.Serialize() - bitcoinSig1 = authProof.BitcoinSig1.Serialize() - bitcoinSig2 = authProof.BitcoinSig2.Serialize() + nodeSig1 = authProof.NodeSig1Bytes + nodeSig2 = authProof.NodeSig2Bytes + bitcoinSig1 = authProof.BitcoinSig1Bytes + bitcoinSig2 = authProof.BitcoinSig2Bytes } if err := wire.WriteVarBytes(&b, 0, nodeSig1); err != nil { @@ -1791,33 +2040,20 @@ func fetchChanEdgeInfo(edgeIndex *bolt.Bucket, func deserializeChanEdgeInfo(r io.Reader) (*ChannelEdgeInfo, error) { var ( - err error - pubKeyBytes [33]byte - edgeInfo = &ChannelEdgeInfo{} + err error + edgeInfo = &ChannelEdgeInfo{} ) - readKey := func() (*btcec.PublicKey, error) { - if _, err := io.ReadFull(r, pubKeyBytes[:]); err != nil { - return nil, err - } - - return btcec.ParsePubKey(pubKeyBytes[:], btcec.S256()) - } - - edgeInfo.NodeKey1, err = readKey() - if err != nil { + if _, err := io.ReadFull(r, edgeInfo.NodeKey1Bytes[:]); err != nil { return nil, err } - edgeInfo.NodeKey2, err = readKey() - if err != nil { + if _, err := io.ReadFull(r, edgeInfo.NodeKey2Bytes[:]); err != nil { return nil, err } - edgeInfo.BitcoinKey1, err = readKey() - if err != nil { + if _, err := io.ReadFull(r, edgeInfo.BitcoinKey1Bytes[:]); err != nil { return nil, err } - edgeInfo.BitcoinKey2, err = readKey() - if err != nil { + if _, err := io.ReadFull(r, edgeInfo.BitcoinKey2Bytes[:]); err != nil { return nil, err } @@ -1828,32 +2064,23 @@ func deserializeChanEdgeInfo(r io.Reader) (*ChannelEdgeInfo, error) { proof := &ChannelAuthProof{} - readSig := func() (*btcec.Signature, error) { - sigBytes, err := wire.ReadVarBytes(r, 0, 80, "sigs") - if err != nil { - return nil, err - } - - if len(sigBytes) != 0 { - return btcec.ParseSignature(sigBytes, btcec.S256()) - } - - return nil, nil + readSig := func() ([]byte, error) { + return wire.ReadVarBytes(r, 0, 80, "sigs") } - proof.NodeSig1, err = readSig() + proof.NodeSig1Bytes, err = readSig() if err != nil { return nil, err } - proof.NodeSig2, err = readSig() + proof.NodeSig2Bytes, err = readSig() if err != nil { return nil, err } - proof.BitcoinSig1, err = readSig() + proof.BitcoinSig1Bytes, err = readSig() if err != nil { return nil, err } - proof.BitcoinSig2, err = readSig() + proof.BitcoinSig2Bytes, err = readSig() if err != nil { return nil, err } @@ -1887,7 +2114,7 @@ func putChanEdgePolicy(edges *bolt.Bucket, edge *ChannelEdgePolicy, from, to []b var b bytes.Buffer - err := wire.WriteVarBytes(&b, 0, edge.Signature.Serialize()) + err := wire.WriteVarBytes(&b, 0, edge.SigBytes) if err != nil { return err } @@ -1993,11 +2220,7 @@ func deserializeChanEdgePolicy(r io.Reader, if err != nil { return nil, err } - - edge.Signature, err = btcec.ParseSignature(sigBytes, btcec.S256()) - if err != nil { - return nil, err - } + edge.SigBytes = sigBytes if err := binary.Read(r, byteOrder, &edge.ChannelID); err != nil { return nil, err diff --git a/channeldb/graph_test.go b/channeldb/graph_test.go index 9281fa4a..c3ac85d1 100644 --- a/channeldb/graph_test.go +++ b/channeldb/graph_test.go @@ -50,17 +50,19 @@ func createTestVertex(db *DB) (*LightningNode, error) { } pub := priv.PubKey().SerializeCompressed() - return &LightningNode{ + n := &LightningNode{ HaveNodeAnnouncement: true, - AuthSig: testSig, + AuthSigBytes: testSig.Serialize(), LastUpdate: time.Unix(updateTime, 0), - PubKey: priv.PubKey(), Color: color.RGBA{1, 2, 3, 0}, Alias: "kek" + string(pub[:]), Features: testFeatures, Addresses: testAddrs, db: db, - }, nil + } + copy(n.PubKeyBytes[:], priv.PubKey().SerializeCompressed()) + + return n, nil } func TestNodeInsertionAndDeletion(t *testing.T) { @@ -79,15 +81,15 @@ func TestNodeInsertionAndDeletion(t *testing.T) { _, testPub := btcec.PrivKeyFromBytes(btcec.S256(), key[:]) node := &LightningNode{ HaveNodeAnnouncement: true, - AuthSig: testSig, + AuthSigBytes: testSig.Serialize(), LastUpdate: time.Unix(1232342, 0), - PubKey: testPub, Color: color.RGBA{1, 2, 3, 0}, Alias: "kek", Features: testFeatures, Addresses: testAddrs, db: db, } + copy(node.PubKeyBytes[:], testPub.SerializeCompressed()) // First, insert the node into the graph DB. This should succeed // without any errors. @@ -102,7 +104,7 @@ func TestNodeInsertionAndDeletion(t *testing.T) { t.Fatalf("unable to locate node: %v", err) } - if _, exists, err := graph.HasLightningNode(testPub); err != nil { + if _, exists, err := graph.HasLightningNode(dbNode.PubKeyBytes); err != nil { t.Fatalf("unable to query for node: %v", err) } else if !exists { t.Fatalf("node should be found but wasn't") @@ -144,9 +146,9 @@ func TestPartialNode(t *testing.T) { // PubKey set. _, testPub := btcec.PrivKeyFromBytes(btcec.S256(), key[:]) node := &LightningNode{ - PubKey: testPub, HaveNodeAnnouncement: false, } + copy(node.PubKeyBytes[:], testPub.SerializeCompressed()) if err := graph.AddLightningNode(node); err != nil { t.Fatalf("unable to add node: %v", err) @@ -159,7 +161,7 @@ func TestPartialNode(t *testing.T) { t.Fatalf("unable to locate node: %v", err) } - if _, exists, err := graph.HasLightningNode(testPub); err != nil { + if _, exists, err := graph.HasLightningNode(dbNode.PubKeyBytes); err != nil { t.Fatalf("unable to query for node: %v", err) } else if !exists { t.Fatalf("node should be found but wasn't") @@ -168,11 +170,11 @@ func TestPartialNode(t *testing.T) { // The two nodes should match exactly! (with default values for // LastUpdate and db set to satisfy compareNodes()) node = &LightningNode{ - PubKey: testPub, HaveNodeAnnouncement: false, LastUpdate: time.Unix(0, 0), db: db, } + copy(node.PubKeyBytes[:], testPub.SerializeCompressed()) if err := compareNodes(node, dbNode); err != nil { t.Fatalf("nodes don't match: %v", err) @@ -181,7 +183,7 @@ func TestPartialNode(t *testing.T) { // Next, delete the node from the graph, this should purge all data // related to the node. if err := graph.DeleteLightningNode(testPub); err != nil { - t.Fatalf("unable to delete node; %v", err) + t.Fatalf("unable to delete node: %v", err) } // Finally, attempt to fetch the node again. This should fail as the @@ -218,7 +220,11 @@ func TestAliasLookup(t *testing.T) { // Next, attempt to lookup the alias. The alias should exactly match // the one which the test node was assigned. - dbAlias, err := graph.LookupAlias(testNode.PubKey) + nodePub, err := testNode.PubKey() + if err != nil { + t.Fatalf("unable to generate pubkey: %v", err) + } + dbAlias, err := graph.LookupAlias(nodePub) if err != nil { t.Fatalf("unable to find alias: %v", err) } @@ -232,7 +238,11 @@ func TestAliasLookup(t *testing.T) { if err != nil { t.Fatalf("unable to create test node: %v", err) } - _, err = graph.LookupAlias(node.PubKey) + nodePub, err = node.PubKey() + if err != nil { + t.Fatalf("unable to generate pubkey: %v", err) + } + _, err = graph.LookupAlias(nodePub) if err != ErrNodeAliasNotFound { t.Fatalf("alias lookup should fail for non-existent pubkey") } @@ -311,22 +321,30 @@ func TestEdgeInsertionDeletion(t *testing.T) { // Add the new edge to the database, this should proceed without any // errors. + node1Pub, err := node1.PubKey() + if err != nil { + t.Fatalf("unable to generate node key: %v", err) + } + node2Pub, err := node2.PubKey() + if err != nil { + t.Fatalf("unable to generate node key: %v", err) + } edgeInfo := ChannelEdgeInfo{ - ChannelID: chanID, - ChainHash: key, - NodeKey1: node1.PubKey, - NodeKey2: node2.PubKey, - BitcoinKey1: node1.PubKey, - BitcoinKey2: node2.PubKey, + ChannelID: chanID, + ChainHash: key, AuthProof: &ChannelAuthProof{ - NodeSig1: testSig, - NodeSig2: testSig, - BitcoinSig1: testSig, - BitcoinSig2: testSig, + NodeSig1Bytes: testSig.Serialize(), + NodeSig2Bytes: testSig.Serialize(), + BitcoinSig1Bytes: testSig.Serialize(), + BitcoinSig2Bytes: testSig.Serialize(), }, ChannelPoint: outpoint, Capacity: 9000, } + copy(edgeInfo.NodeKey1Bytes[:], node1Pub.SerializeCompressed()) + copy(edgeInfo.NodeKey2Bytes[:], node2Pub.SerializeCompressed()) + copy(edgeInfo.BitcoinKey1Bytes[:], node1Pub.SerializeCompressed()) + copy(edgeInfo.BitcoinKey2Bytes[:], node2Pub.SerializeCompressed()) if err := graph.AddChannelEdge(&edgeInfo); err != nil { t.Fatalf("unable to create channel edge: %v", err) @@ -413,22 +431,26 @@ func TestDisconnectBlockAtHeight(t *testing.T) { Index: outPointIndex, } + node1Pub, _ := node1.PubKey() + node2Pub, _ := node2.PubKey() edgeInfo := ChannelEdgeInfo{ - ChannelID: shortChanID.ToUint64(), - ChainHash: key, - NodeKey1: node1.PubKey, - NodeKey2: node2.PubKey, - BitcoinKey1: node1.PubKey, - BitcoinKey2: node2.PubKey, + ChannelID: shortChanID.ToUint64(), + ChainHash: key, AuthProof: &ChannelAuthProof{ - NodeSig1: testSig, - NodeSig2: testSig, - BitcoinSig1: testSig, - BitcoinSig2: testSig, + NodeSig1Bytes: testSig.Serialize(), + NodeSig2Bytes: testSig.Serialize(), + BitcoinSig1Bytes: testSig.Serialize(), + BitcoinSig2Bytes: testSig.Serialize(), }, ChannelPoint: outpoint, Capacity: 9000, } + + copy(edgeInfo.NodeKey1Bytes[:], node1Pub.SerializeCompressed()) + copy(edgeInfo.NodeKey2Bytes[:], node2Pub.SerializeCompressed()) + copy(edgeInfo.BitcoinKey1Bytes[:], node1Pub.SerializeCompressed()) + copy(edgeInfo.BitcoinKey2Bytes[:], node2Pub.SerializeCompressed()) + return edgeInfo } @@ -530,16 +552,16 @@ func assertEdgeInfoEqual(t *testing.T, e1 *ChannelEdgeInfo, e2.ChainHash) } - if !e1.NodeKey1.IsEqual(e2.NodeKey1) { + if !bytes.Equal(e1.NodeKey1Bytes[:], e2.NodeKey1Bytes[:]) { t.Fatalf("nodekey1 doesn't match") } - if !e1.NodeKey2.IsEqual(e2.NodeKey2) { + if !bytes.Equal(e1.NodeKey2Bytes[:], e2.NodeKey2Bytes[:]) { t.Fatalf("nodekey2 doesn't match") } - if !e1.BitcoinKey1.IsEqual(e2.BitcoinKey1) { + if !bytes.Equal(e1.BitcoinKey1Bytes[:], e2.BitcoinKey1Bytes[:]) { t.Fatalf("bitcoinkey1 doesn't match") } - if !e1.BitcoinKey2.IsEqual(e2.BitcoinKey2) { + if !bytes.Equal(e1.BitcoinKey2Bytes[:], e2.BitcoinKey2Bytes[:]) { t.Fatalf("bitcoinkey2 doesn't match") } @@ -548,18 +570,18 @@ func assertEdgeInfoEqual(t *testing.T, e1 *ChannelEdgeInfo, e2.Features) } - if !e1.AuthProof.NodeSig1.IsEqual(e2.AuthProof.NodeSig1) { + if !bytes.Equal(e1.AuthProof.NodeSig1Bytes, e2.AuthProof.NodeSig1Bytes) { t.Fatalf("nodesig1 doesn't match: %v vs %v", - spew.Sdump(e1.AuthProof.NodeSig1), - spew.Sdump(e2.AuthProof.NodeSig1)) + spew.Sdump(e1.AuthProof.NodeSig1Bytes), + spew.Sdump(e2.AuthProof.NodeSig1Bytes)) } - if !e1.AuthProof.NodeSig2.IsEqual(e2.AuthProof.NodeSig2) { + if !bytes.Equal(e1.AuthProof.NodeSig2Bytes, e2.AuthProof.NodeSig2Bytes) { t.Fatalf("nodesig2 doesn't match") } - if !e1.AuthProof.BitcoinSig1.IsEqual(e2.AuthProof.BitcoinSig1) { + if !bytes.Equal(e1.AuthProof.BitcoinSig1Bytes, e2.AuthProof.BitcoinSig1Bytes) { t.Fatalf("bitcoinsig1 doesn't match") } - if !e1.AuthProof.BitcoinSig2.IsEqual(e2.AuthProof.BitcoinSig2) { + if !bytes.Equal(e1.AuthProof.BitcoinSig2Bytes, e2.AuthProof.BitcoinSig2Bytes) { t.Fatalf("bitcoinsig2 doesn't match") } @@ -606,9 +628,7 @@ func TestEdgeInfoUpdates(t *testing.T) { firstNode *LightningNode secondNode *LightningNode ) - node1Bytes := node1.PubKey.SerializeCompressed() - node2Bytes := node2.PubKey.SerializeCompressed() - if bytes.Compare(node1Bytes, node2Bytes) == -1 { + if bytes.Compare(node1.PubKeyBytes[:], node2.PubKeyBytes[:]) == -1 { firstNode = node1 secondNode = node2 } else { @@ -627,21 +647,21 @@ func TestEdgeInfoUpdates(t *testing.T) { // Add the new edge to the database, this should proceed without any // errors. edgeInfo := &ChannelEdgeInfo{ - ChannelID: chanID, - ChainHash: key, - NodeKey1: firstNode.PubKey, - NodeKey2: secondNode.PubKey, - BitcoinKey1: firstNode.PubKey, - BitcoinKey2: secondNode.PubKey, + ChannelID: chanID, + ChainHash: key, AuthProof: &ChannelAuthProof{ - NodeSig1: testSig, - NodeSig2: testSig, - BitcoinSig1: testSig, - BitcoinSig2: testSig, + NodeSig1Bytes: testSig.Serialize(), + NodeSig2Bytes: testSig.Serialize(), + BitcoinSig1Bytes: testSig.Serialize(), + BitcoinSig2Bytes: testSig.Serialize(), }, ChannelPoint: outpoint, Capacity: 1000, } + copy(edgeInfo.NodeKey1Bytes[:], firstNode.PubKeyBytes[:]) + copy(edgeInfo.NodeKey2Bytes[:], secondNode.PubKeyBytes[:]) + copy(edgeInfo.BitcoinKey1Bytes[:], firstNode.PubKeyBytes[:]) + copy(edgeInfo.BitcoinKey2Bytes[:], secondNode.PubKeyBytes[:]) if err := graph.AddChannelEdge(edgeInfo); err != nil { t.Fatalf("unable to create channel edge: %v", err) } @@ -649,7 +669,7 @@ func TestEdgeInfoUpdates(t *testing.T) { // With the edge added, we can now create some fake edge information to // update for both edges. edge1 := &ChannelEdgePolicy{ - Signature: testSig, + SigBytes: testSig.Serialize(), ChannelID: chanID, LastUpdate: time.Unix(433453, 0), Flags: 0, @@ -661,7 +681,7 @@ func TestEdgeInfoUpdates(t *testing.T) { db: db, } edge2 := &ChannelEdgePolicy{ - Signature: testSig, + SigBytes: testSig.Serialize(), ChannelID: chanID, LastUpdate: time.Unix(124234, 0), Flags: 1, @@ -796,9 +816,7 @@ func TestGraphTraversal(t *testing.T) { // Determine which node is "smaller", we'll need this in order to // properly create the edges for the graph. var firstNode, secondNode *LightningNode - node1Bytes := nodes[0].PubKey.SerializeCompressed() - node2Bytes := nodes[1].PubKey.SerializeCompressed() - if bytes.Compare(node1Bytes, node2Bytes) == -1 { + if bytes.Compare(nodes[0].PubKeyBytes[:], nodes[1].PubKeyBytes[:]) == -1 { firstNode = nodes[0] secondNode = nodes[1] } else { @@ -818,21 +836,21 @@ func TestGraphTraversal(t *testing.T) { } edgeInfo := ChannelEdgeInfo{ - ChannelID: chanID, - ChainHash: key, - NodeKey1: nodes[0].PubKey, - NodeKey2: nodes[1].PubKey, - BitcoinKey1: nodes[0].PubKey, - BitcoinKey2: nodes[1].PubKey, + ChannelID: chanID, + ChainHash: key, AuthProof: &ChannelAuthProof{ - NodeSig1: testSig, - NodeSig2: testSig, - BitcoinSig1: testSig, - BitcoinSig2: testSig, + NodeSig1Bytes: testSig.Serialize(), + NodeSig2Bytes: testSig.Serialize(), + BitcoinSig1Bytes: testSig.Serialize(), + BitcoinSig2Bytes: testSig.Serialize(), }, ChannelPoint: op, Capacity: 1000, } + copy(edgeInfo.NodeKey1Bytes[:], nodes[0].PubKeyBytes[:]) + copy(edgeInfo.NodeKey2Bytes[:], nodes[1].PubKeyBytes[:]) + copy(edgeInfo.BitcoinKey1Bytes[:], nodes[0].PubKeyBytes[:]) + copy(edgeInfo.BitcoinKey2Bytes[:], nodes[1].PubKeyBytes[:]) err := graph.AddChannelEdge(&edgeInfo) if err != nil { t.Fatalf("unable to add node: %v", err) @@ -843,7 +861,7 @@ func TestGraphTraversal(t *testing.T) { edge := randEdgePolicy(chanID, op, db) edge.Flags = 0 edge.Node = secondNode - edge.Signature = testSig + edge.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edge); err != nil { t.Fatalf("unable to update edge: %v", err) } @@ -853,7 +871,7 @@ func TestGraphTraversal(t *testing.T) { edge = randEdgePolicy(chanID, op, db) edge.Flags = 1 edge.Node = firstNode - edge.Signature = testSig + edge.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edge); err != nil { t.Fatalf("unable to update edge: %v", err) } @@ -885,13 +903,13 @@ func TestGraphTraversal(t *testing.T) { // Each each should indicate that it's outgoing (pointed // towards the second node). - if !outEdge.Node.PubKey.IsEqual(secondNode.PubKey) { + if !bytes.Equal(outEdge.Node.PubKeyBytes[:], secondNode.PubKeyBytes[:]) { return fmt.Errorf("wrong outgoing edge") } // The incoming edge should also indicate that it's pointing to // the origin node. - if !inEdge.Node.PubKey.IsEqual(firstNode.PubKey) { + if !bytes.Equal(inEdge.Node.PubKeyBytes[:], firstNode.PubKeyBytes[:]) { return fmt.Errorf("wrong outgoing edge") } @@ -1008,22 +1026,21 @@ func TestGraphPruning(t *testing.T) { channelPoints = append(channelPoints, &op) edgeInfo := ChannelEdgeInfo{ - ChannelID: chanID, - ChainHash: key, - NodeKey1: graphNodes[i].PubKey, - NodeKey2: graphNodes[i+1].PubKey, - BitcoinKey1: graphNodes[i].PubKey, - BitcoinKey2: graphNodes[i+1].PubKey, + ChannelID: chanID, + ChainHash: key, AuthProof: &ChannelAuthProof{ - NodeSig1: testSig, - NodeSig2: testSig, - BitcoinSig1: testSig, - BitcoinSig2: testSig, + NodeSig1Bytes: testSig.Serialize(), + NodeSig2Bytes: testSig.Serialize(), + BitcoinSig1Bytes: testSig.Serialize(), + BitcoinSig2Bytes: testSig.Serialize(), }, ChannelPoint: op, Capacity: 1000, } - + copy(edgeInfo.NodeKey1Bytes[:], graphNodes[i].PubKeyBytes[:]) + copy(edgeInfo.NodeKey2Bytes[:], graphNodes[i+1].PubKeyBytes[:]) + copy(edgeInfo.BitcoinKey1Bytes[:], graphNodes[i].PubKeyBytes[:]) + copy(edgeInfo.BitcoinKey2Bytes[:], graphNodes[i+1].PubKeyBytes[:]) if err := graph.AddChannelEdge(&edgeInfo); err != nil { t.Fatalf("unable to add node: %v", err) } @@ -1033,7 +1050,7 @@ func TestGraphPruning(t *testing.T) { edge := randEdgePolicy(chanID, op, db) edge.Flags = 0 edge.Node = graphNodes[i] - edge.Signature = testSig + edge.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edge); err != nil { t.Fatalf("unable to update edge: %v", err) } @@ -1043,7 +1060,7 @@ func TestGraphPruning(t *testing.T) { edge = randEdgePolicy(chanID, op, db) edge.Flags = 1 edge.Node = graphNodes[i] - edge.Signature = testSig + edge.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edge); err != nil { t.Fatalf("unable to update edge: %v", err) } @@ -1159,9 +1176,9 @@ func compareNodes(a, b *LightningNode) error { return fmt.Errorf("Addresses doesn't match: expected %#v, \n "+ "got %#v", a.Addresses, b.Addresses) } - if !reflect.DeepEqual(a.PubKey, b.PubKey) { + if !reflect.DeepEqual(a.PubKeyBytes, b.PubKeyBytes) { return fmt.Errorf("PubKey doesn't match: expected %#v, \n "+ - "got %#v", a.PubKey, b.PubKey) + "got %#v", a.PubKeyBytes, b.PubKeyBytes) } if !reflect.DeepEqual(a.Color, b.Color) { return fmt.Errorf("Color doesn't match: expected %#v, \n "+ diff --git a/channeldb/waitingproof_test.go b/channeldb/waitingproof_test.go index 9b3b8a4c..2f8a64b0 100644 --- a/channeldb/waitingproof_test.go +++ b/channeldb/waitingproof_test.go @@ -21,8 +21,8 @@ func TestWaitingProofStore(t *testing.T) { defer cleanup() proof1 := NewWaitingProof(true, &lnwire.AnnounceSignatures{ - NodeSignature: testSig, - BitcoinSignature: testSig, + NodeSignature: wireSig, + BitcoinSignature: wireSig, }) store, err := NewWaitingProofStore(db) From cd9d2d7e6fada29b00b2ade6094a6fa704442dc4 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2018 20:23:14 -0800 Subject: [PATCH 07/16] discovery: update graph API usage to match recent API changes --- discovery/ann_validation.go | 61 ++++++++++-- discovery/gossiper.go | 187 +++++++++++++++++++++++------------- discovery/gossiper_test.go | 141 ++++++++++++++++++++------- discovery/utils.go | 52 +++++++--- 4 files changed, 319 insertions(+), 122 deletions(-) diff --git a/discovery/ann_validation.go b/discovery/ann_validation.go index 058bf7c1..e93559bf 100644 --- a/discovery/ann_validation.go +++ b/discovery/ann_validation.go @@ -26,23 +26,56 @@ func ValidateChannelAnn(a *lnwire.ChannelAnnouncement) error { // First we'll verify that the passed bitcoin key signature is indeed a // signature over the computed hash digest. - if !a.BitcoinSig1.Verify(dataHash, copyPubKey(a.BitcoinKey1)) { + bitcoinSig1, err := a.BitcoinSig1.ToSignature() + if err != nil { + return err + } + bitcoinKey1, err := btcec.ParsePubKey(a.BitcoinKey1[:], btcec.S256()) + if err != nil { + return err + } + if !bitcoinSig1.Verify(dataHash, bitcoinKey1) { return errors.New("can't verify first bitcoin signature") } // If that checks out, then we'll verify that the second bitcoin // signature is a valid signature of the bitcoin public key over hash // digest as well. - if !a.BitcoinSig2.Verify(dataHash, copyPubKey(a.BitcoinKey2)) { + bitcoinSig2, err := a.BitcoinSig2.ToSignature() + if err != nil { + return err + } + bitcoinKey2, err := btcec.ParsePubKey(a.BitcoinKey2[:], btcec.S256()) + if err != nil { + return err + } + if !bitcoinSig2.Verify(dataHash, bitcoinKey2) { return errors.New("can't verify second bitcoin signature") } // Both node signatures attached should indeed be a valid signature // over the selected digest of the channel announcement signature. - if !a.NodeSig1.Verify(dataHash, copyPubKey(a.NodeID1)) { + nodeSig1, err := a.NodeSig1.ToSignature() + if err != nil { + return err + } + nodeKey1, err := btcec.ParsePubKey(a.NodeID1[:], btcec.S256()) + if err != nil { + return err + } + if !nodeSig1.Verify(dataHash, nodeKey1) { return errors.New("can't verify data in first node signature") } - if !a.NodeSig2.Verify(dataHash, copyPubKey(a.NodeID2)) { + + nodeSig2, err := a.NodeSig2.ToSignature() + if err != nil { + return err + } + nodeKey2, err := btcec.ParsePubKey(a.NodeID2[:], btcec.S256()) + if err != nil { + return err + } + if !nodeSig2.Verify(dataHash, nodeKey2) { return errors.New("can't verify data in second node signature") } @@ -61,17 +94,26 @@ func ValidateNodeAnn(a *lnwire.NodeAnnouncement) error { return err } + nodeSig, err := a.Signature.ToSignature() + if err != nil { + return err + } + nodeKey, err := btcec.ParsePubKey(a.NodeID[:], btcec.S256()) + if err != nil { + return err + } + // Finally ensure that the passed signature is valid, if not we'll // return an error so this node announcement can be rejected. dataHash := chainhash.DoubleHashB(data) - if !a.Signature.Verify(dataHash, copyPubKey(a.NodeID)) { + if !nodeSig.Verify(dataHash, nodeKey) { var msgBuf bytes.Buffer if _, err := lnwire.WriteMessage(&msgBuf, a, 0); err != nil { return err } return errors.Errorf("signature on NodeAnnouncement(%x) is "+ - "invalid: %x", a.NodeID.SerializeCompressed(), + "invalid: %x", nodeKey.SerializeCompressed(), msgBuf.Bytes()) } @@ -90,7 +132,12 @@ func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, } dataHash := chainhash.DoubleHashB(data) - if !a.Signature.Verify(dataHash, copyPubKey(pubKey)) { + nodeSig, err := a.Signature.ToSignature() + if err != nil { + return err + } + + if !nodeSig.Verify(dataHash, pubKey) { return errors.Errorf("invalid signature for channel "+ "update %v", spew.Sdump(a)) } diff --git a/discovery/gossiper.go b/discovery/gossiper.go index e49907d7..5d722caf 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -157,17 +157,17 @@ type AuthenticatedGossiper struct { // TODO(roasbeef): limit premature networkMsgs to N prematureAnnouncements map[uint32][]*networkMsg - // prematureChannelUpdates is a map of ChannelUpdates we have - // received that wasn't associated with any channel we know about. - // We store them temporarily, such that we can reprocess them when - // a ChannelAnnouncement for the channel is received. + // prematureChannelUpdates is a map of ChannelUpdates we have received + // that wasn't associated with any channel we know about. We store + // them temporarily, such that we can reprocess them when a + // ChannelAnnouncement for the channel is received. prematureChannelUpdates map[uint64][]*networkMsg pChanUpdMtx sync.Mutex // waitingProofs is a persistent storage of partial channel proof // announcement messages. We use it to buffer half of the material - // needed to reconstruct a full authenticated channel announcement. Once - // we receive the other half the channel proof, we'll be able to + // needed to reconstruct a full authenticated channel announcement. + // Once we receive the other half the channel proof, we'll be able to // properly validate it an re-broadcast it out to the network. waitingProofs *channeldb.WaitingProofStore @@ -176,8 +176,8 @@ type AuthenticatedGossiper struct { // networkHandler. networkMsgs chan *networkMsg - // chanPolicyUpdates is a channel that requests to update the forwarding - // policy of a set of channels is sent over. + // chanPolicyUpdates is a channel that requests to update the + // forwarding policy of a set of channels is sent over. chanPolicyUpdates chan *chanPolicyUpdateRequest // bestHeight is the height of the block at the tip of the main chain @@ -232,17 +232,22 @@ func (d *AuthenticatedGossiper) SynchronizeNode(pub *btcec.PublicKey) error { // containing all the messages to be sent to the target peer. var announceMessages []lnwire.Message - makeNodeAnn := func(n *channeldb.LightningNode) *lnwire.NodeAnnouncement { + makeNodeAnn := func(n *channeldb.LightningNode) (*lnwire.NodeAnnouncement, error) { alias, _ := lnwire.NewNodeAlias(n.Alias) + + wireSig, err := lnwire.NewSigFromRawSignature(n.AuthSigBytes) + if err != nil { + return nil, err + } return &lnwire.NodeAnnouncement{ - Signature: n.AuthSig, + Signature: wireSig, Timestamp: uint32(n.LastUpdate.Unix()), Addresses: n.Addresses, - NodeID: n.PubKey, + NodeID: n.PubKeyBytes, Features: n.Features.RawFeatureVector, RGBColor: n.Color, Alias: alias, - } + }, nil } // As peers are expecting channel announcements before node @@ -262,8 +267,12 @@ func (d *AuthenticatedGossiper) SynchronizeNode(pub *btcec.PublicKey) error { // also has known validated nodes, then we'll send that as // well. if chanInfo.AuthProof != nil { - chanAnn, e1Ann, e2Ann := createChanAnnouncement( - chanInfo.AuthProof, chanInfo, e1, e2) + chanAnn, e1Ann, e2Ann, err := createChanAnnouncement( + chanInfo.AuthProof, chanInfo, e1, e2, + ) + if err != nil { + return err + } announceMessages = append(announceMessages, chanAnn) if e1Ann != nil { @@ -272,7 +281,10 @@ func (d *AuthenticatedGossiper) SynchronizeNode(pub *btcec.PublicKey) error { // If this edge has a validated node // announcement, then we'll send that as well. if e1.Node.HaveNodeAnnouncement { - nodeAnn := makeNodeAnn(e1.Node) + nodeAnn, err := makeNodeAnn(e1.Node) + if err != nil { + return err + } announceMessages = append( announceMessages, nodeAnn, ) @@ -285,7 +297,10 @@ func (d *AuthenticatedGossiper) SynchronizeNode(pub *btcec.PublicKey) error { // If this edge has a validated node // announcement, then we'll send that as well. if e2.Node.HaveNodeAnnouncement { - nodeAnn := makeNodeAnn(e2.Node) + nodeAnn, err := makeNodeAnn(e2.Node) + if err != nil { + return err + } announceMessages = append( announceMessages, nodeAnn, ) @@ -588,7 +603,7 @@ func (d *deDupedAnnouncements) addMsg(message networkMsg) { // NodeID to create the corresponding Vertex. case *lnwire.NodeAnnouncement: sender := routing.NewVertex(message.peer) - deDupKey := routing.NewVertex(msg.NodeID) + deDupKey := routing.Vertex(msg.NodeID) // We do the same for node announcements as we did for channel // updates, as they also carry a timestamp. @@ -1182,9 +1197,12 @@ func (d *AuthenticatedGossiper) processRejectedEdge(chanAnnMsg *lnwire.ChannelAn // We'll then create then validate the new fully assembled // announcement. - chanAnn, e1Ann, e2Ann := createChanAnnouncement( + chanAnn, e1Ann, e2Ann, err := createChanAnnouncement( proof, chanInfo, e1, e2, ) + if err != nil { + return nil, err + } err = ValidateChannelAnn(chanAnn) if err != nil { err := errors.Errorf("assembled channel announcement proof "+ @@ -1265,9 +1283,9 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n HaveNodeAnnouncement: true, LastUpdate: time.Unix(int64(msg.Timestamp), 0), Addresses: msg.Addresses, - PubKey: msg.NodeID, + PubKeyBytes: msg.NodeID, Alias: msg.Alias.String(), - AuthSig: msg.Signature, + AuthSigBytes: msg.Signature.ToSignatureBytes(), Features: features, Color: msg.RGBColor, } @@ -1348,10 +1366,10 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n // itself to the database so we can fetch it later when // gossiping with other nodes. proof = &channeldb.ChannelAuthProof{ - NodeSig1: msg.NodeSig1, - NodeSig2: msg.NodeSig2, - BitcoinSig1: msg.BitcoinSig1, - BitcoinSig2: msg.BitcoinSig2, + NodeSig1Bytes: msg.NodeSig1.ToSignatureBytes(), + NodeSig2Bytes: msg.NodeSig2.ToSignatureBytes(), + BitcoinSig1Bytes: msg.BitcoinSig1.ToSignatureBytes(), + BitcoinSig2Bytes: msg.BitcoinSig2.ToSignatureBytes(), } } @@ -1365,14 +1383,14 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n } edge := &channeldb.ChannelEdgeInfo{ - ChannelID: msg.ShortChannelID.ToUint64(), - ChainHash: msg.ChainHash, - NodeKey1: msg.NodeID1, - NodeKey2: msg.NodeID2, - BitcoinKey1: msg.BitcoinKey1, - BitcoinKey2: msg.BitcoinKey2, - AuthProof: proof, - Features: featureBuf.Bytes(), + ChannelID: msg.ShortChannelID.ToUint64(), + ChainHash: msg.ChainHash, + NodeKey1Bytes: msg.NodeID1, + NodeKey2Bytes: msg.NodeID2, + BitcoinKey1Bytes: msg.BitcoinKey1, + BitcoinKey2Bytes: msg.BitcoinKey2, + AuthProof: proof, + Features: featureBuf.Bytes(), } // We will add the edge to the channel router. If the nodes @@ -1560,7 +1578,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n d.prematureChannelUpdates[shortChanID], nMsg) d.pChanUpdMtx.Unlock() - log.Infof("Got ChannelUpdate for edge not "+ + log.Debugf("Got ChannelUpdate for edge not "+ "found in graph(shortChanID=%v), "+ "saving for reprocessing later", shortChanID) @@ -1582,9 +1600,9 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n var pubKey *btcec.PublicKey switch { case msg.Flags&lnwire.ChanUpdateDirection == 0: - pubKey = chanInfo.NodeKey1 + pubKey, _ = chanInfo.NodeKey1() case msg.Flags&lnwire.ChanUpdateDirection == 1: - pubKey = chanInfo.NodeKey2 + pubKey, _ = chanInfo.NodeKey2() } // Validate the channel announcement with the expected public @@ -1601,7 +1619,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n } update := &channeldb.ChannelEdgePolicy{ - Signature: msg.Signature, + SigBytes: msg.Signature.ToSignatureBytes(), ChannelID: shortChanID, LastUpdate: time.Unix(int64(msg.Timestamp), 0), Flags: msg.Flags, @@ -1632,9 +1650,9 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n var remotePeer *btcec.PublicKey switch { case msg.Flags&lnwire.ChanUpdateDirection == 0: - remotePeer = chanInfo.NodeKey2 + remotePeer, _ = chanInfo.NodeKey2() case msg.Flags&lnwire.ChanUpdateDirection == 1: - remotePeer = chanInfo.NodeKey1 + remotePeer, _ = chanInfo.NodeKey1() } // Send ChannelUpdate directly to remotePeer. @@ -1726,9 +1744,9 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n } isFirstNode := bytes.Equal(nMsg.peer.SerializeCompressed(), - chanInfo.NodeKey1.SerializeCompressed()) + chanInfo.NodeKey1Bytes[:]) isSecondNode := bytes.Equal(nMsg.peer.SerializeCompressed(), - chanInfo.NodeKey2.SerializeCompressed()) + chanInfo.NodeKey2Bytes[:]) // Ensure that channel that was retrieved belongs to the peer // which sent the proof announcement. @@ -1748,9 +1766,9 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n if !nMsg.isRemote { var remotePeer *btcec.PublicKey if isFirstNode { - remotePeer = chanInfo.NodeKey2 + remotePeer, _ = chanInfo.NodeKey2() } else { - remotePeer = chanInfo.NodeKey1 + remotePeer, _ = chanInfo.NodeKey1() } // Since the remote peer might not be online // we'll call a method that will attempt to @@ -1786,9 +1804,14 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n msg.ChannelID, peerID) - chanAnn, _, _ := createChanAnnouncement( - chanInfo.AuthProof, chanInfo, e1, e2) - err := d.cfg.SendToPeer(nMsg.peer, chanAnn) + chanAnn, _, _, err := createChanAnnouncement( + chanInfo.AuthProof, chanInfo, e1, e2, + ) + if err != nil { + log.Error("unable to gen ann: %v", err) + return + } + err = d.cfg.SendToPeer(nMsg.peer, chanAnn) if err != nil { log.Errorf("Failed sending "+ "full proof to "+ @@ -1846,17 +1869,22 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n // validate it shortly below. var dbProof channeldb.ChannelAuthProof if isFirstNode { - dbProof.NodeSig1 = msg.NodeSignature - dbProof.NodeSig2 = oppositeProof.NodeSignature - dbProof.BitcoinSig1 = msg.BitcoinSignature - dbProof.BitcoinSig2 = oppositeProof.BitcoinSignature + dbProof.NodeSig1Bytes = msg.NodeSignature.ToSignatureBytes() + dbProof.NodeSig2Bytes = oppositeProof.NodeSignature.ToSignatureBytes() + dbProof.BitcoinSig1Bytes = msg.BitcoinSignature.ToSignatureBytes() + dbProof.BitcoinSig2Bytes = oppositeProof.BitcoinSignature.ToSignatureBytes() } else { - dbProof.NodeSig1 = oppositeProof.NodeSignature - dbProof.NodeSig2 = msg.NodeSignature - dbProof.BitcoinSig1 = oppositeProof.BitcoinSignature - dbProof.BitcoinSig2 = msg.BitcoinSignature + dbProof.NodeSig1Bytes = oppositeProof.NodeSignature.ToSignatureBytes() + dbProof.NodeSig2Bytes = msg.NodeSignature.ToSignatureBytes() + dbProof.BitcoinSig1Bytes = oppositeProof.BitcoinSignature.ToSignatureBytes() + dbProof.BitcoinSig2Bytes = msg.BitcoinSignature.ToSignatureBytes() + } + chanAnn, e1Ann, e2Ann, err := createChanAnnouncement(&dbProof, chanInfo, e1, e2) + if err != nil { + log.Error(err) + nMsg.err <- err + return nil } - chanAnn, e1Ann, e2Ann := createChanAnnouncement(&dbProof, chanInfo, e1, e2) // With all the necessary components assembled validate the // full channel announcement proof. @@ -2017,6 +2045,8 @@ func (d *AuthenticatedGossiper) sendAnnSigReliably( func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo, edge *channeldb.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement, *lnwire.ChannelUpdate, error) { + var err error + // Make sure timestamp is always increased, such that our update // gets propagated. timestamp := time.Now().Unix() @@ -2025,7 +2055,6 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo, } edge.LastUpdate = time.Unix(timestamp, 0) chanUpdate := &lnwire.ChannelUpdate{ - Signature: edge.Signature, ChainHash: info.ChainHash, ShortChannelID: lnwire.NewShortChanIDFromInt(edge.ChannelID), Timestamp: uint32(timestamp), @@ -2035,6 +2064,10 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo, BaseFee: uint32(edge.FeeBaseMSat), FeeRate: uint32(edge.FeeProportionalMillionths), } + chanUpdate.Signature, err = lnwire.NewSigFromRawSignature(edge.SigBytes) + if err != nil { + return nil, nil, err + } // With the update applied, we'll generate a new signature over a // digest of the channel announcement itself. @@ -2045,8 +2078,11 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo, // Next, we'll set the new signature in place, and update the reference // in the backing slice. - edge.Signature = sig - chanUpdate.Signature = sig + edge.SigBytes = sig.Serialize() + chanUpdate.Signature, err = lnwire.NewSigFromSignature(sig) + if err != nil { + return nil, nil, err + } // To ensure that our signature is valid, we'll verify it ourself // before committing it to the slice returned. @@ -2057,7 +2093,6 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo, } // Finally, we'll write the new edge policy to disk. - edge.Node.PubKey.Curve = nil if err := d.cfg.Router.UpdateEdge(edge); err != nil { return nil, nil, err } @@ -2069,17 +2104,37 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo, if info.AuthProof != nil { chanID := lnwire.NewShortChanIDFromInt(info.ChannelID) chanAnn = &lnwire.ChannelAnnouncement{ - NodeSig1: info.AuthProof.NodeSig1, - NodeSig2: info.AuthProof.NodeSig2, ShortChannelID: chanID, - BitcoinSig1: info.AuthProof.BitcoinSig1, - BitcoinSig2: info.AuthProof.BitcoinSig2, - NodeID1: info.NodeKey1, - NodeID2: info.NodeKey2, + NodeID1: info.NodeKey1Bytes, + NodeID2: info.NodeKey2Bytes, ChainHash: info.ChainHash, - BitcoinKey1: info.BitcoinKey1, + BitcoinKey1: info.BitcoinKey1Bytes, Features: lnwire.NewRawFeatureVector(), - BitcoinKey2: info.BitcoinKey2, + BitcoinKey2: info.BitcoinKey2Bytes, + } + chanAnn.NodeSig1, err = lnwire.NewSigFromRawSignature( + info.AuthProof.NodeSig1Bytes, + ) + if err != nil { + return nil, nil, err + } + chanAnn.NodeSig2, err = lnwire.NewSigFromRawSignature( + info.AuthProof.NodeSig2Bytes, + ) + if err != nil { + return nil, nil, err + } + chanAnn.BitcoinSig1, err = lnwire.NewSigFromRawSignature( + info.AuthProof.BitcoinSig1Bytes, + ) + if err != nil { + return nil, nil, err + } + chanAnn.BitcoinSig2, err = lnwire.NewSigFromRawSignature( + info.AuthProof.BitcoinSig2Bytes, + ) + if err != nil { + return nil, nil, err } } diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go index 6bfe9867..4a8502d4 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -305,10 +305,6 @@ func createAnnouncements(blockHeight uint32) (*annBatch, error) { if err != nil { return nil, err } - batch.localChanAnn.BitcoinSig1 = nil - batch.localChanAnn.BitcoinSig2 = nil - batch.localChanAnn.NodeSig1 = nil - batch.localChanAnn.NodeSig2 = nil batch.chanUpdAnn1, err = createUpdateAnnouncement( blockHeight, 0, nodeKeyPriv1, timestamp, @@ -342,13 +338,18 @@ func createNodeAnnouncement(priv *btcec.PrivateKey, a := &lnwire.NodeAnnouncement{ Timestamp: timestamp, Addresses: testAddrs, - NodeID: priv.PubKey(), Alias: alias, Features: testFeatures, } + copy(a.NodeID[:], priv.PubKey().SerializeCompressed()) signer := mockSigner{priv} - a.Signature, err = SignAnnouncement(&signer, priv.PubKey(), a) + sig, err := SignAnnouncement(&signer, priv.PubKey(), a) + if err != nil { + return nil, err + } + + a.Signature, err = lnwire.NewSigFromSignature(sig) if err != nil { return nil, err } @@ -376,7 +377,13 @@ func createUpdateAnnouncement(blockHeight uint32, flags lnwire.ChanUpdateFlag, pub := nodeKey.PubKey() signer := mockSigner{nodeKey} - if a.Signature, err = SignAnnouncement(&signer, pub, a); err != nil { + sig, err := SignAnnouncement(&signer, pub, a) + if err != nil { + return nil, err + } + + a.Signature, err = lnwire.NewSigFromSignature(sig) + if err != nil { return nil, err } @@ -392,34 +399,54 @@ func createRemoteChannelAnnouncement(blockHeight uint32) (*lnwire.ChannelAnnounc TxIndex: 0, TxPosition: 0, }, - NodeID1: nodeKeyPub1, - NodeID2: nodeKeyPub2, - BitcoinKey1: bitcoinKeyPub1, - BitcoinKey2: bitcoinKeyPub2, - Features: testFeatures, + Features: testFeatures, } + copy(a.NodeID1[:], nodeKeyPub1.SerializeCompressed()) + copy(a.NodeID2[:], nodeKeyPub2.SerializeCompressed()) + copy(a.BitcoinKey1[:], bitcoinKeyPub1.SerializeCompressed()) + copy(a.BitcoinKey2[:], bitcoinKeyPub2.SerializeCompressed()) pub := nodeKeyPriv1.PubKey() signer := mockSigner{nodeKeyPriv1} - if a.NodeSig1, err = SignAnnouncement(&signer, pub, a); err != nil { + sig, err := SignAnnouncement(&signer, pub, a) + if err != nil { + return nil, err + } + a.NodeSig1, err = lnwire.NewSigFromSignature(sig) + if err != nil { return nil, err } pub = nodeKeyPriv2.PubKey() signer = mockSigner{nodeKeyPriv2} - if a.NodeSig2, err = SignAnnouncement(&signer, pub, a); err != nil { + sig, err = SignAnnouncement(&signer, pub, a) + if err != nil { + return nil, err + } + a.NodeSig2, err = lnwire.NewSigFromSignature(sig) + if err != nil { return nil, err } pub = bitcoinKeyPriv1.PubKey() signer = mockSigner{bitcoinKeyPriv1} - if a.BitcoinSig1, err = SignAnnouncement(&signer, pub, a); err != nil { + sig, err = SignAnnouncement(&signer, pub, a) + if err != nil { + return nil, err + } + a.BitcoinSig1, err = lnwire.NewSigFromSignature(sig) + if err != nil { return nil, err } pub = bitcoinKeyPriv2.PubKey() signer = mockSigner{bitcoinKeyPriv2} - if a.BitcoinSig2, err = SignAnnouncement(&signer, pub, a); err != nil { + sig, err = SignAnnouncement(&signer, pub, a) + if err != nil { + return nil, err + } + a.BitcoinSig2, err = lnwire.NewSigFromSignature(sig) + if err != nil { return nil, err } @@ -521,8 +548,10 @@ func TestProcessAnnouncement(t *testing.T) { t.Fatalf("can't create node announcement: %v", err) } + nodePub := nodeKeyPriv1.PubKey() + select { - case err = <-ctx.gossiper.ProcessRemoteAnnouncement(na, na.NodeID): + case err = <-ctx.gossiper.ProcessRemoteAnnouncement(na, nodePub): case <-time.After(2 * time.Second): t.Fatal("remote announcement not processed") } @@ -532,7 +561,7 @@ func TestProcessAnnouncement(t *testing.T) { select { case msg := <-ctx.broadcastedMessage: - assertSenderExistence(na.NodeID, msg) + assertSenderExistence(nodePub, msg) case <-time.After(2 * trickleDelay): t.Fatal("announcement wasn't proceeded") } @@ -550,7 +579,7 @@ func TestProcessAnnouncement(t *testing.T) { } select { - case err = <-ctx.gossiper.ProcessRemoteAnnouncement(ca, na.NodeID): + case err = <-ctx.gossiper.ProcessRemoteAnnouncement(ca, nodePub): case <-time.After(2 * time.Second): t.Fatal("remote announcement not processed") } @@ -560,7 +589,7 @@ func TestProcessAnnouncement(t *testing.T) { select { case msg := <-ctx.broadcastedMessage: - assertSenderExistence(na.NodeID, msg) + assertSenderExistence(nodePub, msg) case <-time.After(2 * trickleDelay): t.Fatal("announcement wasn't proceeded") } @@ -578,7 +607,7 @@ func TestProcessAnnouncement(t *testing.T) { } select { - case err = <-ctx.gossiper.ProcessRemoteAnnouncement(ua, na.NodeID): + case err = <-ctx.gossiper.ProcessRemoteAnnouncement(ua, nodePub): case <-time.After(2 * time.Second): t.Fatal("remote announcement not processed") } @@ -588,7 +617,7 @@ func TestProcessAnnouncement(t *testing.T) { select { case msg := <-ctx.broadcastedMessage: - assertSenderExistence(na.NodeID, msg) + assertSenderExistence(nodePub, msg) case <-time.After(2 * trickleDelay): t.Fatal("announcement wasn't proceeded") } @@ -612,11 +641,13 @@ func TestPrematureAnnouncement(t *testing.T) { } defer cleanup() - na, err := createNodeAnnouncement(nodeKeyPriv1, timestamp) + _, err = createNodeAnnouncement(nodeKeyPriv1, timestamp) if err != nil { t.Fatalf("can't create node announcement: %v", err) } + nodePub := nodeKeyPriv1.PubKey() + // Pretending that we receive the valid channel announcement from // remote side, but block height of this announcement is greater than // highest know to us, for that reason it should be added to the @@ -627,7 +658,7 @@ func TestPrematureAnnouncement(t *testing.T) { } select { - case <-ctx.gossiper.ProcessRemoteAnnouncement(ca, na.NodeID): + case <-ctx.gossiper.ProcessRemoteAnnouncement(ca, nodePub): t.Fatal("announcement was proceeded") case <-time.After(100 * time.Millisecond): } @@ -646,7 +677,7 @@ func TestPrematureAnnouncement(t *testing.T) { } select { - case <-ctx.gossiper.ProcessRemoteAnnouncement(ua, na.NodeID): + case <-ctx.gossiper.ProcessRemoteAnnouncement(ua, nodePub): t.Fatal("announcement was proceeded") case <-time.After(100 * time.Millisecond): } @@ -709,8 +740,14 @@ func TestSignatureAnnouncementLocalFirst(t *testing.T) { t.Fatalf("can't generate announcements: %v", err) } - localKey := batch.nodeAnn1.NodeID - remoteKey := batch.nodeAnn2.NodeID + localKey, err := btcec.ParsePubKey(batch.nodeAnn1.NodeID[:], btcec.S256()) + if err != nil { + t.Fatalf("unable to parse pubkey: %v", err) + } + remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:], btcec.S256()) + if err != nil { + t.Fatalf("unable to parse pubkey: %v", err) + } // Recreate lightning network topology. Initialize router with channel // between two nodes. @@ -865,8 +902,14 @@ func TestOrphanSignatureAnnouncement(t *testing.T) { t.Fatalf("can't generate announcements: %v", err) } - localKey := batch.nodeAnn1.NodeID - remoteKey := batch.nodeAnn2.NodeID + localKey, err := btcec.ParsePubKey(batch.nodeAnn1.NodeID[:], btcec.S256()) + if err != nil { + t.Fatalf("unable to parse pubkey: %v", err) + } + remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:], btcec.S256()) + if err != nil { + t.Fatalf("unable to parse pubkey: %v", err) + } // Pretending that we receive local channel announcement from funding // manager, thereby kick off the announcement exchange process, in @@ -1021,8 +1064,14 @@ func TestSignatureAnnouncementRetry(t *testing.T) { t.Fatalf("can't generate announcements: %v", err) } - localKey := batch.nodeAnn1.NodeID - remoteKey := batch.nodeAnn2.NodeID + localKey, err := btcec.ParsePubKey(batch.nodeAnn1.NodeID[:], btcec.S256()) + if err != nil { + t.Fatalf("unable to parse pubkey: %v", err) + } + remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:], btcec.S256()) + if err != nil { + t.Fatalf("unable to parse pubkey: %v", err) + } // Recreate lightning network topology. Initialize router with channel // between two nodes. @@ -1203,8 +1252,14 @@ func TestSignatureAnnouncementRetryAtStartup(t *testing.T) { t.Fatalf("can't generate announcements: %v", err) } - localKey := batch.nodeAnn1.NodeID - remoteKey := batch.nodeAnn2.NodeID + localKey, err := btcec.ParsePubKey(batch.nodeAnn1.NodeID[:], btcec.S256()) + if err != nil { + t.Fatalf("unable to parse pubkey: %v", err) + } + remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:], btcec.S256()) + if err != nil { + t.Fatalf("unable to parse pubkey: %v", err) + } // Recreate lightning network topology. Initialize router with channel // between two nodes. @@ -1422,8 +1477,14 @@ func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) { t.Fatalf("can't generate announcements: %v", err) } - localKey := batch.nodeAnn1.NodeID - remoteKey := batch.nodeAnn2.NodeID + localKey, err := btcec.ParsePubKey(batch.nodeAnn1.NodeID[:], btcec.S256()) + if err != nil { + t.Fatalf("unable to parse pubkey: %v", err) + } + remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:], btcec.S256()) + if err != nil { + t.Fatalf("unable to parse pubkey: %v", err) + } // Recreate lightning network topology. Initialize router with channel // between two nodes. @@ -1819,8 +1880,14 @@ func TestReceiveRemoteChannelUpdateFirst(t *testing.T) { t.Fatalf("can't generate announcements: %v", err) } - localKey := batch.nodeAnn1.NodeID - remoteKey := batch.nodeAnn2.NodeID + localKey, err := btcec.ParsePubKey(batch.nodeAnn1.NodeID[:], btcec.S256()) + if err != nil { + t.Fatalf("unable to parse pubkey: %v", err) + } + remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:], btcec.S256()) + if err != nil { + t.Fatalf("unable to parse pubkey: %v", err) + } // Recreate the case where the remote node is sending us its ChannelUpdate // before we have been able to process our own ChannelAnnouncement and diff --git a/discovery/utils.go b/discovery/utils.go index 3b52403b..c9f85a8e 100644 --- a/discovery/utils.go +++ b/discovery/utils.go @@ -16,24 +16,46 @@ import ( func createChanAnnouncement(chanProof *channeldb.ChannelAuthProof, chanInfo *channeldb.ChannelEdgeInfo, e1, e2 *channeldb.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement, - *lnwire.ChannelUpdate, *lnwire.ChannelUpdate) { + *lnwire.ChannelUpdate, *lnwire.ChannelUpdate, error) { // First, using the parameters of the channel, along with the channel // authentication chanProof, we'll create re-create the original // authenticated channel announcement. chanID := lnwire.NewShortChanIDFromInt(chanInfo.ChannelID) chanAnn := &lnwire.ChannelAnnouncement{ - NodeSig1: chanProof.NodeSig1, - NodeSig2: chanProof.NodeSig2, ShortChannelID: chanID, - BitcoinSig1: chanProof.BitcoinSig1, - BitcoinSig2: chanProof.BitcoinSig2, - NodeID1: chanInfo.NodeKey1, - NodeID2: chanInfo.NodeKey2, + NodeID1: chanInfo.NodeKey1Bytes, + NodeID2: chanInfo.NodeKey2Bytes, ChainHash: chanInfo.ChainHash, - BitcoinKey1: chanInfo.BitcoinKey1, + BitcoinKey1: chanInfo.BitcoinKey1Bytes, + BitcoinKey2: chanInfo.BitcoinKey2Bytes, Features: lnwire.NewRawFeatureVector(), - BitcoinKey2: chanInfo.BitcoinKey2, + } + + var err error + chanAnn.BitcoinSig1, err = lnwire.NewSigFromRawSignature( + chanProof.BitcoinSig1Bytes, + ) + if err != nil { + return nil, nil, nil, err + } + chanAnn.BitcoinSig2, err = lnwire.NewSigFromRawSignature( + chanProof.BitcoinSig2Bytes, + ) + if err != nil { + return nil, nil, nil, err + } + chanAnn.NodeSig1, err = lnwire.NewSigFromRawSignature( + chanProof.NodeSig1Bytes, + ) + if err != nil { + return nil, nil, nil, err + } + chanAnn.NodeSig2, err = lnwire.NewSigFromRawSignature( + chanProof.NodeSig2Bytes, + ) + if err != nil { + return nil, nil, nil, err } // We'll unconditionally queue the channel's existence chanProof as it @@ -46,7 +68,6 @@ func createChanAnnouncement(chanProof *channeldb.ChannelAuthProof, var edge1Ann, edge2Ann *lnwire.ChannelUpdate if e1 != nil { edge1Ann = &lnwire.ChannelUpdate{ - Signature: e1.Signature, ChainHash: chanInfo.ChainHash, ShortChannelID: chanID, Timestamp: uint32(e1.LastUpdate.Unix()), @@ -56,10 +77,13 @@ func createChanAnnouncement(chanProof *channeldb.ChannelAuthProof, BaseFee: uint32(e1.FeeBaseMSat), FeeRate: uint32(e1.FeeProportionalMillionths), } + edge1Ann.Signature, err = lnwire.NewSigFromRawSignature(e1.SigBytes) + if err != nil { + return nil, nil, nil, err + } } if e2 != nil { edge2Ann = &lnwire.ChannelUpdate{ - Signature: e2.Signature, ChainHash: chanInfo.ChainHash, ShortChannelID: chanID, Timestamp: uint32(e2.LastUpdate.Unix()), @@ -69,9 +93,13 @@ func createChanAnnouncement(chanProof *channeldb.ChannelAuthProof, BaseFee: uint32(e2.FeeBaseMSat), FeeRate: uint32(e2.FeeProportionalMillionths), } + edge2Ann.Signature, err = lnwire.NewSigFromRawSignature(e2.SigBytes) + if err != nil { + return nil, nil, nil, err + } } - return chanAnn, edge1Ann, edge2Ann + return chanAnn, edge1Ann, edge2Ann, nil } // copyPubKey performs a copy of the target public key, setting a fresh curve From cb48a5827a488a522a59345a884421d24df63ba2 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2018 20:23:56 -0800 Subject: [PATCH 08/16] autopilot: update API usage to account for recent channeldb changes --- autopilot/agent_test.go | 1 + autopilot/graph.go | 41 +++++++++++++++++++---------------------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/autopilot/agent_test.go b/autopilot/agent_test.go index dda906ff..b4587b5b 100644 --- a/autopilot/agent_test.go +++ b/autopilot/agent_test.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" + "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcutil" diff --git a/autopilot/graph.go b/autopilot/graph.go index b2dcfaf4..fe5f1f60 100644 --- a/autopilot/graph.go +++ b/autopilot/graph.go @@ -63,7 +63,8 @@ var _ Node = (*dbNode)(nil) // // NOTE: Part of the autopilot.Node interface. func (d dbNode) PubKey() *btcec.PublicKey { - return d.node.PubKey + pubKey, _ := d.node.PubKey() + return pubKey } // Addrs returns a slice of publicly reachable public TCP addresses that the @@ -84,12 +85,13 @@ func (d dbNode) ForEachChannel(cb func(ChannelEdge) error) error { return d.node.ForEachChannel(d.tx, func(tx *bolt.Tx, ei *channeldb.ChannelEdgeInfo, ep, _ *channeldb.ChannelEdgePolicy) error { + pubkey, _ := ep.Node.PubKey() edge := ChannelEdge{ Channel: Channel{ ChanID: lnwire.NewShortChanIDFromInt(ep.ChannelID), Capacity: ei.Capacity, FundedAmt: ei.Capacity, - Node: NewNodeID(ep.Node.PubKey), + Node: NewNodeID(pubkey), }, Peer: dbNode{ tx: tx, @@ -138,7 +140,6 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, fallthrough case err == channeldb.ErrGraphNotFound: graphNode := &channeldb.LightningNode{ - PubKey: pub, HaveNodeAnnouncement: true, Addresses: []net.Addr{ &net.TCPAddr{ @@ -147,8 +148,9 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, }, Features: lnwire.NewFeatureVector(nil, lnwire.GlobalFeatures), - AuthSig: testSig, + AuthSigBytes: testSig.Serialize(), } + graphNode.AddPubKey(pub) if err := d.db.AddLightningNode(graphNode); err != nil { return nil, err } @@ -164,16 +166,16 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, return nil, err } dbNode := &channeldb.LightningNode{ - PubKey: nodeKey, HaveNodeAnnouncement: true, Addresses: []net.Addr{ &net.TCPAddr{ IP: bytes.Repeat([]byte("a"), 16), }, }, - Features: lnwire.NewFeatureVector(nil, lnwire.GlobalFeatures), - AuthSig: testSig, + Features: lnwire.NewFeatureVector(nil, lnwire.GlobalFeatures), + AuthSigBytes: testSig.Serialize(), } + dbNode.AddPubKey(nodeKey) if err := d.db.AddLightningNode(dbNode); err != nil { return nil, err } @@ -192,30 +194,25 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, } var lnNode1, lnNode2 *btcec.PublicKey - node1Bytes := vertex1.PubKey.SerializeCompressed() - node2Bytes := vertex2.PubKey.SerializeCompressed() - if bytes.Compare(node1Bytes, node2Bytes) == -1 { - lnNode1 = vertex1.PubKey - lnNode2 = vertex2.PubKey + if bytes.Compare(vertex1.PubKeyBytes[:], vertex2.PubKeyBytes[:]) == -1 { + lnNode1, _ = vertex1.PubKey() + lnNode2, _ = vertex2.PubKey() } else { - lnNode1 = vertex2.PubKey - lnNode2 = vertex1.PubKey + lnNode1, _ = vertex2.PubKey() + lnNode2, _ = vertex1.PubKey() } chanID := randChanID() edge := &channeldb.ChannelEdgeInfo{ - ChannelID: chanID.ToUint64(), - NodeKey1: lnNode1, - NodeKey2: lnNode2, - BitcoinKey1: vertex1.PubKey, - BitcoinKey2: vertex2.PubKey, - Capacity: capacity, + ChannelID: chanID.ToUint64(), + Capacity: capacity, } + edge.AddNodeKeys(lnNode1, lnNode2, lnNode1, lnNode2) if err := d.db.AddChannelEdge(edge); err != nil { return nil, nil, err } edgePolicy := &channeldb.ChannelEdgePolicy{ - Signature: testSig, + SigBytes: testSig.Serialize(), ChannelID: chanID.ToUint64(), LastUpdate: time.Now(), TimeLockDelta: 10, @@ -229,7 +226,7 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, return nil, nil, err } edgePolicy = &channeldb.ChannelEdgePolicy{ - Signature: testSig, + SigBytes: testSig.Serialize(), ChannelID: chanID.ToUint64(), LastUpdate: time.Now(), TimeLockDelta: 10, From 6751cd8b9fb5f9a8135d9281c0726c84cdc59aef Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2018 20:26:26 -0800 Subject: [PATCH 09/16] routing: update package to account for recent channeldb API changes --- routing/missioncontrol.go | 2 +- routing/notifications.go | 19 +++- routing/notifications_test.go | 119 ++++++++++--------- routing/pathfind.go | 35 +++--- routing/pathfind_test.go | 53 +++++---- routing/router.go | 48 ++++---- routing/router_test.go | 209 +++++++++++++++++++--------------- routing/validation_barrier.go | 17 +-- 8 files changed, 274 insertions(+), 228 deletions(-) diff --git a/routing/missioncontrol.go b/routing/missioncontrol.go index 7789c93f..8d8e595c 100644 --- a/routing/missioncontrol.go +++ b/routing/missioncontrol.go @@ -239,7 +239,7 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment, // With the next candidate path found, we'll attempt to turn this into // a route by applying the time-lock and fee requirements. - sourceVertex := NewVertex(p.mc.selfNode.PubKey) + sourceVertex := Vertex(p.mc.selfNode.PubKeyBytes) route, err := newRoute(payment.Amount, sourceVertex, path, height, finalCltvDelta) if err != nil { diff --git a/routing/notifications.go b/routing/notifications.go index 0a1c067d..c263fc2e 100644 --- a/routing/notifications.go +++ b/routing/notifications.go @@ -296,9 +296,13 @@ func addToTopologyChange(graph *channeldb.ChannelGraph, update *TopologyChange, // Any node announcement maps directly to a NetworkNodeUpdate struct. // No further data munging or db queries are required. case *channeldb.LightningNode: + pubKey, err := m.PubKey() + if err != nil { + return err + } nodeUpdate := &NetworkNodeUpdate{ Addresses: m.Addresses, - IdentityKey: m.PubKey, + IdentityKey: pubKey, Alias: m.Alias, } nodeUpdate.IdentityKey.Curve = nil @@ -332,6 +336,15 @@ func addToTopologyChange(graph *channeldb.ChannelGraph, update *TopologyChange, connectingNode = edgeInfo.NodeKey1 } + aNode, err := sourceNode() + if err != nil { + return err + } + cNode, err := connectingNode() + if err != nil { + return err + } + edgeUpdate := &ChannelEdgeUpdate{ ChanID: m.ChannelID, ChanPoint: edgeInfo.ChannelPoint, @@ -340,8 +353,8 @@ func addToTopologyChange(graph *channeldb.ChannelGraph, update *TopologyChange, MinHTLC: m.MinHTLC, BaseFee: m.FeeBaseMSat, FeeRate: m.FeeProportionalMillionths, - AdvertisingNode: sourceNode, - ConnectingNode: connectingNode, + AdvertisingNode: aNode, + ConnectingNode: cNode, } edgeUpdate.AdvertisingNode.Curve = nil edgeUpdate.ConnectingNode.Curve = nil diff --git a/routing/notifications_test.go b/routing/notifications_test.go index 8e3c8df3..7e09514a 100644 --- a/routing/notifications_test.go +++ b/routing/notifications_test.go @@ -51,23 +51,25 @@ func createTestNode() (*channeldb.LightningNode, error) { } pub := priv.PubKey().SerializeCompressed() - return &channeldb.LightningNode{ + n := &channeldb.LightningNode{ HaveNodeAnnouncement: true, LastUpdate: time.Unix(updateTime, 0), Addresses: testAddrs, - PubKey: priv.PubKey(), Color: color.RGBA{1, 2, 3, 0}, Alias: "kek" + string(pub[:]), - AuthSig: testSig, + AuthSigBytes: testSig.Serialize(), Features: testFeatures, - }, nil + } + copy(n.PubKeyBytes[:], pub) + + return n, nil } func randEdgePolicy(chanID *lnwire.ShortChannelID, node *channeldb.LightningNode) *channeldb.ChannelEdgePolicy { return &channeldb.ChannelEdgePolicy{ - Signature: testSig, + SigBytes: testSig.Serialize(), ChannelID: chanID.ToUint64(), LastUpdate: time.Unix(int64(prand.Int31()), 0), TimeLockDelta: uint16(prand.Int63()), @@ -371,18 +373,18 @@ func TestEdgeUpdateNotification(t *testing.T) { // Finally, to conclude our test set up, we'll create a channel // update to announce the created channel between the two nodes. edge := &channeldb.ChannelEdgeInfo{ - ChannelID: chanID.ToUint64(), - NodeKey1: node1.PubKey, - NodeKey2: node2.PubKey, - BitcoinKey1: bitcoinKey1, - BitcoinKey2: bitcoinKey2, + ChannelID: chanID.ToUint64(), + NodeKey1Bytes: node1.PubKeyBytes, + NodeKey2Bytes: node2.PubKeyBytes, AuthProof: &channeldb.ChannelAuthProof{ - NodeSig1: testSig, - NodeSig2: testSig, - BitcoinSig1: testSig, - BitcoinSig2: testSig, + NodeSig1Bytes: testSig.Serialize(), + NodeSig2Bytes: testSig.Serialize(), + BitcoinSig1Bytes: testSig.Serialize(), + BitcoinSig2Bytes: testSig.Serialize(), }, } + copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) + copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) if err := ctx.router.AddEdge(edge); err != nil { t.Fatalf("unable to add edge: %v", err) @@ -450,8 +452,17 @@ func TestEdgeUpdateNotification(t *testing.T) { // Create lookup map for notifications we are intending to receive. Entries // are removed from the map when the anticipated notification is received. var waitingFor = map[Vertex]int{ - NewVertex(node1.PubKey): 1, - NewVertex(node2.PubKey): 2, + Vertex(node1.PubKeyBytes): 1, + Vertex(node2.PubKeyBytes): 2, + } + + node1Pub, err := node1.PubKey() + if err != nil { + t.Fatalf("unable to encode key: %v", err) + } + node2Pub, err := node2.PubKey() + if err != nil { + t.Fatalf("unable to encode key: %v", err) } const numEdgePolicies = 2 @@ -473,20 +484,20 @@ func TestEdgeUpdateNotification(t *testing.T) { case 1: // Received notification corresponding to edge1. assertEdgeCorrect(t, edgeUpdate, edge1) - if !edgeUpdate.AdvertisingNode.IsEqual(node1.PubKey) { + if !edgeUpdate.AdvertisingNode.IsEqual(node1Pub) { t.Fatal("advertising node mismatch") } - if !edgeUpdate.ConnectingNode.IsEqual(node2.PubKey) { + if !edgeUpdate.ConnectingNode.IsEqual(node2Pub) { t.Fatal("connecting node mismatch") } case 2: // Received notification corresponding to edge2. assertEdgeCorrect(t, edgeUpdate, edge2) - if !edgeUpdate.AdvertisingNode.IsEqual(node2.PubKey) { + if !edgeUpdate.AdvertisingNode.IsEqual(node2Pub) { t.Fatal("advertising node mismatch") } - if !edgeUpdate.ConnectingNode.IsEqual(node1.PubKey) { + if !edgeUpdate.ConnectingNode.IsEqual(node1Pub) { t.Fatal("connecting node mismatch") } @@ -494,8 +505,8 @@ func TestEdgeUpdateNotification(t *testing.T) { t.Fatal("invalid edge index") } - // Remove entry from waitingFor map to ensure we don't double count a - // repeat notification. + // Remove entry from waitingFor map to ensure + // we don't double count a repeat notification. delete(waitingFor, nodeVertex) } else { @@ -552,18 +563,18 @@ func TestNodeUpdateNotification(t *testing.T) { } edge := &channeldb.ChannelEdgeInfo{ - ChannelID: chanID.ToUint64(), - NodeKey1: node1.PubKey, - NodeKey2: node2.PubKey, - BitcoinKey1: bitcoinKey1, - BitcoinKey2: bitcoinKey2, + ChannelID: chanID.ToUint64(), + NodeKey1Bytes: node1.PubKeyBytes, + NodeKey2Bytes: node2.PubKeyBytes, AuthProof: &channeldb.ChannelAuthProof{ - NodeSig1: testSig, - NodeSig2: testSig, - BitcoinSig1: testSig, - BitcoinSig2: testSig, + NodeSig1Bytes: testSig.Serialize(), + NodeSig2Bytes: testSig.Serialize(), + BitcoinSig1Bytes: testSig.Serialize(), + BitcoinSig2Bytes: testSig.Serialize(), }, } + copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) + copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) // Adding the edge will add the nodes to the graph, but with no info // except the pubkey known. @@ -589,15 +600,17 @@ func TestNodeUpdateNotification(t *testing.T) { assertNodeNtfnCorrect := func(t *testing.T, ann *channeldb.LightningNode, nodeUpdate *NetworkNodeUpdate) { + nodeKey, _ := ann.PubKey() + // The notification received should directly map the // announcement originally sent. if nodeUpdate.Addresses[0] != ann.Addresses[0] { t.Fatalf("node address doesn't match: expected %v, got %v", nodeUpdate.Addresses[0], ann.Addresses[0]) } - if !nodeUpdate.IdentityKey.IsEqual(ann.PubKey) { + if !nodeUpdate.IdentityKey.IsEqual(nodeKey) { t.Fatalf("node identity keys don't match: expected %x, "+ - "got %x", ann.PubKey.SerializeCompressed(), + "got %x", nodeKey.SerializeCompressed(), nodeUpdate.IdentityKey.SerializeCompressed()) } if nodeUpdate.Alias != ann.Alias { @@ -609,8 +622,8 @@ func TestNodeUpdateNotification(t *testing.T) { // Create lookup map for notifications we are intending to receive. Entries // are removed from the map when the anticipated notification is received. var waitingFor = map[Vertex]int{ - NewVertex(node1.PubKey): 1, - NewVertex(node2.PubKey): 2, + Vertex(node1.PubKeyBytes): 1, + Vertex(node2.PubKeyBytes): 2, } // Exactly two notifications should be sent, each corresponding to the @@ -738,18 +751,18 @@ func TestNotificationCancellation(t *testing.T) { ntfnClient.Cancel() edge := &channeldb.ChannelEdgeInfo{ - ChannelID: chanID.ToUint64(), - NodeKey1: node1.PubKey, - NodeKey2: node2.PubKey, - BitcoinKey1: bitcoinKey1, - BitcoinKey2: bitcoinKey2, + ChannelID: chanID.ToUint64(), + NodeKey1Bytes: node1.PubKeyBytes, + NodeKey2Bytes: node2.PubKeyBytes, AuthProof: &channeldb.ChannelAuthProof{ - NodeSig1: testSig, - NodeSig2: testSig, - BitcoinSig1: testSig, - BitcoinSig2: testSig, + NodeSig1Bytes: testSig.Serialize(), + NodeSig2Bytes: testSig.Serialize(), + BitcoinSig1Bytes: testSig.Serialize(), + BitcoinSig2Bytes: testSig.Serialize(), }, } + copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) + copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) if err := ctx.router.AddEdge(edge); err != nil { t.Fatalf("unable to add edge: %v", err) } @@ -819,18 +832,18 @@ func TestChannelCloseNotification(t *testing.T) { // Finally, to conclude our test set up, we'll create a channel // announcement to announce the created channel between the two nodes. edge := &channeldb.ChannelEdgeInfo{ - ChannelID: chanID.ToUint64(), - NodeKey1: node1.PubKey, - NodeKey2: node2.PubKey, - BitcoinKey1: bitcoinKey1, - BitcoinKey2: bitcoinKey2, + ChannelID: chanID.ToUint64(), + NodeKey1Bytes: node1.PubKeyBytes, + NodeKey2Bytes: node2.PubKeyBytes, AuthProof: &channeldb.ChannelAuthProof{ - NodeSig1: testSig, - NodeSig2: testSig, - BitcoinSig1: testSig, - BitcoinSig2: testSig, + NodeSig1Bytes: testSig.Serialize(), + NodeSig2Bytes: testSig.Serialize(), + BitcoinSig1Bytes: testSig.Serialize(), + BitcoinSig2Bytes: testSig.Serialize(), }, } + copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) + copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) if err := ctx.router.AddEdge(edge); err != nil { t.Fatalf("unable to add edge: %v", err) } diff --git a/routing/pathfind.go b/routing/pathfind.go index fce5ceee..f4fdde51 100644 --- a/routing/pathfind.go +++ b/routing/pathfind.go @@ -1,6 +1,7 @@ package routing import ( + "bytes" "encoding/binary" "fmt" "math" @@ -148,7 +149,7 @@ type Route struct { // target node is not found in the route, then false is returned. func (r *Route) nextHopVertex(n *btcec.PublicKey) (Vertex, bool) { hop, ok := r.nextHopMap[NewVertex(n)] - return NewVertex(hop.Node.PubKey), ok + return Vertex(hop.Node.PubKeyBytes), ok } // nextHopChannel returns the uint64 channel ID of the next hop after the @@ -259,7 +260,7 @@ func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex Vertex, // First, we'll update both the node and channel index, to // indicate that this Vertex, and outgoing channel link are // present within this route. - v := NewVertex(edge.Node.PubKey) + v := Vertex(edge.Node.PubKeyBytes) route.nodeIndex[v] = struct{}{} route.chanIndex[edge.ChannelID] = struct{}{} @@ -314,7 +315,6 @@ func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex Vertex, AmtToForward: amtToForward, Fee: fee, } - edge.Node.PubKey.Curve = nil route.TotalFees += nextHop.Fee @@ -361,7 +361,7 @@ func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex Vertex, // We'll then make a second run through our route in order to set up // our prev hop mapping. for _, hop := range route.Hops { - vertex := NewVertex(hop.Channel.Node.PubKey) + vertex := Vertex(hop.Channel.Node.PubKeyBytes) route.prevHopMap[vertex] = hop.Channel } @@ -393,7 +393,7 @@ func (v Vertex) String() string { // directional edge with the node's ID in the opposite direction. type edgeWithPrev struct { edge *ChannelHop - prevNode *btcec.PublicKey + prevNode [33]byte } // edgeWeight computes the weight of an edge. This value is used when searching @@ -440,7 +440,7 @@ func findPath(tx *bolt.Tx, graph *channeldb.ChannelGraph, if err := graph.ForEachNode(tx, func(_ *bolt.Tx, node *channeldb.LightningNode) error { // TODO(roasbeef): with larger graph can just use disk seeks // with a visited map - distance[NewVertex(node.PubKey)] = nodeWithDist{ + distance[Vertex(node.PubKeyBytes)] = nodeWithDist{ dist: infinity, node: node, } @@ -455,7 +455,7 @@ func findPath(tx *bolt.Tx, graph *channeldb.ChannelGraph, // To start, we add the source of our path finding attempt to the // distance map with with a distance of 0. This indicates our starting // point in the graph traversal. - sourceVertex := NewVertex(sourceNode.PubKey) + sourceVertex := Vertex(sourceNode.PubKeyBytes) distance[sourceVertex] = nodeWithDist{ dist: 0, node: sourceNode, @@ -465,6 +465,8 @@ func findPath(tx *bolt.Tx, graph *channeldb.ChannelGraph, // heap. heap.Push(&nodeHeap, distance[sourceVertex]) + targetBytes := target.SerializeCompressed() + // We'll use this map as a series of "previous" hop pointers. So to get // to `Vertex` we'll take the edge that it's mapped to within `prev`. prev := make(map[Vertex]edgeWithPrev) @@ -477,19 +479,19 @@ func findPath(tx *bolt.Tx, graph *channeldb.ChannelGraph, // If we've reached our target (or we don't have any outgoing // edges), then we're done here and can exit the graph // traversal early. - if bestNode.PubKey.IsEqual(target) { + if bytes.Equal(bestNode.PubKeyBytes[:], targetBytes) { break } // Now that we've found the next potential step to take we'll // examine all the outgoing edge (channels) from this node to // further our graph traversal. - pivot := NewVertex(bestNode.PubKey) + pivot := Vertex(bestNode.PubKeyBytes) err := bestNode.ForEachChannel(tx, func(tx *bolt.Tx, edgeInfo *channeldb.ChannelEdgeInfo, outEdge, inEdge *channeldb.ChannelEdgePolicy) error { - v := NewVertex(outEdge.Node.PubKey) + v := Vertex(outEdge.Node.PubKeyBytes) // If the outgoing edge is currently disabled, then // we'll stop here, as we shouldn't attempt to route @@ -538,7 +540,7 @@ func findPath(tx *bolt.Tx, graph *channeldb.ChannelGraph, ChannelEdgePolicy: outEdge, Capacity: edgeInfo.Capacity, }, - prevNode: bestNode.PubKey, + prevNode: bestNode.PubKeyBytes, } // Add this new node to our heap as we'd like @@ -573,9 +575,8 @@ func findPath(tx *bolt.Tx, graph *channeldb.ChannelGraph, // backwards from this hop via the prev pointer for this hop // within the prevHop map. pathEdges = append(pathEdges, prev[prevNode].edge) - prev[prevNode].edge.Node.PubKey.Curve = nil - prevNode = NewVertex(prev[prevNode].prevNode) + prevNode = Vertex(prev[prevNode].prevNode) } // The route is invalid if it spans more than 20 hops. The current @@ -648,8 +649,6 @@ func findPaths(tx *bolt.Tx, graph *channeldb.ChannelGraph, shortestPaths = append(shortestPaths, firstPath) - source.PubKey.Curve = nil - // While we still have candidate paths to explore we'll keep exploring // the sub-graphs created to find the next k-th shortest path. for k := 1; k < 100; k++ { @@ -688,12 +687,12 @@ func findPaths(tx *bolt.Tx, graph *channeldb.ChannelGraph, // Next we'll remove all entries in the root path that // aren't the current spur node from the graph. for _, hop := range rootPath { - node := hop.Node.PubKey - if node.IsEqual(spurNode.PubKey) { + node := hop.Node.PubKeyBytes + if node == spurNode.PubKeyBytes { continue } - ignoredVertexes[NewVertex(node)] = struct{}{} + ignoredVertexes[Vertex(node)] = struct{}{} } // With the edges that are part of our root path, and diff --git a/routing/pathfind_test.go b/routing/pathfind_test.go index 68658686..b77c08ad 100644 --- a/routing/pathfind_test.go +++ b/routing/pathfind_test.go @@ -53,10 +53,10 @@ var ( _, _ = testSig.S.SetString("18801056069249825825291287104931333862866033135609736119018462340006816851118", 10) testAuthProof = channeldb.ChannelAuthProof{ - NodeSig1: testSig, - NodeSig2: testSig, - BitcoinSig1: testSig, - BitcoinSig2: testSig, + NodeSig1Bytes: testSig.Serialize(), + NodeSig2Bytes: testSig.Serialize(), + BitcoinSig1Bytes: testSig.Serialize(), + BitcoinSig2Bytes: testSig.Serialize(), } ) @@ -165,20 +165,16 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err if err != nil { return nil, nil, nil, err } - pub, err := btcec.ParsePubKey(pubBytes, btcec.S256()) - if err != nil { - return nil, nil, nil, err - } dbNode := &channeldb.LightningNode{ HaveNodeAnnouncement: true, - AuthSig: testSig, + AuthSigBytes: testSig.Serialize(), LastUpdate: time.Now(), Addresses: testAddrs, - PubKey: pub, Alias: node.Alias, Features: testFeatures, } + copy(dbNode.PubKeyBytes[:], pubBytes) // We require all aliases within the graph to be unique for our // tests. @@ -187,6 +183,11 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err "must be unique!") } + pub, err := btcec.ParsePubKey(pubBytes, btcec.S256()) + if err != nil { + return nil, nil, nil, err + } + // If the alias is unique, then add the node to the // alias map for easy lookup. aliasMap[node.Alias] = pub @@ -228,19 +229,11 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err if err != nil { return nil, nil, nil, err } - node1Pub, err := btcec.ParsePubKey(node1Bytes, btcec.S256()) - if err != nil { - return nil, nil, nil, err - } node2Bytes, err := hex.DecodeString(edge.Node2) if err != nil { return nil, nil, nil, err } - node2Pub, err := btcec.ParsePubKey(node2Bytes, btcec.S256()) - if err != nil { - return nil, nil, nil, err - } fundingTXID := strings.Split(edge.ChannelPoint, ":")[0] txidBytes, err := chainhash.NewHashFromStr(fundingTXID) @@ -256,21 +249,23 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err // nodes. edgeInfo := channeldb.ChannelEdgeInfo{ ChannelID: edge.ChannelID, - NodeKey1: node1Pub, - NodeKey2: node2Pub, - BitcoinKey1: node1Pub, - BitcoinKey2: node2Pub, AuthProof: &testAuthProof, ChannelPoint: fundingPoint, Capacity: btcutil.Amount(edge.Capacity), } + + copy(edgeInfo.NodeKey1Bytes[:], node1Bytes) + copy(edgeInfo.NodeKey2Bytes[:], node2Bytes) + copy(edgeInfo.BitcoinKey1Bytes[:], node1Bytes) + copy(edgeInfo.BitcoinKey2Bytes[:], node2Bytes) + err = graph.AddChannelEdge(&edgeInfo) if err != nil && err != channeldb.ErrEdgeAlreadyExist { return nil, nil, nil, err } edgePolicy := &channeldb.ChannelEdgePolicy{ - Signature: testSig, + SigBytes: testSig.Serialize(), Flags: lnwire.ChanUpdateFlag(edge.Flags), ChannelID: edge.ChannelID, LastUpdate: time.Now(), @@ -300,7 +295,7 @@ func TestBasicGraphPathFinding(t *testing.T) { if err != nil { t.Fatalf("unable to fetch source node: %v", err) } - sourceVertex := NewVertex(sourceNode.PubKey) + sourceVertex := Vertex(sourceNode.PubKeyBytes) ignoredEdges := make(map[uint64]struct{}) ignoredVertexes := make(map[Vertex]struct{}) @@ -342,13 +337,17 @@ func TestBasicGraphPathFinding(t *testing.T) { } // The first hop in the path should be an edge from roasbeef to goku. - if !route.Hops[0].Channel.Node.PubKey.IsEqual(aliases["songoku"]) { + if !bytes.Equal(route.Hops[0].Channel.Node.PubKeyBytes[:], + aliases["songoku"].SerializeCompressed()) { + t.Fatalf("first hop should be goku, is instead: %v", route.Hops[0].Channel.Node.Alias) } // The second hop should be from goku to sophon. - if !route.Hops[1].Channel.Node.PubKey.IsEqual(aliases["sophon"]) { + if !bytes.Equal(route.Hops[1].Channel.Node.PubKeyBytes[:], + aliases["sophon"].SerializeCompressed()) { + t.Fatalf("second hop should be sophon, is instead: %v", route.Hops[0].Channel.Node.Alias) } @@ -833,7 +832,7 @@ func TestPathFindSpecExample(t *testing.T) { if err != nil { t.Fatalf("unable to retrieve source node: %v", err) } - if !source.PubKey.IsEqual(alice) { + if !bytes.Equal(source.PubKeyBytes[:], alice.SerializeCompressed()) { t.Fatalf("source node not set") } diff --git a/routing/router.go b/routing/router.go index 6dc5afdf..4e92ff15 100644 --- a/routing/router.go +++ b/routing/router.go @@ -814,7 +814,7 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { // attack by node announcements, we will ignore such nodes. If // we do know about this node, check that this update brings // info newer than what we already have. - lastUpdate, exists, err := r.cfg.Graph.HasLightningNode(msg.PubKey) + lastUpdate, exists, err := r.cfg.Graph.HasLightningNode(msg.PubKeyBytes) if err != nil { return errors.Errorf("unable to query for the "+ "existence of node: %v", err) @@ -822,7 +822,7 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { if !exists { return newErrf(ErrIgnored, "Ignoring node announcement"+ " for node not found in channel graph (%x)", - msg.PubKey.SerializeCompressed()) + msg.PubKeyBytes) } // If we've reached this point then we're aware of the vertex @@ -833,16 +833,15 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { lastUpdate.Equal(msg.LastUpdate) { return newErrf(ErrOutdated, "Ignoring outdated "+ - "announcement for %x", msg.PubKey.SerializeCompressed()) + "announcement for %x", msg.PubKeyBytes) } if err := r.cfg.Graph.AddLightningNode(msg); err != nil { return errors.Errorf("unable to add node %v to the "+ - "graph: %v", msg.PubKey.SerializeCompressed(), err) + "graph: %v", msg.PubKeyBytes, err) } - log.Infof("Updated vertex data for node=%x", - msg.PubKey.SerializeCompressed()) + log.Infof("Updated vertex data for node=%x", msg.PubKeyBytes) case *channeldb.ChannelEdgeInfo: // Prior to processing the announcement we first check if we @@ -859,30 +858,28 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { // Query the database for the existence of the two nodes in this // channel. If not found, add a partial node to the database, // containing only the node keys. - _, exists, _ = r.cfg.Graph.HasLightningNode(msg.NodeKey1) + _, exists, _ = r.cfg.Graph.HasLightningNode(msg.NodeKey1Bytes) if !exists { node1 := &channeldb.LightningNode{ - PubKey: msg.NodeKey1, + PubKeyBytes: msg.NodeKey1Bytes, HaveNodeAnnouncement: false, } err := r.cfg.Graph.AddLightningNode(node1) if err != nil { return errors.Errorf("unable to add node %v to"+ - " the graph: %v", - node1.PubKey.SerializeCompressed(), err) + " the graph: %v", node1.PubKeyBytes, err) } } - _, exists, _ = r.cfg.Graph.HasLightningNode(msg.NodeKey2) + _, exists, _ = r.cfg.Graph.HasLightningNode(msg.NodeKey2Bytes) if !exists { node2 := &channeldb.LightningNode{ - PubKey: msg.NodeKey2, + PubKeyBytes: msg.NodeKey2Bytes, HaveNodeAnnouncement: false, } err := r.cfg.Graph.AddLightningNode(node2) if err != nil { return errors.Errorf("unable to add node %v to"+ - " the graph: %v", - node2.PubKey.SerializeCompressed(), err) + " the graph: %v", node2.PubKeyBytes, err) } } @@ -911,8 +908,7 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { // edge bitcoin keys and channel value corresponds to the // reality. _, witnessOutput, err := lnwallet.GenFundingPkScript( - msg.BitcoinKey1.SerializeCompressed(), - msg.BitcoinKey2.SerializeCompressed(), + msg.BitcoinKey1Bytes[:], msg.BitcoinKey2Bytes[:], chanUtxo.Value, ) if err != nil { @@ -942,8 +938,7 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { log.Infof("New channel discovered! Link "+ "connects %x and %x with ChannelPoint(%v): "+ "chan_id=%v, capacity=%v", - msg.NodeKey1.SerializeCompressed(), - msg.NodeKey2.SerializeCompressed(), + msg.NodeKey1Bytes, msg.NodeKey2Bytes, fundingPoint, msg.ChannelID, msg.Capacity) // As a new edge has been added to the channel graph, we'll @@ -1183,7 +1178,8 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey, // We can short circuit the routing by opportunistically checking to // see if the target vertex event exists in the current graph. - if _, exists, err := r.cfg.Graph.HasLightningNode(target); err != nil { + targetVertex := NewVertex(target) + if _, exists, err := r.cfg.Graph.HasLightningNode(targetVertex); err != nil { return nil, err } else if !exists { log.Debugf("Target %x is not in known graph", dest) @@ -1221,7 +1217,7 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey, // aren't able to support the total satoshis flow once fees have been // factored in. validRoutes := make([]*Route, 0, len(shortestPaths)) - sourceVertex := NewVertex(r.selfNode.PubKey) + sourceVertex := Vertex(r.selfNode.PubKeyBytes) for _, path := range shortestPaths { // Attempt to make the path into a route. We snip off the first // hop in the path as it contains a "self-hop" that is inserted @@ -1289,10 +1285,14 @@ func generateSphinxPacket(route *Route, paymentHash []byte) ([]byte, // We create a new instance of the public key to avoid possibly // mutating the curve parameters, which are unset in a higher // level in order to avoid spamming the logs. + nodePub, err := hop.Channel.Node.PubKey() + if err != nil { + return nil, nil, err + } pub := btcec.PublicKey{ Curve: btcec.S256(), - X: hop.Channel.Node.PubKey.X, - Y: hop.Channel.Node.PubKey.Y, + X: nodePub.X, + Y: nodePub.Y, } nodes[i] = &pub } @@ -1453,7 +1453,7 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route // Attempt to send this payment through the network to complete // the payment. If this attempt fails, then we'll continue on // to the next available route. - firstHop := route.Hops[0].Channel.Node.PubKey + firstHop := route.Hops[0].Channel.Node.PubKeyBytes preImage, sendError = r.cfg.SendToSwitch(firstHop, htlcAdd, circuit) if sendError != nil { @@ -1693,7 +1693,7 @@ func (r *ChannelRouter) applyChannelUpdate(msg *lnwire.ChannelUpdate) error { } err := r.UpdateEdge(&channeldb.ChannelEdgePolicy{ - Signature: msg.Signature, + SigBytes: msg.Signature.ToSignatureBytes(), ChannelID: msg.ShortChannelID.ToUint64(), LastUpdate: time.Unix(int64(msg.Timestamp), 0), Flags: msg.Flags, diff --git a/routing/router_test.go b/routing/router_test.go index c3b43098..48193c96 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -42,7 +42,7 @@ func (c *testCtx) RestartRouter() error { Graph: c.graph, Chain: c.chain, ChainView: c.chainView, - SendToSwitch: func(_ *btcec.PublicKey, + SendToSwitch: func(_ [33]byte, _ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) { return [32]byte{}, nil }, @@ -117,8 +117,9 @@ func createTestCtx(startingHeight uint32, testGraph ...string) (*testCtx, func() Graph: graph, Chain: chain, ChainView: chainView, - SendToSwitch: func(_ *btcec.PublicKey, - _ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) { + SendToSwitch: func(_ [33]byte, _ *lnwire.UpdateAddHTLC, + _ *sphinx.Circuit) ([32]byte, error) { + return [32]byte{}, nil }, ChannelPruneExpiry: time.Hour * 24, @@ -230,12 +231,16 @@ func TestSendPaymentRouteFailureFallback(t *testing.T) { // router's configuration to ignore the path that has luo ji as the // first hop. This should force the router to instead take the // available two hop path (through satoshi). - ctx.router.cfg.SendToSwitch = func(n *btcec.PublicKey, + ctx.router.cfg.SendToSwitch = func(n [33]byte, _ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) { - if ctx.aliases["luoji"].IsEqual(n) { + if bytes.Equal(ctx.aliases["luoji"].SerializeCompressed(), n[:]) { + pub, err := sourceNode.PubKey() + if err != nil { + return preImage, err + } return [32]byte{}, &htlcswitch.ForwardingError{ - ErrorSource: sourceNode.PubKey, + ErrorSource: pub, // TODO(roasbeef): temp node failure should be? FailureMessage: &lnwire.FailTemporaryChannelFailure{}, } @@ -301,21 +306,26 @@ func TestSendPaymentErrorPathPruning(t *testing.T) { t.Fatalf("unable to fetch source node: %v", err) } + sourcePub, err := sourceNode.PubKey() + if err != nil { + t.Fatalf("unable to fetch source node pub: %v", err) + } + // First, we'll modify the SendToSwitch method to return an error // indicating that the channel from roasbeef to luoji is not operable // with an UnknownNextPeer. // // TODO(roasbeef): filtering should be intelligent enough so just not // go through satoshi at all at this point. - ctx.router.cfg.SendToSwitch = func(n *btcec.PublicKey, + ctx.router.cfg.SendToSwitch = func(n [33]byte, _ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) { - if ctx.aliases["luoji"].IsEqual(n) { + if bytes.Equal(ctx.aliases["luoji"].SerializeCompressed(), n[:]) { // We'll first simulate an error from the first // outgoing link to simulate the channel from luo ji to // roasbeef not having enough capacity. return [32]byte{}, &htlcswitch.ForwardingError{ - ErrorSource: sourceNode.PubKey, + ErrorSource: sourcePub, FailureMessage: &lnwire.FailTemporaryChannelFailure{}, } } @@ -323,7 +333,7 @@ func TestSendPaymentErrorPathPruning(t *testing.T) { // Next, we'll create an error from satoshi to indicate // that the luoji node is not longer online, which should // prune out the rest of the routes. - if ctx.aliases["satoshi"].IsEqual(n) { + if bytes.Equal(ctx.aliases["satoshi"].SerializeCompressed(), n[:]) { return [32]byte{}, &htlcswitch.ForwardingError{ ErrorSource: ctx.aliases["satoshi"], FailureMessage: &lnwire.FailUnknownNextPeer{}, @@ -353,12 +363,12 @@ func TestSendPaymentErrorPathPruning(t *testing.T) { // Next, we'll modify the SendToSwitch method to indicate that luo ji // wasn't originally online. This should also halt the send all // together as all paths contain luoji and he can't be reached. - ctx.router.cfg.SendToSwitch = func(n *btcec.PublicKey, + ctx.router.cfg.SendToSwitch = func(n [33]byte, _ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) { - if ctx.aliases["luoji"].IsEqual(n) { + if bytes.Equal(ctx.aliases["luoji"].SerializeCompressed(), n[:]) { return [32]byte{}, &htlcswitch.ForwardingError{ - ErrorSource: sourceNode.PubKey, + ErrorSource: sourcePub, FailureMessage: &lnwire.FailUnknownNextPeer{}, } } @@ -380,14 +390,14 @@ func TestSendPaymentErrorPathPruning(t *testing.T) { // Finally, we'll modify the SendToSwitch function to indicate that the // roasbeef -> luoji channel has insufficient capacity. - ctx.router.cfg.SendToSwitch = func(n *btcec.PublicKey, + ctx.router.cfg.SendToSwitch = func(n [33]byte, _ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) { - if ctx.aliases["luoji"].IsEqual(n) { + if bytes.Equal(ctx.aliases["luoji"].SerializeCompressed(), n[:]) { // We'll first simulate an error from the first // outgoing link to simulate the channel from luo ji to // roasbeef not having enough capacity. return [32]byte{}, &htlcswitch.ForwardingError{ - ErrorSource: sourceNode.PubKey, + ErrorSource: sourcePub, FailureMessage: &lnwire.FailTemporaryChannelFailure{}, } } @@ -457,13 +467,13 @@ func TestAddProof(t *testing.T) { // After utxo was recreated adding the edge without the proof. edge := &channeldb.ChannelEdgeInfo{ - ChannelID: chanID.ToUint64(), - NodeKey1: copyPubKey(node1.PubKey), - NodeKey2: copyPubKey(node2.PubKey), - BitcoinKey1: copyPubKey(bitcoinKey1), - BitcoinKey2: copyPubKey(bitcoinKey2), - AuthProof: nil, + ChannelID: chanID.ToUint64(), + NodeKey1Bytes: node1.PubKeyBytes, + NodeKey2Bytes: node2.PubKeyBytes, + AuthProof: nil, } + copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) + copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) if err := ctx.router.AddEdge(edge); err != nil { t.Fatalf("unable to add edge: %v", err) @@ -495,16 +505,17 @@ func TestIgnoreNodeAnnouncement(t *testing.T) { t.Fatalf("unable to create router: %v", err) } + pub := priv1.PubKey() node := &channeldb.LightningNode{ HaveNodeAnnouncement: true, LastUpdate: time.Unix(123, 0), Addresses: testAddrs, - PubKey: copyPubKey(priv1.PubKey()), Color: color.RGBA{1, 2, 3, 0}, Alias: "node11", - AuthSig: testSig, + AuthSigBytes: testSig.Serialize(), Features: testFeatures, } + copy(node.PubKeyBytes[:], pub.SerializeCompressed()) err = ctx.router.AddNode(node) if !IsError(err, ErrIgnored) { @@ -527,15 +538,21 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { t.Fatalf("unable to create router: %v", err) } + var pub1 [33]byte + copy(pub1[:], priv1.PubKey().SerializeCompressed()) + + var pub2 [33]byte + copy(pub2[:], priv2.PubKey().SerializeCompressed()) + // The two nodes we are about to add should not exist yet. - _, exists1, err := ctx.graph.HasLightningNode(priv1.PubKey()) + _, exists1, err := ctx.graph.HasLightningNode(pub1) if err != nil { t.Fatalf("unable to query graph: %v", err) } if exists1 { t.Fatalf("node already existed") } - _, exists2, err := ctx.graph.HasLightningNode(priv2.PubKey()) + _, exists2, err := ctx.graph.HasLightningNode(pub2) if err != nil { t.Fatalf("unable to query graph: %v", err) } @@ -558,12 +575,12 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight) edge := &channeldb.ChannelEdgeInfo{ - ChannelID: chanID.ToUint64(), - NodeKey1: copyPubKey(priv1.PubKey()), - NodeKey2: copyPubKey(priv2.PubKey()), - BitcoinKey1: copyPubKey(bitcoinKey1), - BitcoinKey2: copyPubKey(bitcoinKey2), - AuthProof: nil, + ChannelID: chanID.ToUint64(), + NodeKey1Bytes: pub1, + NodeKey2Bytes: pub2, + BitcoinKey1Bytes: pub1, + BitcoinKey2Bytes: pub2, + AuthProof: nil, } if err := ctx.router.AddEdge(edge); err != nil { t.Fatalf("expected to be able to add edge to the channel graph,"+ @@ -573,7 +590,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { // We must add the edge policy to be able to use the edge for route // finding. edgePolicy := &channeldb.ChannelEdgePolicy{ - Signature: testSig, + SigBytes: testSig.Serialize(), ChannelID: edge.ChannelID, LastUpdate: time.Now(), TimeLockDelta: 10, @@ -589,7 +606,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { // Create edge in the other direction as well. edgePolicy = &channeldb.ChannelEdgePolicy{ - Signature: testSig, + SigBytes: testSig.Serialize(), ChannelID: edge.ChannelID, LastUpdate: time.Now(), TimeLockDelta: 10, @@ -605,14 +622,14 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { // After adding the edge between the two previously unknown nodes, they // should have been added to the graph. - _, exists1, err = ctx.graph.HasLightningNode(priv1.PubKey()) + _, exists1, err = ctx.graph.HasLightningNode(pub1) if err != nil { t.Fatalf("unable to query graph: %v", err) } if !exists1 { t.Fatalf("node1 was not added to the graph") } - _, exists2, err = ctx.graph.HasLightningNode(priv2.PubKey()) + _, exists2, err = ctx.graph.HasLightningNode(pub2) if err != nil { t.Fatalf("unable to query graph: %v", err) } @@ -656,20 +673,20 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight) edge = &channeldb.ChannelEdgeInfo{ - ChannelID: chanID.ToUint64(), - NodeKey1: pubKey1, - NodeKey2: pubKey2, - BitcoinKey1: pubKey1, - BitcoinKey2: pubKey2, - AuthProof: nil, + ChannelID: chanID.ToUint64(), + AuthProof: nil, } + copy(edge.NodeKey1Bytes[:], node1Bytes) + copy(edge.NodeKey2Bytes[:], node2Bytes) + copy(edge.BitcoinKey1Bytes[:], node1Bytes) + copy(edge.BitcoinKey2Bytes[:], node2Bytes) if err := ctx.router.AddEdge(edge); err != nil { t.Fatalf("unable to add edge to the channel graph: %v.", err) } edgePolicy = &channeldb.ChannelEdgePolicy{ - Signature: testSig, + SigBytes: testSig.Serialize(), ChannelID: edge.ChannelID, LastUpdate: time.Now(), TimeLockDelta: 10, @@ -684,7 +701,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { } edgePolicy = &channeldb.ChannelEdgePolicy{ - Signature: testSig, + SigBytes: testSig.Serialize(), ChannelID: edge.ChannelID, LastUpdate: time.Now(), TimeLockDelta: 10, @@ -716,12 +733,12 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { HaveNodeAnnouncement: true, LastUpdate: time.Unix(123, 0), Addresses: testAddrs, - PubKey: copyPubKey(priv1.PubKey()), Color: color.RGBA{1, 2, 3, 0}, Alias: "node11", - AuthSig: testSig, + AuthSigBytes: testSig.Serialize(), Features: testFeatures, } + copy(n1.PubKeyBytes[:], priv1.PubKey().SerializeCompressed()) if err := ctx.router.AddNode(n1); err != nil { t.Fatalf("could not add node: %v", err) @@ -731,12 +748,12 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { HaveNodeAnnouncement: true, LastUpdate: time.Unix(123, 0), Addresses: testAddrs, - PubKey: copyPubKey(priv2.PubKey()), Color: color.RGBA{1, 2, 3, 0}, Alias: "node22", - AuthSig: testSig, + AuthSigBytes: testSig.Serialize(), Features: testFeatures, } + copy(n2.PubKeyBytes[:], priv2.PubKey().SerializeCompressed()) if err := ctx.router.AddNode(n2); err != nil { t.Fatalf("could not add node: %v", err) @@ -865,36 +882,36 @@ func TestWakeUpOnStaleBranch(t *testing.T) { } edge1 := &channeldb.ChannelEdgeInfo{ - ChannelID: chanID1, - NodeKey1: copyPubKey(node1.PubKey), - NodeKey2: copyPubKey(node2.PubKey), - BitcoinKey1: copyPubKey(bitcoinKey1), - BitcoinKey2: copyPubKey(bitcoinKey2), + ChannelID: chanID1, + NodeKey1Bytes: node1.PubKeyBytes, + NodeKey2Bytes: node2.PubKeyBytes, AuthProof: &channeldb.ChannelAuthProof{ - NodeSig1: testSig, - NodeSig2: testSig, - BitcoinSig1: testSig, - BitcoinSig2: testSig, + NodeSig1Bytes: testSig.Serialize(), + NodeSig2Bytes: testSig.Serialize(), + BitcoinSig1Bytes: testSig.Serialize(), + BitcoinSig2Bytes: testSig.Serialize(), }, } + copy(edge1.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) + copy(edge1.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) if err := ctx.router.AddEdge(edge1); err != nil { t.Fatalf("unable to add edge: %v", err) } edge2 := &channeldb.ChannelEdgeInfo{ - ChannelID: chanID2, - NodeKey1: copyPubKey(node1.PubKey), - NodeKey2: copyPubKey(node2.PubKey), - BitcoinKey1: copyPubKey(bitcoinKey1), - BitcoinKey2: copyPubKey(bitcoinKey2), + ChannelID: chanID2, + NodeKey1Bytes: node1.PubKeyBytes, + NodeKey2Bytes: node2.PubKeyBytes, AuthProof: &channeldb.ChannelAuthProof{ - NodeSig1: testSig, - NodeSig2: testSig, - BitcoinSig1: testSig, - BitcoinSig2: testSig, + NodeSig1Bytes: testSig.Serialize(), + NodeSig2Bytes: testSig.Serialize(), + BitcoinSig1Bytes: testSig.Serialize(), + BitcoinSig2Bytes: testSig.Serialize(), }, } + copy(edge2.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) + copy(edge2.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) if err := ctx.router.AddEdge(edge2); err != nil { t.Fatalf("unable to add edge: %v", err) @@ -940,7 +957,7 @@ func TestWakeUpOnStaleBranch(t *testing.T) { Graph: ctx.graph, Chain: ctx.chain, ChainView: ctx.chainView, - SendToSwitch: func(_ *btcec.PublicKey, + SendToSwitch: func(_ [33]byte, _ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) { return [32]byte{}, nil }, @@ -1067,36 +1084,40 @@ func TestDisconnectedBlocks(t *testing.T) { } edge1 := &channeldb.ChannelEdgeInfo{ - ChannelID: chanID1, - NodeKey1: copyPubKey(node1.PubKey), - NodeKey2: copyPubKey(node2.PubKey), - BitcoinKey1: copyPubKey(bitcoinKey1), - BitcoinKey2: copyPubKey(bitcoinKey2), + ChannelID: chanID1, + NodeKey1Bytes: node1.PubKeyBytes, + NodeKey2Bytes: node2.PubKeyBytes, + BitcoinKey1Bytes: node1.PubKeyBytes, + BitcoinKey2Bytes: node2.PubKeyBytes, AuthProof: &channeldb.ChannelAuthProof{ - NodeSig1: testSig, - NodeSig2: testSig, - BitcoinSig1: testSig, - BitcoinSig2: testSig, + NodeSig1Bytes: testSig.Serialize(), + NodeSig2Bytes: testSig.Serialize(), + BitcoinSig1Bytes: testSig.Serialize(), + BitcoinSig2Bytes: testSig.Serialize(), }, } + copy(edge1.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) + copy(edge1.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) if err := ctx.router.AddEdge(edge1); err != nil { t.Fatalf("unable to add edge: %v", err) } edge2 := &channeldb.ChannelEdgeInfo{ - ChannelID: chanID2, - NodeKey1: copyPubKey(node1.PubKey), - NodeKey2: copyPubKey(node2.PubKey), - BitcoinKey1: copyPubKey(bitcoinKey1), - BitcoinKey2: copyPubKey(bitcoinKey2), + ChannelID: chanID2, + NodeKey1Bytes: node1.PubKeyBytes, + NodeKey2Bytes: node2.PubKeyBytes, + BitcoinKey1Bytes: node1.PubKeyBytes, + BitcoinKey2Bytes: node2.PubKeyBytes, AuthProof: &channeldb.ChannelAuthProof{ - NodeSig1: testSig, - NodeSig2: testSig, - BitcoinSig1: testSig, - BitcoinSig2: testSig, + NodeSig1Bytes: testSig.Serialize(), + NodeSig2Bytes: testSig.Serialize(), + BitcoinSig1Bytes: testSig.Serialize(), + BitcoinSig2Bytes: testSig.Serialize(), }, } + copy(edge2.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) + copy(edge2.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) if err := ctx.router.AddEdge(edge2); err != nil { t.Fatalf("unable to add edge: %v", err) @@ -1207,18 +1228,18 @@ func TestRouterChansClosedOfflinePruneGraph(t *testing.T) { t.Fatalf("unable to create test node: %v", err) } edge1 := &channeldb.ChannelEdgeInfo{ - ChannelID: chanID1.ToUint64(), - NodeKey1: copyPubKey(node1.PubKey), - NodeKey2: copyPubKey(node2.PubKey), - BitcoinKey1: copyPubKey(bitcoinKey1), - BitcoinKey2: copyPubKey(bitcoinKey2), + ChannelID: chanID1.ToUint64(), + NodeKey1Bytes: node1.PubKeyBytes, + NodeKey2Bytes: node2.PubKeyBytes, AuthProof: &channeldb.ChannelAuthProof{ - NodeSig1: testSig, - NodeSig2: testSig, - BitcoinSig1: testSig, - BitcoinSig2: testSig, + NodeSig1Bytes: testSig.Serialize(), + NodeSig2Bytes: testSig.Serialize(), + BitcoinSig1Bytes: testSig.Serialize(), + BitcoinSig2Bytes: testSig.Serialize(), }, } + copy(edge1.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) + copy(edge1.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) if err := ctx.router.AddEdge(edge1); err != nil { t.Fatalf("unable to add edge: %v", err) } diff --git a/routing/validation_barrier.go b/routing/validation_barrier.go index b33bb457..7878099e 100644 --- a/routing/validation_barrier.go +++ b/routing/validation_barrier.go @@ -104,8 +104,8 @@ func (v *ValidationBarrier) InitJobDependencies(job interface{}) { v.chanAnnFinSignal[msg.ShortChannelID] = annFinCond v.chanEdgeDependencies[msg.ShortChannelID] = annFinCond - v.nodeAnnDependencies[NewVertex(msg.NodeID1)] = annFinCond - v.nodeAnnDependencies[NewVertex(msg.NodeID2)] = annFinCond + v.nodeAnnDependencies[Vertex(msg.NodeID1)] = annFinCond + v.nodeAnnDependencies[Vertex(msg.NodeID2)] = annFinCond } case *channeldb.ChannelEdgeInfo: @@ -116,8 +116,8 @@ func (v *ValidationBarrier) InitJobDependencies(job interface{}) { v.chanAnnFinSignal[shortID] = annFinCond v.chanEdgeDependencies[shortID] = annFinCond - v.nodeAnnDependencies[NewVertex(msg.NodeKey1)] = annFinCond - v.nodeAnnDependencies[NewVertex(msg.NodeKey2)] = annFinCond + v.nodeAnnDependencies[Vertex(msg.NodeKey1)] = annFinCond + v.nodeAnnDependencies[Vertex(msg.NodeKey2)] = annFinCond } // These other types don't have any dependants, so no further @@ -127,6 +127,7 @@ func (v *ValidationBarrier) InitJobDependencies(job interface{}) { case *lnwire.ChannelUpdate: return case *lnwire.NodeAnnouncement: + // TODO(roasbeef): node ann needs to wait on existing channel updates return case *channeldb.LightningNode: return @@ -167,12 +168,12 @@ func (v *ValidationBarrier) WaitForDependants(job interface{}) { shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID) signal, ok = v.chanEdgeDependencies[shortID] case *channeldb.LightningNode: - vertex := NewVertex(msg.PubKey) + vertex := Vertex(msg.PubKey) signal, ok = v.nodeAnnDependencies[vertex] case *lnwire.ChannelUpdate: signal, ok = v.chanEdgeDependencies[msg.ShortChannelID] case *lnwire.NodeAnnouncement: - vertex := NewVertex(msg.NodeID) + vertex := Vertex(msg.NodeID) signal, ok = v.nodeAnnDependencies[vertex] // Other types of jobs can be executed immediately, so we'll just @@ -233,9 +234,9 @@ func (v *ValidationBarrier) SignalDependants(job interface{}) { // map, as if we reach this point, then all dependants have already // finished executing and we can proceed. case *channeldb.LightningNode: - delete(v.nodeAnnDependencies, NewVertex(msg.PubKey)) + delete(v.nodeAnnDependencies, Vertex(msg.PubKey)) case *lnwire.NodeAnnouncement: - delete(v.nodeAnnDependencies, NewVertex(msg.NodeID)) + delete(v.nodeAnnDependencies, Vertex(msg.NodeID)) case *lnwire.ChannelUpdate: delete(v.chanEdgeDependencies, msg.ShortChannelID) case *channeldb.ChannelEdgePolicy: From 9f0214428adc2d4f93e6008f189b2928cf1e3454 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2018 20:29:12 -0800 Subject: [PATCH 10/16] zpay32: update parsing to use new lnwire.Sig API --- zpay32/invoice.go | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/zpay32/invoice.go b/zpay32/invoice.go index 72f13461..0856d3ea 100644 --- a/zpay32/invoice.go +++ b/zpay32/invoice.go @@ -327,8 +327,8 @@ func Decode(invoice string) (*Invoice, error) { if err != nil { return nil, err } - var sigBytes [64]byte - copy(sigBytes[:], sigBase256[:64]) + var sig lnwire.Sig + copy(sig[:], sigBase256[:64]) recoveryID := sigBase256[64] // The signature is over the hrp + the data the invoice, encoded in @@ -347,8 +347,7 @@ func Decode(invoice string) (*Invoice, error) { // If the destination pubkey was provided as a tagged field, use that // to verify the signature, if not do public key recovery. if decodedInvoice.Destination != nil { - var signature *btcec.Signature - err := lnwire.DeserializeSigFromWire(&signature, sigBytes) + signature, err := sig.ToSignature() if err != nil { return nil, fmt.Errorf("unable to deserialize "+ "signature: %v", err) @@ -358,7 +357,7 @@ func Decode(invoice string) (*Invoice, error) { } } else { headerByte := recoveryID + 27 + 4 - compactSign := append([]byte{headerByte}, sigBytes[:]...) + compactSign := append([]byte{headerByte}, sig[:]...) pubkey, _, err := btcec.RecoverCompact(btcec.S256(), compactSign, hash) if err != nil { @@ -449,18 +448,18 @@ func (invoice *Invoice) Encode(signer MessageSigner) (string, error) { // From the header byte we can extract the recovery ID, and the last 64 // bytes encode the signature. recoveryID := sign[0] - 27 - 4 - var sigBytes [64]byte - copy(sigBytes[:], sign[1:]) + var sig lnwire.Sig + copy(sig[:], sign[1:]) // If the pubkey field was explicitly set, it must be set to the pubkey // used to create the signature. if invoice.Destination != nil { - var signature *btcec.Signature - err = lnwire.DeserializeSigFromWire(&signature, sigBytes) + signature, err := sig.ToSignature() if err != nil { return "", fmt.Errorf("unable to deserialize "+ "signature: %v", err) } + valid := signature.Verify(hash, invoice.Destination) if !valid { return "", fmt.Errorf("signature does not match " + @@ -469,7 +468,7 @@ func (invoice *Invoice) Encode(signer MessageSigner) (string, error) { } // Convert the signature to base32 before writing it to the buffer. - signBase32, err := bech32.ConvertBits(append(sigBytes[:], recoveryID), 8, 5, true) + signBase32, err := bech32.ConvertBits(append(sig[:], recoveryID), 8, 5, true) if err != nil { return "", err } From 22951cb36429e25ad5705f3c1aa4a6e2518f04f5 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2018 20:30:00 -0800 Subject: [PATCH 11/16] lnd: account for new lnwire.Sig API and channeldb API changes --- chancloser.go | 15 +++++----- fundingmanager.go | 63 +++++++++++++++++++++++------------------- fundingmanager_test.go | 12 ++++++-- peer.go | 25 ++++++++--------- peer_test.go | 21 ++++++-------- rpcserver.go | 15 ++++++---- server.go | 45 +++++++++++++++++++----------- 7 files changed, 110 insertions(+), 86 deletions(-) diff --git a/chancloser.go b/chancloser.go index bd518555..15f8e9b4 100644 --- a/chancloser.go +++ b/chancloser.go @@ -10,7 +10,6 @@ import ( "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" - "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcutil" @@ -410,12 +409,12 @@ func (c *channelCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message, b // transaction! We'll craft the final closing transaction so // we can broadcast it to the network. matchingSig := c.priorFeeOffers[remoteProposedFee].Signature - localSig := append( - matchingSig.Serialize(), byte(txscript.SigHashAll), - ) - remoteSig := append( - closeSignedMsg.Signature.Serialize(), byte(txscript.SigHashAll), - ) + localSigBytes := matchingSig.ToSignatureBytes() + localSig := append(localSigBytes, byte(txscript.SigHashAll)) + + remoteSigBytes := closeSignedMsg.Signature.ToSignatureBytes() + remoteSig := append(remoteSigBytes, byte(txscript.SigHashAll)) + closeTx, finalLocalBalance, err := c.cfg.channel.CompleteCooperativeClose( localSig, remoteSig, c.localDeliveryScript, c.remoteDeliveryScript, remoteProposedFee, @@ -512,7 +511,7 @@ func (c *channelCloser) proposeCloseSigned(fee btcutil.Amount) (*lnwire.ClosingS // party responds we'll be able to decide if we've agreed on fees or // not. c.lastFeeProposal = fee - parsedSig, err := btcec.ParseSignature(rawSig, btcec.S256()) + parsedSig, err := lnwire.NewSigFromRawSignature(rawSig) if err != nil { return nil, err } diff --git a/fundingmanager.go b/fundingmanager.go index 6b8c6c4d..434e7711 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -1062,14 +1062,6 @@ func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) { // the commitment transaction to the remote peer. outPoint := resCtx.reservation.FundingOutpoint() _, sig := resCtx.reservation.OurSignatures() - commitSig, err := btcec.ParseSignature(sig, btcec.S256()) - if err != nil { - fndgLog.Errorf("Unable to parse signature: %v", err) - f.failFundingFlow(fmsg.peerAddress.IdentityKey, - msg.PendingChannelID, []byte(err.Error())) - resCtx.err <- err - return - } // A new channel has almost finished the funding process. In order to // properly synchronize with the writeHandler goroutine, we add a new @@ -1095,7 +1087,14 @@ func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) { fundingCreated := &lnwire.FundingCreated{ PendingChannelID: pendingChanID, FundingPoint: *outPoint, - CommitSig: commitSig, + } + fundingCreated.CommitSig, err = lnwire.NewSigFromRawSignature(sig) + if err != nil { + fndgLog.Errorf("Unable to parse signature: %v", err) + f.failFundingFlow(fmsg.peerAddress.IdentityKey, + msg.PendingChannelID, []byte(err.Error())) + resCtx.err <- err + return } err = f.cfg.SendToPeer(fmsg.peerAddress.IdentityKey, fundingCreated) if err != nil { @@ -1148,7 +1147,7 @@ func (f *fundingManager) handleFundingCreated(fmsg *fundingCreatedMsg) { // funding transaction will broadcast after our next message. // CompleteReservationSingle will also mark the channel as 'IsPending' // in the database. - commitSig := fmsg.msg.CommitSig.Serialize() + commitSig := fmsg.msg.CommitSig.ToSignatureBytes() completeChan, err := resCtx.reservation.CompleteReservationSingle( &fundingOut, commitSig) if err != nil { @@ -1191,11 +1190,8 @@ func (f *fundingManager) handleFundingCreated(fmsg *fundingCreatedMsg) { // With their signature for our version of the commitment transaction // verified, we can now send over our signature to the remote peer. - // - // TODO(roasbeef): just have raw bytes in wire msg? avoids decoding - // then decoding shortly afterwards. _, sig := resCtx.reservation.OurSignatures() - ourCommitSig, err := btcec.ParseSignature(sig, btcec.S256()) + ourCommitSig, err := lnwire.NewSigFromRawSignature(sig) if err != nil { fndgLog.Errorf("unable to parse signature: %v", err) f.failFundingFlow(fmsg.peerAddress.IdentityKey, @@ -1344,7 +1340,7 @@ func (f *fundingManager) handleFundingSigned(fmsg *fundingSignedMsg) { // The remote peer has responded with a signature for our commitment // transaction. We'll verify the signature for validity, then commit // the state to disk as we can now open the channel. - commitSig := fmsg.msg.CommitSig.Serialize() + commitSig := fmsg.msg.CommitSig.ToSignatureBytes() completeChan, err := resCtx.reservation.CompleteReservation(nil, commitSig) if err != nil { fndgLog.Errorf("Unable to complete reservation sign complete: %v", err) @@ -2127,19 +2123,19 @@ func (f *fundingManager) newChanAnnouncement(localPubKey, remotePubKey *btcec.Pu selfBytes := localPubKey.SerializeCompressed() remoteBytes := remotePubKey.SerializeCompressed() if bytes.Compare(selfBytes, remoteBytes) == -1 { - chanAnn.NodeID1 = localPubKey - chanAnn.NodeID2 = remotePubKey - chanAnn.BitcoinKey1 = localFundingKey - chanAnn.BitcoinKey2 = remoteFundingKey + copy(chanAnn.NodeID1[:], localPubKey.SerializeCompressed()) + copy(chanAnn.NodeID2[:], remotePubKey.SerializeCompressed()) + copy(chanAnn.BitcoinKey1[:], localFundingKey.SerializeCompressed()) + copy(chanAnn.BitcoinKey2[:], remoteFundingKey.SerializeCompressed()) // If we're the first node then update the chanFlags to // indicate the "direction" of the update. chanFlags = 0 } else { - chanAnn.NodeID1 = remotePubKey - chanAnn.NodeID2 = localPubKey - chanAnn.BitcoinKey1 = remoteFundingKey - chanAnn.BitcoinKey2 = localFundingKey + copy(chanAnn.NodeID1[:], remotePubKey.SerializeCompressed()) + copy(chanAnn.NodeID2[:], localPubKey.SerializeCompressed()) + copy(chanAnn.BitcoinKey1[:], remoteFundingKey.SerializeCompressed()) + copy(chanAnn.BitcoinKey2[:], localFundingKey.SerializeCompressed()) // If we're the second node then update the chanFlags to // indicate the "direction" of the update. @@ -2171,7 +2167,12 @@ func (f *fundingManager) newChanAnnouncement(localPubKey, remotePubKey *btcec.Pu if err != nil { return nil, err } - chanUpdateAnn.Signature, err = f.cfg.SignMessage(f.cfg.IDKey, chanUpdateMsg) + sig, err := f.cfg.SignMessage(f.cfg.IDKey, chanUpdateMsg) + if err != nil { + return nil, errors.Errorf("unable to generate channel "+ + "update announcement signature: %v", err) + } + chanUpdateAnn.Signature, err = lnwire.NewSigFromSignature(sig) if err != nil { return nil, errors.Errorf("unable to generate channel "+ "update announcement signature: %v", err) @@ -2203,10 +2204,16 @@ func (f *fundingManager) newChanAnnouncement(localPubKey, remotePubKey *btcec.Pu // provide the other side with the necessary signatures required to // allow them to reconstruct the full channel announcement. proof := &lnwire.AnnounceSignatures{ - ChannelID: chanID, - ShortChannelID: shortChanID, - NodeSignature: nodeSig, - BitcoinSignature: bitcoinSig, + ChannelID: chanID, + ShortChannelID: shortChanID, + } + proof.NodeSignature, err = lnwire.NewSigFromSignature(nodeSig) + if err != nil { + return nil, err + } + proof.BitcoinSignature, err = lnwire.NewSigFromSignature(bitcoinSig) + if err != nil { + return nil, err } return &chanAnnouncement{ diff --git a/fundingmanager_test.go b/fundingmanager_test.go index ffa48b44..e00dd72f 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -5,6 +5,7 @@ package main import ( "fmt" "io/ioutil" + "math/big" "net" "os" "path/filepath" @@ -79,6 +80,13 @@ var ( IdentityKey: bobPubKey, Address: bobTCPAddr, } + + testSig = &btcec.Signature{ + R: new(big.Int), + S: new(big.Int), + } + _, _ = testSig.R.SetString("63724406601629180062774974542967536251589935445068131219452686511677818569431", 10) + _, _ = testSig.S.SetString("18801056069249825825291287104931333862866033135609736119018462340006816851118", 10) ) type mockNotifier struct { @@ -217,7 +225,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey, Notifier: chainNotifier, FeeEstimator: estimator, SignMessage: func(pubKey *btcec.PublicKey, msg []byte) (*btcec.Signature, error) { - return nil, nil + return testSig, nil }, SendAnnouncement: func(msg lnwire.Message) error { select { @@ -319,7 +327,7 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) { FeeEstimator: oldCfg.FeeEstimator, SignMessage: func(pubKey *btcec.PublicKey, msg []byte) (*btcec.Signature, error) { - return nil, nil + return testSig, nil }, SendAnnouncement: func(msg lnwire.Message) error { select { diff --git a/peer.go b/peer.go index 041198c4..b950a536 100644 --- a/peer.go +++ b/peer.go @@ -344,7 +344,9 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) error { // TODO(roasbeef): can add helper method to get policy for // particular channel. var selfPolicy *channeldb.ChannelEdgePolicy - if info != nil && info.NodeKey1.IsEqual(p.server.identityPriv.PubKey()) { + if info != nil && bytes.Equal(info.NodeKey1Bytes[:], + p.server.identityPriv.PubKey().SerializeCompressed()) { + selfPolicy = p1 } else { selfPolicy = p2 @@ -900,8 +902,7 @@ func messageSummary(msg lnwire.Message) string { case *lnwire.NodeAnnouncement: return fmt.Sprintf("node=%x, update_time=%v", - msg.NodeID.SerializeCompressed(), - time.Unix(int64(msg.Timestamp), 0)) + msg.NodeID, time.Unix(int64(msg.Timestamp), 0)) case *lnwire.Ping: // No summary. @@ -957,13 +958,6 @@ func (p *peer) logWireMessage(msg lnwire.Message, read bool) { } case *lnwire.RevokeAndAck: m.NextRevocationKey.Curve = nil - case *lnwire.NodeAnnouncement: - m.NodeID.Curve = nil - case *lnwire.ChannelAnnouncement: - m.NodeID1.Curve = nil - m.NodeID2.Curve = nil - m.BitcoinKey1.Curve = nil - m.BitcoinKey2.Curve = nil case *lnwire.AcceptChannel: m.FundingKey.Curve = nil m.RevocationPoint.Curve = nil @@ -1774,16 +1768,17 @@ func createGetLastUpdate(router *routing.ChannelRouter, "channel by ShortChannelID(%v)", chanID) } + // If we're the outgoing node on the first edge, then that + // means the second edge is our policy. Otherwise, the first + // edge is our policy. var local *channeldb.ChannelEdgePolicy - if bytes.Compare(edge1.Node.PubKey.SerializeCompressed(), - pubKey[:]) == 0 { + if bytes.Equal(edge1.Node.PubKeyBytes[:], pubKey[:]) { local = edge2 } else { local = edge1 } update := &lnwire.ChannelUpdate{ - Signature: local.Signature, ChainHash: info.ChainHash, ShortChannelID: lnwire.NewShortChanIDFromInt(local.ChannelID), Timestamp: uint32(local.LastUpdate.Unix()), @@ -1793,6 +1788,10 @@ func createGetLastUpdate(router *routing.ChannelRouter, BaseFee: uint32(local.FeeBaseMSat), FeeRate: uint32(local.FeeProportionalMillionths), } + update.Signature, err = lnwire.NewSigFromRawSignature(local.SigBytes) + if err != nil { + return nil, err + } hswcLog.Debugf("Sending latest channel_update: %v", spew.Sdump(update)) diff --git a/peer_test.go b/peer_test.go index 90d7d694..4e9f93f0 100644 --- a/peer_test.go +++ b/peer_test.go @@ -14,8 +14,6 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" - "github.com/roasbeef/btcd/btcec" - "github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcutil" ) @@ -95,8 +93,7 @@ func TestPeerChannelClosureAcceptFeeResponder(t *testing.T) { t.Fatalf("error creating close proposal: %v", err) } - initSig := append(initiatorSig, byte(txscript.SigHashAll)) - parsedSig, err := btcec.ParseSignature(initSig, btcec.S256()) + parsedSig, err := lnwire.NewSigFromRawSignature(initiatorSig) if err != nil { t.Fatalf("error parsing signature: %v", err) } @@ -183,7 +180,7 @@ func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) { if err != nil { t.Fatalf("unable to create close proposal: %v", err) } - parsedSig, err := btcec.ParseSignature(closeSig, btcec.S256()) + parsedSig, err := lnwire.NewSigFromRawSignature(closeSig) if err != nil { t.Fatalf("unable to parse signature: %v", err) } @@ -295,7 +292,7 @@ func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) { t.Fatalf("error creating close proposal: %v", err) } - parsedSig, err := btcec.ParseSignature(initiatorSig, btcec.S256()) + parsedSig, err := lnwire.NewSigFromRawSignature(initiatorSig) if err != nil { t.Fatalf("error parsing signature: %v", err) } @@ -339,7 +336,7 @@ func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) { t.Fatalf("error creating close proposal: %v", err) } - parsedSig, err = btcec.ParseSignature(initiatorSig, btcec.S256()) + parsedSig, err = lnwire.NewSigFromRawSignature(initiatorSig) if err != nil { t.Fatalf("error parsing signature: %v", err) } @@ -384,8 +381,7 @@ func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) { t.Fatalf("error creating close proposal: %v", err) } - initSig := append(initiatorSig, byte(txscript.SigHashAll)) - parsedSig, err = btcec.ParseSignature(initSig, btcec.S256()) + parsedSig, err = lnwire.NewSigFromRawSignature(initiatorSig) if err != nil { t.Fatalf("error parsing signature: %v", err) } @@ -478,7 +474,7 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) { if err != nil { t.Fatalf("unable to create close proposal: %v", err) } - parsedSig, err := btcec.ParseSignature(closeSig, btcec.S256()) + parsedSig, err := lnwire.NewSigFromRawSignature(closeSig) if err != nil { t.Fatalf("unable to parse signature: %v", err) } @@ -544,7 +540,7 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) { t.Fatalf("error creating close proposal: %v", err) } - parsedSig, err = btcec.ParseSignature(responderSig, btcec.S256()) + parsedSig, err = lnwire.NewSigFromRawSignature(responderSig) if err != nil { t.Fatalf("error parsing signature: %v", err) } @@ -590,8 +586,7 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) { t.Fatalf("error creating close proposal: %v", err) } - respSig := append(responderSig, byte(txscript.SigHashAll)) - parsedSig, err = btcec.ParseSignature(respSig, btcec.S256()) + parsedSig, err = lnwire.NewSigFromRawSignature(responderSig) if err != nil { t.Fatalf("error parsing signature: %v", err) } diff --git a/rpcserver.go b/rpcserver.go index 414c6d3d..c709e998 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -535,11 +535,14 @@ func (r *rpcServer) VerifyMessage(ctx context.Context, } pubKeyHex := hex.EncodeToString(pubKey.SerializeCompressed()) + var pub [33]byte + copy(pub[:], pubKey.SerializeCompressed()) + // Query the channel graph to ensure a node in the network with active // channels signed the message. // TODO(phlip9): Require valid nodes to have capital in active channels. graph := r.server.chanDB.ChannelGraph() - _, active, err := graph.HasLightningNode(pubKey) + _, active, err := graph.HasLightningNode(pub) if err != nil { return nil, fmt.Errorf("failed to query graph: %v", err) } @@ -1576,8 +1579,8 @@ func (r *rpcServer) savePayment(route *routing.Route, amount lnwire.MilliSatoshi paymentPath := make([][33]byte, len(route.Hops)) for i, hop := range route.Hops { - hopPub := hop.Channel.Node.PubKey.SerializeCompressed() - copy(paymentPath[i][:], hopPub) + hopPub := hop.Channel.Node.PubKeyBytes + copy(paymentPath[i][:], hopPub[:]) } payment := &channeldb.OutgoingPayment{ @@ -2407,7 +2410,7 @@ func (r *rpcServer) DescribeGraph(ctx context.Context, nodeColor := fmt.Sprintf("#%02x%02x%02x", node.Color.R, node.Color.G, node.Color.B) resp.Nodes = append(resp.Nodes, &lnrpc.LightningNode{ LastUpdate: uint32(node.LastUpdate.Unix()), - PubKey: hex.EncodeToString(node.PubKey.SerializeCompressed()), + PubKey: hex.EncodeToString(node.PubKeyBytes[:]), Addresses: nodeAddrs, Alias: node.Alias, Color: nodeColor, @@ -2455,8 +2458,8 @@ func marshalDbEdge(edgeInfo *channeldb.ChannelEdgeInfo, ChanPoint: edgeInfo.ChannelPoint.String(), // TODO(roasbeef): update should be on edge info itself LastUpdate: uint32(lastUpdate), - Node1Pub: hex.EncodeToString(edgeInfo.NodeKey1.SerializeCompressed()), - Node2Pub: hex.EncodeToString(edgeInfo.NodeKey2.SerializeCompressed()), + Node1Pub: hex.EncodeToString(edgeInfo.NodeKey1Bytes[:]), + Node2Pub: hex.EncodeToString(edgeInfo.NodeKey2Bytes[:]), Capacity: int64(edgeInfo.Capacity), } diff --git a/server.go b/server.go index 44470974..86a9f06d 100644 --- a/server.go +++ b/server.go @@ -258,11 +258,11 @@ func newServer(listenAddrs []string, chanDB *channeldb.DB, cc *chainControl, HaveNodeAnnouncement: true, LastUpdate: time.Now(), Addresses: selfAddrs, - PubKey: privKey.PubKey(), Alias: nodeAlias.String(), Features: s.globalFeatures, Color: color, } + copy(selfNode.PubKeyBytes[:], privKey.PubKey().SerializeCompressed()) // If our information has changed since our last boot, then we'll // re-sign our node announcement so a fresh authenticated version of it @@ -272,31 +272,35 @@ func newServer(listenAddrs []string, chanDB *channeldb.DB, cc *chainControl, nodeAnn := &lnwire.NodeAnnouncement{ Timestamp: uint32(selfNode.LastUpdate.Unix()), Addresses: selfNode.Addresses, - NodeID: selfNode.PubKey, + NodeID: selfNode.PubKeyBytes, Alias: nodeAlias, Features: selfNode.Features.RawFeatureVector, RGBColor: color, } - selfNode.AuthSig, err = discovery.SignAnnouncement(s.nodeSigner, - s.identityPriv.PubKey(), nodeAnn, + authSig, err := discovery.SignAnnouncement( + s.nodeSigner, s.identityPriv.PubKey(), nodeAnn, ) if err != nil { return nil, fmt.Errorf("unable to generate signature for "+ "self node announcement: %v", err) } + selfNode.AuthSigBytes = authSig.Serialize() + s.currentNodeAnn = nodeAnn + if err := chanGraph.SetSourceNode(selfNode); err != nil { return nil, fmt.Errorf("can't set self node: %v", err) } - nodeAnn.Signature = selfNode.AuthSig - s.currentNodeAnn = nodeAnn - + nodeAnn.Signature, err = lnwire.NewSigFromRawSignature(selfNode.AuthSigBytes) + if err != nil { + return nil, err + } s.chanRouter, err = routing.New(routing.Config{ Graph: chanGraph, Chain: cc.chainIO, ChainView: cc.chainView, - SendToSwitch: func(firstHop *btcec.PublicKey, + SendToSwitch: func(firstHopPub [33]byte, htlcAdd *lnwire.UpdateAddHTLC, circuit *sphinx.Circuit) ([32]byte, error) { @@ -307,9 +311,6 @@ func newServer(listenAddrs []string, chanDB *channeldb.DB, cc *chainControl, OnionErrorDecrypter: sphinx.NewOnionErrorDecrypter(circuit), } - var firstHopPub [33]byte - copy(firstHopPub[:], firstHop.SerializeCompressed()) - return s.htlcSwitch.SendHTLC(firstHopPub, htlcAdd, errorDecryptor) }, ChannelPruneExpiry: time.Duration(time.Hour * 24 * 14), @@ -806,11 +807,19 @@ func (s *server) genNodeAnnouncement( } s.currentNodeAnn.Timestamp = newStamp - s.currentNodeAnn.Signature, err = discovery.SignAnnouncement( + sig, err := discovery.SignAnnouncement( s.nodeSigner, s.identityPriv.PubKey(), s.currentNodeAnn, ) + if err != nil { + return lnwire.NodeAnnouncement{}, err + } - return *s.currentNodeAnn, err + s.currentNodeAnn.Signature, err = lnwire.NewSigFromSignature(sig) + if err != nil { + return lnwire.NodeAnnouncement{}, err + } + + return *s.currentNodeAnn, nil } type nodeAddresses struct { @@ -870,7 +879,7 @@ func (s *server) establishPersistentConnections() error { _ *channeldb.ChannelEdgeInfo, policy, _ *channeldb.ChannelEdgePolicy) error { - pubStr := string(policy.Node.PubKey.SerializeCompressed()) + pubStr := string(policy.Node.PubKeyBytes[:]) // Add addresses from channel graph/NodeAnnouncements to the // list of addresses we'll connect to. If there are duplicates @@ -906,11 +915,15 @@ func (s *server) establishPersistentConnections() error { } } - nodeAddrsMap[pubStr] = &nodeAddresses{ - pubKey: policy.Node.PubKey, + n := &nodeAddresses{ addresses: addrs, } + n.pubKey, err = policy.Node.PubKey() + if err != nil { + return err + } + nodeAddrsMap[pubStr] = n return nil }) if err != nil && err != channeldb.ErrGraphNoEdgesFound { From 1f3124f48a0af43492b5aecf1bf9a1d0d5371634 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2018 20:31:25 -0800 Subject: [PATCH 12/16] routing: use [33]byte instead of *btcutil.Publickey for SendToSwitch With this change, we can avoid unnecessarily serializing a public key. --- routing/router.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/router.go b/routing/router.go index 4e92ff15..beba40ad 100644 --- a/routing/router.go +++ b/routing/router.go @@ -130,7 +130,7 @@ type Config struct { // forward a fully encoded payment to the first hop in the route // denoted by its public key. A non-nil error is to be returned if the // payment was unsuccessful. - SendToSwitch func(firstHop *btcec.PublicKey, htlcAdd *lnwire.UpdateAddHTLC, + SendToSwitch func(firstHop [33]byte, htlcAdd *lnwire.UpdateAddHTLC, circuit *sphinx.Circuit) ([sha256.Size]byte, error) // ChannelPruneExpiry is the duration used to determine if a channel From 6d05cb5aae87b7c6c8400a318e905350b8c4bf8e Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2018 20:34:40 -0800 Subject: [PATCH 13/16] routing: extract zombie pruning to distinct method --- routing/router.go | 163 +++++++++++++++++++--------------- routing/validation_barrier.go | 8 +- 2 files changed, 94 insertions(+), 77 deletions(-) diff --git a/routing/router.go b/routing/router.go index beba40ad..0ea7ed7b 100644 --- a/routing/router.go +++ b/routing/router.go @@ -504,6 +504,94 @@ func (r *ChannelRouter) syncGraphWithChain() error { return nil } +// pruneZombieChans is a method that will be called periodically to prune out +// any "zombie" channels. We consider channels zombies if *both* edges haven't +// been updated since our zombie horizon. We do this periodically to keep a +// health, lively routing table. +func (r *ChannelRouter) pruneZombieChans() error { + var chansToPrune []wire.OutPoint + chanExpiry := r.cfg.ChannelPruneExpiry + + log.Infof("Examining Channel Graph for zombie channels") + + // First, we'll collect all the channels which are eligible for garbage + // collection due to being zombies. + filterPruneChans := func(info *channeldb.ChannelEdgeInfo, + e1, e2 *channeldb.ChannelEdgePolicy) error { + + // We'll ensure that we don't attempt to prune our *own* + // channels from the graph, as in any case this should be + // re-advertised by the sub-system above us. + if info.NodeKey1Bytes == r.selfNode.PubKeyBytes || + info.NodeKey2Bytes == r.selfNode.PubKeyBytes { + + return nil + } + + // If *both* edges haven't been updated for a period of + // chanExpiry, then we'll mark the channel itself as eligible + // for graph pruning. + e1Zombie, e2Zombie := true, true + if e1 != nil { + e1Zombie = time.Since(e1.LastUpdate) >= chanExpiry + if e1Zombie { + log.Tracef("Edge #1 of ChannelPoint(%v) "+ + "last update: %v", + info.ChannelPoint, e1.LastUpdate) + } + } + if e2 != nil { + e2Zombie = time.Since(e2.LastUpdate) >= chanExpiry + if e2Zombie { + log.Tracef("Edge #2 of ChannelPoint(%v) "+ + "last update: %v", + info.ChannelPoint, e2.LastUpdate) + } + } + if e1Zombie && e2Zombie { + log.Debugf("ChannelPoint(%v) is a zombie, collecting "+ + "to prune", info.ChannelPoint) + + // TODO(roasbeef): add ability to delete single + // directional edge + chansToPrune = append(chansToPrune, info.ChannelPoint) + + // As we're detecting this as a zombie channel, we'll + // add this to the set of recently rejected items so we + // don't re-accept it shortly after. + r.rejectCache[info.ChannelID] = struct{}{} + } + + return nil + } + + r.rejectMtx.Lock() + err := r.cfg.Graph.ForEachChannel(filterPruneChans) + if err != nil { + r.rejectMtx.Unlock() + return fmt.Errorf("Unable to filter local zombie "+ + "chans: %v", err) + } + + log.Infof("Pruning %v Zombie Channels", len(chansToPrune)) + + // With the set zombie-like channels obtained, we'll do another pass to + // delete al zombie channels from the channel graph. + for _, chanToPrune := range chansToPrune { + log.Tracef("Pruning zombie chan ChannelPoint(%v)", chanToPrune) + + err := r.cfg.Graph.DeleteChannelEdge(&chanToPrune) + if err != nil { + r.rejectMtx.Unlock() + return fmt.Errorf("Unable to prune zombie "+ + "chans: %v", err) + } + } + r.rejectMtx.Unlock() + + return nil +} + // networkHandler is the primary goroutine for the ChannelRouter. The roles of // this goroutine include answering queries related to the state of the // network, pruning the graph on new block notification, applying network @@ -716,79 +804,8 @@ func (r *ChannelRouter) networkHandler() { // state of the known graph to filter out any zombie channels // for pruning. case <-graphPruneTicker.C: - - var chansToPrune []wire.OutPoint - chanExpiry := r.cfg.ChannelPruneExpiry - - log.Infof("Examining Channel Graph for zombie channels") - - // First, we'll collect all the channels which are - // eligible for garbage collection due to being - // zombies. - filterPruneChans := func(info *channeldb.ChannelEdgeInfo, - e1, e2 *channeldb.ChannelEdgePolicy) error { - - // We'll ensure that we don't attempt to prune - // our *own* channels from the graph, as in any - // case this should be re-advertised by the - // sub-system above us. - if info.NodeKey1.IsEqual(r.selfNode.PubKey) || - info.NodeKey2.IsEqual(r.selfNode.PubKey) { - - return nil - } - - // If *both* edges haven't been updated for a - // period of chanExpiry, then we'll mark the - // channel itself as eligible for graph - // pruning. - e1Zombie, e2Zombie := true, true - if e1 != nil { - e1Zombie = time.Since(e1.LastUpdate) >= chanExpiry - log.Tracef("Edge #1 of ChannelPoint(%v) "+ - "last update: %v", - info.ChannelPoint, e1.LastUpdate) - } - if e2 != nil { - e2Zombie = time.Since(e2.LastUpdate) >= chanExpiry - log.Tracef("Edge #2 of ChannelPoint(%v) "+ - "last update: %v", - info.ChannelPoint, e2.LastUpdate) - } - if e1Zombie && e2Zombie { - log.Infof("ChannelPoint(%v) is a "+ - "zombie, collecting to prune", - info.ChannelPoint) - - // TODO(roasbeef): add ability to - // delete single directional edge - chansToPrune = append(chansToPrune, - info.ChannelPoint) - } - - return nil - } - err := r.cfg.Graph.ForEachChannel(filterPruneChans) - if err != nil { - log.Errorf("Unable to local zombie chans: %v", err) - continue - } - - log.Infof("Pruning %v Zombie Channels", len(chansToPrune)) - - // With the set zombie-like channels obtained, we'll do - // another pass to delete al zombie channels from the - // channel graph. - for _, chanToPrune := range chansToPrune { - log.Tracef("Pruning zombie chan ChannelPoint(%v)", - chanToPrune) - - err := r.cfg.Graph.DeleteChannelEdge(&chanToPrune) - if err != nil { - log.Errorf("Unable to prune zombie "+ - "chans: %v", err) - continue - } + if err := r.pruneZombieChans(); err != nil { + log.Errorf("unable to prune zombies: %v", err) } // The router has been signalled to exit, to we exit our main diff --git a/routing/validation_barrier.go b/routing/validation_barrier.go index 7878099e..63bdfa55 100644 --- a/routing/validation_barrier.go +++ b/routing/validation_barrier.go @@ -116,8 +116,8 @@ func (v *ValidationBarrier) InitJobDependencies(job interface{}) { v.chanAnnFinSignal[shortID] = annFinCond v.chanEdgeDependencies[shortID] = annFinCond - v.nodeAnnDependencies[Vertex(msg.NodeKey1)] = annFinCond - v.nodeAnnDependencies[Vertex(msg.NodeKey2)] = annFinCond + v.nodeAnnDependencies[Vertex(msg.NodeKey1Bytes)] = annFinCond + v.nodeAnnDependencies[Vertex(msg.NodeKey2Bytes)] = annFinCond } // These other types don't have any dependants, so no further @@ -168,7 +168,7 @@ func (v *ValidationBarrier) WaitForDependants(job interface{}) { shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID) signal, ok = v.chanEdgeDependencies[shortID] case *channeldb.LightningNode: - vertex := Vertex(msg.PubKey) + vertex := Vertex(msg.PubKeyBytes) signal, ok = v.nodeAnnDependencies[vertex] case *lnwire.ChannelUpdate: signal, ok = v.chanEdgeDependencies[msg.ShortChannelID] @@ -234,7 +234,7 @@ func (v *ValidationBarrier) SignalDependants(job interface{}) { // map, as if we reach this point, then all dependants have already // finished executing and we can proceed. case *channeldb.LightningNode: - delete(v.nodeAnnDependencies, Vertex(msg.PubKey)) + delete(v.nodeAnnDependencies, Vertex(msg.PubKeyBytes)) case *lnwire.NodeAnnouncement: delete(v.nodeAnnDependencies, Vertex(msg.NodeID)) case *lnwire.ChannelUpdate: From b54b8dd7f1f8f24f1af4983f90b09d57eb8a8786 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2018 20:35:19 -0800 Subject: [PATCH 14/16] routing: reject any new announcements which were pruned as zombies --- routing/router.go | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/routing/router.go b/routing/router.go index 0ea7ed7b..2663755a 100644 --- a/routing/router.go +++ b/routing/router.go @@ -237,6 +237,9 @@ type ChannelRouter struct { // consistency between the various database accesses. channelEdgeMtx *multimutex.Mutex + rejectMtx sync.RWMutex + rejectCache map[uint64]struct{} + sync.RWMutex quit chan struct{} @@ -268,6 +271,7 @@ func New(cfg Config) (*ChannelRouter, error) { channelEdgeMtx: multimutex.NewMutex(), selfNode: selfNode, routeCache: make(map[routeTuple][]*Route), + rejectCache: make(map[uint64]struct{}), quit: make(chan struct{}), }, nil } @@ -566,9 +570,10 @@ func (r *ChannelRouter) pruneZombieChans() error { } r.rejectMtx.Lock() + defer r.rejectMtx.Unlock() + err := r.cfg.Graph.ForEachChannel(filterPruneChans) if err != nil { - r.rejectMtx.Unlock() return fmt.Errorf("Unable to filter local zombie "+ "chans: %v", err) } @@ -582,12 +587,10 @@ func (r *ChannelRouter) pruneZombieChans() error { err := r.cfg.Graph.DeleteChannelEdge(&chanToPrune) if err != nil { - r.rejectMtx.Unlock() return fmt.Errorf("Unable to prune zombie "+ "chans: %v", err) } } - r.rejectMtx.Unlock() return nil } @@ -861,6 +864,16 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { log.Infof("Updated vertex data for node=%x", msg.PubKeyBytes) case *channeldb.ChannelEdgeInfo: + // If we recently rejected this channel edge, then we won't + // attempt to re-process it. + r.rejectMtx.RLock() + if _, ok := r.rejectCache[msg.ChannelID]; ok { + r.rejectMtx.RUnlock() + return newErrf(ErrIgnored, "recently rejected "+ + "chan_id=%v", msg.ChannelID) + } + r.rejectMtx.RUnlock() + // Prior to processing the announcement we first check if we // already know of this channel, if so, then we can exit early. _, _, exists, err := r.cfg.Graph.HasChannelEdge(msg.ChannelID) @@ -972,6 +985,16 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { } case *channeldb.ChannelEdgePolicy: + // If we recently rejected this channel edge, then we won't + // attempt to re-process it. + r.rejectMtx.RLock() + if _, ok := r.rejectCache[msg.ChannelID]; ok { + r.rejectMtx.RUnlock() + return newErrf(ErrIgnored, "recently rejected "+ + "chan_id=%v", msg.ChannelID) + } + r.rejectMtx.RUnlock() + channelID := lnwire.NewShortChanIDFromInt(msg.ChannelID) // We make sure to hold the mutex for this channel ID, From e578cea375590726eafea388ae99d31b5ce985e9 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2018 20:40:41 -0800 Subject: [PATCH 15/16] channeldb: fix linter errors --- channeldb/graph.go | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/channeldb/graph.go b/channeldb/graph.go index 7c518cbe..9bec2105 100644 --- a/channeldb/graph.go +++ b/channeldb/graph.go @@ -1020,17 +1020,17 @@ type LightningNode struct { // // NOTE: By having this method to access an attribute, we ensure we only need // to fully deserialize the pubkey if absolutely necessary. -func (c *LightningNode) PubKey() (*btcec.PublicKey, error) { - if c.pubKey != nil { - return c.pubKey, nil +func (l *LightningNode) PubKey() (*btcec.PublicKey, error) { + if l.pubKey != nil { + return l.pubKey, nil } - key, err := btcec.ParsePubKey(c.PubKeyBytes[:], btcec.S256()) + key, err := btcec.ParsePubKey(l.PubKeyBytes[:], btcec.S256()) if err != nil { return nil, err } - c.pubKey = key - c.pubKey.Curve = nil + l.pubKey = key + l.pubKey.Curve = nil return key, nil } @@ -1040,15 +1040,15 @@ func (c *LightningNode) PubKey() (*btcec.PublicKey, error) { // // NOTE: By having this method to access an attribute, we ensure we only need // to fully deserialize the signature if absolutely necessary. -func (c *LightningNode) AuthSig() (*btcec.Signature, error) { - return btcec.ParseSignature(c.AuthSigBytes, btcec.S256()) +func (l *LightningNode) AuthSig() (*btcec.Signature, error) { + return btcec.ParseSignature(l.AuthSigBytes, btcec.S256()) } // AddPubKey is a setter-link method that can be used to swap out the public // key for a node. -func (c *LightningNode) AddPubKey(key *btcec.PublicKey) { - c.pubKey = key - copy(c.PubKeyBytes[:], key.SerializeCompressed()) +func (l *LightningNode) AddPubKey(key *btcec.PublicKey) { + l.pubKey = key + copy(l.PubKeyBytes[:], key.SerializeCompressed()) } // FetchLightningNode attempts to look up a target node by its identity public @@ -1442,7 +1442,7 @@ type ChannelAuthProof struct { BitcoinSig2Bytes []byte } -// NodeSig1 is the signature using the identity key of the node that is first +// Node1Sig is the signature using the identity key of the node that is first // in a lexicographical ordering of the serialized public keys of the two nodes // that created the channel. // @@ -1463,7 +1463,7 @@ func (c *ChannelAuthProof) Node1Sig() (*btcec.Signature, error) { return sig, nil } -// NodeSig2 is the signature using the identity key of the node that is second +// Node2Sig is the signature using the identity key of the node that is second // in a lexicographical ordering of the serialized public keys of the two nodes // that created the channel. // @@ -1526,11 +1526,11 @@ func (c *ChannelAuthProof) BitcoinSig2() (*btcec.Signature, error) { // IsEmpty check is the authentication proof is empty Proof is empty if at // least one of the signatures are equal to nil. -func (p *ChannelAuthProof) IsEmpty() bool { - return len(p.NodeSig1Bytes) == 0 || - len(p.NodeSig2Bytes) == 0 || - len(p.BitcoinSig1Bytes) == 0 || - len(p.BitcoinSig2Bytes) == 0 +func (c *ChannelAuthProof) IsEmpty() bool { + return len(c.NodeSig1Bytes) == 0 || + len(c.NodeSig2Bytes) == 0 || + len(c.BitcoinSig1Bytes) == 0 || + len(c.BitcoinSig2Bytes) == 0 } // ChannelEdgePolicy represents a *directed* edge within the channel graph. For From bf05e4778046643d55739a755be175ed9e9402fe Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2018 20:41:27 -0800 Subject: [PATCH 16/16] discovery: add additional gossiper level reject cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit, we’ll add a new reject cache to ensure that we don’t attempt to re-process any announcements already rejected by the ChannelRouter. --- discovery/gossiper.go | 52 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/discovery/gossiper.go b/discovery/gossiper.go index 5d722caf..64dee356 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -193,6 +193,9 @@ type AuthenticatedGossiper struct { // consistent between when the DB is first read until it's written. channelMtx *multimutex.Mutex + rejectMtx sync.RWMutex + recentRejects map[uint64]struct{} + sync.Mutex } @@ -214,6 +217,7 @@ func New(cfg Config, selfKey *btcec.PublicKey) (*AuthenticatedGossiper, error) { prematureChannelUpdates: make(map[uint64][]*networkMsg), waitingProofs: storage, channelMtx: multimutex.NewMutex(), + recentRejects: make(map[uint64]struct{}), }, nil } @@ -890,6 +894,13 @@ func (d *AuthenticatedGossiper) networkHandler() { continue } + // If this message was recently rejected, then we won't + // attempt to re-process it. + if d.isRecentlyRejectedMsg(announcement.msg) { + announcement.err <- fmt.Errorf("recently rejected") + continue + } + // We'll set up any dependent, and wait until a free // slot for this job opens up, this allow us to not // have thousands of goroutines active. @@ -1014,6 +1025,26 @@ func (d *AuthenticatedGossiper) networkHandler() { } } +// isRecentlyRejectedMsg returns true if we recently rejected a message, and +// false otherwise, This avoids expensive reprocessing of the message. +func (d *AuthenticatedGossiper) isRecentlyRejectedMsg(msg lnwire.Message) bool { + d.rejectMtx.RLock() + defer d.rejectMtx.RUnlock() + + switch m := msg.(type) { + case *lnwire.ChannelUpdate: + _, ok := d.recentRejects[m.ShortChannelID.ToUint64()] + return ok + + case *lnwire.ChannelAnnouncement: + _, ok := d.recentRejects[m.ShortChannelID.ToUint64()] + return ok + + default: + return false + } +} + // retransmitStaleChannels examines all outgoing channels that the source node // is known to maintain to check to see if any of them are "stale". A channel // is stale iff, the last timestamp of its rebroadcast is older then @@ -1325,6 +1356,9 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n log.Error("Ignoring ChannelAnnouncement from "+ "chain=%v, gossiper on chain=%v", msg.ChainHash, d.cfg.ChainHash) + d.rejectMtx.Lock() + d.recentRejects[msg.ShortChannelID.ToUint64()] = struct{}{} + d.rejectMtx.Unlock() return nil } @@ -1356,6 +1390,9 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n if err := ValidateChannelAnn(msg); err != nil { err := errors.Errorf("unable to validate "+ "announcement: %v", err) + d.rejectMtx.Lock() + d.recentRejects[msg.ShortChannelID.ToUint64()] = struct{}{} + d.rejectMtx.Unlock() log.Error(err) nMsg.err <- err @@ -1416,6 +1453,9 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n // see if we get any new announcements. anns, rErr := d.processRejectedEdge(msg, proof) if rErr != nil { + d.rejectMtx.Lock() + d.recentRejects[msg.ShortChannelID.ToUint64()] = struct{}{} + d.rejectMtx.Unlock() nMsg.err <- rErr return nil } @@ -1517,6 +1557,9 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n log.Error("Ignoring ChannelUpdate from "+ "chain=%v, gossiper on chain=%v", msg.ChainHash, d.cfg.ChainHash) + d.rejectMtx.Lock() + d.recentRejects[msg.ShortChannelID.ToUint64()] = struct{}{} + d.rejectMtx.Unlock() return nil } @@ -1590,6 +1633,10 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n shortChanID, err) log.Error(err) nMsg.err <- err + + d.rejectMtx.Lock() + d.recentRejects[msg.ShortChannelID.ToUint64()] = struct{}{} + d.rejectMtx.Unlock() return nil } } @@ -1633,6 +1680,9 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n if routing.IsError(err, routing.ErrOutdated, routing.ErrIgnored) { log.Debug(err) } else { + d.rejectMtx.Lock() + d.recentRejects[msg.ShortChannelID.ToUint64()] = struct{}{} + d.rejectMtx.Unlock() log.Error(err) } @@ -1808,7 +1858,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(nMsg *networkMsg) []n chanInfo.AuthProof, chanInfo, e1, e2, ) if err != nil { - log.Error("unable to gen ann: %v", err) + log.Errorf("unable to gen ann: %v", err) return } err = d.cfg.SendToPeer(nMsg.peer, chanAnn)