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
|
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
|
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 {
|
func invalidResponseSizeError(size int) error {
|
||||||
return fmt.Errorf("unexpected payload size: %d", size)
|
return fmt.Errorf("unexpected payload size: %d", size)
|
||||||
}
|
}
|
|
@ -25,6 +25,7 @@ import (
|
||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
mrand "math/rand"
|
mrand "math/rand"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
@ -33,6 +34,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/crypto/ecies"
|
"github.com/ethereum/go-ethereum/crypto/ecies"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MessageParams specifies the exact way a message should be wrapped
|
// 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.
|
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 {
|
func isMessageSigned(flags byte) bool {
|
||||||
return (flags & signatureFlag) != 0
|
return (flags & signatureFlag) != 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,9 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
@ -182,9 +185,6 @@ func TestMessageSeal(t *testing.T) {
|
||||||
params.TTL = 1
|
params.TTL = 1
|
||||||
|
|
||||||
env := NewEnvelope(params.TTL, params.Topic, msg, time.Now())
|
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
|
env.Expiry = uint32(seed) // make it deterministic
|
||||||
target := 32.0
|
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
|
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")
|
var ErrRateLimitExceeded = errors.New("rate limit has been exceeded")
|
||||||
|
|
||||||
type DropPeerRateLimiterHandler struct {
|
type DropPeerRateLimiterHandler struct {
|
||||||
|
|
|
@ -277,7 +277,7 @@ func TestSymKeyManagement(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
var k1, k2 []byte
|
var k1, k2 []byte
|
||||||
w := New(nil, nil)
|
w := New(nil, nil)
|
||||||
id2 := string("arbitrary-string-2")
|
id2 := "arbitrary-string-2"
|
||||||
|
|
||||||
id1, err := w.GenerateSymKey()
|
id1, err := w.GenerateSymKey()
|
||||||
if err != nil {
|
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()
|
defer w.poolMu.RUnlock()
|
||||||
return w.envelopes[hash]
|
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
|
// InitSingleTest should be called in the beginning of every
|
||||||
// test, which uses RNG, in order to make the tests
|
// test, which uses RNG, in order to make the tests
|
||||||
// reproduciblity independent of their sequence.
|
// reproducibility independent of their sequence.
|
||||||
func InitSingleTest() {
|
func InitSingleTest() {
|
||||||
seed = time.Now().Unix()
|
seed = time.Now().Unix()
|
||||||
mrand.Seed(seed)
|
mrand.Seed(seed)
|
||||||
|
@ -88,7 +88,7 @@ func generateTestCases(t *testing.T, SizeTestFilters int) []FilterTestCase {
|
||||||
for i := 0; i < SizeTestFilters; i++ {
|
for i := 0; i < SizeTestFilters; i++ {
|
||||||
f, _ := generateFilter(t, true)
|
f, _ := generateFilter(t, true)
|
||||||
cases[i].f = f
|
cases[i].f = f
|
||||||
cases[i].alive = mrand.Int()&int(1) == 0
|
cases[i].alive = mrand.Int()&1 == 0
|
||||||
}
|
}
|
||||||
return cases
|
return cases
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -14,6 +15,69 @@ const (
|
||||||
cursorSize = 36
|
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 {
|
func invalidResponseSizeError(size int) error {
|
||||||
return fmt.Errorf("unexpected payload size: %d", size)
|
return fmt.Errorf("unexpected payload size: %d", size)
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/syndtr/goleveldb/leveldb/errors"
|
"github.com/syndtr/goleveldb/leveldb/errors"
|
||||||
|
|
||||||
|
@ -78,3 +79,38 @@ func TestCreateMailServerEvent(t *testing.T) {
|
||||||
_, err = CreateMailServerEvent(enode.ID{}, payloadTooBig)
|
_, err = CreateMailServerEvent(enode.ID{}, payloadTooBig)
|
||||||
require.Error(t, err)
|
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"
|
crand "crypto/rand"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
mrand "math/rand"
|
mrand "math/rand"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
@ -33,6 +34,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/crypto/ecies"
|
"github.com/ethereum/go-ethereum/crypto/ecies"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MessageParams specifies the exact way a message should be wrapped
|
// 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.
|
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 {
|
func isMessageSigned(flags byte) bool {
|
||||||
return (flags & signatureFlag) != 0
|
return (flags & signatureFlag) != 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,9 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
@ -180,9 +183,6 @@ func TestMessageSeal(t *testing.T) {
|
||||||
params.TTL = 1
|
params.TTL = 1
|
||||||
|
|
||||||
env := NewEnvelope(params.TTL, params.Topic, msg, time.Now())
|
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
|
env.Expiry = uint32(seed) // make it deterministic
|
||||||
target := 32.0
|
target := 32.0
|
||||||
|
@ -351,7 +351,7 @@ func TestRlpEncode(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var decoded Envelope
|
var decoded Envelope
|
||||||
rlp.DecodeBytes(raw, &decoded)
|
err = rlp.DecodeBytes(raw, &decoded)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("RLP decode failed: %s.", err)
|
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) {
|
func checkPowExchangeForNodeZero(t *testing.T) {
|
||||||
const iterations = 200
|
const iterations = 200
|
||||||
for j := 0; j < iterations; j++ {
|
for j := 0; j < iterations; j++ {
|
||||||
lastCycle := (j == iterations-1)
|
lastCycle := j == iterations-1
|
||||||
ok := checkPowExchangeForNodeZeroOnce(t, lastCycle)
|
ok := checkPowExchangeForNodeZeroOnce(t, lastCycle)
|
||||||
if ok {
|
if ok {
|
||||||
break
|
break
|
||||||
|
@ -485,7 +485,7 @@ func checkBloomFilterExchangeOnce(t *testing.T, mustPass bool) bool {
|
||||||
func checkBloomFilterExchange(t *testing.T) {
|
func checkBloomFilterExchange(t *testing.T) {
|
||||||
const iterations = 200
|
const iterations = 200
|
||||||
for j := 0; j < iterations; j++ {
|
for j := 0; j < iterations; j++ {
|
||||||
lastCycle := (j == iterations-1)
|
lastCycle := j == iterations-1
|
||||||
ok := checkBloomFilterExchangeOnce(t, lastCycle)
|
ok := checkBloomFilterExchangeOnce(t, lastCycle)
|
||||||
if ok {
|
if ok {
|
||||||
break
|
break
|
||||||
|
|
|
@ -30,6 +30,18 @@ func (MetricsRateLimiterHandler) ExceedIPLimit() error {
|
||||||
return nil
|
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")
|
var ErrRateLimitExceeded = errors.New("rate limit has been exceeded")
|
||||||
|
|
||||||
type DropPeerRateLimiterHandler struct {
|
type DropPeerRateLimiterHandler struct {
|
||||||
|
|
|
@ -275,9 +275,9 @@ func TestWhisperSymKeyManagement(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
var k1, k2 []byte
|
var k1, k2 []byte
|
||||||
w := New(&DefaultConfig)
|
w := New(&DefaultConfig)
|
||||||
id2 := string("arbitrary-string-2")
|
id2 := "arbitrary-string-2"
|
||||||
|
|
||||||
id1, err = w.GenerateSymKey()
|
id1, err := w.GenerateSymKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed GenerateSymKey with seed %d: %s.", seed, err)
|
t.Fatalf("failed GenerateSymKey with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue