mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-16 17:03:46 +00:00
les: optional signed announce messages
This commit is contained in:
parent
4a96afcf95
commit
bc23b730b0
4 changed files with 101 additions and 8 deletions
|
|
@ -365,11 +365,23 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
// Block header query, collect the requested headers and reply
|
||||
case AnnounceMsg:
|
||||
p.Log().Trace("Received announce message")
|
||||
if p.requestAnnounceType == announceTypeNone {
|
||||
return errResp(ErrUnexpectedResponse, "")
|
||||
}
|
||||
|
||||
var req announceData
|
||||
if err := msg.Decode(&req); err != nil {
|
||||
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)
|
||||
if pm.fetcher != nil {
|
||||
pm.fetcher.announce(p, &req)
|
||||
|
|
|
|||
20
les/peer.go
20
les/peer.go
|
|
@ -18,6 +18,7 @@
|
|||
package les
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -42,14 +43,23 @@ var (
|
|||
|
||||
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 {
|
||||
*p2p.Peer
|
||||
pubKey *ecdsa.PublicKey
|
||||
|
||||
rw p2p.MsgReadWriter
|
||||
|
||||
version int // Protocol version negotiated
|
||||
network uint64 // Network ID being on
|
||||
|
||||
announceType, requestAnnounceType uint64
|
||||
|
||||
id string
|
||||
|
||||
headInfo *announceData
|
||||
|
|
@ -70,9 +80,11 @@ type peer struct {
|
|||
|
||||
func newPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
||||
id := p.ID()
|
||||
pubKey, _ := id.Pubkey()
|
||||
|
||||
return &peer{
|
||||
Peer: p,
|
||||
pubKey: pubKey,
|
||||
rw: rw,
|
||||
version: version,
|
||||
network: network,
|
||||
|
|
@ -325,7 +337,7 @@ func (l keyValueList) decode() keyValueMap {
|
|||
func (m keyValueMap) get(key string, val interface{}) error {
|
||||
enc, ok := m[key]
|
||||
if !ok {
|
||||
return errResp(ErrHandshakeMissingKey, "%s", key)
|
||||
return errResp(ErrMissingKey, "%s", key)
|
||||
}
|
||||
if val == nil {
|
||||
return nil
|
||||
|
|
@ -384,6 +396,9 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis
|
|||
list := server.fcCostStats.getCurrentList()
|
||||
send = send.add("flowControl/MRC", list)
|
||||
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)
|
||||
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 {
|
||||
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)
|
||||
} else {
|
||||
if recv.get("serveChainSince", nil) != nil {
|
||||
|
|
|
|||
|
|
@ -18,11 +18,17 @@
|
|||
package les
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
|
|
@ -88,7 +94,7 @@ const (
|
|||
ErrUnexpectedResponse
|
||||
ErrInvalidResponse
|
||||
ErrTooManyTimeouts
|
||||
ErrHandshakeMissingKey
|
||||
ErrMissingKey
|
||||
)
|
||||
|
||||
func (e errCode) String() string {
|
||||
|
|
@ -110,7 +116,13 @@ var errorToString = map[int]string{
|
|||
ErrUnexpectedResponse: "Unexpected response",
|
||||
ErrInvalidResponse: "Invalid response",
|
||||
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.
|
||||
|
|
@ -122,6 +134,32 @@ type announceData struct {
|
|||
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 {
|
||||
Hash common.Hash // Hash of one particular block being announced
|
||||
Number uint64 // Number of one particular block being announced
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package les
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"sync"
|
||||
|
|
@ -41,6 +42,7 @@ type LesServer struct {
|
|||
fcCostStats *requestCostStats
|
||||
defParams *flowcontrol.ServerParams
|
||||
lesTopics []discv5.Topic
|
||||
privateKey *ecdsa.PrivateKey
|
||||
quitSync chan struct{}
|
||||
|
||||
chtIndexer, bltIndexer *core.ChainIndexer
|
||||
|
|
@ -52,7 +54,6 @@ func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pm.blockLoop()
|
||||
|
||||
lesTopics := make([]discv5.Topic, len(ServerProtocolVersions))
|
||||
for i, pv := range ServerProtocolVersions {
|
||||
|
|
@ -95,6 +96,8 @@ func (s *LesServer) Start(srvr *p2p.Server) {
|
|||
srvr.DiscV5.RegisterTopic(topic, s.quitSync)
|
||||
}()
|
||||
}
|
||||
s.privateKey = srvr.PrivateKey
|
||||
s.protocolManager.blockLoop()
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg}
|
||||
var (
|
||||
signed bool
|
||||
signedAnnounce announceData
|
||||
)
|
||||
|
||||
for _, p := range peers {
|
||||
select {
|
||||
case p.announceChn <- announce:
|
||||
default:
|
||||
pm.removePeer(p.id)
|
||||
switch p.announceType {
|
||||
|
||||
case announceTypeSimple:
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue