Skip to content

Commit ebe2d9d

Browse files
committed
First draft of Whisper messages relaying
1 parent f06543f commit ebe2d9d

File tree

7 files changed

+472
-0
lines changed

7 files changed

+472
-0
lines changed

whisper/envelope.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package whisper
2+
3+
import (
4+
"bytes"
5+
"encoding/binary"
6+
"io"
7+
"time"
8+
9+
"github.com/ethereum/go-ethereum/crypto"
10+
"github.com/ethereum/go-ethereum/ethutil"
11+
"github.com/ethereum/go-ethereum/rlp"
12+
)
13+
14+
const (
15+
DefaultTtl = 50 * time.Second
16+
)
17+
18+
type Envelope struct {
19+
Expiry int32 // Whisper protocol specifies int32, really should be int64
20+
Ttl int32 // ^^^^^^
21+
Topics [][]byte
22+
Data []byte
23+
Nonce uint32
24+
25+
hash Hash
26+
}
27+
28+
func NewEnvelopeFromReader(reader io.Reader) (*Envelope, error) {
29+
var envelope Envelope
30+
31+
buf := new(bytes.Buffer)
32+
buf.ReadFrom(reader)
33+
34+
h := H(crypto.Sha3(buf.Bytes()))
35+
if err := rlp.Decode(buf, &envelope); err != nil {
36+
return nil, err
37+
}
38+
39+
envelope.hash = h
40+
41+
return &envelope, nil
42+
}
43+
44+
func (self *Envelope) Hash() Hash {
45+
if self.hash == EmptyHash {
46+
self.hash = H(crypto.Sha3(ethutil.Encode(self)))
47+
}
48+
49+
return self.hash
50+
}
51+
52+
func NewEnvelope(ttl time.Duration, topics [][]byte, data *Message) *Envelope {
53+
exp := time.Now().Add(ttl)
54+
55+
return &Envelope{int32(exp.Unix()), int32(ttl.Seconds()), topics, data.Bytes(), 0, Hash{}}
56+
}
57+
58+
func (self *Envelope) Seal() {
59+
self.proveWork(DefaultTtl)
60+
}
61+
62+
func (self *Envelope) proveWork(dura time.Duration) {
63+
var bestBit int
64+
d := make([]byte, 64)
65+
copy(d[:32], ethutil.Encode(self.withoutNonce()))
66+
67+
then := time.Now().Add(dura).UnixNano()
68+
for n := uint32(0); time.Now().UnixNano() < then; {
69+
for i := 0; i < 1024; i++ {
70+
binary.BigEndian.PutUint32(d[60:], n)
71+
72+
fbs := ethutil.FirstBitSet(ethutil.BigD(crypto.Sha3(d)))
73+
if fbs > bestBit {
74+
bestBit = fbs
75+
self.Nonce = n
76+
}
77+
78+
n++
79+
}
80+
}
81+
}
82+
83+
func (self *Envelope) valid() bool {
84+
d := make([]byte, 64)
85+
copy(d[:32], ethutil.Encode(self.withoutNonce()))
86+
binary.BigEndian.PutUint32(d[60:], self.Nonce)
87+
return ethutil.FirstBitSet(ethutil.BigD(crypto.Sha3(d))) > 0
88+
}
89+
90+
func (self *Envelope) withoutNonce() interface{} {
91+
return []interface{}{self.Expiry, self.Ttl, ethutil.ByteSliceToInterface(self.Topics), self.Data}
92+
}
93+
94+
func (self *Envelope) RlpData() interface{} {
95+
return []interface{}{self.Expiry, self.Ttl, ethutil.ByteSliceToInterface(self.Topics), self.Data, self.Nonce}
96+
}

whisper/main.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// +build none
2+
3+
package main
4+
5+
import (
6+
"fmt"
7+
"log"
8+
"net"
9+
"os"
10+
11+
"github.com/ethereum/go-ethereum/logger"
12+
"github.com/ethereum/go-ethereum/p2p"
13+
"github.com/ethereum/go-ethereum/whisper"
14+
"github.com/obscuren/secp256k1-go"
15+
)
16+
17+
func main() {
18+
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.InfoLevel))
19+
20+
pub, sec := secp256k1.GenerateKeyPair()
21+
22+
whisper := whisper.New(pub, sec)
23+
24+
srv := p2p.Server{
25+
MaxPeers: 10,
26+
Identity: p2p.NewSimpleClientIdentity("whisper-go", "1.0", "", string(pub)),
27+
ListenAddr: ":30303",
28+
NAT: p2p.UPNP(),
29+
30+
Protocols: []p2p.Protocol{whisper.Protocol()},
31+
}
32+
if err := srv.Start(); err != nil {
33+
fmt.Println("could not start server:", err)
34+
os.Exit(1)
35+
}
36+
37+
// add seed peers
38+
seed, err := net.ResolveTCPAddr("tcp", "poc-7.ethdev.com:30300")
39+
if err != nil {
40+
fmt.Println("couldn't resolve:", err)
41+
os.Exit(1)
42+
}
43+
srv.SuggestPeer(seed.IP, seed.Port, nil)
44+
45+
select {}
46+
}

whisper/message.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package whisper
2+
3+
type Message struct {
4+
Flags byte
5+
Signature []byte
6+
Payload []byte
7+
}
8+
9+
func NewMessage(payload []byte) *Message {
10+
return &Message{Flags: 0, Payload: payload}
11+
}
12+
13+
func (self *Message) Bytes() []byte {
14+
return append([]byte{self.Flags}, append(self.Signature, self.Payload...)...)
15+
}

whisper/peer.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package whisper
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"time"
7+
8+
"github.com/ethereum/go-ethereum/p2p"
9+
"gopkg.in/fatih/set.v0"
10+
)
11+
12+
const (
13+
protocolVersion = 0x02
14+
)
15+
16+
type peer struct {
17+
host *Whisper
18+
peer *p2p.Peer
19+
ws p2p.MsgReadWriter
20+
21+
// XXX Eventually this is going to reach exceptional large space. We need an expiry here
22+
known *set.Set
23+
24+
quit chan struct{}
25+
}
26+
27+
func NewPeer(host *Whisper, p *p2p.Peer, ws p2p.MsgReadWriter) *peer {
28+
return &peer{host, p, ws, set.New(), make(chan struct{})}
29+
}
30+
31+
func (self *peer) init() error {
32+
if err := self.handleStatus(); err != nil {
33+
return err
34+
}
35+
36+
return nil
37+
}
38+
39+
func (self *peer) start() {
40+
go self.update()
41+
}
42+
43+
func (self *peer) update() {
44+
relay := time.NewTicker(300 * time.Millisecond)
45+
out:
46+
for {
47+
select {
48+
case <-relay.C:
49+
err := self.broadcast(self.host.envelopes())
50+
if err != nil {
51+
self.peer.Infoln(err)
52+
break out
53+
}
54+
55+
case <-self.quit:
56+
break out
57+
}
58+
}
59+
}
60+
61+
func (self *peer) broadcast(envelopes []*Envelope) error {
62+
envs := make([]interface{}, len(envelopes))
63+
i := 0
64+
for _, envelope := range envelopes {
65+
if !self.known.Has(envelope.Hash()) {
66+
envs[i] = envelope
67+
self.known.Add(envelope.Hash())
68+
i++
69+
}
70+
}
71+
72+
msg := p2p.NewMsg(envelopesMsg, envs[:i]...)
73+
if err := self.ws.WriteMsg(msg); err != nil {
74+
return err
75+
}
76+
77+
return nil
78+
}
79+
80+
func (self *peer) handleStatus() error {
81+
ws := self.ws
82+
83+
if err := ws.WriteMsg(self.statusMsg()); err != nil {
84+
return err
85+
}
86+
87+
msg, err := ws.ReadMsg()
88+
if err != nil {
89+
return err
90+
}
91+
92+
if msg.Code != statusMsg {
93+
return fmt.Errorf("peer send %x before status msg", msg.Code)
94+
}
95+
96+
data, err := ioutil.ReadAll(msg.Payload)
97+
if err != nil {
98+
return err
99+
}
100+
101+
if len(data) == 0 {
102+
return fmt.Errorf("malformed status. data len = 0")
103+
}
104+
105+
if pv := data[0]; pv != protocolVersion {
106+
return fmt.Errorf("protocol version mismatch %d != %d", pv, protocolVersion)
107+
}
108+
109+
return nil
110+
}
111+
112+
func (self *peer) statusMsg() p2p.Msg {
113+
return p2p.NewMsg(statusMsg, protocolVersion)
114+
}

whisper/sort.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package whisper
2+
3+
import "sort"
4+
5+
type sortedKeys struct {
6+
k []int32
7+
}
8+
9+
func (self *sortedKeys) Len() int { return len(self.k) }
10+
func (self *sortedKeys) Less(i, j int) bool { return self.k[i] < self.k[j] }
11+
func (self *sortedKeys) Swap(i, j int) { self.k[i], self.k[j] = self.k[j], self.k[i] }
12+
13+
func sortKeys(m map[int32]Hash) []int32 {
14+
sorted := new(sortedKeys)
15+
sorted.k = make([]int32, len(m))
16+
i := 0
17+
for key, _ := range m {
18+
sorted.k[i] = key
19+
i++
20+
}
21+
22+
sort.Sort(sorted)
23+
24+
return sorted.k
25+
}

whisper/sort_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package whisper
2+
3+
import "testing"
4+
5+
func TestSorting(t *testing.T) {
6+
m := map[int32]Hash{
7+
1: HS("1"),
8+
3: HS("3"),
9+
2: HS("2"),
10+
5: HS("5"),
11+
}
12+
exp := []int32{1, 2, 3, 5}
13+
res := sortKeys(m)
14+
for i, k := range res {
15+
if k != exp[i] {
16+
t.Error(k, "failed. Expected", exp[i])
17+
}
18+
}
19+
}

0 commit comments

Comments
 (0)