Skip to content

Commit 690f6ea

Browse files
gluk256fjl
authored andcommitted
cmd/wnode, whisper: add whisper CLI tool and mail server (#3580)
1 parent 1c140f7 commit 690f6ea

File tree

8 files changed

+769
-30
lines changed

8 files changed

+769
-30
lines changed

cmd/wnode/main.go

Lines changed: 537 additions & 0 deletions
Large diffs are not rendered by default.

whisper/mailserver/mailserver.go

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// Copyright 2016 The go-ethereum Authors
2+
// This file is part of go-ethereum.
3+
//
4+
// go-ethereum is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// go-ethereum is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package mailserver
18+
19+
import (
20+
"bytes"
21+
"encoding/binary"
22+
23+
"github.com/ethereum/go-ethereum/cmd/utils"
24+
"github.com/ethereum/go-ethereum/common"
25+
"github.com/ethereum/go-ethereum/logger"
26+
"github.com/ethereum/go-ethereum/logger/glog"
27+
"github.com/ethereum/go-ethereum/rlp"
28+
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
29+
"github.com/syndtr/goleveldb/leveldb"
30+
"github.com/syndtr/goleveldb/leveldb/util"
31+
)
32+
33+
const MailServerKeyName = "958e04ab302fb36ad2616a352cbac79d"
34+
35+
type WMailServer struct {
36+
db *leveldb.DB
37+
w *whisper.Whisper
38+
pow float64
39+
key []byte
40+
}
41+
42+
type DBKey struct {
43+
timestamp uint32
44+
hash common.Hash
45+
raw []byte
46+
}
47+
48+
func NewDbKey(t uint32, h common.Hash) *DBKey {
49+
const sz = common.HashLength + 4
50+
var k DBKey
51+
k.timestamp = t
52+
k.hash = h
53+
k.raw = make([]byte, sz)
54+
binary.BigEndian.PutUint32(k.raw, k.timestamp)
55+
copy(k.raw[4:], k.hash[:])
56+
return &k
57+
}
58+
59+
func (s *WMailServer) Init(shh *whisper.Whisper, path string, password string, pow float64) {
60+
var err error
61+
if len(path) == 0 {
62+
utils.Fatalf("DB file is not specified")
63+
}
64+
65+
if len(password) == 0 {
66+
utils.Fatalf("Password is not specified for MailServer")
67+
}
68+
69+
s.db, err = leveldb.OpenFile(path, nil)
70+
if err != nil {
71+
utils.Fatalf("Failed to open DB file: %s", err)
72+
}
73+
74+
s.w = shh
75+
s.pow = pow
76+
77+
err = s.w.AddSymKey(MailServerKeyName, []byte(password))
78+
if err != nil {
79+
utils.Fatalf("Failed to create symmetric key for MailServer: %s", err)
80+
}
81+
s.key = s.w.GetSymKey(MailServerKeyName)
82+
}
83+
84+
func (s *WMailServer) Close() {
85+
if s.db != nil {
86+
s.db.Close()
87+
}
88+
}
89+
90+
func (s *WMailServer) Archive(env *whisper.Envelope) {
91+
key := NewDbKey(env.Expiry-env.TTL, env.Hash())
92+
rawEnvelope, err := rlp.EncodeToBytes(env)
93+
if err != nil {
94+
glog.V(logger.Error).Infof("rlp.EncodeToBytes failed: %s", err)
95+
} else {
96+
err = s.db.Put(key.raw, rawEnvelope, nil)
97+
if err != nil {
98+
glog.V(logger.Error).Infof("Writing to DB failed: %s", err)
99+
}
100+
}
101+
}
102+
103+
func (s *WMailServer) DeliverMail(peer *whisper.Peer, request *whisper.Envelope) {
104+
ok, lower, upper, topic := s.validate(peer, request)
105+
if !ok {
106+
return
107+
}
108+
109+
var err error
110+
var zero common.Hash
111+
var empty whisper.TopicType
112+
kl := NewDbKey(lower, zero)
113+
ku := NewDbKey(upper, zero)
114+
i := s.db.NewIterator(&util.Range{Start: kl.raw, Limit: ku.raw}, nil)
115+
defer i.Release()
116+
117+
for i.Next() {
118+
var envelope whisper.Envelope
119+
err = rlp.DecodeBytes(i.Value(), &envelope)
120+
if err != nil {
121+
glog.V(logger.Error).Infof("RLP decoding failed: %s", err)
122+
}
123+
124+
if topic == empty || envelope.Topic == topic {
125+
err = s.w.SendP2PDirect(peer, &envelope)
126+
if err != nil {
127+
glog.V(logger.Error).Infof("Failed to send direct message to peer: %s", err)
128+
return
129+
}
130+
}
131+
}
132+
133+
err = i.Error()
134+
if err != nil {
135+
glog.V(logger.Error).Infof("Level DB iterator error: %s", err)
136+
}
137+
}
138+
139+
func (s *WMailServer) validate(peer *whisper.Peer, request *whisper.Envelope) (bool, uint32, uint32, whisper.TopicType) {
140+
var topic whisper.TopicType
141+
if s.pow > 0.0 && request.PoW() < s.pow {
142+
return false, 0, 0, topic
143+
}
144+
145+
f := whisper.Filter{KeySym: s.key}
146+
decrypted := request.Open(&f)
147+
if decrypted == nil {
148+
glog.V(logger.Warn).Infof("Failed to decrypt p2p request")
149+
return false, 0, 0, topic
150+
}
151+
152+
if len(decrypted.Payload) < 8 {
153+
glog.V(logger.Warn).Infof("Undersized p2p request")
154+
return false, 0, 0, topic
155+
}
156+
157+
if bytes.Equal(peer.ID(), decrypted.Signature) {
158+
glog.V(logger.Warn).Infof("Wrong signature of p2p request")
159+
return false, 0, 0, topic
160+
}
161+
162+
lower := binary.BigEndian.Uint32(decrypted.Payload[:4])
163+
upper := binary.BigEndian.Uint32(decrypted.Payload[4:8])
164+
165+
if len(decrypted.Payload) >= 8+whisper.TopicLength {
166+
topic = whisper.BytesToTopic(decrypted.Payload[8:])
167+
}
168+
169+
return true, lower, upper, topic
170+
}

whisper/shhapi/api.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,12 @@ func (api *PublicWhisperAPI) MarkPeerTrusted(peerID hexutil.Bytes) error {
9393
// data contains parameters (time frame, payment details, etc.), required
9494
// by the remote email-like server. Whisper is not aware about the data format,
9595
// it will just forward the raw data to the server.
96-
func (api *PublicWhisperAPI) RequestHistoricMessages(peerID hexutil.Bytes, data hexutil.Bytes) error {
97-
if api.whisper == nil {
98-
return whisperOffLineErr
99-
}
100-
return api.whisper.RequestHistoricMessages(peerID, data)
101-
}
96+
//func (api *PublicWhisperAPI) RequestHistoricMessages(peerID hexutil.Bytes, data hexutil.Bytes) error {
97+
// if api.whisper == nil {
98+
// return whisperOffLineErr
99+
// }
100+
// return api.whisper.RequestHistoricMessages(peerID, data)
101+
//}
102102

103103
// HasIdentity checks if the whisper node is configured with the private key
104104
// of the specified public pair.

whisper/whisperv5/doc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,5 +83,5 @@ func (e unknownVersionError) Error() string {
8383
// in order to bypass the expiry checks.
8484
type MailServer interface {
8585
Archive(env *Envelope)
86-
DeliverMail(whisperPeer *Peer, data []byte)
86+
DeliverMail(whisperPeer *Peer, request *Envelope)
8787
}

whisper/whisperv5/message_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"testing"
2323

2424
"github.com/ethereum/go-ethereum/crypto"
25+
"github.com/ethereum/go-ethereum/rlp"
2526
)
2627

2728
func copyFromBuf(dst []byte, src []byte, beg int) int {
@@ -311,3 +312,35 @@ func TestEncryptWithZeroKey(t *testing.T) {
311312
t.Fatalf("wrapped with nil key, seed: %d.", seed)
312313
}
313314
}
315+
316+
func TestRlpEncode(t *testing.T) {
317+
InitSingleTest()
318+
319+
params, err := generateMessageParams()
320+
if err != nil {
321+
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
322+
}
323+
msg := NewSentMessage(params)
324+
env, err := msg.Wrap(params)
325+
if err != nil {
326+
t.Fatalf("wrapped with zero key, seed: %d.", seed)
327+
}
328+
329+
raw, err := rlp.EncodeToBytes(env)
330+
if err != nil {
331+
t.Fatalf("RLP encode failed: %s.", err)
332+
}
333+
334+
var decoded Envelope
335+
rlp.DecodeBytes(raw, &decoded)
336+
if err != nil {
337+
t.Fatalf("RLP decode failed: %s.", err)
338+
}
339+
340+
he := env.Hash()
341+
hd := decoded.Hash()
342+
343+
if he != hd {
344+
t.Fatalf("Hashes are not equal: %x vs. %x", he, hd)
345+
}
346+
}

whisper/whisperv5/peer.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,8 @@ func (p *Peer) broadcast() error {
175175
glog.V(logger.Detail).Infoln(p.peer, "broadcasted", len(transmit), "message(s)")
176176
return nil
177177
}
178+
179+
func (p *Peer) ID() []byte {
180+
id := p.peer.ID()
181+
return id[:]
182+
}

whisper/whisperv5/whisper.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import (
3131
"github.com/ethereum/go-ethereum/logger"
3232
"github.com/ethereum/go-ethereum/logger/glog"
3333
"github.com/ethereum/go-ethereum/p2p"
34-
"github.com/ethereum/go-ethereum/rlp"
3534
"golang.org/x/crypto/pbkdf2"
3635
set "gopkg.in/fatih/set.v0"
3736
)
@@ -125,13 +124,13 @@ func (w *Whisper) MarkPeerTrusted(peerID []byte) error {
125124
return nil
126125
}
127126

128-
func (w *Whisper) RequestHistoricMessages(peerID []byte, data []byte) error {
127+
func (w *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) error {
129128
p, err := w.getPeer(peerID)
130129
if err != nil {
131130
return err
132131
}
133132
p.trusted = true
134-
return p2p.Send(p.ws, p2pRequestCode, data)
133+
return p2p.Send(p.ws, p2pRequestCode, envelope)
135134
}
136135

137136
func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error {
@@ -142,6 +141,10 @@ func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error {
142141
return p2p.Send(p.ws, p2pCode, envelope)
143142
}
144143

144+
func (w *Whisper) SendP2PDirect(peer *Peer, envelope *Envelope) error {
145+
return p2p.Send(peer.ws, p2pCode, envelope)
146+
}
147+
145148
// NewIdentity generates a new cryptographic identity for the client, and injects
146149
// it into the known identities for message decryption.
147150
func (w *Whisper) NewIdentity() *ecdsa.PrivateKey {
@@ -347,35 +350,29 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
347350
return fmt.Errorf("invalid envelope")
348351
}
349352
p.mark(envelope)
350-
if wh.mailServer != nil {
351-
wh.mailServer.Archive(envelope)
352-
}
353353
}
354354
case p2pCode:
355355
// peer-to-peer message, sent directly to peer bypassing PoW checks, etc.
356356
// this message is not supposed to be forwarded to other peers, and
357357
// therefore might not satisfy the PoW, expiry and other requirements.
358358
// these messages are only accepted from the trusted peer.
359359
if p.trusted {
360-
var envelopes []*Envelope
361-
if err := packet.Decode(&envelopes); err != nil {
360+
var envelope Envelope
361+
if err := packet.Decode(&envelope); err != nil {
362362
glog.V(logger.Warn).Infof("%v: failed to decode direct message: [%v], peer will be disconnected", p.peer, err)
363363
return fmt.Errorf("garbage received (directMessage)")
364364
}
365-
for _, envelope := range envelopes {
366-
wh.postEvent(envelope, true)
367-
}
365+
wh.postEvent(&envelope, true)
368366
}
369367
case p2pRequestCode:
370368
// Must be processed if mail server is implemented. Otherwise ignore.
371369
if wh.mailServer != nil {
372-
s := rlp.NewStream(packet.Payload, uint64(packet.Size))
373-
data, err := s.Bytes()
374-
if err == nil {
375-
wh.mailServer.DeliverMail(p, data)
376-
} else {
377-
glog.V(logger.Error).Infof("%v: bad requestHistoricMessages received: [%v]", p.peer, err)
370+
var request Envelope
371+
if err := packet.Decode(&request); err != nil {
372+
glog.V(logger.Warn).Infof("%v: failed to decode p2p request message: [%v], peer will be disconnected", p.peer, err)
373+
return fmt.Errorf("garbage received (p2p request)")
378374
}
375+
wh.mailServer.DeliverMail(p, &request)
379376
}
380377
default:
381378
// New message types might be implemented in the future versions of Whisper.
@@ -454,6 +451,9 @@ func (wh *Whisper) add(envelope *Envelope) error {
454451
} else {
455452
glog.V(logger.Detail).Infof("cached whisper envelope [%x]: %v\n", envelope.Hash(), envelope)
456453
wh.postEvent(envelope, false) // notify the local node about the new message
454+
if wh.mailServer != nil {
455+
wh.mailServer.Archive(envelope)
456+
}
457457
}
458458
return nil
459459
}

whisper/whisperv5/whisper_test.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,6 @@ func TestWhisperBasic(t *testing.T) {
5757
if err := w.MarkPeerTrusted(peerID); err == nil {
5858
t.Fatalf("failed MarkPeerTrusted.")
5959
}
60-
if err := w.RequestHistoricMessages(peerID, peerID); err == nil {
61-
t.Fatalf("failed RequestHistoricMessages.")
62-
}
63-
if err := w.SendP2PMessage(peerID, nil); err == nil {
64-
t.Fatalf("failed SendP2PMessage.")
65-
}
6660
exist := w.HasSymKey("non-existing")
6761
if exist {
6862
t.Fatalf("failed HasSymKey.")

0 commit comments

Comments
 (0)