Implement new waku/0 handshake (#1848)
This commit is contained in:
parent
da4f30ffd2
commit
1b515a5a5e
3
Makefile
3
Makefile
|
@ -261,7 +261,8 @@ test-unit: UNIT_TEST_PACKAGES = $(shell go list ./... | \
|
||||||
grep -v /lib | \
|
grep -v /lib | \
|
||||||
grep -v /transactions/fake )
|
grep -v /transactions/fake )
|
||||||
test-unit: ##@tests Run unit and integration tests
|
test-unit: ##@tests Run unit and integration tests
|
||||||
go test -v -failfast $(UNIT_TEST_PACKAGES) $(gotest_extraflags) && cd ./protocol && $(MAKE) test
|
go test -v -failfast $(UNIT_TEST_PACKAGES) $(gotest_extraflags)
|
||||||
|
cd ./waku && go test -v -failfast ./... $(gotest_extraflags)
|
||||||
|
|
||||||
test-unit-race: gotest_extraflags=-race
|
test-unit-race: gotest_extraflags=-race
|
||||||
test-unit-race: test-unit ##@tests Run unit and integration tests with -race flag
|
test-unit-race: test-unit ##@tests Run unit and integration tests with -race flag
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
GO111MODULE = on
|
|
||||||
|
|
||||||
ENABLE_METRICS ?= true
|
|
||||||
BUILD_FLAGS ?= $(shell echo "-ldflags '\
|
|
||||||
-X github.com/status-im/status-go/protocol/vendor/github.com/ethereum/go-ethereum/metrics.EnabledStr=$(ENABLE_METRICS)'")
|
|
||||||
|
|
||||||
test:
|
|
||||||
go test ./...
|
|
||||||
.PHONY: test
|
|
||||||
|
|
||||||
test-race:
|
|
||||||
go test -race ./...
|
|
||||||
.PHONY: test-race
|
|
||||||
|
|
||||||
lint:
|
|
||||||
golangci-lint run -v
|
|
||||||
.PHONY: lint
|
|
||||||
|
|
||||||
install-linter:
|
|
||||||
# install linter
|
|
||||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.21.0
|
|
||||||
.PHONY: install-linter
|
|
||||||
|
|
||||||
install-dev:
|
|
||||||
# a tool to vendor non-go files
|
|
||||||
go get github.com/goware/modvendor@latest
|
|
||||||
|
|
||||||
go get github.com/golang/mock/gomock@latest
|
|
||||||
go install github.com/golang/mock/mockgen
|
|
||||||
|
|
||||||
go get github.com/kevinburke/go-bindata/go-bindata@v3.13.0
|
|
||||||
go get github.com/golang/protobuf/protoc-gen-go@v1.3.1
|
|
||||||
.PHONY: install-dev
|
|
||||||
|
|
||||||
generate:
|
|
||||||
go generate ./...
|
|
||||||
.PHONY: generate
|
|
||||||
|
|
||||||
new-migration:
|
|
||||||
@if [ -z "$$DIR" ]; then \
|
|
||||||
echo 'missing DIR var'; \
|
|
||||||
exit 1; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
@if [ -z "$$NAME" ]; then \
|
|
||||||
echo 'missing NAME var'; \
|
|
||||||
exit 1; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p $(DIR)
|
|
||||||
touch $(DIR)/`date +"%s"`_$(NAME).down.sql ./$(DIR)/`date +"%s"`_$(NAME).up.sql
|
|
||||||
.PHONY: create-migration
|
|
|
@ -2,7 +2,7 @@ module github.com/status-im/status-go/waku
|
||||||
|
|
||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
replace github.com/ethereum/go-ethereum v1.9.5 => github.com/status-im/go-ethereum v1.9.5-status.6
|
replace github.com/ethereum/go-ethereum v1.9.5 => github.com/status-im/go-ethereum v1.9.5-status.7
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aristanetworks/goarista v0.0.0-20191106175434-873d404c7f40 // indirect
|
github.com/aristanetworks/goarista v0.0.0-20191106175434-873d404c7f40 // indirect
|
||||||
|
|
|
@ -27,6 +27,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/btcsuite/btcd v0.20.0-beta h1:DnZGUjFbRkpytojHWwy6nfUSA7vFrzWXDLpFNzt74ZA=
|
github.com/btcsuite/btcd v0.20.0-beta h1:DnZGUjFbRkpytojHWwy6nfUSA7vFrzWXDLpFNzt74ZA=
|
||||||
github.com/btcsuite/btcd v0.20.0-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
github.com/btcsuite/btcd v0.20.0-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||||
|
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||||
|
@ -191,6 +192,9 @@ github.com/status-im/go-ethereum v1.9.5-status.5 h1:d2RJC6ltNZJM2mrAW6kDWYdzewF8
|
||||||
github.com/status-im/go-ethereum v1.9.5-status.5/go.mod h1:g2+E89NWtyA+55p6XEl5Sdt7Mtez3V0T3+Y7mJNb+tI=
|
github.com/status-im/go-ethereum v1.9.5-status.5/go.mod h1:g2+E89NWtyA+55p6XEl5Sdt7Mtez3V0T3+Y7mJNb+tI=
|
||||||
github.com/status-im/go-ethereum v1.9.5-status.6 h1:ytuTO1yBIAuTVRtRQoc2mrdyngtP+XOQ9IHIibbz7/I=
|
github.com/status-im/go-ethereum v1.9.5-status.6 h1:ytuTO1yBIAuTVRtRQoc2mrdyngtP+XOQ9IHIibbz7/I=
|
||||||
github.com/status-im/go-ethereum v1.9.5-status.6/go.mod h1:08JvQWE+IOnAFSe4UD4ACLNe2fDd9XmWMCq5Yzy9mk0=
|
github.com/status-im/go-ethereum v1.9.5-status.6/go.mod h1:08JvQWE+IOnAFSe4UD4ACLNe2fDd9XmWMCq5Yzy9mk0=
|
||||||
|
github.com/status-im/go-ethereum v1.9.5-status.7 h1:DKH1GiF52LwaZaw6YDBliFEgm/JDsbIT+hn7ph6X94Q=
|
||||||
|
github.com/status-im/go-ethereum v1.9.5-status.7/go.mod h1:YyH5DKB6+z+Vaya7eIm67pnuPZ1oiUMbbsZW41ktN0g=
|
||||||
|
github.com/status-im/status-go/extkeys v1.0.0/go.mod h1:GdqJbrcpkNm5ZsSCpp+PdMxnXx+OcRBdm3PI0rs1FpU=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
@ -226,6 +230,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4=
|
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4=
|
||||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
package waku
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// statusOptions defines additional information shared between peers
|
||||||
|
// during the handshake.
|
||||||
|
// There might be more options provided then fields in statusOptions
|
||||||
|
// and they should be ignored during deserialization to stay forward compatible.
|
||||||
|
// In the case of RLP, options should be serialized to an array of tuples
|
||||||
|
// where the first item is a field name and the second is a RLP-serialized value.
|
||||||
|
type statusOptions struct {
|
||||||
|
PoWRequirement uint64 `rlp:"key=0"` // RLP does not support float64 natively
|
||||||
|
BloomFilter []byte `rlp:"key=1"`
|
||||||
|
LightNodeEnabled bool `rlp:"key=2"`
|
||||||
|
ConfirmationsEnabled bool `rlp:"key=3"`
|
||||||
|
RateLimits RateLimits `rlp:"key=4"`
|
||||||
|
TopicInterest []TopicType `rlp:"key=5"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var idxFieldKey = make(map[int]string)
|
||||||
|
var keyFieldIdx = func() map[string]int {
|
||||||
|
result := make(map[string]int)
|
||||||
|
opts := statusOptions{}
|
||||||
|
v := reflect.ValueOf(opts)
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
// skip unexported fields
|
||||||
|
if !v.Field(i).CanInterface() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rlpTag := v.Type().Field(i).Tag.Get("rlp")
|
||||||
|
// skip fields without rlp field tag
|
||||||
|
if rlpTag == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := strings.Split(rlpTag, "=")[1]
|
||||||
|
result[key] = i
|
||||||
|
idxFieldKey[i] = key
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
func (o statusOptions) PoWRequirementF() float64 {
|
||||||
|
return math.Float64frombits(o.PoWRequirement)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *statusOptions) SetPoWRequirementFromF(val float64) {
|
||||||
|
o.PoWRequirement = math.Float64bits(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o statusOptions) EncodeRLP(w io.Writer) error {
|
||||||
|
v := reflect.ValueOf(o)
|
||||||
|
optionsList := make([]interface{}, 0, v.NumField())
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
value := v.Field(i).Interface()
|
||||||
|
key, ok := idxFieldKey[i]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
optionsList = append(optionsList, []interface{}{key, value})
|
||||||
|
}
|
||||||
|
return rlp.Encode(w, optionsList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *statusOptions) DecodeRLP(s *rlp.Stream) error {
|
||||||
|
_, err := s.List()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("expected an outer list: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
v := reflect.ValueOf(o)
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
_, err := s.List()
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
// continue to decode a key
|
||||||
|
case rlp.EOL:
|
||||||
|
break loop
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("expected an inner list: %w", err)
|
||||||
|
}
|
||||||
|
var key string
|
||||||
|
if err := s.Decode(&key); err != nil {
|
||||||
|
return fmt.Errorf("invalid key: %w", err)
|
||||||
|
}
|
||||||
|
// Skip processing if a key does not exist.
|
||||||
|
// It might happen when there is a new peer
|
||||||
|
// which supports a new option with
|
||||||
|
// a higher index.
|
||||||
|
idx, ok := keyFieldIdx[key]
|
||||||
|
if !ok {
|
||||||
|
// Read the rest of the list items and dump them.
|
||||||
|
_, err := s.Raw()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read the value of key %s: %w", key, err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := s.Decode(v.Elem().Field(idx).Addr().Interface()); err != nil {
|
||||||
|
return fmt.Errorf("failed to decode an option %s: %w", key, err)
|
||||||
|
}
|
||||||
|
if err := s.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.ListEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o statusOptions) Validate() error {
|
||||||
|
if len(o.TopicInterest) > 1000 {
|
||||||
|
return errors.New("topic interest is limited by 1000 items")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package waku
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEncodeDecodeRLP(t *testing.T) {
|
||||||
|
opts := statusOptions{
|
||||||
|
PoWRequirement: math.Float64bits(6.02),
|
||||||
|
BloomFilter: TopicToBloom(TopicType{0xaa, 0xbb, 0xcc, 0xdd}),
|
||||||
|
LightNodeEnabled: true,
|
||||||
|
ConfirmationsEnabled: true,
|
||||||
|
RateLimits: RateLimits{
|
||||||
|
IPLimits: 10,
|
||||||
|
PeerIDLimits: 5,
|
||||||
|
TopicLimits: 1,
|
||||||
|
},
|
||||||
|
TopicInterest: []TopicType{{0x01}, {0x02}, {0x03}, {0x04}},
|
||||||
|
}
|
||||||
|
data, err := rlp.EncodeToBytes(opts)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var optsDecoded statusOptions
|
||||||
|
err = rlp.DecodeBytes(data, &optsDecoded)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, opts, optsDecoded)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackwardCompatibility(t *testing.T) {
|
||||||
|
alist := []interface{}{
|
||||||
|
[]interface{}{"0", math.Float64bits(2.05)},
|
||||||
|
}
|
||||||
|
data, err := rlp.EncodeToBytes(alist)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var optsDecoded statusOptions
|
||||||
|
err = rlp.DecodeBytes(data, &optsDecoded)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, statusOptions{PoWRequirement: math.Float64bits(2.05)}, optsDecoded)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestForwardCompatibility(t *testing.T) {
|
||||||
|
alist := []interface{}{
|
||||||
|
[]interface{}{"0", math.Float64bits(2.05)},
|
||||||
|
[]interface{}{"99", uint(10)}, // some future option
|
||||||
|
}
|
||||||
|
data, err := rlp.EncodeToBytes(alist)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var optsDecoded statusOptions
|
||||||
|
err = rlp.DecodeBytes(data, &optsDecoded)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, statusOptions{PoWRequirement: math.Float64bits(2.05)}, optsDecoded)
|
||||||
|
}
|
97
waku/peer.go
97
waku/peer.go
|
@ -96,13 +96,15 @@ func (p *Peer) handshake() error {
|
||||||
isLightNode := p.host.LightClientMode()
|
isLightNode := p.host.LightClientMode()
|
||||||
isRestrictedLightNodeConnection := p.host.LightClientModeConnectionRestricted()
|
isRestrictedLightNodeConnection := p.host.LightClientModeConnectionRestricted()
|
||||||
go func() {
|
go func() {
|
||||||
pow := p.host.MinPow()
|
opts := statusOptions{
|
||||||
powConverted := math.Float64bits(pow)
|
BloomFilter: p.host.BloomFilter(),
|
||||||
bloom := p.host.BloomFilter()
|
LightNodeEnabled: isLightNode,
|
||||||
confirmationsEnabled := p.host.ConfirmationsEnabled()
|
ConfirmationsEnabled: p.host.ConfirmationsEnabled(),
|
||||||
rateLimits := p.host.RateLimits()
|
RateLimits: p.host.RateLimits(),
|
||||||
|
TopicInterest: nil,
|
||||||
errc <- p2p.SendItems(p.ws, statusCode, ProtocolVersion, powConverted, bloom, isLightNode, confirmationsEnabled, rateLimits)
|
}
|
||||||
|
opts.SetPoWRequirementFromF(p.host.MinPow())
|
||||||
|
errc <- p2p.SendItems(p.ws, statusCode, ProtocolVersion, opts)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Fetch the remote status packet and verify protocol match
|
// Fetch the remote status packet and verify protocol match
|
||||||
|
@ -113,56 +115,51 @@ func (p *Peer) handshake() error {
|
||||||
if packet.Code != statusCode {
|
if packet.Code != statusCode {
|
||||||
return fmt.Errorf("p [%x] sent packet %x before status packet", p.ID(), packet.Code)
|
return fmt.Errorf("p [%x] sent packet %x before status packet", p.ID(), packet.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
peerProtocolVersion uint64
|
||||||
|
peerOptions statusOptions
|
||||||
|
)
|
||||||
s := rlp.NewStream(packet.Payload, uint64(packet.Size))
|
s := rlp.NewStream(packet.Payload, uint64(packet.Size))
|
||||||
_, err = s.List()
|
if _, err := s.List(); err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("p [%x]: failed to decode status packet: %w", p.ID(), err)
|
||||||
return fmt.Errorf("p [%x] sent bad status message: %v", p.ID(), err)
|
|
||||||
}
|
}
|
||||||
peerVersion, err := s.Uint()
|
// Validate protocol version.
|
||||||
if err != nil {
|
if err := s.Decode(&peerProtocolVersion); err != nil {
|
||||||
return fmt.Errorf("p [%x] sent bad status message (unable to decode version): %v", p.ID(), err)
|
return fmt.Errorf("p [%x]: failed to decode peer protocol version: %w", p.ID(), err)
|
||||||
}
|
}
|
||||||
if peerVersion != ProtocolVersion {
|
if peerProtocolVersion != ProtocolVersion {
|
||||||
return fmt.Errorf("p [%x]: protocol version mismatch %d != %d", p.ID(), peerVersion, ProtocolVersion)
|
return fmt.Errorf("p [%x]: protocol version mismatch %d != %d", p.ID(), peerProtocolVersion, ProtocolVersion)
|
||||||
}
|
}
|
||||||
|
// Decode and validate other status packet options.
|
||||||
// only version is mandatory, subsequent parameters are optional
|
if err := s.Decode(&peerOptions); err != nil {
|
||||||
powRaw, err := s.Uint()
|
return fmt.Errorf("p [%x]: failed to decode status options: %w", p.ID(), err)
|
||||||
if err == nil {
|
|
||||||
pow := math.Float64frombits(powRaw)
|
|
||||||
if math.IsInf(pow, 0) || math.IsNaN(pow) || pow < 0.0 {
|
|
||||||
return fmt.Errorf("p [%x] sent bad status message: invalid pow", p.ID())
|
|
||||||
}
|
|
||||||
p.powRequirement = pow
|
|
||||||
|
|
||||||
var bloom []byte
|
|
||||||
err = s.Decode(&bloom)
|
|
||||||
if err == nil {
|
|
||||||
sz := len(bloom)
|
|
||||||
if sz != BloomFilterSize && sz != 0 {
|
|
||||||
return fmt.Errorf("p [%x] sent bad status message: wrong bloom filter size %d", p.ID(), sz)
|
|
||||||
}
|
|
||||||
p.setBloomFilter(bloom)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if err := s.ListEnd(); err != nil {
|
||||||
isRemotePeerLightNode, _ := s.Bool()
|
return fmt.Errorf("p [%x]: failed to decode status packet: %w", p.ID(), err)
|
||||||
if isRemotePeerLightNode && isLightNode && isRestrictedLightNodeConnection {
|
}
|
||||||
|
if err := peerOptions.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("p [%x]: sent invalid options: %w", p.ID(), err)
|
||||||
|
}
|
||||||
|
// Validate and save peer's PoW.
|
||||||
|
pow := peerOptions.PoWRequirementF()
|
||||||
|
if math.IsInf(pow, 0) || math.IsNaN(pow) || pow < 0.0 {
|
||||||
|
return fmt.Errorf("p [%x]: sent bad status message: invalid pow", p.ID())
|
||||||
|
}
|
||||||
|
p.powRequirement = pow
|
||||||
|
// Validate and save peer's bloom filters.
|
||||||
|
bloom := peerOptions.BloomFilter
|
||||||
|
bloomSize := len(bloom)
|
||||||
|
if bloomSize != 0 && bloomSize != BloomFilterSize {
|
||||||
|
return fmt.Errorf("p [%x] sent bad status message: wrong bloom filter size %d", p.ID(), bloomSize)
|
||||||
|
}
|
||||||
|
p.setBloomFilter(bloom)
|
||||||
|
// Validate and save other peer's options.
|
||||||
|
if peerOptions.LightNodeEnabled && isLightNode && isRestrictedLightNodeConnection {
|
||||||
return fmt.Errorf("p [%x] is useless: two light client communication restricted", p.ID())
|
return fmt.Errorf("p [%x] is useless: two light client communication restricted", p.ID())
|
||||||
}
|
}
|
||||||
confirmationsEnabled, err := s.Bool()
|
p.confirmationsEnabled = peerOptions.ConfirmationsEnabled
|
||||||
if err != nil || !confirmationsEnabled {
|
p.setRateLimits(peerOptions.RateLimits)
|
||||||
p.logger.Info("confirmations are disabled for peer", zap.Binary("peer", p.ID()))
|
|
||||||
} else {
|
|
||||||
p.confirmationsEnabled = confirmationsEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
var rateLimits RateLimits
|
|
||||||
if err := s.Decode(&rateLimits); err != nil {
|
|
||||||
p.logger.Info("rate limiting is disabled for peer", zap.Binary("peer", p.ID()))
|
|
||||||
} else {
|
|
||||||
p.setRateLimits(rateLimits)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := <-errc; err != nil {
|
if err := <-errc; err != nil {
|
||||||
return fmt.Errorf("p [%x] failed to send status packet: %v", p.ID(), err)
|
return fmt.Errorf("p [%x] failed to send status packet: %v", p.ID(), err)
|
||||||
|
|
|
@ -514,7 +514,10 @@ func TestPeerHandshakeWithTwoFullNode(t *testing.T) {
|
||||||
p1 := newPeer(
|
p1 := newPeer(
|
||||||
&w1,
|
&w1,
|
||||||
p2p.NewPeer(enode.ID{}, "test", []p2p.Cap{}),
|
p2p.NewPeer(enode.ID{}, "test", []p2p.Cap{}),
|
||||||
&rwStub{[]interface{}{ProtocolVersion, uint64(123), make([]byte, BloomFilterSize), false}},
|
&rwStub{[]interface{}{
|
||||||
|
ProtocolVersion,
|
||||||
|
statusOptions{PoWRequirement: 123},
|
||||||
|
}},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
err := p1.handshake()
|
err := p1.handshake()
|
||||||
|
@ -529,7 +532,10 @@ func TestHandshakeWithOldVersionWithoutLightModeFlag(t *testing.T) {
|
||||||
p1 := newPeer(
|
p1 := newPeer(
|
||||||
&w1,
|
&w1,
|
||||||
p2p.NewPeer(enode.ID{}, "test", []p2p.Cap{}),
|
p2p.NewPeer(enode.ID{}, "test", []p2p.Cap{}),
|
||||||
&rwStub{[]interface{}{ProtocolVersion, uint64(123), make([]byte, BloomFilterSize)}},
|
&rwStub{[]interface{}{
|
||||||
|
ProtocolVersion,
|
||||||
|
statusOptions{PoWRequirement: 123},
|
||||||
|
}},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
err := p1.handshake()
|
err := p1.handshake()
|
||||||
|
@ -546,7 +552,10 @@ func TestTwoLightPeerHandshakeRestrictionOff(t *testing.T) {
|
||||||
p1 := newPeer(
|
p1 := newPeer(
|
||||||
&w1,
|
&w1,
|
||||||
p2p.NewPeer(enode.ID{}, "test", []p2p.Cap{}),
|
p2p.NewPeer(enode.ID{}, "test", []p2p.Cap{}),
|
||||||
&rwStub{[]interface{}{ProtocolVersion, uint64(123), make([]byte, BloomFilterSize), true}},
|
&rwStub{[]interface{}{
|
||||||
|
ProtocolVersion,
|
||||||
|
statusOptions{PoWRequirement: 123, LightNodeEnabled: true},
|
||||||
|
}},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
err := p1.handshake()
|
err := p1.handshake()
|
||||||
|
|
|
@ -28,6 +28,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/crypto/pbkdf2"
|
"golang.org/x/crypto/pbkdf2"
|
||||||
|
@ -913,6 +915,7 @@ func TestSendP2PDirect(t *testing.T) {
|
||||||
|
|
||||||
rwStub := &rwP2PMessagesStub{}
|
rwStub := &rwP2PMessagesStub{}
|
||||||
peerW := newPeer(w, p2p.NewPeer(enode.ID{}, "test", []p2p.Cap{}), rwStub, nil)
|
peerW := newPeer(w, p2p.NewPeer(enode.ID{}, "test", []p2p.Cap{}), rwStub, nil)
|
||||||
|
w.peers[peerW] = struct{}{}
|
||||||
|
|
||||||
params, err := generateMessageParams()
|
params, err := generateMessageParams()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -929,7 +932,7 @@ func TestSendP2PDirect(t *testing.T) {
|
||||||
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = w.SendP2PDirect(peerW, env, env, env)
|
err = w.SendP2PDirect(peerW.ID(), env, env, env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to send envelope with seed %d: %s.", seed, err)
|
t.Fatalf("failed to send envelope with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
@ -1053,7 +1056,14 @@ func testConfirmationsHandshake(t *testing.T, expectConfirmations bool) {
|
||||||
p2p.ExpectMsg(
|
p2p.ExpectMsg(
|
||||||
rw1,
|
rw1,
|
||||||
statusCode,
|
statusCode,
|
||||||
[]interface{}{ProtocolVersion, math.Float64bits(w.MinPow()), w.BloomFilter(), false, expectConfirmations, RateLimits{}},
|
[]interface{}{
|
||||||
|
ProtocolVersion,
|
||||||
|
statusOptions{
|
||||||
|
PoWRequirement: math.Float64bits(w.MinPow()),
|
||||||
|
LightNodeEnabled: false,
|
||||||
|
ConfirmationsEnabled: expectConfirmations,
|
||||||
|
},
|
||||||
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1067,12 +1077,14 @@ func TestHandshakeWithConfirmationsDisabled(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfirmationReceived(t *testing.T) {
|
func TestConfirmationReceived(t *testing.T) {
|
||||||
|
logger, err := zap.NewDevelopment()
|
||||||
|
require.NoError(t, err)
|
||||||
conf := &Config{
|
conf := &Config{
|
||||||
MinimumAcceptedPoW: 0,
|
MinimumAcceptedPoW: 0,
|
||||||
MaxMessageSize: 10 << 20,
|
MaxMessageSize: 10 << 20,
|
||||||
EnableConfirmations: true,
|
EnableConfirmations: true,
|
||||||
}
|
}
|
||||||
w := New(conf, nil)
|
w := New(conf, logger)
|
||||||
p := p2p.NewPeer(enode.ID{1}, "1", []p2p.Cap{{"waku", 0}})
|
p := p2p.NewPeer(enode.ID{1}, "1", []p2p.Cap{{"waku", 0}})
|
||||||
rw1, rw2 := p2p.MsgPipe()
|
rw1, rw2 := p2p.MsgPipe()
|
||||||
errorc := make(chan error, 1)
|
errorc := make(chan error, 1)
|
||||||
|
@ -1080,15 +1092,27 @@ func TestConfirmationReceived(t *testing.T) {
|
||||||
err := w.HandlePeer(p, rw2)
|
err := w.HandlePeer(p, rw2)
|
||||||
errorc <- err
|
errorc <- err
|
||||||
}()
|
}()
|
||||||
time.AfterFunc(5*time.Second, func() {
|
go func() {
|
||||||
rw1.Close()
|
select {
|
||||||
})
|
case err := <-errorc:
|
||||||
|
t.Log(err)
|
||||||
|
case <-time.After(time.Second * 5):
|
||||||
|
rw1.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
require.NoError(
|
require.NoError(
|
||||||
t,
|
t,
|
||||||
p2p.ExpectMsg(
|
p2p.ExpectMsg(
|
||||||
rw1,
|
rw1,
|
||||||
statusCode,
|
statusCode,
|
||||||
[]interface{}{ProtocolVersion, math.Float64bits(w.MinPow()), w.BloomFilter(), false, true, RateLimits{}},
|
[]interface{}{
|
||||||
|
ProtocolVersion,
|
||||||
|
statusOptions{
|
||||||
|
PoWRequirement: math.Float64bits(w.MinPow()),
|
||||||
|
BloomFilter: w.BloomFilter(),
|
||||||
|
ConfirmationsEnabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
require.NoError(
|
require.NoError(
|
||||||
|
@ -1097,10 +1121,12 @@ func TestConfirmationReceived(t *testing.T) {
|
||||||
rw1,
|
rw1,
|
||||||
statusCode,
|
statusCode,
|
||||||
ProtocolVersion,
|
ProtocolVersion,
|
||||||
math.Float64bits(w.MinPow()),
|
statusOptions{
|
||||||
w.BloomFilter(),
|
PoWRequirement: math.Float64bits(w.MinPow()),
|
||||||
true,
|
BloomFilter: w.BloomFilter(),
|
||||||
true,
|
ConfirmationsEnabled: true,
|
||||||
|
LightNodeEnabled: true,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1142,7 +1168,14 @@ func TestMessagesResponseWithError(t *testing.T) {
|
||||||
p2p.ExpectMsg(
|
p2p.ExpectMsg(
|
||||||
rw1,
|
rw1,
|
||||||
statusCode,
|
statusCode,
|
||||||
[]interface{}{ProtocolVersion, math.Float64bits(w.MinPow()), w.BloomFilter(), false, true, RateLimits{}},
|
[]interface{}{
|
||||||
|
ProtocolVersion,
|
||||||
|
statusOptions{
|
||||||
|
PoWRequirement: math.Float64bits(w.MinPow()),
|
||||||
|
BloomFilter: w.BloomFilter(),
|
||||||
|
ConfirmationsEnabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
require.NoError(
|
require.NoError(
|
||||||
|
@ -1151,10 +1184,12 @@ func TestMessagesResponseWithError(t *testing.T) {
|
||||||
rw1,
|
rw1,
|
||||||
statusCode,
|
statusCode,
|
||||||
ProtocolVersion,
|
ProtocolVersion,
|
||||||
math.Float64bits(w.MinPow()), w.BloomFilter(),
|
statusOptions{
|
||||||
true,
|
PoWRequirement: math.Float64bits(w.MinPow()),
|
||||||
true,
|
BloomFilter: w.BloomFilter(),
|
||||||
RateLimits{},
|
ConfirmationsEnabled: true,
|
||||||
|
LightNodeEnabled: true,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1204,9 +1239,29 @@ func testConfirmationEvents(t *testing.T, envelope Envelope, envelopeErrors []En
|
||||||
time.AfterFunc(5*time.Second, func() {
|
time.AfterFunc(5*time.Second, func() {
|
||||||
rw1.Close()
|
rw1.Close()
|
||||||
})
|
})
|
||||||
require.NoError(t, p2p.ExpectMsg(rw1, statusCode, []interface{}{ProtocolVersion, math.Float64bits(w.MinPow()), w.BloomFilter(), false, true, RateLimits{}}))
|
require.NoError(t, p2p.ExpectMsg(
|
||||||
require.NoError(t, p2p.SendItems(rw1, statusCode, ProtocolVersion, math.Float64bits(w.MinPow()), w.BloomFilter(), true, true))
|
rw1,
|
||||||
|
statusCode,
|
||||||
|
[]interface{}{
|
||||||
|
ProtocolVersion,
|
||||||
|
statusOptions{
|
||||||
|
PoWRequirement: math.Float64bits(w.MinPow()),
|
||||||
|
BloomFilter: w.BloomFilter(),
|
||||||
|
ConfirmationsEnabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
|
require.NoError(t, p2p.SendItems(
|
||||||
|
rw1,
|
||||||
|
statusCode,
|
||||||
|
ProtocolVersion,
|
||||||
|
statusOptions{
|
||||||
|
PoWRequirement: math.Float64bits(w.MinPow()),
|
||||||
|
BloomFilter: w.BloomFilter(),
|
||||||
|
ConfirmationsEnabled: true,
|
||||||
|
LightNodeEnabled: true,
|
||||||
|
},
|
||||||
|
))
|
||||||
require.NoError(t, w.Send(&envelope))
|
require.NoError(t, w.Send(&envelope))
|
||||||
require.NoError(t, p2p.ExpectMsg(rw1, messagesCode, []*Envelope{&envelope}))
|
require.NoError(t, p2p.ExpectMsg(rw1, messagesCode, []*Envelope{&envelope}))
|
||||||
|
|
||||||
|
@ -1286,7 +1341,13 @@ func TestEventsWithoutConfirmation(t *testing.T) {
|
||||||
p2p.ExpectMsg(
|
p2p.ExpectMsg(
|
||||||
rw1,
|
rw1,
|
||||||
statusCode,
|
statusCode,
|
||||||
[]interface{}{ProtocolVersion, math.Float64bits(w.MinPow()), w.BloomFilter(), false, false, RateLimits{}},
|
[]interface{}{
|
||||||
|
ProtocolVersion,
|
||||||
|
statusOptions{
|
||||||
|
PoWRequirement: math.Float64bits(w.MinPow()),
|
||||||
|
BloomFilter: w.BloomFilter(),
|
||||||
|
},
|
||||||
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
require.NoError(
|
require.NoError(
|
||||||
|
@ -1295,11 +1356,11 @@ func TestEventsWithoutConfirmation(t *testing.T) {
|
||||||
rw1,
|
rw1,
|
||||||
statusCode,
|
statusCode,
|
||||||
ProtocolVersion,
|
ProtocolVersion,
|
||||||
math.Float64bits(w.MinPow()),
|
statusOptions{
|
||||||
w.BloomFilter(),
|
PoWRequirement: math.Float64bits(w.MinPow()),
|
||||||
true,
|
BloomFilter: w.BloomFilter(),
|
||||||
false,
|
LightNodeEnabled: true,
|
||||||
RateLimits{},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1464,10 +1525,17 @@ func TestRateLimiterIntegration(t *testing.T) {
|
||||||
p2p.ExpectMsg(
|
p2p.ExpectMsg(
|
||||||
rw1,
|
rw1,
|
||||||
statusCode,
|
statusCode,
|
||||||
[]interface{}{ProtocolVersion, math.Float64bits(w.MinPow()), w.BloomFilter(), false, false, RateLimits{
|
[]interface{}{
|
||||||
IPLimits: 10,
|
ProtocolVersion,
|
||||||
PeerIDLimits: 5,
|
statusOptions{
|
||||||
}},
|
PoWRequirement: math.Float64bits(w.MinPow()),
|
||||||
|
BloomFilter: w.BloomFilter(),
|
||||||
|
RateLimits: RateLimits{
|
||||||
|
IPLimits: 10,
|
||||||
|
PeerIDLimits: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
select {
|
select {
|
||||||
|
|
|
@ -911,6 +911,7 @@ func TestSendP2PDirect(t *testing.T) {
|
||||||
|
|
||||||
rwStub := &rwP2PMessagesStub{}
|
rwStub := &rwP2PMessagesStub{}
|
||||||
peerW := newPeer(w, p2p.NewPeer(enode.ID{}, "test", []p2p.Cap{}), rwStub)
|
peerW := newPeer(w, p2p.NewPeer(enode.ID{}, "test", []p2p.Cap{}), rwStub)
|
||||||
|
w.peers[peerW] = struct{}{}
|
||||||
|
|
||||||
params, err := generateMessageParams()
|
params, err := generateMessageParams()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -928,7 +929,7 @@ func TestSendP2PDirect(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify sending a single envelope
|
// verify sending a single envelope
|
||||||
err = w.SendP2PDirect(peerW, env)
|
err = w.SendP2PDirect(peerW.ID(), env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to send envelope with seed %d: %s.", seed, err)
|
t.Fatalf("failed to send envelope with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue