Refactor/waku.doc (#1950)
* Refactor of waku.doc.go Moved out all code from docs.go, placed much of the seemingly misc code into much more closely related files, created a dedicated const file for housing all the package consts. * Moved doc_test to message_test * Refactored type collision and check on nil err * message_test.go fmt * Implemented whisper/doc.go refactor * import order change to attempt to please the linter. * Bug fix on whisper_test.go I also spotted a load of redundant type casting and fixed some of them
This commit is contained in:
parent
7bbe9561de
commit
cc8d57752f
|
@ -0,0 +1,77 @@
|
|||
// Copyright 2019 The Waku Library Authors.
|
||||
//
|
||||
// The Waku library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The Waku library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty off
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the Waku library. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// This software uses the go-ethereum library, which is licensed
|
||||
// under the GNU Lesser General Public Library, version 3 or any later.
|
||||
|
||||
package waku
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
// Waku protocol parameters
|
||||
const (
|
||||
ProtocolVersion = uint64(0) // Protocol version number
|
||||
ProtocolVersionStr = "0" // The same, as a string
|
||||
ProtocolName = "waku" // Nickname of the protocol
|
||||
|
||||
// Waku protocol message codes, according to https://github.com/vacp2p/specs/blob/master/waku.md
|
||||
statusCode = 0 // used in the handshake
|
||||
messagesCode = 1 // regular message
|
||||
statusUpdateCode = 22 // update of settings
|
||||
batchAcknowledgedCode = 11 // confirmation that batch of envelopes was received
|
||||
messageResponseCode = 12 // includes confirmation for delivery and information about errors
|
||||
p2pRequestCompleteCode = 125 // peer-to-peer message, used by Dapp protocol
|
||||
p2pRequestCode = 126 // peer-to-peer message, used by Dapp protocol
|
||||
p2pMessageCode = 127 // peer-to-peer message (to be consumed by the peer, but not forwarded any further)
|
||||
NumberOfMessageCodes = 128
|
||||
|
||||
SizeMask = byte(3) // mask used to extract the size of payload size field from the flags
|
||||
signatureFlag = byte(4)
|
||||
|
||||
TopicLength = 4 // in bytes
|
||||
signatureLength = crypto.SignatureLength // in bytes
|
||||
aesKeyLength = 32 // in bytes
|
||||
aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize()
|
||||
keyIDSize = 32 // in bytes
|
||||
BloomFilterSize = 64 // in bytes
|
||||
MaxTopicInterest = 10000
|
||||
flagsLength = 1
|
||||
|
||||
EnvelopeHeaderLength = 20
|
||||
|
||||
MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message.
|
||||
DefaultMaxMessageSize = uint32(1024 * 1024)
|
||||
DefaultMinimumPoW = 0.2
|
||||
|
||||
padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol
|
||||
messageQueueLimit = 1024
|
||||
|
||||
expirationCycle = time.Second
|
||||
transmissionCycle = 300 * time.Millisecond
|
||||
|
||||
DefaultTTL = 50 // seconds
|
||||
DefaultSyncAllowance = 10 // seconds
|
||||
|
||||
MaxLimitInSyncMailRequest = 1000
|
||||
|
||||
EnvelopeTimeNotSynced uint = iota + 1
|
||||
EnvelopeOtherError
|
||||
|
||||
MaxLimitInMessagesRequest = 1000
|
||||
)
|
216
waku/doc.go
216
waku/doc.go
|
@ -1,216 +0,0 @@
|
|||
// Copyright 2019 The Waku Library Authors.
|
||||
//
|
||||
// The Waku library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The Waku library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty off
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the Waku library. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// This software uses the go-ethereum library, which is licensed
|
||||
// under the GNU Lesser General Public Library, version 3 or any later.
|
||||
|
||||
package waku
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// Waku protocol parameters
|
||||
const (
|
||||
ProtocolVersion = uint64(0) // Protocol version number
|
||||
ProtocolVersionStr = "0" // The same, as a string
|
||||
ProtocolName = "waku" // Nickname of the protocol
|
||||
|
||||
// Waku protocol message codes, according to https://github.com/vacp2p/specs/blob/master/waku.md
|
||||
statusCode = 0 // used in the handshake
|
||||
messagesCode = 1 // regular message
|
||||
statusUpdateCode = 22 // update of settings
|
||||
batchAcknowledgedCode = 11 // confirmation that batch of envelopes was received
|
||||
messageResponseCode = 12 // includes confirmation for delivery and information about errors
|
||||
p2pRequestCompleteCode = 125 // peer-to-peer message, used by Dapp protocol
|
||||
p2pRequestCode = 126 // peer-to-peer message, used by Dapp protocol
|
||||
p2pMessageCode = 127 // peer-to-peer message (to be consumed by the peer, but not forwarded any further)
|
||||
NumberOfMessageCodes = 128
|
||||
|
||||
SizeMask = byte(3) // mask used to extract the size of payload size field from the flags
|
||||
signatureFlag = byte(4)
|
||||
|
||||
TopicLength = 4 // in bytes
|
||||
signatureLength = crypto.SignatureLength // in bytes
|
||||
aesKeyLength = 32 // in bytes
|
||||
aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize()
|
||||
keyIDSize = 32 // in bytes
|
||||
BloomFilterSize = 64 // in bytes
|
||||
MaxTopicInterest = 10000
|
||||
flagsLength = 1
|
||||
|
||||
EnvelopeHeaderLength = 20
|
||||
|
||||
MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message.
|
||||
DefaultMaxMessageSize = uint32(1024 * 1024)
|
||||
DefaultMinimumPoW = 0.2
|
||||
|
||||
padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol
|
||||
messageQueueLimit = 1024
|
||||
|
||||
expirationCycle = time.Second
|
||||
transmissionCycle = 300 * time.Millisecond
|
||||
|
||||
DefaultTTL = 50 // seconds
|
||||
DefaultSyncAllowance = 10 // seconds
|
||||
|
||||
MaxLimitInSyncMailRequest = 1000
|
||||
|
||||
EnvelopeTimeNotSynced uint = iota + 1
|
||||
EnvelopeOtherError
|
||||
|
||||
MaxLimitInMessagesRequest = 1000
|
||||
)
|
||||
|
||||
// MailServer represents a mail server, capable of
|
||||
// archiving the old messages for subsequent delivery
|
||||
// to the peers. Any implementation must ensure that both
|
||||
// functions are thread-safe. Also, they must return ASAP.
|
||||
// DeliverMail should use p2pMessageCode for delivery,
|
||||
// in order to bypass the expiry checks.
|
||||
type MailServer interface {
|
||||
Archive(env *Envelope)
|
||||
DeliverMail(peerID []byte, request *Envelope) // DEPRECATED; use Deliver()
|
||||
Deliver(peerID []byte, request MessagesRequest)
|
||||
}
|
||||
|
||||
// MessagesRequest contains details of a request of historic messages.
|
||||
type MessagesRequest struct {
|
||||
// ID of the request. The current implementation requires ID to be 32-byte array,
|
||||
// however, it's not enforced for future implementation.
|
||||
ID []byte `json:"id"`
|
||||
|
||||
// From is a lower bound of time range.
|
||||
From uint32 `json:"from"`
|
||||
|
||||
// To is a upper bound of time range.
|
||||
To uint32 `json:"to"`
|
||||
|
||||
// Limit determines the number of messages sent by the mail server
|
||||
// for the current paginated request.
|
||||
Limit uint32 `json:"limit"`
|
||||
|
||||
// Cursor is used as starting point for paginated requests.
|
||||
Cursor []byte `json:"cursor"`
|
||||
|
||||
// Bloom is a filter to match requested messages.
|
||||
Bloom []byte `json:"bloom"`
|
||||
|
||||
// Topics is a list of topics. A returned message should
|
||||
// belong to one of the topics from the list.
|
||||
Topics [][]byte `json:"topics"`
|
||||
}
|
||||
|
||||
func (r MessagesRequest) Validate() error {
|
||||
if len(r.ID) != common.HashLength {
|
||||
return errors.New("invalid 'ID', expected a 32-byte slice")
|
||||
}
|
||||
|
||||
if r.From > r.To {
|
||||
return errors.New("invalid 'From' value which is greater than To")
|
||||
}
|
||||
|
||||
if r.Limit > MaxLimitInMessagesRequest {
|
||||
return fmt.Errorf("invalid 'Limit' value, expected value lower than %d", MaxLimitInMessagesRequest)
|
||||
}
|
||||
|
||||
if len(r.Bloom) == 0 && len(r.Topics) == 0 {
|
||||
return errors.New("invalid 'Bloom' or 'Topics', one must be non-empty")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MessagesResponse sent as a response after processing batch of envelopes.
|
||||
type MessagesResponse struct {
|
||||
// Hash is a hash of all envelopes sent in the single batch.
|
||||
Hash common.Hash
|
||||
// Per envelope error.
|
||||
Errors []EnvelopeError
|
||||
}
|
||||
|
||||
// EnvelopeError code and optional description of the error.
|
||||
type EnvelopeError struct {
|
||||
Hash common.Hash
|
||||
Code uint
|
||||
Description string
|
||||
}
|
||||
|
||||
// MultiVersionResponse allows to decode response into chosen version.
|
||||
type MultiVersionResponse struct {
|
||||
Version uint
|
||||
Response rlp.RawValue
|
||||
}
|
||||
|
||||
// DecodeResponse1 decodes response into first version of the messages response.
|
||||
func (m MultiVersionResponse) DecodeResponse1() (resp MessagesResponse, err error) {
|
||||
return resp, rlp.DecodeBytes(m.Response, &resp)
|
||||
}
|
||||
|
||||
// Version1MessageResponse first version of the message response.
|
||||
type Version1MessageResponse struct {
|
||||
Version uint
|
||||
Response MessagesResponse
|
||||
}
|
||||
|
||||
// NewMessagesResponse returns instance of the version messages response.
|
||||
func NewMessagesResponse(batch common.Hash, errors []EnvelopeError) Version1MessageResponse {
|
||||
return Version1MessageResponse{
|
||||
Version: 1,
|
||||
Response: MessagesResponse{
|
||||
Hash: batch,
|
||||
Errors: errors,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorToEnvelopeError converts common golang error into EnvelopeError with a code.
|
||||
func ErrorToEnvelopeError(hash common.Hash, err error) EnvelopeError {
|
||||
code := EnvelopeOtherError
|
||||
switch err.(type) {
|
||||
case TimeSyncError:
|
||||
code = EnvelopeTimeNotSynced
|
||||
}
|
||||
return EnvelopeError{
|
||||
Hash: hash,
|
||||
Code: code,
|
||||
Description: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
// MailServerResponse is the response payload sent by the mailserver.
|
||||
type MailServerResponse struct {
|
||||
LastEnvelopeHash common.Hash
|
||||
Cursor []byte
|
||||
Error error
|
||||
}
|
||||
|
||||
// RateLimits contains information about rate limit settings.
|
||||
// It is exchanged using rateLimitingCode packet or in the handshake.
|
||||
type RateLimits struct {
|
||||
IPLimits uint64 // messages per second from a single IP (default 0, no limits)
|
||||
PeerIDLimits uint64 // messages per second from a single peer ID (default 0, no limits)
|
||||
TopicLimits uint64 // messages per second from a single topic (default 0, no limits)
|
||||
}
|
||||
|
||||
func (r RateLimits) IsZero() bool {
|
||||
return r == (RateLimits{})
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright 2019 The Waku Library Authors.
|
||||
//
|
||||
// The Waku library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The Waku library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty off
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the Waku library. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// This software uses the go-ethereum library, which is licensed
|
||||
// under the GNU Lesser General Public Library, version 3 or any later.
|
||||
|
||||
package waku
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
func TestEncodeDecodeVersionedResponse(t *testing.T) {
|
||||
response := NewMessagesResponse(common.Hash{1}, []EnvelopeError{{Code: 1}})
|
||||
bytes, err := rlp.EncodeToBytes(response)
|
||||
require.NoError(t, err)
|
||||
|
||||
var mresponse MultiVersionResponse
|
||||
require.NoError(t, rlp.DecodeBytes(bytes, &mresponse))
|
||||
v1resp, err := mresponse.DecodeResponse1()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, response.Response.Hash, v1resp.Hash)
|
||||
}
|
|
@ -270,3 +270,24 @@ func TopicToBloom(topic TopicType) []byte {
|
|||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// EnvelopeError code and optional description of the error.
|
||||
type EnvelopeError struct {
|
||||
Hash common.Hash
|
||||
Code uint
|
||||
Description string
|
||||
}
|
||||
|
||||
// ErrorToEnvelopeError converts common golang error into EnvelopeError with a code.
|
||||
func ErrorToEnvelopeError(hash common.Hash, err error) EnvelopeError {
|
||||
code := EnvelopeOtherError
|
||||
switch err.(type) {
|
||||
case TimeSyncError:
|
||||
code = EnvelopeTimeNotSynced
|
||||
}
|
||||
return EnvelopeError{
|
||||
Hash: hash,
|
||||
Code: code,
|
||||
Description: err.Error(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,25 @@ const (
|
|||
cursorSize = 36
|
||||
)
|
||||
|
||||
// MailServer represents a mail server, capable of
|
||||
// archiving the old messages for subsequent delivery
|
||||
// to the peers. Any implementation must ensure that both
|
||||
// functions are thread-safe. Also, they must return ASAP.
|
||||
// DeliverMail should use p2pMessageCode for delivery,
|
||||
// in order to bypass the expiry checks.
|
||||
type MailServer interface {
|
||||
Archive(env *Envelope)
|
||||
DeliverMail(peerID []byte, request *Envelope) // DEPRECATED; use Deliver()
|
||||
Deliver(peerID []byte, request MessagesRequest)
|
||||
}
|
||||
|
||||
// MailServerResponse is the response payload sent by the mailserver.
|
||||
type MailServerResponse struct {
|
||||
LastEnvelopeHash common.Hash
|
||||
Cursor []byte
|
||||
Error error
|
||||
}
|
||||
|
||||
func invalidResponseSizeError(size int) error {
|
||||
return fmt.Errorf("unexpected payload size: %d", size)
|
||||
}
|
|
@ -25,6 +25,7 @@ import (
|
|||
crand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
mrand "math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
|
@ -33,6 +34,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/ecies"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// MessageParams specifies the exact way a message should be wrapped
|
||||
|
@ -79,6 +81,89 @@ type ReceivedMessage struct {
|
|||
P2P bool // is set to true if this message was received from mail server.
|
||||
}
|
||||
|
||||
// MessagesRequest contains details of a request of historic messages.
|
||||
type MessagesRequest struct {
|
||||
// ID of the request. The current implementation requires ID to be 32-byte array,
|
||||
// however, it's not enforced for future implementation.
|
||||
ID []byte `json:"id"`
|
||||
|
||||
// From is a lower bound of time range.
|
||||
From uint32 `json:"from"`
|
||||
|
||||
// To is a upper bound of time range.
|
||||
To uint32 `json:"to"`
|
||||
|
||||
// Limit determines the number of messages sent by the mail server
|
||||
// for the current paginated request.
|
||||
Limit uint32 `json:"limit"`
|
||||
|
||||
// Cursor is used as starting point for paginated requests.
|
||||
Cursor []byte `json:"cursor"`
|
||||
|
||||
// Bloom is a filter to match requested messages.
|
||||
Bloom []byte `json:"bloom"`
|
||||
|
||||
// Topics is a list of topics. A returned message should
|
||||
// belong to one of the topics from the list.
|
||||
Topics [][]byte `json:"topics"`
|
||||
}
|
||||
|
||||
func (r MessagesRequest) Validate() error {
|
||||
if len(r.ID) != common.HashLength {
|
||||
return errors.New("invalid 'ID', expected a 32-byte slice")
|
||||
}
|
||||
|
||||
if r.From > r.To {
|
||||
return errors.New("invalid 'From' value which is greater than To")
|
||||
}
|
||||
|
||||
if r.Limit > MaxLimitInMessagesRequest {
|
||||
return fmt.Errorf("invalid 'Limit' value, expected value lower than %d", MaxLimitInMessagesRequest)
|
||||
}
|
||||
|
||||
if len(r.Bloom) == 0 && len(r.Topics) == 0 {
|
||||
return errors.New("invalid 'Bloom' or 'Topics', one must be non-empty")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MessagesResponse sent as a response after processing batch of envelopes.
|
||||
type MessagesResponse struct {
|
||||
// Hash is a hash of all envelopes sent in the single batch.
|
||||
Hash common.Hash
|
||||
// Per envelope error.
|
||||
Errors []EnvelopeError
|
||||
}
|
||||
|
||||
// MultiVersionResponse allows to decode response into chosen version.
|
||||
type MultiVersionResponse struct {
|
||||
Version uint
|
||||
Response rlp.RawValue
|
||||
}
|
||||
|
||||
// DecodeResponse1 decodes response into first version of the messages response.
|
||||
func (m MultiVersionResponse) DecodeResponse1() (resp MessagesResponse, err error) {
|
||||
return resp, rlp.DecodeBytes(m.Response, &resp)
|
||||
}
|
||||
|
||||
// Version1MessageResponse first version of the message response.
|
||||
type Version1MessageResponse struct {
|
||||
Version uint
|
||||
Response MessagesResponse
|
||||
}
|
||||
|
||||
// NewMessagesResponse returns instance of the version messages response.
|
||||
func NewMessagesResponse(batch common.Hash, errors []EnvelopeError) Version1MessageResponse {
|
||||
return Version1MessageResponse{
|
||||
Version: 1,
|
||||
Response: MessagesResponse{
|
||||
Hash: batch,
|
||||
Errors: errors,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func isMessageSigned(flags byte) bool {
|
||||
return (flags & signatureFlag) != 0
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
|
@ -182,9 +185,6 @@ func TestMessageSeal(t *testing.T) {
|
|||
params.TTL = 1
|
||||
|
||||
env := NewEnvelope(params.TTL, params.Topic, msg, time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||||
}
|
||||
|
||||
env.Expiry = uint32(seed) // make it deterministic
|
||||
target := 32.0
|
||||
|
@ -495,3 +495,15 @@ func TestValidateAndParseSizeOfPayloadSize(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeDecodeVersionedResponse(t *testing.T) {
|
||||
response := NewMessagesResponse(common.Hash{1}, []EnvelopeError{{Code: 1}})
|
||||
b, err := rlp.EncodeToBytes(response)
|
||||
require.NoError(t, err)
|
||||
|
||||
var mresponse MultiVersionResponse
|
||||
require.NoError(t, rlp.DecodeBytes(b, &mresponse))
|
||||
v1resp, err := mresponse.DecodeResponse1()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, response.Response.Hash, v1resp.Hash)
|
||||
}
|
||||
|
|
|
@ -48,6 +48,18 @@ func (MetricsRateLimiterHandler) ExceedIPLimit() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RateLimits contains information about rate limit settings.
|
||||
// It is exchanged using rateLimitingCode packet or in the handshake.
|
||||
type RateLimits struct {
|
||||
IPLimits uint64 // messages per second from a single IP (default 0, no limits)
|
||||
PeerIDLimits uint64 // messages per second from a single peer ID (default 0, no limits)
|
||||
TopicLimits uint64 // messages per second from a single topic (default 0, no limits)
|
||||
}
|
||||
|
||||
func (r RateLimits) IsZero() bool {
|
||||
return r == (RateLimits{})
|
||||
}
|
||||
|
||||
var ErrRateLimitExceeded = errors.New("rate limit has been exceeded")
|
||||
|
||||
type DropPeerRateLimiterHandler struct {
|
||||
|
|
|
@ -277,7 +277,7 @@ func TestSymKeyManagement(t *testing.T) {
|
|||
var err error
|
||||
var k1, k2 []byte
|
||||
w := New(nil, nil)
|
||||
id2 := string("arbitrary-string-2")
|
||||
id2 := "arbitrary-string-2"
|
||||
|
||||
id1, err := w.GenerateSymKey()
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/*
|
||||
Package whisper implements the Whisper protocol (version 6).
|
||||
|
||||
Whisper combines aspects of both DHTs and datagram messaging systems (e.g. UDP).
|
||||
As such it may be likened and compared to both, not dissimilar to the
|
||||
matter/energy duality (apologies to physicists for the blatant abuse of a
|
||||
fundamental and beautiful natural principle).
|
||||
|
||||
Whisper is a pure identity-based messaging system. Whisper provides a low-level
|
||||
(non-application-specific) but easily-accessible API without being based upon
|
||||
or prejudiced by the low-level hardware attributes and characteristics,
|
||||
particularly the notion of singular endpoints.
|
||||
*/
|
||||
|
||||
// Contains the Whisper protocol constant definitions
|
||||
|
||||
// Originally inherited from https://github.com/ethereum/go-ethereum/blob/master/whisper/whisperv6/doc.go,
|
||||
// refactored due to https://github.com/status-im/status-go/pull/1950
|
||||
|
||||
package whisper
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
// Whisper protocol parameters
|
||||
const (
|
||||
ProtocolVersion = uint64(6) // Protocol version number
|
||||
ProtocolVersionStr = "6.0" // The same, as a string
|
||||
ProtocolName = "shh" // Nickname of the protocol in geth
|
||||
|
||||
// whisper protocol message codes, according to EIP-627
|
||||
statusCode = 0 // used by whisper protocol
|
||||
messagesCode = 1 // normal whisper message
|
||||
powRequirementCode = 2 // PoW requirement
|
||||
bloomFilterExCode = 3 // bloom filter exchange
|
||||
batchAcknowledgedCode = 11 // confirmation that batch of envelopes was received
|
||||
messageResponseCode = 12 // includes confirmation for delivery and information about errors
|
||||
rateLimitingCode = 20 // includes peer's rate limiting settings
|
||||
p2pSyncRequestCode = 123 // used to sync envelopes between two mail servers
|
||||
p2pSyncResponseCode = 124 // used to sync envelopes between two mail servers
|
||||
p2pRequestCompleteCode = 125 // peer-to-peer message, used by Dapp protocol
|
||||
p2pRequestCode = 126 // peer-to-peer message, used by Dapp protocol
|
||||
p2pMessageCode = 127 // peer-to-peer message (to be consumed by the peer, but not forwarded any further)
|
||||
NumberOfMessageCodes = 128
|
||||
|
||||
SizeMask = byte(3) // mask used to extract the size of payload size field from the flags
|
||||
signatureFlag = byte(4)
|
||||
|
||||
TopicLength = 4 // in bytes
|
||||
signatureLength = crypto.SignatureLength // in bytes
|
||||
aesKeyLength = 32 // in bytes
|
||||
aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize()
|
||||
keyIDSize = 32 // in bytes
|
||||
BloomFilterSize = 64 // in bytes
|
||||
flagsLength = 1
|
||||
|
||||
EnvelopeHeaderLength = 20
|
||||
|
||||
MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message.
|
||||
DefaultMaxMessageSize = uint32(1024 * 1024)
|
||||
DefaultMinimumPoW = 0.2
|
||||
|
||||
padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol
|
||||
messageQueueLimit = 1024
|
||||
|
||||
expirationCycle = time.Second
|
||||
transmissionCycle = 300 * time.Millisecond
|
||||
|
||||
DefaultTTL = 50 // seconds
|
||||
DefaultSyncAllowance = 10 // seconds
|
||||
|
||||
MaxLimitInSyncMailRequest = 1000
|
||||
|
||||
EnvelopeTimeNotSynced uint = iota + 1
|
||||
EnvelopeOtherError
|
||||
|
||||
MaxLimitInMessagesRequest = 1000
|
||||
)
|
273
whisper/doc.go
273
whisper/doc.go
|
@ -1,273 +0,0 @@
|
|||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/*
|
||||
Package whisper implements the Whisper protocol (version 6).
|
||||
|
||||
Whisper combines aspects of both DHTs and datagram messaging systems (e.g. UDP).
|
||||
As such it may be likened and compared to both, not dissimilar to the
|
||||
matter/energy duality (apologies to physicists for the blatant abuse of a
|
||||
fundamental and beautiful natural principle).
|
||||
|
||||
Whisper is a pure identity-based messaging system. Whisper provides a low-level
|
||||
(non-application-specific) but easily-accessible API without being based upon
|
||||
or prejudiced by the low-level hardware attributes and characteristics,
|
||||
particularly the notion of singular endpoints.
|
||||
*/
|
||||
|
||||
// Contains the Whisper protocol constant definitions
|
||||
|
||||
package whisper
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// Whisper protocol parameters
|
||||
const (
|
||||
ProtocolVersion = uint64(6) // Protocol version number
|
||||
ProtocolVersionStr = "6.0" // The same, as a string
|
||||
ProtocolName = "shh" // Nickname of the protocol in geth
|
||||
|
||||
// whisper protocol message codes, according to EIP-627
|
||||
statusCode = 0 // used by whisper protocol
|
||||
messagesCode = 1 // normal whisper message
|
||||
powRequirementCode = 2 // PoW requirement
|
||||
bloomFilterExCode = 3 // bloom filter exchange
|
||||
batchAcknowledgedCode = 11 // confirmation that batch of envelopes was received
|
||||
messageResponseCode = 12 // includes confirmation for delivery and information about errors
|
||||
rateLimitingCode = 20 // includes peer's rate limiting settings
|
||||
p2pSyncRequestCode = 123 // used to sync envelopes between two mail servers
|
||||
p2pSyncResponseCode = 124 // used to sync envelopes between two mail servers
|
||||
p2pRequestCompleteCode = 125 // peer-to-peer message, used by Dapp protocol
|
||||
p2pRequestCode = 126 // peer-to-peer message, used by Dapp protocol
|
||||
p2pMessageCode = 127 // peer-to-peer message (to be consumed by the peer, but not forwarded any further)
|
||||
NumberOfMessageCodes = 128
|
||||
|
||||
SizeMask = byte(3) // mask used to extract the size of payload size field from the flags
|
||||
signatureFlag = byte(4)
|
||||
|
||||
TopicLength = 4 // in bytes
|
||||
signatureLength = crypto.SignatureLength // in bytes
|
||||
aesKeyLength = 32 // in bytes
|
||||
aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize()
|
||||
keyIDSize = 32 // in bytes
|
||||
BloomFilterSize = 64 // in bytes
|
||||
flagsLength = 1
|
||||
|
||||
EnvelopeHeaderLength = 20
|
||||
|
||||
MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message.
|
||||
DefaultMaxMessageSize = uint32(1024 * 1024)
|
||||
DefaultMinimumPoW = 0.2
|
||||
|
||||
padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol
|
||||
messageQueueLimit = 1024
|
||||
|
||||
expirationCycle = time.Second
|
||||
transmissionCycle = 300 * time.Millisecond
|
||||
|
||||
DefaultTTL = 50 // seconds
|
||||
DefaultSyncAllowance = 10 // seconds
|
||||
|
||||
MaxLimitInSyncMailRequest = 1000
|
||||
|
||||
EnvelopeTimeNotSynced uint = iota + 1
|
||||
EnvelopeOtherError
|
||||
|
||||
MaxLimitInMessagesRequest = 1000
|
||||
)
|
||||
|
||||
// MailServer represents a mail server, capable of
|
||||
// archiving the old messages for subsequent delivery
|
||||
// to the peers. Any implementation must ensure that both
|
||||
// functions are thread-safe. Also, they must return ASAP.
|
||||
// DeliverMail should use directMessagesCode for delivery,
|
||||
// in order to bypass the expiry checks.
|
||||
type MailServer interface {
|
||||
Archive(env *Envelope)
|
||||
DeliverMail(peerID []byte, req *Envelope) // DEPRECATED; user Deliver instead
|
||||
Deliver(peerID []byte, req MessagesRequest)
|
||||
SyncMail(peerID []byte, req SyncMailRequest) error
|
||||
}
|
||||
|
||||
// MessagesRequest contains details of a request of historic messages.
|
||||
type MessagesRequest struct {
|
||||
// ID of the request. The current implementation requires ID to be 32-byte array,
|
||||
// however, it's not enforced for future implementation.
|
||||
ID []byte `json:"id"`
|
||||
|
||||
// From is a lower bound of time range.
|
||||
From uint32 `json:"from"`
|
||||
|
||||
// To is a upper bound of time range.
|
||||
To uint32 `json:"to"`
|
||||
|
||||
// Limit determines the number of messages sent by the mail server
|
||||
// for the current paginated request.
|
||||
Limit uint32 `json:"limit"`
|
||||
|
||||
// Cursor is used as starting point for paginated requests.
|
||||
Cursor []byte `json:"cursor"`
|
||||
|
||||
// Bloom is a filter to match requested messages.
|
||||
Bloom []byte `json:"bloom"`
|
||||
}
|
||||
|
||||
func (r MessagesRequest) Validate() error {
|
||||
if len(r.ID) != common.HashLength {
|
||||
return errors.New("invalid 'ID', expected a 32-byte slice")
|
||||
}
|
||||
|
||||
if r.From > r.To {
|
||||
return errors.New("invalid 'From' value which is greater than To")
|
||||
}
|
||||
|
||||
if r.Limit > MaxLimitInMessagesRequest {
|
||||
return fmt.Errorf("invalid 'Limit' value, expected value lower than %d", MaxLimitInMessagesRequest)
|
||||
}
|
||||
|
||||
if len(r.Bloom) == 0 {
|
||||
return errors.New("invalid 'Bloom' provided")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SyncMailRequest contains details which envelopes should be synced
|
||||
// between Mail Servers.
|
||||
type SyncMailRequest struct {
|
||||
// Lower is a lower bound of time range for which messages are requested.
|
||||
Lower uint32
|
||||
// Upper is a lower bound of time range for which messages are requested.
|
||||
Upper uint32
|
||||
// Bloom is a bloom filter to filter envelopes.
|
||||
Bloom []byte
|
||||
// Limit is the max number of envelopes to return.
|
||||
Limit uint32
|
||||
// Cursor is used for pagination of the results.
|
||||
Cursor []byte
|
||||
}
|
||||
|
||||
// Validate checks request's fields if they are valid.
|
||||
func (r SyncMailRequest) Validate() error {
|
||||
if r.Limit == 0 {
|
||||
return errors.New("invalid 'Limit' value, expected value greater than 0")
|
||||
}
|
||||
|
||||
if r.Limit > MaxLimitInSyncMailRequest {
|
||||
return fmt.Errorf("invalid 'Limit' value, expected value lower than %d", MaxLimitInSyncMailRequest)
|
||||
}
|
||||
|
||||
if r.Lower > r.Upper {
|
||||
return errors.New("invalid 'Lower' value, can't be greater than 'Upper'")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SyncResponse is a struct representing a response sent to the peer
|
||||
// asking for syncing archived envelopes.
|
||||
type SyncResponse struct {
|
||||
Envelopes []*Envelope
|
||||
Cursor []byte
|
||||
Final bool // if true it means all envelopes were processed
|
||||
Error string
|
||||
}
|
||||
|
||||
// RawSyncResponse is a struct representing a response sent to the peer
|
||||
// asking for syncing archived envelopes.
|
||||
type RawSyncResponse struct {
|
||||
Envelopes []rlp.RawValue
|
||||
Cursor []byte
|
||||
Final bool // if true it means all envelopes were processed
|
||||
Error string
|
||||
}
|
||||
|
||||
// MessagesResponse sent as a response after processing batch of envelopes.
|
||||
type MessagesResponse struct {
|
||||
// Hash is a hash of all envelopes sent in the single batch.
|
||||
Hash common.Hash
|
||||
// Per envelope error.
|
||||
Errors []EnvelopeError
|
||||
}
|
||||
|
||||
// EnvelopeError code and optional description of the error.
|
||||
type EnvelopeError struct {
|
||||
Hash common.Hash
|
||||
Code uint
|
||||
Description string
|
||||
}
|
||||
|
||||
// MultiVersionResponse allows to decode response into chosen version.
|
||||
type MultiVersionResponse struct {
|
||||
Version uint
|
||||
Response rlp.RawValue
|
||||
}
|
||||
|
||||
// DecodeResponse1 decodes response into first version of the messages response.
|
||||
func (m MultiVersionResponse) DecodeResponse1() (resp MessagesResponse, err error) {
|
||||
return resp, rlp.DecodeBytes(m.Response, &resp)
|
||||
}
|
||||
|
||||
// Version1MessageResponse first version of the message response.
|
||||
type Version1MessageResponse struct {
|
||||
Version uint
|
||||
Response MessagesResponse
|
||||
}
|
||||
|
||||
// NewMessagesResponse returns instane of the version messages response.
|
||||
func NewMessagesResponse(batch common.Hash, errors []EnvelopeError) Version1MessageResponse {
|
||||
return Version1MessageResponse{
|
||||
Version: 1,
|
||||
Response: MessagesResponse{
|
||||
Hash: batch,
|
||||
Errors: errors,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorToEnvelopeError converts common golang error into EnvelopeError with a code.
|
||||
func ErrorToEnvelopeError(hash common.Hash, err error) EnvelopeError {
|
||||
code := EnvelopeOtherError
|
||||
switch err.(type) {
|
||||
case TimeSyncError:
|
||||
code = EnvelopeTimeNotSynced
|
||||
}
|
||||
return EnvelopeError{
|
||||
Hash: hash,
|
||||
Code: code,
|
||||
Description: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
// RateLimits contains information about rate limit settings.
|
||||
// It is exchanged using rateLimitingCode packet or in the handshake.
|
||||
type RateLimits struct {
|
||||
IPLimits uint64 // messages per second from a single IP (default 0, no limits)
|
||||
PeerIDLimits uint64 // messages per second from a single peer ID (default 0, no limits)
|
||||
TopicLimits uint64 // messages per second from a single topic (default 0, no limits)
|
||||
}
|
||||
|
||||
func (r RateLimits) IsZero() bool {
|
||||
return r == (RateLimits{})
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
package whisper
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
func TestSyncMailRequestValidate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Name string
|
||||
Req SyncMailRequest
|
||||
Error string
|
||||
}{
|
||||
{
|
||||
Name: "invalid zero Limit",
|
||||
Req: SyncMailRequest{},
|
||||
Error: "invalid 'Limit' value, expected value greater than 0",
|
||||
},
|
||||
{
|
||||
Name: "invalid large Limit",
|
||||
Req: SyncMailRequest{Limit: 1e6},
|
||||
Error: "invalid 'Limit' value, expected value lower than 1000",
|
||||
},
|
||||
{
|
||||
Name: "invalid Lower",
|
||||
Req: SyncMailRequest{Limit: 10, Lower: 10, Upper: 5},
|
||||
Error: "invalid 'Lower' value, can't be greater than 'Upper'",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
err := tc.Req.Validate()
|
||||
if tc.Error != "" {
|
||||
assert.EqualError(t, err, tc.Error)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeDecodeVersionedResponse(t *testing.T) {
|
||||
response := NewMessagesResponse(common.Hash{1}, []EnvelopeError{{Code: 1}})
|
||||
bytes, err := rlp.EncodeToBytes(response)
|
||||
require.NoError(t, err)
|
||||
var mresponse MultiVersionResponse
|
||||
require.NoError(t, rlp.DecodeBytes(bytes, &mresponse))
|
||||
v1resp, err := mresponse.DecodeResponse1()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, response.Response.Hash, v1resp.Hash)
|
||||
}
|
|
@ -278,3 +278,24 @@ func (w *Whisper) GetEnvelope(hash common.Hash) *Envelope {
|
|||
defer w.poolMu.RUnlock()
|
||||
return w.envelopes[hash]
|
||||
}
|
||||
|
||||
// EnvelopeError code and optional description of the error.
|
||||
type EnvelopeError struct {
|
||||
Hash common.Hash
|
||||
Code uint
|
||||
Description string
|
||||
}
|
||||
|
||||
// ErrorToEnvelopeError converts common golang error into EnvelopeError with a code.
|
||||
func ErrorToEnvelopeError(hash common.Hash, err error) EnvelopeError {
|
||||
code := EnvelopeOtherError
|
||||
switch err.(type) {
|
||||
case TimeSyncError:
|
||||
code = EnvelopeTimeNotSynced
|
||||
}
|
||||
return EnvelopeError{
|
||||
Hash: hash,
|
||||
Code: code,
|
||||
Description: err.Error(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ 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.
|
||||
// reproducibility independent of their sequence.
|
||||
func InitSingleTest() {
|
||||
seed = time.Now().Unix()
|
||||
mrand.Seed(seed)
|
||||
|
@ -88,7 +88,7 @@ func generateTestCases(t *testing.T, SizeTestFilters int) []FilterTestCase {
|
|||
for i := 0; i < SizeTestFilters; i++ {
|
||||
f, _ := generateFilter(t, true)
|
||||
cases[i].f = f
|
||||
cases[i].alive = mrand.Int()&int(1) == 0
|
||||
cases[i].alive = mrand.Int()&1 == 0
|
||||
}
|
||||
return cases
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -14,6 +15,69 @@ const (
|
|||
cursorSize = 36
|
||||
)
|
||||
|
||||
// MailServer represents a mail server, capable of
|
||||
// archiving the old messages for subsequent delivery
|
||||
// to the peers. Any implementation must ensure that both
|
||||
// functions are thread-safe. Also, they must return ASAP.
|
||||
// DeliverMail should use directMessagesCode for delivery,
|
||||
// in order to bypass the expiry checks.
|
||||
type MailServer interface {
|
||||
Archive(env *Envelope)
|
||||
DeliverMail(peerID []byte, req *Envelope) // DEPRECATED; user Deliver instead
|
||||
Deliver(peerID []byte, req MessagesRequest)
|
||||
SyncMail(peerID []byte, req SyncMailRequest) error
|
||||
}
|
||||
|
||||
// SyncMailRequest contains details which envelopes should be synced
|
||||
// between Mail Servers.
|
||||
type SyncMailRequest struct {
|
||||
// Lower is a lower bound of time range for which messages are requested.
|
||||
Lower uint32
|
||||
// Upper is a lower bound of time range for which messages are requested.
|
||||
Upper uint32
|
||||
// Bloom is a bloom filter to filter envelopes.
|
||||
Bloom []byte
|
||||
// Limit is the max number of envelopes to return.
|
||||
Limit uint32
|
||||
// Cursor is used for pagination of the results.
|
||||
Cursor []byte
|
||||
}
|
||||
|
||||
// Validate checks request's fields if they are valid.
|
||||
func (r SyncMailRequest) Validate() error {
|
||||
if r.Limit == 0 {
|
||||
return errors.New("invalid 'Limit' value, expected value greater than 0")
|
||||
}
|
||||
|
||||
if r.Limit > MaxLimitInSyncMailRequest {
|
||||
return fmt.Errorf("invalid 'Limit' value, expected value lower than %d", MaxLimitInSyncMailRequest)
|
||||
}
|
||||
|
||||
if r.Lower > r.Upper {
|
||||
return errors.New("invalid 'Lower' value, can't be greater than 'Upper'")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SyncResponse is a struct representing a response sent to the peer
|
||||
// asking for syncing archived envelopes.
|
||||
type SyncResponse struct {
|
||||
Envelopes []*Envelope
|
||||
Cursor []byte
|
||||
Final bool // if true it means all envelopes were processed
|
||||
Error string
|
||||
}
|
||||
|
||||
// RawSyncResponse is a struct representing a response sent to the peer
|
||||
// asking for syncing archived envelopes.
|
||||
type RawSyncResponse struct {
|
||||
Envelopes []rlp.RawValue
|
||||
Cursor []byte
|
||||
Final bool // if true it means all envelopes were processed
|
||||
Error string
|
||||
}
|
||||
|
||||
func invalidResponseSizeError(size int) error {
|
||||
return fmt.Errorf("unexpected payload size: %d", size)
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/binary"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/syndtr/goleveldb/leveldb/errors"
|
||||
|
||||
|
@ -78,3 +79,38 @@ func TestCreateMailServerEvent(t *testing.T) {
|
|||
_, err = CreateMailServerEvent(enode.ID{}, payloadTooBig)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestSyncMailRequestValidate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Name string
|
||||
Req SyncMailRequest
|
||||
Error string
|
||||
}{
|
||||
{
|
||||
Name: "invalid zero Limit",
|
||||
Req: SyncMailRequest{},
|
||||
Error: "invalid 'Limit' value, expected value greater than 0",
|
||||
},
|
||||
{
|
||||
Name: "invalid large Limit",
|
||||
Req: SyncMailRequest{Limit: 1e6},
|
||||
Error: "invalid 'Limit' value, expected value lower than 1000",
|
||||
},
|
||||
{
|
||||
Name: "invalid Lower",
|
||||
Req: SyncMailRequest{Limit: 10, Lower: 10, Upper: 5},
|
||||
Error: "invalid 'Lower' value, can't be greater than 'Upper'",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
err := tc.Req.Validate()
|
||||
if tc.Error != "" {
|
||||
assert.EqualError(t, err, tc.Error)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ import (
|
|||
crand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
mrand "math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
|
@ -33,6 +34,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/ecies"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// MessageParams specifies the exact way a message should be wrapped
|
||||
|
@ -79,6 +81,85 @@ type ReceivedMessage struct {
|
|||
P2P bool // is set to true if this message was received from mail server.
|
||||
}
|
||||
|
||||
// MessagesRequest contains details of a request of historic messages.
|
||||
type MessagesRequest struct {
|
||||
// ID of the request. The current implementation requires ID to be 32-byte array,
|
||||
// however, it's not enforced for future implementation.
|
||||
ID []byte `json:"id"`
|
||||
|
||||
// From is a lower bound of time range.
|
||||
From uint32 `json:"from"`
|
||||
|
||||
// To is a upper bound of time range.
|
||||
To uint32 `json:"to"`
|
||||
|
||||
// Limit determines the number of messages sent by the mail server
|
||||
// for the current paginated request.
|
||||
Limit uint32 `json:"limit"`
|
||||
|
||||
// Cursor is used as starting point for paginated requests.
|
||||
Cursor []byte `json:"cursor"`
|
||||
|
||||
// Bloom is a filter to match requested messages.
|
||||
Bloom []byte `json:"bloom"`
|
||||
}
|
||||
|
||||
func (r MessagesRequest) Validate() error {
|
||||
if len(r.ID) != common.HashLength {
|
||||
return errors.New("invalid 'ID', expected a 32-byte slice")
|
||||
}
|
||||
|
||||
if r.From > r.To {
|
||||
return errors.New("invalid 'From' value which is greater than To")
|
||||
}
|
||||
|
||||
if r.Limit > MaxLimitInMessagesRequest {
|
||||
return fmt.Errorf("invalid 'Limit' value, expected value lower than %d", MaxLimitInMessagesRequest)
|
||||
}
|
||||
|
||||
if len(r.Bloom) == 0 {
|
||||
return errors.New("invalid 'Bloom' provided")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MessagesResponse sent as a response after processing batch of envelopes.
|
||||
type MessagesResponse struct {
|
||||
// Hash is a hash of all envelopes sent in the single batch.
|
||||
Hash common.Hash
|
||||
// Per envelope error.
|
||||
Errors []EnvelopeError
|
||||
}
|
||||
|
||||
// MultiVersionResponse allows to decode response into chosen version.
|
||||
type MultiVersionResponse struct {
|
||||
Version uint
|
||||
Response rlp.RawValue
|
||||
}
|
||||
|
||||
// DecodeResponse1 decodes response into first version of the messages response.
|
||||
func (m MultiVersionResponse) DecodeResponse1() (resp MessagesResponse, err error) {
|
||||
return resp, rlp.DecodeBytes(m.Response, &resp)
|
||||
}
|
||||
|
||||
// Version1MessageResponse first version of the message response.
|
||||
type Version1MessageResponse struct {
|
||||
Version uint
|
||||
Response MessagesResponse
|
||||
}
|
||||
|
||||
// NewMessagesResponse returns instane of the version messages response.
|
||||
func NewMessagesResponse(batch common.Hash, errors []EnvelopeError) Version1MessageResponse {
|
||||
return Version1MessageResponse{
|
||||
Version: 1,
|
||||
Response: MessagesResponse{
|
||||
Hash: batch,
|
||||
Errors: errors,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func isMessageSigned(flags byte) bool {
|
||||
return (flags & signatureFlag) != 0
|
||||
}
|
||||
|
|
|
@ -24,6 +24,9 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
|
@ -180,9 +183,6 @@ func TestMessageSeal(t *testing.T) {
|
|||
params.TTL = 1
|
||||
|
||||
env := NewEnvelope(params.TTL, params.Topic, msg, time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||||
}
|
||||
|
||||
env.Expiry = uint32(seed) // make it deterministic
|
||||
target := 32.0
|
||||
|
@ -351,7 +351,7 @@ func TestRlpEncode(t *testing.T) {
|
|||
}
|
||||
|
||||
var decoded Envelope
|
||||
rlp.DecodeBytes(raw, &decoded)
|
||||
err = rlp.DecodeBytes(raw, &decoded)
|
||||
if err != nil {
|
||||
t.Fatalf("RLP decode failed: %s.", err)
|
||||
}
|
||||
|
@ -493,3 +493,14 @@ func TestValidateAndParseSizeOfPayloadSize(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeDecodeVersionedResponse(t *testing.T) {
|
||||
response := NewMessagesResponse(common.Hash{1}, []EnvelopeError{{Code: 1}})
|
||||
b, err := rlp.EncodeToBytes(response)
|
||||
require.NoError(t, err)
|
||||
var mresponse MultiVersionResponse
|
||||
require.NoError(t, rlp.DecodeBytes(b, &mresponse))
|
||||
v1resp, err := mresponse.DecodeResponse1()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, response.Response.Hash, v1resp.Hash)
|
||||
}
|
||||
|
|
|
@ -418,7 +418,7 @@ func TestPeerBasic(t *testing.T) {
|
|||
func checkPowExchangeForNodeZero(t *testing.T) {
|
||||
const iterations = 200
|
||||
for j := 0; j < iterations; j++ {
|
||||
lastCycle := (j == iterations-1)
|
||||
lastCycle := j == iterations-1
|
||||
ok := checkPowExchangeForNodeZeroOnce(t, lastCycle)
|
||||
if ok {
|
||||
break
|
||||
|
@ -485,7 +485,7 @@ func checkBloomFilterExchangeOnce(t *testing.T, mustPass bool) bool {
|
|||
func checkBloomFilterExchange(t *testing.T) {
|
||||
const iterations = 200
|
||||
for j := 0; j < iterations; j++ {
|
||||
lastCycle := (j == iterations-1)
|
||||
lastCycle := j == iterations-1
|
||||
ok := checkBloomFilterExchangeOnce(t, lastCycle)
|
||||
if ok {
|
||||
break
|
||||
|
|
|
@ -30,6 +30,18 @@ func (MetricsRateLimiterHandler) ExceedIPLimit() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RateLimits contains information about rate limit settings.
|
||||
// It is exchanged using rateLimitingCode packet or in the handshake.
|
||||
type RateLimits struct {
|
||||
IPLimits uint64 // messages per second from a single IP (default 0, no limits)
|
||||
PeerIDLimits uint64 // messages per second from a single peer ID (default 0, no limits)
|
||||
TopicLimits uint64 // messages per second from a single topic (default 0, no limits)
|
||||
}
|
||||
|
||||
func (r RateLimits) IsZero() bool {
|
||||
return r == (RateLimits{})
|
||||
}
|
||||
|
||||
var ErrRateLimitExceeded = errors.New("rate limit has been exceeded")
|
||||
|
||||
type DropPeerRateLimiterHandler struct {
|
||||
|
|
|
@ -275,9 +275,9 @@ func TestWhisperSymKeyManagement(t *testing.T) {
|
|||
var err error
|
||||
var k1, k2 []byte
|
||||
w := New(&DefaultConfig)
|
||||
id2 := string("arbitrary-string-2")
|
||||
id2 := "arbitrary-string-2"
|
||||
|
||||
id1, err = w.GenerateSymKey()
|
||||
id1, err := w.GenerateSymKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed GenerateSymKey with seed %d: %s.", seed, err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue