mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 01:13:45 +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
|
// 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)
|
||||||
|
|
|
||||||
20
les/peer.go
20
les/peer.go
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,12 +316,34 @@ 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 {
|
||||||
|
switch p.announceType {
|
||||||
|
|
||||||
|
case announceTypeSimple:
|
||||||
select {
|
select {
|
||||||
case p.announceChn <- announce:
|
case p.announceChn <- announce:
|
||||||
default:
|
default:
|
||||||
pm.removePeer(p.id)
|
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