diff --git a/whisper/whisperv6/api.go b/whisper/whisperv6/api.go index c60bc46a13..2e475e4850 100644 --- a/whisper/whisperv6/api.go +++ b/whisper/whisperv6/api.go @@ -192,17 +192,17 @@ func (api *PublicWhisperAPI) DeleteSymKey(ctx context.Context, id string) bool { return api.w.DeleteSymKey(id) } -// MakeLightClient turns the node into light client, which does not forward +// MakeLightClient turns the node into pure light client, which does not forward // any incoming messages, and sends only messages originated in this node. func (api *PublicWhisperAPI) MakeLightClient(ctx context.Context) bool { - api.w.lightClient = true - return api.w.lightClient + api.w.SetLightClientMode(true) + return true } -// CancelLightClient cancels light client mode. +// CancelLightClient cancels pure light client mode. func (api *PublicWhisperAPI) CancelLightClient(ctx context.Context) bool { - api.w.lightClient = false - return !api.w.lightClient + api.w.SetLightClientMode(false) + return true } //go:generate gencodec -type NewMessage -field-override newMessageOverride -out gen_newmessage_json.go diff --git a/whisper/whisperv6/peer.go b/whisper/whisperv6/peer.go index 2bf1c905b2..a662397c64 100644 --- a/whisper/whisperv6/peer.go +++ b/whisper/whisperv6/peer.go @@ -79,11 +79,13 @@ func (peer *Peer) stop() { func (peer *Peer) handshake() error { // Send the handshake status message asynchronously errc := make(chan error, 1) + isLightNode := peer.host.LightClientMode() go func() { pow := peer.host.MinPow() powConverted := math.Float64bits(pow) 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 @@ -127,6 +129,11 @@ func (peer *Peer) handshake() error { } } + b, err := s.Bool() + if b && isLightNode { + return fmt.Errorf("peer [%x]: useless peer: two pure light node communication", peer.ID()) + } + if err := <-errc; err != nil { return fmt.Errorf("peer [%x] failed to send status packet: %v", peer.ID(), err) } diff --git a/whisper/whisperv6/peer_test.go b/whisper/whisperv6/peer_test.go index ec985ae65b..076b024b85 100644 --- a/whisper/whisperv6/peer_test.go +++ b/whisper/whisperv6/peer_test.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/rlp" ) var keys = []string{ @@ -512,3 +513,47 @@ func waitForServersToStart(t *testing.T) { } t.Fatalf("Failed to start all the servers, running: %d", started) } + +func TestPureLightPeerHandshake(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() + } +} + +func TestPureLightPeerHandshakeWithOldVersion(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() + } +} + +func TestPureLightPeerHandshakeError(t *testing.T) { + w1 := Whisper{} + 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 +} diff --git a/whisper/whisperv6/whisper.go b/whisper/whisperv6/whisper.go index 880cced098..4384fed9ed 100644 --- a/whisper/whisperv6/whisper.go +++ b/whisper/whisperv6/whisper.go @@ -55,6 +55,7 @@ const ( minPowToleranceIdx // Minimal PoW tolerated by the whisper node for a limited time bloomFilterIdx // Bloom filter for topics of interest for this node bloomFilterToleranceIdx // Bloom filter tolerated by the whisper node for a limited time + lightClientModeIdx // Pure light client mode. (does not forward any messages) ) // Whisper represents a dark communication interface through the Ethereum @@ -82,8 +83,6 @@ type Whisper struct { 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 stats Statistics // Statistics of whisper node @@ -276,6 +275,24 @@ func (whisper *Whisper) SetMinimumPowTest(val float64) { whisper.settings.Store(minPowToleranceIdx, val) } +//SetLightClientMode makes node pure light client (does not forward any messages) +func (whisper *Whisper) SetLightClientMode(v bool) { + whisper.settings.Store(lightClientModeIdx, v) +} + +//LightClientMode indicates is this node is pure 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) + if !ok { + return false + } + return v +} + func (whisper *Whisper) notifyPeersAboutPowRequirementChange(pow float64) { arr := whisper.getPeers() for _, p := range arr { @@ -672,7 +689,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { trouble := false for _, env := range envelopes { - cached, err := whisper.add(env, whisper.lightClient) + cached, err := whisper.add(env, whisper.LightClientMode()) if err != nil { trouble = true log.Error("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err)