discovery+funding: add validation of the announcement messages
Add validation functions and include validation checks in the annoncement process function.
This commit is contained in:
parent
a23715a9c7
commit
d4055d7830
@ -1,14 +1,11 @@
|
||||
package discovery
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"bytes"
|
||||
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
@ -81,20 +78,6 @@ type Config struct {
|
||||
|
||||
// New create new discovery service structure.
|
||||
func New(cfg Config) (*Discovery, error) {
|
||||
// TODO(roasbeef): remove this place holder after sigs are properly
|
||||
// stored in the graph.
|
||||
s := "30450221008ce2bc69281ce27da07e6683571319d18e949ddfa2965fb6caa" +
|
||||
"1bf0314f882d70220299105481d63e0f4bc2a88121167221b6700d72a0e" +
|
||||
"ad154c03be696a292d24ae"
|
||||
fakeSigHex, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fakeSig, err := btcec.ParseSignature(fakeSigHex, btcec.S256())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Discovery{
|
||||
cfg: &cfg,
|
||||
networkMsgs: make(chan *networkMsg),
|
||||
@ -102,7 +85,6 @@ func New(cfg Config) (*Discovery, error) {
|
||||
syncRequests: make(chan *syncRequest),
|
||||
prematureAnnouncements: make(map[uint32][]*networkMsg),
|
||||
waitingProofs: make(map[waitingProofKey]*lnwire.AnnounceSignatures),
|
||||
fakeSig: fakeSig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -157,8 +139,6 @@ type Discovery struct {
|
||||
// bestHeight is the height of the block at the tip of the main chain
|
||||
// as we know it.
|
||||
bestHeight uint32
|
||||
|
||||
fakeSig *btcec.Signature
|
||||
}
|
||||
|
||||
// ProcessRemoteAnnouncement sends a new remote announcement message along with
|
||||
@ -444,7 +424,13 @@ func (d *Discovery) processNetworkAnnouncement(nMsg *networkMsg) []lnwire.Messag
|
||||
// node, or a node updating previously advertised information.
|
||||
case *lnwire.NodeAnnouncement:
|
||||
if nMsg.isRemote {
|
||||
// TODO(andrew.shvv) add validation
|
||||
if err := d.validateNodeAnn(msg); err != nil {
|
||||
err := errors.Errorf("unable to validate "+
|
||||
"node announcement: %v", err)
|
||||
log.Error(err)
|
||||
nMsg.err <- err
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
node := &channeldb.LightningNode{
|
||||
@ -500,7 +486,13 @@ func (d *Discovery) processNetworkAnnouncement(nMsg *networkMsg) []lnwire.Messag
|
||||
|
||||
var proof *channeldb.ChannelAuthProof
|
||||
if nMsg.isRemote {
|
||||
// TODO(andrew.shvv) Add validation
|
||||
if err := d.validateChannelAnn(msg); err != nil {
|
||||
err := errors.Errorf("unable to validate "+
|
||||
"announcement: %v", err)
|
||||
log.Error(err)
|
||||
nMsg.err <- err
|
||||
return nil
|
||||
}
|
||||
|
||||
proof = &channeldb.ChannelAuthProof{
|
||||
NodeSig1: msg.NodeSig1,
|
||||
@ -577,7 +569,21 @@ func (d *Discovery) processNetworkAnnouncement(nMsg *networkMsg) []lnwire.Messag
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(andrew.shvv) Add validation
|
||||
var pubKey *btcec.PublicKey
|
||||
switch msg.Flags {
|
||||
case 0:
|
||||
pubKey = chanInfo.NodeKey1
|
||||
case 1:
|
||||
pubKey = chanInfo.NodeKey2
|
||||
}
|
||||
|
||||
if err := d.validateChannelUpdateAnn(pubKey, msg); err != nil {
|
||||
err := errors.Errorf("unable to validate channel"+
|
||||
"update announcement for shortChanID=%v: %v", msg.ShortChannelID, err)
|
||||
log.Error(err)
|
||||
nMsg.err <- err
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(roasbeef): should be msat here
|
||||
update := &channeldb.ChannelEdgePolicy{
|
||||
@ -732,7 +738,14 @@ func (d *Discovery) processNetworkAnnouncement(nMsg *networkMsg) []lnwire.Messag
|
||||
|
||||
chanAnn, e1Ann, e2Ann := createChanAnnouncement(&dbProof, chanInfo, e1, e2)
|
||||
|
||||
// TODO(andrew.shvv) Add validation
|
||||
if err := d.validateChannelAnn(chanAnn); err != nil {
|
||||
err := errors.Errorf("channel announcement proof "+
|
||||
"for shortChanID=%v isn't valid: %v",
|
||||
shortChanID, err)
|
||||
log.Error(err)
|
||||
nMsg.err <- err
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the channel was returned by the router it means that
|
||||
// existence of funding point and inclusion of nodes bitcoin
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
@ -251,6 +252,10 @@ 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
|
||||
|
||||
return &batch, nil
|
||||
|
||||
@ -258,6 +263,7 @@ func createAnnouncements(blockHeight uint32) (*annBatch, error) {
|
||||
|
||||
func createNodeAnnouncement(priv *btcec.PrivateKey) (*lnwire.NodeAnnouncement,
|
||||
error) {
|
||||
var err error
|
||||
|
||||
alias, err := lnwire.NewAlias("kek" + string(priv.Serialize()))
|
||||
if err != nil {
|
||||
@ -265,7 +271,6 @@ func createNodeAnnouncement(priv *btcec.PrivateKey) (*lnwire.NodeAnnouncement,
|
||||
}
|
||||
|
||||
a := &lnwire.NodeAnnouncement{
|
||||
Signature: testSig,
|
||||
Timestamp: uint32(prand.Int31()),
|
||||
Addresses: testAddrs,
|
||||
NodeID: priv.PubKey(),
|
||||
@ -273,14 +278,19 @@ func createNodeAnnouncement(priv *btcec.PrivateKey) (*lnwire.NodeAnnouncement,
|
||||
Features: testFeatures,
|
||||
}
|
||||
|
||||
signer := lnwallet.NewMessageSigner(nodeKeyPriv1)
|
||||
if a.Signature, err = SignAnnouncement(signer, a); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func createUpdateAnnouncement(blockHeight uint32) (*lnwire.ChannelUpdateAnnouncement,
|
||||
error) {
|
||||
var err error
|
||||
|
||||
a := &lnwire.ChannelUpdateAnnouncement{
|
||||
Signature: testSig,
|
||||
ShortChannelID: lnwire.ShortChannelID{
|
||||
BlockHeight: blockHeight,
|
||||
},
|
||||
@ -291,11 +301,17 @@ func createUpdateAnnouncement(blockHeight uint32) (*lnwire.ChannelUpdateAnnounce
|
||||
FeeProportionalMillionths: uint32(prand.Int31()),
|
||||
}
|
||||
|
||||
signer := lnwallet.NewMessageSigner(nodeKeyPriv1)
|
||||
if a.Signature, err = SignAnnouncement(signer, a); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func createRemoteChannelAnnouncement(blockHeight uint32) (*lnwire.ChannelAnnouncement,
|
||||
error) {
|
||||
var err error
|
||||
|
||||
a := &lnwire.ChannelAnnouncement{
|
||||
ShortChannelID: lnwire.ShortChannelID{
|
||||
@ -307,11 +323,28 @@ func createRemoteChannelAnnouncement(blockHeight uint32) (*lnwire.ChannelAnnounc
|
||||
NodeID2: nodeKeyPub2,
|
||||
BitcoinKey1: bitcoinKeyPub1,
|
||||
BitcoinKey2: bitcoinKeyPub2,
|
||||
}
|
||||
|
||||
NodeSig1: testSig,
|
||||
NodeSig2: testSig,
|
||||
BitcoinSig1: testSig,
|
||||
BitcoinSig2: testSig,
|
||||
signer := lnwallet.NewMessageSigner(nodeKeyPriv1)
|
||||
if a.NodeSig1, err = SignAnnouncement(signer, a); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signer = lnwallet.NewMessageSigner(nodeKeyPriv2)
|
||||
if a.NodeSig2, err = SignAnnouncement(signer, a); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hash := chainhash.DoubleHashB(nodeKeyPub1.SerializeCompressed())
|
||||
a.BitcoinSig1, err = bitcoinKeyPriv1.Sign(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hash = chainhash.DoubleHashB(nodeKeyPub2.SerializeCompressed())
|
||||
a.BitcoinSig2, err = bitcoinKeyPriv2.Sign(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a, nil
|
||||
|
@ -3,8 +3,11 @@ package discovery
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
)
|
||||
|
||||
// newProofKey constructs new announcement signature message key.
|
||||
@ -91,3 +94,39 @@ func createChanAnnouncement(chanProof *channeldb.ChannelAuthProof,
|
||||
|
||||
return chanAnn, edge1Ann, edge2Ann
|
||||
}
|
||||
|
||||
// copyPubKey is copying the public key and setting curve.
|
||||
// NOTE: At the moment of creation the function was need only because we are
|
||||
// setting the curve to nil in the read message function and in order to
|
||||
// properly validate the signatures we need to set the curve again.
|
||||
func copyPubKey(pub *btcec.PublicKey) *btcec.PublicKey {
|
||||
return &btcec.PublicKey{
|
||||
Curve: btcec.S256(),
|
||||
X: pub.X,
|
||||
Y: pub.Y,
|
||||
}
|
||||
}
|
||||
|
||||
// SignAnnouncement helper function which is used for signing the announce
|
||||
// messages.
|
||||
func SignAnnouncement(signer *lnwallet.MessageSigner,
|
||||
msg lnwire.Message) (*btcec.Signature, error) {
|
||||
var data []byte
|
||||
var err error
|
||||
switch m := msg.(type) {
|
||||
case *lnwire.ChannelAnnouncement:
|
||||
data, err = m.DataToSign()
|
||||
case *lnwire.ChannelUpdateAnnouncement:
|
||||
data, err = m.DataToSign()
|
||||
case *lnwire.NodeAnnouncement:
|
||||
data, err = m.DataToSign()
|
||||
default:
|
||||
return nil, errors.New("can't sign message " +
|
||||
"of this format")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("can't get data to sign: %v", err)
|
||||
}
|
||||
|
||||
return signer.SignData(data)
|
||||
}
|
||||
|
83
discovery/validation.go
Normal file
83
discovery/validation.go
Normal file
@ -0,0 +1,83 @@
|
||||
package discovery
|
||||
|
||||
import (
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
// validateChannelAnn validates the channel announcement message and checks
|
||||
// that node signatures contains the announcement message, and that the
|
||||
// bitcoin signatures contains the node keys.
|
||||
func (d *Discovery) validateChannelAnn(a *lnwire.ChannelAnnouncement) error {
|
||||
sigHash := chainhash.DoubleHashB(a.NodeID1.SerializeCompressed())
|
||||
if !a.BitcoinSig1.Verify(sigHash, copyPubKey(a.BitcoinKey1)) {
|
||||
return errors.New("can't verify first bitcoin signature")
|
||||
}
|
||||
|
||||
sigHash = chainhash.DoubleHashB(a.NodeID2.SerializeCompressed())
|
||||
if !a.BitcoinSig2.Verify(sigHash, copyPubKey(a.BitcoinKey2)) {
|
||||
return errors.New("can't verify second bitcoin signature")
|
||||
}
|
||||
|
||||
// Get the data of announcement which should be encapsulated in
|
||||
// signature and then check it.
|
||||
data, err := a.DataToSign()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dataHash := chainhash.DoubleHashB(data)
|
||||
|
||||
if !a.NodeSig1.Verify(dataHash, copyPubKey(a.NodeID1)) {
|
||||
return errors.New("can't verify data in first node signature")
|
||||
}
|
||||
|
||||
if !a.NodeSig2.Verify(dataHash, copyPubKey(a.NodeID2)) {
|
||||
return errors.New("can't verify data in second node signature")
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// validateNodeAnn validates the node announcement by checking that the
|
||||
// the signature corresponds to the node key and have been created with
|
||||
// announcement data.
|
||||
func (d *Discovery) validateNodeAnn(a *lnwire.NodeAnnouncement) error {
|
||||
|
||||
// Get the data of announcement which should be encapsulated in
|
||||
// signature and then check it.
|
||||
data, err := a.DataToSign()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dataHash := chainhash.DoubleHashB(data)
|
||||
if !a.Signature.Verify(dataHash, copyPubKey(a.NodeID)) {
|
||||
return errors.New("can't check the node annoucement signature")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateChannelUpdateAnn validates the channel update announcement by
|
||||
// checking that the the signature corresponds to the node key and have been
|
||||
// created with announcement data.
|
||||
func (d *Discovery) validateChannelUpdateAnn(pubKey *btcec.PublicKey,
|
||||
a *lnwire.ChannelUpdateAnnouncement) error {
|
||||
|
||||
// Get the data of announcement which should be encapsulated in
|
||||
// signature and then check it.
|
||||
data, err := a.DataToSign()
|
||||
if err != nil {
|
||||
return errors.Errorf("can't retrieve data to sign: %v", err)
|
||||
}
|
||||
dataHash := chainhash.DoubleHashB(data)
|
||||
|
||||
if !a.Signature.Verify(dataHash, copyPubKey(pubKey)) {
|
||||
return errors.Errorf("verification of channel updates "+
|
||||
"failed chan_id=%v", a.ShortChannelID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
9
lnd.go
9
lnd.go
@ -140,8 +140,8 @@ func lndMain() error {
|
||||
|
||||
// Create, and start the lnwallet, which handles the core payment
|
||||
// channel logic, and exposes control via proxy state machines.
|
||||
wallet, err := lnwallet.NewLightningWallet(chanDB, notifier,
|
||||
wc, signer, bio, activeNetParams.Params)
|
||||
wallet, err := lnwallet.NewLightningWallet(chanDB, notifier, wc, signer,
|
||||
bio, activeNetParams.Params)
|
||||
if err != nil {
|
||||
fmt.Printf("unable to create wallet: %v\n", err)
|
||||
return err
|
||||
@ -157,7 +157,10 @@ func lndMain() error {
|
||||
defaultListenAddrs := []string{
|
||||
net.JoinHostPort("", strconv.Itoa(cfg.PeerPort)),
|
||||
}
|
||||
server, err := newServer(defaultListenAddrs, notifier, bio, wallet, chanDB)
|
||||
|
||||
fundingSigner := btcwallet.NewFundingSigner(wc)
|
||||
server, err := newServer(defaultListenAddrs, notifier, bio, wallet,
|
||||
chanDB, fundingSigner)
|
||||
if err != nil {
|
||||
srvrLog.Errorf("unable to create server: %v\n", err)
|
||||
return err
|
||||
|
3
peer.go
3
peer.go
@ -502,7 +502,8 @@ out:
|
||||
p.server.discoverSrv.ProcessRemoteAnnouncement(msg,
|
||||
p.addr.IdentityKey)
|
||||
default:
|
||||
peerLog.Errorf("unknown message received from peer "+"%v", p)
|
||||
peerLog.Errorf("unknown message received from peer "+
|
||||
"%v", p)
|
||||
}
|
||||
|
||||
if isChanUpdate {
|
||||
|
62
server.go
62
server.go
@ -17,6 +17,7 @@ import (
|
||||
"github.com/lightningnetwork/lnd/discovery"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
@ -95,7 +96,7 @@ type server struct {
|
||||
// passed listener address.
|
||||
func newServer(listenAddrs []string, notifier chainntnfs.ChainNotifier,
|
||||
bio lnwallet.BlockChainIO, wallet *lnwallet.LightningWallet,
|
||||
chanDB *channeldb.DB) (*server, error) {
|
||||
chanDB *channeldb.DB, fundingSigner *btcwallet.FundingSigner) (*server, error) {
|
||||
|
||||
privKey, err := wallet.GetIdentitykey()
|
||||
if err != nil {
|
||||
@ -168,32 +169,41 @@ func newServer(listenAddrs []string, notifier chainntnfs.ChainNotifier,
|
||||
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()
|
||||
|
||||
// In order to have ability to announce the self node we need to
|
||||
// sign the node announce message, and include the signature in the
|
||||
// node channeldb object.
|
||||
alias, err := lnwire.NewAlias(hex.EncodeToString(serializedPubKey[:10]))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't create alias: %v", err)
|
||||
}
|
||||
self := &channeldb.LightningNode{
|
||||
AuthSig: fakeSig,
|
||||
LastUpdate: time.Now(),
|
||||
Addresses: selfAddrs,
|
||||
PubKey: privKey.PubKey(),
|
||||
// TODO(roasbeef): make alias configurable
|
||||
Alias: hex.EncodeToString(serializedPubKey[:10]),
|
||||
Alias: alias.String(),
|
||||
Features: globalFeatures,
|
||||
}
|
||||
|
||||
// Initialize graph with authenticated lightning node, signature is
|
||||
// needed in order to be able to announce node to other network.
|
||||
messageSigner := lnwallet.NewMessageSigner(s.identityPriv)
|
||||
if self.AuthSig, err = discovery.SignAnnouncement(messageSigner,
|
||||
&lnwire.NodeAnnouncement{
|
||||
Timestamp: uint32(self.LastUpdate.Unix()),
|
||||
Addresses: self.Addresses,
|
||||
NodeID: self.PubKey,
|
||||
Alias: alias,
|
||||
Features: self.Features,
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("unable to generate signature for "+
|
||||
"self node announcement: %v", err)
|
||||
}
|
||||
|
||||
if err := chanGraph.SetSourceNode(self); err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("can't set self node: %v", err)
|
||||
}
|
||||
|
||||
s.chanRouter, err = routing.New(routing.Config{
|
||||
@ -213,7 +223,7 @@ func newServer(listenAddrs []string, notifier chainntnfs.ChainNotifier,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("can't create router: %v", err)
|
||||
}
|
||||
|
||||
s.discoverSrv, err = discovery.New(discovery.Config{
|
||||
@ -235,17 +245,19 @@ func newServer(listenAddrs []string, notifier chainntnfs.ChainNotifier,
|
||||
IDKey: s.identityPriv.PubKey(),
|
||||
Wallet: wallet,
|
||||
Notifier: s.chainNotifier,
|
||||
SignNodeKey: func(nodeKey, fundingKey *btcec.PublicKey) (*btcec.Signature,
|
||||
error) {
|
||||
return fakeSig, nil
|
||||
SignNodeKey: func(nodeKey,
|
||||
fundingKey *btcec.PublicKey) (*btcec.Signature, error) {
|
||||
data := nodeKey.SerializeCompressed()
|
||||
return fundingSigner.SignData(data, fundingKey)
|
||||
},
|
||||
SignAnnouncement: func(msg lnwire.Message) (*btcec.Signature,
|
||||
error) {
|
||||
return fakeSig, nil
|
||||
return discovery.SignAnnouncement(messageSigner, msg)
|
||||
},
|
||||
SendToDiscovery: func(msg lnwire.Message) chan error {
|
||||
return s.discoverSrv.ProcessLocalAnnouncement(msg,
|
||||
SendToDiscovery: func(msg lnwire.Message) error {
|
||||
s.discoverSrv.ProcessLocalAnnouncement(msg,
|
||||
s.identityPriv.PubKey())
|
||||
return nil
|
||||
},
|
||||
ArbiterChan: s.breachArbiter.newContracts,
|
||||
SendToPeer: s.sendToPeer,
|
||||
|
Loading…
Reference in New Issue
Block a user