2020-01-20 20:56:06 +00:00
package ext
2019-01-15 09:21:33 +00:00
import (
"fmt"
"hash/fnv"
"sync"
"time"
2019-11-23 17:57:05 +00:00
"github.com/status-im/status-go/eth-node/types"
2019-01-15 09:21:33 +00:00
)
const (
2020-01-20 20:56:06 +00:00
// DefaultRequestsDelay will be used in RequestsRegistry if no other was provided.
DefaultRequestsDelay = 3 * time . Second
2019-01-15 09:21:33 +00:00
)
type requestMeta struct {
timestamp time . Time
2019-11-23 17:57:05 +00:00
lastUID types . Hash
2019-01-15 09:21:33 +00:00
}
// NewRequestsRegistry creates instance of the RequestsRegistry and returns pointer to it.
func NewRequestsRegistry ( delay time . Duration ) * RequestsRegistry {
2019-02-26 12:55:01 +00:00
r := & RequestsRegistry {
delay : delay ,
2019-01-15 09:21:33 +00:00
}
2019-02-26 12:55:01 +00:00
r . Clear ( )
return r
2019-01-15 09:21:33 +00:00
}
// RequestsRegistry keeps map for all requests with timestamp when they were made.
type RequestsRegistry struct {
mu sync . Mutex
delay time . Duration
2019-11-23 17:57:05 +00:00
uidToTopics map [ types . Hash ] types . Hash
byTopicsHash map [ types . Hash ] requestMeta
2019-01-15 09:21:33 +00:00
}
// Register request with given topics. If request with same topics was made in less then configured delay then error
// will be returned.
2019-11-23 17:57:05 +00:00
func ( r * RequestsRegistry ) Register ( uid types . Hash , topics [ ] types . TopicType ) error {
2019-01-15 09:21:33 +00:00
r . mu . Lock ( )
defer r . mu . Unlock ( )
topicsHash := topicsToHash ( topics )
if meta , exist := r . byTopicsHash [ topicsHash ] ; exist {
if time . Since ( meta . timestamp ) < r . delay {
return fmt . Errorf ( "another request with the same topics was sent less than %s ago. Please wait for a bit longer, or set `force` to true in request parameters" , r . delay )
}
}
newMeta := requestMeta {
timestamp : time . Now ( ) ,
lastUID : uid ,
}
r . uidToTopics [ uid ] = topicsHash
r . byTopicsHash [ topicsHash ] = newMeta
return nil
}
2019-04-30 06:46:12 +00:00
// Has returns true if given uid is stored in registry.
2019-11-23 17:57:05 +00:00
func ( r * RequestsRegistry ) Has ( uid types . Hash ) bool {
2019-04-30 06:46:12 +00:00
r . mu . Lock ( )
defer r . mu . Unlock ( )
_ , exist := r . uidToTopics [ uid ]
return exist
}
2019-01-15 09:21:33 +00:00
// Unregister removes request with given UID from registry.
2019-11-23 17:57:05 +00:00
func ( r * RequestsRegistry ) Unregister ( uid types . Hash ) {
2019-01-15 09:21:33 +00:00
r . mu . Lock ( )
defer r . mu . Unlock ( )
topicsHash , exist := r . uidToTopics [ uid ]
if ! exist {
return
}
delete ( r . uidToTopics , uid )
meta := r . byTopicsHash [ topicsHash ]
// remove topicsHash only if we are trying to unregister last request with this topic.
if meta . lastUID == uid {
delete ( r . byTopicsHash , topicsHash )
}
}
2019-02-26 12:55:01 +00:00
// Clear recreates all structures used for caching requests.
func ( r * RequestsRegistry ) Clear ( ) {
r . mu . Lock ( )
defer r . mu . Unlock ( )
2019-11-23 17:57:05 +00:00
r . uidToTopics = map [ types . Hash ] types . Hash { }
r . byTopicsHash = map [ types . Hash ] requestMeta { }
2019-02-26 12:55:01 +00:00
}
2019-01-15 09:21:33 +00:00
// topicsToHash returns non-cryptographic hash of the topics.
2019-11-23 17:57:05 +00:00
func topicsToHash ( topics [ ] types . TopicType ) types . Hash {
2019-01-15 09:21:33 +00:00
hash := fnv . New32 ( )
for i := range topics {
_ , _ = hash . Write ( topics [ i ] [ : ] ) // never returns error per documentation
}
2019-11-23 17:57:05 +00:00
return types . BytesToHash ( hash . Sum ( nil ) )
2019-01-15 09:21:33 +00:00
}