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, 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/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..9bec2105 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 (l *LightningNode) PubKey() (*btcec.PublicKey, error) { + if l.pubKey != nil { + return l.pubKey, nil + } + + key, err := btcec.ParsePubKey(l.PubKeyBytes[:], btcec.S256()) + if err != nil { + return nil, err + } + l.pubKey = key + l.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 (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 (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 // 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 +} + +// 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. +// +// 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 +} + +// 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. +// +// 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 +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 @@ -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) 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..64dee356 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 @@ -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 } @@ -232,17 +236,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 +271,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 +285,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 +301,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 +607,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. @@ -875,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. @@ -999,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 @@ -1182,9 +1228,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 +1314,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, } @@ -1307,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 } @@ -1338,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 @@ -1348,10 +1403,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 +1420,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 @@ -1398,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 } @@ -1499,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 } @@ -1560,7 +1621,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) @@ -1572,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 } } @@ -1582,9 +1647,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 +1666,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, @@ -1615,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) } @@ -1632,9 +1700,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 +1794,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 +1816,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 +1854,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.Errorf("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 +1919,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 +2095,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 +2105,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 +2114,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 +2128,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 +2143,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 +2154,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 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/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 } 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, 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..93eaa990 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 @@ -42,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/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..10c565ae 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: @@ -210,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 } @@ -469,16 +464,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 +483,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: @@ -541,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 1eaeade3..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 ( @@ -50,7 +48,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 @@ -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 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.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 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) 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/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..2663755a 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 @@ -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 } @@ -504,6 +508,93 @@ 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() + defer r.rejectMtx.Unlock() + + err := r.cfg.Graph.ForEachChannel(filterPruneChans) + if err != nil { + 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 { + return fmt.Errorf("Unable to prune zombie "+ + "chans: %v", err) + } + } + + 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 +807,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 @@ -814,7 +834,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 +842,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,18 +853,27 @@ 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: + // 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) @@ -859,30 +888,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 +938,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 +968,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 @@ -960,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, @@ -1183,7 +1218,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 +1257,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 +1325,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 +1493,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 +1733,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..63bdfa55 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.NodeKey1Bytes)] = annFinCond + v.nodeAnnDependencies[Vertex(msg.NodeKey2Bytes)] = 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.PubKeyBytes) 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.PubKeyBytes)) 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: 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 { 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 }