From 684aa766dac994c4f54c95cb8b2b015203742b3e Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 14 Sep 2018 09:55:41 +0300 Subject: [PATCH] Instrument whisper with configurable source of time Whisper depends on time synchronization between connected peers. Time doesn't have to be perfectly synchronized, but time difference between two connected peers should be at most 10s. When whisper is used on mobile devices or personal laptops it is not possible to control time syncronization. In our patched version of whisper we are using NTP time source that periodically computes mean time from multiple queries to ntp servers. --- whisper/whisperv6/api.go | 2 +- whisper/whisperv6/benchmarks_test.go | 15 ++++++++------- whisper/whisperv6/envelope.go | 4 ++-- whisper/whisperv6/envelope_test.go | 3 ++- whisper/whisperv6/filter_test.go | 16 ++++++++-------- whisper/whisperv6/message.go | 5 +++-- whisper/whisperv6/message_test.go | 21 +++++++++++---------- whisper/whisperv6/peer_test.go | 4 ++-- whisper/whisperv6/whisper.go | 20 ++++++++++++++++++-- whisper/whisperv6/whisper_test.go | 12 ++++++------ 10 files changed, 61 insertions(+), 41 deletions(-) diff --git a/whisper/whisperv6/api.go b/whisper/whisperv6/api.go index e1dab7b226..174fee3fe5 100644 --- a/whisper/whisperv6/api.go +++ b/whisper/whisperv6/api.go @@ -284,7 +284,7 @@ func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (hexutil. } var result []byte - env, err := whisperMsg.Wrap(params) + env, err := whisperMsg.Wrap(params, api.w.GetCurrentTime()) if err != nil { return nil, err } diff --git a/whisper/whisperv6/benchmarks_test.go b/whisper/whisperv6/benchmarks_test.go index 0473179da5..b96f9695c5 100644 --- a/whisper/whisperv6/benchmarks_test.go +++ b/whisper/whisperv6/benchmarks_test.go @@ -19,6 +19,7 @@ package whisperv6 import ( "crypto/sha256" "testing" + "time" "github.com/ethereum/go-ethereum/crypto" "golang.org/x/crypto/pbkdf2" @@ -40,7 +41,7 @@ func BenchmarkEncryptionSym(b *testing.B) { for i := 0; i < b.N; i++ { msg, _ := NewSentMessage(params) - _, err := msg.Wrap(params) + _, err := msg.Wrap(params, time.Now()) 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)) @@ -65,7 +66,7 @@ func BenchmarkEncryptionAsym(b *testing.B) { for i := 0; i < b.N; i++ { msg, _ := NewSentMessage(params) - _, err := msg.Wrap(params) + _, err := msg.Wrap(params, time.Now()) if err != nil { b.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -80,7 +81,7 @@ func BenchmarkDecryptionSymValid(b *testing.B) { b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) } msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { b.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -102,7 +103,7 @@ func BenchmarkDecryptionSymInvalid(b *testing.B) { b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) } msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { b.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -131,7 +132,7 @@ func BenchmarkDecryptionAsymValid(b *testing.B) { params.KeySym = nil params.Dst = &key.PublicKey msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { b.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -158,7 +159,7 @@ func BenchmarkDecryptionAsymInvalid(b *testing.B) { params.KeySym = nil params.Dst = &key.PublicKey msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { b.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -200,7 +201,7 @@ func BenchmarkPoW(b *testing.B) { for i := 0; i < b.N; i++ { increment(params.Payload) msg, _ := NewSentMessage(params) - _, err := msg.Wrap(params) + _, err := msg.Wrap(params, time.Now()) if err != nil { b.Fatalf("failed Wrap with seed %d: %s.", seed, err) } diff --git a/whisper/whisperv6/envelope.go b/whisper/whisperv6/envelope.go index c42d1fa8ac..3b65fdba03 100644 --- a/whisper/whisperv6/envelope.go +++ b/whisper/whisperv6/envelope.go @@ -62,9 +62,9 @@ func (e *Envelope) rlpWithoutNonce() []byte { // NewEnvelope wraps a Whisper message with expiration and destination data // included into an envelope for network forwarding. -func NewEnvelope(ttl uint32, topic TopicType, msg *sentMessage) *Envelope { +func NewEnvelope(ttl uint32, topic TopicType, msg *sentMessage, now time.Time) *Envelope { env := Envelope{ - Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()), + Expiry: uint32(now.Add(time.Second * time.Duration(ttl)).Unix()), TTL: ttl, Topic: topic, Data: msg.Raw, diff --git a/whisper/whisperv6/envelope_test.go b/whisper/whisperv6/envelope_test.go index 410b250a3f..d7a4fc1665 100644 --- a/whisper/whisperv6/envelope_test.go +++ b/whisper/whisperv6/envelope_test.go @@ -21,6 +21,7 @@ package whisperv6 import ( mrand "math/rand" "testing" + "time" "github.com/ethereum/go-ethereum/crypto" ) @@ -50,7 +51,7 @@ func TestEnvelopeOpenAcceptsOnlyOneKeyTypeInFilter(t *testing.T) { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - e, err := msg.Wrap(¶ms) + e, err := msg.Wrap(¶ms, time.Now()) if err != nil { t.Fatalf("Failed to Wrap the message in an envelope with seed %d: %s", seed, err) } diff --git a/whisper/whisperv6/filter_test.go b/whisper/whisperv6/filter_test.go index 82e4aa0241..2054049cb4 100644 --- a/whisper/whisperv6/filter_test.go +++ b/whisper/whisperv6/filter_test.go @@ -203,7 +203,7 @@ func TestInstallIdenticalFilters(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := sentMessage.Wrap(params) + env, err := sentMessage.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -309,7 +309,7 @@ func TestMatchEnvelope(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -322,7 +322,7 @@ func TestMatchEnvelope(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err = msg.Wrap(params) + env, err = msg.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap() with seed %d: %s.", seed, err) } @@ -367,7 +367,7 @@ func TestMatchEnvelope(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err = msg.Wrap(params) + env, err = msg.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap() with seed %d: %s.", seed, err) } @@ -449,7 +449,7 @@ func TestMatchMessageSym(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := sentMessage.Wrap(params) + env, err := sentMessage.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -543,7 +543,7 @@ func TestMatchMessageAsym(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := sentMessage.Wrap(params) + env, err := sentMessage.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -628,7 +628,7 @@ func generateCompatibeEnvelope(t *testing.T, f *Filter) *Envelope { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := sentMessage.Wrap(params) + env, err := sentMessage.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) return nil @@ -804,7 +804,7 @@ func TestVariableTopics(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) } diff --git a/whisper/whisperv6/message.go b/whisper/whisperv6/message.go index 2d4e862441..a12b445e2f 100644 --- a/whisper/whisperv6/message.go +++ b/whisper/whisperv6/message.go @@ -27,6 +27,7 @@ import ( "errors" mrand "math/rand" "strconv" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -234,7 +235,7 @@ func generateSecureRandomData(length int) ([]byte, error) { } // Wrap bundles the message into an Envelope to transmit over the network. -func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err error) { +func (msg *sentMessage) Wrap(options *MessageParams, now time.Time) (envelope *Envelope, err error) { if options.TTL == 0 { options.TTL = DefaultTTL } @@ -254,7 +255,7 @@ func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er return nil, err } - envelope = NewEnvelope(options.TTL, options.Topic, msg) + envelope = NewEnvelope(options.TTL, options.Topic, msg, now) if err = envelope.Seal(options); err != nil { return nil, err } diff --git a/whisper/whisperv6/message_test.go b/whisper/whisperv6/message_test.go index 0a5c1c8533..d1a5ddbc81 100644 --- a/whisper/whisperv6/message_test.go +++ b/whisper/whisperv6/message_test.go @@ -22,6 +22,7 @@ import ( "crypto/cipher" mrand "math/rand" "testing" + "time" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" @@ -77,7 +78,7 @@ func singleMessageTest(t *testing.T, symmetric bool) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -138,7 +139,7 @@ func TestMessageWrap(t *testing.T) { params.TTL = 1 params.WorkTime = 12 params.PoW = target - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -156,7 +157,7 @@ func TestMessageWrap(t *testing.T) { params.TTL = 1000000 params.WorkTime = 1 params.PoW = 10000000.0 - _, err = msg2.Wrap(params) + _, err = msg2.Wrap(params, time.Now()) if err == nil { t.Fatalf("unexpectedly reached the PoW target with seed %d.", seed) } @@ -178,7 +179,7 @@ func TestMessageSeal(t *testing.T) { } params.TTL = 1 - env := NewEnvelope(params.TTL, params.Topic, msg) + env := NewEnvelope(params.TTL, params.Topic, msg, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -238,7 +239,7 @@ func singleEnvelopeOpenTest(t *testing.T, symmetric bool) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -294,7 +295,7 @@ func TestEncryptWithZeroKey(t *testing.T) { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } params.KeySym = make([]byte, aesKeyLength) - _, err = msg.Wrap(params) + _, err = msg.Wrap(params, time.Now()) if err == nil { t.Fatalf("wrapped with zero key, seed: %d.", seed) } @@ -308,7 +309,7 @@ func TestEncryptWithZeroKey(t *testing.T) { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } params.KeySym = make([]byte, 0) - _, err = msg.Wrap(params) + _, err = msg.Wrap(params, time.Now()) if err == nil { t.Fatalf("wrapped with empty key, seed: %d.", seed) } @@ -322,7 +323,7 @@ func TestEncryptWithZeroKey(t *testing.T) { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } params.KeySym = nil - _, err = msg.Wrap(params) + _, err = msg.Wrap(params, time.Now()) if err == nil { t.Fatalf("wrapped with nil key, seed: %d.", seed) } @@ -339,7 +340,7 @@ func TestRlpEncode(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { t.Fatalf("wrapped with zero key, seed: %d.", seed) } @@ -383,7 +384,7 @@ func singlePaddingTest(t *testing.T, padSize int) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed to wrap, seed: %d and sz=%d.", seed, padSize) } diff --git a/whisper/whisperv6/peer_test.go b/whisper/whisperv6/peer_test.go index fe31922cb2..b2f45a9053 100644 --- a/whisper/whisperv6/peer_test.go +++ b/whisper/whisperv6/peer_test.go @@ -381,7 +381,7 @@ func sendMsg(t *testing.T, expected bool, id int) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - envelope, err := msg.Wrap(&opt) + envelope, err := msg.Wrap(&opt, time.Now()) if err != nil { t.Fatalf("failed to seal message: %s", err) } @@ -405,7 +405,7 @@ func TestPeerBasic(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d.", seed) } diff --git a/whisper/whisperv6/whisper.go b/whisper/whisperv6/whisper.go index eb713f84ee..c4512f0f11 100644 --- a/whisper/whisperv6/whisper.go +++ b/whisper/whisperv6/whisper.go @@ -59,6 +59,9 @@ const ( restrictConnectionBetweenLightClientsIdx // Restrict connection between two light clients ) +// timeSource is a functon that returns time. +type timeSource func() time.Time + // Whisper represents a dark communication interface through the Ethereum // network, using its very own P2P communication layer. type Whisper struct { @@ -87,6 +90,8 @@ type Whisper struct { statsMu sync.Mutex // guard stats stats Statistics // Statistics of whisper node + timeSource timeSource // source of time for internal whisper usage. time.Now by default. + mailServer MailServer // MailServer interface } @@ -106,6 +111,7 @@ func New(cfg *Config) *Whisper { p2pMsgQueue: make(chan *Envelope, messageQueueLimit), quit: make(chan struct{}), syncAllowance: DefaultSyncAllowance, + timeSource: time.Now, } whisper.filters = NewFilters(whisper) @@ -212,6 +218,16 @@ func (whisper *Whisper) RegisterServer(server MailServer) { whisper.mailServer = server } +// UseTimeSource assigns provided time source as a default for a whisper. +func (whisper *Whisper) UseTimeSource(source timeSource) { + whisper.timeSource = source +} + +// GetCurrentTime returns time according to a source used by whisper. +func (whisper *Whisper) GetCurrentTime() time.Time { + return whisper.timeSource() +} + // Protocols returns the whisper sub-protocols ran by this particular client. func (whisper *Whisper) Protocols() []p2p.Protocol { return []p2p.Protocol{whisper.protocol} @@ -773,7 +789,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { // appropriate time-stamp. In case of error, connection should be dropped. // param isP2P indicates whether the message is peer-to-peer (should not be forwarded). func (whisper *Whisper) add(envelope *Envelope, isP2P bool) (bool, error) { - now := uint32(time.Now().Unix()) + now := uint32(whisper.GetCurrentTime().Unix()) sent := envelope.Expiry - envelope.TTL if sent > now { @@ -916,7 +932,7 @@ func (whisper *Whisper) expire() { whisper.statsMu.Lock() defer whisper.statsMu.Unlock() whisper.stats.reset() - now := uint32(time.Now().Unix()) + now := uint32(whisper.GetCurrentTime().Unix()) for expiry, hashSet := range whisper.expirations { if expiry < now { // Dump all expired messages and remove timestamp diff --git a/whisper/whisperv6/whisper_test.go b/whisper/whisperv6/whisper_test.go index 895bb2b969..0df876ef45 100644 --- a/whisper/whisperv6/whisper_test.go +++ b/whisper/whisperv6/whisper_test.go @@ -477,7 +477,7 @@ func TestExpiry(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -544,7 +544,7 @@ func TestCustomization(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -565,7 +565,7 @@ func TestCustomization(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err = msg.Wrap(params) + env, err = msg.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -649,7 +649,7 @@ func TestSymmetricSendCycle(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -727,7 +727,7 @@ func TestSymmetricSendWithoutAKey(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -793,7 +793,7 @@ func TestSymmetricSendKeyMismatch(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := msg.Wrap(params) + env, err := msg.Wrap(params, time.Now()) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) }