Skip to content
Snippets Groups Projects
Unverified Commit 1aaf9c57 authored by Jonas Theis's avatar Jonas Theis Committed by GitHub
Browse files

Clock synchronization according to proposal (#748)

* Add clock synchronization according to proposal

* go mod tidy

* Disable clock plugin in Docker network by default
parent 1b455af6
Branches
No related tags found
No related merge requests found
......@@ -4,6 +4,7 @@ go 1.14
require (
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
github.com/beevik/ntp v0.3.0
github.com/dgraph-io/badger/v2 v2.0.3
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/drand/drand v1.1.1
......@@ -33,6 +34,7 @@ require (
go.uber.org/zap v1.15.0
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
golang.org/x/tools v0.0.0-20200330040139-fa3cc9eebcfe // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
google.golang.org/grpc v1.30.0
google.golang.org/protobuf v1.25.0
gopkg.in/src-d/go-git.v4 v4.13.1
......
package clock
import (
"errors"
"time"
"github.com/beevik/ntp"
"golang.org/x/xerrors"
)
// ErrNTPQueryFailed is returned if an NTP query failed.
var ErrNTPQueryFailed = errors.New("NTP query failed")
// difference between network time and node's local time.
var offset time.Duration
// FetchTimeOffset establishes the difference in local vs network time.
// This difference is stored in offset so that it can be used to adjust the local clock.
func FetchTimeOffset(host string) error {
resp, err := ntp.Query(host)
if err != nil {
return xerrors.Errorf("NTP query error (%v): %w", err, ErrNTPQueryFailed)
}
offset = resp.ClockOffset
return nil
}
// SyncedTime gets the synchronized time (according to the network) of a node.
func SyncedTime() time.Time {
return time.Now().Add(offset)
}
......@@ -6,6 +6,7 @@ import (
"github.com/iotaledger/goshimmer/plugins/autopeering"
"github.com/iotaledger/goshimmer/plugins/banner"
"github.com/iotaledger/goshimmer/plugins/cli"
"github.com/iotaledger/goshimmer/plugins/clock"
"github.com/iotaledger/goshimmer/plugins/config"
"github.com/iotaledger/goshimmer/plugins/database"
"github.com/iotaledger/goshimmer/plugins/drng"
......@@ -45,4 +46,5 @@ var PLUGINS = node.Plugins(
valuetransfers.App(),
syncbeacon.Plugin(),
syncbeaconfollower.Plugin(),
clock.Plugin(),
)
package clock
import (
"errors"
"math/rand"
"sync"
"time"
"github.com/iotaledger/goshimmer/packages/clock"
"github.com/iotaledger/goshimmer/packages/shutdown"
"github.com/iotaledger/goshimmer/plugins/config"
"github.com/iotaledger/goshimmer/plugins/gracefulshutdown"
"github.com/iotaledger/hive.go/daemon"
"github.com/iotaledger/hive.go/logger"
"github.com/iotaledger/hive.go/node"
flag "github.com/spf13/pflag"
)
const (
// CfgNTPPools defines the config flag of the NTP pools.
CfgNTPPools = "clock.ntpPools"
// PluginName is the name of the clock plugin.
PluginName = "Clock"
maxTries = 3
)
var (
plugin *node.Plugin
pluginOnce sync.Once
log *logger.Logger
ntpPools []string
// ErrSynchronizeClock is used when the local clock could not be synchronized.
ErrSynchronizeClock = errors.New("could not synchronize clock")
)
// Plugin gets the clock plugin instance.
func Plugin() *node.Plugin {
pluginOnce.Do(func() {
plugin = node.NewPlugin(PluginName, node.Enabled, configure, run)
})
return plugin
}
func init() {
flag.StringSlice(CfgNTPPools, []string{"0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org"}, "list of NTP pools to synchronize time from")
}
func configure(plugin *node.Plugin) {
log = logger.NewLogger(PluginName)
ntpPools = config.Node().GetStringSlice(CfgNTPPools)
if len(ntpPools) == 0 {
log.Fatalf("%s needs to provide at least 1 NTP pool to synchronize the local clock.", CfgNTPPools)
}
}
func run(plugin *node.Plugin) {
if err := daemon.BackgroundWorker(PluginName, func(shutdownSignal <-chan struct{}) {
// sync clock on startup
queryNTPPool()
// sync clock every hour to counter drift
ticker := time.NewTicker(1 * time.Hour)
defer ticker.Stop()
for {
select {
case <-shutdownSignal:
return
case <-ticker.C:
queryNTPPool()
}
}
}, shutdown.PrioritySynchronization); err != nil {
log.Panicf("Failed to start as daemon: %s", err)
}
}
// queryNTPPool queries configured ntpPools for maxTries.
// If clock could not be successfully synchronized shuts down node gracefully.
func queryNTPPool() {
log.Info("Synchronizing clock...")
ok := false
for t := maxTries; t > 0; t-- {
index := rand.Int() % len(ntpPools)
err := clock.FetchTimeOffset(ntpPools[index])
if err == nil {
ok = true
break
}
log.Warn("error while trying to sync clock")
}
if ok {
log.Info("Synchronizing clock... done")
} else {
gracefulshutdown.ShutdownWithError(ErrSynchronizeClock)
}
}
......@@ -22,7 +22,7 @@ services:
--prometheus.bindAddress=0.0.0.0:9312
--prometheus.processMetrics=false
--node.enablePlugins=analysis-server,analysis-dashboard,prometheus
--node.disablePlugins=portcheck,dashboard,analysis-client,gossip,drng,issuer,syncbeaconfollower,messagelayer,pow,valuetransfers,webapi,webapibroadcastdataendpoint,webapifindtransactionhashesendpoint,webapigetneighborsendpoint,webapigettransactionobjectsbyhashendpoint,webapigettransactiontrytesbyhashendpoint
--node.disablePlugins=portcheck,clock,dashboard,analysis-client,gossip,drng,issuer,syncbeaconfollower,messagelayer,pow,valuetransfers,webapi,webapibroadcastdataendpoint,webapifindtransactionhashesendpoint,webapigetneighborsendpoint,webapigettransactionobjectsbyhashendpoint,webapigettransactiontrytesbyhashendpoint
volumes:
- ./config.docker.json:/tmp/config.json:ro
- goshimmer-cache:/go
......@@ -42,7 +42,7 @@ services:
--database.directory=/tmp/mainnetdb
--autopeering.seed=base58:8q491c3YWjbPwLmF2WD95YmCgh61j2kenCKHfGfByoWi
--node.enablePlugins=prometheus,spammer,faucet,syncbeacon
--node.disablePlugins=syncbeaconfollower
--node.disablePlugins=syncbeaconfollower,clock
--faucet.seed=7R1itJx5hVuo9w9hjg5cwKFmek4HMSoBDgJZN8hKGxih
--valueLayer.snapshot.file=/tmp/assets/7R1itJx5hVuo9w9hjg5cwKFmek4HMSoBDgJZN8hKGxih.bin
--syncbeacon.broadcastInterval=5
......@@ -67,7 +67,7 @@ services:
--database.directory=/tmp/mainnetdb
--node.enablePlugins=bootstrap
--valueLayer.snapshot.file=/tmp/assets/7R1itJx5hVuo9w9hjg5cwKFmek4HMSoBDgJZN8hKGxih.bin
--node.disablePlugins=portcheck
--node.disablePlugins=portcheck,clock
--syncbeaconfollower.followNodes=EYsaGXnUVA9aTYL9FwYEvoQ8d1HCJveQVL7vogu6pqCP
volumes:
- ./config.docker.json:/tmp/config.json:ro
......
......@@ -52,6 +52,7 @@ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN
github.com/aws/aws-sdk-go v1.32.11/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
github.com/beevik/ntp v0.3.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
github.com/benbjohnson/clock v1.0.1/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment