Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
729bf365
Unverified
Commit
729bf365
authored
Mar 01, 2019
by
Guillaume Ballet
Committed by
GitHub
Mar 01, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
whisper: Remove v5 (#18432)
parent
4e9230ea
Changes
18
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
0 additions
and
5592 deletions
+0
-5592
api.go
whisper/whisperv5/api.go
+0
-561
benchmarks_test.go
whisper/whisperv5/benchmarks_test.go
+0
-206
config.go
whisper/whisperv5/config.go
+0
-27
doc.go
whisper/whisperv5/doc.go
+0
-87
envelope.go
whisper/whisperv5/envelope.go
+0
-246
filter.go
whisper/whisperv5/filter.go
+0
-243
filter_test.go
whisper/whisperv5/filter_test.go
+0
-848
gen_criteria_json.go
whisper/whisperv5/gen_criteria_json.go
+0
-64
gen_message_json.go
whisper/whisperv5/gen_message_json.go
+0
-82
gen_newmessage_json.go
whisper/whisperv5/gen_newmessage_json.go
+0
-88
message.go
whisper/whisperv5/message.go
+0
-352
message_test.go
whisper/whisperv5/message_test.go
+0
-415
peer.go
whisper/whisperv5/peer.go
+0
-174
peer_test.go
whisper/whisperv5/peer_test.go
+0
-297
topic.go
whisper/whisperv5/topic.go
+0
-55
topic_test.go
whisper/whisperv5/topic_test.go
+0
-134
whisper.go
whisper/whisperv5/whisper.go
+0
-855
whisper_test.go
whisper/whisperv5/whisper_test.go
+0
-858
No files found.
whisper/whisperv5/api.go
deleted
100644 → 0
View file @
4e9230ea
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package
whisperv5
import
(
"context"
"crypto/ecdsa"
"errors"
"fmt"
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/rpc"
)
var
(
ErrSymAsym
=
errors
.
New
(
"specify either a symmetric or an asymmetric key"
)
ErrInvalidSymmetricKey
=
errors
.
New
(
"invalid symmetric key"
)
ErrInvalidPublicKey
=
errors
.
New
(
"invalid public key"
)
ErrInvalidSigningPubKey
=
errors
.
New
(
"invalid signing public key"
)
ErrTooLowPoW
=
errors
.
New
(
"message rejected, PoW too low"
)
ErrNoTopics
=
errors
.
New
(
"missing topic(s)"
)
)
// PublicWhisperAPI provides the whisper RPC service that can be
// use publicly without security implications.
type
PublicWhisperAPI
struct
{
w
*
Whisper
mu
sync
.
Mutex
lastUsed
map
[
string
]
time
.
Time
// keeps track when a filter was polled for the last time.
}
// NewPublicWhisperAPI create a new RPC whisper service.
func
NewPublicWhisperAPI
(
w
*
Whisper
)
*
PublicWhisperAPI
{
api
:=
&
PublicWhisperAPI
{
w
:
w
,
lastUsed
:
make
(
map
[
string
]
time
.
Time
),
}
return
api
}
// Version returns the Whisper sub-protocol version.
func
(
api
*
PublicWhisperAPI
)
Version
(
ctx
context
.
Context
)
string
{
return
ProtocolVersionStr
}
// Info contains diagnostic information.
type
Info
struct
{
Memory
int
`json:"memory"`
// Memory size of the floating messages in bytes.
Messages
int
`json:"messages"`
// Number of floating messages.
MinPow
float64
`json:"minPow"`
// Minimal accepted PoW
MaxMessageSize
uint32
`json:"maxMessageSize"`
// Maximum accepted message size
}
// Info returns diagnostic information about the whisper node.
func
(
api
*
PublicWhisperAPI
)
Info
(
ctx
context
.
Context
)
Info
{
stats
:=
api
.
w
.
Stats
()
return
Info
{
Memory
:
stats
.
memoryUsed
,
Messages
:
len
(
api
.
w
.
messageQueue
)
+
len
(
api
.
w
.
p2pMsgQueue
),
MinPow
:
api
.
w
.
MinPow
(),
MaxMessageSize
:
api
.
w
.
MaxMessageSize
(),
}
}
// SetMaxMessageSize sets the maximum message size that is accepted.
// Upper limit is defined in whisperv5.MaxMessageSize.
func
(
api
*
PublicWhisperAPI
)
SetMaxMessageSize
(
ctx
context
.
Context
,
size
uint32
)
(
bool
,
error
)
{
return
true
,
api
.
w
.
SetMaxMessageSize
(
size
)
}
// SetMinPoW sets the minimum PoW for a message before it is accepted.
func
(
api
*
PublicWhisperAPI
)
SetMinPoW
(
ctx
context
.
Context
,
pow
float64
)
(
bool
,
error
)
{
return
true
,
api
.
w
.
SetMinimumPoW
(
pow
)
}
// 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.
func
(
api
*
PublicWhisperAPI
)
MarkTrustedPeer
(
ctx
context
.
Context
,
url
string
)
(
bool
,
error
)
{
n
,
err
:=
enode
.
ParseV4
(
url
)
if
err
!=
nil
{
return
false
,
err
}
return
true
,
api
.
w
.
AllowP2PMessagesFromPeer
(
n
.
ID
()
.
Bytes
())
}
// NewKeyPair generates a new public and private key pair for message decryption and encryption.
// It returns an ID that can be used to refer to the keypair.
func
(
api
*
PublicWhisperAPI
)
NewKeyPair
(
ctx
context
.
Context
)
(
string
,
error
)
{
return
api
.
w
.
NewKeyPair
()
}
// AddPrivateKey imports the given private key.
func
(
api
*
PublicWhisperAPI
)
AddPrivateKey
(
ctx
context
.
Context
,
privateKey
hexutil
.
Bytes
)
(
string
,
error
)
{
key
,
err
:=
crypto
.
ToECDSA
(
privateKey
)
if
err
!=
nil
{
return
""
,
err
}
return
api
.
w
.
AddKeyPair
(
key
)
}
// DeleteKeyPair removes the key with the given key if it exists.
func
(
api
*
PublicWhisperAPI
)
DeleteKeyPair
(
ctx
context
.
Context
,
key
string
)
(
bool
,
error
)
{
if
ok
:=
api
.
w
.
DeleteKeyPair
(
key
);
ok
{
return
true
,
nil
}
return
false
,
fmt
.
Errorf
(
"key pair %s not found"
,
key
)
}
// HasKeyPair returns an indication if the node has a key pair that is associated with the given id.
func
(
api
*
PublicWhisperAPI
)
HasKeyPair
(
ctx
context
.
Context
,
id
string
)
bool
{
return
api
.
w
.
HasKeyPair
(
id
)
}
// GetPublicKey returns the public 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.
func
(
api
*
PublicWhisperAPI
)
GetPublicKey
(
ctx
context
.
Context
,
id
string
)
(
hexutil
.
Bytes
,
error
)
{
key
,
err
:=
api
.
w
.
GetPrivateKey
(
id
)
if
err
!=
nil
{
return
hexutil
.
Bytes
{},
err
}
return
crypto
.
FromECDSAPub
(
&
key
.
PublicKey
),
nil
}
// 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.
func
(
api
*
PublicWhisperAPI
)
GetPrivateKey
(
ctx
context
.
Context
,
id
string
)
(
hexutil
.
Bytes
,
error
)
{
key
,
err
:=
api
.
w
.
GetPrivateKey
(
id
)
if
err
!=
nil
{
return
hexutil
.
Bytes
{},
err
}
return
crypto
.
FromECDSA
(
key
),
nil
}
// NewSymKey generate a random symmetric key.
// It returns an ID that can be used to refer to the key.
// Can be used encrypting and decrypting messages where the key is known to both parties.
func
(
api
*
PublicWhisperAPI
)
NewSymKey
(
ctx
context
.
Context
)
(
string
,
error
)
{
return
api
.
w
.
GenerateSymKey
()
}
// AddSymKey import a symmetric key.
// It returns an ID that can be used to refer to the key.
// Can be used encrypting and decrypting messages where the key is known to both parties.
func
(
api
*
PublicWhisperAPI
)
AddSymKey
(
ctx
context
.
Context
,
key
hexutil
.
Bytes
)
(
string
,
error
)
{
return
api
.
w
.
AddSymKeyDirect
([]
byte
(
key
))
}
// GenerateSymKeyFromPassword derive a key from the given password, stores it, and returns its ID.
func
(
api
*
PublicWhisperAPI
)
GenerateSymKeyFromPassword
(
ctx
context
.
Context
,
passwd
string
)
(
string
,
error
)
{
return
api
.
w
.
AddSymKeyFromPassword
(
passwd
)
}
// HasSymKey returns an indication if the node has a symmetric key associated with the given key.
func
(
api
*
PublicWhisperAPI
)
HasSymKey
(
ctx
context
.
Context
,
id
string
)
bool
{
return
api
.
w
.
HasSymKey
(
id
)
}
// GetSymKey returns the symmetric key associated with the given id.
func
(
api
*
PublicWhisperAPI
)
GetSymKey
(
ctx
context
.
Context
,
id
string
)
(
hexutil
.
Bytes
,
error
)
{
return
api
.
w
.
GetSymKey
(
id
)
}
// DeleteSymKey deletes the symmetric key that is associated with the given id.
func
(
api
*
PublicWhisperAPI
)
DeleteSymKey
(
ctx
context
.
Context
,
id
string
)
bool
{
return
api
.
w
.
DeleteSymKey
(
id
)
}
//go:generate gencodec -type NewMessage -field-override newMessageOverride -out gen_newmessage_json.go
// NewMessage represents a new whisper message that is posted through the RPC.
type
NewMessage
struct
{
SymKeyID
string
`json:"symKeyID"`
PublicKey
[]
byte
`json:"pubKey"`
Sig
string
`json:"sig"`
TTL
uint32
`json:"ttl"`
Topic
TopicType
`json:"topic"`
Payload
[]
byte
`json:"payload"`
Padding
[]
byte
`json:"padding"`
PowTime
uint32
`json:"powTime"`
PowTarget
float64
`json:"powTarget"`
TargetPeer
string
`json:"targetPeer"`
}
type
newMessageOverride
struct
{
PublicKey
hexutil
.
Bytes
Payload
hexutil
.
Bytes
Padding
hexutil
.
Bytes
}
// Post a message on the Whisper network.
func
(
api
*
PublicWhisperAPI
)
Post
(
ctx
context
.
Context
,
req
NewMessage
)
(
bool
,
error
)
{
var
(
symKeyGiven
=
len
(
req
.
SymKeyID
)
>
0
pubKeyGiven
=
len
(
req
.
PublicKey
)
>
0
err
error
)
// user must specify either a symmetric or an asymmetric key
if
(
symKeyGiven
&&
pubKeyGiven
)
||
(
!
symKeyGiven
&&
!
pubKeyGiven
)
{
return
false
,
ErrSymAsym
}
params
:=
&
MessageParams
{
TTL
:
req
.
TTL
,
Payload
:
req
.
Payload
,
Padding
:
req
.
Padding
,
WorkTime
:
req
.
PowTime
,
PoW
:
req
.
PowTarget
,
Topic
:
req
.
Topic
,
}
// Set key that is used to sign the message
if
len
(
req
.
Sig
)
>
0
{
if
params
.
Src
,
err
=
api
.
w
.
GetPrivateKey
(
req
.
Sig
);
err
!=
nil
{
return
false
,
err
}
}
// Set symmetric key that is used to encrypt the message
if
symKeyGiven
{
if
params
.
Topic
==
(
TopicType
{})
{
// topics are mandatory with symmetric encryption
return
false
,
ErrNoTopics
}
if
params
.
KeySym
,
err
=
api
.
w
.
GetSymKey
(
req
.
SymKeyID
);
err
!=
nil
{
return
false
,
err
}
if
!
validateSymmetricKey
(
params
.
KeySym
)
{
return
false
,
ErrInvalidSymmetricKey
}
}
// Set asymmetric key that is used to encrypt the message
if
pubKeyGiven
{
if
params
.
Dst
,
err
=
crypto
.
UnmarshalPubkey
(
req
.
PublicKey
);
err
!=
nil
{
return
false
,
ErrInvalidPublicKey
}
}
// encrypt and sent message
whisperMsg
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
return
false
,
err
}
env
,
err
:=
whisperMsg
.
Wrap
(
params
)
if
err
!=
nil
{
return
false
,
err
}
// send to specific node (skip PoW check)
if
len
(
req
.
TargetPeer
)
>
0
{
n
,
err
:=
enode
.
ParseV4
(
req
.
TargetPeer
)
if
err
!=
nil
{
return
false
,
fmt
.
Errorf
(
"failed to parse target peer: %s"
,
err
)
}
return
true
,
api
.
w
.
SendP2PMessage
(
n
.
ID
()
.
Bytes
(),
env
)
}
// ensure that the message PoW meets the node's minimum accepted PoW
if
req
.
PowTarget
<
api
.
w
.
MinPow
()
{
return
false
,
ErrTooLowPoW
}
return
true
,
api
.
w
.
Send
(
env
)
}
//go:generate gencodec -type Criteria -field-override criteriaOverride -out gen_criteria_json.go
// Criteria holds various filter options for inbound messages.
type
Criteria
struct
{
SymKeyID
string
`json:"symKeyID"`
PrivateKeyID
string
`json:"privateKeyID"`
Sig
[]
byte
`json:"sig"`
MinPow
float64
`json:"minPow"`
Topics
[]
TopicType
`json:"topics"`
AllowP2P
bool
`json:"allowP2P"`
}
type
criteriaOverride
struct
{
Sig
hexutil
.
Bytes
}
// Messages set up a subscription that fires events when messages arrive that match
// the given set of criteria.
func
(
api
*
PublicWhisperAPI
)
Messages
(
ctx
context
.
Context
,
crit
Criteria
)
(
*
rpc
.
Subscription
,
error
)
{
var
(
symKeyGiven
=
len
(
crit
.
SymKeyID
)
>
0
pubKeyGiven
=
len
(
crit
.
PrivateKeyID
)
>
0
err
error
)
// ensure that the RPC connection supports subscriptions
notifier
,
supported
:=
rpc
.
NotifierFromContext
(
ctx
)
if
!
supported
{
return
nil
,
rpc
.
ErrNotificationsUnsupported
}
// user must specify either a symmetric or an asymmetric key
if
(
symKeyGiven
&&
pubKeyGiven
)
||
(
!
symKeyGiven
&&
!
pubKeyGiven
)
{
return
nil
,
ErrSymAsym
}
filter
:=
Filter
{
PoW
:
crit
.
MinPow
,
Messages
:
make
(
map
[
common
.
Hash
]
*
ReceivedMessage
),
AllowP2P
:
crit
.
AllowP2P
,
}
if
len
(
crit
.
Sig
)
>
0
{
if
filter
.
Src
,
err
=
crypto
.
UnmarshalPubkey
(
crit
.
Sig
);
err
!=
nil
{
return
nil
,
ErrInvalidSigningPubKey
}
}
for
i
,
bt
:=
range
crit
.
Topics
{
if
len
(
bt
)
==
0
||
len
(
bt
)
>
4
{
return
nil
,
fmt
.
Errorf
(
"subscribe: topic %d has wrong size: %d"
,
i
,
len
(
bt
))
}
filter
.
Topics
=
append
(
filter
.
Topics
,
bt
[
:
])
}
// listen for message that are encrypted with the given symmetric key
if
symKeyGiven
{
if
len
(
filter
.
Topics
)
==
0
{
return
nil
,
ErrNoTopics
}
key
,
err
:=
api
.
w
.
GetSymKey
(
crit
.
SymKeyID
)
if
err
!=
nil
{
return
nil
,
err
}
if
!
validateSymmetricKey
(
key
)
{
return
nil
,
ErrInvalidSymmetricKey
}
filter
.
KeySym
=
key
filter
.
SymKeyHash
=
crypto
.
Keccak256Hash
(
filter
.
KeySym
)
}
// listen for messages that are encrypted with the given public key
if
pubKeyGiven
{
filter
.
KeyAsym
,
err
=
api
.
w
.
GetPrivateKey
(
crit
.
PrivateKeyID
)
if
err
!=
nil
||
filter
.
KeyAsym
==
nil
{
return
nil
,
ErrInvalidPublicKey
}
}
id
,
err
:=
api
.
w
.
Subscribe
(
&
filter
)
if
err
!=
nil
{
return
nil
,
err
}
// create subscription and start waiting for message events
rpcSub
:=
notifier
.
CreateSubscription
()
go
func
()
{
// for now poll internally, refactor whisper internal for channel support
ticker
:=
time
.
NewTicker
(
250
*
time
.
Millisecond
)
defer
ticker
.
Stop
()
for
{
select
{
case
<-
ticker
.
C
:
if
filter
:=
api
.
w
.
GetFilter
(
id
);
filter
!=
nil
{
for
_
,
rpcMessage
:=
range
toMessage
(
filter
.
Retrieve
())
{
if
err
:=
notifier
.
Notify
(
rpcSub
.
ID
,
rpcMessage
);
err
!=
nil
{
log
.
Error
(
"Failed to send notification"
,
"err"
,
err
)
}
}
}
case
<-
rpcSub
.
Err
()
:
api
.
w
.
Unsubscribe
(
id
)
return
case
<-
notifier
.
Closed
()
:
api
.
w
.
Unsubscribe
(
id
)
return
}
}
}()
return
rpcSub
,
nil
}
//go:generate gencodec -type Message -field-override messageOverride -out gen_message_json.go
// Message is the RPC representation of a whisper message.
type
Message
struct
{
Sig
[]
byte
`json:"sig,omitempty"`
TTL
uint32
`json:"ttl"`
Timestamp
uint32
`json:"timestamp"`
Topic
TopicType
`json:"topic"`
Payload
[]
byte
`json:"payload"`
Padding
[]
byte
`json:"padding"`
PoW
float64
`json:"pow"`
Hash
[]
byte
`json:"hash"`
Dst
[]
byte
`json:"recipientPublicKey,omitempty"`
}
type
messageOverride
struct
{
Sig
hexutil
.
Bytes
Payload
hexutil
.
Bytes
Padding
hexutil
.
Bytes
Hash
hexutil
.
Bytes
Dst
hexutil
.
Bytes
}
// ToWhisperMessage converts an internal message into an API version.
func
ToWhisperMessage
(
message
*
ReceivedMessage
)
*
Message
{
msg
:=
Message
{
Payload
:
message
.
Payload
,
Padding
:
message
.
Padding
,
Timestamp
:
message
.
Sent
,
TTL
:
message
.
TTL
,
PoW
:
message
.
PoW
,
Hash
:
message
.
EnvelopeHash
.
Bytes
(),
Topic
:
message
.
Topic
,
}
if
message
.
Dst
!=
nil
{
b
:=
crypto
.
FromECDSAPub
(
message
.
Dst
)
if
b
!=
nil
{
msg
.
Dst
=
b
}
}
if
isMessageSigned
(
message
.
Raw
[
0
])
{
b
:=
crypto
.
FromECDSAPub
(
message
.
SigToPubKey
())
if
b
!=
nil
{
msg
.
Sig
=
b
}
}
return
&
msg
}
// toMessage converts a set of messages to its RPC representation.
func
toMessage
(
messages
[]
*
ReceivedMessage
)
[]
*
Message
{
msgs
:=
make
([]
*
Message
,
len
(
messages
))
for
i
,
msg
:=
range
messages
{
msgs
[
i
]
=
ToWhisperMessage
(
msg
)
}
return
msgs
}
// GetFilterMessages returns the messages that match the filter criteria and
// are received between the last poll and now.
func
(
api
*
PublicWhisperAPI
)
GetFilterMessages
(
id
string
)
([]
*
Message
,
error
)
{
api
.
mu
.
Lock
()
f
:=
api
.
w
.
GetFilter
(
id
)
if
f
==
nil
{
api
.
mu
.
Unlock
()
return
nil
,
fmt
.
Errorf
(
"filter not found"
)
}
api
.
lastUsed
[
id
]
=
time
.
Now
()
api
.
mu
.
Unlock
()
receivedMessages
:=
f
.
Retrieve
()
messages
:=
make
([]
*
Message
,
0
,
len
(
receivedMessages
))
for
_
,
msg
:=
range
receivedMessages
{
messages
=
append
(
messages
,
ToWhisperMessage
(
msg
))
}
return
messages
,
nil
}
// DeleteMessageFilter deletes a filter.
func
(
api
*
PublicWhisperAPI
)
DeleteMessageFilter
(
id
string
)
(
bool
,
error
)
{
api
.
mu
.
Lock
()
defer
api
.
mu
.
Unlock
()
delete
(
api
.
lastUsed
,
id
)
return
true
,
api
.
w
.
Unsubscribe
(
id
)
}
// NewMessageFilter creates a new filter that can be used to poll for
// (new) messages that satisfy the given criteria.
func
(
api
*
PublicWhisperAPI
)
NewMessageFilter
(
req
Criteria
)
(
string
,
error
)
{
var
(
src
*
ecdsa
.
PublicKey
keySym
[]
byte
keyAsym
*
ecdsa
.
PrivateKey
topics
[][]
byte
symKeyGiven
=
len
(
req
.
SymKeyID
)
>
0
asymKeyGiven
=
len
(
req
.
PrivateKeyID
)
>
0
err
error
)
// user must specify either a symmetric or an asymmetric key
if
(
symKeyGiven
&&
asymKeyGiven
)
||
(
!
symKeyGiven
&&
!
asymKeyGiven
)
{
return
""
,
ErrSymAsym
}
if
len
(
req
.
Sig
)
>
0
{
if
src
,
err
=
crypto
.
UnmarshalPubkey
(
req
.
Sig
);
err
!=
nil
{
return
""
,
ErrInvalidSigningPubKey
}
}
if
symKeyGiven
{
if
keySym
,
err
=
api
.
w
.
GetSymKey
(
req
.
SymKeyID
);
err
!=
nil
{
return
""
,
err
}
if
!
validateSymmetricKey
(
keySym
)
{
return
""
,
ErrInvalidSymmetricKey
}
}
if
asymKeyGiven
{
if
keyAsym
,
err
=
api
.
w
.
GetPrivateKey
(
req
.
PrivateKeyID
);
err
!=
nil
{
return
""
,
err
}
}
if
len
(
req
.
Topics
)
>
0
{
topics
=
make
([][]
byte
,
0
,
len
(
req
.
Topics
))
for
_
,
topic
:=
range
req
.
Topics
{
topics
=
append
(
topics
,
topic
[
:
])
}
}
f
:=
&
Filter
{
Src
:
src
,
KeySym
:
keySym
,
KeyAsym
:
keyAsym
,
PoW
:
req
.
MinPow
,
AllowP2P
:
req
.
AllowP2P
,
Topics
:
topics
,
Messages
:
make
(
map
[
common
.
Hash
]
*
ReceivedMessage
),
}
id
,
err
:=
api
.
w
.
Subscribe
(
f
)
if
err
!=
nil
{
return
""
,
err
}
api
.
mu
.
Lock
()
api
.
lastUsed
[
id
]
=
time
.
Now
()
api
.
mu
.
Unlock
()
return
id
,
nil
}
whisper/whisperv5/benchmarks_test.go
deleted
100644 → 0
View file @
4e9230ea
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package
whisperv5
import
(
"testing"
"github.com/ethereum/go-ethereum/crypto"
)
func
BenchmarkDeriveKeyMaterial
(
b
*
testing
.
B
)
{
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
deriveKeyMaterial
([]
byte
(
"test"
),
0
)
}
}
func
BenchmarkEncryptionSym
(
b
*
testing
.
B
)
{
InitSingleTest
()
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
b
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
msg
,
_
:=
NewSentMessage
(
params
)
_
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
b
.
Errorf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
b
.
Errorf
(
"i = %d, len(msg.Raw) = %d, params.Payload = %d."
,
i
,
len
(
msg
.
Raw
),
len
(
params
.
Payload
))
return
}
}
}
func
BenchmarkEncryptionAsym
(
b
*
testing
.
B
)
{
InitSingleTest
()
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
b
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
key
,
err
:=
crypto
.
GenerateKey
()
if
err
!=
nil
{
b
.
Fatalf
(
"failed GenerateKey with seed %d: %s."
,
seed
,
err
)
}
params
.
KeySym
=
nil
params
.
Dst
=
&
key
.
PublicKey
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
msg
,
_
:=
NewSentMessage
(
params
)
_
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
b
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
}
}
func
BenchmarkDecryptionSymValid
(
b
*
testing
.
B
)
{
InitSingleTest
()
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
b
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
msg
,
_
:=
NewSentMessage
(
params
)
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
b
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
f
:=
Filter
{
KeySym
:
params
.
KeySym
}
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
msg
:=
env
.
Open
(
&
f
)
if
msg
==
nil
{
b
.
Fatalf
(
"failed to open with seed %d."
,
seed
)
}
}
}
func
BenchmarkDecryptionSymInvalid
(
b
*
testing
.
B
)
{
InitSingleTest
()
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
b
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
msg
,
_
:=
NewSentMessage
(
params
)
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
b
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
f
:=
Filter
{
KeySym
:
[]
byte
(
"arbitrary stuff here"
)}
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
msg
:=
env
.
Open
(
&
f
)
if
msg
!=
nil
{
b
.
Fatalf
(
"opened envelope with invalid key, seed: %d."
,
seed
)
}
}
}
func
BenchmarkDecryptionAsymValid
(
b
*
testing
.
B
)
{
InitSingleTest
()
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
b
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
key
,
err
:=
crypto
.
GenerateKey
()
if
err
!=
nil
{
b
.
Fatalf
(
"failed GenerateKey with seed %d: %s."
,
seed
,
err
)
}
f
:=
Filter
{
KeyAsym
:
key
}
params
.
KeySym
=
nil
params
.
Dst
=
&
key
.
PublicKey
msg
,
_
:=
NewSentMessage
(
params
)
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
b
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
msg
:=
env
.
Open
(
&
f
)
if
msg
==
nil
{
b
.
Fatalf
(
"fail to open, seed: %d."
,
seed
)
}
}
}
func
BenchmarkDecryptionAsymInvalid
(
b
*
testing
.
B
)
{
InitSingleTest
()
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
b
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
key
,
err
:=
crypto
.
GenerateKey
()
if
err
!=
nil
{
b
.
Fatalf
(
"failed GenerateKey with seed %d: %s."
,
seed
,
err
)
}
params
.
KeySym
=
nil
params
.
Dst
=
&
key
.
PublicKey
msg
,
_
:=
NewSentMessage
(
params
)
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
b
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
key
,
err
=
crypto
.
GenerateKey
()
if
err
!=
nil
{
b
.
Fatalf
(
"failed GenerateKey with seed %d: %s."
,
seed
,
err
)
}
f
:=
Filter
{
KeyAsym
:
key
}
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
msg
:=
env
.
Open
(
&
f
)
if
msg
!=
nil
{
b
.
Fatalf
(
"opened envelope with invalid key, seed: %d."
,
seed
)
}
}
}
func
increment
(
x
[]
byte
)
{
for
i
:=
0
;
i
<
len
(
x
);
i
++
{
x
[
i
]
++
if
x
[
i
]
!=
0
{
break
}
}
}
func
BenchmarkPoW
(
b
*
testing
.
B
)
{
InitSingleTest
()
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
b
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
params
.
Payload
=
make
([]
byte
,
32
)
params
.
PoW
=
10.0
params
.
TTL
=
1
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
increment
(
params
.
Payload
)
msg
,
_
:=
NewSentMessage
(
params
)
_
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
b
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
}
}
whisper/whisperv5/config.go
deleted
100644 → 0
View file @
4e9230ea
// 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/>.
package
whisperv5
type
Config
struct
{
MaxMessageSize
uint32
`toml:",omitempty"`
MinimumAcceptedPOW
float64
`toml:",omitempty"`
}
var
DefaultConfig
=
Config
{
MaxMessageSize
:
DefaultMaxMessageSize
,
MinimumAcceptedPOW
:
DefaultMinimumPoW
,
}
whisper/whisperv5/doc.go
deleted
100644 → 0
View file @
4e9230ea
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
/*
Package whisperv5 implements the Whisper protocol (version 5).
Whisper combines aspects of both DHTs and datagram messaging systems (e.g. UDP).
As such it may be likened and compared to both, not dissimilar to the
matter/energy duality (apologies to physicists for the blatant abuse of a
fundamental and beautiful natural principle).
Whisper is a pure identity-based messaging system. Whisper provides a low-level
(non-application-specific) but easily-accessible API without being based upon
or prejudiced by the low-level hardware attributes and characteristics,
particularly the notion of singular endpoints.
*/
package
whisperv5
import
(
"fmt"
"time"
)
const
(
EnvelopeVersion
=
uint64
(
0
)
ProtocolVersion
=
uint64
(
5
)
ProtocolVersionStr
=
"5.0"
ProtocolName
=
"shh"
statusCode
=
0
// used by whisper protocol
messagesCode
=
1
// normal whisper message
p2pCode
=
2
// peer-to-peer message (to be consumed by the peer, but not forwarded any further)
p2pRequestCode
=
3
// peer-to-peer message, used by Dapp protocol
NumberOfMessageCodes
=
64
paddingMask
=
byte
(
3
)
signatureFlag
=
byte
(
4
)
TopicLength
=
4
signatureLength
=
65
aesKeyLength
=
32
AESNonceLength
=
12
keyIdSize
=
32
MaxMessageSize
=
uint32
(
10
*
1024
*
1024
)
// maximum accepted size of a message.
DefaultMaxMessageSize
=
uint32
(
1024
*
1024
)
DefaultMinimumPoW
=
0.2
padSizeLimit
=
256
// just an arbitrary number, could be changed without breaking the protocol (must not exceed 2^24)
messageQueueLimit
=
1024
expirationCycle
=
time
.
Second
transmissionCycle
=
300
*
time
.
Millisecond
DefaultTTL
=
50
// seconds
SynchAllowance
=
10
// seconds
)
type
unknownVersionError
uint64
func
(
e
unknownVersionError
)
Error
()
string
{
return
fmt
.
Sprintf
(
"invalid envelope version %d"
,
uint64
(
e
))
}
// MailServer represents a mail server, capable of
// archiving the old messages for subsequent delivery
// to the peers. Any implementation must ensure that both
// functions are thread-safe. Also, they must return ASAP.
// DeliverMail should use directMessagesCode for delivery,
// in order to bypass the expiry checks.
type
MailServer
interface
{
Archive
(
env
*
Envelope
)
DeliverMail
(
whisperPeer
*
Peer
,
request
*
Envelope
)
}
whisper/whisperv5/envelope.go
deleted
100644 → 0
View file @
4e9230ea
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Contains the Whisper protocol Envelope element.
package
whisperv5
import
(
"crypto/ecdsa"
"encoding/binary"
"fmt"
gmath
"math"
"math/big"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/ethereum/go-ethereum/rlp"
)
// Envelope represents a clear-text data packet to transmit through the Whisper
// network. Its contents may or may not be encrypted and signed.
type
Envelope
struct
{
Version
[]
byte
Expiry
uint32
TTL
uint32
Topic
TopicType
AESNonce
[]
byte
Data
[]
byte
EnvNonce
uint64
pow
float64
// Message-specific PoW as described in the Whisper specification.
hash
common
.
Hash
// Cached hash of the envelope to avoid rehashing every time.
// Don't access hash directly, use Hash() function instead.
}
// size returns the size of envelope as it is sent (i.e. public fields only)
func
(
e
*
Envelope
)
size
()
int
{
return
20
+
len
(
e
.
Version
)
+
len
(
e
.
AESNonce
)
+
len
(
e
.
Data
)
}
// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce.
func
(
e
*
Envelope
)
rlpWithoutNonce
()
[]
byte
{
res
,
_
:=
rlp
.
EncodeToBytes
([]
interface
{}{
e
.
Version
,
e
.
Expiry
,
e
.
TTL
,
e
.
Topic
,
e
.
AESNonce
,
e
.
Data
})
return
res
}
// NewEnvelope wraps a Whisper message with expiration and destination data
// included into an envelope for network forwarding.
func
NewEnvelope
(
ttl
uint32
,
topic
TopicType
,
aesNonce
[]
byte
,
msg
*
sentMessage
)
*
Envelope
{
env
:=
Envelope
{
Version
:
make
([]
byte
,
1
),
Expiry
:
uint32
(
time
.
Now
()
.
Add
(
time
.
Second
*
time
.
Duration
(
ttl
))
.
Unix
()),
TTL
:
ttl
,
Topic
:
topic
,
AESNonce
:
aesNonce
,
Data
:
msg
.
Raw
,
EnvNonce
:
0
,
}
if
EnvelopeVersion
<
256
{
env
.
Version
[
0
]
=
byte
(
EnvelopeVersion
)
}
else
{
panic
(
"please increase the size of Envelope.Version before releasing this version"
)
}
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
// of work on hashing the data.
func
(
e
*
Envelope
)
Seal
(
options
*
MessageParams
)
error
{
var
target
,
bestBit
int
if
options
.
PoW
==
0
{
// adjust for the duration of Seal() execution only if execution time is predefined unconditionally
e
.
Expiry
+=
options
.
WorkTime
}
else
{
target
=
e
.
powToFirstBit
(
options
.
PoW
)
if
target
<
1
{
target
=
1
}
}
buf
:=
make
([]
byte
,
64
)
h
:=
crypto
.
Keccak256
(
e
.
rlpWithoutNonce
())
copy
(
buf
[
:
32
],
h
)
finish
:=
time
.
Now
()
.
Add
(
time
.
Duration
(
options
.
WorkTime
)
*
time
.
Second
)
.
UnixNano
()
for
nonce
:=
uint64
(
0
);
time
.
Now
()
.
UnixNano
()
<
finish
;
{
for
i
:=
0
;
i
<
1024
;
i
++
{
binary
.
BigEndian
.
PutUint64
(
buf
[
56
:
],
nonce
)
d
:=
new
(
big
.
Int
)
.
SetBytes
(
crypto
.
Keccak256
(
buf
))
firstBit
:=
math
.
FirstBitSet
(
d
)
if
firstBit
>
bestBit
{
e
.
EnvNonce
,
bestBit
=
nonce
,
firstBit
if
target
>
0
&&
bestBit
>=
target
{
return
nil
}
}
nonce
++
}
}
if
target
>
0
&&
bestBit
<
target
{
return
fmt
.
Errorf
(
"failed to reach the PoW target, specified pow time (%d seconds) was insufficient"
,
options
.
WorkTime
)
}
return
nil
}
func
(
e
*
Envelope
)
PoW
()
float64
{
if
e
.
pow
==
0
{
e
.
calculatePoW
(
0
)
}
return
e
.
pow
}
func
(
e
*
Envelope
)
calculatePoW
(
diff
uint32
)
{
buf
:=
make
([]
byte
,
64
)
h
:=
crypto
.
Keccak256
(
e
.
rlpWithoutNonce
())
copy
(
buf
[
:
32
],
h
)
binary
.
BigEndian
.
PutUint64
(
buf
[
56
:
],
e
.
EnvNonce
)
d
:=
new
(
big
.
Int
)
.
SetBytes
(
crypto
.
Keccak256
(
buf
))
firstBit
:=
math
.
FirstBitSet
(
d
)
x
:=
gmath
.
Pow
(
2
,
float64
(
firstBit
))
x
/=
float64
(
e
.
size
())
x
/=
float64
(
e
.
TTL
+
diff
)
e
.
pow
=
x
}
func
(
e
*
Envelope
)
powToFirstBit
(
pow
float64
)
int
{
x
:=
pow
x
*=
float64
(
e
.
size
())
x
*=
float64
(
e
.
TTL
)
bits
:=
gmath
.
Log2
(
x
)
bits
=
gmath
.
Ceil
(
bits
)
return
int
(
bits
)
}
// Hash returns the SHA3 hash of the envelope, calculating it if not yet done.
func
(
e
*
Envelope
)
Hash
()
common
.
Hash
{
if
(
e
.
hash
==
common
.
Hash
{})
{
encoded
,
_
:=
rlp
.
EncodeToBytes
(
e
)
e
.
hash
=
crypto
.
Keccak256Hash
(
encoded
)
}
return
e
.
hash
}
// DecodeRLP decodes an Envelope from an RLP data stream.
func
(
e
*
Envelope
)
DecodeRLP
(
s
*
rlp
.
Stream
)
error
{
raw
,
err
:=
s
.
Raw
()
if
err
!=
nil
{
return
err
}
// The decoding of Envelope uses the struct fields but also needs
// to compute the hash of the whole RLP-encoded envelope. This
// type has the same structure as Envelope but is not an
// rlp.Decoder (does not implement DecodeRLP function).
// Only public members will be encoded.
type
rlpenv
Envelope
if
err
:=
rlp
.
DecodeBytes
(
raw
,
(
*
rlpenv
)(
e
));
err
!=
nil
{
return
err
}
e
.
hash
=
crypto
.
Keccak256Hash
(
raw
)
return
nil
}
// OpenAsymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
func
(
e
*
Envelope
)
OpenAsymmetric
(
key
*
ecdsa
.
PrivateKey
)
(
*
ReceivedMessage
,
error
)
{
message
:=
&
ReceivedMessage
{
Raw
:
e
.
Data
}
err
:=
message
.
decryptAsymmetric
(
key
)
switch
err
{
case
nil
:
return
message
,
nil
case
ecies
.
ErrInvalidPublicKey
:
// addressed to somebody else
return
nil
,
err
default
:
return
nil
,
fmt
.
Errorf
(
"unable to open envelope, decrypt failed: %v"
,
err
)
}
}
// OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
func
(
e
*
Envelope
)
OpenSymmetric
(
key
[]
byte
)
(
msg
*
ReceivedMessage
,
err
error
)
{
msg
=
&
ReceivedMessage
{
Raw
:
e
.
Data
}
err
=
msg
.
decryptSymmetric
(
key
,
e
.
AESNonce
)
if
err
!=
nil
{
msg
=
nil
}
return
msg
,
err
}
// Open tries to decrypt an envelope, and populates the message fields in case of success.
func
(
e
*
Envelope
)
Open
(
watcher
*
Filter
)
(
msg
*
ReceivedMessage
)
{
if
e
.
isAsymmetric
()
{
msg
,
_
=
e
.
OpenAsymmetric
(
watcher
.
KeyAsym
)
if
msg
!=
nil
{
msg
.
Dst
=
&
watcher
.
KeyAsym
.
PublicKey
}
}
else
if
e
.
IsSymmetric
()
{
msg
,
_
=
e
.
OpenSymmetric
(
watcher
.
KeySym
)
if
msg
!=
nil
{
msg
.
SymKeyHash
=
crypto
.
Keccak256Hash
(
watcher
.
KeySym
)
}
}
if
msg
!=
nil
{
ok
:=
msg
.
Validate
()
if
!
ok
{
return
nil
}
msg
.
Topic
=
e
.
Topic
msg
.
PoW
=
e
.
PoW
()
msg
.
TTL
=
e
.
TTL
msg
.
Sent
=
e
.
Expiry
-
e
.
TTL
msg
.
EnvelopeHash
=
e
.
Hash
()
msg
.
EnvelopeVersion
=
e
.
Ver
()
}
return
msg
}
whisper/whisperv5/filter.go
deleted
100644 → 0
View file @
4e9230ea
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package
whisperv5
import
(
"crypto/ecdsa"
"fmt"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
)
type
Filter
struct
{
Src
*
ecdsa
.
PublicKey
// Sender of the message
KeyAsym
*
ecdsa
.
PrivateKey
// Private Key of recipient
KeySym
[]
byte
// Key associated with the Topic
Topics
[][]
byte
// Topics to filter messages with
PoW
float64
// Proof of work as described in the Whisper spec
AllowP2P
bool
// Indicates whether this filter is interested in direct peer-to-peer messages
SymKeyHash
common
.
Hash
// The Keccak256Hash of the symmetric key, needed for optimization
Messages
map
[
common
.
Hash
]
*
ReceivedMessage
mutex
sync
.
RWMutex
}
type
Filters
struct
{
watchers
map
[
string
]
*
Filter
whisper
*
Whisper
mutex
sync
.
RWMutex
}
func
NewFilters
(
w
*
Whisper
)
*
Filters
{
return
&
Filters
{
watchers
:
make
(
map
[
string
]
*
Filter
),
whisper
:
w
,
}
}
func
(
fs
*
Filters
)
Install
(
watcher
*
Filter
)
(
string
,
error
)
{
if
watcher
.
Messages
==
nil
{
watcher
.
Messages
=
make
(
map
[
common
.
Hash
]
*
ReceivedMessage
)
}
id
,
err
:=
GenerateRandomID
()
if
err
!=
nil
{
return
""
,
err
}
fs
.
mutex
.
Lock
()
defer
fs
.
mutex
.
Unlock
()
if
fs
.
watchers
[
id
]
!=
nil
{
return
""
,
fmt
.
Errorf
(
"failed to generate unique ID"
)
}
if
watcher
.
expectsSymmetricEncryption
()
{
watcher
.
SymKeyHash
=
crypto
.
Keccak256Hash
(
watcher
.
KeySym
)
}
fs
.
watchers
[
id
]
=
watcher
return
id
,
err
}
func
(
fs
*
Filters
)
Uninstall
(
id
string
)
bool
{
fs
.
mutex
.
Lock
()
defer
fs
.
mutex
.
Unlock
()
if
fs
.
watchers
[
id
]
!=
nil
{
delete
(
fs
.
watchers
,
id
)
return
true
}
return
false
}
func
(
fs
*
Filters
)
Get
(
id
string
)
*
Filter
{
fs
.
mutex
.
RLock
()
defer
fs
.
mutex
.
RUnlock
()
return
fs
.
watchers
[
id
]
}
func
(
fs
*
Filters
)
NotifyWatchers
(
env
*
Envelope
,
p2pMessage
bool
)
{
var
msg
*
ReceivedMessage
fs
.
mutex
.
RLock
()
defer
fs
.
mutex
.
RUnlock
()
i
:=
-
1
// only used for logging info
for
_
,
watcher
:=
range
fs
.
watchers
{
i
++
if
p2pMessage
&&
!
watcher
.
AllowP2P
{
log
.
Trace
(
fmt
.
Sprintf
(
"msg [%x], filter [%d]: p2p messages are not allowed"
,
env
.
Hash
(),
i
))
continue
}
var
match
bool
if
msg
!=
nil
{
match
=
watcher
.
MatchMessage
(
msg
)
}
else
{
match
=
watcher
.
MatchEnvelope
(
env
)
if
match
{
msg
=
env
.
Open
(
watcher
)
if
msg
==
nil
{
log
.
Trace
(
"processing message: failed to open"
,
"message"
,
env
.
Hash
()
.
Hex
(),
"filter"
,
i
)
}
}
else
{
log
.
Trace
(
"processing message: does not match"
,
"message"
,
env
.
Hash
()
.
Hex
(),
"filter"
,
i
)
}
}
if
match
&&
msg
!=
nil
{
log
.
Trace
(
"processing message: decrypted"
,
"hash"
,
env
.
Hash
()
.
Hex
())
if
watcher
.
Src
==
nil
||
IsPubKeyEqual
(
msg
.
Src
,
watcher
.
Src
)
{
watcher
.
Trigger
(
msg
)
}
}
}
}
func
(
f
*
Filter
)
processEnvelope
(
env
*
Envelope
)
*
ReceivedMessage
{
if
f
.
MatchEnvelope
(
env
)
{
msg
:=
env
.
Open
(
f
)
if
msg
!=
nil
{
return
msg
}
else
{
log
.
Trace
(
"processing envelope: failed to open"
,
"hash"
,
env
.
Hash
()
.
Hex
())
}
}
else
{
log
.
Trace
(
"processing envelope: does not match"
,
"hash"
,
env
.
Hash
()
.
Hex
())
}
return
nil
}
func
(
f
*
Filter
)
expectsAsymmetricEncryption
()
bool
{
return
f
.
KeyAsym
!=
nil
}
func
(
f
*
Filter
)
expectsSymmetricEncryption
()
bool
{
return
f
.
KeySym
!=
nil
}
func
(
f
*
Filter
)
Trigger
(
msg
*
ReceivedMessage
)
{
f
.
mutex
.
Lock
()
defer
f
.
mutex
.
Unlock
()
if
_
,
exist
:=
f
.
Messages
[
msg
.
EnvelopeHash
];
!
exist
{
f
.
Messages
[
msg
.
EnvelopeHash
]
=
msg
}
}
func
(
f
*
Filter
)
Retrieve
()
(
all
[]
*
ReceivedMessage
)
{
f
.
mutex
.
Lock
()
defer
f
.
mutex
.
Unlock
()
all
=
make
([]
*
ReceivedMessage
,
0
,
len
(
f
.
Messages
))
for
_
,
msg
:=
range
f
.
Messages
{
all
=
append
(
all
,
msg
)
}
f
.
Messages
=
make
(
map
[
common
.
Hash
]
*
ReceivedMessage
)
// delete old messages
return
all
}
func
(
f
*
Filter
)
MatchMessage
(
msg
*
ReceivedMessage
)
bool
{
if
f
.
PoW
>
0
&&
msg
.
PoW
<
f
.
PoW
{
return
false
}
if
f
.
expectsAsymmetricEncryption
()
&&
msg
.
isAsymmetricEncryption
()
{
return
IsPubKeyEqual
(
&
f
.
KeyAsym
.
PublicKey
,
msg
.
Dst
)
&&
f
.
MatchTopic
(
msg
.
Topic
)
}
else
if
f
.
expectsSymmetricEncryption
()
&&
msg
.
isSymmetricEncryption
()
{
return
f
.
SymKeyHash
==
msg
.
SymKeyHash
&&
f
.
MatchTopic
(
msg
.
Topic
)
}
return
false
}
func
(
f
*
Filter
)
MatchEnvelope
(
envelope
*
Envelope
)
bool
{
if
f
.
PoW
>
0
&&
envelope
.
pow
<
f
.
PoW
{
return
false
}
if
f
.
expectsAsymmetricEncryption
()
&&
envelope
.
isAsymmetric
()
{
return
f
.
MatchTopic
(
envelope
.
Topic
)
}
else
if
f
.
expectsSymmetricEncryption
()
&&
envelope
.
IsSymmetric
()
{
return
f
.
MatchTopic
(
envelope
.
Topic
)
}
return
false
}
func
(
f
*
Filter
)
MatchTopic
(
topic
TopicType
)
bool
{
if
len
(
f
.
Topics
)
==
0
{
// any topic matches
return
true
}
for
_
,
bt
:=
range
f
.
Topics
{
if
matchSingleTopic
(
topic
,
bt
)
{
return
true
}
}
return
false
}
func
matchSingleTopic
(
topic
TopicType
,
bt
[]
byte
)
bool
{
if
len
(
bt
)
>
TopicLength
{
bt
=
bt
[
:
TopicLength
]
}
if
len
(
bt
)
==
0
{
return
false
}
for
j
,
b
:=
range
bt
{
if
topic
[
j
]
!=
b
{
return
false
}
}
return
true
}
func
IsPubKeyEqual
(
a
,
b
*
ecdsa
.
PublicKey
)
bool
{
if
!
ValidatePublicKey
(
a
)
{
return
false
}
else
if
!
ValidatePublicKey
(
b
)
{
return
false
}
// the curve is always the same, just compare the points
return
a
.
X
.
Cmp
(
b
.
X
)
==
0
&&
a
.
Y
.
Cmp
(
b
.
Y
)
==
0
}
whisper/whisperv5/filter_test.go
deleted
100644 → 0
View file @
4e9230ea
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package
whisperv5
import
(
"math/big"
mrand
"math/rand"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
var
seed
int64
// InitSingleTest should be called in the beginning of every
// test, which uses RNG, in order to make the tests
// reproduciblity independent of their sequence.
func
InitSingleTest
()
{
seed
=
time
.
Now
()
.
Unix
()
mrand
.
Seed
(
seed
)
}
func
InitDebugTest
(
i
int64
)
{
seed
=
i
mrand
.
Seed
(
seed
)
}
type
FilterTestCase
struct
{
f
*
Filter
id
string
alive
bool
msgCnt
int
}
func
generateFilter
(
t
*
testing
.
T
,
symmetric
bool
)
(
*
Filter
,
error
)
{
var
f
Filter
f
.
Messages
=
make
(
map
[
common
.
Hash
]
*
ReceivedMessage
)
const
topicNum
=
8
f
.
Topics
=
make
([][]
byte
,
topicNum
)
for
i
:=
0
;
i
<
topicNum
;
i
++
{
f
.
Topics
[
i
]
=
make
([]
byte
,
4
)
mrand
.
Read
(
f
.
Topics
[
i
])
f
.
Topics
[
i
][
0
]
=
0x01
}
key
,
err
:=
crypto
.
GenerateKey
()
if
err
!=
nil
{
t
.
Fatalf
(
"generateFilter 1 failed with seed %d."
,
seed
)
return
nil
,
err
}
f
.
Src
=
&
key
.
PublicKey
if
symmetric
{
f
.
KeySym
=
make
([]
byte
,
aesKeyLength
)
mrand
.
Read
(
f
.
KeySym
)
f
.
SymKeyHash
=
crypto
.
Keccak256Hash
(
f
.
KeySym
)
}
else
{
f
.
KeyAsym
,
err
=
crypto
.
GenerateKey
()
if
err
!=
nil
{
t
.
Fatalf
(
"generateFilter 2 failed with seed %d."
,
seed
)
return
nil
,
err
}
}
// AcceptP2P & PoW are not set
return
&
f
,
nil
}
func
generateTestCases
(
t
*
testing
.
T
,
SizeTestFilters
int
)
[]
FilterTestCase
{
cases
:=
make
([]
FilterTestCase
,
SizeTestFilters
)
for
i
:=
0
;
i
<
SizeTestFilters
;
i
++
{
f
,
_
:=
generateFilter
(
t
,
true
)
cases
[
i
]
.
f
=
f
cases
[
i
]
.
alive
=
mrand
.
Int
()
&
int
(
1
)
==
0
}
return
cases
}
func
TestInstallFilters
(
t
*
testing
.
T
)
{
InitSingleTest
()
const
SizeTestFilters
=
256
w
:=
New
(
&
Config
{})
filters
:=
NewFilters
(
w
)
tst
:=
generateTestCases
(
t
,
SizeTestFilters
)
var
err
error
var
j
string
for
i
:=
0
;
i
<
SizeTestFilters
;
i
++
{
j
,
err
=
filters
.
Install
(
tst
[
i
]
.
f
)
if
err
!=
nil
{
t
.
Fatalf
(
"seed %d: failed to install filter: %s"
,
seed
,
err
)
}
tst
[
i
]
.
id
=
j
if
len
(
j
)
!=
keyIdSize
*
2
{
t
.
Fatalf
(
"seed %d: wrong filter id size [%d]"
,
seed
,
len
(
j
))
}
}
for
_
,
testCase
:=
range
tst
{
if
!
testCase
.
alive
{
filters
.
Uninstall
(
testCase
.
id
)
}
}
for
i
,
testCase
:=
range
tst
{
fil
:=
filters
.
Get
(
testCase
.
id
)
exist
:=
fil
!=
nil
if
exist
!=
testCase
.
alive
{
t
.
Fatalf
(
"seed %d: failed alive: %d, %v, %v"
,
seed
,
i
,
exist
,
testCase
.
alive
)
}
if
exist
&&
fil
.
PoW
!=
testCase
.
f
.
PoW
{
t
.
Fatalf
(
"seed %d: failed Get: %d, %v, %v"
,
seed
,
i
,
exist
,
testCase
.
alive
)
}
}
}
func
TestInstallSymKeyGeneratesHash
(
t
*
testing
.
T
)
{
InitSingleTest
()
w
:=
New
(
&
Config
{})
filters
:=
NewFilters
(
w
)
filter
,
_
:=
generateFilter
(
t
,
true
)
// save the current SymKeyHash for comparison
initialSymKeyHash
:=
filter
.
SymKeyHash
// ensure the SymKeyHash is invalid, for Install to recreate it
var
invalid
common
.
Hash
filter
.
SymKeyHash
=
invalid
_
,
err
:=
filters
.
Install
(
filter
)
if
err
!=
nil
{
t
.
Fatalf
(
"Error installing the filter: %s"
,
err
)
}
for
i
,
b
:=
range
filter
.
SymKeyHash
{
if
b
!=
initialSymKeyHash
[
i
]
{
t
.
Fatalf
(
"The filter's symmetric key hash was not properly generated by Install"
)
}
}
}
func
TestInstallIdenticalFilters
(
t
*
testing
.
T
)
{
InitSingleTest
()
w
:=
New
(
&
Config
{})
filters
:=
NewFilters
(
w
)
filter1
,
_
:=
generateFilter
(
t
,
true
)
// Copy the first filter since some of its fields
// are randomly gnerated.
filter2
:=
&
Filter
{
KeySym
:
filter1
.
KeySym
,
Topics
:
filter1
.
Topics
,
PoW
:
filter1
.
PoW
,
AllowP2P
:
filter1
.
AllowP2P
,
Messages
:
make
(
map
[
common
.
Hash
]
*
ReceivedMessage
),
}
_
,
err
:=
filters
.
Install
(
filter1
)
if
err
!=
nil
{
t
.
Fatalf
(
"Error installing the first filter with seed %d: %s"
,
seed
,
err
)
}
_
,
err
=
filters
.
Install
(
filter2
)
if
err
!=
nil
{
t
.
Fatalf
(
"Error installing the second filter with seed %d: %s"
,
seed
,
err
)
}
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"Error generating message parameters with seed %d: %s"
,
seed
,
err
)
}
params
.
KeySym
=
filter1
.
KeySym
params
.
Topic
=
BytesToTopic
(
filter1
.
Topics
[
0
])
filter1
.
Src
=
&
params
.
Src
.
PublicKey
filter2
.
Src
=
&
params
.
Src
.
PublicKey
sentMessage
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
:=
sentMessage
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
msg
:=
env
.
Open
(
filter1
)
if
msg
==
nil
{
t
.
Fatalf
(
"failed to Open with filter1"
)
}
if
!
filter1
.
MatchEnvelope
(
env
)
{
t
.
Fatalf
(
"failed matching with the first filter"
)
}
if
!
filter2
.
MatchEnvelope
(
env
)
{
t
.
Fatalf
(
"failed matching with the first filter"
)
}
if
!
filter1
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed matching with the second filter"
)
}
if
!
filter2
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed matching with the second filter"
)
}
}
func
TestComparePubKey
(
t
*
testing
.
T
)
{
InitSingleTest
()
key1
,
err
:=
crypto
.
GenerateKey
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed to generate first key with seed %d: %s."
,
seed
,
err
)
}
key2
,
err
:=
crypto
.
GenerateKey
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed to generate second key with seed %d: %s."
,
seed
,
err
)
}
if
IsPubKeyEqual
(
&
key1
.
PublicKey
,
&
key2
.
PublicKey
)
{
t
.
Fatalf
(
"public keys are equal, seed %d."
,
seed
)
}
// generate key3 == key1
mrand
.
Seed
(
seed
)
key3
,
err
:=
crypto
.
GenerateKey
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed to generate third key with seed %d: %s."
,
seed
,
err
)
}
if
IsPubKeyEqual
(
&
key1
.
PublicKey
,
&
key3
.
PublicKey
)
{
t
.
Fatalf
(
"key1 == key3, seed %d."
,
seed
)
}
}
func
TestMatchEnvelope
(
t
*
testing
.
T
)
{
InitSingleTest
()
fsym
,
err
:=
generateFilter
(
t
,
true
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateFilter with seed %d: %s."
,
seed
,
err
)
}
fasym
,
err
:=
generateFilter
(
t
,
false
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateFilter() with seed %d: %s."
,
seed
,
err
)
}
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
params
.
Topic
[
0
]
=
0xFF
// ensure mismatch
// mismatch with pseudo-random data
msg
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
match
:=
fsym
.
MatchEnvelope
(
env
)
if
match
{
t
.
Fatalf
(
"failed MatchEnvelope symmetric with seed %d."
,
seed
)
}
match
=
fasym
.
MatchEnvelope
(
env
)
if
match
{
t
.
Fatalf
(
"failed MatchEnvelope asymmetric with seed %d."
,
seed
)
}
// encrypt symmetrically
i
:=
mrand
.
Int
()
%
4
fsym
.
Topics
[
i
]
=
params
.
Topic
[
:
]
fasym
.
Topics
[
i
]
=
params
.
Topic
[
:
]
msg
,
err
=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap() with seed %d: %s."
,
seed
,
err
)
}
// symmetric + matching topic: match
match
=
fsym
.
MatchEnvelope
(
env
)
if
!
match
{
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
fsym
.
PoW
=
env
.
PoW
()
+
1.0
match
=
fsym
.
MatchEnvelope
(
env
)
if
match
{
t
.
Fatalf
(
"failed MatchEnvelope(symmetric + matching topic + insufficient PoW) asymmetric with seed %d."
,
seed
)
}
// symmetric + matching topic + sufficient PoW: match
fsym
.
PoW
=
env
.
PoW
()
/
2
match
=
fsym
.
MatchEnvelope
(
env
)
if
!
match
{
t
.
Fatalf
(
"failed MatchEnvelope(symmetric + matching topic + sufficient PoW) with seed %d."
,
seed
)
}
// symmetric + topics are nil (wildcard): match
prevTopics
:=
fsym
.
Topics
fsym
.
Topics
=
nil
match
=
fsym
.
MatchEnvelope
(
env
)
if
!
match
{
t
.
Fatalf
(
"failed MatchEnvelope(symmetric + topics are nil) with seed %d."
,
seed
)
}
fsym
.
Topics
=
prevTopics
// encrypt asymmetrically
key
,
err
:=
crypto
.
GenerateKey
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed GenerateKey with seed %d: %s."
,
seed
,
err
)
}
params
.
KeySym
=
nil
params
.
Dst
=
&
key
.
PublicKey
msg
,
err
=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap() with seed %d: %s."
,
seed
,
err
)
}
// encryption method mismatch
match
=
fsym
.
MatchEnvelope
(
env
)
if
match
{
t
.
Fatalf
(
"failed MatchEnvelope(encryption method mismatch) with seed %d."
,
seed
)
}
// asymmetric + mismatching topic: mismatch
match
=
fasym
.
MatchEnvelope
(
env
)
if
!
match
{
t
.
Fatalf
(
"failed MatchEnvelope(asymmetric + mismatching topic) with seed %d."
,
seed
)
}
// asymmetric + matching topic: match
fasym
.
Topics
[
i
]
=
fasym
.
Topics
[
i
+
1
]
match
=
fasym
.
MatchEnvelope
(
env
)
if
match
{
t
.
Fatalf
(
"failed MatchEnvelope(asymmetric + matching topic) with seed %d."
,
seed
)
}
// asymmetric + filter without topic (wildcard): match
fasym
.
Topics
=
nil
match
=
fasym
.
MatchEnvelope
(
env
)
if
!
match
{
t
.
Fatalf
(
"failed MatchEnvelope(asymmetric + filter without topic) with seed %d."
,
seed
)
}
// asymmetric + insufficient PoW: mismatch
fasym
.
PoW
=
env
.
PoW
()
+
1.0
match
=
fasym
.
MatchEnvelope
(
env
)
if
match
{
t
.
Fatalf
(
"failed MatchEnvelope(asymmetric + insufficient PoW) with seed %d."
,
seed
)
}
// asymmetric + sufficient PoW: match
fasym
.
PoW
=
env
.
PoW
()
/
2
match
=
fasym
.
MatchEnvelope
(
env
)
if
!
match
{
t
.
Fatalf
(
"failed MatchEnvelope(asymmetric + sufficient PoW) with seed %d."
,
seed
)
}
// filter without topic + envelope without topic: match
env
.
Topic
=
TopicType
{}
match
=
fasym
.
MatchEnvelope
(
env
)
if
!
match
{
t
.
Fatalf
(
"failed MatchEnvelope(filter without topic + envelope without topic) with seed %d."
,
seed
)
}
// filter with topic + envelope without topic: mismatch
fasym
.
Topics
=
fsym
.
Topics
match
=
fasym
.
MatchEnvelope
(
env
)
if
match
{
t
.
Fatalf
(
"failed MatchEnvelope(filter without topic + envelope without topic) with seed %d."
,
seed
)
}
}
func
TestMatchMessageSym
(
t
*
testing
.
T
)
{
InitSingleTest
()
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
f
,
err
:=
generateFilter
(
t
,
true
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateFilter with seed %d: %s."
,
seed
,
err
)
}
const
index
=
1
params
.
KeySym
=
f
.
KeySym
params
.
Topic
=
BytesToTopic
(
f
.
Topics
[
index
])
sentMessage
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
:=
sentMessage
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
msg
:=
env
.
Open
(
f
)
if
msg
==
nil
{
t
.
Fatalf
(
"failed Open with seed %d."
,
seed
)
}
// Src: match
*
f
.
Src
.
X
=
*
params
.
Src
.
PublicKey
.
X
*
f
.
Src
.
Y
=
*
params
.
Src
.
PublicKey
.
Y
if
!
f
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed MatchEnvelope(src match) with seed %d."
,
seed
)
}
// insufficient PoW: mismatch
f
.
PoW
=
msg
.
PoW
+
1.0
if
f
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed MatchEnvelope(insufficient PoW) with seed %d."
,
seed
)
}
// sufficient PoW: match
f
.
PoW
=
msg
.
PoW
/
2
if
!
f
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed MatchEnvelope(sufficient PoW) with seed %d."
,
seed
)
}
// topic mismatch
f
.
Topics
[
index
][
0
]
++
if
f
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed MatchEnvelope(topic mismatch) with seed %d."
,
seed
)
}
f
.
Topics
[
index
][
0
]
--
// key mismatch
f
.
SymKeyHash
[
0
]
++
if
f
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed MatchEnvelope(key mismatch) with seed %d."
,
seed
)
}
f
.
SymKeyHash
[
0
]
--
// Src absent: match
f
.
Src
=
nil
if
!
f
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed MatchEnvelope(src absent) with seed %d."
,
seed
)
}
// key hash mismatch
h
:=
f
.
SymKeyHash
f
.
SymKeyHash
=
common
.
Hash
{}
if
f
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed MatchEnvelope(key hash mismatch) with seed %d."
,
seed
)
}
f
.
SymKeyHash
=
h
if
!
f
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed MatchEnvelope(key hash match) with seed %d."
,
seed
)
}
// encryption method mismatch
f
.
KeySym
=
nil
f
.
KeyAsym
,
err
=
crypto
.
GenerateKey
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed GenerateKey with seed %d: %s."
,
seed
,
err
)
}
if
f
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed MatchEnvelope(encryption method mismatch) with seed %d."
,
seed
)
}
}
func
TestMatchMessageAsym
(
t
*
testing
.
T
)
{
InitSingleTest
()
f
,
err
:=
generateFilter
(
t
,
false
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateFilter with seed %d: %s."
,
seed
,
err
)
}
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
const
index
=
1
params
.
Topic
=
BytesToTopic
(
f
.
Topics
[
index
])
params
.
Dst
=
&
f
.
KeyAsym
.
PublicKey
keySymOrig
:=
params
.
KeySym
params
.
KeySym
=
nil
sentMessage
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
:=
sentMessage
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
msg
:=
env
.
Open
(
f
)
if
msg
==
nil
{
t
.
Fatalf
(
"failed to open with seed %d."
,
seed
)
}
// Src: match
*
f
.
Src
.
X
=
*
params
.
Src
.
PublicKey
.
X
*
f
.
Src
.
Y
=
*
params
.
Src
.
PublicKey
.
Y
if
!
f
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed MatchMessage(src match) with seed %d."
,
seed
)
}
// insufficient PoW: mismatch
f
.
PoW
=
msg
.
PoW
+
1.0
if
f
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed MatchEnvelope(insufficient PoW) with seed %d."
,
seed
)
}
// sufficient PoW: match
f
.
PoW
=
msg
.
PoW
/
2
if
!
f
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed MatchEnvelope(sufficient PoW) with seed %d."
,
seed
)
}
// topic mismatch
f
.
Topics
[
index
][
0
]
++
if
f
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed MatchEnvelope(topic mismatch) with seed %d."
,
seed
)
}
f
.
Topics
[
index
][
0
]
--
// key mismatch
prev
:=
*
f
.
KeyAsym
.
PublicKey
.
X
zero
:=
*
big
.
NewInt
(
0
)
*
f
.
KeyAsym
.
PublicKey
.
X
=
zero
if
f
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed MatchEnvelope(key mismatch) with seed %d."
,
seed
)
}
*
f
.
KeyAsym
.
PublicKey
.
X
=
prev
// Src absent: match
f
.
Src
=
nil
if
!
f
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed MatchEnvelope(src absent) with seed %d."
,
seed
)
}
// encryption method mismatch
f
.
KeySym
=
keySymOrig
f
.
KeyAsym
=
nil
if
f
.
MatchMessage
(
msg
)
{
t
.
Fatalf
(
"failed MatchEnvelope(encryption method mismatch) with seed %d."
,
seed
)
}
}
func
cloneFilter
(
orig
*
Filter
)
*
Filter
{
var
clone
Filter
clone
.
Messages
=
make
(
map
[
common
.
Hash
]
*
ReceivedMessage
)
clone
.
Src
=
orig
.
Src
clone
.
KeyAsym
=
orig
.
KeyAsym
clone
.
KeySym
=
orig
.
KeySym
clone
.
Topics
=
orig
.
Topics
clone
.
PoW
=
orig
.
PoW
clone
.
AllowP2P
=
orig
.
AllowP2P
clone
.
SymKeyHash
=
orig
.
SymKeyHash
return
&
clone
}
func
generateCompatibeEnvelope
(
t
*
testing
.
T
,
f
*
Filter
)
*
Envelope
{
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
return
nil
}
params
.
KeySym
=
f
.
KeySym
params
.
Topic
=
BytesToTopic
(
f
.
Topics
[
2
])
sentMessage
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
:=
sentMessage
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
return
nil
}
return
env
}
func
TestWatchers
(
t
*
testing
.
T
)
{
InitSingleTest
()
const
NumFilters
=
16
const
NumMessages
=
256
var
i
int
var
j
uint32
var
e
*
Envelope
var
x
,
firstID
string
var
err
error
w
:=
New
(
&
Config
{})
filters
:=
NewFilters
(
w
)
tst
:=
generateTestCases
(
t
,
NumFilters
)
for
i
=
0
;
i
<
NumFilters
;
i
++
{
tst
[
i
]
.
f
.
Src
=
nil
x
,
err
=
filters
.
Install
(
tst
[
i
]
.
f
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to install filter with seed %d: %s."
,
seed
,
err
)
}
tst
[
i
]
.
id
=
x
if
len
(
firstID
)
==
0
{
firstID
=
x
}
}
lastID
:=
x
var
envelopes
[
NumMessages
]
*
Envelope
for
i
=
0
;
i
<
NumMessages
;
i
++
{
j
=
mrand
.
Uint32
()
%
NumFilters
e
=
generateCompatibeEnvelope
(
t
,
tst
[
j
]
.
f
)
envelopes
[
i
]
=
e
tst
[
j
]
.
msgCnt
++
}
for
i
=
0
;
i
<
NumMessages
;
i
++
{
filters
.
NotifyWatchers
(
envelopes
[
i
],
false
)
}
var
total
int
var
mail
[]
*
ReceivedMessage
var
count
[
NumFilters
]
int
for
i
=
0
;
i
<
NumFilters
;
i
++
{
mail
=
tst
[
i
]
.
f
.
Retrieve
()
count
[
i
]
=
len
(
mail
)
total
+=
len
(
mail
)
}
if
total
!=
NumMessages
{
t
.
Fatalf
(
"failed with seed %d: total = %d, want: %d."
,
seed
,
total
,
NumMessages
)
}
for
i
=
0
;
i
<
NumFilters
;
i
++
{
mail
=
tst
[
i
]
.
f
.
Retrieve
()
if
len
(
mail
)
!=
0
{
t
.
Fatalf
(
"failed with seed %d: i = %d."
,
seed
,
i
)
}
if
tst
[
i
]
.
msgCnt
!=
count
[
i
]
{
t
.
Fatalf
(
"failed with seed %d: count[%d]: get %d, want %d."
,
seed
,
i
,
tst
[
i
]
.
msgCnt
,
count
[
i
])
}
}
// another round with a cloned filter
clone
:=
cloneFilter
(
tst
[
0
]
.
f
)
filters
.
Uninstall
(
lastID
)
total
=
0
last
:=
NumFilters
-
1
tst
[
last
]
.
f
=
clone
filters
.
Install
(
clone
)
for
i
=
0
;
i
<
NumFilters
;
i
++
{
tst
[
i
]
.
msgCnt
=
0
count
[
i
]
=
0
}
// make sure that the first watcher receives at least one message
e
=
generateCompatibeEnvelope
(
t
,
tst
[
0
]
.
f
)
envelopes
[
0
]
=
e
tst
[
0
]
.
msgCnt
++
for
i
=
1
;
i
<
NumMessages
;
i
++
{
j
=
mrand
.
Uint32
()
%
NumFilters
e
=
generateCompatibeEnvelope
(
t
,
tst
[
j
]
.
f
)
envelopes
[
i
]
=
e
tst
[
j
]
.
msgCnt
++
}
for
i
=
0
;
i
<
NumMessages
;
i
++
{
filters
.
NotifyWatchers
(
envelopes
[
i
],
false
)
}
for
i
=
0
;
i
<
NumFilters
;
i
++
{
mail
=
tst
[
i
]
.
f
.
Retrieve
()
count
[
i
]
=
len
(
mail
)
total
+=
len
(
mail
)
}
combined
:=
tst
[
0
]
.
msgCnt
+
tst
[
last
]
.
msgCnt
if
total
!=
NumMessages
+
count
[
0
]
{
t
.
Fatalf
(
"failed with seed %d: total = %d, count[0] = %d."
,
seed
,
total
,
count
[
0
])
}
if
combined
!=
count
[
0
]
{
t
.
Fatalf
(
"failed with seed %d: combined = %d, count[0] = %d."
,
seed
,
combined
,
count
[
0
])
}
if
combined
!=
count
[
last
]
{
t
.
Fatalf
(
"failed with seed %d: combined = %d, count[last] = %d."
,
seed
,
combined
,
count
[
last
])
}
for
i
=
1
;
i
<
NumFilters
-
1
;
i
++
{
mail
=
tst
[
i
]
.
f
.
Retrieve
()
if
len
(
mail
)
!=
0
{
t
.
Fatalf
(
"failed with seed %d: i = %d."
,
seed
,
i
)
}
if
tst
[
i
]
.
msgCnt
!=
count
[
i
]
{
t
.
Fatalf
(
"failed with seed %d: i = %d, get %d, want %d."
,
seed
,
i
,
tst
[
i
]
.
msgCnt
,
count
[
i
])
}
}
// test AcceptP2P
total
=
0
filters
.
NotifyWatchers
(
envelopes
[
0
],
true
)
for
i
=
0
;
i
<
NumFilters
;
i
++
{
mail
=
tst
[
i
]
.
f
.
Retrieve
()
total
+=
len
(
mail
)
}
if
total
!=
0
{
t
.
Fatalf
(
"failed with seed %d: total: got %d, want 0."
,
seed
,
total
)
}
f
:=
filters
.
Get
(
firstID
)
if
f
==
nil
{
t
.
Fatalf
(
"failed to get the filter with seed %d."
,
seed
)
}
f
.
AllowP2P
=
true
total
=
0
filters
.
NotifyWatchers
(
envelopes
[
0
],
true
)
for
i
=
0
;
i
<
NumFilters
;
i
++
{
mail
=
tst
[
i
]
.
f
.
Retrieve
()
total
+=
len
(
mail
)
}
if
total
!=
1
{
t
.
Fatalf
(
"failed with seed %d: total: got %d, want 1."
,
seed
,
total
)
}
}
func
TestVariableTopics
(
t
*
testing
.
T
)
{
InitSingleTest
()
const
lastTopicByte
=
3
var
match
bool
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
msg
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
f
,
err
:=
generateFilter
(
t
,
true
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateFilter with seed %d: %s."
,
seed
,
err
)
}
for
i
:=
0
;
i
<
4
;
i
++
{
env
.
Topic
=
BytesToTopic
(
f
.
Topics
[
i
])
match
=
f
.
MatchEnvelope
(
env
)
if
!
match
{
t
.
Fatalf
(
"failed MatchEnvelope symmetric with seed %d, step %d."
,
seed
,
i
)
}
f
.
Topics
[
i
][
lastTopicByte
]
++
match
=
f
.
MatchEnvelope
(
env
)
if
match
{
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_PartialTopic_ReturnTrue
(
t
*
testing
.
T
)
{
bt
:=
[]
byte
(
"tes"
)
topic
:=
BytesToTopic
([]
byte
(
"test"
))
if
!
matchSingleTopic
(
topic
,
bt
)
{
t
.
FailNow
()
}
}
func
TestMatchSingleTopic_NotEquals_ReturnFalse
(
t
*
testing
.
T
)
{
bt
:=
[]
byte
(
"test"
)
topic
:=
BytesToTopic
([]
byte
(
"not_equal"
))
if
matchSingleTopic
(
topic
,
bt
)
{
t
.
FailNow
()
}
}
whisper/whisperv5/gen_criteria_json.go
deleted
100644 → 0
View file @
4e9230ea
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package
whisperv5
import
(
"encoding/json"
"github.com/ethereum/go-ethereum/common/hexutil"
)
var
_
=
(
*
criteriaOverride
)(
nil
)
func
(
c
Criteria
)
MarshalJSON
()
([]
byte
,
error
)
{
type
Criteria
struct
{
SymKeyID
string
`json:"symKeyID"`
PrivateKeyID
string
`json:"privateKeyID"`
Sig
hexutil
.
Bytes
`json:"sig"`
MinPow
float64
`json:"minPow"`
Topics
[]
TopicType
`json:"topics"`
AllowP2P
bool
`json:"allowP2P"`
}
var
enc
Criteria
enc
.
SymKeyID
=
c
.
SymKeyID
enc
.
PrivateKeyID
=
c
.
PrivateKeyID
enc
.
Sig
=
c
.
Sig
enc
.
MinPow
=
c
.
MinPow
enc
.
Topics
=
c
.
Topics
enc
.
AllowP2P
=
c
.
AllowP2P
return
json
.
Marshal
(
&
enc
)
}
func
(
c
*
Criteria
)
UnmarshalJSON
(
input
[]
byte
)
error
{
type
Criteria
struct
{
SymKeyID
*
string
`json:"symKeyID"`
PrivateKeyID
*
string
`json:"privateKeyID"`
Sig
*
hexutil
.
Bytes
`json:"sig"`
MinPow
*
float64
`json:"minPow"`
Topics
[]
TopicType
`json:"topics"`
AllowP2P
*
bool
`json:"allowP2P"`
}
var
dec
Criteria
if
err
:=
json
.
Unmarshal
(
input
,
&
dec
);
err
!=
nil
{
return
err
}
if
dec
.
SymKeyID
!=
nil
{
c
.
SymKeyID
=
*
dec
.
SymKeyID
}
if
dec
.
PrivateKeyID
!=
nil
{
c
.
PrivateKeyID
=
*
dec
.
PrivateKeyID
}
if
dec
.
Sig
!=
nil
{
c
.
Sig
=
*
dec
.
Sig
}
if
dec
.
MinPow
!=
nil
{
c
.
MinPow
=
*
dec
.
MinPow
}
if
dec
.
Topics
!=
nil
{
c
.
Topics
=
dec
.
Topics
}
if
dec
.
AllowP2P
!=
nil
{
c
.
AllowP2P
=
*
dec
.
AllowP2P
}
return
nil
}
whisper/whisperv5/gen_message_json.go
deleted
100644 → 0
View file @
4e9230ea
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package
whisperv5
import
(
"encoding/json"
"github.com/ethereum/go-ethereum/common/hexutil"
)
var
_
=
(
*
messageOverride
)(
nil
)
func
(
m
Message
)
MarshalJSON
()
([]
byte
,
error
)
{
type
Message
struct
{
Sig
hexutil
.
Bytes
`json:"sig,omitempty"`
TTL
uint32
`json:"ttl"`
Timestamp
uint32
`json:"timestamp"`
Topic
TopicType
`json:"topic"`
Payload
hexutil
.
Bytes
`json:"payload"`
Padding
hexutil
.
Bytes
`json:"padding"`
PoW
float64
`json:"pow"`
Hash
hexutil
.
Bytes
`json:"hash"`
Dst
hexutil
.
Bytes
`json:"recipientPublicKey,omitempty"`
}
var
enc
Message
enc
.
Sig
=
m
.
Sig
enc
.
TTL
=
m
.
TTL
enc
.
Timestamp
=
m
.
Timestamp
enc
.
Topic
=
m
.
Topic
enc
.
Payload
=
m
.
Payload
enc
.
Padding
=
m
.
Padding
enc
.
PoW
=
m
.
PoW
enc
.
Hash
=
m
.
Hash
enc
.
Dst
=
m
.
Dst
return
json
.
Marshal
(
&
enc
)
}
func
(
m
*
Message
)
UnmarshalJSON
(
input
[]
byte
)
error
{
type
Message
struct
{
Sig
*
hexutil
.
Bytes
`json:"sig,omitempty"`
TTL
*
uint32
`json:"ttl"`
Timestamp
*
uint32
`json:"timestamp"`
Topic
*
TopicType
`json:"topic"`
Payload
*
hexutil
.
Bytes
`json:"payload"`
Padding
*
hexutil
.
Bytes
`json:"padding"`
PoW
*
float64
`json:"pow"`
Hash
*
hexutil
.
Bytes
`json:"hash"`
Dst
*
hexutil
.
Bytes
`json:"recipientPublicKey,omitempty"`
}
var
dec
Message
if
err
:=
json
.
Unmarshal
(
input
,
&
dec
);
err
!=
nil
{
return
err
}
if
dec
.
Sig
!=
nil
{
m
.
Sig
=
*
dec
.
Sig
}
if
dec
.
TTL
!=
nil
{
m
.
TTL
=
*
dec
.
TTL
}
if
dec
.
Timestamp
!=
nil
{
m
.
Timestamp
=
*
dec
.
Timestamp
}
if
dec
.
Topic
!=
nil
{
m
.
Topic
=
*
dec
.
Topic
}
if
dec
.
Payload
!=
nil
{
m
.
Payload
=
*
dec
.
Payload
}
if
dec
.
Padding
!=
nil
{
m
.
Padding
=
*
dec
.
Padding
}
if
dec
.
PoW
!=
nil
{
m
.
PoW
=
*
dec
.
PoW
}
if
dec
.
Hash
!=
nil
{
m
.
Hash
=
*
dec
.
Hash
}
if
dec
.
Dst
!=
nil
{
m
.
Dst
=
*
dec
.
Dst
}
return
nil
}
whisper/whisperv5/gen_newmessage_json.go
deleted
100644 → 0
View file @
4e9230ea
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package
whisperv5
import
(
"encoding/json"
"github.com/ethereum/go-ethereum/common/hexutil"
)
var
_
=
(
*
newMessageOverride
)(
nil
)
func
(
n
NewMessage
)
MarshalJSON
()
([]
byte
,
error
)
{
type
NewMessage
struct
{
SymKeyID
string
`json:"symKeyID"`
PublicKey
hexutil
.
Bytes
`json:"pubKey"`
Sig
string
`json:"sig"`
TTL
uint32
`json:"ttl"`
Topic
TopicType
`json:"topic"`
Payload
hexutil
.
Bytes
`json:"payload"`
Padding
hexutil
.
Bytes
`json:"padding"`
PowTime
uint32
`json:"powTime"`
PowTarget
float64
`json:"powTarget"`
TargetPeer
string
`json:"targetPeer"`
}
var
enc
NewMessage
enc
.
SymKeyID
=
n
.
SymKeyID
enc
.
PublicKey
=
n
.
PublicKey
enc
.
Sig
=
n
.
Sig
enc
.
TTL
=
n
.
TTL
enc
.
Topic
=
n
.
Topic
enc
.
Payload
=
n
.
Payload
enc
.
Padding
=
n
.
Padding
enc
.
PowTime
=
n
.
PowTime
enc
.
PowTarget
=
n
.
PowTarget
enc
.
TargetPeer
=
n
.
TargetPeer
return
json
.
Marshal
(
&
enc
)
}
func
(
n
*
NewMessage
)
UnmarshalJSON
(
input
[]
byte
)
error
{
type
NewMessage
struct
{
SymKeyID
*
string
`json:"symKeyID"`
PublicKey
*
hexutil
.
Bytes
`json:"pubKey"`
Sig
*
string
`json:"sig"`
TTL
*
uint32
`json:"ttl"`
Topic
*
TopicType
`json:"topic"`
Payload
*
hexutil
.
Bytes
`json:"payload"`
Padding
*
hexutil
.
Bytes
`json:"padding"`
PowTime
*
uint32
`json:"powTime"`
PowTarget
*
float64
`json:"powTarget"`
TargetPeer
*
string
`json:"targetPeer"`
}
var
dec
NewMessage
if
err
:=
json
.
Unmarshal
(
input
,
&
dec
);
err
!=
nil
{
return
err
}
if
dec
.
SymKeyID
!=
nil
{
n
.
SymKeyID
=
*
dec
.
SymKeyID
}
if
dec
.
PublicKey
!=
nil
{
n
.
PublicKey
=
*
dec
.
PublicKey
}
if
dec
.
Sig
!=
nil
{
n
.
Sig
=
*
dec
.
Sig
}
if
dec
.
TTL
!=
nil
{
n
.
TTL
=
*
dec
.
TTL
}
if
dec
.
Topic
!=
nil
{
n
.
Topic
=
*
dec
.
Topic
}
if
dec
.
Payload
!=
nil
{
n
.
Payload
=
*
dec
.
Payload
}
if
dec
.
Padding
!=
nil
{
n
.
Padding
=
*
dec
.
Padding
}
if
dec
.
PowTime
!=
nil
{
n
.
PowTime
=
*
dec
.
PowTime
}
if
dec
.
PowTarget
!=
nil
{
n
.
PowTarget
=
*
dec
.
PowTarget
}
if
dec
.
TargetPeer
!=
nil
{
n
.
TargetPeer
=
*
dec
.
TargetPeer
}
return
nil
}
whisper/whisperv5/message.go
deleted
100644 → 0
View file @
4e9230ea
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Contains the Whisper protocol Message element.
package
whisperv5
import
(
"crypto/aes"
"crypto/cipher"
"crypto/ecdsa"
crand
"crypto/rand"
"encoding/binary"
"errors"
"strconv"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/ethereum/go-ethereum/log"
)
// MessageParams specifies the exact way a message should be wrapped into an Envelope.
type
MessageParams
struct
{
TTL
uint32
Src
*
ecdsa
.
PrivateKey
Dst
*
ecdsa
.
PublicKey
KeySym
[]
byte
Topic
TopicType
WorkTime
uint32
PoW
float64
Payload
[]
byte
Padding
[]
byte
}
// SentMessage represents an end-user data packet to transmit through the
// Whisper protocol. These are wrapped into Envelopes that need not be
// understood by intermediate nodes, just forwarded.
type
sentMessage
struct
{
Raw
[]
byte
}
// ReceivedMessage represents a data packet to be received through the
// Whisper protocol.
type
ReceivedMessage
struct
{
Raw
[]
byte
Payload
[]
byte
Padding
[]
byte
Signature
[]
byte
PoW
float64
// Proof of work as described in the Whisper spec
Sent
uint32
// Time when the message was posted into the network
TTL
uint32
// Maximum time to live allowed for the message
Src
*
ecdsa
.
PublicKey
// Message recipient (identity used to decode the message)
Dst
*
ecdsa
.
PublicKey
// Message recipient (identity used to decode the message)
Topic
TopicType
SymKeyHash
common
.
Hash
// The Keccak256Hash of the key, associated with the Topic
EnvelopeHash
common
.
Hash
// Message envelope hash to act as a unique id
EnvelopeVersion
uint64
}
func
isMessageSigned
(
flags
byte
)
bool
{
return
(
flags
&
signatureFlag
)
!=
0
}
func
(
msg
*
ReceivedMessage
)
isSymmetricEncryption
()
bool
{
return
msg
.
SymKeyHash
!=
common
.
Hash
{}
}
func
(
msg
*
ReceivedMessage
)
isAsymmetricEncryption
()
bool
{
return
msg
.
Dst
!=
nil
}
// NewSentMessage creates and initializes a non-signed, non-encrypted Whisper message.
func
NewSentMessage
(
params
*
MessageParams
)
(
*
sentMessage
,
error
)
{
msg
:=
sentMessage
{}
msg
.
Raw
=
make
([]
byte
,
1
,
len
(
params
.
Payload
)
+
len
(
params
.
Padding
)
+
signatureLength
+
padSizeLimit
)
msg
.
Raw
[
0
]
=
0
// set all the flags to zero
err
:=
msg
.
appendPadding
(
params
)
if
err
!=
nil
{
return
nil
,
err
}
msg
.
Raw
=
append
(
msg
.
Raw
,
params
.
Payload
...
)
return
&
msg
,
nil
}
// getSizeOfLength returns the number of bytes necessary to encode the entire size padding (including these bytes)
func
getSizeOfLength
(
b
[]
byte
)
(
sz
int
,
err
error
)
{
sz
=
intSize
(
len
(
b
))
// first iteration
sz
=
intSize
(
len
(
b
)
+
sz
)
// second iteration
if
sz
>
3
{
err
=
errors
.
New
(
"oversized padding parameter"
)
}
return
sz
,
err
}
// sizeOfIntSize returns minimal number of bytes necessary to encode an integer value
func
intSize
(
i
int
)
(
s
int
)
{
for
s
=
1
;
i
>=
256
;
s
++
{
i
/=
256
}
return
s
}
// appendPadding appends the pseudorandom padding bytes and sets the padding flag.
// The last byte contains the size of padding (thus, its size must not exceed 256).
func
(
msg
*
sentMessage
)
appendPadding
(
params
*
MessageParams
)
error
{
rawSize
:=
len
(
params
.
Payload
)
+
1
if
params
.
Src
!=
nil
{
rawSize
+=
signatureLength
}
odd
:=
rawSize
%
padSizeLimit
if
len
(
params
.
Padding
)
!=
0
{
padSize
:=
len
(
params
.
Padding
)
padLengthSize
,
err
:=
getSizeOfLength
(
params
.
Padding
)
if
err
!=
nil
{
return
err
}
totalPadSize
:=
padSize
+
padLengthSize
buf
:=
make
([]
byte
,
8
)
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
}
return
nil
}
// sign calculates and sets the cryptographic signature for the message,
// also setting the sign flag.
func
(
msg
*
sentMessage
)
sign
(
key
*
ecdsa
.
PrivateKey
)
error
{
if
isMessageSigned
(
msg
.
Raw
[
0
])
{
// this should not happen, but no reason to panic
log
.
Error
(
"failed to sign the message: already signed"
)
return
nil
}
msg
.
Raw
[
0
]
|=
signatureFlag
hash
:=
crypto
.
Keccak256
(
msg
.
Raw
)
signature
,
err
:=
crypto
.
Sign
(
hash
,
key
)
if
err
!=
nil
{
msg
.
Raw
[
0
]
&=
^
signatureFlag
// clear the flag
return
err
}
msg
.
Raw
=
append
(
msg
.
Raw
,
signature
...
)
return
nil
}
// encryptAsymmetric encrypts a message with a public key.
func
(
msg
*
sentMessage
)
encryptAsymmetric
(
key
*
ecdsa
.
PublicKey
)
error
{
if
!
ValidatePublicKey
(
key
)
{
return
errors
.
New
(
"invalid public key provided for asymmetric encryption"
)
}
encrypted
,
err
:=
ecies
.
Encrypt
(
crand
.
Reader
,
ecies
.
ImportECDSAPublic
(
key
),
msg
.
Raw
,
nil
,
nil
)
if
err
==
nil
{
msg
.
Raw
=
encrypted
}
return
err
}
// encryptSymmetric encrypts a message with a topic key, using AES-GCM-256.
// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
func
(
msg
*
sentMessage
)
encryptSymmetric
(
key
[]
byte
)
(
nonce
[]
byte
,
err
error
)
{
if
!
validateSymmetricKey
(
key
)
{
return
nil
,
errors
.
New
(
"invalid key provided for symmetric encryption"
)
}
block
,
err
:=
aes
.
NewCipher
(
key
)
if
err
!=
nil
{
return
nil
,
err
}
aesgcm
,
err
:=
cipher
.
NewGCM
(
block
)
if
err
!=
nil
{
return
nil
,
err
}
// never use more than 2^32 random nonces with a given key
nonce
=
make
([]
byte
,
aesgcm
.
NonceSize
())
_
,
err
=
crand
.
Read
(
nonce
)
if
err
!=
nil
{
return
nil
,
err
}
else
if
!
validateSymmetricKey
(
nonce
)
{
return
nil
,
errors
.
New
(
"crypto/rand failed to generate nonce"
)
}
msg
.
Raw
=
aesgcm
.
Seal
(
nil
,
nonce
,
msg
.
Raw
,
nil
)
return
nonce
,
nil
}
// Wrap bundles the message into an Envelope to transmit over the network.
func
(
msg
*
sentMessage
)
Wrap
(
options
*
MessageParams
)
(
envelope
*
Envelope
,
err
error
)
{
if
options
.
TTL
==
0
{
options
.
TTL
=
DefaultTTL
}
if
options
.
Src
!=
nil
{
if
err
=
msg
.
sign
(
options
.
Src
);
err
!=
nil
{
return
nil
,
err
}
}
var
nonce
[]
byte
if
options
.
Dst
!=
nil
{
err
=
msg
.
encryptAsymmetric
(
options
.
Dst
)
}
else
if
options
.
KeySym
!=
nil
{
nonce
,
err
=
msg
.
encryptSymmetric
(
options
.
KeySym
)
}
else
{
err
=
errors
.
New
(
"unable to encrypt the message: neither symmetric nor assymmetric key provided"
)
}
if
err
!=
nil
{
return
nil
,
err
}
envelope
=
NewEnvelope
(
options
.
TTL
,
options
.
Topic
,
nonce
,
msg
)
if
err
=
envelope
.
Seal
(
options
);
err
!=
nil
{
return
nil
,
err
}
return
envelope
,
nil
}
// decryptSymmetric decrypts a message with a topic key, using AES-GCM-256.
// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
func
(
msg
*
ReceivedMessage
)
decryptSymmetric
(
key
[]
byte
,
nonce
[]
byte
)
error
{
block
,
err
:=
aes
.
NewCipher
(
key
)
if
err
!=
nil
{
return
err
}
aesgcm
,
err
:=
cipher
.
NewGCM
(
block
)
if
err
!=
nil
{
return
err
}
if
len
(
nonce
)
!=
aesgcm
.
NonceSize
()
{
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
{
return
err
}
msg
.
Raw
=
decrypted
return
nil
}
// decryptAsymmetric decrypts an encrypted payload with a private key.
func
(
msg
*
ReceivedMessage
)
decryptAsymmetric
(
key
*
ecdsa
.
PrivateKey
)
error
{
decrypted
,
err
:=
ecies
.
ImportECDSA
(
key
)
.
Decrypt
(
msg
.
Raw
,
nil
,
nil
)
if
err
==
nil
{
msg
.
Raw
=
decrypted
}
return
err
}
// Validate checks the validity and extracts the fields in case of success
func
(
msg
*
ReceivedMessage
)
Validate
()
bool
{
end
:=
len
(
msg
.
Raw
)
if
end
<
1
{
return
false
}
if
isMessageSigned
(
msg
.
Raw
[
0
])
{
end
-=
signatureLength
if
end
<=
1
{
return
false
}
msg
.
Signature
=
msg
.
Raw
[
end
:
]
msg
.
Src
=
msg
.
SigToPubKey
()
if
msg
.
Src
==
nil
{
return
false
}
}
padSize
,
ok
:=
msg
.
extractPadding
(
end
)
if
!
ok
{
return
false
}
msg
.
Payload
=
msg
.
Raw
[
1
+
padSize
:
end
]
return
true
}
// extractPadding extracts the padding from raw message.
// although we don't support sending messages with padding size
// 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
}
// SigToPubKey retrieves the public key of the message signer.
func
(
msg
*
ReceivedMessage
)
SigToPubKey
()
*
ecdsa
.
PublicKey
{
defer
func
()
{
recover
()
}()
// in case of invalid signature
pub
,
err
:=
crypto
.
SigToPub
(
msg
.
hash
(),
msg
.
Signature
)
if
err
!=
nil
{
log
.
Error
(
"failed to recover public key from signature"
,
"err"
,
err
)
return
nil
}
return
pub
}
// hash calculates the SHA3 checksum of the message flags, payload and padding.
func
(
msg
*
ReceivedMessage
)
hash
()
[]
byte
{
if
isMessageSigned
(
msg
.
Raw
[
0
])
{
sz
:=
len
(
msg
.
Raw
)
-
signatureLength
return
crypto
.
Keccak256
(
msg
.
Raw
[
:
sz
])
}
return
crypto
.
Keccak256
(
msg
.
Raw
)
}
whisper/whisperv5/message_test.go
deleted
100644 → 0
View file @
4e9230ea
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package
whisperv5
import
(
"bytes"
mrand
"math/rand"
"testing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
)
func
generateMessageParams
()
(
*
MessageParams
,
error
)
{
// set all the parameters except p.Dst and p.Padding
buf
:=
make
([]
byte
,
4
)
mrand
.
Read
(
buf
)
sz
:=
mrand
.
Intn
(
400
)
var
p
MessageParams
p
.
PoW
=
0.01
p
.
WorkTime
=
1
p
.
TTL
=
uint32
(
mrand
.
Intn
(
1024
))
p
.
Payload
=
make
([]
byte
,
sz
)
p
.
KeySym
=
make
([]
byte
,
aesKeyLength
)
mrand
.
Read
(
p
.
Payload
)
mrand
.
Read
(
p
.
KeySym
)
p
.
Topic
=
BytesToTopic
(
buf
)
var
err
error
p
.
Src
,
err
=
crypto
.
GenerateKey
()
if
err
!=
nil
{
return
nil
,
err
}
return
&
p
,
nil
}
func
singleMessageTest
(
t
*
testing
.
T
,
symmetric
bool
)
{
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
key
,
err
:=
crypto
.
GenerateKey
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed GenerateKey with seed %d: %s."
,
seed
,
err
)
}
if
!
symmetric
{
params
.
KeySym
=
nil
params
.
Dst
=
&
key
.
PublicKey
}
text
:=
make
([]
byte
,
0
,
512
)
text
=
append
(
text
,
params
.
Payload
...
)
msg
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
var
decrypted
*
ReceivedMessage
if
symmetric
{
decrypted
,
err
=
env
.
OpenSymmetric
(
params
.
KeySym
)
}
else
{
decrypted
,
err
=
env
.
OpenAsymmetric
(
key
)
}
if
err
!=
nil
{
t
.
Fatalf
(
"failed to encrypt with seed %d: %s."
,
seed
,
err
)
}
if
!
decrypted
.
Validate
()
{
t
.
Fatalf
(
"failed to validate with seed %d."
,
seed
)
}
if
!
bytes
.
Equal
(
text
,
decrypted
.
Payload
)
{
t
.
Fatalf
(
"failed with seed %d: compare payload."
,
seed
)
}
if
!
isMessageSigned
(
decrypted
.
Raw
[
0
])
{
t
.
Fatalf
(
"failed with seed %d: unsigned."
,
seed
)
}
if
len
(
decrypted
.
Signature
)
!=
signatureLength
{
t
.
Fatalf
(
"failed with seed %d: signature len %d."
,
seed
,
len
(
decrypted
.
Signature
))
}
if
!
IsPubKeyEqual
(
decrypted
.
Src
,
&
params
.
Src
.
PublicKey
)
{
t
.
Fatalf
(
"failed with seed %d: signature mismatch."
,
seed
)
}
}
func
TestMessageEncryption
(
t
*
testing
.
T
)
{
InitSingleTest
()
var
symmetric
bool
for
i
:=
0
;
i
<
256
;
i
++
{
singleMessageTest
(
t
,
symmetric
)
symmetric
=
!
symmetric
}
}
func
TestMessageWrap
(
t
*
testing
.
T
)
{
seed
=
int64
(
1777444222
)
mrand
.
Seed
(
seed
)
target
:=
128.0
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
msg
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
params
.
TTL
=
1
params
.
WorkTime
=
12
params
.
PoW
=
target
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
pow
:=
env
.
PoW
()
if
pow
<
target
{
t
.
Fatalf
(
"failed Wrap with seed %d: pow < target (%f vs. %f)."
,
seed
,
pow
,
target
)
}
// set PoW target too high, expect error
msg2
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
params
.
TTL
=
1000000
params
.
WorkTime
=
1
params
.
PoW
=
10000000.0
_
,
err
=
msg2
.
Wrap
(
params
)
if
err
==
nil
{
t
.
Fatalf
(
"unexpectedly reached the PoW target with seed %d."
,
seed
)
}
}
func
TestMessageSeal
(
t
*
testing
.
T
)
{
// this test depends on deterministic choice of seed (1976726903)
seed
=
int64
(
1976726903
)
mrand
.
Seed
(
seed
)
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
msg
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
params
.
TTL
=
1
aesnonce
:=
make
([]
byte
,
12
)
mrand
.
Read
(
aesnonce
)
env
:=
NewEnvelope
(
params
.
TTL
,
params
.
Topic
,
aesnonce
,
msg
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
env
.
Expiry
=
uint32
(
seed
)
// make it deterministic
target
:=
32.0
params
.
WorkTime
=
4
params
.
PoW
=
target
env
.
Seal
(
params
)
env
.
calculatePoW
(
0
)
pow
:=
env
.
PoW
()
if
pow
<
target
{
t
.
Fatalf
(
"failed Wrap with seed %d: pow < target (%f vs. %f)."
,
seed
,
pow
,
target
)
}
params
.
WorkTime
=
1
params
.
PoW
=
1000000000.0
env
.
Seal
(
params
)
env
.
calculatePoW
(
0
)
pow
=
env
.
PoW
()
if
pow
<
2
*
target
{
t
.
Fatalf
(
"failed Wrap with seed %d: pow too small %f."
,
seed
,
pow
)
}
}
func
TestEnvelopeOpen
(
t
*
testing
.
T
)
{
InitSingleTest
()
var
symmetric
bool
for
i
:=
0
;
i
<
256
;
i
++
{
singleEnvelopeOpenTest
(
t
,
symmetric
)
symmetric
=
!
symmetric
}
}
func
singleEnvelopeOpenTest
(
t
*
testing
.
T
,
symmetric
bool
)
{
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
key
,
err
:=
crypto
.
GenerateKey
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed GenerateKey with seed %d: %s."
,
seed
,
err
)
}
if
!
symmetric
{
params
.
KeySym
=
nil
params
.
Dst
=
&
key
.
PublicKey
}
text
:=
make
([]
byte
,
0
,
512
)
text
=
append
(
text
,
params
.
Payload
...
)
msg
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
f
:=
Filter
{
KeyAsym
:
key
,
KeySym
:
params
.
KeySym
}
decrypted
:=
env
.
Open
(
&
f
)
if
decrypted
==
nil
{
t
.
Fatalf
(
"failed to open with seed %d."
,
seed
)
}
if
!
bytes
.
Equal
(
text
,
decrypted
.
Payload
)
{
t
.
Fatalf
(
"failed with seed %d: compare payload."
,
seed
)
}
if
!
isMessageSigned
(
decrypted
.
Raw
[
0
])
{
t
.
Fatalf
(
"failed with seed %d: unsigned."
,
seed
)
}
if
len
(
decrypted
.
Signature
)
!=
signatureLength
{
t
.
Fatalf
(
"failed with seed %d: signature len %d."
,
seed
,
len
(
decrypted
.
Signature
))
}
if
!
IsPubKeyEqual
(
decrypted
.
Src
,
&
params
.
Src
.
PublicKey
)
{
t
.
Fatalf
(
"failed with seed %d: signature mismatch."
,
seed
)
}
if
decrypted
.
isAsymmetricEncryption
()
==
symmetric
{
t
.
Fatalf
(
"failed with seed %d: asymmetric %v vs. %v."
,
seed
,
decrypted
.
isAsymmetricEncryption
(),
symmetric
)
}
if
decrypted
.
isSymmetricEncryption
()
!=
symmetric
{
t
.
Fatalf
(
"failed with seed %d: symmetric %v vs. %v."
,
seed
,
decrypted
.
isSymmetricEncryption
(),
symmetric
)
}
if
!
symmetric
{
if
decrypted
.
Dst
==
nil
{
t
.
Fatalf
(
"failed with seed %d: dst is nil."
,
seed
)
}
if
!
IsPubKeyEqual
(
decrypted
.
Dst
,
&
key
.
PublicKey
)
{
t
.
Fatalf
(
"failed with seed %d: Dst."
,
seed
)
}
}
}
func
TestEncryptWithZeroKey
(
t
*
testing
.
T
)
{
InitSingleTest
()
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
msg
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
params
.
KeySym
=
make
([]
byte
,
aesKeyLength
)
_
,
err
=
msg
.
Wrap
(
params
)
if
err
==
nil
{
t
.
Fatalf
(
"wrapped with zero key, seed: %d."
,
seed
)
}
params
,
err
=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
msg
,
err
=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
params
.
KeySym
=
make
([]
byte
,
0
)
_
,
err
=
msg
.
Wrap
(
params
)
if
err
==
nil
{
t
.
Fatalf
(
"wrapped with empty key, seed: %d."
,
seed
)
}
params
,
err
=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
msg
,
err
=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
params
.
KeySym
=
nil
_
,
err
=
msg
.
Wrap
(
params
)
if
err
==
nil
{
t
.
Fatalf
(
"wrapped with nil key, seed: %d."
,
seed
)
}
}
func
TestRlpEncode
(
t
*
testing
.
T
)
{
InitSingleTest
()
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
msg
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"wrapped with zero key, seed: %d."
,
seed
)
}
raw
,
err
:=
rlp
.
EncodeToBytes
(
env
)
if
err
!=
nil
{
t
.
Fatalf
(
"RLP encode failed: %s."
,
err
)
}
var
decoded
Envelope
rlp
.
DecodeBytes
(
raw
,
&
decoded
)
if
err
!=
nil
{
t
.
Fatalf
(
"RLP decode failed: %s."
,
err
)
}
he
:=
env
.
Hash
()
hd
:=
decoded
.
Hash
()
if
he
!=
hd
{
t
.
Fatalf
(
"Hashes are not equal: %x vs. %x"
,
he
,
hd
)
}
}
func
singlePaddingTest
(
t
*
testing
.
T
,
padSize
int
)
{
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d and sz=%d: %s."
,
seed
,
padSize
,
err
)
}
params
.
Padding
=
make
([]
byte
,
padSize
)
params
.
PoW
=
0.0000000001
pad
:=
make
([]
byte
,
padSize
)
_
,
err
=
mrand
.
Read
(
pad
)
if
err
!=
nil
{
t
.
Fatalf
(
"padding is not generated (seed %d): %s"
,
seed
,
err
)
}
n
:=
copy
(
params
.
Padding
,
pad
)
if
n
!=
padSize
{
t
.
Fatalf
(
"padding is not copied (seed %d): %s"
,
seed
,
err
)
}
msg
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to wrap, seed: %d and sz=%d."
,
seed
,
padSize
)
}
f
:=
Filter
{
KeySym
:
params
.
KeySym
}
decrypted
:=
env
.
Open
(
&
f
)
if
decrypted
==
nil
{
t
.
Fatalf
(
"failed to open, seed and sz=%d: %d."
,
seed
,
padSize
)
}
if
!
bytes
.
Equal
(
pad
,
decrypted
.
Padding
)
{
t
.
Fatalf
(
"padding is not retireved as expected with seed %d and sz=%d:
\n
[%x]
\n
[%x]."
,
seed
,
padSize
,
pad
,
decrypted
.
Padding
)
}
}
func
TestPadding
(
t
*
testing
.
T
)
{
InitSingleTest
()
for
i
:=
1
;
i
<
260
;
i
++
{
singlePaddingTest
(
t
,
i
)
}
lim
:=
256
*
256
for
i
:=
lim
-
5
;
i
<
lim
+
2
;
i
++
{
singlePaddingTest
(
t
,
i
)
}
for
i
:=
0
;
i
<
256
;
i
++
{
n
:=
mrand
.
Intn
(
256
*
254
)
+
256
singlePaddingTest
(
t
,
n
)
}
for
i
:=
0
;
i
<
256
;
i
++
{
n
:=
mrand
.
Intn
(
256
*
1024
)
+
256
*
256
singlePaddingTest
(
t
,
n
)
}
}
whisper/whisperv5/peer.go
deleted
100644 → 0
View file @
4e9230ea
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package
whisperv5
import
(
"fmt"
"time"
mapset
"github.com/deckarep/golang-set"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rlp"
)
// Peer represents a whisper protocol peer connection.
type
Peer
struct
{
host
*
Whisper
peer
*
p2p
.
Peer
ws
p2p
.
MsgReadWriter
trusted
bool
known
mapset
.
Set
// Messages already known by the peer to avoid wasting bandwidth
quit
chan
struct
{}
}
// newPeer creates a new whisper peer object, but does not run the handshake itself.
func
newPeer
(
host
*
Whisper
,
remote
*
p2p
.
Peer
,
rw
p2p
.
MsgReadWriter
)
*
Peer
{
return
&
Peer
{
host
:
host
,
peer
:
remote
,
ws
:
rw
,
trusted
:
false
,
known
:
mapset
.
NewSet
(),
quit
:
make
(
chan
struct
{}),
}
}
// start initiates the peer updater, periodically broadcasting the whisper packets
// into the network.
func
(
peer
*
Peer
)
start
()
{
go
peer
.
update
()
log
.
Trace
(
"start"
,
"peer"
,
peer
.
ID
())
}
// stop terminates the peer updater, stopping message forwarding to it.
func
(
peer
*
Peer
)
stop
()
{
close
(
peer
.
quit
)
log
.
Trace
(
"stop"
,
"peer"
,
peer
.
ID
())
}
// handshake sends the protocol initiation status message to the remote peer and
// verifies the remote status too.
func
(
peer
*
Peer
)
handshake
()
error
{
// Send the handshake status message asynchronously
errc
:=
make
(
chan
error
,
1
)
go
func
()
{
errc
<-
p2p
.
Send
(
peer
.
ws
,
statusCode
,
ProtocolVersion
)
}()
// Fetch the remote status packet and verify protocol match
packet
,
err
:=
peer
.
ws
.
ReadMsg
()
if
err
!=
nil
{
return
err
}
if
packet
.
Code
!=
statusCode
{
return
fmt
.
Errorf
(
"peer [%x] sent packet %x before status packet"
,
peer
.
ID
(),
packet
.
Code
)
}
s
:=
rlp
.
NewStream
(
packet
.
Payload
,
uint64
(
packet
.
Size
))
peerVersion
,
err
:=
s
.
Uint
()
if
err
!=
nil
{
return
fmt
.
Errorf
(
"peer [%x] sent bad status message: %v"
,
peer
.
ID
(),
err
)
}
if
peerVersion
!=
ProtocolVersion
{
return
fmt
.
Errorf
(
"peer [%x]: protocol version mismatch %d != %d"
,
peer
.
ID
(),
peerVersion
,
ProtocolVersion
)
}
// Wait until out own status is consumed too
if
err
:=
<-
errc
;
err
!=
nil
{
return
fmt
.
Errorf
(
"peer [%x] failed to send status packet: %v"
,
peer
.
ID
(),
err
)
}
return
nil
}
// update executes periodic operations on the peer, including message transmission
// and expiration.
func
(
peer
*
Peer
)
update
()
{
// Start the tickers for the updates
expire
:=
time
.
NewTicker
(
expirationCycle
)
transmit
:=
time
.
NewTicker
(
transmissionCycle
)
// Loop and transmit until termination is requested
for
{
select
{
case
<-
expire
.
C
:
peer
.
expire
()
case
<-
transmit
.
C
:
if
err
:=
peer
.
broadcast
();
err
!=
nil
{
log
.
Trace
(
"broadcast failed"
,
"reason"
,
err
,
"peer"
,
peer
.
ID
())
return
}
case
<-
peer
.
quit
:
return
}
}
}
// mark marks an envelope known to the peer so that it won't be sent back.
func
(
peer
*
Peer
)
mark
(
envelope
*
Envelope
)
{
peer
.
known
.
Add
(
envelope
.
Hash
())
}
// marked checks if an envelope is already known to the remote peer.
func
(
peer
*
Peer
)
marked
(
envelope
*
Envelope
)
bool
{
return
peer
.
known
.
Contains
(
envelope
.
Hash
())
}
// expire iterates over all the known envelopes in the host and removes all
// expired (unknown) ones from the known list.
func
(
peer
*
Peer
)
expire
()
{
unmark
:=
make
(
map
[
common
.
Hash
]
struct
{})
peer
.
known
.
Each
(
func
(
v
interface
{})
bool
{
if
!
peer
.
host
.
isEnvelopeCached
(
v
.
(
common
.
Hash
))
{
unmark
[
v
.
(
common
.
Hash
)]
=
struct
{}{}
}
return
true
})
// Dump all known but no longer cached
for
hash
:=
range
unmark
{
peer
.
known
.
Remove
(
hash
)
}
}
// broadcast iterates over the collection of envelopes and transmits yet unknown
// ones over the network.
func
(
peer
*
Peer
)
broadcast
()
error
{
var
cnt
int
envelopes
:=
peer
.
host
.
Envelopes
()
for
_
,
envelope
:=
range
envelopes
{
if
!
peer
.
marked
(
envelope
)
{
err
:=
p2p
.
Send
(
peer
.
ws
,
messagesCode
,
envelope
)
if
err
!=
nil
{
return
err
}
else
{
peer
.
mark
(
envelope
)
cnt
++
}
}
}
if
cnt
>
0
{
log
.
Trace
(
"broadcast"
,
"num. messages"
,
cnt
)
}
return
nil
}
func
(
peer
*
Peer
)
ID
()
[]
byte
{
id
:=
peer
.
peer
.
ID
()
return
id
[
:
]
}
whisper/whisperv5/peer_test.go
deleted
100644 → 0
View file @
4e9230ea
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package
whisperv5
import
(
"bytes"
"crypto/ecdsa"
"net"
"sync"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/nat"
)
var
keys
=
[]
string
{
"d49dcf37238dc8a7aac57dc61b9fee68f0a97f062968978b9fafa7d1033d03a9"
,
"73fd6143c48e80ed3c56ea159fe7494a0b6b393a392227b422f4c3e8f1b54f98"
,
"119dd32adb1daa7a4c7bf77f847fb28730785aa92947edf42fdd997b54de40dc"
,
"deeda8709dea935bb772248a3144dea449ffcc13e8e5a1fd4ef20ce4e9c87837"
,
"5bd208a079633befa349441bdfdc4d85ba9bd56081525008380a63ac38a407cf"
,
"1d27fb4912002d58a2a42a50c97edb05c1b3dffc665dbaa42df1fe8d3d95c9b5"
,
"15def52800c9d6b8ca6f3066b7767a76afc7b611786c1276165fbc61636afb68"
,
"51be6ab4b2dc89f251ff2ace10f3c1cc65d6855f3e083f91f6ff8efdfd28b48c"
,
"ef1ef7441bf3c6419b162f05da6037474664f198b58db7315a6f4de52414b4a0"
,
"09bdf6985aabc696dc1fbeb5381aebd7a6421727343872eb2fadfc6d82486fd9"
,
"15d811bf2e01f99a224cdc91d0cf76cea08e8c67905c16fee9725c9be71185c4"
,
"2f83e45cf1baaea779789f755b7da72d8857aeebff19362dd9af31d3c9d14620"
,
"73f04e34ac6532b19c2aae8f8e52f38df1ac8f5cd10369f92325b9b0494b0590"
,
"1e2e07b69e5025537fb73770f483dc8d64f84ae3403775ef61cd36e3faf162c1"
,
"8963d9bbb3911aac6d30388c786756b1c423c4fbbc95d1f96ddbddf39809e43a"
,
"0422da85abc48249270b45d8de38a4cc3c02032ede1fcf0864a51092d58a2f1f"
,
"8ae5c15b0e8c7cade201fdc149831aa9b11ff626a7ffd27188886cc108ad0fa8"
,
"acd8f5a71d4aecfcb9ad00d32aa4bcf2a602939b6a9dd071bab443154184f805"
,
"a285a922125a7481600782ad69debfbcdb0316c1e97c267aff29ef50001ec045"
,
"28fd4eee78c6cd4bf78f39f8ab30c32c67c24a6223baa40e6f9c9a0e1de7cef5"
,
"c5cca0c9e6f043b288c6f1aef448ab59132dab3e453671af5d0752961f013fc7"
,
"46df99b051838cb6f8d1b73f232af516886bd8c4d0ee07af9a0a033c391380fd"
,
"c6a06a53cbaadbb432884f36155c8f3244e244881b5ee3e92e974cfa166d793f"
,
"783b90c75c63dc72e2f8d11b6f1b4de54d63825330ec76ee8db34f06b38ea211"
,
"9450038f10ca2c097a8013e5121b36b422b95b04892232f930a29292d9935611"
,
"e215e6246ed1cfdcf7310d4d8cdbe370f0d6a8371e4eb1089e2ae05c0e1bc10f"
,
"487110939ed9d64ebbc1f300adeab358bc58875faf4ca64990fbd7fe03b78f2b"
,
"824a70ea76ac81366da1d4f4ac39de851c8ac49dca456bb3f0a186ceefa269a5"
,
"ba8f34fa40945560d1006a328fe70c42e35cc3d1017e72d26864cd0d1b150f15"
,
"30a5dfcfd144997f428901ea88a43c8d176b19c79dde54cc58eea001aa3d246c"
,
"de59f7183aca39aa245ce66a05245fecfc7e2c75884184b52b27734a4a58efa2"
,
"92629e2ff5f0cb4f5f08fffe0f64492024d36f045b901efb271674b801095c5a"
,
"7184c1701569e3a4c4d2ddce691edd983b81e42e09196d332e1ae2f1e062cff4"
,
}
const
NumNodes
=
16
// must not exceed the number of keys (32)
type
TestData
struct
{
counter
[
NumNodes
]
int
mutex
sync
.
RWMutex
}
type
TestNode
struct
{
shh
*
Whisper
id
*
ecdsa
.
PrivateKey
server
*
p2p
.
Server
filerId
string
}
var
result
TestData
var
nodes
[
NumNodes
]
*
TestNode
var
sharedKey
=
[]
byte
(
"some arbitrary data here"
)
var
sharedTopic
TopicType
=
TopicType
{
0xF
,
0x1
,
0x2
,
0
}
var
expectedMessage
=
[]
byte
(
"per rectum ad astra"
)
// 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
)
{
initialize
(
t
)
for
i
:=
0
;
i
<
NumNodes
;
i
++
{
sendMsg
(
t
,
false
,
i
)
}
sendMsg
(
t
,
true
,
0
)
checkPropagation
(
t
)
stopServers
()
}
func
initialize
(
t
*
testing
.
T
)
{
var
err
error
for
i
:=
0
;
i
<
NumNodes
;
i
++
{
var
node
TestNode
node
.
shh
=
New
(
&
DefaultConfig
)
node
.
shh
.
SetMinimumPoW
(
0.00000001
)
node
.
shh
.
Start
(
nil
)
topics
:=
make
([]
TopicType
,
0
)
topics
=
append
(
topics
,
sharedTopic
)
f
:=
Filter
{
KeySym
:
sharedKey
}
f
.
Topics
=
[][]
byte
{
topics
[
0
][
:
]}
node
.
filerId
,
err
=
node
.
shh
.
Subscribe
(
&
f
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to install the filter: %s."
,
err
)
}
node
.
id
,
err
=
crypto
.
HexToECDSA
(
keys
[
i
])
if
err
!=
nil
{
t
.
Fatalf
(
"failed convert the key: %s."
,
keys
[
i
])
}
name
:=
common
.
MakeName
(
"whisper-go"
,
"2.0"
)
node
.
server
=
&
p2p
.
Server
{
Config
:
p2p
.
Config
{
PrivateKey
:
node
.
id
,
MaxPeers
:
NumNodes
/
2
+
1
,
Name
:
name
,
Protocols
:
node
.
shh
.
Protocols
(),
ListenAddr
:
"127.0.0.1:0"
,
NAT
:
nat
.
Any
(),
},
}
err
=
node
.
server
.
Start
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed to start server %d. err: %v"
,
i
,
err
)
}
for
j
:=
0
;
j
<
i
;
j
++
{
peerNodeId
:=
nodes
[
j
]
.
id
address
,
_
:=
net
.
ResolveTCPAddr
(
"tcp"
,
nodes
[
j
]
.
server
.
ListenAddr
)
peerPort
:=
uint16
(
address
.
Port
)
peer
:=
enode
.
NewV4
(
&
peerNodeId
.
PublicKey
,
address
.
IP
,
int
(
peerPort
),
int
(
peerPort
))
node
.
server
.
AddPeer
(
peer
)
}
nodes
[
i
]
=
&
node
}
}
func
stopServers
()
{
for
i
:=
0
;
i
<
NumNodes
;
i
++
{
n
:=
nodes
[
i
]
if
n
!=
nil
{
n
.
shh
.
Unsubscribe
(
n
.
filerId
)
n
.
shh
.
Stop
()
n
.
server
.
Stop
()
}
}
}
func
checkPropagation
(
t
*
testing
.
T
)
{
if
t
.
Failed
()
{
return
}
const
cycle
=
100
const
iterations
=
100
for
j
:=
0
;
j
<
iterations
;
j
++
{
time
.
Sleep
(
cycle
*
time
.
Millisecond
)
for
i
:=
0
;
i
<
NumNodes
;
i
++
{
f
:=
nodes
[
i
]
.
shh
.
GetFilter
(
nodes
[
i
]
.
filerId
)
if
f
==
nil
{
t
.
Fatalf
(
"failed to get filterId %s from node %d."
,
nodes
[
i
]
.
filerId
,
i
)
}
mail
:=
f
.
Retrieve
()
if
!
validateMail
(
t
,
i
,
mail
)
{
return
}
if
isTestComplete
()
{
return
}
}
}
t
.
Fatalf
(
"Test was not complete: timeout %d seconds."
,
iterations
*
cycle
/
1000
)
}
func
validateMail
(
t
*
testing
.
T
,
index
int
,
mail
[]
*
ReceivedMessage
)
bool
{
var
cnt
int
for
_
,
m
:=
range
mail
{
if
bytes
.
Equal
(
m
.
Payload
,
expectedMessage
)
{
cnt
++
}
}
if
cnt
==
0
{
// no messages received yet: nothing is wrong
return
true
}
if
cnt
>
1
{
t
.
Fatalf
(
"node %d received %d."
,
index
,
cnt
)
return
false
}
if
cnt
>
0
{
result
.
mutex
.
Lock
()
defer
result
.
mutex
.
Unlock
()
result
.
counter
[
index
]
+=
cnt
if
result
.
counter
[
index
]
>
1
{
t
.
Fatalf
(
"node %d accumulated %d."
,
index
,
result
.
counter
[
index
])
}
}
return
true
}
func
isTestComplete
()
bool
{
result
.
mutex
.
RLock
()
defer
result
.
mutex
.
RUnlock
()
for
i
:=
0
;
i
<
NumNodes
;
i
++
{
if
result
.
counter
[
i
]
<
1
{
return
false
}
}
for
i
:=
0
;
i
<
NumNodes
;
i
++
{
envelopes
:=
nodes
[
i
]
.
shh
.
Envelopes
()
if
len
(
envelopes
)
<
2
{
return
false
}
}
return
true
}
func
sendMsg
(
t
*
testing
.
T
,
expected
bool
,
id
int
)
{
if
t
.
Failed
()
{
return
}
opt
:=
MessageParams
{
KeySym
:
sharedKey
,
Topic
:
sharedTopic
,
Payload
:
expectedMessage
,
PoW
:
0.00000001
,
WorkTime
:
1
}
if
!
expected
{
opt
.
KeySym
[
0
]
++
opt
.
Topic
[
0
]
++
opt
.
Payload
=
opt
.
Payload
[
1
:
]
}
msg
,
err
:=
NewSentMessage
(
&
opt
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
envelope
,
err
:=
msg
.
Wrap
(
&
opt
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to seal message: %s"
,
err
)
}
err
=
nodes
[
id
]
.
shh
.
Send
(
envelope
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to send message: %s"
,
err
)
}
}
func
TestPeerBasic
(
t
*
testing
.
T
)
{
InitSingleTest
()
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d."
,
seed
)
}
params
.
PoW
=
0.001
msg
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d."
,
seed
)
}
p
:=
newPeer
(
nil
,
nil
,
nil
)
p
.
mark
(
env
)
if
!
p
.
marked
(
env
)
{
t
.
Fatalf
(
"failed mark with seed %d."
,
seed
)
}
}
whisper/whisperv5/topic.go
deleted
100644 → 0
View file @
4e9230ea
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Contains the Whisper protocol Topic element.
package
whisperv5
import
(
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)
// TopicType represents a cryptographically secure, probabilistic partial
// 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.
type
TopicType
[
TopicLength
]
byte
func
BytesToTopic
(
b
[]
byte
)
(
t
TopicType
)
{
sz
:=
TopicLength
if
x
:=
len
(
b
);
x
<
TopicLength
{
sz
=
x
}
for
i
:=
0
;
i
<
sz
;
i
++
{
t
[
i
]
=
b
[
i
]
}
return
t
}
// String converts a topic byte array to a string representation.
func
(
t
*
TopicType
)
String
()
string
{
return
common
.
ToHex
(
t
[
:
])
}
// MarshalText returns the hex representation of t.
func
(
t
TopicType
)
MarshalText
()
([]
byte
,
error
)
{
return
hexutil
.
Bytes
(
t
[
:
])
.
MarshalText
()
}
// UnmarshalText parses a hex representation to a topic.
func
(
t
*
TopicType
)
UnmarshalText
(
input
[]
byte
)
error
{
return
hexutil
.
UnmarshalFixedText
(
"Topic"
,
input
,
t
[
:
])
}
whisper/whisperv5/topic_test.go
deleted
100644 → 0
View file @
4e9230ea
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package
whisperv5
import
(
"encoding/json"
"testing"
)
var
topicStringTests
=
[]
struct
{
topic
TopicType
str
string
}{
{
topic
:
TopicType
{
0x00
,
0x00
,
0x00
,
0x00
},
str
:
"0x00000000"
},
{
topic
:
TopicType
{
0x00
,
0x7f
,
0x80
,
0xff
},
str
:
"0x007f80ff"
},
{
topic
:
TopicType
{
0xff
,
0x80
,
0x7f
,
0x00
},
str
:
"0xff807f00"
},
{
topic
:
TopicType
{
0xf2
,
0x6e
,
0x77
,
0x79
},
str
:
"0xf26e7779"
},
}
func
TestTopicString
(
t
*
testing
.
T
)
{
for
i
,
tst
:=
range
topicStringTests
{
s
:=
tst
.
topic
.
String
()
if
s
!=
tst
.
str
{
t
.
Fatalf
(
"failed test %d: have %s, want %s."
,
i
,
s
,
tst
.
str
)
}
}
}
var
bytesToTopicTests
=
[]
struct
{
data
[]
byte
topic
TopicType
}{
{
topic
:
TopicType
{
0x8f
,
0x9a
,
0x2b
,
0x7d
},
data
:
[]
byte
{
0x8f
,
0x9a
,
0x2b
,
0x7d
}},
{
topic
:
TopicType
{
0x00
,
0x7f
,
0x80
,
0xff
},
data
:
[]
byte
{
0x00
,
0x7f
,
0x80
,
0xff
}},
{
topic
:
TopicType
{
0x00
,
0x00
,
0x00
,
0x00
},
data
:
[]
byte
{
0x00
,
0x00
,
0x00
,
0x00
}},
{
topic
:
TopicType
{
0x00
,
0x00
,
0x00
,
0x00
},
data
:
[]
byte
{
0x00
,
0x00
,
0x00
}},
{
topic
:
TopicType
{
0x01
,
0x00
,
0x00
,
0x00
},
data
:
[]
byte
{
0x01
}},
{
topic
:
TopicType
{
0x00
,
0xfe
,
0x00
,
0x00
},
data
:
[]
byte
{
0x00
,
0xfe
}},
{
topic
:
TopicType
{
0xea
,
0x1d
,
0x43
,
0x00
},
data
:
[]
byte
{
0xea
,
0x1d
,
0x43
}},
{
topic
:
TopicType
{
0x6f
,
0x3c
,
0xb0
,
0xdd
},
data
:
[]
byte
{
0x6f
,
0x3c
,
0xb0
,
0xdd
,
0x0f
,
0x00
,
0x90
}},
{
topic
:
TopicType
{
0x00
,
0x00
,
0x00
,
0x00
},
data
:
[]
byte
{}},
{
topic
:
TopicType
{
0x00
,
0x00
,
0x00
,
0x00
},
data
:
nil
},
}
var
unmarshalTestsGood
=
[]
struct
{
topic
TopicType
data
[]
byte
}{
{
topic
:
TopicType
{
0x00
,
0x00
,
0x00
,
0x00
},
data
:
[]
byte
(
`"0x00000000"`
)},
{
topic
:
TopicType
{
0x00
,
0x7f
,
0x80
,
0xff
},
data
:
[]
byte
(
`"0x007f80ff"`
)},
{
topic
:
TopicType
{
0xff
,
0x80
,
0x7f
,
0x00
},
data
:
[]
byte
(
`"0xff807f00"`
)},
{
topic
:
TopicType
{
0xf2
,
0x6e
,
0x77
,
0x79
},
data
:
[]
byte
(
`"0xf26e7779"`
)},
}
var
unmarshalTestsBad
=
[]
struct
{
topic
TopicType
data
[]
byte
}{
{
topic
:
TopicType
{
0x00
,
0x00
,
0x00
,
0x00
},
data
:
[]
byte
(
`"0x000000"`
)},
{
topic
:
TopicType
{
0x00
,
0x00
,
0x00
,
0x00
},
data
:
[]
byte
(
`"0x0000000"`
)},
{
topic
:
TopicType
{
0x00
,
0x00
,
0x00
,
0x00
},
data
:
[]
byte
(
`"0x000000000"`
)},
{
topic
:
TopicType
{
0x00
,
0x00
,
0x00
,
0x00
},
data
:
[]
byte
(
`"0x0000000000"`
)},
{
topic
:
TopicType
{
0x00
,
0x00
,
0x00
,
0x00
},
data
:
[]
byte
(
`"000000"`
)},
{
topic
:
TopicType
{
0x00
,
0x00
,
0x00
,
0x00
},
data
:
[]
byte
(
`"0000000"`
)},
{
topic
:
TopicType
{
0x00
,
0x00
,
0x00
,
0x00
},
data
:
[]
byte
(
`"000000000"`
)},
{
topic
:
TopicType
{
0x00
,
0x00
,
0x00
,
0x00
},
data
:
[]
byte
(
`"0000000000"`
)},
{
topic
:
TopicType
{
0x00
,
0x00
,
0x00
,
0x00
},
data
:
[]
byte
(
`"abcdefg0"`
)},
}
var
unmarshalTestsUgly
=
[]
struct
{
topic
TopicType
data
[]
byte
}{
{
topic
:
TopicType
{
0x01
,
0x00
,
0x00
,
0x00
},
data
:
[]
byte
(
`"0x00000001"`
)},
}
func
TestBytesToTopic
(
t
*
testing
.
T
)
{
for
i
,
tst
:=
range
bytesToTopicTests
{
top
:=
BytesToTopic
(
tst
.
data
)
if
top
!=
tst
.
topic
{
t
.
Fatalf
(
"failed test %d: have %v, want %v."
,
i
,
t
,
tst
.
topic
)
}
}
}
func
TestUnmarshalTestsGood
(
t
*
testing
.
T
)
{
for
i
,
tst
:=
range
unmarshalTestsGood
{
var
top
TopicType
err
:=
json
.
Unmarshal
(
tst
.
data
,
&
top
)
if
err
!=
nil
{
t
.
Errorf
(
"failed test %d. input: %v. err: %v"
,
i
,
tst
.
data
,
err
)
}
else
if
top
!=
tst
.
topic
{
t
.
Errorf
(
"failed test %d: have %v, want %v."
,
i
,
t
,
tst
.
topic
)
}
}
}
func
TestUnmarshalTestsBad
(
t
*
testing
.
T
)
{
// in this test UnmarshalJSON() is supposed to fail
for
i
,
tst
:=
range
unmarshalTestsBad
{
var
top
TopicType
err
:=
json
.
Unmarshal
(
tst
.
data
,
&
top
)
if
err
==
nil
{
t
.
Fatalf
(
"failed test %d. input: %v."
,
i
,
tst
.
data
)
}
}
}
func
TestUnmarshalTestsUgly
(
t
*
testing
.
T
)
{
// in this test UnmarshalJSON() is NOT supposed to fail, but result should be wrong
for
i
,
tst
:=
range
unmarshalTestsUgly
{
var
top
TopicType
err
:=
json
.
Unmarshal
(
tst
.
data
,
&
top
)
if
err
!=
nil
{
t
.
Errorf
(
"failed test %d. input: %v."
,
i
,
tst
.
data
)
}
else
if
top
==
tst
.
topic
{
t
.
Errorf
(
"failed test %d: have %v, want %v."
,
i
,
top
,
tst
.
topic
)
}
}
}
whisper/whisperv5/whisper.go
deleted
100644 → 0
View file @
4e9230ea
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package
whisperv5
import
(
"bytes"
"crypto/ecdsa"
crand
"crypto/rand"
"crypto/sha256"
"fmt"
"runtime"
"sync"
"time"
mapset
"github.com/deckarep/golang-set"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc"
"github.com/syndtr/goleveldb/leveldb/errors"
"golang.org/x/crypto/pbkdf2"
"golang.org/x/sync/syncmap"
)
type
Statistics
struct
{
messagesCleared
int
memoryCleared
int
memoryUsed
int
cycles
int
totalMessagesCleared
int
}
const
(
minPowIdx
=
iota
// Minimal PoW required by the whisper node
maxMsgSizeIdx
=
iota
// Maximal message length allowed by the whisper node
overflowIdx
=
iota
// Indicator of message queue overflow
)
// Whisper represents a dark communication interface through the Ethereum
// network, using its very own P2P communication layer.
type
Whisper
struct
{
protocol
p2p
.
Protocol
// Protocol description and parameters
filters
*
Filters
// Message filters installed with Subscribe function
privateKeys
map
[
string
]
*
ecdsa
.
PrivateKey
// Private key storage
symKeys
map
[
string
][]
byte
// Symmetric key storage
keyMu
sync
.
RWMutex
// Mutex associated with key storages
poolMu
sync
.
RWMutex
// Mutex to sync the message and expiration pools
envelopes
map
[
common
.
Hash
]
*
Envelope
// Pool of envelopes currently tracked by this node
expirations
map
[
uint32
]
mapset
.
Set
// Message expiration pool
peerMu
sync
.
RWMutex
// Mutex to sync the active peer set
peers
map
[
*
Peer
]
struct
{}
// Set of currently active peers
messageQueue
chan
*
Envelope
// Message queue for normal whisper messages
p2pMsgQueue
chan
*
Envelope
// Message queue for peer-to-peer messages (not to be forwarded any further)
quit
chan
struct
{}
// Channel used for graceful exit
settings
syncmap
.
Map
// holds configuration settings that can be dynamically changed
statsMu
sync
.
Mutex
// guard stats
stats
Statistics
// Statistics of whisper node
mailServer
MailServer
// MailServer interface
}
// New creates a Whisper client ready to communicate through the Ethereum P2P network.
func
New
(
cfg
*
Config
)
*
Whisper
{
if
cfg
==
nil
{
cfg
=
&
DefaultConfig
}
whisper
:=
&
Whisper
{
privateKeys
:
make
(
map
[
string
]
*
ecdsa
.
PrivateKey
),
symKeys
:
make
(
map
[
string
][]
byte
),
envelopes
:
make
(
map
[
common
.
Hash
]
*
Envelope
),
expirations
:
make
(
map
[
uint32
]
mapset
.
Set
),
peers
:
make
(
map
[
*
Peer
]
struct
{}),
messageQueue
:
make
(
chan
*
Envelope
,
messageQueueLimit
),
p2pMsgQueue
:
make
(
chan
*
Envelope
,
messageQueueLimit
),
quit
:
make
(
chan
struct
{}),
}
whisper
.
filters
=
NewFilters
(
whisper
)
whisper
.
settings
.
Store
(
minPowIdx
,
cfg
.
MinimumAcceptedPOW
)
whisper
.
settings
.
Store
(
maxMsgSizeIdx
,
cfg
.
MaxMessageSize
)
whisper
.
settings
.
Store
(
overflowIdx
,
false
)
// p2p whisper sub protocol handler
whisper
.
protocol
=
p2p
.
Protocol
{
Name
:
ProtocolName
,
Version
:
uint
(
ProtocolVersion
),
Length
:
NumberOfMessageCodes
,
Run
:
whisper
.
HandlePeer
,
NodeInfo
:
func
()
interface
{}
{
return
map
[
string
]
interface
{}{
"version"
:
ProtocolVersionStr
,
"maxMessageSize"
:
whisper
.
MaxMessageSize
(),
"minimumPoW"
:
whisper
.
MinPow
(),
}
},
}
return
whisper
}
func
(
w
*
Whisper
)
MinPow
()
float64
{
val
,
_
:=
w
.
settings
.
Load
(
minPowIdx
)
return
val
.
(
float64
)
}
// MaxMessageSize returns the maximum accepted message size.
func
(
w
*
Whisper
)
MaxMessageSize
()
uint32
{
val
,
_
:=
w
.
settings
.
Load
(
maxMsgSizeIdx
)
return
val
.
(
uint32
)
}
// Overflow returns an indication if the message queue is full.
func
(
w
*
Whisper
)
Overflow
()
bool
{
val
,
_
:=
w
.
settings
.
Load
(
overflowIdx
)
return
val
.
(
bool
)
}
// APIs returns the RPC descriptors the Whisper implementation offers
func
(
w
*
Whisper
)
APIs
()
[]
rpc
.
API
{
return
[]
rpc
.
API
{
{
Namespace
:
ProtocolName
,
Version
:
ProtocolVersionStr
,
Service
:
NewPublicWhisperAPI
(
w
),
Public
:
true
,
},
}
}
// RegisterServer registers MailServer interface.
// MailServer will process all the incoming messages with p2pRequestCode.
func
(
w
*
Whisper
)
RegisterServer
(
server
MailServer
)
{
w
.
mailServer
=
server
}
// Protocols returns the whisper sub-protocols ran by this particular client.
func
(
w
*
Whisper
)
Protocols
()
[]
p2p
.
Protocol
{
return
[]
p2p
.
Protocol
{
w
.
protocol
}
}
// Version returns the whisper sub-protocols version number.
func
(
w
*
Whisper
)
Version
()
uint
{
return
w
.
protocol
.
Version
}
// SetMaxMessageSize sets the maximal message size allowed by this node
func
(
w
*
Whisper
)
SetMaxMessageSize
(
size
uint32
)
error
{
if
size
>
MaxMessageSize
{
return
fmt
.
Errorf
(
"message size too large [%d>%d]"
,
size
,
MaxMessageSize
)
}
w
.
settings
.
Store
(
maxMsgSizeIdx
,
size
)
return
nil
}
// SetMinimumPoW sets the minimal PoW required by this node
func
(
w
*
Whisper
)
SetMinimumPoW
(
val
float64
)
error
{
if
val
<=
0.0
{
return
fmt
.
Errorf
(
"invalid PoW: %f"
,
val
)
}
w
.
settings
.
Store
(
minPowIdx
,
val
)
return
nil
}
// getPeer retrieves peer by ID
func
(
w
*
Whisper
)
getPeer
(
peerID
[]
byte
)
(
*
Peer
,
error
)
{
w
.
peerMu
.
Lock
()
defer
w
.
peerMu
.
Unlock
()
for
p
:=
range
w
.
peers
{
id
:=
p
.
peer
.
ID
()
if
bytes
.
Equal
(
peerID
,
id
[
:
])
{
return
p
,
nil
}
}
return
nil
,
fmt
.
Errorf
(
"Could not find peer with ID: %x"
,
peerID
)
}
// AllowP2PMessagesFromPeer marks specific peer trusted,
// which will allow it to send historic (expired) messages.
func
(
w
*
Whisper
)
AllowP2PMessagesFromPeer
(
peerID
[]
byte
)
error
{
p
,
err
:=
w
.
getPeer
(
peerID
)
if
err
!=
nil
{
return
err
}
p
.
trusted
=
true
return
nil
}
// RequestHistoricMessages sends a message with p2pRequestCode to a specific peer,
// which is known to implement MailServer interface, and is supposed to process this
// request and respond with a number of peer-to-peer messages (possibly expired),
// which are not supposed to be forwarded any further.
// The whisper protocol is agnostic of the format and contents of envelope.
func
(
w
*
Whisper
)
RequestHistoricMessages
(
peerID
[]
byte
,
envelope
*
Envelope
)
error
{
p
,
err
:=
w
.
getPeer
(
peerID
)
if
err
!=
nil
{
return
err
}
p
.
trusted
=
true
return
p2p
.
Send
(
p
.
ws
,
p2pRequestCode
,
envelope
)
}
// SendP2PMessage sends a peer-to-peer message to a specific peer.
func
(
w
*
Whisper
)
SendP2PMessage
(
peerID
[]
byte
,
envelope
*
Envelope
)
error
{
p
,
err
:=
w
.
getPeer
(
peerID
)
if
err
!=
nil
{
return
err
}
return
w
.
SendP2PDirect
(
p
,
envelope
)
}
// SendP2PDirect sends a peer-to-peer message to a specific peer.
func
(
w
*
Whisper
)
SendP2PDirect
(
peer
*
Peer
,
envelope
*
Envelope
)
error
{
return
p2p
.
Send
(
peer
.
ws
,
p2pCode
,
envelope
)
}
// NewKeyPair generates a new cryptographic identity for the client, and injects
// it into the known identities for message decryption. Returns ID of the new key pair.
func
(
w
*
Whisper
)
NewKeyPair
()
(
string
,
error
)
{
key
,
err
:=
crypto
.
GenerateKey
()
if
err
!=
nil
||
!
validatePrivateKey
(
key
)
{
key
,
err
=
crypto
.
GenerateKey
()
// retry once
}
if
err
!=
nil
{
return
""
,
err
}
if
!
validatePrivateKey
(
key
)
{
return
""
,
fmt
.
Errorf
(
"failed to generate valid key"
)
}
id
,
err
:=
GenerateRandomID
()
if
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"failed to generate ID: %s"
,
err
)
}
w
.
keyMu
.
Lock
()
defer
w
.
keyMu
.
Unlock
()
if
w
.
privateKeys
[
id
]
!=
nil
{
return
""
,
fmt
.
Errorf
(
"failed to generate unique ID"
)
}
w
.
privateKeys
[
id
]
=
key
return
id
,
nil
}
// DeleteKeyPair deletes the specified key if it exists.
func
(
w
*
Whisper
)
DeleteKeyPair
(
key
string
)
bool
{
w
.
keyMu
.
Lock
()
defer
w
.
keyMu
.
Unlock
()
if
w
.
privateKeys
[
key
]
!=
nil
{
delete
(
w
.
privateKeys
,
key
)
return
true
}
return
false
}
// AddKeyPair imports a asymmetric private key and returns it identifier.
func
(
w
*
Whisper
)
AddKeyPair
(
key
*
ecdsa
.
PrivateKey
)
(
string
,
error
)
{
id
,
err
:=
GenerateRandomID
()
if
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"failed to generate ID: %s"
,
err
)
}
w
.
keyMu
.
Lock
()
w
.
privateKeys
[
id
]
=
key
w
.
keyMu
.
Unlock
()
return
id
,
nil
}
// HasKeyPair checks if the whisper node is configured with the private key
// of the specified public pair.
func
(
w
*
Whisper
)
HasKeyPair
(
id
string
)
bool
{
w
.
keyMu
.
RLock
()
defer
w
.
keyMu
.
RUnlock
()
return
w
.
privateKeys
[
id
]
!=
nil
}
// GetPrivateKey retrieves the private key of the specified identity.
func
(
w
*
Whisper
)
GetPrivateKey
(
id
string
)
(
*
ecdsa
.
PrivateKey
,
error
)
{
w
.
keyMu
.
RLock
()
defer
w
.
keyMu
.
RUnlock
()
key
:=
w
.
privateKeys
[
id
]
if
key
==
nil
{
return
nil
,
fmt
.
Errorf
(
"invalid id"
)
}
return
key
,
nil
}
// GenerateSymKey generates a random symmetric key and stores it under id,
// which is then returned. Will be used in the future for session key exchange.
func
(
w
*
Whisper
)
GenerateSymKey
()
(
string
,
error
)
{
key
:=
make
([]
byte
,
aesKeyLength
)
_
,
err
:=
crand
.
Read
(
key
)
if
err
!=
nil
{
return
""
,
err
}
else
if
!
validateSymmetricKey
(
key
)
{
return
""
,
fmt
.
Errorf
(
"error in GenerateSymKey: crypto/rand failed to generate random data"
)
}
id
,
err
:=
GenerateRandomID
()
if
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"failed to generate ID: %s"
,
err
)
}
w
.
keyMu
.
Lock
()
defer
w
.
keyMu
.
Unlock
()
if
w
.
symKeys
[
id
]
!=
nil
{
return
""
,
fmt
.
Errorf
(
"failed to generate unique ID"
)
}
w
.
symKeys
[
id
]
=
key
return
id
,
nil
}
// AddSymKeyDirect stores the key, and returns its id.
func
(
w
*
Whisper
)
AddSymKeyDirect
(
key
[]
byte
)
(
string
,
error
)
{
if
len
(
key
)
!=
aesKeyLength
{
return
""
,
fmt
.
Errorf
(
"wrong key size: %d"
,
len
(
key
))
}
id
,
err
:=
GenerateRandomID
()
if
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"failed to generate ID: %s"
,
err
)
}
w
.
keyMu
.
Lock
()
defer
w
.
keyMu
.
Unlock
()
if
w
.
symKeys
[
id
]
!=
nil
{
return
""
,
fmt
.
Errorf
(
"failed to generate unique ID"
)
}
w
.
symKeys
[
id
]
=
key
return
id
,
nil
}
// AddSymKeyFromPassword generates the key from password, stores it, and returns its id.
func
(
w
*
Whisper
)
AddSymKeyFromPassword
(
password
string
)
(
string
,
error
)
{
id
,
err
:=
GenerateRandomID
()
if
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"failed to generate ID: %s"
,
err
)
}
if
w
.
HasSymKey
(
id
)
{
return
""
,
fmt
.
Errorf
(
"failed to generate unique ID"
)
}
derived
,
err
:=
deriveKeyMaterial
([]
byte
(
password
),
EnvelopeVersion
)
if
err
!=
nil
{
return
""
,
err
}
w
.
keyMu
.
Lock
()
defer
w
.
keyMu
.
Unlock
()
// double check is necessary, because deriveKeyMaterial() is very slow
if
w
.
symKeys
[
id
]
!=
nil
{
return
""
,
fmt
.
Errorf
(
"critical error: failed to generate unique ID"
)
}
w
.
symKeys
[
id
]
=
derived
return
id
,
nil
}
// HasSymKey returns true if there is a key associated with the given id.
// Otherwise returns false.
func
(
w
*
Whisper
)
HasSymKey
(
id
string
)
bool
{
w
.
keyMu
.
RLock
()
defer
w
.
keyMu
.
RUnlock
()
return
w
.
symKeys
[
id
]
!=
nil
}
// DeleteSymKey deletes the key associated with the name string if it exists.
func
(
w
*
Whisper
)
DeleteSymKey
(
id
string
)
bool
{
w
.
keyMu
.
Lock
()
defer
w
.
keyMu
.
Unlock
()
if
w
.
symKeys
[
id
]
!=
nil
{
delete
(
w
.
symKeys
,
id
)
return
true
}
return
false
}
// GetSymKey returns the symmetric key associated with the given id.
func
(
w
*
Whisper
)
GetSymKey
(
id
string
)
([]
byte
,
error
)
{
w
.
keyMu
.
RLock
()
defer
w
.
keyMu
.
RUnlock
()
if
w
.
symKeys
[
id
]
!=
nil
{
return
w
.
symKeys
[
id
],
nil
}
return
nil
,
fmt
.
Errorf
(
"non-existent key ID"
)
}
// Subscribe installs a new message handler used for filtering, decrypting
// and subsequent storing of incoming messages.
func
(
w
*
Whisper
)
Subscribe
(
f
*
Filter
)
(
string
,
error
)
{
return
w
.
filters
.
Install
(
f
)
}
// GetFilter returns the filter by id.
func
(
w
*
Whisper
)
GetFilter
(
id
string
)
*
Filter
{
return
w
.
filters
.
Get
(
id
)
}
// Unsubscribe removes an installed message handler.
func
(
w
*
Whisper
)
Unsubscribe
(
id
string
)
error
{
ok
:=
w
.
filters
.
Uninstall
(
id
)
if
!
ok
{
return
fmt
.
Errorf
(
"Unsubscribe: Invalid ID"
)
}
return
nil
}
// Send injects a message into the whisper send queue, to be distributed in the
// network in the coming cycles.
func
(
w
*
Whisper
)
Send
(
envelope
*
Envelope
)
error
{
ok
,
err
:=
w
.
add
(
envelope
)
if
err
!=
nil
{
return
err
}
if
!
ok
{
return
fmt
.
Errorf
(
"failed to add envelope"
)
}
return
err
}
// Start implements node.Service, starting the background data propagation thread
// of the Whisper protocol.
func
(
w
*
Whisper
)
Start
(
*
p2p
.
Server
)
error
{
log
.
Info
(
"started whisper v."
+
ProtocolVersionStr
)
go
w
.
update
()
numCPU
:=
runtime
.
NumCPU
()
for
i
:=
0
;
i
<
numCPU
;
i
++
{
go
w
.
processQueue
()
}
return
nil
}
// Stop implements node.Service, stopping the background data propagation thread
// of the Whisper protocol.
func
(
w
*
Whisper
)
Stop
()
error
{
close
(
w
.
quit
)
log
.
Info
(
"whisper stopped"
)
return
nil
}
// HandlePeer is called by the underlying P2P layer when the whisper sub-protocol
// connection is negotiated.
func
(
w
*
Whisper
)
HandlePeer
(
peer
*
p2p
.
Peer
,
rw
p2p
.
MsgReadWriter
)
error
{
// Create the new peer and start tracking it
whisperPeer
:=
newPeer
(
w
,
peer
,
rw
)
w
.
peerMu
.
Lock
()
w
.
peers
[
whisperPeer
]
=
struct
{}{}
w
.
peerMu
.
Unlock
()
defer
func
()
{
w
.
peerMu
.
Lock
()
delete
(
w
.
peers
,
whisperPeer
)
w
.
peerMu
.
Unlock
()
}()
// Run the peer handshake and state updates
if
err
:=
whisperPeer
.
handshake
();
err
!=
nil
{
return
err
}
whisperPeer
.
start
()
defer
whisperPeer
.
stop
()
return
w
.
runMessageLoop
(
whisperPeer
,
rw
)
}
// runMessageLoop reads and processes inbound messages directly to merge into client-global state.
func
(
w
*
Whisper
)
runMessageLoop
(
p
*
Peer
,
rw
p2p
.
MsgReadWriter
)
error
{
for
{
// fetch the next packet
packet
,
err
:=
rw
.
ReadMsg
()
if
err
!=
nil
{
log
.
Info
(
"message loop"
,
"peer"
,
p
.
peer
.
ID
(),
"err"
,
err
)
return
err
}
if
packet
.
Size
>
w
.
MaxMessageSize
()
{
log
.
Warn
(
"oversized message received"
,
"peer"
,
p
.
peer
.
ID
())
return
errors
.
New
(
"oversized message received"
)
}
switch
packet
.
Code
{
case
statusCode
:
// this should not happen, but no need to panic; just ignore this message.
log
.
Warn
(
"unxepected status message received"
,
"peer"
,
p
.
peer
.
ID
())
case
messagesCode
:
// decode the contained envelopes
var
envelope
Envelope
if
err
:=
packet
.
Decode
(
&
envelope
);
err
!=
nil
{
log
.
Warn
(
"failed to decode envelope, peer will be disconnected"
,
"peer"
,
p
.
peer
.
ID
(),
"err"
,
err
)
return
errors
.
New
(
"invalid envelope"
)
}
cached
,
err
:=
w
.
add
(
&
envelope
)
if
err
!=
nil
{
log
.
Warn
(
"bad envelope received, peer will be disconnected"
,
"peer"
,
p
.
peer
.
ID
(),
"err"
,
err
)
return
errors
.
New
(
"invalid envelope"
)
}
if
cached
{
p
.
mark
(
&
envelope
)
}
case
p2pCode
:
// peer-to-peer message, sent directly to peer bypassing PoW checks, etc.
// this message is not supposed to be forwarded to other peers, and
// therefore might not satisfy the PoW, expiry and other requirements.
// these messages are only accepted from the trusted peer.
if
p
.
trusted
{
var
envelope
Envelope
if
err
:=
packet
.
Decode
(
&
envelope
);
err
!=
nil
{
log
.
Warn
(
"failed to decode direct message, peer will be disconnected"
,
"peer"
,
p
.
peer
.
ID
(),
"err"
,
err
)
return
errors
.
New
(
"invalid direct message"
)
}
w
.
postEvent
(
&
envelope
,
true
)
}
case
p2pRequestCode
:
// Must be processed if mail server is implemented. Otherwise ignore.
if
w
.
mailServer
!=
nil
{
var
request
Envelope
if
err
:=
packet
.
Decode
(
&
request
);
err
!=
nil
{
log
.
Warn
(
"failed to decode p2p request message, peer will be disconnected"
,
"peer"
,
p
.
peer
.
ID
(),
"err"
,
err
)
return
errors
.
New
(
"invalid p2p request"
)
}
w
.
mailServer
.
DeliverMail
(
p
,
&
request
)
}
default
:
// New message types might be implemented in the future versions of Whisper.
// For forward compatibility, just ignore.
}
packet
.
Discard
()
}
}
// add inserts a new envelope into the message pool to be distributed within the
// whisper network. It also inserts the envelope into the expiration pool at the
// appropriate time-stamp. In case of error, connection should be dropped.
func
(
w
*
Whisper
)
add
(
envelope
*
Envelope
)
(
bool
,
error
)
{
now
:=
uint32
(
time
.
Now
()
.
Unix
())
sent
:=
envelope
.
Expiry
-
envelope
.
TTL
if
sent
>
now
{
if
sent
-
SynchAllowance
>
now
{
return
false
,
fmt
.
Errorf
(
"envelope created in the future [%x]"
,
envelope
.
Hash
())
}
// recalculate PoW, adjusted for the time difference, plus one second for latency
envelope
.
calculatePoW
(
sent
-
now
+
1
)
}
if
envelope
.
Expiry
<
now
{
if
envelope
.
Expiry
+
SynchAllowance
*
2
<
now
{
return
false
,
fmt
.
Errorf
(
"very old message"
)
}
log
.
Debug
(
"expired envelope dropped"
,
"hash"
,
envelope
.
Hash
()
.
Hex
())
return
false
,
nil
// drop envelope without error
}
if
uint32
(
envelope
.
size
())
>
w
.
MaxMessageSize
()
{
return
false
,
fmt
.
Errorf
(
"huge messages are not allowed [%x]"
,
envelope
.
Hash
())
}
if
len
(
envelope
.
Version
)
>
4
{
return
false
,
fmt
.
Errorf
(
"oversized version [%x]"
,
envelope
.
Hash
())
}
aesNonceSize
:=
len
(
envelope
.
AESNonce
)
if
aesNonceSize
!=
0
&&
aesNonceSize
!=
AESNonceLength
{
// the standard AES GCM nonce size is 12 bytes,
// but constant gcmStandardNonceSize cannot be accessed (not exported)
return
false
,
fmt
.
Errorf
(
"wrong size of AESNonce: %d bytes [env: %x]"
,
aesNonceSize
,
envelope
.
Hash
())
}
if
envelope
.
PoW
()
<
w
.
MinPow
()
{
log
.
Debug
(
"envelope with low PoW dropped"
,
"PoW"
,
envelope
.
PoW
(),
"hash"
,
envelope
.
Hash
()
.
Hex
())
return
false
,
nil
// drop envelope without error
}
hash
:=
envelope
.
Hash
()
w
.
poolMu
.
Lock
()
_
,
alreadyCached
:=
w
.
envelopes
[
hash
]
if
!
alreadyCached
{
w
.
envelopes
[
hash
]
=
envelope
if
w
.
expirations
[
envelope
.
Expiry
]
==
nil
{
w
.
expirations
[
envelope
.
Expiry
]
=
mapset
.
NewThreadUnsafeSet
()
}
if
!
w
.
expirations
[
envelope
.
Expiry
]
.
Contains
(
hash
)
{
w
.
expirations
[
envelope
.
Expiry
]
.
Add
(
hash
)
}
}
w
.
poolMu
.
Unlock
()
if
alreadyCached
{
log
.
Trace
(
"whisper envelope already cached"
,
"hash"
,
envelope
.
Hash
()
.
Hex
())
}
else
{
log
.
Trace
(
"cached whisper envelope"
,
"hash"
,
envelope
.
Hash
()
.
Hex
())
w
.
statsMu
.
Lock
()
w
.
stats
.
memoryUsed
+=
envelope
.
size
()
w
.
statsMu
.
Unlock
()
w
.
postEvent
(
envelope
,
false
)
// notify the local node about the new message
if
w
.
mailServer
!=
nil
{
w
.
mailServer
.
Archive
(
envelope
)
}
}
return
true
,
nil
}
// postEvent queues the message for further processing.
func
(
w
*
Whisper
)
postEvent
(
envelope
*
Envelope
,
isP2P
bool
)
{
// if the version of incoming message is higher than
// currently supported version, we can not decrypt it,
// and therefore just ignore this message
if
envelope
.
Ver
()
<=
EnvelopeVersion
{
if
isP2P
{
w
.
p2pMsgQueue
<-
envelope
}
else
{
w
.
checkOverflow
()
w
.
messageQueue
<-
envelope
}
}
}
// checkOverflow checks if message queue overflow occurs and reports it if necessary.
func
(
w
*
Whisper
)
checkOverflow
()
{
queueSize
:=
len
(
w
.
messageQueue
)
if
queueSize
==
messageQueueLimit
{
if
!
w
.
Overflow
()
{
w
.
settings
.
Store
(
overflowIdx
,
true
)
log
.
Warn
(
"message queue overflow"
)
}
}
else
if
queueSize
<=
messageQueueLimit
/
2
{
if
w
.
Overflow
()
{
w
.
settings
.
Store
(
overflowIdx
,
false
)
log
.
Warn
(
"message queue overflow fixed (back to normal)"
)
}
}
}
// processQueue delivers the messages to the watchers during the lifetime of the whisper node.
func
(
w
*
Whisper
)
processQueue
()
{
var
e
*
Envelope
for
{
select
{
case
<-
w
.
quit
:
return
case
e
=
<-
w
.
messageQueue
:
w
.
filters
.
NotifyWatchers
(
e
,
false
)
case
e
=
<-
w
.
p2pMsgQueue
:
w
.
filters
.
NotifyWatchers
(
e
,
true
)
}
}
}
// update loops until the lifetime of the whisper node, updating its internal
// state by expiring stale messages from the pool.
func
(
w
*
Whisper
)
update
()
{
// Start a ticker to check for expirations
expire
:=
time
.
NewTicker
(
expirationCycle
)
// Repeat updates until termination is requested
for
{
select
{
case
<-
expire
.
C
:
w
.
expire
()
case
<-
w
.
quit
:
return
}
}
}
// expire iterates over all the expiration timestamps, removing all stale
// messages from the pools.
func
(
w
*
Whisper
)
expire
()
{
w
.
poolMu
.
Lock
()
defer
w
.
poolMu
.
Unlock
()
w
.
statsMu
.
Lock
()
defer
w
.
statsMu
.
Unlock
()
w
.
stats
.
reset
()
now
:=
uint32
(
time
.
Now
()
.
Unix
())
for
expiry
,
hashSet
:=
range
w
.
expirations
{
if
expiry
<
now
{
// Dump all expired messages and remove timestamp
hashSet
.
Each
(
func
(
v
interface
{})
bool
{
sz
:=
w
.
envelopes
[
v
.
(
common
.
Hash
)]
.
size
()
delete
(
w
.
envelopes
,
v
.
(
common
.
Hash
))
w
.
stats
.
messagesCleared
++
w
.
stats
.
memoryCleared
+=
sz
w
.
stats
.
memoryUsed
-=
sz
return
false
})
w
.
expirations
[
expiry
]
.
Clear
()
delete
(
w
.
expirations
,
expiry
)
}
}
}
// Stats returns the whisper node statistics.
func
(
w
*
Whisper
)
Stats
()
Statistics
{
w
.
statsMu
.
Lock
()
defer
w
.
statsMu
.
Unlock
()
return
w
.
stats
}
// Envelopes retrieves all the messages currently pooled by the node.
func
(
w
*
Whisper
)
Envelopes
()
[]
*
Envelope
{
w
.
poolMu
.
RLock
()
defer
w
.
poolMu
.
RUnlock
()
all
:=
make
([]
*
Envelope
,
0
,
len
(
w
.
envelopes
))
for
_
,
envelope
:=
range
w
.
envelopes
{
all
=
append
(
all
,
envelope
)
}
return
all
}
// Messages iterates through all currently floating envelopes
// and retrieves all the messages, that this filter could decrypt.
func
(
w
*
Whisper
)
Messages
(
id
string
)
[]
*
ReceivedMessage
{
result
:=
make
([]
*
ReceivedMessage
,
0
)
w
.
poolMu
.
RLock
()
defer
w
.
poolMu
.
RUnlock
()
if
filter
:=
w
.
filters
.
Get
(
id
);
filter
!=
nil
{
for
_
,
env
:=
range
w
.
envelopes
{
msg
:=
filter
.
processEnvelope
(
env
)
if
msg
!=
nil
{
result
=
append
(
result
,
msg
)
}
}
}
return
result
}
// isEnvelopeCached checks if envelope with specific hash has already been received and cached.
func
(
w
*
Whisper
)
isEnvelopeCached
(
hash
common
.
Hash
)
bool
{
w
.
poolMu
.
Lock
()
defer
w
.
poolMu
.
Unlock
()
_
,
exist
:=
w
.
envelopes
[
hash
]
return
exist
}
// reset resets the node's statistics after each expiry cycle.
func
(
s
*
Statistics
)
reset
()
{
s
.
cycles
++
s
.
totalMessagesCleared
+=
s
.
messagesCleared
s
.
memoryCleared
=
0
s
.
messagesCleared
=
0
}
// ValidatePublicKey checks the format of the given public key.
func
ValidatePublicKey
(
k
*
ecdsa
.
PublicKey
)
bool
{
return
k
!=
nil
&&
k
.
X
!=
nil
&&
k
.
Y
!=
nil
&&
k
.
X
.
Sign
()
!=
0
&&
k
.
Y
.
Sign
()
!=
0
}
// validatePrivateKey checks the format of the given private key.
func
validatePrivateKey
(
k
*
ecdsa
.
PrivateKey
)
bool
{
if
k
==
nil
||
k
.
D
==
nil
||
k
.
D
.
Sign
()
==
0
{
return
false
}
return
ValidatePublicKey
(
&
k
.
PublicKey
)
}
// validateSymmetricKey returns false if the key contains all zeros
func
validateSymmetricKey
(
k
[]
byte
)
bool
{
return
len
(
k
)
>
0
&&
!
containsOnlyZeros
(
k
)
}
// containsOnlyZeros checks if the data contain only zeros.
func
containsOnlyZeros
(
data
[]
byte
)
bool
{
for
_
,
b
:=
range
data
{
if
b
!=
0
{
return
false
}
}
return
true
}
// bytesToUintLittleEndian converts the slice to 64-bit unsigned integer.
func
bytesToUintLittleEndian
(
b
[]
byte
)
(
res
uint64
)
{
mul
:=
uint64
(
1
)
for
i
:=
0
;
i
<
len
(
b
);
i
++
{
res
+=
uint64
(
b
[
i
])
*
mul
mul
*=
256
}
return
res
}
// BytesToUintBigEndian converts the slice to 64-bit unsigned integer.
func
BytesToUintBigEndian
(
b
[]
byte
)
(
res
uint64
)
{
for
i
:=
0
;
i
<
len
(
b
);
i
++
{
res
*=
256
res
+=
uint64
(
b
[
i
])
}
return
res
}
// deriveKeyMaterial derives symmetric key material from the key or password.
// pbkdf2 is used for security, in case people use password instead of randomly generated keys.
func
deriveKeyMaterial
(
key
[]
byte
,
version
uint64
)
(
derivedKey
[]
byte
,
err
error
)
{
if
version
==
0
{
// kdf should run no less than 0.1 seconds on average compute,
// because it's a once in a session experience
derivedKey
:=
pbkdf2
.
Key
(
key
,
nil
,
65356
,
aesKeyLength
,
sha256
.
New
)
return
derivedKey
,
nil
}
return
nil
,
unknownVersionError
(
version
)
}
// GenerateRandomID generates a random string, which is then returned to be used as a key id
func
GenerateRandomID
()
(
id
string
,
err
error
)
{
buf
:=
make
([]
byte
,
keyIdSize
)
_
,
err
=
crand
.
Read
(
buf
)
if
err
!=
nil
{
return
""
,
err
}
if
!
validateSymmetricKey
(
buf
)
{
return
""
,
fmt
.
Errorf
(
"error in generateRandomID: crypto/rand failed to generate random data"
)
}
id
=
common
.
Bytes2Hex
(
buf
)
return
id
,
err
}
whisper/whisperv5/whisper_test.go
deleted
100644 → 0
View file @
4e9230ea
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package
whisperv5
import
(
"bytes"
"crypto/ecdsa"
mrand
"math/rand"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
)
func
TestWhisperBasic
(
t
*
testing
.
T
)
{
w
:=
New
(
&
DefaultConfig
)
p
:=
w
.
Protocols
()
shh
:=
p
[
0
]
if
shh
.
Name
!=
ProtocolName
{
t
.
Fatalf
(
"failed Protocol Name: %v."
,
shh
.
Name
)
}
if
uint64
(
shh
.
Version
)
!=
ProtocolVersion
{
t
.
Fatalf
(
"failed Protocol Version: %v."
,
shh
.
Version
)
}
if
shh
.
Length
!=
NumberOfMessageCodes
{
t
.
Fatalf
(
"failed Protocol Length: %v."
,
shh
.
Length
)
}
if
shh
.
Run
==
nil
{
t
.
Fatalf
(
"failed shh.Run."
)
}
if
uint64
(
w
.
Version
())
!=
ProtocolVersion
{
t
.
Fatalf
(
"failed whisper Version: %v."
,
shh
.
Version
)
}
if
w
.
GetFilter
(
"non-existent"
)
!=
nil
{
t
.
Fatalf
(
"failed GetFilter."
)
}
peerID
:=
make
([]
byte
,
64
)
mrand
.
Read
(
peerID
)
peer
,
_
:=
w
.
getPeer
(
peerID
)
if
peer
!=
nil
{
t
.
Fatal
(
"found peer for random key."
)
}
if
err
:=
w
.
AllowP2PMessagesFromPeer
(
peerID
);
err
==
nil
{
t
.
Fatalf
(
"failed MarkPeerTrusted."
)
}
exist
:=
w
.
HasSymKey
(
"non-existing"
)
if
exist
{
t
.
Fatalf
(
"failed HasSymKey."
)
}
key
,
err
:=
w
.
GetSymKey
(
"non-existing"
)
if
err
==
nil
{
t
.
Fatalf
(
"failed GetSymKey(non-existing): false positive."
)
}
if
key
!=
nil
{
t
.
Fatalf
(
"failed GetSymKey: false positive."
)
}
mail
:=
w
.
Envelopes
()
if
len
(
mail
)
!=
0
{
t
.
Fatalf
(
"failed w.Envelopes()."
)
}
m
:=
w
.
Messages
(
"non-existent"
)
if
len
(
m
)
!=
0
{
t
.
Fatalf
(
"failed w.Messages."
)
}
var
derived
[]
byte
ver
:=
uint64
(
0xDEADBEEF
)
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
)
}
if
containsOnlyZeros
(
derived
)
{
t
.
Fatalf
(
"failed containsOnlyZeros with param = %v."
,
derived
)
}
buf
:=
[]
byte
{
0xFF
,
0xE5
,
0x80
,
0x2
,
0
}
le
:=
bytesToUintLittleEndian
(
buf
)
be
:=
BytesToUintBigEndian
(
buf
)
if
le
!=
uint64
(
0x280e5ff
)
{
t
.
Fatalf
(
"failed bytesToIntLittleEndian: %d."
,
le
)
}
if
be
!=
uint64
(
0xffe5800200
)
{
t
.
Fatalf
(
"failed BytesToIntBigEndian: %d."
,
be
)
}
id
,
err
:=
w
.
NewKeyPair
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed to generate new key pair: %s."
,
err
)
}
pk
,
err
:=
w
.
GetPrivateKey
(
id
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve new key pair: %s."
,
err
)
}
if
!
validatePrivateKey
(
pk
)
{
t
.
Fatalf
(
"failed validatePrivateKey: %v."
,
pk
)
}
if
!
ValidatePublicKey
(
&
pk
.
PublicKey
)
{
t
.
Fatalf
(
"failed ValidatePublicKey: %v."
,
pk
)
}
}
func
TestWhisperAsymmetricKeyImport
(
t
*
testing
.
T
)
{
var
(
w
=
New
(
&
DefaultConfig
)
privateKeys
[]
*
ecdsa
.
PrivateKey
)
for
i
:=
0
;
i
<
50
;
i
++
{
id
,
err
:=
w
.
NewKeyPair
()
if
err
!=
nil
{
t
.
Fatalf
(
"could not generate key: %v"
,
err
)
}
pk
,
err
:=
w
.
GetPrivateKey
(
id
)
if
err
!=
nil
{
t
.
Fatalf
(
"could not export private key: %v"
,
err
)
}
privateKeys
=
append
(
privateKeys
,
pk
)
if
!
w
.
DeleteKeyPair
(
id
)
{
t
.
Fatalf
(
"could not delete private key"
)
}
}
for
_
,
pk
:=
range
privateKeys
{
if
_
,
err
:=
w
.
AddKeyPair
(
pk
);
err
!=
nil
{
t
.
Fatalf
(
"could not import private key: %v"
,
err
)
}
}
}
func
TestWhisperIdentityManagement
(
t
*
testing
.
T
)
{
w
:=
New
(
&
DefaultConfig
)
id1
,
err
:=
w
.
NewKeyPair
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed to generate new key pair: %s."
,
err
)
}
id2
,
err
:=
w
.
NewKeyPair
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed to generate new key pair: %s."
,
err
)
}
pk1
,
err
:=
w
.
GetPrivateKey
(
id1
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve the key pair: %s."
,
err
)
}
pk2
,
err
:=
w
.
GetPrivateKey
(
id2
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve the key pair: %s."
,
err
)
}
if
!
w
.
HasKeyPair
(
id1
)
{
t
.
Fatalf
(
"failed HasIdentity(pk1)."
)
}
if
!
w
.
HasKeyPair
(
id2
)
{
t
.
Fatalf
(
"failed HasIdentity(pk2)."
)
}
if
pk1
==
nil
{
t
.
Fatalf
(
"failed GetIdentity(pk1)."
)
}
if
pk2
==
nil
{
t
.
Fatalf
(
"failed GetIdentity(pk2)."
)
}
if
!
validatePrivateKey
(
pk1
)
{
t
.
Fatalf
(
"pk1 is invalid."
)
}
if
!
validatePrivateKey
(
pk2
)
{
t
.
Fatalf
(
"pk2 is invalid."
)
}
// Delete one identity
done
:=
w
.
DeleteKeyPair
(
id1
)
if
!
done
{
t
.
Fatalf
(
"failed to delete id1."
)
}
pk1
,
err
=
w
.
GetPrivateKey
(
id1
)
if
err
==
nil
{
t
.
Fatalf
(
"retrieve the key pair: false positive."
)
}
pk2
,
err
=
w
.
GetPrivateKey
(
id2
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve the key pair: %s."
,
err
)
}
if
w
.
HasKeyPair
(
id1
)
{
t
.
Fatalf
(
"failed DeleteIdentity(pub1): still exist."
)
}
if
!
w
.
HasKeyPair
(
id2
)
{
t
.
Fatalf
(
"failed DeleteIdentity(pub1): pub2 does not exist."
)
}
if
pk1
!=
nil
{
t
.
Fatalf
(
"failed DeleteIdentity(pub1): first key still exist."
)
}
if
pk2
==
nil
{
t
.
Fatalf
(
"failed DeleteIdentity(pub1): second key does not exist."
)
}
// Delete again non-existing identity
done
=
w
.
DeleteKeyPair
(
id1
)
if
done
{
t
.
Fatalf
(
"delete id1: false positive."
)
}
pk1
,
err
=
w
.
GetPrivateKey
(
id1
)
if
err
==
nil
{
t
.
Fatalf
(
"retrieve the key pair: false positive."
)
}
pk2
,
err
=
w
.
GetPrivateKey
(
id2
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve the key pair: %s."
,
err
)
}
if
w
.
HasKeyPair
(
id1
)
{
t
.
Fatalf
(
"failed delete non-existing identity: exist."
)
}
if
!
w
.
HasKeyPair
(
id2
)
{
t
.
Fatalf
(
"failed delete non-existing identity: pub2 does not exist."
)
}
if
pk1
!=
nil
{
t
.
Fatalf
(
"failed delete non-existing identity: first key exist."
)
}
if
pk2
==
nil
{
t
.
Fatalf
(
"failed delete non-existing identity: second key does not exist."
)
}
// Delete second identity
done
=
w
.
DeleteKeyPair
(
id2
)
if
!
done
{
t
.
Fatalf
(
"failed to delete id2."
)
}
pk1
,
err
=
w
.
GetPrivateKey
(
id1
)
if
err
==
nil
{
t
.
Fatalf
(
"retrieve the key pair: false positive."
)
}
pk2
,
err
=
w
.
GetPrivateKey
(
id2
)
if
err
==
nil
{
t
.
Fatalf
(
"retrieve the key pair: false positive."
)
}
if
w
.
HasKeyPair
(
id1
)
{
t
.
Fatalf
(
"failed delete second identity: first identity exist."
)
}
if
w
.
HasKeyPair
(
id2
)
{
t
.
Fatalf
(
"failed delete second identity: still exist."
)
}
if
pk1
!=
nil
{
t
.
Fatalf
(
"failed delete second identity: first key exist."
)
}
if
pk2
!=
nil
{
t
.
Fatalf
(
"failed delete second identity: second key exist."
)
}
}
func
TestWhisperSymKeyManagement
(
t
*
testing
.
T
)
{
InitSingleTest
()
var
err
error
var
k1
,
k2
[]
byte
w
:=
New
(
&
DefaultConfig
)
id1
:=
string
(
"arbitrary-string-1"
)
id2
:=
string
(
"arbitrary-string-2"
)
id1
,
err
=
w
.
GenerateSymKey
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed GenerateSymKey with seed %d: %s."
,
seed
,
err
)
}
k1
,
err
=
w
.
GetSymKey
(
id1
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed GetSymKey(id1)."
)
}
k2
,
err
=
w
.
GetSymKey
(
id2
)
if
err
==
nil
{
t
.
Fatalf
(
"failed GetSymKey(id2): false positive."
)
}
if
!
w
.
HasSymKey
(
id1
)
{
t
.
Fatalf
(
"failed HasSymKey(id1)."
)
}
if
w
.
HasSymKey
(
id2
)
{
t
.
Fatalf
(
"failed HasSymKey(id2): false positive."
)
}
if
k1
==
nil
{
t
.
Fatalf
(
"first key does not exist."
)
}
if
k2
!=
nil
{
t
.
Fatalf
(
"second key still exist."
)
}
// add existing id, nothing should change
randomKey
:=
make
([]
byte
,
aesKeyLength
)
mrand
.
Read
(
randomKey
)
id1
,
err
=
w
.
AddSymKeyDirect
(
randomKey
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed AddSymKey with seed %d: %s."
,
seed
,
err
)
}
k1
,
err
=
w
.
GetSymKey
(
id1
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed w.GetSymKey(id1)."
)
}
k2
,
err
=
w
.
GetSymKey
(
id2
)
if
err
==
nil
{
t
.
Fatalf
(
"failed w.GetSymKey(id2): false positive."
)
}
if
!
w
.
HasSymKey
(
id1
)
{
t
.
Fatalf
(
"failed w.HasSymKey(id1)."
)
}
if
w
.
HasSymKey
(
id2
)
{
t
.
Fatalf
(
"failed w.HasSymKey(id2): false positive."
)
}
if
k1
==
nil
{
t
.
Fatalf
(
"first key does not exist."
)
}
if
!
bytes
.
Equal
(
k1
,
randomKey
)
{
t
.
Fatalf
(
"k1 != randomKey."
)
}
if
k2
!=
nil
{
t
.
Fatalf
(
"second key already exist."
)
}
id2
,
err
=
w
.
AddSymKeyDirect
(
randomKey
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed AddSymKey(id2) with seed %d: %s."
,
seed
,
err
)
}
k1
,
err
=
w
.
GetSymKey
(
id1
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed w.GetSymKey(id1)."
)
}
k2
,
err
=
w
.
GetSymKey
(
id2
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed w.GetSymKey(id2)."
)
}
if
!
w
.
HasSymKey
(
id1
)
{
t
.
Fatalf
(
"HasSymKey(id1) failed."
)
}
if
!
w
.
HasSymKey
(
id2
)
{
t
.
Fatalf
(
"HasSymKey(id2) failed."
)
}
if
k1
==
nil
{
t
.
Fatalf
(
"k1 does not exist."
)
}
if
k2
==
nil
{
t
.
Fatalf
(
"k2 does not exist."
)
}
if
!
bytes
.
Equal
(
k1
,
k2
)
{
t
.
Fatalf
(
"k1 != k2."
)
}
if
!
bytes
.
Equal
(
k1
,
randomKey
)
{
t
.
Fatalf
(
"k1 != randomKey."
)
}
if
len
(
k1
)
!=
aesKeyLength
{
t
.
Fatalf
(
"wrong length of k1."
)
}
if
len
(
k2
)
!=
aesKeyLength
{
t
.
Fatalf
(
"wrong length of k2."
)
}
w
.
DeleteSymKey
(
id1
)
k1
,
err
=
w
.
GetSymKey
(
id1
)
if
err
==
nil
{
t
.
Fatalf
(
"failed w.GetSymKey(id1): false positive."
)
}
if
k1
!=
nil
{
t
.
Fatalf
(
"failed GetSymKey(id1): false positive."
)
}
k2
,
err
=
w
.
GetSymKey
(
id2
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed w.GetSymKey(id2)."
)
}
if
w
.
HasSymKey
(
id1
)
{
t
.
Fatalf
(
"failed to delete first key: still exist."
)
}
if
!
w
.
HasSymKey
(
id2
)
{
t
.
Fatalf
(
"failed to delete first key: second key does not exist."
)
}
if
k1
!=
nil
{
t
.
Fatalf
(
"failed to delete first key."
)
}
if
k2
==
nil
{
t
.
Fatalf
(
"failed to delete first key: second key is nil."
)
}
w
.
DeleteSymKey
(
id1
)
w
.
DeleteSymKey
(
id2
)
k1
,
err
=
w
.
GetSymKey
(
id1
)
if
err
==
nil
{
t
.
Fatalf
(
"failed w.GetSymKey(id1): false positive."
)
}
k2
,
err
=
w
.
GetSymKey
(
id2
)
if
err
==
nil
{
t
.
Fatalf
(
"failed w.GetSymKey(id2): false positive."
)
}
if
k1
!=
nil
||
k2
!=
nil
{
t
.
Fatalf
(
"k1 or k2 is not nil"
)
}
if
w
.
HasSymKey
(
id1
)
{
t
.
Fatalf
(
"failed to delete second key: first key exist."
)
}
if
w
.
HasSymKey
(
id2
)
{
t
.
Fatalf
(
"failed to delete second key: still exist."
)
}
if
k1
!=
nil
{
t
.
Fatalf
(
"failed to delete second key: first key is not nil."
)
}
if
k2
!=
nil
{
t
.
Fatalf
(
"failed to delete second key: second key is not nil."
)
}
randomKey
=
make
([]
byte
,
aesKeyLength
+
1
)
mrand
.
Read
(
randomKey
)
_
,
err
=
w
.
AddSymKeyDirect
(
randomKey
)
if
err
==
nil
{
t
.
Fatalf
(
"added the key with wrong size, seed %d."
,
seed
)
}
const
password
=
"arbitrary data here"
id1
,
err
=
w
.
AddSymKeyFromPassword
(
password
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed AddSymKeyFromPassword(id1) with seed %d: %s."
,
seed
,
err
)
}
id2
,
err
=
w
.
AddSymKeyFromPassword
(
password
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed AddSymKeyFromPassword(id2) with seed %d: %s."
,
seed
,
err
)
}
k1
,
err
=
w
.
GetSymKey
(
id1
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed w.GetSymKey(id1)."
)
}
k2
,
err
=
w
.
GetSymKey
(
id2
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed w.GetSymKey(id2)."
)
}
if
!
w
.
HasSymKey
(
id1
)
{
t
.
Fatalf
(
"HasSymKey(id1) failed."
)
}
if
!
w
.
HasSymKey
(
id2
)
{
t
.
Fatalf
(
"HasSymKey(id2) failed."
)
}
if
k1
==
nil
{
t
.
Fatalf
(
"k1 does not exist."
)
}
if
k2
==
nil
{
t
.
Fatalf
(
"k2 does not exist."
)
}
if
!
bytes
.
Equal
(
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
)
{
InitSingleTest
()
w
:=
New
(
&
DefaultConfig
)
w
.
SetMinimumPoW
(
0.0000001
)
defer
w
.
SetMinimumPoW
(
DefaultMinimumPoW
)
w
.
Start
(
nil
)
defer
w
.
Stop
()
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
params
.
TTL
=
1
messagesCount
:=
5
// Send a few messages one after another. Due to low PoW and expiration buckets
// with one second resolution, it covers a case when there are multiple items
// in a single expiration bucket.
for
i
:=
0
;
i
<
messagesCount
;
i
++
{
msg
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
err
=
w
.
Send
(
env
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to send envelope with seed %d: %s."
,
seed
,
err
)
}
}
// wait till received or timeout
var
received
,
expired
bool
for
j
:=
0
;
j
<
20
;
j
++
{
time
.
Sleep
(
100
*
time
.
Millisecond
)
if
len
(
w
.
Envelopes
())
==
messagesCount
{
received
=
true
break
}
}
if
!
received
{
t
.
Fatalf
(
"did not receive the sent envelope, seed: %d."
,
seed
)
}
// wait till expired or timeout
for
j
:=
0
;
j
<
20
;
j
++
{
time
.
Sleep
(
100
*
time
.
Millisecond
)
if
len
(
w
.
Envelopes
())
==
0
{
expired
=
true
break
}
}
if
!
expired
{
t
.
Fatalf
(
"expire failed, seed: %d."
,
seed
)
}
}
func
TestCustomization
(
t
*
testing
.
T
)
{
InitSingleTest
()
w
:=
New
(
&
DefaultConfig
)
defer
w
.
SetMinimumPoW
(
DefaultMinimumPoW
)
defer
w
.
SetMaxMessageSize
(
DefaultMaxMessageSize
)
w
.
Start
(
nil
)
defer
w
.
Stop
()
const
smallPoW
=
0.00001
f
,
err
:=
generateFilter
(
t
,
true
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
params
.
KeySym
=
f
.
KeySym
params
.
Topic
=
BytesToTopic
(
f
.
Topics
[
2
])
params
.
PoW
=
smallPoW
params
.
TTL
=
3600
*
24
// one day
msg
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
err
=
w
.
Send
(
env
)
if
err
==
nil
{
t
.
Fatalf
(
"successfully sent envelope with PoW %.06f, false positive (seed %d)."
,
env
.
PoW
(),
seed
)
}
w
.
SetMinimumPoW
(
smallPoW
/
2
)
err
=
w
.
Send
(
env
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to send envelope with seed %d: %s."
,
seed
,
err
)
}
params
.
TTL
++
msg
,
err
=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
w
.
SetMaxMessageSize
(
uint32
(
env
.
size
()
-
1
))
err
=
w
.
Send
(
env
)
if
err
==
nil
{
t
.
Fatalf
(
"successfully sent oversized envelope (seed %d): false positive."
,
seed
)
}
w
.
SetMaxMessageSize
(
DefaultMaxMessageSize
)
err
=
w
.
Send
(
env
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to send second envelope with seed %d: %s."
,
seed
,
err
)
}
// wait till received or timeout
var
received
bool
for
j
:=
0
;
j
<
20
;
j
++
{
time
.
Sleep
(
100
*
time
.
Millisecond
)
if
len
(
w
.
Envelopes
())
>
1
{
received
=
true
break
}
}
if
!
received
{
t
.
Fatalf
(
"did not receive the sent envelope, seed: %d."
,
seed
)
}
// check w.messages()
id
,
err
:=
w
.
Subscribe
(
f
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed subscribe with seed %d: %s."
,
seed
,
err
)
}
time
.
Sleep
(
5
*
time
.
Millisecond
)
mail
:=
f
.
Retrieve
()
if
len
(
mail
)
>
0
{
t
.
Fatalf
(
"received premature mail"
)
}
mail
=
w
.
Messages
(
id
)
if
len
(
mail
)
!=
2
{
t
.
Fatalf
(
"failed to get whisper messages"
)
}
}
func
TestSymmetricSendCycle
(
t
*
testing
.
T
)
{
InitSingleTest
()
w
:=
New
(
&
DefaultConfig
)
defer
w
.
SetMinimumPoW
(
DefaultMinimumPoW
)
defer
w
.
SetMaxMessageSize
(
DefaultMaxMessageSize
)
w
.
Start
(
nil
)
defer
w
.
Stop
()
filter1
,
err
:=
generateFilter
(
t
,
true
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
filter1
.
PoW
=
DefaultMinimumPoW
// Copy the first filter since some of its fields
// are randomly gnerated.
filter2
:=
&
Filter
{
KeySym
:
filter1
.
KeySym
,
Topics
:
filter1
.
Topics
,
PoW
:
filter1
.
PoW
,
AllowP2P
:
filter1
.
AllowP2P
,
Messages
:
make
(
map
[
common
.
Hash
]
*
ReceivedMessage
),
}
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
filter1
.
Src
=
&
params
.
Src
.
PublicKey
filter2
.
Src
=
&
params
.
Src
.
PublicKey
params
.
KeySym
=
filter1
.
KeySym
params
.
Topic
=
BytesToTopic
(
filter1
.
Topics
[
2
])
params
.
PoW
=
filter1
.
PoW
params
.
WorkTime
=
10
params
.
TTL
=
50
msg
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
_
,
err
=
w
.
Subscribe
(
filter1
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed subscribe 1 with seed %d: %s."
,
seed
,
err
)
}
_
,
err
=
w
.
Subscribe
(
filter2
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed subscribe 2 with seed %d: %s."
,
seed
,
err
)
}
err
=
w
.
Send
(
env
)
if
err
!=
nil
{
t
.
Fatalf
(
"Failed sending envelope with PoW %.06f (seed %d): %s"
,
env
.
PoW
(),
seed
,
err
)
}
// wait till received or timeout
var
received
bool
for
j
:=
0
;
j
<
200
;
j
++
{
time
.
Sleep
(
10
*
time
.
Millisecond
)
if
len
(
w
.
Envelopes
())
>
0
{
received
=
true
break
}
}
if
!
received
{
t
.
Fatalf
(
"did not receive the sent envelope, seed: %d."
,
seed
)
}
// check w.messages()
time
.
Sleep
(
5
*
time
.
Millisecond
)
mail1
:=
filter1
.
Retrieve
()
mail2
:=
filter2
.
Retrieve
()
if
len
(
mail2
)
==
0
{
t
.
Fatalf
(
"did not receive any email for filter 2"
)
}
if
len
(
mail1
)
==
0
{
t
.
Fatalf
(
"did not receive any email for filter 1"
)
}
}
func
TestSymmetricSendWithoutAKey
(
t
*
testing
.
T
)
{
InitSingleTest
()
w
:=
New
(
&
DefaultConfig
)
defer
w
.
SetMinimumPoW
(
DefaultMinimumPoW
)
defer
w
.
SetMaxMessageSize
(
DefaultMaxMessageSize
)
w
.
Start
(
nil
)
defer
w
.
Stop
()
filter
,
err
:=
generateFilter
(
t
,
true
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
filter
.
PoW
=
DefaultMinimumPoW
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
filter
.
Src
=
nil
params
.
KeySym
=
filter
.
KeySym
params
.
Topic
=
BytesToTopic
(
filter
.
Topics
[
2
])
params
.
PoW
=
filter
.
PoW
params
.
WorkTime
=
10
params
.
TTL
=
50
msg
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
_
,
err
=
w
.
Subscribe
(
filter
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed subscribe 1 with seed %d: %s."
,
seed
,
err
)
}
err
=
w
.
Send
(
env
)
if
err
!=
nil
{
t
.
Fatalf
(
"Failed sending envelope with PoW %.06f (seed %d): %s"
,
env
.
PoW
(),
seed
,
err
)
}
// wait till received or timeout
var
received
bool
for
j
:=
0
;
j
<
200
;
j
++
{
time
.
Sleep
(
10
*
time
.
Millisecond
)
if
len
(
w
.
Envelopes
())
>
0
{
received
=
true
break
}
}
if
!
received
{
t
.
Fatalf
(
"did not receive the sent envelope, seed: %d."
,
seed
)
}
// check w.messages()
time
.
Sleep
(
5
*
time
.
Millisecond
)
mail
:=
filter
.
Retrieve
()
if
len
(
mail
)
==
0
{
t
.
Fatalf
(
"did not receive message in spite of not setting a public key"
)
}
}
func
TestSymmetricSendKeyMismatch
(
t
*
testing
.
T
)
{
InitSingleTest
()
w
:=
New
(
&
DefaultConfig
)
defer
w
.
SetMinimumPoW
(
DefaultMinimumPoW
)
defer
w
.
SetMaxMessageSize
(
DefaultMaxMessageSize
)
w
.
Start
(
nil
)
defer
w
.
Stop
()
filter
,
err
:=
generateFilter
(
t
,
true
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
filter
.
PoW
=
DefaultMinimumPoW
params
,
err
:=
generateMessageParams
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed generateMessageParams with seed %d: %s."
,
seed
,
err
)
}
params
.
KeySym
=
filter
.
KeySym
params
.
Topic
=
BytesToTopic
(
filter
.
Topics
[
2
])
params
.
PoW
=
filter
.
PoW
params
.
WorkTime
=
10
params
.
TTL
=
50
msg
,
err
:=
NewSentMessage
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create new message with seed %d: %s."
,
seed
,
err
)
}
env
,
err
:=
msg
.
Wrap
(
params
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed Wrap with seed %d: %s."
,
seed
,
err
)
}
_
,
err
=
w
.
Subscribe
(
filter
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed subscribe 1 with seed %d: %s."
,
seed
,
err
)
}
err
=
w
.
Send
(
env
)
if
err
!=
nil
{
t
.
Fatalf
(
"Failed sending envelope with PoW %.06f (seed %d): %s"
,
env
.
PoW
(),
seed
,
err
)
}
// wait till received or timeout
var
received
bool
for
j
:=
0
;
j
<
200
;
j
++
{
time
.
Sleep
(
10
*
time
.
Millisecond
)
if
len
(
w
.
Envelopes
())
>
0
{
received
=
true
break
}
}
if
!
received
{
t
.
Fatalf
(
"did not receive the sent envelope, seed: %d."
,
seed
)
}
// check w.messages()
time
.
Sleep
(
5
*
time
.
Millisecond
)
mail
:=
filter
.
Retrieve
()
if
len
(
mail
)
>
0
{
t
.
Fatalf
(
"received a message when keys weren't matching"
)
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment