les: optional signed announce messages

This commit is contained in:
Zsolt Felfoldi 2017-08-23 18:17:35 +02:00
parent 4a96afcf95
commit bc23b730b0
4 changed files with 101 additions and 8 deletions

View file

@ -365,11 +365,23 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
// Block header query, collect the requested headers and reply // Block header query, collect the requested headers and reply
case AnnounceMsg: case AnnounceMsg:
p.Log().Trace("Received announce message") p.Log().Trace("Received announce message")
if p.requestAnnounceType == announceTypeNone {
return errResp(ErrUnexpectedResponse, "")
}
var req announceData var req announceData
if err := msg.Decode(&req); err != nil { if err := msg.Decode(&req); err != nil {
return errResp(ErrDecode, "%v: %v", msg, err) return errResp(ErrDecode, "%v: %v", msg, err)
} }
if p.requestAnnounceType == announceTypeSigned {
if err := req.checkSignature(p.pubKey); err != nil {
p.Log().Trace("Invalid announcement signature", "err", err)
return err
}
p.Log().Trace("Valid announcement signature")
}
p.Log().Trace("Announce message content", "number", req.Number, "hash", req.Hash, "td", req.Td, "reorg", req.ReorgDepth) p.Log().Trace("Announce message content", "number", req.Number, "hash", req.Hash, "td", req.Td, "reorg", req.ReorgDepth)
if pm.fetcher != nil { if pm.fetcher != nil {
pm.fetcher.announce(p, &req) pm.fetcher.announce(p, &req)

View file

@ -18,6 +18,7 @@
package les package les
import ( import (
"crypto/ecdsa"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
@ -42,14 +43,23 @@ var (
const maxResponseErrors = 50 // number of invalid responses tolerated (makes the protocol less brittle but still avoids spam) const maxResponseErrors = 50 // number of invalid responses tolerated (makes the protocol less brittle but still avoids spam)
const (
announceTypeNone = iota
announceTypeSimple
announceTypeSigned
)
type peer struct { type peer struct {
*p2p.Peer *p2p.Peer
pubKey *ecdsa.PublicKey
rw p2p.MsgReadWriter rw p2p.MsgReadWriter
version int // Protocol version negotiated version int // Protocol version negotiated
network uint64 // Network ID being on network uint64 // Network ID being on
announceType, requestAnnounceType uint64
id string id string
headInfo *announceData headInfo *announceData
@ -70,9 +80,11 @@ type peer struct {
func newPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *peer { func newPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
id := p.ID() id := p.ID()
pubKey, _ := id.Pubkey()
return &peer{ return &peer{
Peer: p, Peer: p,
pubKey: pubKey,
rw: rw, rw: rw,
version: version, version: version,
network: network, network: network,
@ -325,7 +337,7 @@ func (l keyValueList) decode() keyValueMap {
func (m keyValueMap) get(key string, val interface{}) error { func (m keyValueMap) get(key string, val interface{}) error {
enc, ok := m[key] enc, ok := m[key]
if !ok { if !ok {
return errResp(ErrHandshakeMissingKey, "%s", key) return errResp(ErrMissingKey, "%s", key)
} }
if val == nil { if val == nil {
return nil return nil
@ -384,6 +396,9 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis
list := server.fcCostStats.getCurrentList() list := server.fcCostStats.getCurrentList()
send = send.add("flowControl/MRC", list) send = send.add("flowControl/MRC", list)
p.fcCosts = list.decode() p.fcCosts = list.decode()
} else {
p.requestAnnounceType = announceTypeSimple // set to default until "very light" client mode is implemented
send = send.add("announceType", p.requestAnnounceType)
} }
recvList, err := p.sendReceiveHandshake(send) recvList, err := p.sendReceiveHandshake(send)
if err != nil { if err != nil {
@ -428,6 +443,9 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis
/*if recv.get("serveStateSince", nil) == nil { /*if recv.get("serveStateSince", nil) == nil {
return errResp(ErrUselessPeer, "wanted client, got server") return errResp(ErrUselessPeer, "wanted client, got server")
}*/ }*/
if recv.get("announceType", &p.announceType) != nil {
p.announceType = announceTypeSimple
}
p.fcClient = flowcontrol.NewClientNode(server.fcManager, server.defParams) p.fcClient = flowcontrol.NewClientNode(server.fcManager, server.defParams)
} else { } else {
if recv.get("serveChainSince", nil) != nil { if recv.get("serveChainSince", nil) != nil {

View file

@ -18,11 +18,17 @@
package les package les
import ( import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"errors"
"fmt" "fmt"
"io" "io"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -88,7 +94,7 @@ const (
ErrUnexpectedResponse ErrUnexpectedResponse
ErrInvalidResponse ErrInvalidResponse
ErrTooManyTimeouts ErrTooManyTimeouts
ErrHandshakeMissingKey ErrMissingKey
) )
func (e errCode) String() string { func (e errCode) String() string {
@ -110,7 +116,13 @@ var errorToString = map[int]string{
ErrUnexpectedResponse: "Unexpected response", ErrUnexpectedResponse: "Unexpected response",
ErrInvalidResponse: "Invalid response", ErrInvalidResponse: "Invalid response",
ErrTooManyTimeouts: "Too many request timeouts", ErrTooManyTimeouts: "Too many request timeouts",
ErrHandshakeMissingKey: "Key missing from handshake message", ErrMissingKey: "Key missing from list",
}
type announceBlock struct {
Hash common.Hash // Hash of one particular block being announced
Number uint64 // Number of one particular block being announced
Td *big.Int // Total difficulty of one particular block being announced
} }
// announceData is the network packet for the block announcements. // announceData is the network packet for the block announcements.
@ -122,6 +134,32 @@ type announceData struct {
Update keyValueList Update keyValueList
} }
// sign adds a signature to the block announcement by the given privKey
func (a *announceData) sign(privKey *ecdsa.PrivateKey) {
rlp, _ := rlp.EncodeToBytes(announceBlock{a.Hash, a.Number, a.Td})
sig, _ := crypto.Sign(crypto.Keccak256(rlp), privKey)
a.Update = a.Update.add("sign", sig)
}
// checkSignature verifies if the block announcement has a valid signature by the given pubKey
func (a *announceData) checkSignature(pubKey *ecdsa.PublicKey) error {
var sig []byte
if err := a.Update.decode().get("sign", &sig); err != nil {
return err
}
rlp, _ := rlp.EncodeToBytes(announceBlock{a.Hash, a.Number, a.Td})
recPubkey, err := secp256k1.RecoverPubkey(crypto.Keccak256(rlp), sig)
if err != nil {
return err
}
pbytes := elliptic.Marshal(pubKey.Curve, pubKey.X, pubKey.Y)
if bytes.Equal(pbytes, recPubkey) {
return nil
} else {
return errors.New("Wrong signature")
}
}
type blockInfo struct { type blockInfo struct {
Hash common.Hash // Hash of one particular block being announced Hash common.Hash // Hash of one particular block being announced
Number uint64 // Number of one particular block being announced Number uint64 // Number of one particular block being announced

View file

@ -18,6 +18,7 @@
package les package les
import ( import (
"crypto/ecdsa"
"encoding/binary" "encoding/binary"
"math" "math"
"sync" "sync"
@ -41,6 +42,7 @@ type LesServer struct {
fcCostStats *requestCostStats fcCostStats *requestCostStats
defParams *flowcontrol.ServerParams defParams *flowcontrol.ServerParams
lesTopics []discv5.Topic lesTopics []discv5.Topic
privateKey *ecdsa.PrivateKey
quitSync chan struct{} quitSync chan struct{}
chtIndexer, bltIndexer *core.ChainIndexer chtIndexer, bltIndexer *core.ChainIndexer
@ -52,7 +54,6 @@ func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
pm.blockLoop()
lesTopics := make([]discv5.Topic, len(ServerProtocolVersions)) lesTopics := make([]discv5.Topic, len(ServerProtocolVersions))
for i, pv := range ServerProtocolVersions { for i, pv := range ServerProtocolVersions {
@ -95,6 +96,8 @@ func (s *LesServer) Start(srvr *p2p.Server) {
srvr.DiscV5.RegisterTopic(topic, s.quitSync) srvr.DiscV5.RegisterTopic(topic, s.quitSync)
}() }()
} }
s.privateKey = srvr.PrivateKey
s.protocolManager.blockLoop()
} }
func (s *LesServer) SetBloomBitsIndexer(bbIndexer *core.ChainIndexer) { func (s *LesServer) SetBloomBitsIndexer(bbIndexer *core.ChainIndexer) {
@ -313,11 +316,33 @@ func (pm *ProtocolManager) blockLoop() {
log.Debug("Announcing block to peers", "number", number, "hash", hash, "td", td, "reorg", reorg) log.Debug("Announcing block to peers", "number", number, "hash", hash, "td", td, "reorg", reorg)
announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg} announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg}
var (
signed bool
signedAnnounce announceData
)
for _, p := range peers { for _, p := range peers {
select { switch p.announceType {
case p.announceChn <- announce:
default: case announceTypeSimple:
pm.removePeer(p.id) select {
case p.announceChn <- announce:
default:
pm.removePeer(p.id)
}
case announceTypeSigned:
if !signed {
signedAnnounce = announce
signedAnnounce.sign(pm.server.privateKey)
signed = true
}
select {
case p.announceChn <- signedAnnounce:
default:
pm.removePeer(p.id)
}
} }
} }
} }