mirror of https://github.com/status-im/go-waku.git
feat: add ntp timesource
This commit is contained in:
parent
066185ee86
commit
84c7022e2d
|
@ -46,6 +46,7 @@ func main() {
|
||||||
wakuNode, err := node.New(ctx,
|
wakuNode, err := node.New(ctx,
|
||||||
node.WithPrivateKey(prvKey),
|
node.WithPrivateKey(prvKey),
|
||||||
node.WithHostAddress(hostAddr),
|
node.WithHostAddress(hostAddr),
|
||||||
|
node.WithNTP(),
|
||||||
node.WithWakuRelay(),
|
node.WithWakuRelay(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -84,7 +85,7 @@ func write(ctx context.Context, wakuNode *node.WakuNode, msgContent string) {
|
||||||
contentTopic := protocol.NewContentTopic("basic2", 1, "test", "proto")
|
contentTopic := protocol.NewContentTopic("basic2", 1, "test", "proto")
|
||||||
|
|
||||||
var version uint32 = 0
|
var version uint32 = 0
|
||||||
var timestamp int64 = utils.GetUnixEpoch()
|
var timestamp int64 = utils.GetUnixEpoch(wakuNode.Timesource())
|
||||||
|
|
||||||
p := new(node.Payload)
|
p := new(node.Payload)
|
||||||
p.Data = []byte(wakuNode.ID() + ": " + msgContent)
|
p.Data = []byte(wakuNode.ID() + ": " + msgContent)
|
||||||
|
|
|
@ -249,7 +249,7 @@ func (c *Chat) SendMessage(line string) {
|
||||||
|
|
||||||
func (c *Chat) publish(ctx context.Context, message string) error {
|
func (c *Chat) publish(ctx context.Context, message string) error {
|
||||||
msg := &pb.Chat2Message{
|
msg := &pb.Chat2Message{
|
||||||
Timestamp: uint64(time.Now().Unix()),
|
Timestamp: uint64(c.node.Timesource().Now().Unix()),
|
||||||
Nick: c.nick,
|
Nick: c.nick,
|
||||||
Payload: []byte(message),
|
Payload: []byte(message),
|
||||||
}
|
}
|
||||||
|
@ -260,8 +260,7 @@ func (c *Chat) publish(ctx context.Context, message string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var version uint32
|
var version uint32
|
||||||
var t = time.Now()
|
var timestamp int64 = utils.GetUnixEpochFrom(c.node.Timesource().Now())
|
||||||
var timestamp int64 = utils.GetUnixEpochFrom(t)
|
|
||||||
var keyInfo *node.KeyInfo = &node.KeyInfo{}
|
var keyInfo *node.KeyInfo = &node.KeyInfo{}
|
||||||
|
|
||||||
if c.options.UsePayloadV1 { // Use WakuV1 encryption
|
if c.options.UsePayloadV1 { // Use WakuV1 encryption
|
||||||
|
|
|
@ -31,8 +31,9 @@ func execute(options Options) {
|
||||||
|
|
||||||
opts := []node.WakuNodeOption{
|
opts := []node.WakuNodeOption{
|
||||||
node.WithPrivateKey(options.NodeKey),
|
node.WithPrivateKey(options.NodeKey),
|
||||||
|
node.WithNTP(),
|
||||||
node.WithHostAddress(hostAddr),
|
node.WithHostAddress(hostAddr),
|
||||||
node.WithWakuStore(false, false),
|
node.WithWakuStore(false, nil),
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.Relay.Enable {
|
if options.Relay.Enable {
|
||||||
|
|
|
@ -143,7 +143,7 @@ func randomHex(n int) (string, error) {
|
||||||
|
|
||||||
func write(ctx context.Context, wakuNode *node.WakuNode, msgContent string) {
|
func write(ctx context.Context, wakuNode *node.WakuNode, msgContent string) {
|
||||||
var version uint32 = 0
|
var version uint32 = 0
|
||||||
var timestamp int64 = utils.GetUnixEpoch()
|
var timestamp int64 = utils.GetUnixEpoch(wakuNode.Timesource())
|
||||||
|
|
||||||
p := new(node.Payload)
|
p := new(node.Payload)
|
||||||
p.Data = []byte(wakuNode.ID() + ": " + msgContent)
|
p.Data = []byte(wakuNode.ID() + ": " + msgContent)
|
||||||
|
|
|
@ -213,6 +213,7 @@ func Execute(options Options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeOpts = append(nodeOpts, node.WithLibP2POptions(libp2pOpts...))
|
nodeOpts = append(nodeOpts, node.WithLibP2POptions(libp2pOpts...))
|
||||||
|
nodeOpts = append(nodeOpts, node.WithNTP())
|
||||||
|
|
||||||
if options.Relay.Enable {
|
if options.Relay.Enable {
|
||||||
var wakurelayopts []pubsub.Option
|
var wakurelayopts []pubsub.Option
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/waku-org/go-waku/waku/persistence/migrations"
|
"github.com/waku-org/go-waku/waku/persistence/migrations"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol"
|
"github.com/waku-org/go-waku/waku/v2/protocol"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
"github.com/waku-org/go-waku/waku/v2/utils"
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -20,6 +21,7 @@ type MessageProvider interface {
|
||||||
Put(env *protocol.Envelope) error
|
Put(env *protocol.Envelope) error
|
||||||
Query(query *pb.HistoryQuery) ([]StoredMessage, error)
|
Query(query *pb.HistoryQuery) ([]StoredMessage, error)
|
||||||
MostRecentTimestamp() (int64, error)
|
MostRecentTimestamp() (int64, error)
|
||||||
|
Start(timesource timesource.Timesource) error
|
||||||
Stop()
|
Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +34,7 @@ const WALMode = "wal"
|
||||||
type DBStore struct {
|
type DBStore struct {
|
||||||
MessageProvider
|
MessageProvider
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
|
timesource timesource.Timesource
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
|
|
||||||
maxMessages int
|
maxMessages int
|
||||||
|
@ -146,15 +149,21 @@ func NewDBStore(log *zap.Logger, options ...DBOption) (*DBStore, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = result.cleanOlderRecords()
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DBStore) Start(timesource timesource.Timesource) error {
|
||||||
|
d.timesource = timesource
|
||||||
|
|
||||||
|
err := d.cleanOlderRecords()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
result.wg.Add(1)
|
d.wg.Add(1)
|
||||||
go result.checkForOlderRecords(60 * time.Second)
|
go d.checkForOlderRecords(60 * time.Second)
|
||||||
|
|
||||||
return result, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DBStore) cleanOlderRecords() error {
|
func (d *DBStore) cleanOlderRecords() error {
|
||||||
|
@ -164,7 +173,7 @@ func (d *DBStore) cleanOlderRecords() error {
|
||||||
if d.maxDuration > 0 {
|
if d.maxDuration > 0 {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
sqlStmt := `DELETE FROM message WHERE receiverTimestamp < ?`
|
sqlStmt := `DELETE FROM message WHERE receiverTimestamp < ?`
|
||||||
_, err := d.db.Exec(sqlStmt, utils.GetUnixEpochFrom(time.Now().Add(-d.maxDuration)))
|
_, err := d.db.Exec(sqlStmt, utils.GetUnixEpochFrom(d.timesource.Now().Add(-d.maxDuration)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/waku-org/go-waku/tests"
|
"github.com/waku-org/go-waku/tests"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol"
|
"github.com/waku-org/go-waku/waku/v2/protocol"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
"github.com/waku-org/go-waku/waku/v2/utils"
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -28,6 +29,9 @@ func TestDbStore(t *testing.T) {
|
||||||
store, err := NewDBStore(utils.Logger(), option)
|
store, err := NewDBStore(utils.Logger(), option)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = store.Start(timesource.NewDefaultClock())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
res, err := store.GetAll()
|
res, err := store.GetAll()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Empty(t, res)
|
require.Empty(t, res)
|
||||||
|
@ -45,6 +49,9 @@ func TestStoreRetention(t *testing.T) {
|
||||||
store, err := NewDBStore(utils.Logger(), WithDB(db), WithRetentionPolicy(5, 20*time.Second))
|
store, err := NewDBStore(utils.Logger(), WithDB(db), WithRetentionPolicy(5, 20*time.Second))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = store.Start(timesource.NewDefaultClock())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
insertTime := time.Now()
|
insertTime := time.Now()
|
||||||
|
|
||||||
_ = store.Put(protocol.NewEnvelope(tests.CreateWakuMessage("test1", insertTime.Add(-70*time.Second).UnixNano()), insertTime.Add(-70*time.Second).UnixNano(), "test"))
|
_ = store.Put(protocol.NewEnvelope(tests.CreateWakuMessage("test1", insertTime.Add(-70*time.Second).UnixNano()), insertTime.Add(-70*time.Second).UnixNano(), "test"))
|
||||||
|
@ -65,6 +72,9 @@ func TestStoreRetention(t *testing.T) {
|
||||||
store, err = NewDBStore(utils.Logger(), WithDB(db), WithRetentionPolicy(5, 40*time.Second))
|
store, err = NewDBStore(utils.Logger(), WithDB(db), WithRetentionPolicy(5, 40*time.Second))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = store.Start(timesource.NewDefaultClock())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
dbResults, err = store.GetAll()
|
dbResults, err = store.GetAll()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, dbResults, 3)
|
require.Len(t, dbResults, 3)
|
||||||
|
|
|
@ -24,14 +24,14 @@ func (w *WakuNode) startKeepAlive(t time.Duration) {
|
||||||
ticker := time.NewTicker(t)
|
ticker := time.NewTicker(t)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
lastTimeExecuted := time.Now()
|
lastTimeExecuted := w.timesource.Now()
|
||||||
|
|
||||||
sleepDetectionInterval := int64(t) * 3
|
sleepDetectionInterval := int64(t) * 3
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
difference := time.Now().UnixNano() - lastTimeExecuted.UnixNano()
|
difference := w.timesource.Now().UnixNano() - lastTimeExecuted.UnixNano()
|
||||||
if difference > sleepDetectionInterval {
|
if difference > sleepDetectionInterval {
|
||||||
w.log.Warn("keep alive hasnt been executed recently. Killing all connections to peers")
|
w.log.Warn("keep alive hasnt been executed recently. Killing all connections to peers")
|
||||||
for _, p := range w.host.Network().Peers() {
|
for _, p := range w.host.Network().Peers() {
|
||||||
|
@ -40,7 +40,7 @@ func (w *WakuNode) startKeepAlive(t time.Duration) {
|
||||||
w.log.Warn("while disconnecting peer", zap.Error(err))
|
w.log.Warn("while disconnecting peer", zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastTimeExecuted = time.Now()
|
lastTimeExecuted = w.timesource.Now()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ func (w *WakuNode) startKeepAlive(t time.Duration) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lastTimeExecuted = time.Now()
|
lastTimeExecuted = w.timesource.Now()
|
||||||
case <-w.quit:
|
case <-w.quit:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ import (
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/store"
|
"github.com/waku-org/go-waku/waku/v2/protocol/store"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/swap"
|
"github.com/waku-org/go-waku/waku/v2/protocol/swap"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
|
|
||||||
"github.com/waku-org/go-waku/waku/v2/utils"
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
)
|
)
|
||||||
|
@ -66,6 +67,7 @@ type WakuNode struct {
|
||||||
host host.Host
|
host host.Host
|
||||||
opts *WakuNodeParameters
|
opts *WakuNodeParameters
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
|
timesource timesource.Timesource
|
||||||
|
|
||||||
relay *relay.WakuRelay
|
relay *relay.WakuRelay
|
||||||
filter *filter.WakuFilter
|
filter *filter.WakuFilter
|
||||||
|
@ -105,7 +107,7 @@ type WakuNode struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultStoreFactory(w *WakuNode) store.Store {
|
func defaultStoreFactory(w *WakuNode) store.Store {
|
||||||
return store.NewWakuStore(w.host, w.swap, w.opts.messageProvider, w.log)
|
return store.NewWakuStore(w.host, w.swap, w.opts.messageProvider, w.timesource, w.log)
|
||||||
}
|
}
|
||||||
|
|
||||||
// New is used to instantiate a WakuNode using a set of WakuNodeOptions
|
// New is used to instantiate a WakuNode using a set of WakuNodeOptions
|
||||||
|
@ -174,6 +176,12 @@ func New(ctx context.Context, opts ...WakuNodeOption) (*WakuNode, error) {
|
||||||
w.keepAliveFails = make(map[peer.ID]int)
|
w.keepAliveFails = make(map[peer.ID]int)
|
||||||
w.wakuFlag = utils.NewWakuEnrBitfield(w.opts.enableLightPush, w.opts.enableFilter, w.opts.enableStore, w.opts.enableRelay)
|
w.wakuFlag = utils.NewWakuEnrBitfield(w.opts.enableLightPush, w.opts.enableFilter, w.opts.enableStore, w.opts.enableRelay)
|
||||||
|
|
||||||
|
if params.enableNTP {
|
||||||
|
w.timesource = timesource.NewNTPTimesource(w.opts.ntpURLs, w.log)
|
||||||
|
} else {
|
||||||
|
w.timesource = timesource.NewDefaultClock()
|
||||||
|
}
|
||||||
|
|
||||||
if params.storeFactory != nil {
|
if params.storeFactory != nil {
|
||||||
w.storeFactory = params.storeFactory
|
w.storeFactory = params.storeFactory
|
||||||
} else {
|
} else {
|
||||||
|
@ -259,6 +267,13 @@ func (w *WakuNode) checkForAddressChanges() {
|
||||||
|
|
||||||
// Start initializes all the protocols that were setup in the WakuNode
|
// Start initializes all the protocols that were setup in the WakuNode
|
||||||
func (w *WakuNode) Start() error {
|
func (w *WakuNode) Start() error {
|
||||||
|
if w.opts.enableNTP {
|
||||||
|
err := w.timesource.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if w.opts.enableSwap {
|
if w.opts.enableSwap {
|
||||||
w.swap = swap.NewWakuSwap(w.log, []swap.SwapOption{
|
w.swap = swap.NewWakuSwap(w.log, []swap.SwapOption{
|
||||||
swap.WithMode(w.opts.swapMode),
|
swap.WithMode(w.opts.swapMode),
|
||||||
|
@ -268,11 +283,14 @@ func (w *WakuNode) Start() error {
|
||||||
|
|
||||||
w.store = w.storeFactory(w)
|
w.store = w.storeFactory(w)
|
||||||
if w.opts.enableStore {
|
if w.opts.enableStore {
|
||||||
w.startStore()
|
err := w.startStore()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.opts.enableFilter {
|
if w.opts.enableFilter {
|
||||||
filter, err := filter.NewWakuFilter(w.ctx, w.host, w.opts.isFilterFullNode, w.log, w.opts.filterOpts...)
|
filter, err := filter.NewWakuFilter(w.ctx, w.host, w.bcaster, w.opts.isFilterFullNode, w.timesource, w.log, w.opts.filterOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -302,10 +320,12 @@ func (w *WakuNode) Start() error {
|
||||||
w.opts.wOpts = append(w.opts.wOpts, pubsub.WithDiscovery(w.discoveryV5, w.opts.discV5Opts...))
|
w.opts.wOpts = append(w.opts.wOpts, pubsub.WithDiscovery(w.discoveryV5, w.opts.discV5Opts...))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if w.opts.enableRelay {
|
||||||
err = w.mountRelay(w.opts.minRelayPeersToPublish, w.opts.wOpts...)
|
err = w.mountRelay(w.opts.minRelayPeersToPublish, w.opts.wOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if w.opts.enableRLN {
|
if w.opts.enableRLN {
|
||||||
err = w.mountRlnRelay()
|
err = w.mountRlnRelay()
|
||||||
|
@ -360,11 +380,19 @@ func (w *WakuNode) Stop() {
|
||||||
w.discoveryV5.Stop()
|
w.discoveryV5.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if w.opts.enableRelay {
|
||||||
w.relay.Stop()
|
w.relay.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
w.lightPush.Stop()
|
w.lightPush.Stop()
|
||||||
w.store.Stop()
|
w.store.Stop()
|
||||||
_ = w.stopRlnRelay()
|
_ = w.stopRlnRelay()
|
||||||
|
|
||||||
|
err := w.timesource.Stop()
|
||||||
|
if err != nil {
|
||||||
|
w.log.Error("stopping timesource", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
w.host.Close()
|
w.host.Close()
|
||||||
|
|
||||||
w.wg.Wait()
|
w.wg.Wait()
|
||||||
|
@ -395,6 +423,12 @@ func (w *WakuNode) ENR() *enode.Node {
|
||||||
return w.localNode.Node()
|
return w.localNode.Node()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Timesource returns the timesource used by this node to obtain the current wall time
|
||||||
|
// Depending on the configuration it will be the local time or a ntp syncd time
|
||||||
|
func (w *WakuNode) Timesource() timesource.Timesource {
|
||||||
|
return w.timesource
|
||||||
|
}
|
||||||
|
|
||||||
// Relay is used to access any operation related to Waku Relay protocol
|
// Relay is used to access any operation related to Waku Relay protocol
|
||||||
func (w *WakuNode) Relay() *relay.WakuRelay {
|
func (w *WakuNode) Relay() *relay.WakuRelay {
|
||||||
return w.relay
|
return w.relay
|
||||||
|
@ -461,7 +495,7 @@ func (w *WakuNode) Publish(ctx context.Context, msg *pb.WakuMessage) error {
|
||||||
|
|
||||||
func (w *WakuNode) mountRelay(minRelayPeersToPublish int, opts ...pubsub.Option) error {
|
func (w *WakuNode) mountRelay(minRelayPeersToPublish int, opts ...pubsub.Option) error {
|
||||||
var err error
|
var err error
|
||||||
w.relay, err = relay.NewWakuRelay(w.ctx, w.host, w.bcaster, minRelayPeersToPublish, w.log, opts...)
|
w.relay, err = relay.NewWakuRelay(w.ctx, w.host, w.bcaster, minRelayPeersToPublish, w.timesource, w.log, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -499,8 +533,12 @@ func (w *WakuNode) mountPeerExchange() error {
|
||||||
return w.peerExchange.Start()
|
return w.peerExchange.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WakuNode) startStore() {
|
func (w *WakuNode) startStore() error {
|
||||||
w.store.Start(w.ctx)
|
err := w.store.Start(w.ctx)
|
||||||
|
if err != nil {
|
||||||
|
w.log.Error("starting store", zap.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if len(w.opts.resumeNodes) != 0 {
|
if len(w.opts.resumeNodes) != 0 {
|
||||||
// TODO: extract this to a function and run it when you go offline
|
// TODO: extract this to a function and run it when you go offline
|
||||||
|
@ -527,6 +565,7 @@ func (w *WakuNode) startStore() {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WakuNode) addPeer(info *peer.AddrInfo, protocols ...string) error {
|
func (w *WakuNode) addPeer(info *peer.AddrInfo, protocols ...string) error {
|
||||||
|
|
|
@ -47,7 +47,7 @@ func (w *WakuNode) mountRlnRelay() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// mount rlnrelay in off-chain mode with a static group of users
|
// mount rlnrelay in off-chain mode with a static group of users
|
||||||
rlnRelay, err := rln.RlnRelayStatic(w.ctx, w.relay, groupKeys, memKeyPair, memIndex, w.opts.rlnRelayPubsubTopic, w.opts.rlnRelayContentTopic, w.opts.rlnSpamHandler, w.log)
|
rlnRelay, err := rln.RlnRelayStatic(w.ctx, w.relay, groupKeys, memKeyPair, memIndex, w.opts.rlnRelayPubsubTopic, w.opts.rlnRelayContentTopic, w.opts.rlnSpamHandler, w.timesource, w.log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ func (w *WakuNode) mountRlnRelay() error {
|
||||||
|
|
||||||
// mount the rln relay protocol in the on-chain/dynamic mode
|
// mount the rln relay protocol in the on-chain/dynamic mode
|
||||||
var err error
|
var err error
|
||||||
w.rlnRelay, err = rln.RlnRelayDynamic(context.Background(), w.relay, w.opts.rlnETHClientAddress, w.opts.rlnETHPrivateKey, w.opts.rlnMembershipContractAddress, memKeyPair, w.opts.rlnRelayMemIndex, w.opts.rlnRelayPubsubTopic, w.opts.rlnRelayContentTopic, w.opts.rlnSpamHandler, w.opts.rlnRegistrationHandler, w.log)
|
w.rlnRelay, err = rln.RlnRelayDynamic(context.Background(), w.relay, w.opts.rlnETHClientAddress, w.opts.rlnETHPrivateKey, w.opts.rlnMembershipContractAddress, memKeyPair, w.opts.rlnRelayMemIndex, w.opts.rlnRelayPubsubTopic, w.opts.rlnRelayContentTopic, w.opts.rlnSpamHandler, w.opts.rlnRegistrationHandler, w.timesource, w.log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/filter"
|
"github.com/waku-org/go-waku/waku/v2/protocol/filter"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/store"
|
"github.com/waku-org/go-waku/waku/v2/protocol/store"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
"github.com/waku-org/go-waku/waku/v2/utils"
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -45,6 +46,9 @@ type WakuNodeParameters struct {
|
||||||
privKey *ecdsa.PrivateKey
|
privKey *ecdsa.PrivateKey
|
||||||
libP2POpts []libp2p.Option
|
libP2POpts []libp2p.Option
|
||||||
|
|
||||||
|
enableNTP bool
|
||||||
|
ntpURLs []string
|
||||||
|
|
||||||
enableWS bool
|
enableWS bool
|
||||||
wsPort int
|
wsPort int
|
||||||
enableWSS bool
|
enableWSS bool
|
||||||
|
@ -106,7 +110,6 @@ type WakuNodeOption func(*WakuNodeParameters) error
|
||||||
// Default options used in the libp2p node
|
// Default options used in the libp2p node
|
||||||
var DefaultWakuNodeOptions = []WakuNodeOption{
|
var DefaultWakuNodeOptions = []WakuNodeOption{
|
||||||
WithLogger(utils.Logger()),
|
WithLogger(utils.Logger()),
|
||||||
WithWakuRelay(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MultiAddresses return the list of multiaddresses configured in the node
|
// MultiAddresses return the list of multiaddresses configured in the node
|
||||||
|
@ -234,6 +237,21 @@ func WithPrivateKey(privKey *ecdsa.PrivateKey) WakuNodeOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithNTP is used to use ntp for any operation that requires obtaining time
|
||||||
|
// A list of ntp servers can be passed but if none is specified, some defaults
|
||||||
|
// will be used
|
||||||
|
func WithNTP(ntpURLs ...string) WakuNodeOption {
|
||||||
|
return func(params *WakuNodeParameters) error {
|
||||||
|
if len(ntpURLs) == 0 {
|
||||||
|
ntpURLs = timesource.DefaultServers
|
||||||
|
}
|
||||||
|
|
||||||
|
params.enableNTP = true
|
||||||
|
params.ntpURLs = ntpURLs
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GetPrivKey returns the private key used in the node
|
// GetPrivKey returns the private key used in the node
|
||||||
func (w *WakuNodeParameters) GetPrivKey() *crypto.PrivKey {
|
func (w *WakuNodeParameters) GetPrivKey() *crypto.PrivKey {
|
||||||
privKey := crypto.PrivKey(utils.EcdsaPrivKeyToSecp256k1PrivKey(w.privKey))
|
privKey := crypto.PrivKey(utils.EcdsaPrivKeyToSecp256k1PrivKey(w.privKey))
|
||||||
|
|
|
@ -30,7 +30,7 @@ func TestWakuOptions(t *testing.T) {
|
||||||
advertiseAddr, _ := net.ResolveTCPAddr("tcp", "0.0.0.0:0")
|
advertiseAddr, _ := net.ResolveTCPAddr("tcp", "0.0.0.0:0")
|
||||||
|
|
||||||
storeFactory := func(w *WakuNode) store.Store {
|
storeFactory := func(w *WakuNode) store.Store {
|
||||||
return store.NewWakuStore(w.host, w.swap, w.opts.messageProvider, w.log)
|
return store.NewWakuStore(w.host, w.swap, w.opts.messageProvider, w.timesource, w.log)
|
||||||
}
|
}
|
||||||
|
|
||||||
options := []WakuNodeOption{
|
options := []WakuNodeOption{
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol"
|
"github.com/waku-org/go-waku/waku/v2/protocol"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
"github.com/waku-org/go-waku/waku/v2/utils"
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ func makeWakuRelay(t *testing.T, topic string) (*relay.WakuRelay, *relay.Subscri
|
||||||
host, err := tests.MakeHost(context.Background(), port, rand.Reader)
|
host, err := tests.MakeHost(context.Background(), port, rand.Reader)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
relay, err := relay.NewWakuRelay(context.Background(), host, v2.NewBroadcaster(10), 0, utils.Logger())
|
relay, err := relay.NewWakuRelay(context.Background(), host, v2.NewBroadcaster(10), 0, timesource.NewDefaultClock(), utils.Logger())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
sub, err := relay.SubscribeToTopic(context.Background(), topic)
|
sub, err := relay.SubscribeToTopic(context.Background(), topic)
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
"github.com/waku-org/go-waku/waku/v2/metrics"
|
"github.com/waku-org/go-waku/waku/v2/metrics"
|
||||||
waku_proto "github.com/waku-org/go-waku/waku/v2/protocol"
|
waku_proto "github.com/waku-org/go-waku/waku/v2/protocol"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
||||||
"github.com/waku-org/go-waku/waku/v2/utils"
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
)
|
)
|
||||||
|
|
||||||
const WakuRelayID_v200 = protocol.ID("/vac/waku/relay/2.0.0")
|
const WakuRelayID_v200 = protocol.ID("/vac/waku/relay/2.0.0")
|
||||||
|
@ -32,6 +32,7 @@ var DefaultWakuTopic string = waku_proto.DefaultPubsubTopic().String()
|
||||||
type WakuRelay struct {
|
type WakuRelay struct {
|
||||||
host host.Host
|
host host.Host
|
||||||
pubsub *pubsub.PubSub
|
pubsub *pubsub.PubSub
|
||||||
|
timesource timesource.Timesource
|
||||||
|
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
|
|
||||||
|
@ -55,9 +56,10 @@ func msgIdFn(pmsg *pubsub_pb.Message) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWakuRelay returns a new instance of a WakuRelay struct
|
// NewWakuRelay returns a new instance of a WakuRelay struct
|
||||||
func NewWakuRelay(ctx context.Context, h host.Host, bcaster v2.Broadcaster, minPeersToPublish int, log *zap.Logger, opts ...pubsub.Option) (*WakuRelay, error) {
|
func NewWakuRelay(ctx context.Context, h host.Host, bcaster v2.Broadcaster, minPeersToPublish int, timesource timesource.Timesource, log *zap.Logger, opts ...pubsub.Option) (*WakuRelay, error) {
|
||||||
w := new(WakuRelay)
|
w := new(WakuRelay)
|
||||||
w.host = h
|
w.host = h
|
||||||
|
w.timesource = timesource
|
||||||
w.wakuRelayTopics = make(map[string]*pubsub.Topic)
|
w.wakuRelayTopics = make(map[string]*pubsub.Topic)
|
||||||
w.relaySubs = make(map[string]*pubsub.Subscription)
|
w.relaySubs = make(map[string]*pubsub.Subscription)
|
||||||
w.subscriptions = make(map[string][]*Subscription)
|
w.subscriptions = make(map[string][]*Subscription)
|
||||||
|
@ -343,7 +345,7 @@ func (w *WakuRelay) subscribeToTopic(t string, subscription *Subscription, sub *
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
envelope := waku_proto.NewEnvelope(wakuMessage, utils.GetUnixEpoch(), string(t))
|
envelope := waku_proto.NewEnvelope(wakuMessage, w.timesource.Now().UnixNano(), string(t))
|
||||||
|
|
||||||
w.log.Info("waku.relay received", logging.HexString("hash", envelope.Hash()))
|
w.log.Info("waku.relay received", logging.HexString("hash", envelope.Hash()))
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/waku-org/go-waku/tests"
|
"github.com/waku-org/go-waku/tests"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
"github.com/waku-org/go-waku/waku/v2/utils"
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ func TestWakuRelay(t *testing.T) {
|
||||||
host, err := tests.MakeHost(context.Background(), port, rand.Reader)
|
host, err := tests.MakeHost(context.Background(), port, rand.Reader)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
relay, err := NewWakuRelay(context.Background(), host, nil, 0, utils.Logger())
|
relay, err := NewWakuRelay(context.Background(), host, nil, 0, timesource.NewDefaultClock(), utils.Logger())
|
||||||
defer relay.Stop()
|
defer relay.Stop()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
r "github.com/waku-org/go-zerokit-rln/rln"
|
r "github.com/waku-org/go-zerokit-rln/rln"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -20,6 +21,7 @@ func RlnRelayStatic(
|
||||||
pubsubTopic string,
|
pubsubTopic string,
|
||||||
contentTopic string,
|
contentTopic string,
|
||||||
spamHandler SpamHandler,
|
spamHandler SpamHandler,
|
||||||
|
timesource timesource.Timesource,
|
||||||
log *zap.Logger,
|
log *zap.Logger,
|
||||||
) (*WakuRLNRelay, error) {
|
) (*WakuRLNRelay, error) {
|
||||||
log = log.Named("rln-static")
|
log = log.Named("rln-static")
|
||||||
|
@ -45,6 +47,7 @@ func RlnRelayStatic(
|
||||||
pubsubTopic: pubsubTopic,
|
pubsubTopic: pubsubTopic,
|
||||||
contentTopic: contentTopic,
|
contentTopic: contentTopic,
|
||||||
log: log,
|
log: log,
|
||||||
|
timesource: timesource,
|
||||||
nullifierLog: make(map[r.Epoch][]r.ProofMetadata),
|
nullifierLog: make(map[r.Epoch][]r.ProofMetadata),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +90,7 @@ func RlnRelayDynamic(
|
||||||
contentTopic string,
|
contentTopic string,
|
||||||
spamHandler SpamHandler,
|
spamHandler SpamHandler,
|
||||||
registrationHandler RegistrationHandler,
|
registrationHandler RegistrationHandler,
|
||||||
|
timesource timesource.Timesource,
|
||||||
log *zap.Logger,
|
log *zap.Logger,
|
||||||
) (*WakuRLNRelay, error) {
|
) (*WakuRLNRelay, error) {
|
||||||
log = log.Named("rln-dynamic")
|
log = log.Named("rln-dynamic")
|
||||||
|
@ -109,6 +113,7 @@ func RlnRelayDynamic(
|
||||||
pubsubTopic: pubsubTopic,
|
pubsubTopic: pubsubTopic,
|
||||||
contentTopic: contentTopic,
|
contentTopic: contentTopic,
|
||||||
log: log,
|
log: log,
|
||||||
|
timesource: timesource,
|
||||||
nullifierLog: make(map[r.Epoch][]r.ProofMetadata),
|
nullifierLog: make(map[r.Epoch][]r.ProofMetadata),
|
||||||
registrationHandler: registrationHandler,
|
registrationHandler: registrationHandler,
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/waku-org/go-waku/tests"
|
"github.com/waku-org/go-waku/tests"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
"github.com/waku-org/go-waku/waku/v2/utils"
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
r "github.com/waku-org/go-zerokit-rln/rln"
|
r "github.com/waku-org/go-zerokit-rln/rln"
|
||||||
)
|
)
|
||||||
|
@ -32,7 +33,7 @@ func (s *WakuRLNRelaySuite) TestOffchainMode() {
|
||||||
host, err := tests.MakeHost(context.Background(), port, rand.Reader)
|
host, err := tests.MakeHost(context.Background(), port, rand.Reader)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
relay, err := relay.NewWakuRelay(context.Background(), host, nil, 0, utils.Logger())
|
relay, err := relay.NewWakuRelay(context.Background(), host, nil, 0, timesource.NewDefaultClock(), utils.Logger())
|
||||||
defer relay.Stop()
|
defer relay.Stop()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
@ -49,7 +50,7 @@ func (s *WakuRLNRelaySuite) TestOffchainMode() {
|
||||||
// index also represents the index of the leaf in the Merkle tree that contains node's commitment key
|
// index also represents the index of the leaf in the Merkle tree that contains node's commitment key
|
||||||
index := r.MembershipIndex(5)
|
index := r.MembershipIndex(5)
|
||||||
|
|
||||||
wakuRLNRelay, err := RlnRelayStatic(context.TODO(), relay, groupIDCommitments, groupKeyPairs[index], index, RLNRELAY_PUBSUB_TOPIC, RLNRELAY_CONTENT_TOPIC, nil, utils.Logger())
|
wakuRLNRelay, err := RlnRelayStatic(context.TODO(), relay, groupIDCommitments, groupKeyPairs[index], index, RLNRELAY_PUBSUB_TOPIC, RLNRELAY_CONTENT_TOPIC, nil, timesource.NewDefaultClock(), utils.Logger())
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// get the root of Merkle tree which is constructed inside the mountRlnRelay proc
|
// get the root of Merkle tree which is constructed inside the mountRlnRelay proc
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/libp2p/go-libp2p/core/peer"
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
"github.com/waku-org/go-waku/waku/v2/utils"
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
r "github.com/waku-org/go-zerokit-rln/rln"
|
r "github.com/waku-org/go-zerokit-rln/rln"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
@ -34,6 +35,7 @@ const AcceptableRootWindowSize = 5
|
||||||
|
|
||||||
type WakuRLNRelay struct {
|
type WakuRLNRelay struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
timesource timesource.Timesource
|
||||||
|
|
||||||
membershipKeyPair *r.MembershipKeyPair
|
membershipKeyPair *r.MembershipKeyPair
|
||||||
|
|
||||||
|
@ -206,7 +208,7 @@ func (rln *WakuRLNRelay) ValidateMessage(msg *pb.WakuMessage, optionalTime *time
|
||||||
epoch = r.CalcEpoch(*optionalTime)
|
epoch = r.CalcEpoch(*optionalTime)
|
||||||
} else {
|
} else {
|
||||||
// get current rln epoch
|
// get current rln epoch
|
||||||
epoch = r.GetCurrentEpoch()
|
epoch = r.CalcEpoch(rln.timesource.Now())
|
||||||
}
|
}
|
||||||
|
|
||||||
msgProof := ToRateLimitProof(msg)
|
msgProof := ToRateLimitProof(msg)
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/waku-org/go-waku/tests"
|
"github.com/waku-org/go-waku/tests"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol"
|
"github.com/waku-org/go-waku/waku/v2/protocol"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
"github.com/waku-org/go-waku/waku/v2/utils"
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,7 +22,7 @@ func TestFindLastSeenMessage(t *testing.T) {
|
||||||
msg4 := protocol.NewEnvelope(tests.CreateWakuMessage("4", 4), utils.GetUnixEpoch(), "test")
|
msg4 := protocol.NewEnvelope(tests.CreateWakuMessage("4", 4), utils.GetUnixEpoch(), "test")
|
||||||
msg5 := protocol.NewEnvelope(tests.CreateWakuMessage("5", 5), utils.GetUnixEpoch(), "test")
|
msg5 := protocol.NewEnvelope(tests.CreateWakuMessage("5", 5), utils.GetUnixEpoch(), "test")
|
||||||
|
|
||||||
s := NewWakuStore(nil, nil, MemoryDB(t), utils.Logger())
|
s := NewWakuStore(nil, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
_ = s.storeMessage(msg1)
|
_ = s.storeMessage(msg1)
|
||||||
_ = s.storeMessage(msg3)
|
_ = s.storeMessage(msg3)
|
||||||
_ = s.storeMessage(msg5)
|
_ = s.storeMessage(msg5)
|
||||||
|
@ -41,8 +42,10 @@ func TestResume(t *testing.T) {
|
||||||
host1, err := libp2p.New(libp2p.DefaultTransports, libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
host1, err := libp2p.New(libp2p.DefaultTransports, libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
s1 := NewWakuStore(host1, nil, MemoryDB(t), utils.Logger())
|
s1 := NewWakuStore(host1, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
s1.Start(ctx)
|
err = s1.Start(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
defer s1.Stop()
|
defer s1.Stop()
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
|
@ -59,8 +62,9 @@ func TestResume(t *testing.T) {
|
||||||
host2, err := libp2p.New(libp2p.DefaultTransports, libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
host2, err := libp2p.New(libp2p.DefaultTransports, libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
s2 := NewWakuStore(host2, nil, MemoryDB(t), utils.Logger())
|
s2 := NewWakuStore(host2, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
s2.Start(ctx)
|
err = s2.Start(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
defer s2.Stop()
|
defer s2.Stop()
|
||||||
|
|
||||||
host2.Peerstore().AddAddr(host1.ID(), tests.GetHostAddress(host1), peerstore.PermanentAddrTTL)
|
host2.Peerstore().AddAddr(host1.ID(), tests.GetHostAddress(host1), peerstore.PermanentAddrTTL)
|
||||||
|
@ -95,8 +99,10 @@ func TestResumeWithListOfPeers(t *testing.T) {
|
||||||
host1, err := libp2p.New(libp2p.DefaultTransports, libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
host1, err := libp2p.New(libp2p.DefaultTransports, libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
s1 := NewWakuStore(host1, nil, MemoryDB(t), utils.Logger())
|
s1 := NewWakuStore(host1, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
s1.Start(ctx)
|
err = s1.Start(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
defer s1.Stop()
|
defer s1.Stop()
|
||||||
|
|
||||||
msg0 := &pb.WakuMessage{Payload: []byte{1, 2, 3}, ContentTopic: "2", Version: 0, Timestamp: 0}
|
msg0 := &pb.WakuMessage{Payload: []byte{1, 2, 3}, ContentTopic: "2", Version: 0, Timestamp: 0}
|
||||||
|
@ -106,8 +112,9 @@ func TestResumeWithListOfPeers(t *testing.T) {
|
||||||
host2, err := libp2p.New(libp2p.DefaultTransports, libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
host2, err := libp2p.New(libp2p.DefaultTransports, libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
s2 := NewWakuStore(host2, nil, MemoryDB(t), utils.Logger())
|
s2 := NewWakuStore(host2, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
s2.Start(ctx)
|
err = s2.Start(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
defer s2.Stop()
|
defer s2.Stop()
|
||||||
|
|
||||||
host2.Peerstore().AddAddr(host1.ID(), tests.GetHostAddress(host1), peerstore.PermanentAddrTTL)
|
host2.Peerstore().AddAddr(host1.ID(), tests.GetHostAddress(host1), peerstore.PermanentAddrTTL)
|
||||||
|
@ -131,8 +138,10 @@ func TestResumeWithoutSpecifyingPeer(t *testing.T) {
|
||||||
host1, err := libp2p.New(libp2p.DefaultTransports, libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
host1, err := libp2p.New(libp2p.DefaultTransports, libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
s1 := NewWakuStore(host1, nil, MemoryDB(t), utils.Logger())
|
s1 := NewWakuStore(host1, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
s1.Start(ctx)
|
err = s1.Start(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
defer s1.Stop()
|
defer s1.Stop()
|
||||||
|
|
||||||
msg0 := &pb.WakuMessage{Payload: []byte{1, 2, 3}, ContentTopic: "2", Version: 0, Timestamp: 0}
|
msg0 := &pb.WakuMessage{Payload: []byte{1, 2, 3}, ContentTopic: "2", Version: 0, Timestamp: 0}
|
||||||
|
@ -142,8 +151,10 @@ func TestResumeWithoutSpecifyingPeer(t *testing.T) {
|
||||||
host2, err := libp2p.New(libp2p.DefaultTransports, libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
host2, err := libp2p.New(libp2p.DefaultTransports, libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
s2 := NewWakuStore(host2, nil, MemoryDB(t), utils.Logger())
|
s2 := NewWakuStore(host2, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
s2.Start(ctx)
|
err = s2.Start(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
defer s2.Stop()
|
defer s2.Stop()
|
||||||
|
|
||||||
host2.Peerstore().AddAddr(host1.ID(), tests.GetHostAddress(host1), peerstore.PermanentAddrTTL)
|
host2.Peerstore().AddAddr(host1.ID(), tests.GetHostAddress(host1), peerstore.PermanentAddrTTL)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
libp2pProtocol "github.com/libp2p/go-libp2p/core/protocol"
|
libp2pProtocol "github.com/libp2p/go-libp2p/core/protocol"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol"
|
"github.com/waku-org/go-waku/waku/v2/protocol"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/swap"
|
"github.com/waku-org/go-waku/waku/v2/protocol/swap"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -47,6 +48,7 @@ var (
|
||||||
|
|
||||||
type WakuStore struct {
|
type WakuStore struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
timesource timesource.Timesource
|
||||||
MsgC chan *protocol.Envelope
|
MsgC chan *protocol.Envelope
|
||||||
wg *sync.WaitGroup
|
wg *sync.WaitGroup
|
||||||
|
|
||||||
|
@ -61,7 +63,7 @@ type WakuStore struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWakuStore creates a WakuStore using an specific MessageProvider for storing the messages
|
// NewWakuStore creates a WakuStore using an specific MessageProvider for storing the messages
|
||||||
func NewWakuStore(host host.Host, swap *swap.WakuSwap, p MessageProvider, log *zap.Logger) *WakuStore {
|
func NewWakuStore(host host.Host, swap *swap.WakuSwap, p MessageProvider, timesource timesource.Timesource, log *zap.Logger) *WakuStore {
|
||||||
wakuStore := new(WakuStore)
|
wakuStore := new(WakuStore)
|
||||||
wakuStore.msgProvider = p
|
wakuStore.msgProvider = p
|
||||||
wakuStore.h = host
|
wakuStore.h = host
|
||||||
|
@ -69,6 +71,7 @@ func NewWakuStore(host host.Host, swap *swap.WakuSwap, p MessageProvider, log *z
|
||||||
wakuStore.wg = &sync.WaitGroup{}
|
wakuStore.wg = &sync.WaitGroup{}
|
||||||
wakuStore.log = log.Named("store")
|
wakuStore.log = log.Named("store")
|
||||||
wakuStore.quit = make(chan struct{})
|
wakuStore.quit = make(chan struct{})
|
||||||
|
wakuStore.timesource = timesource
|
||||||
|
|
||||||
return wakuStore
|
return wakuStore
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,14 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol"
|
"github.com/waku-org/go-waku/waku/v2/protocol"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
"github.com/waku-org/go-waku/waku/v2/utils"
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStorePersistence(t *testing.T) {
|
func TestStorePersistence(t *testing.T) {
|
||||||
db := MemoryDB(t)
|
db := MemoryDB(t)
|
||||||
|
|
||||||
s1 := NewWakuStore(nil, nil, db, utils.Logger())
|
s1 := NewWakuStore(nil, nil, db, timesource.NewDefaultClock(), utils.Logger())
|
||||||
|
|
||||||
defaultPubSubTopic := "test"
|
defaultPubSubTopic := "test"
|
||||||
defaultContentTopic := "1"
|
defaultContentTopic := "1"
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
"github.com/waku-org/go-waku/waku/v2/metrics"
|
"github.com/waku-org/go-waku/waku/v2/metrics"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol"
|
"github.com/waku-org/go-waku/waku/v2/protocol"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
||||||
"github.com/waku-org/go-waku/waku/v2/utils"
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MaxTimeVariance is the maximum duration in the future allowed for a message timestamp
|
// MaxTimeVariance is the maximum duration in the future allowed for a message timestamp
|
||||||
|
@ -78,12 +78,13 @@ type MessageProvider interface {
|
||||||
Query(query *pb.HistoryQuery) (*pb.Index, []persistence.StoredMessage, error)
|
Query(query *pb.HistoryQuery) (*pb.Index, []persistence.StoredMessage, error)
|
||||||
Put(env *protocol.Envelope) error
|
Put(env *protocol.Envelope) error
|
||||||
MostRecentTimestamp() (int64, error)
|
MostRecentTimestamp() (int64, error)
|
||||||
|
Start(timesource timesource.Timesource) error
|
||||||
Stop()
|
Stop()
|
||||||
Count() (int, error)
|
Count() (int, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Store interface {
|
type Store interface {
|
||||||
Start(ctx context.Context)
|
Start(ctx context.Context) error
|
||||||
Query(ctx context.Context, query Query, opts ...HistoryRequestOption) (*Result, error)
|
Query(ctx context.Context, query Query, opts ...HistoryRequestOption) (*Result, error)
|
||||||
Find(ctx context.Context, query Query, cb criteriaFN, opts ...HistoryRequestOption) (*pb.WakuMessage, error)
|
Find(ctx context.Context, query Query, cb criteriaFN, opts ...HistoryRequestOption) (*pb.WakuMessage, error)
|
||||||
Next(ctx context.Context, r *Result) (*Result, error)
|
Next(ctx context.Context, r *Result) (*Result, error)
|
||||||
|
@ -98,14 +99,20 @@ func (store *WakuStore) SetMessageProvider(p MessageProvider) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start initializes the WakuStore by enabling the protocol and fetching records from a message provider
|
// Start initializes the WakuStore by enabling the protocol and fetching records from a message provider
|
||||||
func (store *WakuStore) Start(ctx context.Context) {
|
func (store *WakuStore) Start(ctx context.Context) error {
|
||||||
if store.started {
|
if store.started {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if store.msgProvider == nil {
|
if store.msgProvider == nil {
|
||||||
store.log.Info("Store protocol started (no message provider)")
|
store.log.Info("Store protocol started (no message provider)")
|
||||||
return
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := store.msgProvider.Start(store.timesource)
|
||||||
|
if err != nil {
|
||||||
|
store.log.Error("Error starting message provider", zap.Error(err))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
store.started = true
|
store.started = true
|
||||||
|
@ -119,6 +126,8 @@ func (store *WakuStore) Start(ctx context.Context) {
|
||||||
go store.updateMetrics(ctx)
|
go store.updateMetrics(ctx)
|
||||||
|
|
||||||
store.log.Info("Store protocol started")
|
store.log.Info("Store protocol started")
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *WakuStore) storeMessage(env *protocol.Envelope) error {
|
func (store *WakuStore) storeMessage(env *protocol.Envelope) error {
|
||||||
|
@ -226,6 +235,7 @@ func (store *WakuStore) Stop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if store.msgProvider != nil {
|
if store.msgProvider != nil {
|
||||||
|
store.msgProvider.Stop()
|
||||||
store.quit <- struct{}{}
|
store.quit <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,14 +308,13 @@ func (store *WakuStore) Resume(ctx context.Context, pubsubTopic string, peerList
|
||||||
return 0, errors.New("can't resume: store has not started")
|
return 0, errors.New("can't resume: store has not started")
|
||||||
}
|
}
|
||||||
|
|
||||||
currentTime := utils.GetUnixEpoch()
|
|
||||||
lastSeenTime, err := store.findLastSeen()
|
lastSeenTime, err := store.findLastSeen()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var offset int64 = int64(20 * time.Nanosecond)
|
var offset int64 = int64(20 * time.Nanosecond)
|
||||||
currentTime = currentTime + offset
|
currentTime := store.timesource.Now().UnixNano() + offset
|
||||||
lastSeenTime = max(lastSeenTime-offset, 0)
|
lastSeenTime = max(lastSeenTime-offset, 0)
|
||||||
|
|
||||||
rpc := &pb.HistoryQuery{
|
rpc := &pb.HistoryQuery{
|
||||||
|
@ -330,7 +339,7 @@ func (store *WakuStore) Resume(ctx context.Context, pubsubTopic string, peerList
|
||||||
|
|
||||||
msgCount := 0
|
msgCount := 0
|
||||||
for _, msg := range messages {
|
for _, msg := range messages {
|
||||||
if err = store.storeMessage(protocol.NewEnvelope(msg, utils.GetUnixEpoch(), pubsubTopic)); err == nil {
|
if err = store.storeMessage(protocol.NewEnvelope(msg, store.timesource.Now().UnixNano(), pubsubTopic)); err == nil {
|
||||||
msgCount++
|
msgCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/waku-org/go-waku/tests"
|
"github.com/waku-org/go-waku/tests"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol"
|
"github.com/waku-org/go-waku/waku/v2/protocol"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
"github.com/waku-org/go-waku/waku/v2/utils"
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,8 +21,10 @@ func TestWakuStoreProtocolQuery(t *testing.T) {
|
||||||
host1, err := libp2p.New(libp2p.DefaultTransports, libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
host1, err := libp2p.New(libp2p.DefaultTransports, libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
s1 := NewWakuStore(host1, nil, MemoryDB(t), utils.Logger())
|
s1 := NewWakuStore(host1, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
s1.Start(ctx)
|
err = s1.Start(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
defer s1.Stop()
|
defer s1.Stop()
|
||||||
|
|
||||||
topic1 := "1"
|
topic1 := "1"
|
||||||
|
@ -39,8 +42,9 @@ func TestWakuStoreProtocolQuery(t *testing.T) {
|
||||||
// Simulate a message has been received via relay protocol
|
// Simulate a message has been received via relay protocol
|
||||||
s1.MsgC <- protocol.NewEnvelope(msg, utils.GetUnixEpoch(), pubsubTopic1)
|
s1.MsgC <- protocol.NewEnvelope(msg, utils.GetUnixEpoch(), pubsubTopic1)
|
||||||
|
|
||||||
s2 := NewWakuStore(host2, nil, MemoryDB(t), utils.Logger())
|
s2 := NewWakuStore(host2, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
s2.Start(ctx)
|
err = s2.Start(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
defer s2.Stop()
|
defer s2.Stop()
|
||||||
|
|
||||||
host2.Peerstore().AddAddr(host1.ID(), tests.GetHostAddress(host1), peerstore.PermanentAddrTTL)
|
host2.Peerstore().AddAddr(host1.ID(), tests.GetHostAddress(host1), peerstore.PermanentAddrTTL)
|
||||||
|
@ -67,10 +71,9 @@ func TestWakuStoreProtocolNext(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
db := MemoryDB(t)
|
db := MemoryDB(t)
|
||||||
|
s1 := NewWakuStore(host1, nil, db, timesource.NewDefaultClock(), utils.Logger())
|
||||||
s1 := NewWakuStore(host1, nil, db, utils.Logger())
|
err = s1.Start(ctx)
|
||||||
s1.Start(ctx)
|
require.NoError(t, err)
|
||||||
defer s1.Stop()
|
|
||||||
|
|
||||||
topic1 := "1"
|
topic1 := "1"
|
||||||
pubsubTopic1 := "topic1"
|
pubsubTopic1 := "topic1"
|
||||||
|
@ -94,8 +97,9 @@ func TestWakuStoreProtocolNext(t *testing.T) {
|
||||||
err = host2.Peerstore().AddProtocols(host1.ID(), string(StoreID_v20beta4))
|
err = host2.Peerstore().AddProtocols(host1.ID(), string(StoreID_v20beta4))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
s2 := NewWakuStore(host2, nil, db, utils.Logger())
|
s2 := NewWakuStore(host2, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
s2.Start(ctx)
|
err = s2.Start(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
defer s2.Stop()
|
defer s2.Stop()
|
||||||
|
|
||||||
q := Query{
|
q := Query{
|
||||||
|
@ -133,10 +137,9 @@ func TestWakuStoreProtocolFind(t *testing.T) {
|
||||||
host1, err := libp2p.New(libp2p.DefaultTransports, libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
host1, err := libp2p.New(libp2p.DefaultTransports, libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
db := MemoryDB(t)
|
s1 := NewWakuStore(host1, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
|
err = s1.Start(ctx)
|
||||||
s1 := NewWakuStore(host1, nil, db, utils.Logger())
|
require.NoError(t, err)
|
||||||
s1.Start(ctx)
|
|
||||||
defer s1.Stop()
|
defer s1.Stop()
|
||||||
|
|
||||||
topic1 := "1"
|
topic1 := "1"
|
||||||
|
@ -169,8 +172,9 @@ func TestWakuStoreProtocolFind(t *testing.T) {
|
||||||
err = host2.Peerstore().AddProtocols(host1.ID(), string(StoreID_v20beta4))
|
err = host2.Peerstore().AddProtocols(host1.ID(), string(StoreID_v20beta4))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
s2 := NewWakuStore(host2, nil, db, utils.Logger())
|
s2 := NewWakuStore(host2, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
s2.Start(ctx)
|
err = s2.Start(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
defer s2.Stop()
|
defer s2.Stop()
|
||||||
|
|
||||||
q := Query{
|
q := Query{
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/waku-org/go-waku/tests"
|
"github.com/waku-org/go-waku/tests"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol"
|
"github.com/waku-org/go-waku/waku/v2/protocol"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
"github.com/waku-org/go-waku/waku/v2/utils"
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ func TestStoreQuery(t *testing.T) {
|
||||||
msg1 := tests.CreateWakuMessage(defaultContentTopic, utils.GetUnixEpoch())
|
msg1 := tests.CreateWakuMessage(defaultContentTopic, utils.GetUnixEpoch())
|
||||||
msg2 := tests.CreateWakuMessage("2", utils.GetUnixEpoch())
|
msg2 := tests.CreateWakuMessage("2", utils.GetUnixEpoch())
|
||||||
|
|
||||||
s := NewWakuStore(nil, nil, MemoryDB(t), utils.Logger())
|
s := NewWakuStore(nil, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
_ = s.storeMessage(protocol.NewEnvelope(msg1, utils.GetUnixEpoch(), defaultPubSubTopic))
|
_ = s.storeMessage(protocol.NewEnvelope(msg1, utils.GetUnixEpoch(), defaultPubSubTopic))
|
||||||
_ = s.storeMessage(protocol.NewEnvelope(msg2, utils.GetUnixEpoch(), defaultPubSubTopic))
|
_ = s.storeMessage(protocol.NewEnvelope(msg2, utils.GetUnixEpoch(), defaultPubSubTopic))
|
||||||
|
|
||||||
|
@ -43,7 +44,7 @@ func TestStoreQueryMultipleContentFilters(t *testing.T) {
|
||||||
msg2 := tests.CreateWakuMessage(topic2, utils.GetUnixEpoch())
|
msg2 := tests.CreateWakuMessage(topic2, utils.GetUnixEpoch())
|
||||||
msg3 := tests.CreateWakuMessage(topic3, utils.GetUnixEpoch())
|
msg3 := tests.CreateWakuMessage(topic3, utils.GetUnixEpoch())
|
||||||
|
|
||||||
s := NewWakuStore(nil, nil, MemoryDB(t), utils.Logger())
|
s := NewWakuStore(nil, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
|
|
||||||
_ = s.storeMessage(protocol.NewEnvelope(msg1, utils.GetUnixEpoch(), defaultPubSubTopic))
|
_ = s.storeMessage(protocol.NewEnvelope(msg1, utils.GetUnixEpoch(), defaultPubSubTopic))
|
||||||
_ = s.storeMessage(protocol.NewEnvelope(msg2, utils.GetUnixEpoch(), defaultPubSubTopic))
|
_ = s.storeMessage(protocol.NewEnvelope(msg2, utils.GetUnixEpoch(), defaultPubSubTopic))
|
||||||
|
@ -77,7 +78,7 @@ func TestStoreQueryPubsubTopicFilter(t *testing.T) {
|
||||||
msg2 := tests.CreateWakuMessage(topic2, utils.GetUnixEpoch())
|
msg2 := tests.CreateWakuMessage(topic2, utils.GetUnixEpoch())
|
||||||
msg3 := tests.CreateWakuMessage(topic3, utils.GetUnixEpoch())
|
msg3 := tests.CreateWakuMessage(topic3, utils.GetUnixEpoch())
|
||||||
|
|
||||||
s := NewWakuStore(nil, nil, MemoryDB(t), utils.Logger())
|
s := NewWakuStore(nil, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
_ = s.storeMessage(protocol.NewEnvelope(msg1, utils.GetUnixEpoch(), pubsubTopic1))
|
_ = s.storeMessage(protocol.NewEnvelope(msg1, utils.GetUnixEpoch(), pubsubTopic1))
|
||||||
_ = s.storeMessage(protocol.NewEnvelope(msg2, utils.GetUnixEpoch(), pubsubTopic2))
|
_ = s.storeMessage(protocol.NewEnvelope(msg2, utils.GetUnixEpoch(), pubsubTopic2))
|
||||||
_ = s.storeMessage(protocol.NewEnvelope(msg3, utils.GetUnixEpoch(), pubsubTopic2))
|
_ = s.storeMessage(protocol.NewEnvelope(msg3, utils.GetUnixEpoch(), pubsubTopic2))
|
||||||
|
@ -109,7 +110,7 @@ func TestStoreQueryPubsubTopicNoMatch(t *testing.T) {
|
||||||
msg2 := tests.CreateWakuMessage(topic2, utils.GetUnixEpoch())
|
msg2 := tests.CreateWakuMessage(topic2, utils.GetUnixEpoch())
|
||||||
msg3 := tests.CreateWakuMessage(topic3, utils.GetUnixEpoch())
|
msg3 := tests.CreateWakuMessage(topic3, utils.GetUnixEpoch())
|
||||||
|
|
||||||
s := NewWakuStore(nil, nil, MemoryDB(t), utils.Logger())
|
s := NewWakuStore(nil, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
_ = s.storeMessage(protocol.NewEnvelope(msg1, utils.GetUnixEpoch(), pubsubTopic2))
|
_ = s.storeMessage(protocol.NewEnvelope(msg1, utils.GetUnixEpoch(), pubsubTopic2))
|
||||||
_ = s.storeMessage(protocol.NewEnvelope(msg2, utils.GetUnixEpoch(), pubsubTopic2))
|
_ = s.storeMessage(protocol.NewEnvelope(msg2, utils.GetUnixEpoch(), pubsubTopic2))
|
||||||
_ = s.storeMessage(protocol.NewEnvelope(msg3, utils.GetUnixEpoch(), pubsubTopic2))
|
_ = s.storeMessage(protocol.NewEnvelope(msg3, utils.GetUnixEpoch(), pubsubTopic2))
|
||||||
|
@ -131,7 +132,7 @@ func TestStoreQueryPubsubTopicAllMessages(t *testing.T) {
|
||||||
msg2 := tests.CreateWakuMessage(topic2, utils.GetUnixEpoch())
|
msg2 := tests.CreateWakuMessage(topic2, utils.GetUnixEpoch())
|
||||||
msg3 := tests.CreateWakuMessage(topic3, utils.GetUnixEpoch())
|
msg3 := tests.CreateWakuMessage(topic3, utils.GetUnixEpoch())
|
||||||
|
|
||||||
s := NewWakuStore(nil, nil, MemoryDB(t), utils.Logger())
|
s := NewWakuStore(nil, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
_ = s.storeMessage(protocol.NewEnvelope(msg1, utils.GetUnixEpoch(), pubsubTopic1))
|
_ = s.storeMessage(protocol.NewEnvelope(msg1, utils.GetUnixEpoch(), pubsubTopic1))
|
||||||
_ = s.storeMessage(protocol.NewEnvelope(msg2, utils.GetUnixEpoch(), pubsubTopic1))
|
_ = s.storeMessage(protocol.NewEnvelope(msg2, utils.GetUnixEpoch(), pubsubTopic1))
|
||||||
_ = s.storeMessage(protocol.NewEnvelope(msg3, utils.GetUnixEpoch(), pubsubTopic1))
|
_ = s.storeMessage(protocol.NewEnvelope(msg3, utils.GetUnixEpoch(), pubsubTopic1))
|
||||||
|
@ -150,7 +151,7 @@ func TestStoreQueryForwardPagination(t *testing.T) {
|
||||||
topic1 := "1"
|
topic1 := "1"
|
||||||
pubsubTopic1 := "topic1"
|
pubsubTopic1 := "topic1"
|
||||||
|
|
||||||
s := NewWakuStore(nil, nil, MemoryDB(t), utils.Logger())
|
s := NewWakuStore(nil, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
msg := tests.CreateWakuMessage(topic1, utils.GetUnixEpoch())
|
msg := tests.CreateWakuMessage(topic1, utils.GetUnixEpoch())
|
||||||
msg.Payload = []byte{byte(i)}
|
msg.Payload = []byte{byte(i)}
|
||||||
|
@ -174,7 +175,7 @@ func TestStoreQueryBackwardPagination(t *testing.T) {
|
||||||
topic1 := "1"
|
topic1 := "1"
|
||||||
pubsubTopic1 := "topic1"
|
pubsubTopic1 := "topic1"
|
||||||
|
|
||||||
s := NewWakuStore(nil, nil, MemoryDB(t), utils.Logger())
|
s := NewWakuStore(nil, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
msg := &pb.WakuMessage{
|
msg := &pb.WakuMessage{
|
||||||
Payload: []byte{byte(i)},
|
Payload: []byte{byte(i)},
|
||||||
|
@ -200,7 +201,7 @@ func TestStoreQueryBackwardPagination(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTemporalHistoryQueries(t *testing.T) {
|
func TestTemporalHistoryQueries(t *testing.T) {
|
||||||
s := NewWakuStore(nil, nil, MemoryDB(t), utils.Logger())
|
s := NewWakuStore(nil, nil, MemoryDB(t), timesource.NewDefaultClock(), utils.Logger())
|
||||||
|
|
||||||
var messages []*pb.WakuMessage
|
var messages []*pb.WakuMessage
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/waku-org/go-waku/tests"
|
"github.com/waku-org/go-waku/tests"
|
||||||
"github.com/waku-org/go-waku/waku/v2/node"
|
"github.com/waku-org/go-waku/waku/v2/node"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
"github.com/waku-org/go-waku/waku/v2/utils"
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ func TestV1Peers(t *testing.T) {
|
||||||
|
|
||||||
host, err := tests.MakeHost(context.Background(), port, rand.Reader)
|
host, err := tests.MakeHost(context.Background(), port, rand.Reader)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
relay, err := relay.NewWakuRelay(context.Background(), host, nil, 0, utils.Logger())
|
relay, err := relay.NewWakuRelay(context.Background(), host, nil, 0, timesource.NewDefaultClock(), utils.Logger())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer relay.Stop()
|
defer relay.Stop()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,203 @@
|
||||||
|
package timesource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/beevik/ntp"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultMaxAllowedFailures defines how many failures will be tolerated.
|
||||||
|
DefaultMaxAllowedFailures = 1
|
||||||
|
|
||||||
|
// FastNTPSyncPeriod period between ntp synchronizations before the first
|
||||||
|
// successful connection.
|
||||||
|
FastNTPSyncPeriod = 2 * time.Minute
|
||||||
|
|
||||||
|
// SlowNTPSyncPeriod period between ntp synchronizations after the first
|
||||||
|
// successful connection.
|
||||||
|
SlowNTPSyncPeriod = 1 * time.Hour
|
||||||
|
|
||||||
|
// DefaultRPCTimeout defines write deadline for single ntp server request.
|
||||||
|
DefaultRPCTimeout = 2 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultServers will be resolved to the closest available,
|
||||||
|
// and with high probability resolved to the different IPs
|
||||||
|
var DefaultServers = []string{
|
||||||
|
"0.pool.ntp.org",
|
||||||
|
"1.pool.ntp.org",
|
||||||
|
"2.pool.ntp.org",
|
||||||
|
"3.pool.ntp.org",
|
||||||
|
}
|
||||||
|
var errUpdateOffset = errors.New("failed to compute offset")
|
||||||
|
|
||||||
|
type ntpQuery func(string, ntp.QueryOptions) (*ntp.Response, error)
|
||||||
|
|
||||||
|
type queryResponse struct {
|
||||||
|
Offset time.Duration
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
type multiRPCError []error
|
||||||
|
|
||||||
|
func (e multiRPCError) Error() string {
|
||||||
|
var b bytes.Buffer
|
||||||
|
b.WriteString("RPC failed: ")
|
||||||
|
more := false
|
||||||
|
for _, err := range e {
|
||||||
|
if more {
|
||||||
|
b.WriteString("; ")
|
||||||
|
}
|
||||||
|
b.WriteString(err.Error())
|
||||||
|
more = true
|
||||||
|
}
|
||||||
|
b.WriteString(".")
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeOffset(timeQuery ntpQuery, servers []string, allowedFailures int) (time.Duration, error) {
|
||||||
|
if len(servers) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
responses := make(chan queryResponse, len(servers))
|
||||||
|
for _, server := range servers {
|
||||||
|
go func(server string) {
|
||||||
|
response, err := timeQuery(server, ntp.QueryOptions{
|
||||||
|
Timeout: DefaultRPCTimeout,
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
err = response.Validate()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
responses <- queryResponse{Error: err}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
responses <- queryResponse{Offset: response.ClockOffset}
|
||||||
|
}(server)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
rpcErrors multiRPCError
|
||||||
|
offsets []time.Duration
|
||||||
|
collected int
|
||||||
|
)
|
||||||
|
for response := range responses {
|
||||||
|
if response.Error != nil {
|
||||||
|
rpcErrors = append(rpcErrors, response.Error)
|
||||||
|
} else {
|
||||||
|
offsets = append(offsets, response.Offset)
|
||||||
|
}
|
||||||
|
collected++
|
||||||
|
if collected == len(servers) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if lth := len(rpcErrors); lth > allowedFailures {
|
||||||
|
return 0, rpcErrors
|
||||||
|
} else if lth == len(servers) {
|
||||||
|
return 0, rpcErrors
|
||||||
|
}
|
||||||
|
sort.SliceStable(offsets, func(i, j int) bool {
|
||||||
|
return offsets[i] > offsets[j]
|
||||||
|
})
|
||||||
|
mid := len(offsets) / 2
|
||||||
|
if len(offsets)%2 == 0 {
|
||||||
|
return (offsets[mid-1] + offsets[mid]) / 2, nil
|
||||||
|
}
|
||||||
|
return offsets[mid], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNTPTimesource(ntpServers []string, log *zap.Logger) *NTPTimeSource {
|
||||||
|
return &NTPTimeSource{
|
||||||
|
servers: ntpServers,
|
||||||
|
allowedFailures: DefaultMaxAllowedFailures,
|
||||||
|
fastNTPSyncPeriod: FastNTPSyncPeriod,
|
||||||
|
slowNTPSyncPeriod: SlowNTPSyncPeriod,
|
||||||
|
timeQuery: ntp.QueryWithOptions,
|
||||||
|
log: log.Named("timesource"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NTPTimeSource provides source of time that tries to be resistant to time skews.
|
||||||
|
// It does so by periodically querying time offset from ntp servers.
|
||||||
|
type NTPTimeSource struct {
|
||||||
|
servers []string
|
||||||
|
allowedFailures int
|
||||||
|
fastNTPSyncPeriod time.Duration
|
||||||
|
slowNTPSyncPeriod time.Duration
|
||||||
|
timeQuery ntpQuery // for ease of testing
|
||||||
|
log *zap.Logger
|
||||||
|
|
||||||
|
quit chan struct{}
|
||||||
|
wg sync.WaitGroup
|
||||||
|
|
||||||
|
mu sync.RWMutex
|
||||||
|
latestOffset time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now returns time adjusted by latest known offset
|
||||||
|
func (s *NTPTimeSource) Now() time.Time {
|
||||||
|
s.mu.RLock()
|
||||||
|
defer s.mu.RUnlock()
|
||||||
|
return time.Now().Add(s.latestOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NTPTimeSource) updateOffset() error {
|
||||||
|
offset, err := computeOffset(s.timeQuery, s.servers, s.allowedFailures)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Error("failed to compute offset", zap.Error(err))
|
||||||
|
return errUpdateOffset
|
||||||
|
}
|
||||||
|
s.log.Info("Difference with ntp servers", zap.Duration("offset", offset))
|
||||||
|
s.mu.Lock()
|
||||||
|
s.latestOffset = offset
|
||||||
|
s.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// runPeriodically runs periodically the given function based on NTPTimeSource
|
||||||
|
// synchronization limits (fastNTPSyncPeriod / slowNTPSyncPeriod)
|
||||||
|
func (s *NTPTimeSource) runPeriodically(fn func() error) error {
|
||||||
|
var period time.Duration
|
||||||
|
s.quit = make(chan struct{})
|
||||||
|
// we try to do it synchronously so that user can have reliable messages right away
|
||||||
|
s.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-time.After(period):
|
||||||
|
if err := fn(); err == nil {
|
||||||
|
period = s.slowNTPSyncPeriod
|
||||||
|
} else if period != s.slowNTPSyncPeriod {
|
||||||
|
period = s.fastNTPSyncPeriod
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-s.quit:
|
||||||
|
s.wg.Done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start runs a goroutine that updates local offset every updatePeriod.
|
||||||
|
func (s *NTPTimeSource) Start() error {
|
||||||
|
return s.runPeriodically(s.updateOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop goroutine that updates time source.
|
||||||
|
func (s *NTPTimeSource) Stop() error {
|
||||||
|
if s.quit == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
close(s.quit)
|
||||||
|
s.wg.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,249 @@
|
||||||
|
package timesource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/beevik/ntp"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// clockCompareDelta declares time required between multiple calls to time.Now
|
||||||
|
clockCompareDelta = 100 * time.Microsecond
|
||||||
|
)
|
||||||
|
|
||||||
|
// we don't user real servers for tests, but logic depends on
|
||||||
|
// actual number of involved NTP servers.
|
||||||
|
var mockedServers = []string{"ntp1", "ntp2", "ntp3", "ntp4"}
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
description string
|
||||||
|
servers []string
|
||||||
|
allowedFailures int
|
||||||
|
responses []queryResponse
|
||||||
|
expected time.Duration
|
||||||
|
expectError bool
|
||||||
|
|
||||||
|
// actual attempts are mutable
|
||||||
|
mu sync.Mutex
|
||||||
|
actualAttempts int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *testCase) query(string, ntp.QueryOptions) (*ntp.Response, error) {
|
||||||
|
tc.mu.Lock()
|
||||||
|
defer func() {
|
||||||
|
tc.actualAttempts++
|
||||||
|
tc.mu.Unlock()
|
||||||
|
}()
|
||||||
|
response := &ntp.Response{
|
||||||
|
ClockOffset: tc.responses[tc.actualAttempts].Offset,
|
||||||
|
Stratum: 1,
|
||||||
|
}
|
||||||
|
return response, tc.responses[tc.actualAttempts].Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestCases() []*testCase {
|
||||||
|
return []*testCase{
|
||||||
|
{
|
||||||
|
description: "SameResponse",
|
||||||
|
servers: mockedServers,
|
||||||
|
responses: []queryResponse{
|
||||||
|
{Offset: 10 * time.Second},
|
||||||
|
{Offset: 10 * time.Second},
|
||||||
|
{Offset: 10 * time.Second},
|
||||||
|
{Offset: 10 * time.Second},
|
||||||
|
},
|
||||||
|
expected: 10 * time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Median",
|
||||||
|
servers: mockedServers,
|
||||||
|
responses: []queryResponse{
|
||||||
|
{Offset: 10 * time.Second},
|
||||||
|
{Offset: 20 * time.Second},
|
||||||
|
{Offset: 20 * time.Second},
|
||||||
|
{Offset: 30 * time.Second},
|
||||||
|
},
|
||||||
|
expected: 20 * time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "EvenMedian",
|
||||||
|
servers: mockedServers[:2],
|
||||||
|
responses: []queryResponse{
|
||||||
|
{Offset: 10 * time.Second},
|
||||||
|
{Offset: 20 * time.Second},
|
||||||
|
},
|
||||||
|
expected: 15 * time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Error",
|
||||||
|
servers: mockedServers,
|
||||||
|
responses: []queryResponse{
|
||||||
|
{Offset: 10 * time.Second},
|
||||||
|
{Error: errors.New("test")},
|
||||||
|
{Offset: 30 * time.Second},
|
||||||
|
{Offset: 30 * time.Second},
|
||||||
|
},
|
||||||
|
expected: time.Duration(0),
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "MultiError",
|
||||||
|
servers: mockedServers,
|
||||||
|
responses: []queryResponse{
|
||||||
|
{Error: errors.New("test 1")},
|
||||||
|
{Error: errors.New("test 2")},
|
||||||
|
{Error: errors.New("test 3")},
|
||||||
|
{Error: errors.New("test 3")},
|
||||||
|
},
|
||||||
|
expected: time.Duration(0),
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "TolerableError",
|
||||||
|
servers: mockedServers,
|
||||||
|
allowedFailures: 1,
|
||||||
|
responses: []queryResponse{
|
||||||
|
{Offset: 10 * time.Second},
|
||||||
|
{Error: errors.New("test")},
|
||||||
|
{Offset: 20 * time.Second},
|
||||||
|
{Offset: 30 * time.Second},
|
||||||
|
},
|
||||||
|
expected: 20 * time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "NonTolerableError",
|
||||||
|
servers: mockedServers,
|
||||||
|
allowedFailures: 1,
|
||||||
|
responses: []queryResponse{
|
||||||
|
{Offset: 10 * time.Second},
|
||||||
|
{Error: errors.New("test")},
|
||||||
|
{Error: errors.New("test")},
|
||||||
|
{Error: errors.New("test")},
|
||||||
|
},
|
||||||
|
expected: time.Duration(0),
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "AllFailed",
|
||||||
|
servers: mockedServers,
|
||||||
|
allowedFailures: 4,
|
||||||
|
responses: []queryResponse{
|
||||||
|
{Error: errors.New("test")},
|
||||||
|
{Error: errors.New("test")},
|
||||||
|
{Error: errors.New("test")},
|
||||||
|
{Error: errors.New("test")},
|
||||||
|
},
|
||||||
|
expected: time.Duration(0),
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "HalfTolerable",
|
||||||
|
servers: mockedServers,
|
||||||
|
allowedFailures: 2,
|
||||||
|
responses: []queryResponse{
|
||||||
|
{Offset: 10 * time.Second},
|
||||||
|
{Offset: 20 * time.Second},
|
||||||
|
{Error: errors.New("test")},
|
||||||
|
{Error: errors.New("test")},
|
||||||
|
},
|
||||||
|
expected: 15 * time.Second,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComputeOffset(t *testing.T) {
|
||||||
|
for _, tc := range newTestCases() {
|
||||||
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
|
offset, err := computeOffset(tc.query, tc.servers, tc.allowedFailures)
|
||||||
|
if tc.expectError {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, tc.expected, offset)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNTPTimeSource(t *testing.T) {
|
||||||
|
for _, tc := range newTestCases() {
|
||||||
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
|
source := &NTPTimeSource{
|
||||||
|
servers: tc.servers,
|
||||||
|
allowedFailures: tc.allowedFailures,
|
||||||
|
timeQuery: tc.query,
|
||||||
|
log: utils.Logger(),
|
||||||
|
}
|
||||||
|
assert.WithinDuration(t, time.Now(), source.Now(), clockCompareDelta)
|
||||||
|
err := source.updateOffset()
|
||||||
|
if tc.expectError {
|
||||||
|
assert.Equal(t, errUpdateOffset, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
assert.WithinDuration(t, time.Now().Add(tc.expected), source.Now(), clockCompareDelta)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunningPeriodically(t *testing.T) {
|
||||||
|
var hits int
|
||||||
|
var mu sync.RWMutex
|
||||||
|
periods := make([]time.Duration, 0)
|
||||||
|
|
||||||
|
tc := newTestCases()[0]
|
||||||
|
fastHits := 3
|
||||||
|
slowHits := 1
|
||||||
|
|
||||||
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
|
source := &NTPTimeSource{
|
||||||
|
servers: tc.servers,
|
||||||
|
allowedFailures: tc.allowedFailures,
|
||||||
|
timeQuery: tc.query,
|
||||||
|
fastNTPSyncPeriod: time.Duration(fastHits*10) * time.Millisecond,
|
||||||
|
slowNTPSyncPeriod: time.Duration(slowHits*10) * time.Millisecond,
|
||||||
|
log: utils.Logger(),
|
||||||
|
}
|
||||||
|
lastCall := time.Now()
|
||||||
|
// we're simulating a calls to updateOffset, testing ntp calls happens
|
||||||
|
// on NTPTimeSource specified periods (fastNTPSyncPeriod & slowNTPSyncPeriod)
|
||||||
|
err := source.runPeriodically(func() error {
|
||||||
|
mu.Lock()
|
||||||
|
periods = append(periods, time.Since(lastCall))
|
||||||
|
mu.Unlock()
|
||||||
|
hits++
|
||||||
|
if hits < 3 {
|
||||||
|
return errUpdateOffset
|
||||||
|
}
|
||||||
|
if hits == 6 {
|
||||||
|
source.wg.Done()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
source.wg.Wait()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
require.Len(t, periods, 6)
|
||||||
|
defer mu.Unlock()
|
||||||
|
prev := 0
|
||||||
|
for _, period := range periods[1:3] {
|
||||||
|
p := int(period.Seconds() * 100)
|
||||||
|
require.True(t, fastHits <= (p-prev))
|
||||||
|
prev = p
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, period := range periods[3:] {
|
||||||
|
p := int(period.Seconds() * 100)
|
||||||
|
require.True(t, slowHits <= (p-prev))
|
||||||
|
prev = p
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package timesource
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Timesource interface {
|
||||||
|
Now() time.Time
|
||||||
|
Start() error
|
||||||
|
Stop() error
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package timesource
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type WallClockTimeSource struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultClock() *WallClockTimeSource {
|
||||||
|
return &WallClockTimeSource{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *WallClockTimeSource) Now() time.Time {
|
||||||
|
return time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *WallClockTimeSource) Start() error {
|
||||||
|
// Do nothing
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *WallClockTimeSource) Stop() error {
|
||||||
|
// Do nothing
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,14 +1,25 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// GetUnixEpochFrom converts a time into a unix timestamp with nanoseconds
|
// GetUnixEpochFrom converts a time into a unix timestamp with nanoseconds
|
||||||
func GetUnixEpochFrom(now time.Time) int64 {
|
func GetUnixEpochFrom(now time.Time) int64 {
|
||||||
return now.UnixNano()
|
return now.UnixNano()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUnixEpoch returns the current time in unix timestamp with the integer part
|
type Timesource interface {
|
||||||
// representing seconds and the decimal part representing subseconds
|
Now() time.Time
|
||||||
func GetUnixEpoch() int64 {
|
}
|
||||||
return GetUnixEpochFrom(time.Now())
|
|
||||||
|
// GetUnixEpoch returns the current time in unix timestamp with the integer part
|
||||||
|
// representing seconds and the decimal part representing subseconds.
|
||||||
|
// Optionally receives a timesource to obtain the time from
|
||||||
|
func GetUnixEpoch(timesource ...Timesource) int64 {
|
||||||
|
if len(timesource) != 0 {
|
||||||
|
return GetUnixEpochFrom(timesource[0].Now())
|
||||||
|
} else {
|
||||||
|
return GetUnixEpochFrom(time.Now())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue