whisper: add light mode check to handshake (#16725)

This commit is contained in:
b00ris 2018-09-05 11:57:45 +03:00 committed by Guillaume Ballet
parent cf33d8b83c
commit 8711e2b636
8 changed files with 126 additions and 18 deletions

View File

@ -168,6 +168,9 @@ func makeFullNode(ctx *cli.Context) *node.Node {
if ctx.GlobalIsSet(utils.WhisperMinPOWFlag.Name) { if ctx.GlobalIsSet(utils.WhisperMinPOWFlag.Name) {
cfg.Shh.MinimumAcceptedPOW = ctx.Float64(utils.WhisperMinPOWFlag.Name) cfg.Shh.MinimumAcceptedPOW = ctx.Float64(utils.WhisperMinPOWFlag.Name)
} }
if ctx.GlobalIsSet(utils.WhisperRestrictConnectionBetweenLightClientsFlag.Name) {
cfg.Shh.RestrictConnectionBetweenLightClients = true
}
utils.RegisterShhService(stack, &cfg.Shh) utils.RegisterShhService(stack, &cfg.Shh)
} }

View File

@ -151,6 +151,7 @@ var (
utils.WhisperEnabledFlag, utils.WhisperEnabledFlag,
utils.WhisperMaxMessageSizeFlag, utils.WhisperMaxMessageSizeFlag,
utils.WhisperMinPOWFlag, utils.WhisperMinPOWFlag,
utils.WhisperRestrictConnectionBetweenLightClientsFlag,
} }
metricsFlags = []cli.Flag{ metricsFlags = []cli.Flag{

View File

@ -567,6 +567,10 @@ var (
Usage: "Minimum POW accepted", Usage: "Minimum POW accepted",
Value: whisper.DefaultMinimumPoW, Value: whisper.DefaultMinimumPoW,
} }
WhisperRestrictConnectionBetweenLightClientsFlag = cli.BoolFlag{
Name: "shh.restrict-light",
Usage: "Restrict connection between two whisper light clients",
}
// Metrics flags // Metrics flags
MetricsEnabledFlag = cli.BoolFlag{ MetricsEnabledFlag = cli.BoolFlag{
@ -1099,6 +1103,9 @@ func SetShhConfig(ctx *cli.Context, stack *node.Node, cfg *whisper.Config) {
if ctx.GlobalIsSet(WhisperMinPOWFlag.Name) { if ctx.GlobalIsSet(WhisperMinPOWFlag.Name) {
cfg.MinimumAcceptedPOW = ctx.GlobalFloat64(WhisperMinPOWFlag.Name) cfg.MinimumAcceptedPOW = ctx.GlobalFloat64(WhisperMinPOWFlag.Name)
} }
if ctx.GlobalIsSet(WhisperRestrictConnectionBetweenLightClientsFlag.Name) {
cfg.RestrictConnectionBetweenLightClients = true
}
} }
// SetEthConfig applies eth-related command line flags to the config. // SetEthConfig applies eth-related command line flags to the config.

View File

@ -195,14 +195,14 @@ func (api *PublicWhisperAPI) DeleteSymKey(ctx context.Context, id string) bool {
// MakeLightClient turns the node into light client, which does not forward // MakeLightClient turns the node into light client, which does not forward
// any incoming messages, and sends only messages originated in this node. // any incoming messages, and sends only messages originated in this node.
func (api *PublicWhisperAPI) MakeLightClient(ctx context.Context) bool { func (api *PublicWhisperAPI) MakeLightClient(ctx context.Context) bool {
api.w.lightClient = true api.w.SetLightClientMode(true)
return api.w.lightClient return api.w.LightClientMode()
} }
// CancelLightClient cancels light client mode. // CancelLightClient cancels light client mode.
func (api *PublicWhisperAPI) CancelLightClient(ctx context.Context) bool { func (api *PublicWhisperAPI) CancelLightClient(ctx context.Context) bool {
api.w.lightClient = false api.w.SetLightClientMode(false)
return !api.w.lightClient return !api.w.LightClientMode()
} }
//go:generate gencodec -type NewMessage -field-override newMessageOverride -out gen_newmessage_json.go //go:generate gencodec -type NewMessage -field-override newMessageOverride -out gen_newmessage_json.go

View File

@ -18,12 +18,14 @@ package whisperv6
// Config represents the configuration state of a whisper node. // Config represents the configuration state of a whisper node.
type Config struct { type Config struct {
MaxMessageSize uint32 `toml:",omitempty"` MaxMessageSize uint32 `toml:",omitempty"`
MinimumAcceptedPOW float64 `toml:",omitempty"` MinimumAcceptedPOW float64 `toml:",omitempty"`
RestrictConnectionBetweenLightClients bool `toml:",omitempty"`
} }
// DefaultConfig represents (shocker!) the default configuration. // DefaultConfig represents (shocker!) the default configuration.
var DefaultConfig = Config{ var DefaultConfig = Config{
MaxMessageSize: DefaultMaxMessageSize, MaxMessageSize: DefaultMaxMessageSize,
MinimumAcceptedPOW: DefaultMinimumPoW, MinimumAcceptedPOW: DefaultMinimumPoW,
RestrictConnectionBetweenLightClients: true,
} }

View File

@ -79,11 +79,14 @@ func (peer *Peer) stop() {
func (peer *Peer) handshake() error { func (peer *Peer) handshake() error {
// Send the handshake status message asynchronously // Send the handshake status message asynchronously
errc := make(chan error, 1) errc := make(chan error, 1)
isLightNode := peer.host.LightClientMode()
isRestrictedLightNodeConnection := peer.host.LightClientModeConnectionRestricted()
go func() { go func() {
pow := peer.host.MinPow() pow := peer.host.MinPow()
powConverted := math.Float64bits(pow) powConverted := math.Float64bits(pow)
bloom := peer.host.BloomFilter() bloom := peer.host.BloomFilter()
errc <- p2p.SendItems(peer.ws, statusCode, ProtocolVersion, powConverted, bloom)
errc <- p2p.SendItems(peer.ws, statusCode, ProtocolVersion, powConverted, bloom, isLightNode)
}() }()
// Fetch the remote status packet and verify protocol match // Fetch the remote status packet and verify protocol match
@ -127,6 +130,11 @@ func (peer *Peer) handshake() error {
} }
} }
isRemotePeerLightNode, err := s.Bool()
if isRemotePeerLightNode && isLightNode && isRestrictedLightNodeConnection {
return fmt.Errorf("peer [%x] is useless: two light client communication restricted", peer.ID())
}
if err := <-errc; err != nil { if err := <-errc; err != nil {
return fmt.Errorf("peer [%x] failed to send status packet: %v", peer.ID(), err) return fmt.Errorf("peer [%x] failed to send status packet: %v", peer.ID(), err)
} }

View File

@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/rlp"
) )
var keys = []string{ var keys = []string{
@ -507,3 +508,63 @@ func waitForServersToStart(t *testing.T) {
} }
t.Fatalf("Failed to start all the servers, running: %d", started) t.Fatalf("Failed to start all the servers, running: %d", started)
} }
//two generic whisper node handshake
func TestPeerHandshakeWithTwoFullNode(t *testing.T) {
w1 := Whisper{}
p1 := newPeer(&w1, p2p.NewPeer(discover.NodeID{}, "test", []p2p.Cap{}), &rwStub{[]interface{}{ProtocolVersion, uint64(123), make([]byte, BloomFilterSize), false}})
err := p1.handshake()
if err != nil {
t.Fatal()
}
}
//two generic whisper node handshake. one don't send light flag
func TestHandshakeWithOldVersionWithoutLightModeFlag(t *testing.T) {
w1 := Whisper{}
p1 := newPeer(&w1, p2p.NewPeer(discover.NodeID{}, "test", []p2p.Cap{}), &rwStub{[]interface{}{ProtocolVersion, uint64(123), make([]byte, BloomFilterSize)}})
err := p1.handshake()
if err != nil {
t.Fatal()
}
}
//two light nodes handshake. restriction disabled
func TestTwoLightPeerHandshakeRestrictionOff(t *testing.T) {
w1 := Whisper{}
w1.settings.Store(restrictConnectionBetweenLightClientsIdx, false)
w1.SetLightClientMode(true)
p1 := newPeer(&w1, p2p.NewPeer(discover.NodeID{}, "test", []p2p.Cap{}), &rwStub{[]interface{}{ProtocolVersion, uint64(123), make([]byte, BloomFilterSize), true}})
err := p1.handshake()
if err != nil {
t.FailNow()
}
}
//two light nodes handshake. restriction enabled
func TestTwoLightPeerHandshakeError(t *testing.T) {
w1 := Whisper{}
w1.settings.Store(restrictConnectionBetweenLightClientsIdx, true)
w1.SetLightClientMode(true)
p1 := newPeer(&w1, p2p.NewPeer(discover.NodeID{}, "test", []p2p.Cap{}), &rwStub{[]interface{}{ProtocolVersion, uint64(123), make([]byte, BloomFilterSize), true}})
err := p1.handshake()
if err == nil {
t.FailNow()
}
}
type rwStub struct {
payload []interface{}
}
func (stub *rwStub) ReadMsg() (p2p.Msg, error) {
size, r, err := rlp.EncodeToReader(stub.payload)
if err != nil {
return p2p.Msg{}, err
}
return p2p.Msg{Code: statusCode, Size: uint32(size), Payload: r}, nil
}
func (stub *rwStub) WriteMsg(m p2p.Msg) error {
return nil
}

View File

@ -49,12 +49,14 @@ type Statistics struct {
} }
const ( const (
maxMsgSizeIdx = iota // Maximal message length allowed by the whisper node maxMsgSizeIdx = iota // Maximal message length allowed by the whisper node
overflowIdx // Indicator of message queue overflow overflowIdx // Indicator of message queue overflow
minPowIdx // Minimal PoW required by the whisper node minPowIdx // Minimal PoW required by the whisper node
minPowToleranceIdx // Minimal PoW tolerated by the whisper node for a limited time minPowToleranceIdx // Minimal PoW tolerated by the whisper node for a limited time
bloomFilterIdx // Bloom filter for topics of interest for this node bloomFilterIdx // Bloom filter for topics of interest for this node
bloomFilterToleranceIdx // Bloom filter tolerated by the whisper node for a limited time bloomFilterToleranceIdx // Bloom filter tolerated by the whisper node for a limited time
lightClientModeIdx // Light client mode. (does not forward any messages)
restrictConnectionBetweenLightClientsIdx // Restrict connection between two light clients
) )
// Whisper represents a dark communication interface through the Ethereum // Whisper represents a dark communication interface through the Ethereum
@ -82,8 +84,6 @@ type Whisper struct {
syncAllowance int // maximum time in seconds allowed to process the whisper-related messages syncAllowance int // maximum time in seconds allowed to process the whisper-related messages
lightClient bool // indicates is this node is pure light client (does not forward any messages)
statsMu sync.Mutex // guard stats statsMu sync.Mutex // guard stats
stats Statistics // Statistics of whisper node stats Statistics // Statistics of whisper node
@ -113,6 +113,7 @@ func New(cfg *Config) *Whisper {
whisper.settings.Store(minPowIdx, cfg.MinimumAcceptedPOW) whisper.settings.Store(minPowIdx, cfg.MinimumAcceptedPOW)
whisper.settings.Store(maxMsgSizeIdx, cfg.MaxMessageSize) whisper.settings.Store(maxMsgSizeIdx, cfg.MaxMessageSize)
whisper.settings.Store(overflowIdx, false) whisper.settings.Store(overflowIdx, false)
whisper.settings.Store(restrictConnectionBetweenLightClientsIdx, cfg.RestrictConnectionBetweenLightClients)
// p2p whisper sub protocol handler // p2p whisper sub protocol handler
whisper.protocol = p2p.Protocol{ whisper.protocol = p2p.Protocol{
@ -276,6 +277,31 @@ func (whisper *Whisper) SetMinimumPowTest(val float64) {
whisper.settings.Store(minPowToleranceIdx, val) whisper.settings.Store(minPowToleranceIdx, val)
} }
//SetLightClientMode makes node light client (does not forward any messages)
func (whisper *Whisper) SetLightClientMode(v bool) {
whisper.settings.Store(lightClientModeIdx, v)
}
//LightClientMode indicates is this node is light client (does not forward any messages)
func (whisper *Whisper) LightClientMode() bool {
val, exist := whisper.settings.Load(lightClientModeIdx)
if !exist || val == nil {
return false
}
v, ok := val.(bool)
return v && ok
}
//LightClientModeConnectionRestricted indicates that connection to light client in light client mode not allowed
func (whisper *Whisper) LightClientModeConnectionRestricted() bool {
val, exist := whisper.settings.Load(restrictConnectionBetweenLightClientsIdx)
if !exist || val == nil {
return false
}
v, ok := val.(bool)
return v && ok
}
func (whisper *Whisper) notifyPeersAboutPowRequirementChange(pow float64) { func (whisper *Whisper) notifyPeersAboutPowRequirementChange(pow float64) {
arr := whisper.getPeers() arr := whisper.getPeers()
for _, p := range arr { for _, p := range arr {
@ -672,7 +698,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
trouble := false trouble := false
for _, env := range envelopes { for _, env := range envelopes {
cached, err := whisper.add(env, whisper.lightClient) cached, err := whisper.add(env, whisper.LightClientMode())
if err != nil { if err != nil {
trouble = true trouble = true
log.Error("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err) log.Error("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err)