discovery+funding: add 'AnnounceSignature' proof exchange

Add the interaction between nodes of announce signature messages, which
will allow us to exhcnage the half channel announcemen proofs later.
This commit is contained in:
Andrey Samokhvalov authored and Olaoluwa Osuntokun committed 2017-03-29 19:49:05 -07:00
1 parent 07076cce21
commit fbf766e3c6
6 files changed
+891 -289

No files matched your search

+359 -133
View File
@@ -5,6 +5,8 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"bytes"
"encoding/hex" "encoding/hex"
"github.com/go-errors/errors" "github.com/go-errors/errors"
@@ -16,12 +18,23 @@ import (
"github.com/roasbeef/btcutil" "github.com/roasbeef/btcutil"
) )
// waitingProofKey is the proof key which uniquely identifies the
// announcement signature message. The goal of this key is distinguish the
// local and remote proof for the same channel id.
// TODO(andrew.shvv) move to the channeldb package after waiting proof map
// becomes persistent.
type waitingProofKey struct {
chanID uint64
isRemote bool
}
// networkMsg couples a routing related wire message with the peer that // networkMsg couples a routing related wire message with the peer that
// originally sent it. // originally sent it.
type networkMsg struct { type networkMsg struct {
msg lnwire.Message msg lnwire.Message
isRemote bool isRemote bool
peer *btcec.PublicKey peer *btcec.PublicKey
err chan error
} }
// syncRequest represents a request from an outside subsystem to the wallet to // syncRequest represents a request from an outside subsystem to the wallet to
@@ -51,10 +64,19 @@ type Config struct {
// indicates that the target peer should be excluded from the broadcast. // indicates that the target peer should be excluded from the broadcast.
Broadcast func(exclude *btcec.PublicKey, msg ...lnwire.Message) error Broadcast func(exclude *btcec.PublicKey, msg ...lnwire.Message) error
// SendMessages is a function which allows the service to send a set of // SendToPeer is a function which allows the service to send a set of
// messages to a particular peer identified by the target public // messages to a particular peer identified by the target public
// key. // key.
SendMessages func(target *btcec.PublicKey, msg ...lnwire.Message) error SendToPeer func(target *btcec.PublicKey, msg ...lnwire.Message) error
// ProofMatureDelta the number of confirmations which is needed
// before exchange the channel announcement proofs.
ProofMatureDelta uint32
// TrickleDelay the period of trickle timer which flushing to the
// network the pending batch of new announcements we've received since
// the last trickle tick.
TrickleDelay time.Duration
} }
// New create new discovery service structure. // New create new discovery service structure.
@@ -79,6 +101,7 @@ func New(cfg Config) (*Discovery, error) {
quit: make(chan bool), quit: make(chan bool),
syncRequests: make(chan *syncRequest), syncRequests: make(chan *syncRequest),
prematureAnnouncements: make(map[uint32][]*networkMsg), prematureAnnouncements: make(map[uint32][]*networkMsg),
waitingProofs: make(map[waitingProofKey]*lnwire.AnnounceSignatures),
fakeSig: fakeSig, fakeSig: fakeSig,
}, nil }, nil
} }
@@ -104,16 +127,23 @@ type Discovery struct {
// the main chain are sent over. // the main chain are sent over.
newBlocks <-chan *chainntnfs.BlockEpoch newBlocks <-chan *chainntnfs.BlockEpoch
// prematureAnnouncements maps a blockheight to a set of announcements // prematureAnnouncements maps a block height to a set of network
// which are "premature" from our PoV. An message is premature if // messages which are "premature" from our PoV. An message is premature
// it claims to be anchored in a block which is beyond the current main // if it claims to be anchored in a block which is beyond the current
// chain tip as we know it. Premature network messages will be processed // main chain tip as we know it. Premature network messages will be
// once the chain tip as we know it extends to/past the premature // processed once the chain tip as we know it extends to/past the
// height. // premature height.
// //
// TODO(roasbeef): limit premature networkMsgs to N // TODO(roasbeef): limit premature networkMsgs to N
prematureAnnouncements map[uint32][]*networkMsg prematureAnnouncements map[uint32][]*networkMsg
// waitingProofs is the map of proof announcement messages which were
// processed and waiting for opposite local or remote proof to be
// received in order to construct full proof, validate it and
// announce the channel.
// TODO(andrew.shvv) make this map persistent.
waitingProofs map[waitingProofKey]*lnwire.AnnounceSignatures
// networkMsgs is a channel that carries new network broadcasted // networkMsgs is a channel that carries new network broadcasted
// message from outside the discovery service to be processed by the // message from outside the discovery service to be processed by the
// networkHandler. // networkHandler.
@@ -137,20 +167,22 @@ type Discovery struct {
// Remote channel announcements should contain the announcement proof and be // Remote channel announcements should contain the announcement proof and be
// fully validated. // fully validated.
func (d *Discovery) ProcessRemoteAnnouncement(msg lnwire.Message, func (d *Discovery) ProcessRemoteAnnouncement(msg lnwire.Message,
src *btcec.PublicKey) error { src *btcec.PublicKey) chan error {
aMsg := &networkMsg{ nMsg := &networkMsg{
msg: msg, msg: msg,
isRemote: true, isRemote: true,
peer: src, peer: src,
err: make(chan error, 1),
} }
select { select {
case d.networkMsgs <- aMsg: case d.networkMsgs <- nMsg:
return nil
case <-d.quit: case <-d.quit:
return errors.New("discovery has been shutted down") nMsg.err <- errors.New("discovery has shut down")
} }
return nMsg.err
} }
// ProcessLocalAnnouncement sends a new remote announcement message along with // ProcessLocalAnnouncement sends a new remote announcement message along with
@@ -160,20 +192,22 @@ func (d *Discovery) ProcessRemoteAnnouncement(msg lnwire.Message,
// fully validated. The channels proofs will be included farther if nodes agreed // fully validated. The channels proofs will be included farther if nodes agreed
// to announce this channel to the rest of the network. // to announce this channel to the rest of the network.
func (d *Discovery) ProcessLocalAnnouncement(msg lnwire.Message, func (d *Discovery) ProcessLocalAnnouncement(msg lnwire.Message,
src *btcec.PublicKey) error { src *btcec.PublicKey) chan error {
aMsg := &networkMsg{ nMsg := &networkMsg{
msg: msg, msg: msg,
isRemote: false, isRemote: false,
peer: src, peer: src,
err: make(chan error, 1),
} }
select { select {
case d.networkMsgs <- aMsg: case d.networkMsgs <- nMsg:
return nil
case <-d.quit: case <-d.quit:
return errors.New("discovery has been shutted down") nMsg.err <- errors.New("discovery has shut down")
} }
return nMsg.err
} }
// SynchronizeNode sends a message to the service indicating it should // SynchronizeNode sends a message to the service indicating it should
@@ -246,7 +280,7 @@ func (d *Discovery) networkHandler() {
defer retransmitTimer.Stop() defer retransmitTimer.Stop()
// TODO(roasbeef): parametrize the above // TODO(roasbeef): parametrize the above
trickleTimer := time.NewTicker(time.Millisecond * 300) trickleTimer := time.NewTicker(d.cfg.TrickleDelay)
defer trickleTimer.Stop() defer trickleTimer.Stop()
for { for {
@@ -254,18 +288,18 @@ func (d *Discovery) networkHandler() {
case announcement := <-d.networkMsgs: case announcement := <-d.networkMsgs:
// Process the network announcement to determine if // Process the network announcement to determine if
// this is either a new announcement from our PoV or an // this is either a new announcement from our PoV or an
// updates to a prior vertex/edge we previously // edges to a prior vertex/edge we previously
// accepted. // proceeded.
accepted := d.processNetworkAnnouncement(announcement) emittedAnnouncements := d.processNetworkAnnouncement(announcement)
// If the updates was accepted, then add it to our next // If the announcement was accepted, then add the
// announcement batch to be broadcast once the trickle // emitted announcements to our announce batch to be
// timer ticks gain. // broadcast once the trickle timer ticks gain.
if accepted { if emittedAnnouncements != nil {
// TODO(roasbeef): exclude peer that sent // TODO(roasbeef): exclude peer that sent
announcementBatch = append( announcementBatch = append(
announcementBatch, announcementBatch,
announcement.msg, emittedAnnouncements...,
) )
} }
@@ -294,12 +328,11 @@ func (d *Discovery) networkHandler() {
} }
for _, ann := range prematureAnns { for _, ann := range prematureAnns {
accepted := d.processNetworkAnnouncement(ann) emittedAnnouncements := d.processNetworkAnnouncement(ann)
if emittedAnnouncements != nil {
if accepted {
announcementBatch = append( announcementBatch = append(
announcementBatch, announcementBatch,
ann.msg, emittedAnnouncements...,
) )
} }
} }
@@ -309,7 +342,7 @@ func (d *Discovery) networkHandler() {
// flush to the network the pending batch of new announcements // flush to the network the pending batch of new announcements
// we've received since the last trickle tick. // we've received since the last trickle tick.
case <-trickleTimer.C: case <-trickleTimer.C:
// If the current announcement batch is nil, then we // If the current announcements batch is nil, then we
// have no further work here. // have no further work here.
if len(announcementBatch) == 0 { if len(announcementBatch) == 0 {
continue continue
@@ -322,7 +355,8 @@ func (d *Discovery) networkHandler() {
// them to all our immediately connected peers. // them to all our immediately connected peers.
err := d.cfg.Broadcast(nil, announcementBatch...) err := d.cfg.Broadcast(nil, announcementBatch...)
if err != nil { if err != nil {
log.Errorf("unable to send batch announcement: %v", err) log.Errorf("unable to send batch "+
"announcements: %v", err)
continue continue
} }
@@ -343,7 +377,7 @@ func (d *Discovery) networkHandler() {
err := d.cfg.Router.ForAllOutgoingChannels( err := d.cfg.Router.ForAllOutgoingChannels(
func(p *channeldb.ChannelEdgePolicy) error { func(p *channeldb.ChannelEdgePolicy) error {
c := &lnwire.ChannelUpdateAnnouncement{ c := &lnwire.ChannelUpdateAnnouncement{
Signature: d.fakeSig, Signature: p.Signature,
ShortChannelID: lnwire.NewShortChanIDFromInt(p.ChannelID), ShortChannelID: lnwire.NewShortChanIDFromInt(p.ChannelID),
Timestamp: uint32(p.LastUpdate.Unix()), Timestamp: uint32(p.LastUpdate.Unix()),
Flags: p.Flags, Flags: p.Flags,
@@ -381,8 +415,6 @@ func (d *Discovery) networkHandler() {
// through the (subjectively) new information on their own. // through the (subjectively) new information on their own.
case syncReq := <-d.syncRequests: case syncReq := <-d.syncRequests:
nodePub := syncReq.node.SerializeCompressed() nodePub := syncReq.node.SerializeCompressed()
log.Infof("Synchronizing channel graph with %x", nodePub)
if err := d.synchronize(syncReq); err != nil { if err := d.synchronize(syncReq); err != nil {
log.Errorf("unable to sync graph state with %x: %v", log.Errorf("unable to sync graph state with %x: %v",
nodePub, err) nodePub, err)
@@ -397,23 +429,22 @@ func (d *Discovery) networkHandler() {
} }
// processNetworkAnnouncement processes a new network relate authenticated // processNetworkAnnouncement processes a new network relate authenticated
// channel or node announcement. If the updates didn't affect the internal state // channel or node announcement or announcements proofs. If the announcement
// of the draft due to either being out of date, invalid, or redundant, then // didn't affect the internal state due to either being out of date, invalid,
// false is returned. Otherwise, true is returned indicating that the caller // or redundant, then nil is returned. Otherwise, the set of announcements
// may want to batch this request to be broadcast to immediate peers during the // will be returned which should be broadcasted to the rest of the network.
// next announcement epoch. func (d *Discovery) processNetworkAnnouncement(nMsg *networkMsg) []lnwire.Message {
func (d *Discovery) processNetworkAnnouncement(aMsg *networkMsg) bool { var announcements []lnwire.Message
isPremature := func(chanID *lnwire.ShortChannelID) bool { isPremature := func(chanID lnwire.ShortChannelID, delta uint32) bool {
return chanID.BlockHeight > d.bestHeight return chanID.BlockHeight+delta > d.bestHeight
} }
switch msg := aMsg.msg.(type) { switch msg := nMsg.msg.(type) {
// A new node announcement has arrived which either presents a new // A new node announcement has arrived which either presents a new
// node, or a node updating previously advertised information. // node, or a node updating previously advertised information.
case *lnwire.NodeAnnouncement: case *lnwire.NodeAnnouncement:
if aMsg.isRemote { if nMsg.isRemote {
// TODO(andrew.shvv) Add node validation // TODO(andrew.shvv) add validation
} }
node := &channeldb.LightningNode{ node := &channeldb.LightningNode{
@@ -426,10 +457,25 @@ func (d *Discovery) processNetworkAnnouncement(aMsg *networkMsg) bool {
} }
if err := d.cfg.Router.AddNode(node); err != nil { if err := d.cfg.Router.AddNode(node); err != nil {
log.Errorf("unable to add node: %v", err) e := errors.Errorf("unable to add node: %v", err)
return false if routing.IsError(err,
routing.ErrOutdated,
routing.ErrIgnored) {
log.Info(e)
} else {
log.Error(e)
}
nMsg.err <- e
return nil
} }
// Node announcement was successfully proceeded and know it
// might be broadcasted to other connected nodes.
announcements = append(announcements, msg)
nMsg.err <- nil
return announcements
// A new channel announcement has arrived, this indicates the // A new channel announcement has arrived, this indicates the
// *creation* of a new channel within the network. This only advertises // *creation* of a new channel within the network. This only advertises
// the existence of a channel and not yet the routing policies in // the existence of a channel and not yet the routing policies in
@@ -438,7 +484,7 @@ func (d *Discovery) processNetworkAnnouncement(aMsg *networkMsg) bool {
// If the advertised inclusionary block is beyond our knowledge // If the advertised inclusionary block is beyond our knowledge
// of the chain tip, then we'll put the announcement in limbo // of the chain tip, then we'll put the announcement in limbo
// to be fully verified once we advance forward in the chain. // to be fully verified once we advance forward in the chain.
if isPremature(&msg.ShortChannelID) { if isPremature(msg.ShortChannelID, 0) {
blockHeight := msg.ShortChannelID.BlockHeight blockHeight := msg.ShortChannelID.BlockHeight
log.Infof("Announcement for chan_id=(%v), is "+ log.Infof("Announcement for chan_id=(%v), is "+
"premature: advertises height %v, only height "+ "premature: advertises height %v, only height "+
@@ -447,21 +493,21 @@ func (d *Discovery) processNetworkAnnouncement(aMsg *networkMsg) bool {
d.prematureAnnouncements[blockHeight] = append( d.prematureAnnouncements[blockHeight] = append(
d.prematureAnnouncements[blockHeight], d.prematureAnnouncements[blockHeight],
aMsg, nMsg,
) )
return false return nil
} }
var proof *channeldb.ChannelAuthProof var proof *channeldb.ChannelAuthProof
if aMsg.isRemote { if nMsg.isRemote {
// TODO(andrew.shvv) Add channel validation // TODO(andrew.shvv) Add validation
}
proof = &channeldb.ChannelAuthProof{ proof = &channeldb.ChannelAuthProof{
NodeSig1: msg.NodeSig1, NodeSig1: msg.NodeSig1,
NodeSig2: msg.NodeSig2, NodeSig2: msg.NodeSig2,
BitcoinSig1: msg.BitcoinSig1, BitcoinSig1: msg.BitcoinSig1,
BitcoinSig2: msg.BitcoinSig2, BitcoinSig2: msg.BitcoinSig2,
}
} }
edge := &channeldb.ChannelEdgeInfo{ edge := &channeldb.ChannelEdgeInfo{
@@ -474,45 +520,69 @@ func (d *Discovery) processNetworkAnnouncement(aMsg *networkMsg) bool {
} }
if err := d.cfg.Router.AddEdge(edge); err != nil { if err := d.cfg.Router.AddEdge(edge); err != nil {
if !routing.IsError(err, routing.ErrOutdated) { e := errors.Errorf("unable to add edge: %v", err)
log.Errorf("unable to add edge: %v", err) if routing.IsError(err,
routing.ErrOutdated,
routing.ErrIgnored) {
log.Info(e)
} else { } else {
log.Info("Unable to add edge: %v", err) log.Error(e)
} }
nMsg.err <- e
return false return nil
} }
// A new authenticated channel updates has arrived, this indicates // Channel announcement was successfully proceeded and know it
// might be broadcasted to other connected nodes if it was
// announcement with proof (remote).
if proof != nil {
announcements = append(announcements, msg)
}
nMsg.err <- nil
return announcements
// A new authenticated channel edges has arrived, this indicates
// that the directional information for an already known channel has // that the directional information for an already known channel has
// been updated. // been updated.
case *lnwire.ChannelUpdateAnnouncement: case *lnwire.ChannelUpdateAnnouncement:
chanID := msg.ShortChannelID.ToUint64() blockHeight := msg.ShortChannelID.BlockHeight
shortChanID := msg.ShortChannelID.ToUint64()
// If the advertised inclusionary block is beyond our knowledge // If the advertised inclusionary block is beyond our knowledge
// of the chain tip, then we'll put the announcement in limbo // of the chain tip, then we'll put the announcement in limbo
// to be fully verified once we advance forward in the chain. // to be fully verified once we advance forward in the chain.
if isPremature(&msg.ShortChannelID) { if isPremature(msg.ShortChannelID, 0) {
blockHeight := msg.ShortChannelID.BlockHeight log.Infof("Update announcement for "+
log.Infof("Update announcement for chan_id=(%v), is "+ "shortChanID=(%v), is premature: advertises "+
"premature: advertises height %v, only height "+ "height %v, only height %v is known",
"%v is known", chanID, blockHeight, shortChanID, blockHeight, d.bestHeight)
d.bestHeight)
d.prematureAnnouncements[blockHeight] = append( d.prematureAnnouncements[blockHeight] = append(
d.prematureAnnouncements[blockHeight], d.prematureAnnouncements[blockHeight],
aMsg, nMsg,
) )
return false return nil
} }
if aMsg.isRemote { // Get the node pub key as far as we don't have it in
// TODO(andrew.shvv) Add update channel validation // channel update announcement message and verify
// message signature.
chanInfo, _, _, err := d.cfg.Router.GetChannelByID(msg.ShortChannelID)
if err != nil {
err := errors.Errorf("unable to validate "+
"channel update shortChanID=%v: %v",
shortChanID, err)
nMsg.err <- err
return nil
} }
// TODO(andrew.shvv) Add validation
// TODO(roasbeef): should be msat here // TODO(roasbeef): should be msat here
update := &channeldb.ChannelEdgePolicy{ update := &channeldb.ChannelEdgePolicy{
ChannelID: chanID, Signature: msg.Signature,
ChannelID: shortChanID,
LastUpdate: time.Unix(int64(msg.Timestamp), 0), LastUpdate: time.Unix(int64(msg.Timestamp), 0),
Flags: msg.Flags, Flags: msg.Flags,
TimeLockDelta: msg.TimeLockDelta, TimeLockDelta: msg.TimeLockDelta,
@@ -522,12 +592,200 @@ func (d *Discovery) processNetworkAnnouncement(aMsg *networkMsg) bool {
} }
if err := d.cfg.Router.UpdateEdge(update); err != nil { if err := d.cfg.Router.UpdateEdge(update); err != nil {
log.Errorf("unable to update edge: %v", err) e := errors.Errorf("unable to update edge: %v", err)
return false if routing.IsError(err,
} routing.ErrOutdated,
} routing.ErrIgnored) {
log.Info(e)
} else {
log.Error(e)
}
return true nMsg.err <- e
return nil
}
// Channel update announcement was successfully proceeded and
// know it might be broadcasted to other connected nodes.
// We should announce the edge to rest of the network only
// if channel has the authentication proof.
if chanInfo.AuthProof != nil {
announcements = append(announcements, msg)
}
nMsg.err <- nil
return announcements
// New signature announcement received which indicates willingness
// of the parties (to exchange the channel signatures / announce newly
// created channel).
case *lnwire.AnnounceSignatures:
needBlockHeight := msg.ShortChannelID.BlockHeight + d.cfg.ProofMatureDelta
shortChanID := msg.ShortChannelID.ToUint64()
prefix := "local"
if nMsg.isRemote {
prefix = "remote"
}
// By the specification proof should be sent after some number of
// confirmations after channel was registered in bitcoin
// blockchain. So we should check that proof is premature and
// if not send it to the be proceeded again. This allows us to
// be tolerant to other clients if this constraint was changed.
if isPremature(msg.ShortChannelID, d.cfg.ProofMatureDelta) {
d.prematureAnnouncements[needBlockHeight] = append(
d.prematureAnnouncements[needBlockHeight],
nMsg,
)
log.Infof("Premature proof annoucement, "+
"current block height lower than needed: %v <"+
" %v, add announcement to reprocessing batch",
d.bestHeight, needBlockHeight)
return nil
}
// Check that we have channel with such channel id in out
// lightning network topology.
chanInfo, e1, e2, err := d.cfg.Router.GetChannelByID(msg.ShortChannelID)
if err != nil {
err := errors.Errorf("unable to process channel "+
"%v proof with shortChanID=%v: %v", prefix,
shortChanID, err)
nMsg.err <- err
return nil
}
isFirstNode := bytes.Equal(nMsg.peer.SerializeCompressed(),
chanInfo.NodeKey1.SerializeCompressed())
isSecondNode := bytes.Equal(nMsg.peer.SerializeCompressed(),
chanInfo.NodeKey2.SerializeCompressed())
// Check that channel that was retrieved belongs to the peer
// which sent the proof announcement, otherwise the proof for
// might be rewritten by the any lightning network node.
if !(isFirstNode || isSecondNode) {
err := errors.Errorf("channel that was received not "+
"belongs to the peer which sent the proof, "+
"shortChanID=%v", shortChanID)
log.Error(err)
nMsg.err <- err
return nil
}
// Check that we received the opposite proof, if so, than we
// should construct the full proof, and create the channel
// announcement. If we didn't receive the opposite half of the
// proof than we should store it this one, and wait for opposite
// to be received.
oppositeKey := newProofKey(chanInfo.ChannelID, !nMsg.isRemote)
oppositeProof, ok := d.waitingProofs[oppositeKey]
if !ok {
key := newProofKey(chanInfo.ChannelID, nMsg.isRemote)
d.waitingProofs[key] = msg
// If proof was send from funding manager than we
// should send the announce signature message to
// remote side.
if !nMsg.isRemote {
// Check that first node of the channel info
// corresponds to us.
var remotePeer *btcec.PublicKey
if isFirstNode {
remotePeer = chanInfo.NodeKey2
} else {
remotePeer = chanInfo.NodeKey1
}
err := d.cfg.SendToPeer(remotePeer, msg)
if err != nil {
log.Errorf("unable to send "+
"announcement message to "+
"peer: %x",
remotePeer.SerializeCompressed())
}
log.Infof("Send channel announcement proof "+
"for shortChanID=%v to remote peer: "+
"%x", shortChanID, remotePeer.SerializeCompressed())
}
log.Infof("Incoming %v proof announcement for "+
"shortChanID=%v have been proceeded and waiting for opposite proof",
prefix, shortChanID)
nMsg.err <- nil
return nil
}
var dbProof channeldb.ChannelAuthProof
if isFirstNode {
dbProof.NodeSig1 = msg.NodeSignature
dbProof.NodeSig2 = oppositeProof.NodeSignature
dbProof.BitcoinSig1 = msg.BitcoinSignature
dbProof.BitcoinSig2 = oppositeProof.BitcoinSignature
} else {
dbProof.NodeSig1 = oppositeProof.NodeSignature
dbProof.NodeSig2 = msg.NodeSignature
dbProof.BitcoinSig1 = oppositeProof.BitcoinSignature
dbProof.BitcoinSig2 = msg.BitcoinSignature
}
chanAnn, e1Ann, e2Ann := createChanAnnouncement(&dbProof, chanInfo, e1, e2)
// TODO(andrew.shvv) Add validation
// If the channel was returned by the router it means that
// existence of funding point and inclusion of nodes bitcoin
// keys in it already checked by the router. On this stage we
// should check that node keys are corresponds to the bitcoin
// keys by validating the signatures of announcement.
// If proof is valid than we should populate the channel
// edge with it, so we can announce it on peer connect.
err = d.cfg.Router.AddProof(msg.ShortChannelID, &dbProof)
if err != nil {
err := errors.Errorf("unable add proof to the "+
"channel chanID=%v: %v", msg.ChannelID, err)
log.Error(err)
nMsg.err <- err
return nil
}
// Proof was successfully created and now can announce the
// channel to the remain network.
log.Infof("Incoming %v proof announcement for shortChanID=%v"+
" have been proceeded, adding channel announcement in"+
" the broadcasting batch", prefix, shortChanID)
announcements = append(announcements, chanAnn)
if e1Ann != nil {
announcements = append(announcements, e1Ann)
}
if e2Ann != nil {
announcements = append(announcements, e2Ann)
}
if !nMsg.isRemote {
var remotePeer *btcec.PublicKey
if isFirstNode {
remotePeer = chanInfo.NodeKey2
} else {
remotePeer = chanInfo.NodeKey1
}
err = d.cfg.SendToPeer(remotePeer, msg)
if err != nil {
log.Errorf("unable to send announcement "+
"message to peer: %x",
remotePeer.SerializeCompressed())
}
}
nMsg.err <- nil
return announcements
default:
nMsg.err <- errors.New("wrong type of the announcement")
return nil
}
} }
// synchronize attempts to synchronize the target node in the syncReq to // synchronize attempts to synchronize the target node in the syncReq to
@@ -555,7 +813,7 @@ func (d *Discovery) synchronize(syncReq *syncRequest) error {
} }
ann := &lnwire.NodeAnnouncement{ ann := &lnwire.NodeAnnouncement{
Signature: d.fakeSig, Signature: node.AuthSig,
Timestamp: uint32(node.LastUpdate.Unix()), Timestamp: uint32(node.LastUpdate.Unix()),
Addresses: node.Addresses, Addresses: node.Addresses,
NodeID: node.PubKey, NodeID: node.PubKey,
@@ -573,71 +831,39 @@ func (d *Discovery) synchronize(syncReq *syncRequest) error {
// With the vertexes gathered, we'll no retrieve the initial // With the vertexes gathered, we'll no retrieve the initial
// announcement, as well as the latest channel update announcement for // announcement, as well as the latest channel update announcement for
// both of the directed edges that make up the channel. // both of the directed infos that make up the channel.
var numEdges uint32 var numEdges uint32
if err := d.cfg.Router.ForEachChannel(func(chanInfo *channeldb.ChannelEdgeInfo, if err := d.cfg.Router.ForEachChannel(func(chanInfo *channeldb.ChannelEdgeInfo,
e1, e2 *channeldb.ChannelEdgePolicy) error { e1, e2 *channeldb.ChannelEdgePolicy) error {
chanID := lnwire.NewShortChanIDFromInt(chanInfo.ChannelID)
// First, using the parameters of the channel, along with the // First, using the parameters of the channel, along with the
// channel authentication proof, we'll create re-create the // channel authentication proof, we'll create re-create the
// original authenticated channel announcement. // original authenticated channel announcement.
// TODO(andrew.shvv) skip if proof is nil if chanInfo.AuthProof != nil {
authProof := chanInfo.AuthProof chanAnn, e1Ann, e2Ann := createChanAnnouncement(
chanAnn := &lnwire.ChannelAnnouncement{ chanInfo.AuthProof, chanInfo, e1, e2)
NodeSig1: authProof.NodeSig1,
NodeSig2: authProof.NodeSig2,
ShortChannelID: chanID,
BitcoinSig1: authProof.BitcoinSig1,
BitcoinSig2: authProof.BitcoinSig2,
NodeID1: chanInfo.NodeKey1,
NodeID2: chanInfo.NodeKey2,
BitcoinKey1: chanInfo.BitcoinKey1,
BitcoinKey2: chanInfo.BitcoinKey2,
}
announceMessages = append(announceMessages, chanAnn)
// Since it's up to a node's policy as to whether they announceMessages = append(announceMessages, chanAnn)
// advertise the edge in dire direction, we don't create an if e1Ann != nil {
// advertisement if the edge is nil. announceMessages = append(announceMessages, e1Ann)
if e1 != nil { }
announceMessages = append(announceMessages, &lnwire.ChannelUpdateAnnouncement{ if e2Ann != nil {
Signature: d.fakeSig, announceMessages = append(announceMessages, e2Ann)
ShortChannelID: chanID, }
Timestamp: uint32(e1.LastUpdate.Unix()),
Flags: 0, numEdges++
TimeLockDelta: e1.TimeLockDelta,
HtlcMinimumMsat: uint32(e1.MinHTLC),
FeeBaseMsat: uint32(e1.FeeBaseMSat),
FeeProportionalMillionths: uint32(e1.FeeProportionalMillionths),
})
}
if e2 != nil {
announceMessages = append(announceMessages, &lnwire.ChannelUpdateAnnouncement{
Signature: d.fakeSig,
ShortChannelID: chanID,
Timestamp: uint32(e2.LastUpdate.Unix()),
Flags: 1,
TimeLockDelta: e2.TimeLockDelta,
HtlcMinimumMsat: uint32(e2.MinHTLC),
FeeBaseMsat: uint32(e2.FeeBaseMSat),
FeeProportionalMillionths: uint32(e2.FeeProportionalMillionths),
})
} }
numEdges++
return nil return nil
}); err != nil && err != channeldb.ErrGraphNoEdgesFound { }); err != nil && err != channeldb.ErrGraphNoEdgesFound {
log.Errorf("unable to sync edges w/ peer: %v", err) log.Errorf("unable to sync infos with peer: %v", err)
return err return err
} }
log.Infof("Syncing channel graph state with %x, sending %v "+ log.Infof("Syncing channel graph state with %x, sending %v "+
"nodes and %v edges", targetNode.SerializeCompressed(), "nodes and %v infos", targetNode.SerializeCompressed(),
numNodes, numEdges) numNodes, numEdges)
// With all the announcement messages gathered, send them all in a // With all the announcement messages gathered, send them all in a
// single batch to the target peer. // single batch to the target peer.
return d.cfg.SendMessages(targetNode, announceMessages...) return d.cfg.SendToPeer(targetNode, announceMessages...)
} }
+323 -90
View File
@@ -13,6 +13,7 @@ import (
"time" "time"
"github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
@@ -33,18 +34,39 @@ var (
} }
_, _ = testSig.R.SetString("63724406601629180062774974542967536251589935445068131219452686511677818569431", 10) _, _ = testSig.R.SetString("63724406601629180062774974542967536251589935445068131219452686511677818569431", 10)
_, _ = testSig.S.SetString("18801056069249825825291287104931333862866033135609736119018462340006816851118", 10) _, _ = testSig.S.SetString("18801056069249825825291287104931333862866033135609736119018462340006816851118", 10)
inputStr = "147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b"
sha, _ = chainhash.NewHashFromStr(inputStr)
outpoint = wire.NewOutPoint(sha, 0)
bitcoinKeyPriv1, _ = btcec.NewPrivateKey(btcec.S256())
bitcoinKeyPub1 = bitcoinKeyPriv1.PubKey()
nodeKeyPriv1, _ = btcec.NewPrivateKey(btcec.S256())
nodeKeyPub1 = nodeKeyPriv1.PubKey()
bitcoinKeyPriv2, _ = btcec.NewPrivateKey(btcec.S256())
bitcoinKeyPub2 = bitcoinKeyPriv2.PubKey()
nodeKeyPriv2, _ = btcec.NewPrivateKey(btcec.S256())
nodeKeyPub2 = nodeKeyPriv2.PubKey()
trickleDelay = time.Millisecond * 300
proofMatureDelta uint32
) )
type mockGraphSource struct { type mockGraphSource struct {
nodes []*channeldb.LightningNode nodes []*channeldb.LightningNode
edges []*channeldb.ChannelEdgeInfo infos map[uint64]*channeldb.ChannelEdgeInfo
updates []*channeldb.ChannelEdgePolicy edges map[uint64][]*channeldb.ChannelEdgePolicy
bestHeight uint32 bestHeight uint32
} }
func newMockRouter(height uint32) *mockGraphSource { func newMockRouter(height uint32) *mockGraphSource {
return &mockGraphSource{ return &mockGraphSource{
bestHeight: height, bestHeight: height,
infos: make(map[uint64]*channeldb.ChannelEdgeInfo),
edges: make(map[uint64][]*channeldb.ChannelEdgePolicy),
} }
} }
@@ -55,13 +77,19 @@ func (r *mockGraphSource) AddNode(node *channeldb.LightningNode) error {
return nil return nil
} }
func (r *mockGraphSource) AddEdge(edge *channeldb.ChannelEdgeInfo) error { func (r *mockGraphSource) AddEdge(info *channeldb.ChannelEdgeInfo) error {
r.edges = append(r.edges, edge) if _, ok := r.infos[info.ChannelID]; ok {
return errors.New("info already exist")
}
r.infos[info.ChannelID] = info
return nil return nil
} }
func (r *mockGraphSource) UpdateEdge(policy *channeldb.ChannelEdgePolicy) error { func (r *mockGraphSource) UpdateEdge(edge *channeldb.ChannelEdgePolicy) error {
r.updates = append(r.updates, policy) r.edges[edge.ChannelID] = append(
r.edges[edge.ChannelID],
edge,
)
return nil return nil
} }
@@ -91,6 +119,28 @@ func (r *mockGraphSource) ForEachChannel(func(chanInfo *channeldb.ChannelEdgeInf
return nil return nil
} }
func (r *mockGraphSource) GetChannelByID(chanID lnwire.ShortChannelID) (
*channeldb.ChannelEdgeInfo,
*channeldb.ChannelEdgePolicy,
*channeldb.ChannelEdgePolicy, error) {
chanInfo, ok := r.infos[chanID.ToUint64()]
if !ok {
return nil, nil, nil, errors.New("can't find channel info")
}
edges := r.edges[chanID.ToUint64()]
if len(edges) == 0 {
return chanInfo, nil, nil, nil
}
if len(edges) == 1 {
return chanInfo, edges[0], nil, nil
}
return chanInfo, edges[0], edges[1], nil
}
type mockNotifier struct { type mockNotifier struct {
clientCounter uint32 clientCounter uint32
epochClients map[uint32]chan *chainntnfs.BlockEpoch epochClients map[uint32]chan *chainntnfs.BlockEpoch
@@ -149,31 +199,87 @@ func (m *mockNotifier) Stop() error {
return nil return nil
} }
func createNodeAnnouncement() (*lnwire.NodeAnnouncement, type annBatch struct {
nodeAnn1 *lnwire.NodeAnnouncement
nodeAnn2 *lnwire.NodeAnnouncement
localChanAnn *lnwire.ChannelAnnouncement
remoteChanAnn *lnwire.ChannelAnnouncement
chanUpdAnn *lnwire.ChannelUpdateAnnouncement
localProofAnn *lnwire.AnnounceSignatures
remoteProofAnn *lnwire.AnnounceSignatures
}
func createAnnouncements(blockHeight uint32) (*annBatch, error) {
var err error
var batch annBatch
batch.nodeAnn1, err = createNodeAnnouncement(nodeKeyPriv1)
if err != nil {
return nil, err
}
batch.nodeAnn2, err = createNodeAnnouncement(nodeKeyPriv2)
if err != nil {
return nil, err
}
batch.remoteChanAnn, err = createRemoteChannelAnnouncement(blockHeight)
if err != nil {
return nil, err
}
batch.localProofAnn = &lnwire.AnnounceSignatures{
NodeSignature: batch.remoteChanAnn.NodeSig1,
BitcoinSignature: batch.remoteChanAnn.BitcoinSig1,
}
batch.remoteProofAnn = &lnwire.AnnounceSignatures{
NodeSignature: batch.remoteChanAnn.NodeSig2,
BitcoinSignature: batch.remoteChanAnn.BitcoinSig2,
}
batch.localChanAnn, err = createRemoteChannelAnnouncement(blockHeight)
if err != nil {
return nil, err
}
batch.localChanAnn.BitcoinSig1 = nil
batch.localChanAnn.BitcoinSig2 = nil
batch.localChanAnn.NodeSig1 = nil
batch.localChanAnn.NodeSig2 = nil
batch.chanUpdAnn, err = createUpdateAnnouncement(blockHeight)
if err != nil {
return nil, err
}
return &batch, nil
}
func createNodeAnnouncement(priv *btcec.PrivateKey) (*lnwire.NodeAnnouncement,
error) { error) {
priv, err := btcec.NewPrivateKey(btcec.S256())
alias, err := lnwire.NewAlias("kek" + string(priv.Serialize()))
if err != nil { if err != nil {
return nil, err return nil, err
} }
pub := priv.PubKey().SerializeCompressed() a := &lnwire.NodeAnnouncement{
Signature: testSig,
alias, err := lnwire.NewAlias("kek" + string(pub[:]))
if err != nil {
return nil, err
}
return &lnwire.NodeAnnouncement{
Timestamp: uint32(prand.Int31()), Timestamp: uint32(prand.Int31()),
Addresses: testAddrs, Addresses: testAddrs,
NodeID: priv.PubKey(), NodeID: priv.PubKey(),
Alias: alias, Alias: alias,
Features: testFeatures, Features: testFeatures,
}, nil }
return a, nil
} }
func createUpdateAnnouncement(blockHeight uint32) *lnwire.ChannelUpdateAnnouncement { func createUpdateAnnouncement(blockHeight uint32) (*lnwire.ChannelUpdateAnnouncement,
return &lnwire.ChannelUpdateAnnouncement{ error) {
a := &lnwire.ChannelUpdateAnnouncement{
Signature: testSig, Signature: testSig,
ShortChannelID: lnwire.ShortChannelID{ ShortChannelID: lnwire.ShortChannelID{
BlockHeight: blockHeight, BlockHeight: blockHeight,
@@ -184,26 +290,37 @@ func createUpdateAnnouncement(blockHeight uint32) *lnwire.ChannelUpdateAnnouncem
FeeBaseMsat: uint32(prand.Int31()), FeeBaseMsat: uint32(prand.Int31()),
FeeProportionalMillionths: uint32(prand.Int31()), FeeProportionalMillionths: uint32(prand.Int31()),
} }
return a, nil
} }
func createChannelAnnouncement(blockHeight uint32) *lnwire.ChannelAnnouncement { func createRemoteChannelAnnouncement(blockHeight uint32) (*lnwire.ChannelAnnouncement,
// Our fake channel will be "confirmed" at height 101. error) {
chanID := lnwire.ShortChannelID{
BlockHeight: blockHeight, a := &lnwire.ChannelAnnouncement{
TxIndex: 0, ShortChannelID: lnwire.ShortChannelID{
TxPosition: 0, BlockHeight: blockHeight,
TxIndex: 0,
TxPosition: 0,
},
NodeID1: nodeKeyPub1,
NodeID2: nodeKeyPub2,
BitcoinKey1: bitcoinKeyPub1,
BitcoinKey2: bitcoinKeyPub2,
NodeSig1: testSig,
NodeSig2: testSig,
BitcoinSig1: testSig,
BitcoinSig2: testSig,
} }
return &lnwire.ChannelAnnouncement{ return a, nil
ShortChannelID: chanID,
}
} }
type testCtx struct { type testCtx struct {
discovery *Discovery discovery *Discovery
router *mockGraphSource router *mockGraphSource
notifier *mockNotifier notifier *mockNotifier
broadcastedMessage chan lnwire.Message broadcastedMessage chan lnwire.Message
} }
@@ -215,7 +332,7 @@ func createTestCtx(startHeight uint32) (*testCtx, func(), error) {
notifier := newMockNotifier() notifier := newMockNotifier()
router := newMockRouter(startHeight) router := newMockRouter(startHeight)
broadcastedMessage := make(chan lnwire.Message) broadcastedMessage := make(chan lnwire.Message, 10)
discovery, err := New(Config{ discovery, err := New(Config{
Notifier: notifier, Notifier: notifier,
Broadcast: func(_ *btcec.PublicKey, msgs ...lnwire.Message) error { Broadcast: func(_ *btcec.PublicKey, msgs ...lnwire.Message) error {
@@ -224,8 +341,12 @@ func createTestCtx(startHeight uint32) (*testCtx, func(), error) {
} }
return nil return nil
}, },
SendMessages: nil, SendToPeer: func(target *btcec.PublicKey, msg ...lnwire.Message) error {
Router: router, return nil
},
Router: router,
TrickleDelay: trickleDelay,
ProofMatureDelta: proofMatureDelta,
}) })
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("unable to create router %v", err) return nil, nil, fmt.Errorf("unable to create router %v", err)
@@ -255,21 +376,23 @@ func TestProcessAnnouncement(t *testing.T) {
} }
defer cleanup() defer cleanup()
priv, err := btcec.NewPrivateKey(btcec.S256()) // Create node valid, signed announcement, process it with with
if err != nil { // discovery service, check that valid announcement have been
t.Fatalf("can't create node pub key: %v", err) // propagated farther into the lightning network, and check that we
} // added new node into router.
nodePub := priv.PubKey() na, err := createNodeAnnouncement(nodeKeyPriv1)
na, err := createNodeAnnouncement()
if err != nil { if err != nil {
t.Fatalf("can't create node announcement: %v", err) t.Fatalf("can't create node announcement: %v", err)
} }
ctx.discovery.ProcessRemoteAnnouncement(na, nodePub)
err = <-ctx.discovery.ProcessRemoteAnnouncement(na, na.NodeID)
if err != nil {
t.Fatalf("can't process remote announcement: %v", err)
}
select { select {
case <-ctx.broadcastedMessage: case <-ctx.broadcastedMessage:
case <-time.After(time.Second): case <-time.After(2 * trickleDelay):
t.Fatal("announcememt wasn't proceeded") t.Fatal("announcememt wasn't proceeded")
} }
@@ -277,32 +400,54 @@ func TestProcessAnnouncement(t *testing.T) {
t.Fatalf("node wasn't added to router: %v", err) t.Fatalf("node wasn't added to router: %v", err)
} }
ca := createChannelAnnouncement(0) // Pretending that we receive the valid channel announcement from
ctx.discovery.ProcessRemoteAnnouncement(ca, nodePub) // remote side, and check that we broadcasted it to the our network,
// and added channel info in the router.
ca, err := createRemoteChannelAnnouncement(0)
if err != nil {
t.Fatalf("can't create channel announcement: %v", err)
}
err = <-ctx.discovery.ProcessRemoteAnnouncement(ca, na.NodeID)
if err != nil {
t.Fatalf("can't process remote announcement: %v", err)
}
select { select {
case <-ctx.broadcastedMessage: case <-ctx.broadcastedMessage:
case <-time.After(time.Second): case <-time.After(2 * trickleDelay):
t.Fatal("announcememt wasn't proceeded")
}
if len(ctx.router.infos) != 1 {
t.Fatalf("edge wasn't added to router: %v", err)
}
// Pretending that we received valid channel policy update from remote
// side, and check that we broadcasted it to the other network, and
// added updates to the router.
ua, err := createUpdateAnnouncement(0)
if err != nil {
t.Fatalf("can't create update announcement: %v", err)
}
err = <-ctx.discovery.ProcessRemoteAnnouncement(ua, na.NodeID)
if err != nil {
t.Fatalf("can't process remote announcement: %v", err)
}
select {
case <-ctx.broadcastedMessage:
case <-time.After(2 * trickleDelay):
t.Fatal("announcememt wasn't proceeded") t.Fatal("announcememt wasn't proceeded")
} }
if len(ctx.router.edges) != 1 { if len(ctx.router.edges) != 1 {
t.Fatalf("edge wasn't added to router: %v", err)
}
ua := createUpdateAnnouncement(0)
ctx.discovery.ProcessRemoteAnnouncement(ua, nodePub)
select {
case <-ctx.broadcastedMessage:
case <-time.After(time.Second):
t.Fatal("announcememt wasn't proceeded")
}
if len(ctx.router.updates) != 1 {
t.Fatalf("edge update wasn't added to router: %v", err) t.Fatalf("edge update wasn't added to router: %v", err)
} }
} }
// TestPrematureAnnouncement checks that premature networkMsgs are // TestPrematureAnnouncement checks that premature announcements are
// not propagated to the router subsystem until block with according // not propagated to the router subsystem until block with according
// block height received. // block height received.
func TestPrematureAnnouncement(t *testing.T) { func TestPrematureAnnouncement(t *testing.T) {
@@ -312,62 +457,150 @@ func TestPrematureAnnouncement(t *testing.T) {
} }
defer cleanup() defer cleanup()
priv, err := btcec.NewPrivateKey(btcec.S256()) na, err := createNodeAnnouncement(nodeKeyPriv1)
if err != nil { if err != nil {
t.Fatalf("can't create node pub key: %v", err) t.Fatalf("can't create node announcement: %v", err)
}
// 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
// repeat/premature batch.
ca, err := createRemoteChannelAnnouncement(1)
if err != nil {
t.Fatalf("can't create channel announcement: %v", err)
} }
nodePub := priv.PubKey()
ca := createChannelAnnouncement(1)
ctx.discovery.ProcessRemoteAnnouncement(ca, nodePub)
select { select {
case <-ctx.broadcastedMessage: case <-ctx.discovery.ProcessRemoteAnnouncement(ca, na.NodeID):
t.Fatal("announcement was proceeded")
case <-time.After(100 * time.Millisecond):
}
if len(ctx.router.infos) != 0 {
t.Fatal("edge was added to router")
}
// Pretending that we receive the valid channel update 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
// repeat/premature batch.
ua, err := createUpdateAnnouncement(1)
if err != nil {
t.Fatalf("can't create update announcement: %v", err)
}
select {
case <-ctx.discovery.ProcessRemoteAnnouncement(ua, na.NodeID):
t.Fatal("announcement was proceeded") t.Fatal("announcement was proceeded")
case <-time.After(100 * time.Millisecond): case <-time.After(100 * time.Millisecond):
} }
if len(ctx.router.edges) != 0 { if len(ctx.router.edges) != 0 {
t.Fatal("edge was added to router")
}
ua := createUpdateAnnouncement(1)
ctx.discovery.ProcessRemoteAnnouncement(ua, nodePub)
select {
case <-ctx.broadcastedMessage:
t.Fatal("announcement was proceeded")
case <-time.After(100 * time.Millisecond):
}
if len(ctx.router.updates) != 0 {
t.Fatal("edge update was added to router") t.Fatal("edge update was added to router")
} }
// Generate new block and waiting the previously added announcements
// to be proceeded.
newBlock := &wire.MsgBlock{} newBlock := &wire.MsgBlock{}
ctx.notifier.notifyBlock(newBlock.Header.BlockHash(), 1) ctx.notifier.notifyBlock(newBlock.Header.BlockHash(), 1)
select { select {
case <-ctx.broadcastedMessage: case <-ctx.broadcastedMessage:
if err != nil { case <-time.After(2 * trickleDelay):
t.Fatalf("announcememt was proceeded with err: %v", err) t.Fatal("announcememt wasn't broadcasted")
}
case <-time.After(time.Second):
t.Fatal("announcememt wasn't proceeded")
} }
if len(ctx.router.edges) != 1 { if len(ctx.router.infos) != 1 {
t.Fatalf("edge was't added to router: %v", err) t.Fatalf("edge was't added to router: %v", err)
} }
select { select {
case <-ctx.broadcastedMessage: case <-ctx.broadcastedMessage:
if err != nil { case <-time.After(2 * trickleDelay):
t.Fatalf("announcememt was proceeded with err: %v", err) t.Fatal("announcememt wasn't broadcasted")
}
case <-time.After(time.Second):
t.Fatal("announcememt wasn't proceeded")
} }
if len(ctx.router.updates) != 1 { if len(ctx.router.edges) != 1 {
t.Fatalf("edge update wasn't added to router: %v", err) t.Fatalf("edge update wasn't added to router: %v", err)
} }
} }
// TestSignatureAnnouncement....
func TestSignatureAnnouncement(t *testing.T) {
ctx, cleanup, err := createTestCtx(proofMatureDelta)
if err != nil {
t.Fatalf("can't create context: %v", err)
}
defer cleanup()
batch, err := createAnnouncements(0)
if err != nil {
t.Fatalf("can't generate announcements: %v", err)
}
localKey := batch.nodeAnn1.NodeID
remoteKey := batch.nodeAnn2.NodeID
// Recreate lightning network topology. Initialize router with
// channel between two nodes.
err = <-ctx.discovery.ProcessLocalAnnouncement(batch.localChanAnn, localKey)
if err != nil {
t.Fatalf("unable to process :%v", err)
}
select {
case <-ctx.broadcastedMessage:
t.Fatal("channel announcement was broadcasted")
case <-time.After(2 * trickleDelay):
}
err = <-ctx.discovery.ProcessLocalAnnouncement(batch.chanUpdAnn, localKey)
if err != nil {
t.Fatalf("unable to process :%v", err)
}
select {
case <-ctx.broadcastedMessage:
t.Fatal("channel update announcement was broadcasted")
case <-time.After(2 * trickleDelay):
}
err = <-ctx.discovery.ProcessRemoteAnnouncement(batch.chanUpdAnn, remoteKey)
if err != nil {
t.Fatalf("unable to process :%v", err)
}
select {
case <-ctx.broadcastedMessage:
t.Fatal("channel update announcement was broadcasted")
case <-time.After(2 * trickleDelay):
}
// Pretending that we receive local channel announcement from funding
// manager, thereby kick off the announcement exchange process.
err = <-ctx.discovery.ProcessLocalAnnouncement(batch.localProofAnn, localKey)
if err != nil {
t.Fatalf("unable to process :%v", err)
}
select {
case <-ctx.broadcastedMessage:
t.Fatal("announcements were broadcasted")
case <-time.After(2 * trickleDelay):
}
if len(ctx.discovery.waitingProofs) != 1 {
t.Fatal("local proof annoucement should be stored")
}
err = <-ctx.discovery.ProcessRemoteAnnouncement(batch.remoteProofAnn, remoteKey)
if err != nil {
t.Fatalf("unable to process :%v", err)
}
for i := 0; i < 3; i++ {
select {
case <-ctx.broadcastedMessage:
case <-time.After(time.Second):
t.Fatal("announcement wasn't broadcasted")
}
}
}
+93
View File
@@ -0,0 +1,93 @@
package discovery
import (
"encoding/binary"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire"
)
// newProofKey constructs new announcement signature message key.
func newProofKey(chanID uint64, isRemote bool) waitingProofKey {
return waitingProofKey{
chanID: chanID,
isRemote: isRemote,
}
}
// ToBytes represents the key in the byte format.
func (k waitingProofKey) ToBytes() []byte {
var key [10]byte
var b uint8
if k.isRemote {
b = 0
} else {
b = 1
}
binary.BigEndian.PutUint64(key[:], k.chanID)
key[9] = b
return key[:]
}
// createChanAnnouncement helper function which creates the channel announcement
// by the given channeldb objects.
func createChanAnnouncement(chanProof *channeldb.ChannelAuthProof,
chanInfo *channeldb.ChannelEdgeInfo,
e1, e2 *channeldb.ChannelEdgePolicy) (
*lnwire.ChannelAnnouncement,
*lnwire.ChannelUpdateAnnouncement,
*lnwire.ChannelUpdateAnnouncement) {
// 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,
BitcoinKey1: chanInfo.BitcoinKey1,
BitcoinKey2: chanInfo.BitcoinKey2,
}
// We'll unconditionally queue the channel's existence chanProof as
// it will need to be processed before either of the channel
// update networkMsgs.
// Since it's up to a node's policy as to whether they
// advertise the edge in dire direction, we don't create an
// advertisement if the edge is nil.
var edge1Ann, edge2Ann *lnwire.ChannelUpdateAnnouncement
if e1 != nil {
edge1Ann = &lnwire.ChannelUpdateAnnouncement{
Signature: e1.Signature,
ShortChannelID: chanID,
Timestamp: uint32(e1.LastUpdate.Unix()),
Flags: 0,
TimeLockDelta: e1.TimeLockDelta,
HtlcMinimumMsat: uint32(e1.MinHTLC),
FeeBaseMsat: uint32(e1.FeeBaseMSat),
FeeProportionalMillionths: uint32(e1.FeeProportionalMillionths),
}
}
if e2 != nil {
edge2Ann = &lnwire.ChannelUpdateAnnouncement{
Signature: e2.Signature,
ShortChannelID: chanID,
Timestamp: uint32(e2.LastUpdate.Unix()),
Flags: 1,
TimeLockDelta: e2.TimeLockDelta,
HtlcMinimumMsat: uint32(e2.MinHTLC),
FeeBaseMsat: uint32(e2.FeeBaseMSat),
FeeProportionalMillionths: uint32(e2.FeeProportionalMillionths),
}
}
return chanAnn, edge1Ann, edge2Ann
}
+80 -58
View File
@@ -2,7 +2,6 @@ package main
import ( import (
"bytes" "bytes"
"encoding/hex"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@@ -141,9 +140,18 @@ type fundingConfig struct {
// so that the channel creation process can be completed. // so that the channel creation process can be completed.
Notifier chainntnfs.ChainNotifier Notifier chainntnfs.ChainNotifier
// SignNodeKey is used to generate signature for node public key with
// funding keys.
SignNodeKey func(nodeKey, fundingKey *btcec.PublicKey) (*btcec.Signature, error)
// SignAnnouncement is used to generate the signatures for channel
// update, and node announcements, and also to generate the proof for
// the channel announcement.
SignAnnouncement func(msg lnwire.Message) (*btcec.Signature, error)
// SendToDiscovery is used by the FundingManager to announce newly created // SendToDiscovery is used by the FundingManager to announce newly created
// channels to the rest of the Lightning Network. // channels to the rest of the Lightning Network.
SendToDiscovery func(msg lnwire.Message) SendToDiscovery func(msg lnwire.Message) error
// SendToPeer allows the FundingManager to send messages to the peer // SendToPeer allows the FundingManager to send messages to the peer
// node during the multiple steps involved in the creation of the // node during the multiple steps involved in the creation of the
@@ -202,8 +210,6 @@ type fundingManager struct {
barrierMtx sync.RWMutex barrierMtx sync.RWMutex
newChanBarriers map[wire.OutPoint]chan struct{} newChanBarriers map[wire.OutPoint]chan struct{}
fakeProof *channelProof
quit chan struct{} quit chan struct{}
wg sync.WaitGroup wg sync.WaitGroup
} }
@@ -211,22 +217,8 @@ type fundingManager struct {
// newFundingManager creates and initializes a new instance of the // newFundingManager creates and initializes a new instance of the
// fundingManager. // fundingManager.
func newFundingManager(cfg fundingConfig) (*fundingManager, error) { func newFundingManager(cfg fundingConfig) (*fundingManager, error) {
// TODO(roasbeef): remove once we actually sign the funding_locked
// stuffs
s := "30450221008ce2bc69281ce27da07e6683571319d18e949ddfa2965fb6caa" +
"1bf0314f882d70220299105481d63e0f4bc2a88121167221b6700d72a0e" +
"ad154c03be696a292d24ae"
fakeSigHex, _ := hex.DecodeString(s)
fakeSig, _ := btcec.ParseSignature(fakeSigHex, btcec.S256())
return &fundingManager{ return &fundingManager{
cfg: &cfg, cfg: &cfg,
fakeProof: &channelProof{
nodeSig: fakeSig,
bitcoinSig: fakeSig,
},
activeReservations: make(map[serializedPubKey]pendingChannels), activeReservations: make(map[serializedPubKey]pendingChannels),
newChanBarriers: make(map[wire.OutPoint]chan struct{}), newChanBarriers: make(map[wire.OutPoint]chan struct{}),
fundingMsgs: make(chan interface{}, msgBufferSize), fundingMsgs: make(chan interface{}, msgBufferSize),
@@ -998,8 +990,9 @@ func (f *fundingManager) handleFundingLocked(fmsg *fundingLockedMsg) {
// Register the new link with the L3 routing manager so this // Register the new link with the L3 routing manager so this
// new channel can be utilized during path // new channel can be utilized during path
// finding. // finding.
go f.announceChannel(f.cfg.IDKey, fmsg.peerAddress.IdentityKey, channel, go f.announceChannel(f.cfg.IDKey, fmsg.peerAddress.IdentityKey,
fmsg.msg.ChannelID, f.fakeProof, f.fakeProof) channel.LocalFundingKey, channel.RemoteFundingKey,
fmsg.msg.ChannelID, fundingPoint)
} }
// channelProof is one half of the proof necessary to create an authenticated // channelProof is one half of the proof necessary to create an authenticated
@@ -1013,8 +1006,9 @@ type channelProof struct {
// chanAnnouncement encapsulates the two authenticated announcements that we // chanAnnouncement encapsulates the two authenticated announcements that we
// send out to the network after a new channel has been created locally. // send out to the network after a new channel has been created locally.
type chanAnnouncement struct { type chanAnnouncement struct {
chanAnn *lnwire.ChannelAnnouncement chanAnn *lnwire.ChannelAnnouncement
edgeUpdate *lnwire.ChannelUpdateAnnouncement chanUpdateAnn *lnwire.ChannelUpdateAnnouncement
chanProof *lnwire.AnnounceSignatures
} }
// newChanAnnouncement creates the authenticated channel announcement messages // newChanAnnouncement creates the authenticated channel announcement messages
@@ -1024,11 +1018,12 @@ type chanAnnouncement struct {
// identity pub keys of both parties to the channel, and the second segment is // identity pub keys of both parties to the channel, and the second segment is
// authenticated only by us and contains our directional routing policy for the // authenticated only by us and contains our directional routing policy for the
// channel. // channel.
func newChanAnnouncement(localIdentity, remotePub *btcec.PublicKey, func (f *fundingManager) newChanAnnouncement(localPubKey, remotePubKey *btcec.PublicKey,
channel *lnwallet.LightningChannel, chanID lnwire.ShortChannelID, localFundingKey, remoteFundingKey *btcec.PublicKey, chanID lnwire.ShortChannelID,
localProof, remoteProof *channelProof) *chanAnnouncement { chanPoint wire.OutPoint) (*chanAnnouncement, error) {
var err error
// The unconditional section of the announcement is the ChannelID // The unconditional section of the announcement is the ShortChannelID
// itself which compactly encodes the location of the funding output // itself which compactly encodes the location of the funding output
// within the blockchain. // within the blockchain.
chanAnn := &lnwire.ChannelAnnouncement{ chanAnn := &lnwire.ChannelAnnouncement{
@@ -1045,30 +1040,22 @@ func newChanAnnouncement(localIdentity, remotePub *btcec.PublicKey,
// nodes indicates which of the nodes is "first". If our serialized // nodes indicates which of the nodes is "first". If our serialized
// identity key is lower than theirs then we're the "first" node and // identity key is lower than theirs then we're the "first" node and
// second otherwise. // second otherwise.
selfBytes := localIdentity.SerializeCompressed() selfBytes := localPubKey.SerializeCompressed()
remoteBytes := remotePub.SerializeCompressed() remoteBytes := remotePubKey.SerializeCompressed()
if bytes.Compare(selfBytes, remoteBytes) == -1 { if bytes.Compare(selfBytes, remoteBytes) == -1 {
chanAnn.NodeID1 = localIdentity chanAnn.NodeID1 = localPubKey
chanAnn.NodeID2 = remotePub chanAnn.NodeID2 = remotePubKey
chanAnn.NodeSig1 = localProof.nodeSig chanAnn.BitcoinKey1 = localFundingKey
chanAnn.NodeSig2 = remoteProof.nodeSig chanAnn.BitcoinKey2 = remoteFundingKey
chanAnn.BitcoinSig1 = localProof.nodeSig
chanAnn.BitcoinSig2 = remoteProof.nodeSig
chanAnn.BitcoinKey1 = channel.LocalFundingKey
chanAnn.BitcoinKey2 = channel.RemoteFundingKey
// If we're the first node then update the chanFlags to // If we're the first node then update the chanFlags to
// indicate the "direction" of the update. // indicate the "direction" of the update.
chanFlags = 0 chanFlags = 0
} else { } else {
chanAnn.NodeID1 = remotePub chanAnn.NodeID1 = remotePubKey
chanAnn.NodeID2 = localIdentity chanAnn.NodeID2 = localPubKey
chanAnn.NodeSig1 = remoteProof.nodeSig chanAnn.BitcoinKey1 = remoteFundingKey
chanAnn.NodeSig2 = localProof.nodeSig chanAnn.BitcoinKey2 = localFundingKey
chanAnn.BitcoinSig1 = remoteProof.nodeSig
chanAnn.BitcoinSig2 = localProof.nodeSig
chanAnn.BitcoinKey1 = channel.RemoteFundingKey
chanAnn.BitcoinKey2 = channel.LocalFundingKey
// If we're the second node then update the chanFlags to // If we're the second node then update the chanFlags to
// indicate the "direction" of the update. // indicate the "direction" of the update.
@@ -1077,7 +1064,6 @@ func newChanAnnouncement(localIdentity, remotePub *btcec.PublicKey,
// TODO(roasbeef): add real sig, populate proper FeeSchema // TODO(roasbeef): add real sig, populate proper FeeSchema
chanUpdateAnn := &lnwire.ChannelUpdateAnnouncement{ chanUpdateAnn := &lnwire.ChannelUpdateAnnouncement{
Signature: localProof.nodeSig,
ShortChannelID: chanID, ShortChannelID: chanID,
Timestamp: uint32(time.Now().Unix()), Timestamp: uint32(time.Now().Unix()),
Flags: chanFlags, Flags: chanFlags,
@@ -1087,10 +1073,40 @@ func newChanAnnouncement(localIdentity, remotePub *btcec.PublicKey,
FeeProportionalMillionths: 0, FeeProportionalMillionths: 0,
} }
return &chanAnnouncement{ chanUpdateAnn.Signature, err = f.cfg.SignAnnouncement(chanUpdateAnn)
chanAnn: chanAnn, if err != nil {
edgeUpdate: chanUpdateAnn, return nil, errors.Errorf("unable to generate channel "+
"update announcement signature: %v", err)
} }
// Channel proof should be announced in the separate message, so we
// already have the bitcoin signature but in order to construct
// the proof we also need node signature. Use message signer in order
// to sign the message with node private key.
nodeSig, err := f.cfg.SignAnnouncement(chanAnn)
if err != nil {
return nil, errors.Errorf("unable to generate node "+
"signature for channel announcement: %v", err)
}
bitcoinSig, err := f.cfg.SignNodeKey(localPubKey, localFundingKey)
if err != nil {
return nil, errors.Errorf("unable to generate bitcoin "+
"signature for node public key: %v", err)
}
proof := &lnwire.AnnounceSignatures{
ChannelID: chanPoint,
ShortChannelID: chanID,
NodeSignature: nodeSig,
BitcoinSignature: bitcoinSig,
}
return &chanAnnouncement{
chanAnn: chanAnn,
chanUpdateAnn: chanUpdateAnn,
chanProof: proof,
}, nil
} }
// announceChannel announces a newly created channel to the rest of the network // announceChannel announces a newly created channel to the rest of the network
@@ -1098,17 +1114,23 @@ func newChanAnnouncement(localIdentity, remotePub *btcec.PublicKey,
// network to recognize the legitimacy of the channel. The crafted // network to recognize the legitimacy of the channel. The crafted
// announcements are then send to the channel router to handle broadcasting to // announcements are then send to the channel router to handle broadcasting to
// the network during its next trickle. // the network during its next trickle.
func (f *fundingManager) announceChannel(idKey, remoteIDKey *btcec.PublicKey, func (f *fundingManager) announceChannel(localIDKey, remoteIDKey *btcec.PublicKey,
channel *lnwallet.LightningChannel, chanID lnwire.ShortChannelID, localProof, localFundingKey, remoteFundingKey *btcec.PublicKey, chanID lnwire.ShortChannelID,
remoteProof *channelProof) { chanPoint wire.OutPoint) {
// TODO(roasbeef): need a Signer.SignMessage method to finalize ann, err := f.newChanAnnouncement(localIDKey, remoteIDKey, localFundingKey,
// advertisements remoteFundingKey, chanID, chanPoint)
chanAnnouncement := newChanAnnouncement(idKey, remoteIDKey, channel, chanID, if err != nil {
localProof, remoteProof) fndgLog.Errorf("can't generate channel announcement: %v", err)
return
}
f.cfg.SendToDiscovery(chanAnnouncement.chanAnn) fndgLog.Infof("Send channel, channel update, and proof announcements"+
f.cfg.SendToDiscovery(chanAnnouncement.edgeUpdate) " for chanID=%v, shortChannelID=%v to discovery service",
chanPoint, chanID.ToUint64())
f.cfg.SendToDiscovery(ann.chanAnn)
f.cfg.SendToDiscovery(ann.chanUpdateAnn)
f.cfg.SendToDiscovery(ann.chanProof)
} }
// initFundingWorkflow sends a message to the funding manager instructing it // initFundingWorkflow sends a message to the funding manager instructing it
+5 -2
View File
@@ -494,12 +494,15 @@ out:
isChanUpdate = true isChanUpdate = true
targetChan = msg.ChannelPoint targetChan = msg.ChannelPoint
case *lnwire.NodeAnnouncement, case *lnwire.ChannelUpdateAnnouncement,
*lnwire.ChannelAnnouncement, *lnwire.ChannelAnnouncement,
*lnwire.ChannelUpdateAnnouncement: *lnwire.NodeAnnouncement,
*lnwire.AnnounceSignatures:
p.server.discoverSrv.ProcessRemoteAnnouncement(msg, p.server.discoverSrv.ProcessRemoteAnnouncement(msg,
p.addr.IdentityKey) p.addr.IdentityKey)
default:
peerLog.Errorf("unknown message received from peer "+"%v", p)
} }
if isChanUpdate { if isChanUpdate {
+31 -6
View File
@@ -168,8 +168,23 @@ func newServer(listenAddrs []string, notifier chainntnfs.ChainNotifier,
selfAddrs = append(selfAddrs, addr) selfAddrs = append(selfAddrs, addr)
} }
// TODO(roasbeef): remove once we actually sign the funding_locked
// stuffs
fakeSigHex, err := hex.DecodeString("30450221008ce2bc69281ce27da07e66" +
"83571319d18e949ddfa2965fb6caa1bf0314f882d70220299105481d63e0f" +
"4bc2a88121167221b6700d72a0ead154c03be696a292d24ae")
if err != nil {
return nil, err
}
fakeSig, err := btcec.ParseSignature(fakeSigHex, btcec.S256())
if err != nil {
return nil, err
}
chanGraph := chanDB.ChannelGraph() chanGraph := chanDB.ChannelGraph()
self := &channeldb.LightningNode{ self := &channeldb.LightningNode{
AuthSig: fakeSig,
LastUpdate: time.Now(), LastUpdate: time.Now(),
Addresses: selfAddrs, Addresses: selfAddrs,
PubKey: privKey.PubKey(), PubKey: privKey.PubKey(),
@@ -202,10 +217,12 @@ func newServer(listenAddrs []string, notifier chainntnfs.ChainNotifier,
} }
s.discoverSrv, err = discovery.New(discovery.Config{ s.discoverSrv, err = discovery.New(discovery.Config{
Broadcast: s.broadcastMessage, Broadcast: s.broadcastMessage,
Notifier: s.chainNotifier, Notifier: s.chainNotifier,
Router: s.chanRouter, Router: s.chanRouter,
SendMessages: s.sendToPeer, SendToPeer: s.sendToPeer,
TrickleDelay: time.Millisecond * 300,
ProofMatureDelta: 0,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@@ -218,8 +235,16 @@ func newServer(listenAddrs []string, notifier chainntnfs.ChainNotifier,
IDKey: s.identityPriv.PubKey(), IDKey: s.identityPriv.PubKey(),
Wallet: wallet, Wallet: wallet,
Notifier: s.chainNotifier, Notifier: s.chainNotifier,
SendToDiscovery: func(msg lnwire.Message) { SignNodeKey: func(nodeKey, fundingKey *btcec.PublicKey) (*btcec.Signature,
s.discoverSrv.ProcessLocalAnnouncement(msg, error) {
return fakeSig, nil
},
SignAnnouncement: func(msg lnwire.Message) (*btcec.Signature,
error) {
return fakeSig, nil
},
SendToDiscovery: func(msg lnwire.Message) chan error {
return s.discoverSrv.ProcessLocalAnnouncement(msg,
s.identityPriv.PubKey()) s.identityPriv.PubKey())
}, },
ArbiterChan: s.breachArbiter.newContracts, ArbiterChan: s.breachArbiter.newContracts,