-
Notifications
You must be signed in to change notification settings - Fork 110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix (dot/rpc, dot/state): state_subscribeStorage to only notify for value changes #1460
Merged
Merged
Changes from 33 commits
Commits
Show all changes
42 commits
Select commit
Hold shift + click to select a range
af287cb
move websocket messages and listeners into own files
edwardmack 9d0003a
fix notifyStorageSubscriptions to only notify for changes
edwardmack 678d946
Merge branch 'development' into ed/fixRPCsubscribeStorage
edwardmack bf8f887
Merge branch 'development' into ed/fixRPCsubscribeStorage
edwardmack 090028f
address PR comments
edwardmack a9279a7
add to websocket tests
edwardmack 0595ca8
repair append, cleanup filter declareation
edwardmack a479786
fix anti-pattern in log message
edwardmack a9ecb4a
create notifyStorageSubscription for individual sub notify
edwardmack 299448b
add websocket listeners unit tests
edwardmack 0d73536
Merge branch 'development' into ed/fixRPCsubscribeStorage
edwardmack ca61c54
cleanup merge conflicts
edwardmack 3230d70
lint
edwardmack d076caf
add sleep timer
edwardmack 3bd1a53
Merge branch 'development' into ed/fixRPCsubscribeStorage
edwardmack ea3b80a
refactor websocket files
edwardmack 90706be
lint
edwardmack c76452b
a locks to fix data race
edwardmack db4c9ea
Merge branch 'development' into ed/fixRPCsubscribeStorage
edwardmack e720c6a
Merge branch 'development' into ed/fixRPCsubscribeStorage
edwardmack cfa1810
Merge branch 'development' into ed/fixRPCsubscribeStorage
edwardmack dcf5d2a
implement observer design pattern
edwardmack 4f120fb
Merge branch 'development' into ed/fixRPCsubscribeStorage
edwardmack a44e40f
fix race conditions
edwardmack 4a5dd26
Merge branch 'development' into ed/fixRPCsubscribeStorage
edwardmack b344e5a
add tests
edwardmack 1d247f2
add tests
edwardmack 9706d78
Merge branch 'development' into ed/fixRPCsubscribeStorage
edwardmack e57ccb5
add tests
edwardmack f013cf5
add tests
edwardmack bfcd6a8
add tests
edwardmack dcf1493
add tests
edwardmack e32e256
Merge branch 'development' into ed/fixRPCsubscribeStorage
edwardmack c30f34e
add troubleshooting stuff for testing transactions
edwardmack c3a6463
Merge branch 'development' into ed/fixRPCsubscribeStorage
edwardmack ab9fd78
save commit
edwardmack d9e228e
address PR comments
edwardmack 390fadf
lint
edwardmack 2ab0f55
remove unused printf and comments
edwardmack 39d8748
fix test
edwardmack 4de9a74
update tests
edwardmack a2068a3
add return from error
edwardmack File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,121 +30,61 @@ type Listener interface { | |
Listen() | ||
} | ||
|
||
func (c *WSConn) startListener(lid int) { | ||
go c.Subscriptions[lid].Listen() | ||
// WSConnAPI interface defining methors a WSConn should have | ||
type WSConnAPI interface { | ||
safeSend(interface{}) | ||
} | ||
|
||
func (c *WSConn) initStorageChangeListener(reqID float64, params interface{}) (int, error) { | ||
scl := &StorageChangeListener{ | ||
Channel: make(chan *state.SubscriptionResult), | ||
wsconn: c, | ||
} | ||
sub := &state.StorageSubscription{ | ||
Filter: make(map[string]bool), | ||
Listener: scl.Channel, | ||
} | ||
|
||
pA := params.([]interface{}) | ||
for _, param := range pA { | ||
switch p := param.(type) { | ||
case []interface{}: | ||
for _, pp := range param.([]interface{}) { | ||
sub.Filter[pp.(string)] = true | ||
} | ||
case string: | ||
sub.Filter[p] = true | ||
default: | ||
return 0, fmt.Errorf("unknow parameter type") | ||
} | ||
} | ||
// StorageObserver struct to hold data for observer (Observer Design Pattern) | ||
type StorageObserver struct { | ||
id int | ||
filter map[string][]byte | ||
wsconn WSConnAPI | ||
} | ||
|
||
if c.StorageAPI == nil { | ||
c.safeSendError(reqID, nil, "error StorageAPI not set") | ||
return 0, fmt.Errorf("error StorageAPI not set") | ||
// Update is called to notify observer of new value | ||
func (s *StorageObserver) Update(change *state.SubscriptionResult) { | ||
if change == nil { | ||
return | ||
} | ||
|
||
chanID, err := c.StorageAPI.RegisterStorageChangeChannel(*sub) | ||
if err != nil { | ||
return 0, err | ||
result := make(map[string]interface{}) | ||
result["block"] = change.Hash.String() | ||
changes := make([][]string, 0, len(change.Changes)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rename There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated (using new struct). |
||
for _, v := range change.Changes { | ||
kv := []string{common.BytesToHex(v.Key), common.BytesToHex(v.Value)} | ||
changes = append(changes, kv) | ||
} | ||
scl.ChanID = chanID | ||
result["changes"] = changes | ||
|
||
c.qtyListeners++ | ||
scl.subID = c.qtyListeners | ||
c.Subscriptions[scl.subID] = scl | ||
c.StorageSubChannels[scl.subID] = chanID | ||
|
||
initRes := newSubscriptionResponseJSON(scl.subID, reqID) | ||
c.safeSend(initRes) | ||
|
||
return scl.subID, nil | ||
res := newSubcriptionBaseResponseJSON() | ||
res.Method = "state_storage" | ||
res.Params.Result = result | ||
res.Params.SubscriptionID = s.GetID() | ||
s.wsconn.safeSend(res) | ||
} | ||
|
||
// StorageChangeListener for listening to state change channels | ||
type StorageChangeListener struct { | ||
Channel chan *state.SubscriptionResult | ||
wsconn *WSConn | ||
ChanID byte | ||
subID int | ||
// GetID the id for the Observer | ||
func (s *StorageObserver) GetID() int { | ||
return s.id | ||
} | ||
|
||
// Listen implementation of Listen interface to listen for importedChan changes | ||
func (l *StorageChangeListener) Listen() { | ||
for change := range l.Channel { | ||
if change == nil { | ||
continue | ||
} | ||
|
||
result := make(map[string]interface{}) | ||
result["block"] = change.Hash.String() | ||
changes := [][]string{} | ||
for _, v := range change.Changes { | ||
kv := []string{common.BytesToHex(v.Key), common.BytesToHex(v.Value)} | ||
changes = append(changes, kv) | ||
} | ||
result["changes"] = changes | ||
|
||
res := newSubcriptionBaseResponseJSON() | ||
res.Method = "state_storage" | ||
res.Params.Result = result | ||
res.Params.SubscriptionID = l.subID | ||
l.wsconn.safeSend(res) | ||
} | ||
// GetFilter returns the filter the Observer is using | ||
func (s *StorageObserver) GetFilter() map[string][]byte { | ||
return s.filter | ||
} | ||
|
||
// Listen to satisfy Listener interface (but is no longer used by StorageObserver) | ||
func (s *StorageObserver) Listen() {} | ||
|
||
// BlockListener to handle listening for blocks importedChan | ||
type BlockListener struct { | ||
Channel chan *types.Block | ||
wsconn *WSConn | ||
wsconn WSConnAPI | ||
ChanID byte | ||
subID int | ||
} | ||
|
||
func (c *WSConn) initBlockListener(reqID float64) (int, error) { | ||
bl := &BlockListener{ | ||
Channel: make(chan *types.Block), | ||
wsconn: c, | ||
} | ||
|
||
if c.BlockAPI == nil { | ||
c.safeSendError(reqID, nil, "error BlockAPI not set") | ||
return 0, fmt.Errorf("error BlockAPI not set") | ||
} | ||
chanID, err := c.BlockAPI.RegisterImportedChannel(bl.Channel) | ||
if err != nil { | ||
return 0, err | ||
} | ||
bl.ChanID = chanID | ||
c.qtyListeners++ | ||
bl.subID = c.qtyListeners | ||
c.Subscriptions[bl.subID] = bl | ||
c.BlockSubChannels[bl.subID] = chanID | ||
initRes := newSubscriptionResponseJSON(bl.subID, reqID) | ||
c.safeSend(initRes) | ||
|
||
return bl.subID, nil | ||
} | ||
|
||
// Listen implementation of Listen interface to listen for importedChan changes | ||
func (l *BlockListener) Listen() { | ||
for block := range l.Channel { | ||
|
@@ -167,36 +107,11 @@ func (l *BlockListener) Listen() { | |
// BlockFinalizedListener to handle listening for finalized blocks | ||
type BlockFinalizedListener struct { | ||
channel chan *types.Header | ||
wsconn *WSConn | ||
wsconn WSConnAPI | ||
chanID byte | ||
subID int | ||
} | ||
|
||
func (c *WSConn) initBlockFinalizedListener(reqID float64) (int, error) { | ||
bfl := &BlockFinalizedListener{ | ||
channel: make(chan *types.Header), | ||
wsconn: c, | ||
} | ||
|
||
if c.BlockAPI == nil { | ||
c.safeSendError(reqID, nil, "error BlockAPI not set") | ||
return 0, fmt.Errorf("error BlockAPI not set") | ||
} | ||
chanID, err := c.BlockAPI.RegisterFinalizedChannel(bfl.channel) | ||
if err != nil { | ||
return 0, err | ||
} | ||
bfl.chanID = chanID | ||
c.qtyListeners++ | ||
bfl.subID = c.qtyListeners | ||
c.Subscriptions[bfl.subID] = bfl | ||
c.BlockSubChannels[bfl.subID] = chanID | ||
initRes := newSubscriptionResponseJSON(bfl.subID, reqID) | ||
c.safeSend(initRes) | ||
|
||
return bfl.subID, nil | ||
} | ||
|
||
// Listen implementation of Listen interface to listen for importedChan changes | ||
func (l *BlockFinalizedListener) Listen() { | ||
for header := range l.channel { | ||
|
@@ -217,7 +132,7 @@ func (l *BlockFinalizedListener) Listen() { | |
|
||
// ExtrinsicSubmitListener to handle listening for extrinsic events | ||
type ExtrinsicSubmitListener struct { | ||
wsconn *WSConn | ||
wsconn WSConnAPI | ||
subID int | ||
extrinsic types.Extrinsic | ||
|
||
|
@@ -231,55 +146,6 @@ type ExtrinsicSubmitListener struct { | |
// AuthorExtrinsicUpdates method name | ||
const AuthorExtrinsicUpdates = "author_extrinsicUpdate" | ||
|
||
func (c *WSConn) initExtrinsicWatch(reqID float64, params interface{}) (int, error) { | ||
pA := params.([]interface{}) | ||
extBytes, err := common.HexToBytes(pA[0].(string)) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
// listen for built blocks | ||
esl := &ExtrinsicSubmitListener{ | ||
importedChan: make(chan *types.Block), | ||
wsconn: c, | ||
extrinsic: types.Extrinsic(extBytes), | ||
finalizedChan: make(chan *types.Header), | ||
} | ||
|
||
if c.BlockAPI == nil { | ||
return 0, fmt.Errorf("error BlockAPI not set") | ||
} | ||
esl.importedChanID, err = c.BlockAPI.RegisterImportedChannel(esl.importedChan) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
esl.finalizedChanID, err = c.BlockAPI.RegisterFinalizedChannel(esl.finalizedChan) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
c.qtyListeners++ | ||
esl.subID = c.qtyListeners | ||
c.Subscriptions[esl.subID] = esl | ||
c.BlockSubChannels[esl.subID] = esl.importedChanID | ||
|
||
err = c.CoreAPI.HandleSubmittedExtrinsic(extBytes) | ||
if err != nil { | ||
return 0, err | ||
} | ||
c.safeSend(newSubscriptionResponseJSON(esl.subID, reqID)) | ||
|
||
// TODO (ed) since HandleSubmittedExtrinsic has been called we assume the extrinsic is in the tx queue | ||
// should we add a channel to tx queue so we're notified when it's in the queue | ||
if c.CoreAPI.IsBlockProducer() { | ||
c.safeSend(newSubscriptionResponse(AuthorExtrinsicUpdates, esl.subID, "ready")) | ||
} | ||
|
||
// todo (ed) determine which peer extrinsic has been broadcast to, and set status | ||
return esl.subID, err | ||
} | ||
|
||
// Listen implementation of Listen interface to listen for importedChan changes | ||
func (l *ExtrinsicSubmitListener) Listen() { | ||
// listen for imported blocks with extrinsic | ||
|
@@ -322,25 +188,10 @@ type RuntimeVersionListener struct { | |
subID int | ||
} | ||
|
||
func (c *WSConn) initRuntimeVersionListener(reqID float64) (int, error) { | ||
rvl := &RuntimeVersionListener{ | ||
wsconn: c, | ||
} | ||
if c.CoreAPI == nil { | ||
c.safeSendError(reqID, nil, "error CoreAPI not set") | ||
return 0, fmt.Errorf("error CoreAPI not set") | ||
} | ||
c.qtyListeners++ | ||
rvl.subID = c.qtyListeners | ||
c.Subscriptions[rvl.subID] = rvl | ||
initRes := newSubscriptionResponseJSON(rvl.subID, reqID) | ||
c.safeSend(initRes) | ||
|
||
return rvl.subID, nil | ||
} | ||
|
||
// Listen implementation of Listen interface to listen for runtime version changes | ||
func (l *RuntimeVersionListener) Listen() { | ||
// This sends current runtime version once when subscription is created | ||
// TODO (ed) add logic to send updates when runtime version changes | ||
rtVersion, err := l.wsconn.CoreAPI.GetRuntimeVersion(nil) | ||
if err != nil { | ||
return | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace this with
struct
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume it needs to be in this format because it's what the JSON-RPC format is expecting? correct me if I'm wrong @edwardmack
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case, we can use
struct
tags.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@noot yes, I did this for JSON-RPC formatting, and yes using a struct is more readable. I've added that with tags.