mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 17:33:47 +00:00
Update whisperv6 to latest 2b4c7e9
This commit is contained in:
parent
83ef018f66
commit
82213083ac
18 changed files with 1360 additions and 640 deletions
|
|
@ -36,6 +36,7 @@ const (
|
||||||
filterTimeout = 300 // filters are considered timeout out after filterTimeout seconds
|
filterTimeout = 300 // filters are considered timeout out after filterTimeout seconds
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// List of errors
|
||||||
var (
|
var (
|
||||||
ErrSymAsym = errors.New("specify either a symmetric or an asymmetric key")
|
ErrSymAsym = errors.New("specify either a symmetric or an asymmetric key")
|
||||||
ErrInvalidSymmetricKey = errors.New("invalid symmetric key")
|
ErrInvalidSymmetricKey = errors.New("invalid symmetric key")
|
||||||
|
|
@ -116,12 +117,17 @@ func (api *PublicWhisperAPI) SetMaxMessageSize(ctx context.Context, size uint32)
|
||||||
return true, api.w.SetMaxMessageSize(size)
|
return true, api.w.SetMaxMessageSize(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMinPow sets the minimum PoW for a message before it is accepted.
|
// SetMinPoW sets the minimum PoW, and notifies the peers.
|
||||||
func (api *PublicWhisperAPI) SetMinPoW(ctx context.Context, pow float64) (bool, error) {
|
func (api *PublicWhisperAPI) SetMinPoW(ctx context.Context, pow float64) (bool, error) {
|
||||||
return true, api.w.SetMinimumPoW(pow)
|
return true, api.w.SetMinimumPoW(pow)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarkTrustedPeer marks a peer trusted. , which will allow it to send historic (expired) messages.
|
// SetBloomFilter sets the new value of bloom filter, and notifies the peers.
|
||||||
|
func (api *PublicWhisperAPI) SetBloomFilter(ctx context.Context, bloom hexutil.Bytes) (bool, error) {
|
||||||
|
return true, api.w.SetBloomFilter(bloom)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkTrustedPeer marks a peer trusted, which will allow it to send historic (expired) messages.
|
||||||
// Note: This function is not adding new nodes, the node needs to exists as a peer.
|
// Note: This function is not adding new nodes, the node needs to exists as a peer.
|
||||||
func (api *PublicWhisperAPI) MarkTrustedPeer(ctx context.Context, enode string) (bool, error) {
|
func (api *PublicWhisperAPI) MarkTrustedPeer(ctx context.Context, enode string) (bool, error) {
|
||||||
n, err := discover.ParseNode(enode)
|
n, err := discover.ParseNode(enode)
|
||||||
|
|
@ -169,7 +175,7 @@ func (api *PublicWhisperAPI) GetPublicKey(ctx context.Context, id string) (hexut
|
||||||
return crypto.FromECDSAPub(&key.PublicKey), nil
|
return crypto.FromECDSAPub(&key.PublicKey), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPublicKey returns the private key associated with the given key. The key is the hex
|
// GetPrivateKey returns the private key associated with the given key. The key is the hex
|
||||||
// encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62.
|
// encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62.
|
||||||
func (api *PublicWhisperAPI) GetPrivateKey(ctx context.Context, id string) (hexutil.Bytes, error) {
|
func (api *PublicWhisperAPI) GetPrivateKey(ctx context.Context, id string) (hexutil.Bytes, error) {
|
||||||
key, err := api.w.GetPrivateKey(id)
|
key, err := api.w.GetPrivateKey(id)
|
||||||
|
|
@ -272,7 +278,7 @@ func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, er
|
||||||
if params.KeySym, err = api.w.GetSymKey(req.SymKeyID); err != nil {
|
if params.KeySym, err = api.w.GetSymKey(req.SymKeyID); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if !validateSymmetricKey(params.KeySym) {
|
if !validateDataIntegrity(params.KeySym, aesKeyLength) {
|
||||||
return false, ErrInvalidSymmetricKey
|
return false, ErrInvalidSymmetricKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -286,7 +292,7 @@ func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, er
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypt and sent message
|
// encrypt and sent message
|
||||||
whisperMsg, err := NewSentMessage(params)
|
whisperMsg, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
@ -378,7 +384,7 @@ func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !validateSymmetricKey(key) {
|
if !validateDataIntegrity(key, aesKeyLength) {
|
||||||
return nil, ErrInvalidSymmetricKey
|
return nil, ErrInvalidSymmetricKey
|
||||||
}
|
}
|
||||||
filter.KeySym = key
|
filter.KeySym = key
|
||||||
|
|
@ -550,7 +556,7 @@ func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) {
|
||||||
if keySym, err = api.w.GetSymKey(req.SymKeyID); err != nil {
|
if keySym, err = api.w.GetSymKey(req.SymKeyID); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if !validateSymmetricKey(keySym) {
|
if !validateDataIntegrity(keySym, aesKeyLength) {
|
||||||
return "", ErrInvalidSymmetricKey
|
return "", ErrInvalidSymmetricKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -562,7 +568,7 @@ func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(req.Topics) > 0 {
|
if len(req.Topics) > 0 {
|
||||||
topics = make([][]byte, 1)
|
topics = make([][]byte, 0, len(req.Topics))
|
||||||
for _, topic := range req.Topics {
|
for _, topic := range req.Topics {
|
||||||
topics = append(topics, topic[:])
|
topics = append(topics, topic[:])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,16 @@
|
||||||
package whisperv6
|
package whisperv6
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"golang.org/x/crypto/pbkdf2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkDeriveKeyMaterial(b *testing.B) {
|
func BenchmarkDeriveKeyMaterial(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
deriveKeyMaterial([]byte("test"), 0)
|
pbkdf2.Key([]byte("test"), nil, 65356, aesKeyLength, sha256.New)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,7 +39,7 @@ func BenchmarkEncryptionSym(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
msg, _ := NewSentMessage(params)
|
msg, _ := newSentMessage(params)
|
||||||
_, err := msg.Wrap(params)
|
_, err := msg.Wrap(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Errorf("failed Wrap with seed %d: %s.", seed, err)
|
b.Errorf("failed Wrap with seed %d: %s.", seed, err)
|
||||||
|
|
@ -62,7 +64,7 @@ func BenchmarkEncryptionAsym(b *testing.B) {
|
||||||
params.Dst = &key.PublicKey
|
params.Dst = &key.PublicKey
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
msg, _ := NewSentMessage(params)
|
msg, _ := newSentMessage(params)
|
||||||
_, err := msg.Wrap(params)
|
_, err := msg.Wrap(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||||||
|
|
@ -77,7 +79,7 @@ func BenchmarkDecryptionSymValid(b *testing.B) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
msg, _ := NewSentMessage(params)
|
msg, _ := newSentMessage(params)
|
||||||
env, err := msg.Wrap(params)
|
env, err := msg.Wrap(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||||||
|
|
@ -99,7 +101,7 @@ func BenchmarkDecryptionSymInvalid(b *testing.B) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
msg, _ := NewSentMessage(params)
|
msg, _ := newSentMessage(params)
|
||||||
env, err := msg.Wrap(params)
|
env, err := msg.Wrap(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||||||
|
|
@ -128,7 +130,7 @@ func BenchmarkDecryptionAsymValid(b *testing.B) {
|
||||||
f := Filter{KeyAsym: key}
|
f := Filter{KeyAsym: key}
|
||||||
params.KeySym = nil
|
params.KeySym = nil
|
||||||
params.Dst = &key.PublicKey
|
params.Dst = &key.PublicKey
|
||||||
msg, _ := NewSentMessage(params)
|
msg, _ := newSentMessage(params)
|
||||||
env, err := msg.Wrap(params)
|
env, err := msg.Wrap(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||||||
|
|
@ -155,7 +157,7 @@ func BenchmarkDecryptionAsymInvalid(b *testing.B) {
|
||||||
}
|
}
|
||||||
params.KeySym = nil
|
params.KeySym = nil
|
||||||
params.Dst = &key.PublicKey
|
params.Dst = &key.PublicKey
|
||||||
msg, _ := NewSentMessage(params)
|
msg, _ := newSentMessage(params)
|
||||||
env, err := msg.Wrap(params)
|
env, err := msg.Wrap(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||||||
|
|
@ -197,7 +199,7 @@ func BenchmarkPoW(b *testing.B) {
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
increment(params.Payload)
|
increment(params.Payload)
|
||||||
msg, _ := NewSentMessage(params)
|
msg, _ := newSentMessage(params)
|
||||||
_, err := msg.Wrap(params)
|
_, err := msg.Wrap(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,13 @@
|
||||||
|
|
||||||
package whisperv6
|
package whisperv6
|
||||||
|
|
||||||
|
// Config represents the configuration state of a whisper node.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
MaxMessageSize uint32 `toml:",omitempty"`
|
MaxMessageSize uint32 `toml:",omitempty"`
|
||||||
MinimumAcceptedPOW float64 `toml:",omitempty"`
|
MinimumAcceptedPOW float64 `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DefaultConfig represents (shocker!) the default configuration.
|
||||||
var DefaultConfig = Config{
|
var DefaultConfig = Config{
|
||||||
MaxMessageSize: DefaultMaxMessageSize,
|
MaxMessageSize: DefaultMaxMessageSize,
|
||||||
MinimumAcceptedPOW: DefaultMinimumPoW,
|
MinimumAcceptedPOW: DefaultMinimumPoW,
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@ Whisper is a pure identity-based messaging system. Whisper provides a low-level
|
||||||
or prejudiced by the low-level hardware attributes and characteristics,
|
or prejudiced by the low-level hardware attributes and characteristics,
|
||||||
particularly the notion of singular endpoints.
|
particularly the notion of singular endpoints.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Contains the Whisper protocol constant definitions
|
||||||
|
|
||||||
package whisperv6
|
package whisperv6
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -34,39 +37,46 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Whisper protocol parameters
|
||||||
const (
|
const (
|
||||||
EnvelopeVersion = uint64(0)
|
ProtocolVersion = uint64(6) // Protocol version number
|
||||||
ProtocolVersion = uint64(5)
|
ProtocolVersionStr = "6.0" // The same, as a string
|
||||||
ProtocolVersionStr = "5.0"
|
ProtocolName = "shh" // Nickname of the protocol in geth
|
||||||
ProtocolName = "shh"
|
|
||||||
|
|
||||||
|
// whisper protocol message codes, according to EIP-627
|
||||||
statusCode = 0 // used by whisper protocol
|
statusCode = 0 // used by whisper protocol
|
||||||
messagesCode = 1 // normal whisper message
|
messagesCode = 1 // normal whisper message
|
||||||
p2pCode = 2 // peer-to-peer message (to be consumed by the peer, but not forwarded any further)
|
powRequirementCode = 2 // PoW requirement
|
||||||
p2pRequestCode = 3 // peer-to-peer message, used by Dapp protocol
|
bloomFilterExCode = 3 // bloom filter exchange
|
||||||
NumberOfMessageCodes = 64
|
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
|
||||||
|
|
||||||
paddingMask = byte(3)
|
SizeMask = byte(3) // mask used to extract the size of payload size field from the flags
|
||||||
signatureFlag = byte(4)
|
signatureFlag = byte(4)
|
||||||
|
|
||||||
TopicLength = 4
|
TopicLength = 4 // in bytes
|
||||||
signatureLength = 65
|
signatureLength = 65 // in bytes
|
||||||
aesKeyLength = 32
|
aesKeyLength = 32 // in bytes
|
||||||
AESNonceLength = 12
|
aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize()
|
||||||
keyIdSize = 32
|
keyIDSize = 32 // in bytes
|
||||||
|
bloomFilterSize = 64 // in bytes
|
||||||
|
flagsLength = 1
|
||||||
|
|
||||||
|
EnvelopeHeaderLength = 20
|
||||||
|
|
||||||
MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message.
|
MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message.
|
||||||
DefaultMaxMessageSize = uint32(1024 * 1024)
|
DefaultMaxMessageSize = uint32(1024 * 1024)
|
||||||
DefaultMinimumPoW = 0.2
|
DefaultMinimumPoW = 0.2
|
||||||
|
|
||||||
padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol (must not exceed 2^24)
|
padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol
|
||||||
messageQueueLimit = 1024
|
messageQueueLimit = 1024
|
||||||
|
|
||||||
expirationCycle = time.Second
|
expirationCycle = time.Second
|
||||||
transmissionCycle = 300 * time.Millisecond
|
transmissionCycle = 300 * time.Millisecond
|
||||||
|
|
||||||
DefaultTTL = 50 // seconds
|
DefaultTTL = 50 // seconds
|
||||||
SynchAllowance = 10 // seconds
|
DefaultSyncAllowance = 10 // seconds
|
||||||
)
|
)
|
||||||
|
|
||||||
type unknownVersionError uint64
|
type unknownVersionError uint64
|
||||||
|
|
|
||||||
|
|
@ -36,64 +36,44 @@ import (
|
||||||
// Envelope represents a clear-text data packet to transmit through the Whisper
|
// Envelope represents a clear-text data packet to transmit through the Whisper
|
||||||
// network. Its contents may or may not be encrypted and signed.
|
// network. Its contents may or may not be encrypted and signed.
|
||||||
type Envelope struct {
|
type Envelope struct {
|
||||||
Version []byte
|
|
||||||
Expiry uint32
|
Expiry uint32
|
||||||
TTL uint32
|
TTL uint32
|
||||||
Topic TopicType
|
Topic TopicType
|
||||||
AESNonce []byte
|
|
||||||
Data []byte
|
Data []byte
|
||||||
EnvNonce uint64
|
Nonce uint64
|
||||||
|
|
||||||
pow float64 // Message-specific PoW as described in the Whisper specification.
|
pow float64 // Message-specific PoW as described in the Whisper specification.
|
||||||
|
|
||||||
|
// the following variables should not be accessed directly, use the corresponding function instead: Hash(), Bloom()
|
||||||
hash common.Hash // Cached hash of the envelope to avoid rehashing every time.
|
hash common.Hash // Cached hash of the envelope to avoid rehashing every time.
|
||||||
// Don't access hash directly, use Hash() function instead.
|
bloom []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// size returns the size of envelope as it is sent (i.e. public fields only)
|
// size returns the size of envelope as it is sent (i.e. public fields only)
|
||||||
func (e *Envelope) size() int {
|
func (e *Envelope) size() int {
|
||||||
return 20 + len(e.Version) + len(e.AESNonce) + len(e.Data)
|
return EnvelopeHeaderLength + len(e.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce.
|
// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce.
|
||||||
func (e *Envelope) rlpWithoutNonce() []byte {
|
func (e *Envelope) rlpWithoutNonce() []byte {
|
||||||
res, _ := rlp.EncodeToBytes([]interface{}{e.Version, e.Expiry, e.TTL, e.Topic, e.AESNonce, e.Data})
|
res, _ := rlp.EncodeToBytes([]interface{}{e.Expiry, e.TTL, e.Topic, e.Data})
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEnvelope wraps a Whisper message with expiration and destination data
|
// NewEnvelope wraps a Whisper message with expiration and destination data
|
||||||
// included into an envelope for network forwarding.
|
// included into an envelope for network forwarding.
|
||||||
func NewEnvelope(ttl uint32, topic TopicType, aesNonce []byte, msg *sentMessage) *Envelope {
|
func NewEnvelope(ttl uint32, topic TopicType, msg *sentMessage) *Envelope {
|
||||||
env := Envelope{
|
env := Envelope{
|
||||||
Version: make([]byte, 1),
|
|
||||||
Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()),
|
Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()),
|
||||||
TTL: ttl,
|
TTL: ttl,
|
||||||
Topic: topic,
|
Topic: topic,
|
||||||
AESNonce: aesNonce,
|
|
||||||
Data: msg.Raw,
|
Data: msg.Raw,
|
||||||
EnvNonce: 0,
|
Nonce: 0,
|
||||||
}
|
|
||||||
|
|
||||||
if EnvelopeVersion < 256 {
|
|
||||||
env.Version[0] = byte(EnvelopeVersion)
|
|
||||||
} else {
|
|
||||||
panic("please increase the size of Envelope.Version before releasing this version")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &env
|
return &env
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Envelope) IsSymmetric() bool {
|
|
||||||
return len(e.AESNonce) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Envelope) isAsymmetric() bool {
|
|
||||||
return !e.IsSymmetric()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Envelope) Ver() uint64 {
|
|
||||||
return bytesToUintLittleEndian(e.Version)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seal closes the envelope by spending the requested amount of time as a proof
|
// Seal closes the envelope by spending the requested amount of time as a proof
|
||||||
// of work on hashing the data.
|
// of work on hashing the data.
|
||||||
func (e *Envelope) Seal(options *MessageParams) error {
|
func (e *Envelope) Seal(options *MessageParams) error {
|
||||||
|
|
@ -119,7 +99,7 @@ func (e *Envelope) Seal(options *MessageParams) error {
|
||||||
d := new(big.Int).SetBytes(crypto.Keccak256(buf))
|
d := new(big.Int).SetBytes(crypto.Keccak256(buf))
|
||||||
firstBit := math.FirstBitSet(d)
|
firstBit := math.FirstBitSet(d)
|
||||||
if firstBit > bestBit {
|
if firstBit > bestBit {
|
||||||
e.EnvNonce, bestBit = nonce, firstBit
|
e.Nonce, bestBit = nonce, firstBit
|
||||||
if target > 0 && bestBit >= target {
|
if target > 0 && bestBit >= target {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -135,6 +115,8 @@ func (e *Envelope) Seal(options *MessageParams) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PoW computes (if necessary) and returns the proof of work target
|
||||||
|
// of the envelope.
|
||||||
func (e *Envelope) PoW() float64 {
|
func (e *Envelope) PoW() float64 {
|
||||||
if e.pow == 0 {
|
if e.pow == 0 {
|
||||||
e.calculatePoW(0)
|
e.calculatePoW(0)
|
||||||
|
|
@ -146,7 +128,7 @@ func (e *Envelope) calculatePoW(diff uint32) {
|
||||||
buf := make([]byte, 64)
|
buf := make([]byte, 64)
|
||||||
h := crypto.Keccak256(e.rlpWithoutNonce())
|
h := crypto.Keccak256(e.rlpWithoutNonce())
|
||||||
copy(buf[:32], h)
|
copy(buf[:32], h)
|
||||||
binary.BigEndian.PutUint64(buf[56:], e.EnvNonce)
|
binary.BigEndian.PutUint64(buf[56:], e.Nonce)
|
||||||
d := new(big.Int).SetBytes(crypto.Keccak256(buf))
|
d := new(big.Int).SetBytes(crypto.Keccak256(buf))
|
||||||
firstBit := math.FirstBitSet(d)
|
firstBit := math.FirstBitSet(d)
|
||||||
x := gmath.Pow(2, float64(firstBit))
|
x := gmath.Pow(2, float64(firstBit))
|
||||||
|
|
@ -209,7 +191,7 @@ func (e *Envelope) OpenAsymmetric(key *ecdsa.PrivateKey) (*ReceivedMessage, erro
|
||||||
// OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
|
// OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
|
||||||
func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) {
|
func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) {
|
||||||
msg = &ReceivedMessage{Raw: e.Data}
|
msg = &ReceivedMessage{Raw: e.Data}
|
||||||
err = msg.decryptSymmetric(key, e.AESNonce)
|
err = msg.decryptSymmetric(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg = nil
|
msg = nil
|
||||||
}
|
}
|
||||||
|
|
@ -218,12 +200,17 @@ func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) {
|
||||||
|
|
||||||
// Open tries to decrypt an envelope, and populates the message fields in case of success.
|
// Open tries to decrypt an envelope, and populates the message fields in case of success.
|
||||||
func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) {
|
func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) {
|
||||||
if e.isAsymmetric() {
|
// The API interface forbids filters doing both symmetric and asymmetric encryption.
|
||||||
|
if watcher.expectsAsymmetricEncryption() && watcher.expectsSymmetricEncryption() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if watcher.expectsAsymmetricEncryption() {
|
||||||
msg, _ = e.OpenAsymmetric(watcher.KeyAsym)
|
msg, _ = e.OpenAsymmetric(watcher.KeyAsym)
|
||||||
if msg != nil {
|
if msg != nil {
|
||||||
msg.Dst = &watcher.KeyAsym.PublicKey
|
msg.Dst = &watcher.KeyAsym.PublicKey
|
||||||
}
|
}
|
||||||
} else if e.IsSymmetric() {
|
} else if watcher.expectsSymmetricEncryption() {
|
||||||
msg, _ = e.OpenSymmetric(watcher.KeySym)
|
msg, _ = e.OpenSymmetric(watcher.KeySym)
|
||||||
if msg != nil {
|
if msg != nil {
|
||||||
msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
|
msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
|
||||||
|
|
@ -231,7 +218,7 @@ func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg != nil {
|
if msg != nil {
|
||||||
ok := msg.Validate()
|
ok := msg.ValidateAndParse()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -240,7 +227,33 @@ func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) {
|
||||||
msg.TTL = e.TTL
|
msg.TTL = e.TTL
|
||||||
msg.Sent = e.Expiry - e.TTL
|
msg.Sent = e.Expiry - e.TTL
|
||||||
msg.EnvelopeHash = e.Hash()
|
msg.EnvelopeHash = e.Hash()
|
||||||
msg.EnvelopeVersion = e.Ver()
|
|
||||||
}
|
}
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bloom maps 4-bytes Topic into 64-byte bloom filter with 3 bits set (at most).
|
||||||
|
func (e *Envelope) Bloom() []byte {
|
||||||
|
if e.bloom == nil {
|
||||||
|
e.bloom = TopicToBloom(e.Topic)
|
||||||
|
}
|
||||||
|
return e.bloom
|
||||||
|
}
|
||||||
|
|
||||||
|
// TopicToBloom converts the topic (4 bytes) to the bloom filter (64 bytes)
|
||||||
|
func TopicToBloom(topic TopicType) []byte {
|
||||||
|
b := make([]byte, bloomFilterSize)
|
||||||
|
var index [3]int
|
||||||
|
for j := 0; j < 3; j++ {
|
||||||
|
index[j] = int(topic[j])
|
||||||
|
if (topic[3] & (1 << uint(j))) != 0 {
|
||||||
|
index[j] += 256
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < 3; j++ {
|
||||||
|
byteIndex := index[j] / 8
|
||||||
|
bitIndex := index[j] % 8
|
||||||
|
b[byteIndex] = (1 << uint(bitIndex))
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
|
||||||
64
whisper/whisperv6/envelope_test.go
Normal file
64
whisper/whisperv6/envelope_test.go
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright 2017 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/>.
|
||||||
|
|
||||||
|
// Contains the tests associated with the Whisper protocol Envelope object.
|
||||||
|
|
||||||
|
package whisperv6
|
||||||
|
|
||||||
|
import (
|
||||||
|
mrand "math/rand"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEnvelopeOpenAcceptsOnlyOneKeyTypeInFilter(t *testing.T) {
|
||||||
|
symKey := make([]byte, aesKeyLength)
|
||||||
|
mrand.Read(symKey)
|
||||||
|
|
||||||
|
asymKey, err := crypto.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
params := MessageParams{
|
||||||
|
PoW: 0.01,
|
||||||
|
WorkTime: 1,
|
||||||
|
TTL: uint32(mrand.Intn(1024)),
|
||||||
|
Payload: make([]byte, 50),
|
||||||
|
KeySym: symKey,
|
||||||
|
Dst: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
mrand.Read(params.Payload)
|
||||||
|
|
||||||
|
msg, err := newSentMessage(¶ms)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e, err := msg.Wrap(¶ms)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to Wrap the message in an envelope with seed %d: %s", seed, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f := Filter{KeySym: symKey, KeyAsym: asymKey}
|
||||||
|
|
||||||
|
decrypted := e.Open(&f)
|
||||||
|
if decrypted != nil {
|
||||||
|
t.Fatalf("Managed to decrypt a message with an invalid filter, seed %d", seed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Filter represents a Whisper message filter
|
||||||
type Filter struct {
|
type Filter struct {
|
||||||
Src *ecdsa.PublicKey // Sender of the message
|
Src *ecdsa.PublicKey // Sender of the message
|
||||||
KeyAsym *ecdsa.PrivateKey // Private Key of recipient
|
KeyAsym *ecdsa.PrivateKey // Private Key of recipient
|
||||||
|
|
@ -39,12 +40,14 @@ type Filter struct {
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filters represents a collection of filters
|
||||||
type Filters struct {
|
type Filters struct {
|
||||||
watchers map[string]*Filter
|
watchers map[string]*Filter
|
||||||
whisper *Whisper
|
whisper *Whisper
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewFilters returns a newly created filter collection
|
||||||
func NewFilters(w *Whisper) *Filters {
|
func NewFilters(w *Whisper) *Filters {
|
||||||
return &Filters{
|
return &Filters{
|
||||||
watchers: make(map[string]*Filter),
|
watchers: make(map[string]*Filter),
|
||||||
|
|
@ -52,7 +55,12 @@ func NewFilters(w *Whisper) *Filters {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Install will add a new filter to the filter collection
|
||||||
func (fs *Filters) Install(watcher *Filter) (string, error) {
|
func (fs *Filters) Install(watcher *Filter) (string, error) {
|
||||||
|
if watcher.KeySym != nil && watcher.KeyAsym != nil {
|
||||||
|
return "", fmt.Errorf("filters must choose between symmetric and asymmetric keys")
|
||||||
|
}
|
||||||
|
|
||||||
if watcher.Messages == nil {
|
if watcher.Messages == nil {
|
||||||
watcher.Messages = make(map[common.Hash]*ReceivedMessage)
|
watcher.Messages = make(map[common.Hash]*ReceivedMessage)
|
||||||
}
|
}
|
||||||
|
|
@ -77,6 +85,8 @@ func (fs *Filters) Install(watcher *Filter) (string, error) {
|
||||||
return id, err
|
return id, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uninstall will remove a filter whose id has been specified from
|
||||||
|
// the filter collection
|
||||||
func (fs *Filters) Uninstall(id string) bool {
|
func (fs *Filters) Uninstall(id string) bool {
|
||||||
fs.mutex.Lock()
|
fs.mutex.Lock()
|
||||||
defer fs.mutex.Unlock()
|
defer fs.mutex.Unlock()
|
||||||
|
|
@ -87,12 +97,15 @@ func (fs *Filters) Uninstall(id string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get returns a filter from the collection with a specific ID
|
||||||
func (fs *Filters) Get(id string) *Filter {
|
func (fs *Filters) Get(id string) *Filter {
|
||||||
fs.mutex.RLock()
|
fs.mutex.RLock()
|
||||||
defer fs.mutex.RUnlock()
|
defer fs.mutex.RUnlock()
|
||||||
return fs.watchers[id]
|
return fs.watchers[id]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotifyWatchers notifies any filter that has declared interest
|
||||||
|
// for the envelope's topic.
|
||||||
func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
|
func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
|
||||||
var msg *ReceivedMessage
|
var msg *ReceivedMessage
|
||||||
|
|
||||||
|
|
@ -136,9 +149,9 @@ func (f *Filter) processEnvelope(env *Envelope) *ReceivedMessage {
|
||||||
msg := env.Open(f)
|
msg := env.Open(f)
|
||||||
if msg != nil {
|
if msg != nil {
|
||||||
return msg
|
return msg
|
||||||
} else {
|
|
||||||
log.Trace("processing envelope: failed to open", "hash", env.Hash().Hex())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Trace("processing envelope: failed to open", "hash", env.Hash().Hex())
|
||||||
} else {
|
} else {
|
||||||
log.Trace("processing envelope: does not match", "hash", env.Hash().Hex())
|
log.Trace("processing envelope: does not match", "hash", env.Hash().Hex())
|
||||||
}
|
}
|
||||||
|
|
@ -153,6 +166,8 @@ func (f *Filter) expectsSymmetricEncryption() bool {
|
||||||
return f.KeySym != nil
|
return f.KeySym != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trigger adds a yet-unknown message to the filter's list of
|
||||||
|
// received messages.
|
||||||
func (f *Filter) Trigger(msg *ReceivedMessage) {
|
func (f *Filter) Trigger(msg *ReceivedMessage) {
|
||||||
f.mutex.Lock()
|
f.mutex.Lock()
|
||||||
defer f.mutex.Unlock()
|
defer f.mutex.Unlock()
|
||||||
|
|
@ -162,6 +177,8 @@ func (f *Filter) Trigger(msg *ReceivedMessage) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve will return the list of all received messages associated
|
||||||
|
// to a filter.
|
||||||
func (f *Filter) Retrieve() (all []*ReceivedMessage) {
|
func (f *Filter) Retrieve() (all []*ReceivedMessage) {
|
||||||
f.mutex.Lock()
|
f.mutex.Lock()
|
||||||
defer f.mutex.Unlock()
|
defer f.mutex.Unlock()
|
||||||
|
|
@ -175,6 +192,9 @@ func (f *Filter) Retrieve() (all []*ReceivedMessage) {
|
||||||
return all
|
return all
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MatchMessage checks if the filter matches an already decrypted
|
||||||
|
// message (i.e. a Message that has already been handled by
|
||||||
|
// MatchEnvelope when checked by a previous filter)
|
||||||
func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
|
func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
|
||||||
if f.PoW > 0 && msg.PoW < f.PoW {
|
if f.PoW > 0 && msg.PoW < f.PoW {
|
||||||
return false
|
return false
|
||||||
|
|
@ -188,19 +208,18 @@ func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MatchEnvelope checks if it's worth decrypting the message. If
|
||||||
|
// it returns `true`, client code is expected to attempt decrypting
|
||||||
|
// the message and subsequently call MatchMessage.
|
||||||
func (f *Filter) MatchEnvelope(envelope *Envelope) bool {
|
func (f *Filter) MatchEnvelope(envelope *Envelope) bool {
|
||||||
if f.PoW > 0 && envelope.pow < f.PoW {
|
if f.PoW > 0 && envelope.pow < f.PoW {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.expectsAsymmetricEncryption() && envelope.isAsymmetric() {
|
|
||||||
return f.MatchTopic(envelope.Topic)
|
return f.MatchTopic(envelope.Topic)
|
||||||
} else if f.expectsSymmetricEncryption() && envelope.IsSymmetric() {
|
|
||||||
return f.MatchTopic(envelope.Topic)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MatchTopic checks that the filter captures a given topic.
|
||||||
func (f *Filter) MatchTopic(topic TopicType) bool {
|
func (f *Filter) MatchTopic(topic TopicType) bool {
|
||||||
if len(f.Topics) == 0 {
|
if len(f.Topics) == 0 {
|
||||||
// any topic matches
|
// any topic matches
|
||||||
|
|
@ -216,8 +235,12 @@ func (f *Filter) MatchTopic(topic TopicType) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchSingleTopic(topic TopicType, bt []byte) bool {
|
func matchSingleTopic(topic TopicType, bt []byte) bool {
|
||||||
if len(bt) > 4 {
|
if len(bt) > TopicLength {
|
||||||
bt = bt[:4]
|
bt = bt[:TopicLength]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(bt) < TopicLength {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for j, b := range bt {
|
for j, b := range bt {
|
||||||
|
|
@ -228,6 +251,7 @@ func matchSingleTopic(topic TopicType, bt []byte) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPubKeyEqual checks that two public keys are equal
|
||||||
func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool {
|
func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool {
|
||||||
if !ValidatePublicKey(a) {
|
if !ValidatePublicKey(a) {
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
|
|
@ -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()&int(1) == 0
|
||||||
}
|
}
|
||||||
return cases
|
return cases
|
||||||
}
|
}
|
||||||
|
|
@ -109,7 +109,7 @@ func TestInstallFilters(t *testing.T) {
|
||||||
t.Fatalf("seed %d: failed to install filter: %s", seed, err)
|
t.Fatalf("seed %d: failed to install filter: %s", seed, err)
|
||||||
}
|
}
|
||||||
tst[i].id = j
|
tst[i].id = j
|
||||||
if len(j) != keyIdSize*2 {
|
if len(j) != keyIDSize*2 {
|
||||||
t.Fatalf("seed %d: wrong filter id size [%d]", seed, len(j))
|
t.Fatalf("seed %d: wrong filter id size [%d]", seed, len(j))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +122,7 @@ func TestInstallFilters(t *testing.T) {
|
||||||
|
|
||||||
for i, testCase := range tst {
|
for i, testCase := range tst {
|
||||||
fil := filters.Get(testCase.id)
|
fil := filters.Get(testCase.id)
|
||||||
exist := (fil != nil)
|
exist := fil != nil
|
||||||
if exist != testCase.alive {
|
if exist != testCase.alive {
|
||||||
t.Fatalf("seed %d: failed alive: %d, %v, %v", seed, i, exist, testCase.alive)
|
t.Fatalf("seed %d: failed alive: %d, %v, %v", seed, i, exist, testCase.alive)
|
||||||
}
|
}
|
||||||
|
|
@ -199,7 +199,7 @@ func TestInstallIdenticalFilters(t *testing.T) {
|
||||||
filter1.Src = ¶ms.Src.PublicKey
|
filter1.Src = ¶ms.Src.PublicKey
|
||||||
filter2.Src = ¶ms.Src.PublicKey
|
filter2.Src = ¶ms.Src.PublicKey
|
||||||
|
|
||||||
sentMessage, err := NewSentMessage(params)
|
sentMessage, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -229,6 +229,36 @@ func TestInstallIdenticalFilters(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInstallFilterWithSymAndAsymKeys(t *testing.T) {
|
||||||
|
InitSingleTest()
|
||||||
|
|
||||||
|
w := New(&Config{})
|
||||||
|
filters := NewFilters(w)
|
||||||
|
filter1, _ := generateFilter(t, true)
|
||||||
|
|
||||||
|
asymKey, err := crypto.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to create asymetric keys: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the first filter since some of its fields
|
||||||
|
// are randomly gnerated.
|
||||||
|
filter := &Filter{
|
||||||
|
KeySym: filter1.KeySym,
|
||||||
|
KeyAsym: asymKey,
|
||||||
|
Topics: filter1.Topics,
|
||||||
|
PoW: filter1.PoW,
|
||||||
|
AllowP2P: filter1.AllowP2P,
|
||||||
|
Messages: make(map[common.Hash]*ReceivedMessage),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = filters.Install(filter)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Error detecting that a filter had both an asymmetric and symmetric key, with seed %d", seed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestComparePubKey(t *testing.T) {
|
func TestComparePubKey(t *testing.T) {
|
||||||
InitSingleTest()
|
InitSingleTest()
|
||||||
|
|
||||||
|
|
@ -276,7 +306,7 @@ func TestMatchEnvelope(t *testing.T) {
|
||||||
params.Topic[0] = 0xFF // ensure mismatch
|
params.Topic[0] = 0xFF // ensure mismatch
|
||||||
|
|
||||||
// mismatch with pseudo-random data
|
// mismatch with pseudo-random data
|
||||||
msg, err := NewSentMessage(params)
|
msg, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -297,7 +327,7 @@ func TestMatchEnvelope(t *testing.T) {
|
||||||
i := mrand.Int() % 4
|
i := mrand.Int() % 4
|
||||||
fsym.Topics[i] = params.Topic[:]
|
fsym.Topics[i] = params.Topic[:]
|
||||||
fasym.Topics[i] = params.Topic[:]
|
fasym.Topics[i] = params.Topic[:]
|
||||||
msg, err = NewSentMessage(params)
|
msg, err = newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -312,12 +342,6 @@ func TestMatchEnvelope(t *testing.T) {
|
||||||
t.Fatalf("failed MatchEnvelope() symmetric with seed %d.", seed)
|
t.Fatalf("failed MatchEnvelope() symmetric with seed %d.", seed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// asymmetric + matching topic: mismatch
|
|
||||||
match = fasym.MatchEnvelope(env)
|
|
||||||
if match {
|
|
||||||
t.Fatalf("failed MatchEnvelope() asymmetric with seed %d.", seed)
|
|
||||||
}
|
|
||||||
|
|
||||||
// symmetric + matching topic + insufficient PoW: mismatch
|
// symmetric + matching topic + insufficient PoW: mismatch
|
||||||
fsym.PoW = env.PoW() + 1.0
|
fsym.PoW = env.PoW() + 1.0
|
||||||
match = fsym.MatchEnvelope(env)
|
match = fsym.MatchEnvelope(env)
|
||||||
|
|
@ -348,7 +372,7 @@ func TestMatchEnvelope(t *testing.T) {
|
||||||
}
|
}
|
||||||
params.KeySym = nil
|
params.KeySym = nil
|
||||||
params.Dst = &key.PublicKey
|
params.Dst = &key.PublicKey
|
||||||
msg, err = NewSentMessage(params)
|
msg, err = newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -429,7 +453,7 @@ func TestMatchMessageSym(t *testing.T) {
|
||||||
params.KeySym = f.KeySym
|
params.KeySym = f.KeySym
|
||||||
params.Topic = BytesToTopic(f.Topics[index])
|
params.Topic = BytesToTopic(f.Topics[index])
|
||||||
|
|
||||||
sentMessage, err := NewSentMessage(params)
|
sentMessage, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -522,7 +546,7 @@ func TestMatchMessageAsym(t *testing.T) {
|
||||||
keySymOrig := params.KeySym
|
keySymOrig := params.KeySym
|
||||||
params.KeySym = nil
|
params.KeySym = nil
|
||||||
|
|
||||||
sentMessage, err := NewSentMessage(params)
|
sentMessage, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -606,7 +630,7 @@ func generateCompatibeEnvelope(t *testing.T, f *Filter) *Envelope {
|
||||||
|
|
||||||
params.KeySym = f.KeySym
|
params.KeySym = f.KeySym
|
||||||
params.Topic = BytesToTopic(f.Topics[2])
|
params.Topic = BytesToTopic(f.Topics[2])
|
||||||
sentMessage, err := NewSentMessage(params)
|
sentMessage, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -776,12 +800,13 @@ func TestWatchers(t *testing.T) {
|
||||||
func TestVariableTopics(t *testing.T) {
|
func TestVariableTopics(t *testing.T) {
|
||||||
InitSingleTest()
|
InitSingleTest()
|
||||||
|
|
||||||
|
const lastTopicByte = 3
|
||||||
var match bool
|
var match bool
|
||||||
params, err := generateMessageParams()
|
params, err := generateMessageParams()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
msg, err := NewSentMessage(params)
|
msg, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -796,19 +821,52 @@ func TestVariableTopics(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
arr := make([]byte, i+1, 4)
|
env.Topic = BytesToTopic(f.Topics[i])
|
||||||
copy(arr, env.Topic[:i+1])
|
|
||||||
|
|
||||||
f.Topics[4] = arr
|
|
||||||
match = f.MatchEnvelope(env)
|
match = f.MatchEnvelope(env)
|
||||||
if !match {
|
if !match {
|
||||||
t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed, i)
|
t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
f.Topics[4][i]++
|
f.Topics[i][lastTopicByte]++
|
||||||
match = f.MatchEnvelope(env)
|
match = f.MatchEnvelope(env)
|
||||||
if match {
|
if match {
|
||||||
t.Fatalf("MatchEnvelope symmetric with seed %d, step %d: false positive.", seed, i)
|
t.Fatalf("MatchEnvelope symmetric with seed %d, step %d: false positive.", seed, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMatchSingleTopic_ReturnTrue(t *testing.T) {
|
||||||
|
bt := []byte("test")
|
||||||
|
topic := BytesToTopic(bt)
|
||||||
|
|
||||||
|
if !matchSingleTopic(topic, bt) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMatchSingleTopic_WithTail_ReturnTrue(t *testing.T) {
|
||||||
|
bt := []byte("test with tail")
|
||||||
|
topic := BytesToTopic([]byte("test"))
|
||||||
|
|
||||||
|
if !matchSingleTopic(topic, bt) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMatchSingleTopic_NotEquals_ReturnFalse(t *testing.T) {
|
||||||
|
bt := []byte("tes")
|
||||||
|
topic := BytesToTopic(bt)
|
||||||
|
|
||||||
|
if matchSingleTopic(topic, bt) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMatchSingleTopic_InsufficientLength_ReturnFalse(t *testing.T) {
|
||||||
|
bt := []byte("test")
|
||||||
|
topic := BytesToTopic([]byte("not_equal"))
|
||||||
|
|
||||||
|
if matchSingleTopic(topic, bt) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
var _ = (*criteriaOverride)(nil)
|
var _ = (*criteriaOverride)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals type Criteria to a json string
|
||||||
func (c Criteria) MarshalJSON() ([]byte, error) {
|
func (c Criteria) MarshalJSON() ([]byte, error) {
|
||||||
type Criteria struct {
|
type Criteria struct {
|
||||||
SymKeyID string `json:"symKeyID"`
|
SymKeyID string `json:"symKeyID"`
|
||||||
|
|
@ -29,11 +30,12 @@ func (c Criteria) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals type Criteria to a json string
|
||||||
func (c *Criteria) UnmarshalJSON(input []byte) error {
|
func (c *Criteria) UnmarshalJSON(input []byte) error {
|
||||||
type Criteria struct {
|
type Criteria struct {
|
||||||
SymKeyID *string `json:"symKeyID"`
|
SymKeyID *string `json:"symKeyID"`
|
||||||
PrivateKeyID *string `json:"privateKeyID"`
|
PrivateKeyID *string `json:"privateKeyID"`
|
||||||
Sig hexutil.Bytes `json:"sig"`
|
Sig *hexutil.Bytes `json:"sig"`
|
||||||
MinPow *float64 `json:"minPow"`
|
MinPow *float64 `json:"minPow"`
|
||||||
Topics []TopicType `json:"topics"`
|
Topics []TopicType `json:"topics"`
|
||||||
AllowP2P *bool `json:"allowP2P"`
|
AllowP2P *bool `json:"allowP2P"`
|
||||||
|
|
@ -49,7 +51,7 @@ func (c *Criteria) UnmarshalJSON(input []byte) error {
|
||||||
c.PrivateKeyID = *dec.PrivateKeyID
|
c.PrivateKeyID = *dec.PrivateKeyID
|
||||||
}
|
}
|
||||||
if dec.Sig != nil {
|
if dec.Sig != nil {
|
||||||
c.Sig = dec.Sig
|
c.Sig = *dec.Sig
|
||||||
}
|
}
|
||||||
if dec.MinPow != nil {
|
if dec.MinPow != nil {
|
||||||
c.MinPow = *dec.MinPow
|
c.MinPow = *dec.MinPow
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
var _ = (*messageOverride)(nil)
|
var _ = (*messageOverride)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals type Message to a json string
|
||||||
func (m Message) MarshalJSON() ([]byte, error) {
|
func (m Message) MarshalJSON() ([]byte, error) {
|
||||||
type Message struct {
|
type Message struct {
|
||||||
Sig hexutil.Bytes `json:"sig,omitempty"`
|
Sig hexutil.Bytes `json:"sig,omitempty"`
|
||||||
|
|
@ -35,24 +36,25 @@ func (m Message) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals type Message to a json string
|
||||||
func (m *Message) UnmarshalJSON(input []byte) error {
|
func (m *Message) UnmarshalJSON(input []byte) error {
|
||||||
type Message struct {
|
type Message struct {
|
||||||
Sig hexutil.Bytes `json:"sig,omitempty"`
|
Sig *hexutil.Bytes `json:"sig,omitempty"`
|
||||||
TTL *uint32 `json:"ttl"`
|
TTL *uint32 `json:"ttl"`
|
||||||
Timestamp *uint32 `json:"timestamp"`
|
Timestamp *uint32 `json:"timestamp"`
|
||||||
Topic *TopicType `json:"topic"`
|
Topic *TopicType `json:"topic"`
|
||||||
Payload hexutil.Bytes `json:"payload"`
|
Payload *hexutil.Bytes `json:"payload"`
|
||||||
Padding hexutil.Bytes `json:"padding"`
|
Padding *hexutil.Bytes `json:"padding"`
|
||||||
PoW *float64 `json:"pow"`
|
PoW *float64 `json:"pow"`
|
||||||
Hash hexutil.Bytes `json:"hash"`
|
Hash *hexutil.Bytes `json:"hash"`
|
||||||
Dst hexutil.Bytes `json:"recipientPublicKey,omitempty"`
|
Dst *hexutil.Bytes `json:"recipientPublicKey,omitempty"`
|
||||||
}
|
}
|
||||||
var dec Message
|
var dec Message
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if dec.Sig != nil {
|
if dec.Sig != nil {
|
||||||
m.Sig = dec.Sig
|
m.Sig = *dec.Sig
|
||||||
}
|
}
|
||||||
if dec.TTL != nil {
|
if dec.TTL != nil {
|
||||||
m.TTL = *dec.TTL
|
m.TTL = *dec.TTL
|
||||||
|
|
@ -64,19 +66,19 @@ func (m *Message) UnmarshalJSON(input []byte) error {
|
||||||
m.Topic = *dec.Topic
|
m.Topic = *dec.Topic
|
||||||
}
|
}
|
||||||
if dec.Payload != nil {
|
if dec.Payload != nil {
|
||||||
m.Payload = dec.Payload
|
m.Payload = *dec.Payload
|
||||||
}
|
}
|
||||||
if dec.Padding != nil {
|
if dec.Padding != nil {
|
||||||
m.Padding = dec.Padding
|
m.Padding = *dec.Padding
|
||||||
}
|
}
|
||||||
if dec.PoW != nil {
|
if dec.PoW != nil {
|
||||||
m.PoW = *dec.PoW
|
m.PoW = *dec.PoW
|
||||||
}
|
}
|
||||||
if dec.Hash != nil {
|
if dec.Hash != nil {
|
||||||
m.Hash = dec.Hash
|
m.Hash = *dec.Hash
|
||||||
}
|
}
|
||||||
if dec.Dst != nil {
|
if dec.Dst != nil {
|
||||||
m.Dst = dec.Dst
|
m.Dst = *dec.Dst
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
var _ = (*newMessageOverride)(nil)
|
var _ = (*newMessageOverride)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals type NewMessage to a json string
|
||||||
func (n NewMessage) MarshalJSON() ([]byte, error) {
|
func (n NewMessage) MarshalJSON() ([]byte, error) {
|
||||||
type NewMessage struct {
|
type NewMessage struct {
|
||||||
SymKeyID string `json:"symKeyID"`
|
SymKeyID string `json:"symKeyID"`
|
||||||
|
|
@ -37,15 +38,16 @@ func (n NewMessage) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals type NewMessage to a json string
|
||||||
func (n *NewMessage) UnmarshalJSON(input []byte) error {
|
func (n *NewMessage) UnmarshalJSON(input []byte) error {
|
||||||
type NewMessage struct {
|
type NewMessage struct {
|
||||||
SymKeyID *string `json:"symKeyID"`
|
SymKeyID *string `json:"symKeyID"`
|
||||||
PublicKey hexutil.Bytes `json:"pubKey"`
|
PublicKey *hexutil.Bytes `json:"pubKey"`
|
||||||
Sig *string `json:"sig"`
|
Sig *string `json:"sig"`
|
||||||
TTL *uint32 `json:"ttl"`
|
TTL *uint32 `json:"ttl"`
|
||||||
Topic *TopicType `json:"topic"`
|
Topic *TopicType `json:"topic"`
|
||||||
Payload hexutil.Bytes `json:"payload"`
|
Payload *hexutil.Bytes `json:"payload"`
|
||||||
Padding hexutil.Bytes `json:"padding"`
|
Padding *hexutil.Bytes `json:"padding"`
|
||||||
PowTime *uint32 `json:"powTime"`
|
PowTime *uint32 `json:"powTime"`
|
||||||
PowTarget *float64 `json:"powTarget"`
|
PowTarget *float64 `json:"powTarget"`
|
||||||
TargetPeer *string `json:"targetPeer"`
|
TargetPeer *string `json:"targetPeer"`
|
||||||
|
|
@ -58,7 +60,7 @@ func (n *NewMessage) UnmarshalJSON(input []byte) error {
|
||||||
n.SymKeyID = *dec.SymKeyID
|
n.SymKeyID = *dec.SymKeyID
|
||||||
}
|
}
|
||||||
if dec.PublicKey != nil {
|
if dec.PublicKey != nil {
|
||||||
n.PublicKey = dec.PublicKey
|
n.PublicKey = *dec.PublicKey
|
||||||
}
|
}
|
||||||
if dec.Sig != nil {
|
if dec.Sig != nil {
|
||||||
n.Sig = *dec.Sig
|
n.Sig = *dec.Sig
|
||||||
|
|
@ -70,10 +72,10 @@ func (n *NewMessage) UnmarshalJSON(input []byte) error {
|
||||||
n.Topic = *dec.Topic
|
n.Topic = *dec.Topic
|
||||||
}
|
}
|
||||||
if dec.Payload != nil {
|
if dec.Payload != nil {
|
||||||
n.Payload = dec.Payload
|
n.Payload = *dec.Payload
|
||||||
}
|
}
|
||||||
if dec.Padding != nil {
|
if dec.Padding != nil {
|
||||||
n.Padding = dec.Padding
|
n.Padding = *dec.Padding
|
||||||
}
|
}
|
||||||
if dec.PowTime != nil {
|
if dec.PowTime != nil {
|
||||||
n.PowTime = *dec.PowTime
|
n.PowTime = *dec.PowTime
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
mrand "math/rand"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -33,7 +34,8 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options specifies the exact way a message should be wrapped into an Envelope.
|
// MessageParams specifies the exact way a message should be wrapped
|
||||||
|
// into an Envelope.
|
||||||
type MessageParams struct {
|
type MessageParams struct {
|
||||||
TTL uint32
|
TTL uint32
|
||||||
Src *ecdsa.PrivateKey
|
Src *ecdsa.PrivateKey
|
||||||
|
|
@ -54,13 +56,14 @@ type sentMessage struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReceivedMessage represents a data packet to be received through the
|
// ReceivedMessage represents a data packet to be received through the
|
||||||
// Whisper protocol.
|
// Whisper protocol and successfully decrypted.
|
||||||
type ReceivedMessage struct {
|
type ReceivedMessage struct {
|
||||||
Raw []byte
|
Raw []byte
|
||||||
|
|
||||||
Payload []byte
|
Payload []byte
|
||||||
Padding []byte
|
Padding []byte
|
||||||
Signature []byte
|
Signature []byte
|
||||||
|
Salt []byte
|
||||||
|
|
||||||
PoW float64 // Proof of work as described in the Whisper spec
|
PoW float64 // Proof of work as described in the Whisper spec
|
||||||
Sent uint32 // Time when the message was posted into the network
|
Sent uint32 // Time when the message was posted into the network
|
||||||
|
|
@ -69,9 +72,8 @@ type ReceivedMessage struct {
|
||||||
Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message)
|
Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message)
|
||||||
Topic TopicType
|
Topic TopicType
|
||||||
|
|
||||||
SymKeyHash common.Hash // The Keccak256Hash of the key, associated with the Topic
|
SymKeyHash common.Hash // The Keccak256Hash of the key
|
||||||
EnvelopeHash common.Hash // Message envelope hash to act as a unique id
|
EnvelopeHash common.Hash // Message envelope hash to act as a unique id
|
||||||
EnvelopeVersion uint64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isMessageSigned(flags byte) bool {
|
func isMessageSigned(flags byte) bool {
|
||||||
|
|
@ -86,79 +88,62 @@ func (msg *ReceivedMessage) isAsymmetricEncryption() bool {
|
||||||
return msg.Dst != nil
|
return msg.Dst != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMessage creates and initializes a non-signed, non-encrypted Whisper message.
|
// NewSentMessage creates and initializes a non-signed, non-encrypted Whisper message.
|
||||||
func NewSentMessage(params *MessageParams) (*sentMessage, error) {
|
func newSentMessage(params *MessageParams) (*sentMessage, error) {
|
||||||
|
const payloadSizeFieldMaxSize = 4
|
||||||
msg := sentMessage{}
|
msg := sentMessage{}
|
||||||
msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Padding)+signatureLength+padSizeLimit)
|
msg.Raw = make([]byte, 1,
|
||||||
|
flagsLength+payloadSizeFieldMaxSize+len(params.Payload)+len(params.Padding)+signatureLength+padSizeLimit)
|
||||||
msg.Raw[0] = 0 // set all the flags to zero
|
msg.Raw[0] = 0 // set all the flags to zero
|
||||||
err := msg.appendPadding(params)
|
msg.addPayloadSizeField(params.Payload)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
msg.Raw = append(msg.Raw, params.Payload...)
|
msg.Raw = append(msg.Raw, params.Payload...)
|
||||||
return &msg, nil
|
err := msg.appendPadding(params)
|
||||||
|
return &msg, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSizeOfLength returns the number of bytes necessary to encode the entire size padding (including these bytes)
|
// addPayloadSizeField appends the auxiliary field containing the size of payload
|
||||||
func getSizeOfLength(b []byte) (sz int, err error) {
|
func (msg *sentMessage) addPayloadSizeField(payload []byte) {
|
||||||
sz = intSize(len(b)) // first iteration
|
fieldSize := getSizeOfPayloadSizeField(payload)
|
||||||
sz = intSize(len(b) + sz) // second iteration
|
field := make([]byte, 4)
|
||||||
if sz > 3 {
|
binary.LittleEndian.PutUint32(field, uint32(len(payload)))
|
||||||
err = errors.New("oversized padding parameter")
|
field = field[:fieldSize]
|
||||||
}
|
msg.Raw = append(msg.Raw, field...)
|
||||||
return sz, err
|
msg.Raw[0] |= byte(fieldSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// sizeOfIntSize returns minimal number of bytes necessary to encode an integer value
|
// getSizeOfPayloadSizeField returns the number of bytes necessary to encode the size of payload
|
||||||
func intSize(i int) (s int) {
|
func getSizeOfPayloadSizeField(payload []byte) int {
|
||||||
for s = 1; i >= 256; s++ {
|
s := 1
|
||||||
i /= 256
|
for i := len(payload); i >= 256; i /= 256 {
|
||||||
|
s++
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendPadding appends the pseudorandom padding bytes and sets the padding flag.
|
// appendPadding appends the padding specified in params.
|
||||||
// The last byte contains the size of padding (thus, its size must not exceed 256).
|
// If no padding is provided in params, then random padding is generated.
|
||||||
func (msg *sentMessage) appendPadding(params *MessageParams) error {
|
func (msg *sentMessage) appendPadding(params *MessageParams) error {
|
||||||
rawSize := len(params.Payload) + 1
|
if len(params.Padding) != 0 {
|
||||||
|
// padding data was provided by the Dapp, just use it as is
|
||||||
|
msg.Raw = append(msg.Raw, params.Padding...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rawSize := flagsLength + getSizeOfPayloadSizeField(params.Payload) + len(params.Payload)
|
||||||
if params.Src != nil {
|
if params.Src != nil {
|
||||||
rawSize += signatureLength
|
rawSize += signatureLength
|
||||||
}
|
}
|
||||||
odd := rawSize % padSizeLimit
|
odd := rawSize % padSizeLimit
|
||||||
|
paddingSize := padSizeLimit - odd
|
||||||
if len(params.Padding) != 0 {
|
pad := make([]byte, paddingSize)
|
||||||
padSize := len(params.Padding)
|
_, err := crand.Read(pad)
|
||||||
padLengthSize, err := getSizeOfLength(params.Padding)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
totalPadSize := padSize + padLengthSize
|
if !validateDataIntegrity(pad, paddingSize) {
|
||||||
buf := make([]byte, 8)
|
return errors.New("failed to generate random padding of size " + strconv.Itoa(paddingSize))
|
||||||
binary.LittleEndian.PutUint32(buf, uint32(totalPadSize))
|
|
||||||
buf = buf[:padLengthSize]
|
|
||||||
msg.Raw = append(msg.Raw, buf...)
|
|
||||||
msg.Raw = append(msg.Raw, params.Padding...)
|
|
||||||
msg.Raw[0] |= byte(padLengthSize) // number of bytes indicating the padding size
|
|
||||||
} else if odd != 0 {
|
|
||||||
totalPadSize := padSizeLimit - odd
|
|
||||||
if totalPadSize > 255 {
|
|
||||||
// this algorithm is only valid if padSizeLimit < 256.
|
|
||||||
// if padSizeLimit will ever change, please fix the algorithm
|
|
||||||
// (please see also ReceivedMessage.extractPadding() function).
|
|
||||||
panic("please fix the padding algorithm before releasing new version")
|
|
||||||
}
|
|
||||||
buf := make([]byte, totalPadSize)
|
|
||||||
_, err := crand.Read(buf[1:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if totalPadSize > 6 && !validateSymmetricKey(buf) {
|
|
||||||
return errors.New("failed to generate random padding of size " + strconv.Itoa(totalPadSize))
|
|
||||||
}
|
|
||||||
buf[0] = byte(totalPadSize)
|
|
||||||
msg.Raw = append(msg.Raw, buf...)
|
|
||||||
msg.Raw[0] |= byte(0x1) // number of bytes indicating the padding size
|
|
||||||
}
|
}
|
||||||
|
msg.Raw = append(msg.Raw, pad...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,11 +156,11 @@ func (msg *sentMessage) sign(key *ecdsa.PrivateKey) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.Raw[0] |= signatureFlag
|
msg.Raw[0] |= signatureFlag // it is important to set this flag before signing
|
||||||
hash := crypto.Keccak256(msg.Raw)
|
hash := crypto.Keccak256(msg.Raw)
|
||||||
signature, err := crypto.Sign(hash, key)
|
signature, err := crypto.Sign(hash, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg.Raw[0] &= ^signatureFlag // clear the flag
|
msg.Raw[0] &= (0xFF ^ signatureFlag) // clear the flag
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
msg.Raw = append(msg.Raw, signature...)
|
msg.Raw = append(msg.Raw, signature...)
|
||||||
|
|
@ -196,31 +181,56 @@ func (msg *sentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error {
|
||||||
|
|
||||||
// encryptSymmetric encrypts a message with a topic key, using AES-GCM-256.
|
// encryptSymmetric encrypts a message with a topic key, using AES-GCM-256.
|
||||||
// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
|
// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
|
||||||
func (msg *sentMessage) encryptSymmetric(key []byte) (nonce []byte, err error) {
|
func (msg *sentMessage) encryptSymmetric(key []byte) (err error) {
|
||||||
if !validateSymmetricKey(key) {
|
if !validateDataIntegrity(key, aesKeyLength) {
|
||||||
return nil, errors.New("invalid key provided for symmetric encryption")
|
return errors.New("invalid key provided for symmetric encryption, size: " + strconv.Itoa(len(key)))
|
||||||
}
|
}
|
||||||
|
|
||||||
block, err := aes.NewCipher(key)
|
block, err := aes.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
aesgcm, err := cipher.NewGCM(block)
|
aesgcm, err := cipher.NewGCM(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
salt, err := generateSecureRandomData(aesNonceLength) // never use more than 2^32 random nonces with a given key
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
encrypted := aesgcm.Seal(nil, salt, msg.Raw, nil)
|
||||||
|
msg.Raw = append(encrypted, salt...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// never use more than 2^32 random nonces with a given key
|
// generateSecureRandomData generates random data where extra security is required.
|
||||||
nonce = make([]byte, aesgcm.NonceSize())
|
// The purpose of this function is to prevent some bugs in software or in hardware
|
||||||
_, err = crand.Read(nonce)
|
// from delivering not-very-random data. This is especially useful for AES nonce,
|
||||||
|
// where true randomness does not really matter, but it is very important to have
|
||||||
|
// a unique nonce for every message.
|
||||||
|
func generateSecureRandomData(length int) ([]byte, error) {
|
||||||
|
x := make([]byte, length)
|
||||||
|
y := make([]byte, length)
|
||||||
|
res := make([]byte, length)
|
||||||
|
|
||||||
|
_, err := crand.Read(x)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !validateSymmetricKey(nonce) {
|
} else if !validateDataIntegrity(x, length) {
|
||||||
return nil, errors.New("crypto/rand failed to generate nonce")
|
return nil, errors.New("crypto/rand failed to generate secure random data")
|
||||||
}
|
}
|
||||||
|
_, err = mrand.Read(y)
|
||||||
msg.Raw = aesgcm.Seal(nil, nonce, msg.Raw, nil)
|
if err != nil {
|
||||||
return nonce, nil
|
return nil, err
|
||||||
|
} else if !validateDataIntegrity(y, length) {
|
||||||
|
return nil, errors.New("math/rand failed to generate secure random data")
|
||||||
|
}
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
res[i] = x[i] ^ y[i]
|
||||||
|
}
|
||||||
|
if !validateDataIntegrity(res, length) {
|
||||||
|
return nil, errors.New("failed to generate secure random data")
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap bundles the message into an Envelope to transmit over the network.
|
// Wrap bundles the message into an Envelope to transmit over the network.
|
||||||
|
|
@ -233,11 +243,10 @@ func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var nonce []byte
|
|
||||||
if options.Dst != nil {
|
if options.Dst != nil {
|
||||||
err = msg.encryptAsymmetric(options.Dst)
|
err = msg.encryptAsymmetric(options.Dst)
|
||||||
} else if options.KeySym != nil {
|
} else if options.KeySym != nil {
|
||||||
nonce, err = msg.encryptSymmetric(options.KeySym)
|
err = msg.encryptSymmetric(options.KeySym)
|
||||||
} else {
|
} else {
|
||||||
err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided")
|
err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided")
|
||||||
}
|
}
|
||||||
|
|
@ -245,7 +254,7 @@ func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
envelope = NewEnvelope(options.TTL, options.Topic, nonce, msg)
|
envelope = NewEnvelope(options.TTL, options.Topic, msg)
|
||||||
if err = envelope.Seal(options); err != nil {
|
if err = envelope.Seal(options); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -254,7 +263,13 @@ func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er
|
||||||
|
|
||||||
// decryptSymmetric decrypts a message with a topic key, using AES-GCM-256.
|
// decryptSymmetric decrypts a message with a topic key, using AES-GCM-256.
|
||||||
// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
|
// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
|
||||||
func (msg *ReceivedMessage) decryptSymmetric(key []byte, nonce []byte) error {
|
func (msg *ReceivedMessage) decryptSymmetric(key []byte) error {
|
||||||
|
// symmetric messages are expected to contain the 12-byte nonce at the end of the payload
|
||||||
|
if len(msg.Raw) < aesNonceLength {
|
||||||
|
return errors.New("missing salt or invalid payload in symmetric message")
|
||||||
|
}
|
||||||
|
salt := msg.Raw[len(msg.Raw)-aesNonceLength:]
|
||||||
|
|
||||||
block, err := aes.NewCipher(key)
|
block, err := aes.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -263,15 +278,12 @@ func (msg *ReceivedMessage) decryptSymmetric(key []byte, nonce []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(nonce) != aesgcm.NonceSize() {
|
decrypted, err := aesgcm.Open(nil, salt, msg.Raw[:len(msg.Raw)-aesNonceLength], nil)
|
||||||
log.Error("decrypting the message", "AES nonce size", len(nonce))
|
|
||||||
return errors.New("wrong AES nonce size")
|
|
||||||
}
|
|
||||||
decrypted, err := aesgcm.Open(nil, nonce, msg.Raw, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
msg.Raw = decrypted
|
msg.Raw = decrypted
|
||||||
|
msg.Salt = salt
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -284,8 +296,8 @@ func (msg *ReceivedMessage) decryptAsymmetric(key *ecdsa.PrivateKey) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate checks the validity and extracts the fields in case of success
|
// ValidateAndParse checks the message validity and extracts the fields in case of success.
|
||||||
func (msg *ReceivedMessage) Validate() bool {
|
func (msg *ReceivedMessage) ValidateAndParse() bool {
|
||||||
end := len(msg.Raw)
|
end := len(msg.Raw)
|
||||||
if end < 1 {
|
if end < 1 {
|
||||||
return false
|
return false
|
||||||
|
|
@ -296,41 +308,32 @@ func (msg *ReceivedMessage) Validate() bool {
|
||||||
if end <= 1 {
|
if end <= 1 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
msg.Signature = msg.Raw[end:]
|
msg.Signature = msg.Raw[end : end+signatureLength]
|
||||||
msg.Src = msg.SigToPubKey()
|
msg.Src = msg.SigToPubKey()
|
||||||
if msg.Src == nil {
|
if msg.Src == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
padSize, ok := msg.extractPadding(end)
|
beg := 1
|
||||||
if !ok {
|
payloadSize := 0
|
||||||
|
sizeOfPayloadSizeField := int(msg.Raw[0] & SizeMask) // number of bytes indicating the size of payload
|
||||||
|
if sizeOfPayloadSizeField != 0 {
|
||||||
|
payloadSize = int(bytesToUintLittleEndian(msg.Raw[beg : beg+sizeOfPayloadSizeField]))
|
||||||
|
if payloadSize+1 > end {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
beg += sizeOfPayloadSizeField
|
||||||
|
msg.Payload = msg.Raw[beg : beg+payloadSize]
|
||||||
|
}
|
||||||
|
|
||||||
msg.Payload = msg.Raw[1+padSize : end]
|
beg += payloadSize
|
||||||
|
msg.Padding = msg.Raw[beg:end]
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractPadding extracts the padding from raw message.
|
// SigToPubKey returns the public key associated to the message's
|
||||||
// although we don't support sending messages with padding size
|
// signature.
|
||||||
// exceeding 255 bytes, such messages are perfectly valid, and
|
|
||||||
// can be successfully decrypted.
|
|
||||||
func (msg *ReceivedMessage) extractPadding(end int) (int, bool) {
|
|
||||||
paddingSize := 0
|
|
||||||
sz := int(msg.Raw[0] & paddingMask) // number of bytes indicating the entire size of padding (including these bytes)
|
|
||||||
// could be zero -- it means no padding
|
|
||||||
if sz != 0 {
|
|
||||||
paddingSize = int(bytesToUintLittleEndian(msg.Raw[1 : 1+sz]))
|
|
||||||
if paddingSize < sz || paddingSize+1 > end {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
msg.Padding = msg.Raw[1+sz : 1+paddingSize]
|
|
||||||
}
|
|
||||||
return paddingSize, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recover retrieves the public key of the message signer.
|
|
||||||
func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey {
|
func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey {
|
||||||
defer func() { recover() }() // in case of invalid signature
|
defer func() { recover() }() // in case of invalid signature
|
||||||
|
|
||||||
|
|
@ -342,7 +345,7 @@ func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey {
|
||||||
return pub
|
return pub
|
||||||
}
|
}
|
||||||
|
|
||||||
// hash calculates the SHA3 checksum of the message flags, payload and padding.
|
// hash calculates the SHA3 checksum of the message flags, payload size field, payload and padding.
|
||||||
func (msg *ReceivedMessage) hash() []byte {
|
func (msg *ReceivedMessage) hash() []byte {
|
||||||
if isMessageSigned(msg.Raw[0]) {
|
if isMessageSigned(msg.Raw[0]) {
|
||||||
sz := len(msg.Raw) - signatureLength
|
sz := len(msg.Raw) - signatureLength
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,12 @@ package whisperv6
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
mrand "math/rand"
|
mrand "math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"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"
|
||||||
)
|
)
|
||||||
|
|
@ -70,7 +73,7 @@ func singleMessageTest(t *testing.T, symmetric bool) {
|
||||||
text := make([]byte, 0, 512)
|
text := make([]byte, 0, 512)
|
||||||
text = append(text, params.Payload...)
|
text = append(text, params.Payload...)
|
||||||
|
|
||||||
msg, err := NewSentMessage(params)
|
msg, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -90,8 +93,8 @@ func singleMessageTest(t *testing.T, symmetric bool) {
|
||||||
t.Fatalf("failed to encrypt with seed %d: %s.", seed, err)
|
t.Fatalf("failed to encrypt with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !decrypted.Validate() {
|
if !decrypted.ValidateAndParse() {
|
||||||
t.Fatalf("failed to validate with seed %d.", seed)
|
t.Fatalf("failed to validate with seed %d, symmetric = %v.", seed, symmetric)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(text, decrypted.Payload) {
|
if !bytes.Equal(text, decrypted.Payload) {
|
||||||
|
|
@ -128,7 +131,7 @@ func TestMessageWrap(t *testing.T) {
|
||||||
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
msg, err := NewSentMessage(params)
|
msg, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -146,7 +149,7 @@ func TestMessageWrap(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// set PoW target too high, expect error
|
// set PoW target too high, expect error
|
||||||
msg2, err := NewSentMessage(params)
|
msg2, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -169,15 +172,13 @@ func TestMessageSeal(t *testing.T) {
|
||||||
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
msg, err := NewSentMessage(params)
|
msg, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
params.TTL = 1
|
params.TTL = 1
|
||||||
aesnonce := make([]byte, 12)
|
|
||||||
mrand.Read(aesnonce)
|
|
||||||
|
|
||||||
env := NewEnvelope(params.TTL, params.Topic, aesnonce, msg)
|
env := NewEnvelope(params.TTL, params.Topic, msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -208,7 +209,7 @@ func TestEnvelopeOpen(t *testing.T) {
|
||||||
InitSingleTest()
|
InitSingleTest()
|
||||||
|
|
||||||
var symmetric bool
|
var symmetric bool
|
||||||
for i := 0; i < 256; i++ {
|
for i := 0; i < 32; i++ {
|
||||||
singleEnvelopeOpenTest(t, symmetric)
|
singleEnvelopeOpenTest(t, symmetric)
|
||||||
symmetric = !symmetric
|
symmetric = !symmetric
|
||||||
}
|
}
|
||||||
|
|
@ -233,7 +234,7 @@ func singleEnvelopeOpenTest(t *testing.T, symmetric bool) {
|
||||||
text := make([]byte, 0, 512)
|
text := make([]byte, 0, 512)
|
||||||
text = append(text, params.Payload...)
|
text = append(text, params.Payload...)
|
||||||
|
|
||||||
msg, err := NewSentMessage(params)
|
msg, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -242,7 +243,12 @@ func singleEnvelopeOpenTest(t *testing.T, symmetric bool) {
|
||||||
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
f := Filter{KeyAsym: key, KeySym: params.KeySym}
|
var f Filter
|
||||||
|
if symmetric {
|
||||||
|
f = Filter{KeySym: params.KeySym}
|
||||||
|
} else {
|
||||||
|
f = Filter{KeyAsym: key}
|
||||||
|
}
|
||||||
decrypted := env.Open(&f)
|
decrypted := env.Open(&f)
|
||||||
if decrypted == nil {
|
if decrypted == nil {
|
||||||
t.Fatalf("failed to open with seed %d.", seed)
|
t.Fatalf("failed to open with seed %d.", seed)
|
||||||
|
|
@ -283,7 +289,7 @@ func TestEncryptWithZeroKey(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
msg, err := NewSentMessage(params)
|
msg, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -297,7 +303,7 @@ func TestEncryptWithZeroKey(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
msg, err = NewSentMessage(params)
|
msg, err = newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -311,7 +317,7 @@ func TestEncryptWithZeroKey(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
msg, err = NewSentMessage(params)
|
msg, err = newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -329,7 +335,7 @@ func TestRlpEncode(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
msg, err := NewSentMessage(params)
|
msg, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -373,7 +379,7 @@ func singlePaddingTest(t *testing.T, padSize int) {
|
||||||
if n != padSize {
|
if n != padSize {
|
||||||
t.Fatalf("padding is not copied (seed %d): %s", seed, err)
|
t.Fatalf("padding is not copied (seed %d): %s", seed, err)
|
||||||
}
|
}
|
||||||
msg, err := NewSentMessage(params)
|
msg, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -413,3 +419,53 @@ func TestPadding(t *testing.T) {
|
||||||
singlePaddingTest(t, n)
|
singlePaddingTest(t, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPaddingAppendedToSymMessagesWithSignature(t *testing.T) {
|
||||||
|
params := &MessageParams{
|
||||||
|
Payload: make([]byte, 246),
|
||||||
|
KeySym: make([]byte, aesKeyLength),
|
||||||
|
}
|
||||||
|
|
||||||
|
pSrc, err := crypto.GenerateKey()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating the signature key %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params.Src = pSrc
|
||||||
|
|
||||||
|
// Simulate a message with a payload just under 256 so that
|
||||||
|
// payload + flag + signature > 256. Check that the result
|
||||||
|
// is padded on the next 256 boundary.
|
||||||
|
msg := sentMessage{}
|
||||||
|
const payloadSizeFieldMinSize = 1
|
||||||
|
msg.Raw = make([]byte, flagsLength+payloadSizeFieldMinSize+len(params.Payload))
|
||||||
|
|
||||||
|
err = msg.appendPadding(params)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error appending padding to message %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(msg.Raw) != 512-signatureLength {
|
||||||
|
t.Errorf("Invalid size %d != 512", len(msg.Raw))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAesNonce(t *testing.T) {
|
||||||
|
key := hexutil.MustDecode("0x03ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31")
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewCipher failed: %s", err)
|
||||||
|
}
|
||||||
|
aesgcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewGCM failed: %s", err)
|
||||||
|
}
|
||||||
|
// This is the most important single test in this package.
|
||||||
|
// If it fails, whisper will not be working.
|
||||||
|
if aesgcm.NonceSize() != aesNonceLength {
|
||||||
|
t.Fatalf("Nonce size is wrong. This is a critical error. Apparently AES nonce size have changed in the new version of AES GCM package. Whisper will not be working until this problem is resolved.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package whisperv6
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -27,12 +28,15 @@ import (
|
||||||
set "gopkg.in/fatih/set.v0"
|
set "gopkg.in/fatih/set.v0"
|
||||||
)
|
)
|
||||||
|
|
||||||
// peer represents a whisper protocol peer connection.
|
// Peer represents a whisper protocol peer connection.
|
||||||
type Peer struct {
|
type Peer struct {
|
||||||
host *Whisper
|
host *Whisper
|
||||||
peer *p2p.Peer
|
peer *p2p.Peer
|
||||||
ws p2p.MsgReadWriter
|
ws p2p.MsgReadWriter
|
||||||
|
|
||||||
trusted bool
|
trusted bool
|
||||||
|
powRequirement float64
|
||||||
|
bloomFilter []byte // may contain nil in case of full node
|
||||||
|
|
||||||
known *set.Set // Messages already known by the peer to avoid wasting bandwidth
|
known *set.Set // Messages already known by the peer to avoid wasting bandwidth
|
||||||
|
|
||||||
|
|
@ -46,6 +50,7 @@ func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer {
|
||||||
peer: remote,
|
peer: remote,
|
||||||
ws: rw,
|
ws: rw,
|
||||||
trusted: false,
|
trusted: false,
|
||||||
|
powRequirement: 0.0,
|
||||||
known: set.New(),
|
known: set.New(),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
@ -53,51 +58,83 @@ func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer {
|
||||||
|
|
||||||
// start initiates the peer updater, periodically broadcasting the whisper packets
|
// start initiates the peer updater, periodically broadcasting the whisper packets
|
||||||
// into the network.
|
// into the network.
|
||||||
func (p *Peer) start() {
|
func (peer *Peer) start() {
|
||||||
go p.update()
|
go peer.update()
|
||||||
log.Trace("start", "peer", p.ID())
|
log.Trace("start", "peer", peer.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop terminates the peer updater, stopping message forwarding to it.
|
// stop terminates the peer updater, stopping message forwarding to it.
|
||||||
func (p *Peer) stop() {
|
func (peer *Peer) stop() {
|
||||||
close(p.quit)
|
close(peer.quit)
|
||||||
log.Trace("stop", "peer", p.ID())
|
log.Trace("stop", "peer", peer.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
// handshake sends the protocol initiation status message to the remote peer and
|
// handshake sends the protocol initiation status message to the remote peer and
|
||||||
// verifies the remote status too.
|
// verifies the remote status too.
|
||||||
func (p *Peer) handshake() error {
|
func (peer *Peer) handshake() error {
|
||||||
// Send the handshake status message asynchronously
|
// Send the handshake status message asynchronously
|
||||||
errc := make(chan error, 1)
|
errc := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
errc <- p2p.Send(p.ws, statusCode, ProtocolVersion)
|
pow := peer.host.MinPow()
|
||||||
|
powConverted := math.Float64bits(pow)
|
||||||
|
bloom := peer.host.BloomFilter()
|
||||||
|
errc <- p2p.SendItems(peer.ws, statusCode, ProtocolVersion, powConverted, bloom)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Fetch the remote status packet and verify protocol match
|
// Fetch the remote status packet and verify protocol match
|
||||||
packet, err := p.ws.ReadMsg()
|
packet, err := peer.ws.ReadMsg()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if packet.Code != statusCode {
|
if packet.Code != statusCode {
|
||||||
return fmt.Errorf("peer [%x] sent packet %x before status packet", p.ID(), packet.Code)
|
return fmt.Errorf("peer [%x] sent packet %x before status packet", peer.ID(), packet.Code)
|
||||||
}
|
}
|
||||||
s := rlp.NewStream(packet.Payload, uint64(packet.Size))
|
s := rlp.NewStream(packet.Payload, uint64(packet.Size))
|
||||||
|
_, err = s.List()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("peer [%x] sent bad status message: %v", peer.ID(), err)
|
||||||
|
}
|
||||||
peerVersion, err := s.Uint()
|
peerVersion, err := s.Uint()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("peer [%x] sent bad status message: %v", p.ID(), err)
|
return fmt.Errorf("peer [%x] sent bad status message (unable to decode version): %v", peer.ID(), err)
|
||||||
}
|
}
|
||||||
if peerVersion != ProtocolVersion {
|
if peerVersion != ProtocolVersion {
|
||||||
return fmt.Errorf("peer [%x]: protocol version mismatch %d != %d", p.ID(), peerVersion, ProtocolVersion)
|
return fmt.Errorf("peer [%x]: protocol version mismatch %d != %d", peer.ID(), peerVersion, ProtocolVersion)
|
||||||
}
|
}
|
||||||
// Wait until out own status is consumed too
|
|
||||||
|
// only version is mandatory, subsequent parameters are optional
|
||||||
|
powRaw, err := s.Uint()
|
||||||
|
if err == nil {
|
||||||
|
pow := math.Float64frombits(powRaw)
|
||||||
|
if math.IsInf(pow, 0) || math.IsNaN(pow) || pow < 0.0 {
|
||||||
|
return fmt.Errorf("peer [%x] sent bad status message: invalid pow", peer.ID())
|
||||||
|
}
|
||||||
|
peer.powRequirement = pow
|
||||||
|
|
||||||
|
var bloom []byte
|
||||||
|
err = s.Decode(&bloom)
|
||||||
|
if err == nil {
|
||||||
|
sz := len(bloom)
|
||||||
|
if sz != bloomFilterSize && sz != 0 {
|
||||||
|
return fmt.Errorf("peer [%x] sent bad status message: wrong bloom filter size %d", peer.ID(), sz)
|
||||||
|
}
|
||||||
|
if isFullNode(bloom) {
|
||||||
|
peer.bloomFilter = nil
|
||||||
|
} else {
|
||||||
|
peer.bloomFilter = bloom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := <-errc; err != nil {
|
if err := <-errc; err != nil {
|
||||||
return fmt.Errorf("peer [%x] failed to send status packet: %v", p.ID(), err)
|
return fmt.Errorf("peer [%x] failed to send status packet: %v", peer.ID(), err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// update executes periodic operations on the peer, including message transmission
|
// update executes periodic operations on the peer, including message transmission
|
||||||
// and expiration.
|
// and expiration.
|
||||||
func (p *Peer) update() {
|
func (peer *Peer) update() {
|
||||||
// Start the tickers for the updates
|
// Start the tickers for the updates
|
||||||
expire := time.NewTicker(expirationCycle)
|
expire := time.NewTicker(expirationCycle)
|
||||||
transmit := time.NewTicker(transmissionCycle)
|
transmit := time.NewTicker(transmissionCycle)
|
||||||
|
|
@ -106,15 +143,15 @@ func (p *Peer) update() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-expire.C:
|
case <-expire.C:
|
||||||
p.expire()
|
peer.expire()
|
||||||
|
|
||||||
case <-transmit.C:
|
case <-transmit.C:
|
||||||
if err := p.broadcast(); err != nil {
|
if err := peer.broadcast(); err != nil {
|
||||||
log.Trace("broadcast failed", "reason", err, "peer", p.ID())
|
log.Trace("broadcast failed", "reason", err, "peer", peer.ID())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-p.quit:
|
case <-peer.quit:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -148,27 +185,51 @@ func (peer *Peer) expire() {
|
||||||
|
|
||||||
// broadcast iterates over the collection of envelopes and transmits yet unknown
|
// broadcast iterates over the collection of envelopes and transmits yet unknown
|
||||||
// ones over the network.
|
// ones over the network.
|
||||||
func (p *Peer) broadcast() error {
|
func (peer *Peer) broadcast() error {
|
||||||
var cnt int
|
envelopes := peer.host.Envelopes()
|
||||||
envelopes := p.host.Envelopes()
|
bundle := make([]*Envelope, 0, len(envelopes))
|
||||||
for _, envelope := range envelopes {
|
for _, envelope := range envelopes {
|
||||||
if !p.marked(envelope) {
|
if !peer.marked(envelope) && envelope.PoW() >= peer.powRequirement && peer.bloomMatch(envelope) {
|
||||||
err := p2p.Send(p.ws, messagesCode, envelope)
|
bundle = append(bundle, envelope)
|
||||||
if err != nil {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(bundle) > 0 {
|
||||||
|
// transmit the batch of envelopes
|
||||||
|
if err := p2p.Send(peer.ws, messagesCode, bundle); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
|
||||||
p.mark(envelope)
|
|
||||||
cnt++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mark envelopes only if they were successfully sent
|
||||||
|
for _, e := range bundle {
|
||||||
|
peer.mark(e)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if cnt > 0 {
|
log.Trace("broadcast", "num. messages", len(bundle))
|
||||||
log.Trace("broadcast", "num. messages", cnt)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Peer) ID() []byte {
|
// ID returns a peer's id
|
||||||
id := p.peer.ID()
|
func (peer *Peer) ID() []byte {
|
||||||
|
id := peer.peer.ID()
|
||||||
return id[:]
|
return id[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (peer *Peer) notifyAboutPowRequirementChange(pow float64) error {
|
||||||
|
i := math.Float64bits(pow)
|
||||||
|
return p2p.Send(peer.ws, powRequirementCode, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (peer *Peer) notifyAboutBloomFilterChange(bloom []byte) error {
|
||||||
|
return p2p.Send(peer.ws, bloomFilterExCode, bloom)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (peer *Peer) bloomMatch(env *Envelope) bool {
|
||||||
|
if peer.bloomFilter == nil {
|
||||||
|
// no filter - full node, accepts all envelops
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return bloomFilterMatch(peer.bloomFilter, env.Bloom())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,19 +20,21 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
mrand "math/rand"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
)
|
)
|
||||||
|
|
||||||
var keys []string = []string{
|
var keys = []string{
|
||||||
"d49dcf37238dc8a7aac57dc61b9fee68f0a97f062968978b9fafa7d1033d03a9",
|
"d49dcf37238dc8a7aac57dc61b9fee68f0a97f062968978b9fafa7d1033d03a9",
|
||||||
"73fd6143c48e80ed3c56ea159fe7494a0b6b393a392227b422f4c3e8f1b54f98",
|
"73fd6143c48e80ed3c56ea159fe7494a0b6b393a392227b422f4c3e8f1b54f98",
|
||||||
"119dd32adb1daa7a4c7bf77f847fb28730785aa92947edf42fdd997b54de40dc",
|
"119dd32adb1daa7a4c7bf77f847fb28730785aa92947edf42fdd997b54de40dc",
|
||||||
|
|
@ -79,48 +81,108 @@ type TestNode struct {
|
||||||
shh *Whisper
|
shh *Whisper
|
||||||
id *ecdsa.PrivateKey
|
id *ecdsa.PrivateKey
|
||||||
server *p2p.Server
|
server *p2p.Server
|
||||||
filerId string
|
filerID string
|
||||||
}
|
}
|
||||||
|
|
||||||
var result TestData
|
var result TestData
|
||||||
var nodes [NumNodes]*TestNode
|
var nodes [NumNodes]*TestNode
|
||||||
var sharedKey []byte = []byte("some arbitrary data here")
|
var sharedKey = hexutil.MustDecode("0x03ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31")
|
||||||
var sharedTopic TopicType = TopicType{0xF, 0x1, 0x2, 0}
|
var sharedTopic = TopicType{0xF, 0x1, 0x2, 0}
|
||||||
var expectedMessage []byte = []byte("per rectum ad astra")
|
var expectedMessage = []byte("per rectum ad astra")
|
||||||
|
var masterBloomFilter []byte
|
||||||
|
var masterPow = 0.00000001
|
||||||
|
var round = 1
|
||||||
|
|
||||||
// This test does the following:
|
|
||||||
// 1. creates a chain of whisper nodes,
|
|
||||||
// 2. installs the filters with shared (predefined) parameters,
|
|
||||||
// 3. each node sends a number of random (undecryptable) messages,
|
|
||||||
// 4. first node sends one expected (decryptable) message,
|
|
||||||
// 5. checks if each node have received and decrypted exactly one message.
|
|
||||||
func TestSimulation(t *testing.T) {
|
func TestSimulation(t *testing.T) {
|
||||||
|
// create a chain of whisper nodes,
|
||||||
|
// installs the filters with shared (predefined) parameters
|
||||||
initialize(t)
|
initialize(t)
|
||||||
|
|
||||||
|
// each node sends a number of random (undecryptable) messages
|
||||||
for i := 0; i < NumNodes; i++ {
|
for i := 0; i < NumNodes; i++ {
|
||||||
sendMsg(t, false, i)
|
sendMsg(t, false, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// node #0 sends one expected (decryptable) message
|
||||||
sendMsg(t, true, 0)
|
sendMsg(t, true, 0)
|
||||||
checkPropagation(t)
|
|
||||||
|
// check if each node have received and decrypted exactly one message
|
||||||
|
checkPropagation(t, true)
|
||||||
|
|
||||||
|
// check if Status message was correctly decoded
|
||||||
|
checkBloomFilterExchange(t)
|
||||||
|
checkPowExchange(t)
|
||||||
|
|
||||||
|
// send new pow and bloom exchange messages
|
||||||
|
resetParams(t)
|
||||||
|
round++
|
||||||
|
|
||||||
|
// node #1 sends one expected (decryptable) message
|
||||||
|
sendMsg(t, true, 1)
|
||||||
|
|
||||||
|
// check if each node (except node #0) have received and decrypted exactly one message
|
||||||
|
checkPropagation(t, false)
|
||||||
|
|
||||||
|
// check if corresponding protocol-level messages were correctly decoded
|
||||||
|
checkPowExchangeForNodeZero(t)
|
||||||
|
checkBloomFilterExchange(t)
|
||||||
|
|
||||||
stopServers()
|
stopServers()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resetParams(t *testing.T) {
|
||||||
|
// change pow only for node zero
|
||||||
|
masterPow = 7777777.0
|
||||||
|
nodes[0].shh.SetMinimumPoW(masterPow)
|
||||||
|
|
||||||
|
// change bloom for all nodes
|
||||||
|
masterBloomFilter = TopicToBloom(sharedTopic)
|
||||||
|
for i := 0; i < NumNodes; i++ {
|
||||||
|
nodes[i].shh.SetBloomFilter(masterBloomFilter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initBloom(t *testing.T) {
|
||||||
|
masterBloomFilter = make([]byte, bloomFilterSize)
|
||||||
|
_, err := mrand.Read(masterBloomFilter)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("rand failed: %s.", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgBloom := TopicToBloom(sharedTopic)
|
||||||
|
masterBloomFilter = addBloom(masterBloomFilter, msgBloom)
|
||||||
|
for i := 0; i < 32; i++ {
|
||||||
|
masterBloomFilter[i] = 0xFF
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bloomFilterMatch(masterBloomFilter, msgBloom) {
|
||||||
|
t.Fatalf("bloom mismatch on initBloom.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func initialize(t *testing.T) {
|
func initialize(t *testing.T) {
|
||||||
|
initBloom(t)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
ip := net.IPv4(127, 0, 0, 1)
|
ip := net.IPv4(127, 0, 0, 1)
|
||||||
port0 := 30303
|
port0 := 30303
|
||||||
|
|
||||||
for i := 0; i < NumNodes; i++ {
|
for i := 0; i < NumNodes; i++ {
|
||||||
var node TestNode
|
var node TestNode
|
||||||
|
b := make([]byte, bloomFilterSize)
|
||||||
|
copy(b, masterBloomFilter)
|
||||||
node.shh = New(&DefaultConfig)
|
node.shh = New(&DefaultConfig)
|
||||||
node.shh.SetMinimumPoW(0.00000001)
|
node.shh.SetMinimumPoW(masterPow)
|
||||||
|
node.shh.SetBloomFilter(b)
|
||||||
|
if !bytes.Equal(node.shh.BloomFilter(), masterBloomFilter) {
|
||||||
|
t.Fatalf("bloom mismatch on init.")
|
||||||
|
}
|
||||||
node.shh.Start(nil)
|
node.shh.Start(nil)
|
||||||
topics := make([]TopicType, 0)
|
topics := make([]TopicType, 0)
|
||||||
topics = append(topics, sharedTopic)
|
topics = append(topics, sharedTopic)
|
||||||
f := Filter{KeySym: sharedKey}
|
f := Filter{KeySym: sharedKey}
|
||||||
f.Topics = [][]byte{topics[0][:]}
|
f.Topics = [][]byte{topics[0][:]}
|
||||||
node.filerId, err = node.shh.Subscribe(&f)
|
node.filerID, err = node.shh.Subscribe(&f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to install the filter: %s.", err)
|
t.Fatalf("failed to install the filter: %s.", err)
|
||||||
}
|
}
|
||||||
|
|
@ -133,9 +195,9 @@ func initialize(t *testing.T) {
|
||||||
name := common.MakeName("whisper-go", "2.0")
|
name := common.MakeName("whisper-go", "2.0")
|
||||||
var peers []*discover.Node
|
var peers []*discover.Node
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
peerNodeId := nodes[i-1].id
|
peerNodeID := nodes[i-1].id
|
||||||
peerPort := uint16(port - 1)
|
peerPort := uint16(port - 1)
|
||||||
peerNode := discover.PubkeyID(&peerNodeId.PublicKey)
|
peerNode := discover.PubkeyID(&peerNodeID.PublicKey)
|
||||||
peer := discover.NewNode(peerNode, ip, peerPort, peerPort)
|
peer := discover.NewNode(peerNode, ip, peerPort, peerPort)
|
||||||
peers = append(peers, peer)
|
peers = append(peers, peer)
|
||||||
}
|
}
|
||||||
|
|
@ -154,12 +216,17 @@ func initialize(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
err = node.server.Start()
|
nodes[i] = &node
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to start server %d.", i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes[i] = &node
|
for i := 1; i < NumNodes; i++ {
|
||||||
|
go nodes[i].server.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need to wait until the first node actually starts
|
||||||
|
err = nodes[0].server.Start()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to start the fisrt server.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,28 +234,31 @@ func stopServers() {
|
||||||
for i := 0; i < NumNodes; i++ {
|
for i := 0; i < NumNodes; i++ {
|
||||||
n := nodes[i]
|
n := nodes[i]
|
||||||
if n != nil {
|
if n != nil {
|
||||||
n.shh.Unsubscribe(n.filerId)
|
n.shh.Unsubscribe(n.filerID)
|
||||||
n.shh.Stop()
|
n.shh.Stop()
|
||||||
n.server.Stop()
|
n.server.Stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkPropagation(t *testing.T) {
|
func checkPropagation(t *testing.T, includingNodeZero bool) {
|
||||||
if t.Failed() {
|
if t.Failed() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const cycle = 100
|
const cycle = 50
|
||||||
const iterations = 100
|
const iterations = 200
|
||||||
|
|
||||||
|
first := 0
|
||||||
|
if !includingNodeZero {
|
||||||
|
first = 1
|
||||||
|
}
|
||||||
|
|
||||||
for j := 0; j < iterations; j++ {
|
for j := 0; j < iterations; j++ {
|
||||||
time.Sleep(cycle * time.Millisecond)
|
for i := first; i < NumNodes; i++ {
|
||||||
|
f := nodes[i].shh.GetFilter(nodes[i].filerID)
|
||||||
for i := 0; i < NumNodes; i++ {
|
|
||||||
f := nodes[i].shh.GetFilter(nodes[i].filerId)
|
|
||||||
if f == nil {
|
if f == nil {
|
||||||
t.Fatalf("failed to get filterId %s from node %d.", nodes[i].filerId, i)
|
t.Fatalf("failed to get filterId %s from node %d, round %d.", nodes[i].filerID, i, round)
|
||||||
}
|
}
|
||||||
|
|
||||||
mail := f.Retrieve()
|
mail := f.Retrieve()
|
||||||
|
|
@ -200,9 +270,18 @@ func checkPropagation(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
time.Sleep(cycle * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Fatalf("Test was not complete: timeout %d seconds.", iterations*cycle/1000)
|
t.Fatalf("Test was not complete: timeout %d seconds. nodes=%v", iterations*cycle/1000, nodes)
|
||||||
|
|
||||||
|
if !includingNodeZero {
|
||||||
|
f := nodes[0].shh.GetFilter(nodes[0].filerID)
|
||||||
|
if f != nil {
|
||||||
|
t.Fatalf("node zero received a message with low PoW.")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateMail(t *testing.T, index int, mail []*ReceivedMessage) bool {
|
func validateMail(t *testing.T, index int, mail []*ReceivedMessage) bool {
|
||||||
|
|
@ -265,7 +344,7 @@ func sendMsg(t *testing.T, expected bool, id int) {
|
||||||
opt.Payload = opt.Payload[1:]
|
opt.Payload = opt.Payload[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
msg, err := NewSentMessage(&opt)
|
msg, err := newSentMessage(&opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -289,7 +368,7 @@ func TestPeerBasic(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
params.PoW = 0.001
|
params.PoW = 0.001
|
||||||
msg, err := NewSentMessage(params)
|
msg, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -304,3 +383,79 @@ func TestPeerBasic(t *testing.T) {
|
||||||
t.Fatalf("failed mark with seed %d.", seed)
|
t.Fatalf("failed mark with seed %d.", seed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkPowExchangeForNodeZero(t *testing.T) {
|
||||||
|
const iterations = 200
|
||||||
|
for j := 0; j < iterations; j++ {
|
||||||
|
lastCycle := (j == iterations-1)
|
||||||
|
ok := checkPowExchangeForNodeZeroOnce(t, lastCycle)
|
||||||
|
if ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPowExchangeForNodeZeroOnce(t *testing.T, mustPass bool) bool {
|
||||||
|
cnt := 0
|
||||||
|
for i, node := range nodes {
|
||||||
|
for peer := range node.shh.peers {
|
||||||
|
if peer.peer.ID() == discover.PubkeyID(&nodes[0].id.PublicKey) {
|
||||||
|
cnt++
|
||||||
|
if peer.powRequirement != masterPow {
|
||||||
|
if mustPass {
|
||||||
|
t.Fatalf("node %d: failed to set the new pow requirement for node zero.", i)
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cnt == 0 {
|
||||||
|
t.Fatalf("looking for node zero: no matching peers found.")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPowExchange(t *testing.T) {
|
||||||
|
for i, node := range nodes {
|
||||||
|
for peer := range node.shh.peers {
|
||||||
|
if peer.peer.ID() != discover.PubkeyID(&nodes[0].id.PublicKey) {
|
||||||
|
if peer.powRequirement != masterPow {
|
||||||
|
t.Fatalf("node %d: failed to exchange pow requirement in round %d; expected %f, got %f",
|
||||||
|
i, round, masterPow, peer.powRequirement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkBloomFilterExchangeOnce(t *testing.T, mustPass bool) bool {
|
||||||
|
for i, node := range nodes {
|
||||||
|
for peer := range node.shh.peers {
|
||||||
|
if !bytes.Equal(peer.bloomFilter, masterBloomFilter) {
|
||||||
|
if mustPass {
|
||||||
|
t.Fatalf("node %d: failed to exchange bloom filter requirement in round %d. \n%x expected \n%x got",
|
||||||
|
i, round, masterBloomFilter, peer.bloomFilter)
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkBloomFilterExchange(t *testing.T) {
|
||||||
|
const iterations = 200
|
||||||
|
for j := 0; j < iterations; j++ {
|
||||||
|
lastCycle := (j == iterations-1)
|
||||||
|
ok := checkBloomFilterExchangeOnce(t, lastCycle)
|
||||||
|
if ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,13 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Topic represents a cryptographically secure, probabilistic partial
|
// TopicType represents a cryptographically secure, probabilistic partial
|
||||||
// classifications of a message, determined as the first (left) 4 bytes of the
|
// classifications of a message, determined as the first (left) 4 bytes of the
|
||||||
// SHA3 hash of some arbitrary data given by the original author of the message.
|
// SHA3 hash of some arbitrary data given by the original author of the message.
|
||||||
type TopicType [TopicLength]byte
|
type TopicType [TopicLength]byte
|
||||||
|
|
||||||
|
// BytesToTopic converts from the byte array representation of a topic
|
||||||
|
// into the TopicType type.
|
||||||
func BytesToTopic(b []byte) (t TopicType) {
|
func BytesToTopic(b []byte) (t TopicType) {
|
||||||
sz := TopicLength
|
sz := TopicLength
|
||||||
if x := len(b); x < TopicLength {
|
if x := len(b); x < TopicLength {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -19,11 +19,13 @@ package whisperv6
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/sha256"
|
||||||
mrand "math/rand"
|
mrand "math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"golang.org/x/crypto/pbkdf2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWhisperBasic(t *testing.T) {
|
func TestWhisperBasic(t *testing.T) {
|
||||||
|
|
@ -78,16 +80,8 @@ func TestWhisperBasic(t *testing.T) {
|
||||||
t.Fatalf("failed w.Messages.")
|
t.Fatalf("failed w.Messages.")
|
||||||
}
|
}
|
||||||
|
|
||||||
var derived []byte
|
derived := pbkdf2.Key([]byte(peerID), nil, 65356, aesKeyLength, sha256.New)
|
||||||
ver := uint64(0xDEADBEEF)
|
if !validateDataIntegrity(derived, aesKeyLength) {
|
||||||
if _, err := deriveKeyMaterial(peerID, ver); err != unknownVersionError(ver) {
|
|
||||||
t.Fatalf("failed deriveKeyMaterial with param = %v: %s.", peerID, err)
|
|
||||||
}
|
|
||||||
derived, err = deriveKeyMaterial(peerID, 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed second deriveKeyMaterial with param = %v: %s.", peerID, err)
|
|
||||||
}
|
|
||||||
if !validateSymmetricKey(derived) {
|
|
||||||
t.Fatalf("failed validateSymmetricKey with param = %v.", derived)
|
t.Fatalf("failed validateSymmetricKey with param = %v.", derived)
|
||||||
}
|
}
|
||||||
if containsOnlyZeros(derived) {
|
if containsOnlyZeros(derived) {
|
||||||
|
|
@ -454,32 +448,20 @@ func TestWhisperSymKeyManagement(t *testing.T) {
|
||||||
if !w.HasSymKey(id2) {
|
if !w.HasSymKey(id2) {
|
||||||
t.Fatalf("HasSymKey(id2) failed.")
|
t.Fatalf("HasSymKey(id2) failed.")
|
||||||
}
|
}
|
||||||
if k1 == nil {
|
if !validateDataIntegrity(k2, aesKeyLength) {
|
||||||
t.Fatalf("k1 does not exist.")
|
t.Fatalf("key validation failed.")
|
||||||
}
|
|
||||||
if k2 == nil {
|
|
||||||
t.Fatalf("k2 does not exist.")
|
|
||||||
}
|
}
|
||||||
if !bytes.Equal(k1, k2) {
|
if !bytes.Equal(k1, k2) {
|
||||||
t.Fatalf("k1 != k2.")
|
t.Fatalf("k1 != k2.")
|
||||||
}
|
}
|
||||||
if len(k1) != aesKeyLength {
|
|
||||||
t.Fatalf("wrong length of k1.")
|
|
||||||
}
|
|
||||||
if len(k2) != aesKeyLength {
|
|
||||||
t.Fatalf("wrong length of k2.")
|
|
||||||
}
|
|
||||||
if !validateSymmetricKey(k2) {
|
|
||||||
t.Fatalf("key validation failed.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExpiry(t *testing.T) {
|
func TestExpiry(t *testing.T) {
|
||||||
InitSingleTest()
|
InitSingleTest()
|
||||||
|
|
||||||
w := New(&DefaultConfig)
|
w := New(&DefaultConfig)
|
||||||
w.SetMinimumPoW(0.0000001)
|
w.SetMinimumPowTest(0.0000001)
|
||||||
defer w.SetMinimumPoW(DefaultMinimumPoW)
|
defer w.SetMinimumPowTest(DefaultMinimumPoW)
|
||||||
w.Start(nil)
|
w.Start(nil)
|
||||||
defer w.Stop()
|
defer w.Stop()
|
||||||
|
|
||||||
|
|
@ -489,7 +471,7 @@ func TestExpiry(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
params.TTL = 1
|
params.TTL = 1
|
||||||
msg, err := NewSentMessage(params)
|
msg, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -535,7 +517,7 @@ func TestCustomization(t *testing.T) {
|
||||||
InitSingleTest()
|
InitSingleTest()
|
||||||
|
|
||||||
w := New(&DefaultConfig)
|
w := New(&DefaultConfig)
|
||||||
defer w.SetMinimumPoW(DefaultMinimumPoW)
|
defer w.SetMinimumPowTest(DefaultMinimumPoW)
|
||||||
defer w.SetMaxMessageSize(DefaultMaxMessageSize)
|
defer w.SetMaxMessageSize(DefaultMaxMessageSize)
|
||||||
w.Start(nil)
|
w.Start(nil)
|
||||||
defer w.Stop()
|
defer w.Stop()
|
||||||
|
|
@ -555,7 +537,7 @@ func TestCustomization(t *testing.T) {
|
||||||
params.Topic = BytesToTopic(f.Topics[2])
|
params.Topic = BytesToTopic(f.Topics[2])
|
||||||
params.PoW = smallPoW
|
params.PoW = smallPoW
|
||||||
params.TTL = 3600 * 24 // one day
|
params.TTL = 3600 * 24 // one day
|
||||||
msg, err := NewSentMessage(params)
|
msg, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -569,14 +551,14 @@ func TestCustomization(t *testing.T) {
|
||||||
t.Fatalf("successfully sent envelope with PoW %.06f, false positive (seed %d).", env.PoW(), seed)
|
t.Fatalf("successfully sent envelope with PoW %.06f, false positive (seed %d).", env.PoW(), seed)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.SetMinimumPoW(smallPoW / 2)
|
w.SetMinimumPowTest(smallPoW / 2)
|
||||||
err = w.Send(env)
|
err = w.Send(env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to send envelope with seed %d: %s.", seed, err)
|
t.Fatalf("failed to send envelope with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
params.TTL++
|
params.TTL++
|
||||||
msg, err = NewSentMessage(params)
|
msg, err = newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -631,7 +613,7 @@ func TestSymmetricSendCycle(t *testing.T) {
|
||||||
InitSingleTest()
|
InitSingleTest()
|
||||||
|
|
||||||
w := New(&DefaultConfig)
|
w := New(&DefaultConfig)
|
||||||
defer w.SetMinimumPoW(DefaultMinimumPoW)
|
defer w.SetMinimumPowTest(DefaultMinimumPoW)
|
||||||
defer w.SetMaxMessageSize(DefaultMaxMessageSize)
|
defer w.SetMaxMessageSize(DefaultMaxMessageSize)
|
||||||
w.Start(nil)
|
w.Start(nil)
|
||||||
defer w.Stop()
|
defer w.Stop()
|
||||||
|
|
@ -665,7 +647,7 @@ func TestSymmetricSendCycle(t *testing.T) {
|
||||||
params.PoW = filter1.PoW
|
params.PoW = filter1.PoW
|
||||||
params.WorkTime = 10
|
params.WorkTime = 10
|
||||||
params.TTL = 50
|
params.TTL = 50
|
||||||
msg, err := NewSentMessage(params)
|
msg, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -720,7 +702,7 @@ func TestSymmetricSendWithoutAKey(t *testing.T) {
|
||||||
InitSingleTest()
|
InitSingleTest()
|
||||||
|
|
||||||
w := New(&DefaultConfig)
|
w := New(&DefaultConfig)
|
||||||
defer w.SetMinimumPoW(DefaultMinimumPoW)
|
defer w.SetMinimumPowTest(DefaultMinimumPoW)
|
||||||
defer w.SetMaxMessageSize(DefaultMaxMessageSize)
|
defer w.SetMaxMessageSize(DefaultMaxMessageSize)
|
||||||
w.Start(nil)
|
w.Start(nil)
|
||||||
defer w.Stop()
|
defer w.Stop()
|
||||||
|
|
@ -743,7 +725,7 @@ func TestSymmetricSendWithoutAKey(t *testing.T) {
|
||||||
params.PoW = filter.PoW
|
params.PoW = filter.PoW
|
||||||
params.WorkTime = 10
|
params.WorkTime = 10
|
||||||
params.TTL = 50
|
params.TTL = 50
|
||||||
msg, err := NewSentMessage(params)
|
msg, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -788,7 +770,7 @@ func TestSymmetricSendKeyMismatch(t *testing.T) {
|
||||||
InitSingleTest()
|
InitSingleTest()
|
||||||
|
|
||||||
w := New(&DefaultConfig)
|
w := New(&DefaultConfig)
|
||||||
defer w.SetMinimumPoW(DefaultMinimumPoW)
|
defer w.SetMinimumPowTest(DefaultMinimumPoW)
|
||||||
defer w.SetMaxMessageSize(DefaultMaxMessageSize)
|
defer w.SetMaxMessageSize(DefaultMaxMessageSize)
|
||||||
w.Start(nil)
|
w.Start(nil)
|
||||||
defer w.Stop()
|
defer w.Stop()
|
||||||
|
|
@ -809,7 +791,7 @@ func TestSymmetricSendKeyMismatch(t *testing.T) {
|
||||||
params.PoW = filter.PoW
|
params.PoW = filter.PoW
|
||||||
params.WorkTime = 10
|
params.WorkTime = 10
|
||||||
params.TTL = 50
|
params.TTL = 50
|
||||||
msg, err := NewSentMessage(params)
|
msg, err := newSentMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
@ -849,3 +831,64 @@ func TestSymmetricSendKeyMismatch(t *testing.T) {
|
||||||
t.Fatalf("received a message when keys weren't matching")
|
t.Fatalf("received a message when keys weren't matching")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBloom(t *testing.T) {
|
||||||
|
topic := TopicType{0, 0, 255, 6}
|
||||||
|
b := TopicToBloom(topic)
|
||||||
|
x := make([]byte, bloomFilterSize)
|
||||||
|
x[0] = byte(1)
|
||||||
|
x[32] = byte(1)
|
||||||
|
x[bloomFilterSize-1] = byte(128)
|
||||||
|
if !bloomFilterMatch(x, b) || !bloomFilterMatch(b, x) {
|
||||||
|
t.Fatalf("bloom filter does not match the mask")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := mrand.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("math rand error")
|
||||||
|
}
|
||||||
|
_, err = mrand.Read(x)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("math rand error")
|
||||||
|
}
|
||||||
|
if !bloomFilterMatch(b, b) {
|
||||||
|
t.Fatalf("bloom filter does not match self")
|
||||||
|
}
|
||||||
|
x = addBloom(x, b)
|
||||||
|
if !bloomFilterMatch(x, b) {
|
||||||
|
t.Fatalf("bloom filter does not match combined bloom")
|
||||||
|
}
|
||||||
|
if !isFullNode(nil) {
|
||||||
|
t.Fatalf("isFullNode did not recognize nil as full node")
|
||||||
|
}
|
||||||
|
x[17] = 254
|
||||||
|
if isFullNode(x) {
|
||||||
|
t.Fatalf("isFullNode false positive")
|
||||||
|
}
|
||||||
|
for i := 0; i < bloomFilterSize; i++ {
|
||||||
|
b[i] = byte(255)
|
||||||
|
}
|
||||||
|
if !isFullNode(b) {
|
||||||
|
t.Fatalf("isFullNode false negative")
|
||||||
|
}
|
||||||
|
if bloomFilterMatch(x, b) {
|
||||||
|
t.Fatalf("bloomFilterMatch false positive")
|
||||||
|
}
|
||||||
|
if !bloomFilterMatch(b, x) {
|
||||||
|
t.Fatalf("bloomFilterMatch false negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
w := New(&DefaultConfig)
|
||||||
|
f := w.BloomFilter()
|
||||||
|
if f != nil {
|
||||||
|
t.Fatalf("wrong bloom on creation")
|
||||||
|
}
|
||||||
|
err = w.SetBloomFilter(x)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to set bloom filter: %s", err)
|
||||||
|
}
|
||||||
|
f = w.BloomFilter()
|
||||||
|
if !bloomFilterMatch(f, x) || !bloomFilterMatch(x, f) {
|
||||||
|
t.Fatalf("retireved wrong bloom filter")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue