From 299b3fc09322b85e0801fe87d1eaaaa61d26979e Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Wed, 29 Apr 2020 20:07:25 +0200 Subject: [PATCH] Implement waku/1 Why make the change? This implements waku/1 which is a breaking change as waku/0 diverged from the specs. What has changed? - Added v1 namespace - Changed the testing code to test from the outside rather than stubbing p2p specific messages, so that any version specific code is under `vx` namespaces - Split waku vs waku/x test code - Added a test suite that can be used for different versions Things still to do I kept the tests we have for the versioned aspect of waku, probably we want to add some or change other to provide the optimal test coverage. --- waku/v1/README.md | 3 +- waku/v1/const.go | 4 +-- waku/v1/peer.go | 23 +++---------- waku/v1/peer_test.go | 65 +++++++++++++++++++++++++++++++++++ waku/v1/statusoptions.go | 15 +++++--- waku/v1/statusoptions_test.go | 30 ++++++++-------- 6 files changed, 98 insertions(+), 42 deletions(-) diff --git a/waku/v1/README.md b/waku/v1/README.md index cad85fa39..903668304 100644 --- a/waku/v1/README.md +++ b/waku/v1/README.md @@ -1,4 +1,3 @@ ### waku/1 -This namespace implements `waku` 1.0 as described in https://github.com/vacp2p/specs/blob/master/specs/waku/waku-1.md. - +This namespace implements `waku` 1.0 as described in https://github.com/vacp2p/specs/blob/master/specs/waku/waku-1.md diff --git a/waku/v1/const.go b/waku/v1/const.go index ad95cc7a7..8e0ff0472 100644 --- a/waku/v1/const.go +++ b/waku/v1/const.go @@ -2,8 +2,8 @@ package v1 // Waku protocol parameters const ( - Version = uint64(0) // Peer version number - VersionStr = "0" // The same, as a string + Version = uint64(1) // Peer version number + VersionStr = "1" // The same, as a string Name = "waku" // Nickname of the protocol // Waku protocol message codes, according to https://github.com/vacp2p/specs/blob/master/specs/waku/waku-0.md diff --git a/waku/v1/peer.go b/waku/v1/peer.go index 00c5d6aff..3dcbf29af 100644 --- a/waku/v1/peer.go +++ b/waku/v1/peer.go @@ -389,7 +389,7 @@ func (p *Peer) handshake() error { errc := make(chan error, 1) opts := StatusOptionsFromHost(p.host) go func() { - errc <- p2p.SendItems(p.rw, statusCode, Version, opts) + errc <- p2p.Send(p.rw, statusCode, opts) }() // Fetch the remote status packet and verify protocol match @@ -401,34 +401,21 @@ func (p *Peer) handshake() error { return fmt.Errorf("p [%x] sent packet %x before status packet", p.ID(), packet.Code) } - var ( - peerProtocolVersion uint64 - peerOptions StatusOptions - ) + var peerOptions StatusOptions s := rlp.NewStream(packet.Payload, uint64(packet.Size)) - if _, err := s.List(); err != nil { - return fmt.Errorf("p [%x]: failed to decode status packet: %v", p.ID(), err) - } - // Validate protocol version. - if err := s.Decode(&peerProtocolVersion); err != nil { - return fmt.Errorf("p [%x]: failed to decode peer protocol version: %v", p.ID(), err) - } - if peerProtocolVersion != Version { - return fmt.Errorf("p [%x]: protocol version mismatch %d != %d", p.ID(), peerProtocolVersion, Version) - } + // Decode and validate other status packet options. if err := s.Decode(&peerOptions); err != nil { return fmt.Errorf("p [%x]: failed to decode status options: %v", p.ID(), err) } - if err := s.ListEnd(); err != nil { - return fmt.Errorf("p [%x]: failed to decode status packet: %v", p.ID(), err) - } if err := p.setOptions(peerOptions.WithDefaults()); err != nil { return fmt.Errorf("p [%x]: failed to set options: %v", p.ID(), err) } if err := <-errc; err != nil { return fmt.Errorf("p [%x] failed to send status packet: %v", p.ID(), err) } + + _ = packet.Discard() return nil } diff --git a/waku/v1/peer_test.go b/waku/v1/peer_test.go index e82780fdb..7ae61792e 100644 --- a/waku/v1/peer_test.go +++ b/waku/v1/peer_test.go @@ -19,11 +19,25 @@ package v1 import ( + mrand "math/rand" "testing" + "time" + + "github.com/ethereum/go-ethereum/crypto" "github.com/status-im/status-go/waku/common" ) +var seed int64 + +// initSingleTest should be called in the beginning of every +// test, which uses RNG, in order to make the tests +// reproduciblity independent of their sequence. +func initSingleTest() { + seed = time.Now().Unix() + mrand.Seed(seed) +} + var sharedTopic = common.TopicType{0xF, 0x1, 0x2, 0} var wrongTopic = common.TopicType{0, 0, 0, 0} @@ -58,3 +72,54 @@ func TestTopicOrBloomMatchFullNode(t *testing.T) { t.Fatal("envelope should not match") } } + +func TestPeerBasic(t *testing.T) { + initSingleTest() + + params, err := generateMessageParams() + if err != nil { + t.Fatalf("failed generateMessageParams with seed %d.", seed) + } + + params.PoW = 0.001 + msg, err := common.NewSentMessage(params) + if err != nil { + t.Fatalf("failed to create new message with seed %d: %s.", seed, err) + } + env, err := msg.Wrap(params, time.Now()) + if err != nil { + t.Fatalf("failed Wrap with seed %d.", seed) + } + + p := NewPeer(nil, nil, nil, nil) + p.Mark(env) + if !p.Marked(env) { + t.Fatalf("failed mark with seed %d.", seed) + } +} + +func generateMessageParams() (*common.MessageParams, error) { + // set all the parameters except p.Dst and p.Padding + + buf := make([]byte, 4) + mrand.Read(buf) // nolint: gosec + sz := mrand.Intn(400) + + var p common.MessageParams + p.PoW = 0.01 + p.WorkTime = 1 + p.TTL = uint32(mrand.Intn(1024)) + p.Payload = make([]byte, sz) + p.KeySym = make([]byte, common.AESKeyLength) + mrand.Read(p.Payload) // nolint: gosec + mrand.Read(p.KeySym) // nolint: gosec + p.Topic = common.BytesToTopic(buf) + + var err error + p.Src, err = crypto.GenerateKey() + if err != nil { + return nil, err + } + + return &p, nil +} diff --git a/waku/v1/statusoptions.go b/waku/v1/statusoptions.go index 484897379..88e63bac5 100644 --- a/waku/v1/statusoptions.go +++ b/waku/v1/statusoptions.go @@ -6,6 +6,7 @@ import ( "io" "math" "reflect" + "strconv" "strings" "github.com/ethereum/go-ethereum/rlp" @@ -14,7 +15,7 @@ import ( ) // statusOptionKey is a current type used in StatusOptions as a key. -type statusOptionKey string +type statusOptionKey uint64 var ( defaultMinPoW = math.Float64bits(0.001) @@ -84,10 +85,14 @@ func initRLPKeyFields() { if len(keys) != 2 || keys[0] != "key" { panic("invalid value of \"rlp\" tag, expected \"key=N\" where N is uint") } + key, err := strconv.ParseUint(keys[1], 10, 64) + if err != nil { + panic("could not parse \"rlp\" key") + } // typecast key to be of statusOptionKey type - keyFieldIdx[statusOptionKey(keys[1])] = i - idxFieldKey[i] = statusOptionKey(keys[1]) + keyFieldIdx[statusOptionKey(key)] = i + idxFieldKey[i] = statusOptionKey(key) } } @@ -184,12 +189,12 @@ loop: // Read the rest of the list items and dump peer. _, err := s.Raw() if err != nil { - return fmt.Errorf("failed to read the value of key %s: %v", key, err) + return fmt.Errorf("failed to read the value of key %d: %v", key, err) } continue } if err := s.Decode(v.Elem().Field(idx).Addr().Interface()); err != nil { - return fmt.Errorf("failed to decode an option %s: %v", key, err) + return fmt.Errorf("failed to decode an option %d: %v", key, err) } if err := s.ListEnd(); err != nil { return err diff --git a/waku/v1/statusoptions_test.go b/waku/v1/statusoptions_test.go index 7eeb6f29e..a07d3953e 100644 --- a/waku/v1/statusoptions_test.go +++ b/waku/v1/statusoptions_test.go @@ -38,7 +38,7 @@ func TestEncodeDecodeRLP(t *testing.T) { func TestBackwardCompatibility(t *testing.T) { alist := []interface{}{ - []interface{}{"0", math.Float64bits(2.05)}, + []interface{}{uint64(0), math.Float64bits(2.05)}, } data, err := rlp.EncodeToBytes(alist) require.NoError(t, err) @@ -53,8 +53,8 @@ func TestBackwardCompatibility(t *testing.T) { func TestForwardCompatibility(t *testing.T) { pow := math.Float64bits(2.05) alist := []interface{}{ - []interface{}{"0", pow}, - []interface{}{"99", uint(10)}, // some future option + []interface{}{uint64(0), pow}, + []interface{}{uint64(99), uint(10)}, // some future option } data, err := rlp.EncodeToBytes(alist) require.NoError(t, err) @@ -67,20 +67,20 @@ func TestForwardCompatibility(t *testing.T) { func TestInitRLPKeyFields(t *testing.T) { ifk := map[int]statusOptionKey{ - 0: "0", - 1: "1", - 2: "2", - 3: "3", - 4: "4", - 5: "5", + 0: 0, + 1: 1, + 2: 2, + 3: 3, + 4: 4, + 5: 5, } kfi := map[statusOptionKey]int{ - "0": 0, - "1": 1, - "2": 2, - "3": 3, - "4": 4, - "5": 5, + 0: 0, + 1: 1, + 2: 2, + 3: 3, + 4: 4, + 5: 5, } // Test that the kfi length matches the inited global keyFieldIdx length