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:
1 parent
a23715a9c7
commit
d4055d7830
7 files changed
+244
-60
No files matched your search
+38
-25
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
Reference in new issue
Block a user