rpc, whisper, xeth: fix RPC message retrieval data race
This commit is contained in:
parent
5aa523e32b
commit
7948cc0029
@ -467,7 +467,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
|
|||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
if err := json.Unmarshal(req.Params, &args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*reply = api.xeth().Whisper().Messages(args.Id)
|
*reply = api.xeth().Messages(args.Id)
|
||||||
|
|
||||||
// case "eth_register":
|
// case "eth_register":
|
||||||
// // Placeholder for actual type
|
// // Placeholder for actual type
|
||||||
|
@ -73,6 +73,7 @@ func (self *Envelope) Open(key *ecdsa.PrivateKey) (msg *Message, err error) {
|
|||||||
message := &Message{
|
message := &Message{
|
||||||
Flags: data[0],
|
Flags: data[0],
|
||||||
Sent: int64(self.Expiry - self.TTL),
|
Sent: int64(self.Expiry - self.TTL),
|
||||||
|
Hash: self.Hash(),
|
||||||
}
|
}
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
|
|
||||||
|
36
whisper/envelope_test.go
Normal file
36
whisper/envelope_test.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package whisper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEnvelopeOpen(t *testing.T) {
|
||||||
|
payload := []byte("hello world")
|
||||||
|
message := NewMessage(payload)
|
||||||
|
|
||||||
|
envelope, err := message.Wrap(DefaultPoW, Options{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to wrap message: %v", err)
|
||||||
|
}
|
||||||
|
opened, err := envelope.Open(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to open envelope: %v.", err)
|
||||||
|
}
|
||||||
|
if opened.Flags != message.Flags {
|
||||||
|
t.Fatalf("flags mismatch: have %d, want %d", opened.Flags, message.Flags)
|
||||||
|
}
|
||||||
|
if bytes.Compare(opened.Signature, message.Signature) != 0 {
|
||||||
|
t.Fatalf("signature mismatch: have 0x%x, want 0x%x", opened.Signature, message.Signature)
|
||||||
|
}
|
||||||
|
if bytes.Compare(opened.Payload, message.Payload) != 0 {
|
||||||
|
t.Fatalf("payload mismatch: have 0x%x, want 0x%x", opened.Payload, message.Payload)
|
||||||
|
}
|
||||||
|
if opened.Sent != message.Sent {
|
||||||
|
t.Fatalf("send time mismatch: have %d, want %d", opened.Sent, message.Sent)
|
||||||
|
}
|
||||||
|
|
||||||
|
if opened.Hash != envelope.Hash() {
|
||||||
|
t.Fatalf("message hash mismatch: have 0x%x, want 0x%x", opened.Hash, envelope.Hash())
|
||||||
|
}
|
||||||
|
}
|
@ -8,12 +8,13 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Message represents an end-user data packet to trasmit through the Whisper
|
// Message represents an end-user data packet to transmit through the Whisper
|
||||||
// protocol. These are wrapped into Envelopes that need not be understood by
|
// protocol. These are wrapped into Envelopes that need not be understood by
|
||||||
// intermediate nodes, just forwarded.
|
// intermediate nodes, just forwarded.
|
||||||
type Message struct {
|
type Message struct {
|
||||||
@ -22,7 +23,8 @@ type Message struct {
|
|||||||
Payload []byte
|
Payload []byte
|
||||||
Sent int64
|
Sent int64
|
||||||
|
|
||||||
To *ecdsa.PublicKey
|
To *ecdsa.PublicKey // Message recipient (identity used to decode the message)
|
||||||
|
Hash common.Hash // Message envelope hash to act as a unique id in de-duplication
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options specifies the exact way a message should be wrapped into an Envelope.
|
// Options specifies the exact way a message should be wrapped into an Envelope.
|
||||||
|
@ -1,26 +1,84 @@
|
|||||||
// Contains the external API side message filter for watching, pooling and polling
|
// Contains the external API side message filter for watching, pooling and polling
|
||||||
// matched whisper messages.
|
// matched whisper messages, also serializing data access to avoid duplications.
|
||||||
|
|
||||||
package xeth
|
package xeth
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
// whisperFilter is the message cache matching a specific filter, accumulating
|
// whisperFilter is the message cache matching a specific filter, accumulating
|
||||||
// inbound messages until the are requested by the client.
|
// inbound messages until the are requested by the client.
|
||||||
type whisperFilter struct {
|
type whisperFilter struct {
|
||||||
id int // Filter identifier
|
id int // Filter identifier for old message retrieval
|
||||||
|
ref *Whisper // Whisper reference for old message retrieval
|
||||||
|
|
||||||
cache []WhisperMessage // Cache of messages not yet polled
|
cache []WhisperMessage // Cache of messages not yet polled
|
||||||
timeout time.Time // Time when the last message batch was queries
|
skip map[common.Hash]struct{} // List of retrieved messages to avoid duplication
|
||||||
|
update time.Time // Time of the last message query
|
||||||
|
|
||||||
|
lock sync.RWMutex // Lock protecting the filter internals
|
||||||
|
}
|
||||||
|
|
||||||
|
// newWhisperFilter creates a new serialized, poll based whisper topic filter.
|
||||||
|
func newWhisperFilter(id int, ref *Whisper) *whisperFilter {
|
||||||
|
return &whisperFilter{
|
||||||
|
id: id,
|
||||||
|
ref: ref,
|
||||||
|
|
||||||
|
update: time.Now(),
|
||||||
|
skip: make(map[common.Hash]struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// messages retrieves all the cached messages from the entire pool matching the
|
||||||
|
// filter, resetting the filter's change buffer.
|
||||||
|
func (w *whisperFilter) messages() []WhisperMessage {
|
||||||
|
w.lock.Lock()
|
||||||
|
defer w.lock.Unlock()
|
||||||
|
|
||||||
|
w.cache = nil
|
||||||
|
w.update = time.Now()
|
||||||
|
|
||||||
|
w.skip = make(map[common.Hash]struct{})
|
||||||
|
messages := w.ref.Messages(w.id)
|
||||||
|
for _, message := range messages {
|
||||||
|
w.skip[message.ref.Hash] = struct{}{}
|
||||||
|
}
|
||||||
|
return messages
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert injects a new batch of messages into the filter cache.
|
// insert injects a new batch of messages into the filter cache.
|
||||||
func (w *whisperFilter) insert(msgs ...WhisperMessage) {
|
func (w *whisperFilter) insert(messages ...WhisperMessage) {
|
||||||
w.cache = append(w.cache, msgs...)
|
w.lock.Lock()
|
||||||
|
defer w.lock.Unlock()
|
||||||
|
|
||||||
|
for _, message := range messages {
|
||||||
|
if _, ok := w.skip[message.ref.Hash]; !ok {
|
||||||
|
w.cache = append(w.cache, messages...)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// retrieve fetches all the cached messages from the filter.
|
// retrieve fetches all the cached messages from the filter.
|
||||||
func (w *whisperFilter) retrieve() (messages []WhisperMessage) {
|
func (w *whisperFilter) retrieve() (messages []WhisperMessage) {
|
||||||
|
w.lock.Lock()
|
||||||
|
defer w.lock.Unlock()
|
||||||
|
|
||||||
messages, w.cache = w.cache, nil
|
messages, w.cache = w.cache, nil
|
||||||
w.timeout = time.Now()
|
w.update = time.Now()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// activity returns the last time instance when client requests were executed on
|
||||||
|
// the filter.
|
||||||
|
func (w *whisperFilter) activity() time.Time {
|
||||||
|
w.lock.RLock()
|
||||||
|
defer w.lock.RUnlock()
|
||||||
|
|
||||||
|
return w.update
|
||||||
|
}
|
||||||
|
13
xeth/xeth.go
13
xeth/xeth.go
@ -97,7 +97,7 @@ done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
for id, filter := range self.messages {
|
for id, filter := range self.messages {
|
||||||
if time.Since(filter.timeout) > filterTickerTime {
|
if time.Since(filter.activity()) > filterTickerTime {
|
||||||
self.Whisper().Unwatch(id)
|
self.Whisper().Unwatch(id)
|
||||||
delete(self.messages, id)
|
delete(self.messages, id)
|
||||||
}
|
}
|
||||||
@ -461,7 +461,7 @@ func (p *XEth) NewWhisperFilter(to, from string, topics []string) int {
|
|||||||
p.messages[id].insert(msg)
|
p.messages[id].insert(msg)
|
||||||
}
|
}
|
||||||
id = p.Whisper().Watch(to, from, topics, callback)
|
id = p.Whisper().Watch(to, from, topics, callback)
|
||||||
p.messages[id] = &whisperFilter{timeout: time.Now()}
|
p.messages[id] = newWhisperFilter(id, p.Whisper())
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,7 +481,16 @@ func (self *XEth) MessagesChanged(id int) []WhisperMessage {
|
|||||||
if self.messages[id] != nil {
|
if self.messages[id] != nil {
|
||||||
return self.messages[id].retrieve()
|
return self.messages[id].retrieve()
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *XEth) Messages(id int) []WhisperMessage {
|
||||||
|
self.messagesMut.Lock()
|
||||||
|
defer self.messagesMut.Unlock()
|
||||||
|
|
||||||
|
if self.messages[id] != nil {
|
||||||
|
return self.messages[id].messages()
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user