From 930bbe18edf4d0904d151e8991a398fce91642ba Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Mon, 22 Jan 2024 17:36:52 +0100 Subject: [PATCH 01/91] feat: add gnosiskeyper command --- rolling-shutter/cmd/command.go | 2 + .../cmd/gnosiskeyper/gnosiskeyper.go | 53 ++++++++++++++ rolling-shutter/docs/rolling-shutter.md | 1 + .../docs/rolling-shutter_gnosiskeyper.md | 35 +++++++++ ...olling-shutter_gnosiskeyper_dump-config.md | 28 ++++++++ ...ng-shutter_gnosiskeyper_generate-config.md | 28 ++++++++ .../rolling-shutter_gnosiskeyper_initdb.md | 27 +++++++ rolling-shutter/keyperimpl/gnosis/config.go | 72 +++++++++++++++++++ .../keyperimpl/gnosis/database/definition.go | 11 +++ rolling-shutter/keyperimpl/gnosis/keyper.go | 56 +++++++++++++++ 10 files changed, 313 insertions(+) create mode 100644 rolling-shutter/cmd/gnosiskeyper/gnosiskeyper.go create mode 100644 rolling-shutter/docs/rolling-shutter_gnosiskeyper.md create mode 100644 rolling-shutter/docs/rolling-shutter_gnosiskeyper_dump-config.md create mode 100644 rolling-shutter/docs/rolling-shutter_gnosiskeyper_generate-config.md create mode 100644 rolling-shutter/docs/rolling-shutter_gnosiskeyper_initdb.md create mode 100644 rolling-shutter/keyperimpl/gnosis/config.go create mode 100644 rolling-shutter/keyperimpl/gnosis/database/definition.go create mode 100644 rolling-shutter/keyperimpl/gnosis/keyper.go diff --git a/rolling-shutter/cmd/command.go b/rolling-shutter/cmd/command.go index da75b75ba..b4d145fb2 100644 --- a/rolling-shutter/cmd/command.go +++ b/rolling-shutter/cmd/command.go @@ -7,6 +7,7 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/chain" "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/collator" "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/cryptocmd" + "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/gnosiskeyper" "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/mocknode" "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/mocksequencer" "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/p2pnode" @@ -26,6 +27,7 @@ func Subcommands() []*cobra.Command { mocknode.Cmd(), snapshot.Cmd(), snapshotkeyper.Cmd(), + gnosiskeyper.Cmd(), cryptocmd.Cmd(), proxy.Cmd(), mocksequencer.Cmd(), diff --git a/rolling-shutter/cmd/gnosiskeyper/gnosiskeyper.go b/rolling-shutter/cmd/gnosiskeyper/gnosiskeyper.go new file mode 100644 index 000000000..34bf5254c --- /dev/null +++ b/rolling-shutter/cmd/gnosiskeyper/gnosiskeyper.go @@ -0,0 +1,53 @@ +package gnosiskeyper + +import ( + "context" + + "github.com/jackc/pgx/v4/pgxpool" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/shversion" + keyper "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration/command" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" +) + +func Cmd() *cobra.Command { + builder := command.Build( + main, + command.Usage( + "Run a Shutter keyper for Gnosis Chain", + `This command runs a keyper node. It will connect to both a Gnosis and a +Shuttermint node which have to be started separately in advance.`, + ), + command.WithGenerateConfigSubcommand(), + command.WithDumpConfigSubcommand(), + ) + builder.AddInitDBCommand(initDB) + return builder.Command() +} + +func main(config *keyper.Config) error { + log.Info(). + Str("version", shversion.Version()). + Str("address", config.GetAddress().Hex()). + Str("shuttermint", config.Shuttermint.ShuttermintURL). + Msg("starting gnosiskeyper") + + kpr := keyper.New(config) + return service.RunWithSighandler(context.Background(), kpr) +} + +func initDB(cfg *keyper.Config) error { + ctx := context.Background() + dbpool, err := pgxpool.Connect(ctx, cfg.DatabaseURL) + if err != nil { + return errors.Wrap(err, "failed to connect to database") + } + defer dbpool.Close() + return db.InitDB(ctx, dbpool, database.Definition.Name(), database.Definition) +} diff --git a/rolling-shutter/docs/rolling-shutter.md b/rolling-shutter/docs/rolling-shutter.md index 91022711d..ea83e3392 100644 --- a/rolling-shutter/docs/rolling-shutter.md +++ b/rolling-shutter/docs/rolling-shutter.md @@ -17,6 +17,7 @@ A collection of commands to run and interact with Rolling Shutter nodes * [rolling-shutter chain](rolling-shutter_chain.md) - Run a node for Shutter's Tendermint chain * [rolling-shutter collator](rolling-shutter_collator.md) - Run a collator node * [rolling-shutter crypto](rolling-shutter_crypto.md) - CLI tool to access crypto functions +* [rolling-shutter gnosiskeyper](rolling-shutter_gnosiskeyper.md) - Run a Shutter keyper for Gnosis Chain * [rolling-shutter keyper](rolling-shutter_keyper.md) - Run a Shutter keyper node * [rolling-shutter mocknode](rolling-shutter_mocknode.md) - Run a Shutter mock node * [rolling-shutter mocksequencer](rolling-shutter_mocksequencer.md) - Run a Shutter mock sequencer diff --git a/rolling-shutter/docs/rolling-shutter_gnosiskeyper.md b/rolling-shutter/docs/rolling-shutter_gnosiskeyper.md new file mode 100644 index 000000000..5e176edd7 --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_gnosiskeyper.md @@ -0,0 +1,35 @@ +## rolling-shutter gnosiskeyper + +Run a Shutter keyper for Gnosis Chain + +### Synopsis + +This command runs a keyper node. It will connect to both a Gnosis and a +Shuttermint node which have to be started separately in advance. + +``` +rolling-shutter gnosiskeyper [flags] +``` + +### Options + +``` + --config string config file + -h, --help help for gnosiskeyper +``` + +### Options inherited from parent commands + +``` + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter](rolling-shutter.md) - A collection of commands to run and interact with Rolling Shutter nodes +* [rolling-shutter gnosiskeyper dump-config](rolling-shutter_gnosiskeyper_dump-config.md) - Dump a 'gnosiskeyper' configuration file, based on given config and env vars +* [rolling-shutter gnosiskeyper generate-config](rolling-shutter_gnosiskeyper_generate-config.md) - Generate a 'gnosiskeyper' configuration file +* [rolling-shutter gnosiskeyper initdb](rolling-shutter_gnosiskeyper_initdb.md) - Initialize the database of the 'gnosiskeyper' + diff --git a/rolling-shutter/docs/rolling-shutter_gnosiskeyper_dump-config.md b/rolling-shutter/docs/rolling-shutter_gnosiskeyper_dump-config.md new file mode 100644 index 000000000..7db677f4d --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_gnosiskeyper_dump-config.md @@ -0,0 +1,28 @@ +## rolling-shutter gnosiskeyper dump-config + +Dump a 'gnosiskeyper' configuration file, based on given config and env vars + +``` +rolling-shutter gnosiskeyper dump-config [flags] +``` + +### Options + +``` + --config string config file + -h, --help help for dump-config + --output string output file +``` + +### Options inherited from parent commands + +``` + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter gnosiskeyper](rolling-shutter_gnosiskeyper.md) - Run a Shutter keyper for Gnosis Chain + diff --git a/rolling-shutter/docs/rolling-shutter_gnosiskeyper_generate-config.md b/rolling-shutter/docs/rolling-shutter_gnosiskeyper_generate-config.md new file mode 100644 index 000000000..f35f9b490 --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_gnosiskeyper_generate-config.md @@ -0,0 +1,28 @@ +## rolling-shutter gnosiskeyper generate-config + +Generate a 'gnosiskeyper' configuration file + +``` +rolling-shutter gnosiskeyper generate-config [flags] +``` + +### Options + +``` + -h, --help help for generate-config + --output string output file +``` + +### Options inherited from parent commands + +``` + --config string config file + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter gnosiskeyper](rolling-shutter_gnosiskeyper.md) - Run a Shutter keyper for Gnosis Chain + diff --git a/rolling-shutter/docs/rolling-shutter_gnosiskeyper_initdb.md b/rolling-shutter/docs/rolling-shutter_gnosiskeyper_initdb.md new file mode 100644 index 000000000..e8bc1a5ab --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_gnosiskeyper_initdb.md @@ -0,0 +1,27 @@ +## rolling-shutter gnosiskeyper initdb + +Initialize the database of the 'gnosiskeyper' + +``` +rolling-shutter gnosiskeyper initdb [flags] +``` + +### Options + +``` + -h, --help help for initdb +``` + +### Options inherited from parent commands + +``` + --config string config file + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter gnosiskeyper](rolling-shutter_gnosiskeyper.md) - Run a Shutter keyper for Gnosis Chain + diff --git a/rolling-shutter/keyperimpl/gnosis/config.go b/rolling-shutter/keyperimpl/gnosis/config.go new file mode 100644 index 000000000..ad1549396 --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/config.go @@ -0,0 +1,72 @@ +package gnosis + +import ( + "io" + + "github.com/ethereum/go-ethereum/common" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/kprconfig" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/metricsserver" + "github.com/shutter-network/rolling-shutter/rolling-shutter/p2p" +) + +var _ configuration.Config = &Config{} + +func NewConfig() *Config { + c := &Config{} + c.Init() + return c +} + +func (c *Config) Init() { + c.P2P = p2p.NewConfig() + c.Gnosis = configuration.NewEthnodeConfig() + c.Shuttermint = kprconfig.NewShuttermintConfig() + c.Metrics = metricsserver.NewConfig() +} + +type Config struct { + InstanceID uint64 `shconfig:",required"` + DatabaseURL string `shconfig:",required" comment:"If it's empty, we use the standard PG_ environment variables"` + + HTTPEnabled bool + HTTPListenAddress string + + P2P *p2p.Config + Gnosis *configuration.EthnodeConfig + Shuttermint *kprconfig.ShuttermintConfig + Metrics *metricsserver.MetricsConfig +} + +func (c *Config) Validate() error { + return nil +} + +func (c *Config) Name() string { + return "gnosiskeyper" +} + +func (c *Config) SetDefaultValues() error { + c.HTTPEnabled = false + c.HTTPListenAddress = ":3000" + return nil +} + +func (c *Config) SetExampleValues() error { + err := c.SetDefaultValues() + if err != nil { + return err + } + c.InstanceID = 42 + c.DatabaseURL = "postgres://pguser:pgpassword@localhost:5432/shutter" + return nil +} + +func (c Config) TOMLWriteHeader(_ io.Writer) (int, error) { + return 0, nil +} + +func (c *Config) GetAddress() common.Address { + return c.Gnosis.PrivateKey.EthereumAddress() +} diff --git a/rolling-shutter/keyperimpl/gnosis/database/definition.go b/rolling-shutter/keyperimpl/gnosis/database/definition.go new file mode 100644 index 000000000..18bfcda0f --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/database/definition.go @@ -0,0 +1,11 @@ +package database + +import ( + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" +) + +var Definition = db.NewAggregateDefinition( + "gnosiskeyper", + database.Definition, +) diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go new file mode 100644 index 000000000..28b346176 --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -0,0 +1,56 @@ +package gnosis + +import ( + "context" + + "github.com/pkg/errors" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/epochkghandler" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/kprconfig" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/broker" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" +) + +type Keyper struct { + core *keyper.KeyperCore + config *Config +} + +func New(c *Config) *Keyper { + return &Keyper{ + config: c, + } +} + +func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { + decrTrigChan := make(chan *broker.Event[*epochkghandler.DecryptionTrigger]) + runner.Defer(func() { close(decrTrigChan) }) + + dbpool, err := db.Connect(ctx, runner, kpr.config.DatabaseURL, database.Definition.Name()) + if err != nil { + return errors.Wrap(err, "failed to connect to database") + } + + kpr.core, err = keyper.New( + &kprconfig.Config{ + InstanceID: kpr.config.InstanceID, + DatabaseURL: kpr.config.DatabaseURL, + HTTPEnabled: kpr.config.HTTPEnabled, + HTTPListenAddress: kpr.config.HTTPListenAddress, + P2P: kpr.config.P2P, + Ethereum: kpr.config.Gnosis, + Shuttermint: kpr.config.Shuttermint, + Metrics: kpr.config.Metrics, + }, + decrTrigChan, + keyper.WithDBPool(dbpool), + ) + if err != nil { + return errors.Wrap(err, "can't instantiate keyper core") + } + + return runner.StartService(kpr.core) +} From 9879b8d392b69cdf9037b44154fb67bcc7a22fa0 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Wed, 31 Jan 2024 10:42:52 +0100 Subject: [PATCH 02/91] Fix linting error --- rolling-shutter/keyper/kprapi/http.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rolling-shutter/keyper/kprapi/http.go b/rolling-shutter/keyper/kprapi/http.go index 1d54adfbd..df484804c 100644 --- a/rolling-shutter/keyper/kprapi/http.go +++ b/rolling-shutter/keyper/kprapi/http.go @@ -32,7 +32,7 @@ func (srv *Server) Ping(w http.ResponseWriter, _ *http.Request) { _, _ = w.Write([]byte("pong")) } -func (srv *Server) Shutdown(w http.ResponseWriter, _ *http.Request) { +func (srv *Server) Shutdown(_ http.ResponseWriter, _ *http.Request) { srv.shutdownSig <- struct{}{} // We still want to return here and thus return 200 to the caller after this. // Not immediately closing open connctions is taken care From f8f8b917135818a42f85705fb4c4ccfca5a04daf Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Fri, 1 Dec 2023 16:36:47 +0100 Subject: [PATCH 03/91] chore(op): add initial op-shutter keyper --- rolling-shutter/cmd/command.go | 2 + rolling-shutter/cmd/optimism/keyper.go | 56 +++++ rolling-shutter/cmd/optimism/keyper_test.go | 25 ++ rolling-shutter/docs/rolling-shutter.md | 1 + .../docs/rolling-shutter_optimismkeyper.md | 34 +++ ...-shutter_optimismkeyper_generate-config.md | 28 +++ .../rolling-shutter_optimismkeyper_initdb.md | 27 +++ rolling-shutter/go.mod | 46 ++-- rolling-shutter/go.sum | 225 ++++++------------ .../keyperimpl/optimism/config/config.go | 72 ++++++ .../keyperimpl/optimism/config/ethereum.go | 57 +++++ .../optimism/database/definition.go | 11 + rolling-shutter/keyperimpl/optimism/keyper.go | 119 +++++++++ rolling-shutter/keyperimpl/optimism/logger.go | 4 + .../keyperimpl/optimism/setup_test.go | 43 ++++ 15 files changed, 575 insertions(+), 175 deletions(-) create mode 100644 rolling-shutter/cmd/optimism/keyper.go create mode 100644 rolling-shutter/cmd/optimism/keyper_test.go create mode 100644 rolling-shutter/docs/rolling-shutter_optimismkeyper.md create mode 100644 rolling-shutter/docs/rolling-shutter_optimismkeyper_generate-config.md create mode 100644 rolling-shutter/docs/rolling-shutter_optimismkeyper_initdb.md create mode 100644 rolling-shutter/keyperimpl/optimism/config/config.go create mode 100644 rolling-shutter/keyperimpl/optimism/config/ethereum.go create mode 100644 rolling-shutter/keyperimpl/optimism/database/definition.go create mode 100644 rolling-shutter/keyperimpl/optimism/keyper.go create mode 100644 rolling-shutter/keyperimpl/optimism/logger.go create mode 100644 rolling-shutter/keyperimpl/optimism/setup_test.go diff --git a/rolling-shutter/cmd/command.go b/rolling-shutter/cmd/command.go index b4d145fb2..ce5c3be0b 100644 --- a/rolling-shutter/cmd/command.go +++ b/rolling-shutter/cmd/command.go @@ -10,6 +10,7 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/gnosiskeyper" "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/mocknode" "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/mocksequencer" + "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/optimism" "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/p2pnode" "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/proxy" "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/rollupkeyper" @@ -23,6 +24,7 @@ func Subcommands() []*cobra.Command { bootstrap.Cmd(), chain.Cmd(), collator.Cmd(), + optimism.Cmd(), rollupkeyper.Cmd(), mocknode.Cmd(), snapshot.Cmd(), diff --git a/rolling-shutter/cmd/optimism/keyper.go b/rolling-shutter/cmd/optimism/keyper.go new file mode 100644 index 000000000..f23d091be --- /dev/null +++ b/rolling-shutter/cmd/optimism/keyper.go @@ -0,0 +1,56 @@ +package optimism + +import ( + "context" + + "github.com/jackc/pgx/v4/pgxpool" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/shversion" + keyper "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/config" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration/command" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" +) + +func Cmd() *cobra.Command { + builder := command.Build( + main, + command.Usage( + "Run a Shutter optimism keyper node", + `This command runs a keyper node. It will connect to both an Optimism and a +Shuttermint node which have to be started separately in advance.`, + ), + command.WithGenerateConfigSubcommand(), + ) + builder.AddInitDBCommand(initDB) + return builder.Command() +} + +func main(cfg *config.Config) error { + log.Info(). + Str("version", shversion.Version()). + Str("address", cfg.GetAddress().Hex()). + Str("shuttermint", cfg.Shuttermint.ShuttermintURL). + Msg("starting keyper") + kpr, err := keyper.New(cfg) + if err != nil { + return err + } + return service.RunWithSighandler(context.Background(), kpr) +} + +func initDB(cfg *config.Config) error { + ctx := context.Background() + dbpool, err := pgxpool.Connect(ctx, cfg.DatabaseURL) + if err != nil { + return errors.Wrap(err, "failed to connect to database") + } + defer dbpool.Close() + + return db.InitDB(ctx, dbpool, database.Definition.Name(), database.Definition) +} diff --git a/rolling-shutter/cmd/optimism/keyper_test.go b/rolling-shutter/cmd/optimism/keyper_test.go new file mode 100644 index 000000000..e06cf7ff5 --- /dev/null +++ b/rolling-shutter/cmd/optimism/keyper_test.go @@ -0,0 +1,25 @@ +package optimism_test + +import ( + "testing" + + "gotest.tools/assert" + + keyper "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/rollup" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration/test" +) + +func TestSmokeGenerateConfig(t *testing.T) { + config := keyper.NewConfig() + test.SmokeGenerateConfig(t, config) +} + +func TestParsedConfig(t *testing.T) { + config := keyper.NewConfig() + + err := configuration.SetExampleValuesRecursive(config) + assert.NilError(t, err) + parsedConfig := test.RoundtripParseConfig(t, config) + assert.DeepEqual(t, config, parsedConfig) +} diff --git a/rolling-shutter/docs/rolling-shutter.md b/rolling-shutter/docs/rolling-shutter.md index ea83e3392..b6a7b834d 100644 --- a/rolling-shutter/docs/rolling-shutter.md +++ b/rolling-shutter/docs/rolling-shutter.md @@ -21,6 +21,7 @@ A collection of commands to run and interact with Rolling Shutter nodes * [rolling-shutter keyper](rolling-shutter_keyper.md) - Run a Shutter keyper node * [rolling-shutter mocknode](rolling-shutter_mocknode.md) - Run a Shutter mock node * [rolling-shutter mocksequencer](rolling-shutter_mocksequencer.md) - Run a Shutter mock sequencer +* [rolling-shutter optimismkeyper](rolling-shutter_optimismkeyper.md) - Run a Shutter optimism keyper node * [rolling-shutter p2pnode](rolling-shutter_p2pnode.md) - Run a Shutter p2p bootstrap node * [rolling-shutter proxy](rolling-shutter_proxy.md) - Run a Ethereum JSON RPC proxy * [rolling-shutter snapshot](rolling-shutter_snapshot.md) - Run the Snapshot Hub communication module diff --git a/rolling-shutter/docs/rolling-shutter_optimismkeyper.md b/rolling-shutter/docs/rolling-shutter_optimismkeyper.md new file mode 100644 index 000000000..bf0a546e0 --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_optimismkeyper.md @@ -0,0 +1,34 @@ +## rolling-shutter optimismkeyper + +Run a Shutter optimism keyper node + +### Synopsis + +This command runs a keyper node. It will connect to both an Optimism and a +Shuttermint node which have to be started separately in advance. + +``` +rolling-shutter optimismkeyper [flags] +``` + +### Options + +``` + --config string config file + -h, --help help for optimismkeyper +``` + +### Options inherited from parent commands + +``` + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter](rolling-shutter.md) - A collection of commands to run and interact with Rolling Shutter nodes +* [rolling-shutter optimismkeyper generate-config](rolling-shutter_optimismkeyper_generate-config.md) - Generate a 'optimismkeyper' configuration file +* [rolling-shutter optimismkeyper initdb](rolling-shutter_optimismkeyper_initdb.md) - Initialize the database of the 'optimismkeyper' + diff --git a/rolling-shutter/docs/rolling-shutter_optimismkeyper_generate-config.md b/rolling-shutter/docs/rolling-shutter_optimismkeyper_generate-config.md new file mode 100644 index 000000000..55d179a08 --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_optimismkeyper_generate-config.md @@ -0,0 +1,28 @@ +## rolling-shutter optimismkeyper generate-config + +Generate a 'optimismkeyper' configuration file + +``` +rolling-shutter optimismkeyper generate-config [flags] +``` + +### Options + +``` + -h, --help help for generate-config + --output string output file +``` + +### Options inherited from parent commands + +``` + --config string config file + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter optimismkeyper](rolling-shutter_optimismkeyper.md) - Run a Shutter optimism keyper node + diff --git a/rolling-shutter/docs/rolling-shutter_optimismkeyper_initdb.md b/rolling-shutter/docs/rolling-shutter_optimismkeyper_initdb.md new file mode 100644 index 000000000..cec7e7bc0 --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_optimismkeyper_initdb.md @@ -0,0 +1,27 @@ +## rolling-shutter optimismkeyper initdb + +Initialize the database of the 'optimismkeyper' + +``` +rolling-shutter optimismkeyper initdb [flags] +``` + +### Options + +``` + -h, --help help for initdb +``` + +### Options inherited from parent commands + +``` + --config string config file + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter optimismkeyper](rolling-shutter_optimismkeyper.md) - Run a Shutter optimism keyper node + diff --git a/rolling-shutter/go.mod b/rolling-shutter/go.mod index b8c5f1801..b23d639a4 100644 --- a/rolling-shutter/go.mod +++ b/rolling-shutter/go.mod @@ -1,19 +1,21 @@ module github.com/shutter-network/rolling-shutter/rolling-shutter -go 1.21 +go 1.21.4 -toolchain go1.21.4 +replace github.com/ethereum-optimism/optimism v1.1.6-rc.2 => ../../shoptimism/ + +replace github.com/ethereum/go-ethereum v1.13.1 => ../../shop-geth/ require ( github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 github.com/benbjohnson/clock v1.3.5 github.com/bitwurx/jrpc2 v0.0.0-20220302204700-52c6dbbeb536 github.com/deepmap/oapi-codegen v1.9.1 - github.com/ethereum/go-ethereum v1.13.4 + github.com/ethereum/go-ethereum v1.13.5 github.com/getkin/kin-openapi v0.87.0 github.com/go-chi/chi/v5 v5.0.10 github.com/google/go-cmp v0.6.0 - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.4.0 github.com/hashicorp/go-multierror v1.1.1 github.com/ipfs/go-log/v2 v2.5.1 github.com/jackc/pgconn v1.14.1 @@ -25,9 +27,9 @@ require ( github.com/libp2p/go-libp2p-pubsub v0.10.0 github.com/mitchellh/mapstructure v1.5.0 github.com/multiformats/go-multiaddr v0.12.0 - github.com/pelletier/go-toml/v2 v2.0.9 + github.com/pelletier/go-toml/v2 v2.1.0 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.16.0 + github.com/prometheus/client_golang v1.17.0 github.com/rs/zerolog v1.28.0 github.com/shutter-network/shutter/shlib v0.1.13 github.com/shutter-network/txtypes v0.1.0 @@ -43,8 +45,8 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.21.0 go.opentelemetry.io/otel/trace v1.21.0 go.opentelemetry.io/proto/otlp v1.0.0 - golang.org/x/crypto v0.14.0 - golang.org/x/sync v0.4.0 + golang.org/x/crypto v0.15.0 + golang.org/x/sync v0.5.0 google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v3 v3.0.1 gotest.tools v2.2.0+incompatible @@ -53,16 +55,19 @@ require ( require ( github.com/DataDog/zstd v1.5.2 // indirect - github.com/VictoriaMetrics/fastcache v1.6.0 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/VictoriaMetrics/fastcache v1.12.1 // indirect + github.com/allegro/bigcache v1.2.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/cockroachdb/errors v1.9.1 // indirect + github.com/cockroachdb/errors v1.11.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect - github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 // indirect - github.com/cockroachdb/redact v1.1.3 // indirect + github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cosmos/gogoproto v1.4.1 // indirect @@ -81,7 +86,7 @@ require ( github.com/fjl/memsize v0.0.1 // indirect github.com/flynn/noise v1.0.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect github.com/getsentry/sentry-go v0.18.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect @@ -98,9 +103,11 @@ require ( github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.0.1 // indirect + github.com/google/gofuzz v1.2.1-0.20220503160820-4a35382e8fc8 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/orderedcode v0.0.1 // indirect github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect @@ -177,13 +184,14 @@ require ( github.com/multiformats/go-varint v0.0.7 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/onsi/ginkgo/v2 v2.13.0 // indirect + github.com/onsi/gomega v1.30.0 // indirect github.com/opencontainers/runtime-spec v1.1.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/petermattis/goid v0.0.0-20230808133559-b036b712a89b // indirect github.com/polydawn/refmt v0.89.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.11.1 // indirect github.com/quic-go/qpack v0.4.0 // indirect @@ -192,9 +200,9 @@ require ( github.com/quic-go/webtransport-go v0.6.0 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/rivo/uniseg v0.4.3 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect - github.com/rs/cors v1.8.3 // indirect + github.com/rs/cors v1.9.0 // indirect github.com/rs/xid v1.4.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect @@ -202,10 +210,11 @@ require ( github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect - github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect github.com/tendermint/tm-db v0.6.7 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect + github.com/urfave/cli/v2 v2.25.7 // indirect github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect go.etcd.io/bbolt v1.3.7 // indirect @@ -220,7 +229,8 @@ require ( golang.org/x/mod v0.13.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.14.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.4.0 // indirect golang.org/x/tools v0.14.0 // indirect gonum.org/v1/gonum v0.13.0 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect diff --git a/rolling-shutter/go.sum b/rolling-shutter/go.sum index 239a3f656..aa89b933b 100644 --- a/rolling-shutter/go.sum +++ b/rolling-shutter/go.sum @@ -45,38 +45,32 @@ dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 h1:Dy3M9aegiI7d7PF1LUdjbVigJReo+QOceYsMyFh9qoE= github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesnDfrtF6RFUGzQfLo= -github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= -github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= -github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= -github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= -github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= +github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= +github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= -github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= +github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= @@ -100,7 +94,6 @@ github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -113,18 +106,18 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= -github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= -github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= -github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= -github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= +github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk= -github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM= -github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= -github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= @@ -172,7 +165,6 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etly github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/deepmap/oapi-codegen v1.9.1 h1:yHmEnA7jSTUMQgV+uN02WpZtwHnz2CBW3mZRIxr1vtI= github.com/deepmap/oapi-codegen v1.9.1/go.mod h1:PLqNAhdedP8ttRpBBkzLKU3bp+Fpy+tTgeAMlztR2cw= -github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= @@ -189,7 +181,6 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= @@ -198,9 +189,7 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/ethereum/go-ethereum v1.12.0 h1:bdnhLPtqETd4m3mS8BGMNvBTf36bO5bx/hxE2zljOa0= github.com/ethereum/go-ethereum v1.12.0/go.mod h1:/oo2X/dZLJjf2mJ6YT9wcWxa4nNJDBKDBU6sFIpx1Gs= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= @@ -209,8 +198,6 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojt github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= -github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fjl/memsize v0.0.1 h1:+zhkb+dhUgx0/e+M8sF0QqiouvMQUiKR+QYvdxIOKcQ= github.com/fjl/memsize v0.0.1/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= @@ -224,24 +211,20 @@ github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0X github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/getkin/kin-openapi v0.87.0 h1:eeb0WBIgRiXra7ZY0Vo+jWloqvaF2kNEaxAyb+39N+E= github.com/getkin/kin-openapi v0.87.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= -github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= @@ -264,7 +247,6 @@ github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -284,12 +266,10 @@ github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSG github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -300,18 +280,13 @@ github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= -github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= -github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -350,7 +325,6 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= -github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= @@ -372,8 +346,8 @@ github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+u github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64= -github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.1-0.20220503160820-4a35382e8fc8 h1:Ep/joEub9YwcjRY6ND3+Y/w0ncE540RtGatVhtZL0/Q= +github.com/google/gofuzz v1.2.1-0.20220503160820-4a35382e8fc8/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -391,12 +365,13 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0= github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -407,7 +382,6 @@ github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRid github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -423,7 +397,6 @@ github.com/hashicorp/go-bexpr v0.1.11 h1:6DqdA/KBjurGby9yTY0bmkathya0lfwF2SeuubC github.com/hashicorp/go-bexpr v0.1.11/go.mod h1:f03lAo0duBlDIUMGCuad8oLcgejw4m7U+N8T+6Kz1AE= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= @@ -439,10 +412,8 @@ github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZm github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= -github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -463,11 +434,6 @@ github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= github.com/ipld/go-ipld-prime v0.20.0 h1:Ud3VwE9ClxpO2LkCYP7vWPc0Fo+dYdYzgxUJZ3uRG4g= github.com/ipld/go-ipld-prime v0.20.0/go.mod h1:PzqZ/ZR981eKbgdr3y2DJYeD/8bgMawdGVlJDE8kK+M= -github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= -github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= -github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= -github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= -github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= @@ -539,21 +505,12 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= -github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= -github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= -github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= -github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -574,11 +531,9 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= github.com/labstack/echo/v4 v4.6.3/go.mod h1:Hk5OiHj0kDqmFq7aHe7eDqI7CUhuCrfpupQtLGGLm7A= github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M= github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= @@ -642,17 +597,13 @@ github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8 github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -661,13 +612,10 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= @@ -698,7 +646,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= @@ -726,27 +673,27 @@ github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dy github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= @@ -766,8 +713,8 @@ github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhM github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= -github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/petermattis/goid v0.0.0-20230808133559-b036b712a89b h1:vab8deKC4QoIfm9fJM59iuNz1ELGsuLoYYpiF+pHiG8= github.com/petermattis/goid v0.0.0-20230808133559-b036b712a89b/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= @@ -783,12 +730,12 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= @@ -807,17 +754,17 @@ github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtB github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= +github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo= -github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= +github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= @@ -830,11 +777,9 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= @@ -873,10 +818,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= @@ -917,14 +860,15 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= -github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= +github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= @@ -938,7 +882,6 @@ github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+F github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -947,35 +890,23 @@ github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxW github.com/ulope/jrpc2 v0.0.0-20230706135348-a95cf3d96bd2 h1:rk0z/6CEJbstiHqv8+4ZIMv4Sm2zBZ5v5C56P8JXd+I= github.com/ulope/jrpc2 v0.0.0-20230706135348-a95cf3d96bd2/go.mod h1:bzOCUO4YLqjPZbPM4jeZzu/WIEauP/ouNWLRysNQdc0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.10 h1:p8Fspmz3iTctJstry1PYS3HVdllxnEzTEsgIgtxTrCk= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bCzK15ozrqo2sZxkh0FHynJZOTVoV6Q= -github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= -github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= +github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= -github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= -github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= -github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1050,10 +981,8 @@ golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaE golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= @@ -1066,8 +995,8 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1094,7 +1023,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -1114,12 +1042,10 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1128,7 +1054,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1143,7 +1068,6 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -1153,8 +1077,10 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= @@ -1183,8 +1109,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1227,22 +1153,19 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1250,13 +1173,12 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1273,31 +1195,28 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY= +golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -1344,12 +1263,12 @@ golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4X golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= @@ -1360,6 +1279,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.13.0 h1:a0T3bh+7fhRyqeNbiC3qVHYmkiQgit3wnNan/2c0HMM= gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= @@ -1393,7 +1313,6 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -1434,10 +1353,8 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -1457,7 +1374,6 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI= google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -1482,14 +1398,10 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= @@ -1503,7 +1415,6 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/rolling-shutter/keyperimpl/optimism/config/config.go b/rolling-shutter/keyperimpl/optimism/config/config.go new file mode 100644 index 000000000..971004012 --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/config/config.go @@ -0,0 +1,72 @@ +package config + +import ( + "io" + + "github.com/ethereum/go-ethereum/common" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/kprconfig" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/metricsserver" + "github.com/shutter-network/rolling-shutter/rolling-shutter/p2p" +) + +var _ configuration.Config = &Config{} + +func NewConfig() *Config { + c := &Config{} + c.Init() + return c +} + +func (c *Config) Init() { + c.P2P = p2p.NewConfig() + c.Optimism = NewEthnodeConfig() + c.Shuttermint = kprconfig.NewShuttermintConfig() + c.Metrics = metricsserver.NewConfig() +} + +type Config struct { + InstanceID uint64 `shconfig:",required"` + DatabaseURL string `shconfig:",required" comment:"If it's empty, we use the standard PG_ environment variables"` + + HTTPEnabled bool + HTTPListenAddress string + + P2P *p2p.Config + Optimism *OptimismConfig + Shuttermint *kprconfig.ShuttermintConfig + Metrics *metricsserver.MetricsConfig +} + +func (c *Config) Validate() error { + return nil +} + +func (c *Config) Name() string { + return "keyper" +} + +func (c *Config) SetDefaultValues() error { + c.HTTPEnabled = false + c.HTTPListenAddress = ":3000" + return nil +} + +func (c *Config) SetExampleValues() error { + err := c.SetDefaultValues() + if err != nil { + return err + } + c.InstanceID = 42 + c.DatabaseURL = "postgres://pguser:pgpassword@localhost:5432/shutter" + return nil +} + +func (c Config) TOMLWriteHeader(_ io.Writer) (int, error) { + return 0, nil +} + +func (c *Config) GetAddress() common.Address { + return c.Optimism.PrivateKey.EthereumAddress() +} diff --git a/rolling-shutter/keyperimpl/optimism/config/ethereum.go b/rolling-shutter/keyperimpl/optimism/config/ethereum.go new file mode 100644 index 000000000..c72dbdd46 --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/config/ethereum.go @@ -0,0 +1,57 @@ +package config + +import ( + "crypto/rand" + "fmt" + "io" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/keys" +) + +var _ configuration.Config = &OptimismConfig{} + +func NewEthnodeConfig() *OptimismConfig { + c := &OptimismConfig{} + c.Init() + return c +} + +type OptimismConfig struct { + PrivateKey *keys.ECDSAPrivate `shconfig:",required"` + JSONRPCURL string ` comment:"The op-geth JSON RPC endpoint"` +} + +func (c *OptimismConfig) Init() { + c.PrivateKey = &keys.ECDSAPrivate{} +} + +func (c *OptimismConfig) Name() string { + return "ethnode" +} + +func (c *OptimismConfig) Validate() error { + return nil +} + +func (c *OptimismConfig) SetDefaultValues() error { + c.JSONRPCURL = "http://127.0.0.1:8545/" + return nil +} + +func (c *OptimismConfig) SetExampleValues() error { + err := c.SetDefaultValues() + if err != nil { + return err + } + + c.PrivateKey, err = keys.GenerateECDSAKey(rand.Reader) + if err != nil { + return err + } + return nil +} + +func (c OptimismConfig) TOMLWriteHeader(w io.Writer) (int, error) { + return fmt.Fprintf(w, "# Ethereum address: %s\n", c.PrivateKey.EthereumAddress()) +} diff --git a/rolling-shutter/keyperimpl/optimism/database/definition.go b/rolling-shutter/keyperimpl/optimism/database/definition.go new file mode 100644 index 000000000..befb8550b --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/database/definition.go @@ -0,0 +1,11 @@ +package database + +import ( + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" +) + +var Definition = db.NewAggregateDefinition( + "opkeyper", + database.Definition, +) diff --git a/rolling-shutter/keyperimpl/optimism/keyper.go b/rolling-shutter/keyperimpl/optimism/keyper.go new file mode 100644 index 000000000..6352c2578 --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/keyper.go @@ -0,0 +1,119 @@ +package optimism + +import ( + "context" + + shopclient "github.com/ethereum-optimism/optimism/shutter-node/client" + shopevent "github.com/ethereum-optimism/optimism/shutter-node/client/event" + "github.com/jackc/pgx/v4/pgxpool" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/epochkghandler" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/kprconfig" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/config" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/rollup/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/broker" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" +) + +type Keyper struct { + core *keyper.KeyperCore + dbpool *pgxpool.Pool + config *config.Config + + trigger chan<- *broker.Event[*epochkghandler.DecryptionTrigger] +} + +func New(c *config.Config) (*Keyper, error) { //nolint:unparam + return &Keyper{ + config: c, + }, nil +} + +func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { + var err error + + dbpool, err := db.Connect(ctx, runner, kpr.config.DatabaseURL, database.Definition.Name()) + if err != nil { + return errors.Wrap(err, "failed to connect to database") + } + + // TODO: the new latest block handler function will put values into this channel + trigger := make(chan *broker.Event[*epochkghandler.DecryptionTrigger]) + kpr.trigger = trigger + + // TODO: this will be more generic, since we don't need the contract deployments for + // the keyper core + ethConfig := configuration.NewEthnodeConfig() + ethConfig.EthereumURL = kpr.config.Optimism.JSONRPCURL + ethConfig.PrivateKey = kpr.config.Optimism.PrivateKey + kpr.core, err = keyper.New( + &kprconfig.Config{ + InstanceID: kpr.config.InstanceID, + DatabaseURL: kpr.config.DatabaseURL, + HTTPEnabled: kpr.config.HTTPEnabled, + HTTPListenAddress: kpr.config.HTTPListenAddress, + P2P: kpr.config.P2P, + Ethereum: ethConfig, + Shuttermint: kpr.config.Shuttermint, + Metrics: kpr.config.Metrics, + }, + trigger, + keyper.WithDBPool(dbpool), + keyper.NoBroadcastEonPublicKey(), + keyper.WithEonPublicKeyHandler(kpr.newEonPublicKey), + ) + if err != nil { + return errors.Wrap(err, "can't instantiate keyper core") + } + // TODO: wrap the logger and pass in + l2Client, err := shopclient.NewShutterL2Client( + ctx, + shopclient.WithClientURL(kpr.config.Optimism.JSONRPCURL), + shopclient.WithSyncNewBlock(kpr.newBlock), + shopclient.WithSyncNewKeyperSet(kpr.newKeyperSet), + ) + // TODO: how to deal with polling past state? (sounds like a big addition to the l2Client) + if err != nil { + return err + } + return runner.StartService(kpr.core, l2Client) +} + +func (kpr *Keyper) newBlock(ev *shopevent.NewLatestBlock) error { + log.Info(). + Int64("number", ev.Number.Int64()). + Str("hash", ev.BlockHash.Hex()). + Msg("new latest unsafe head on L2") + + // TODO: sanity checks + + idPreimage := identitypreimage.BigToIdentityPreimage(ev.Number) + trig := &epochkghandler.DecryptionTrigger{ + BlockNumber: ev.Number.Uint64(), + IdentityPreimages: []identitypreimage.IdentityPreimage{idPreimage}, + } + + // TODO: what to do if this blocks? + kpr.trigger <- broker.NewEvent(trig) + return nil +} + +func (kpr *Keyper) newKeyperSet(ev *shopevent.NewKeyperSetAdded) error { + log.Info(). + Uint64("activation-block", ev.ActivationBlock). + Msg("new keyper set added") + // TODO: set keyper set in the chainobsdb + + return nil +} + +func (kpr *Keyper) newEonPublicKey(ctx context.Context, pk keyper.EonPublicKey) error { + // TODO: post the public key to the contract + return nil +} diff --git a/rolling-shutter/keyperimpl/optimism/logger.go b/rolling-shutter/keyperimpl/optimism/logger.go new file mode 100644 index 000000000..874a91958 --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/logger.go @@ -0,0 +1,4 @@ +package optimism + +// TODO: wrap the zerolog logger. +type LoggerAdapter struct{} diff --git a/rolling-shutter/keyperimpl/optimism/setup_test.go b/rolling-shutter/keyperimpl/optimism/setup_test.go new file mode 100644 index 000000000..7881d0543 --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/setup_test.go @@ -0,0 +1,43 @@ +package optimism + +import ( + "crypto/ecdsa" + + "github.com/ethereum/go-ethereum/common" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/pkg/errors" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/testsetup" +) + +func init() { + var err error + testConfig.collatorKey, err = ethcrypto.GenerateKey() + if err != nil { + panic(errors.Wrap(err, "ethcrypto.GenerateKey failed")) + } +} + +type TestConfig struct { + collatorKey *ecdsa.PrivateKey +} + +var testConfig = &TestConfig{} + +func (TestConfig) GetAddress() common.Address { + return common.HexToAddress("0x2222222222222222222222222222222222222222") +} + +func (TestConfig) GetInstanceID() uint64 { + return 55 +} + +func (TestConfig) GetEon() uint64 { + return 22 +} + +func (c *TestConfig) GetCollatorKey() *ecdsa.PrivateKey { + return testConfig.collatorKey +} + +var _ testsetup.TestConfig = &TestConfig{} From 01cf53c52e5a8ba4d94e2f37236133ff0c1397d9 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Mon, 4 Dec 2023 17:02:19 +0100 Subject: [PATCH 04/91] chore(op): fix chain init --- rolling-shutter/cmd/chain/init.go | 67 ++++++++++++++++--------------- rolling-shutter/go.mod | 2 +- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/rolling-shutter/cmd/chain/init.go b/rolling-shutter/cmd/chain/init.go index 3e1df6991..e18e146cb 100644 --- a/rolling-shutter/cmd/chain/init.go +++ b/rolling-shutter/cmd/chain/init.go @@ -189,43 +189,44 @@ func initFilesWithConfig(tendermintConfig *cfg.Config, config *Config, appState Msg("Generated private validator") } - validatorPubKeyPath := filepath.Join(tendermintConfig.RootDir, "config", "priv_validator_pubkey.hex") - validatorPublicKeyHex := hex.EncodeToString(pv.Key.PubKey.Bytes()) - err = os.WriteFile(validatorPubKeyPath, []byte(validatorPublicKeyHex), 0o644) - if err != nil { - return errors.Wrapf(err, "Could not write to %s", validatorPubKeyPath) - } - log.Info().Str("path", validatorPubKeyPath).Str("validatorPublicKey", validatorPublicKeyHex).Msg("Saved private validator publickey") - - // genesis file - genFile := tendermintConfig.GenesisFile() - if tmos.FileExists(genFile) { - log.Info().Str("path", genFile).Msg("Found genesis file") - } else { - appStateBytes, err := amino.NewCodec().MarshalJSONIndent(appState, "", " ") - if err != nil { - return err - } - genDoc := types.GenesisDoc{ - ChainID: fmt.Sprintf("shutter-test-chain-%v", tmrand.Str(6)), - GenesisTime: time.Now(), - ConsensusParams: types.DefaultConsensusParams(), - AppState: appStateBytes, - } - pubKey, err := pv.GetPubKey() + validatorPubKeyPath := filepath.Join(tendermintConfig.RootDir, "config", "priv_validator_pubkey.hex") + validatorPublicKeyHex := hex.EncodeToString(pv.Key.PubKey.Bytes()) + err = os.WriteFile(validatorPubKeyPath, []byte(validatorPublicKeyHex), 0o644) if err != nil { - return errors.Wrap(err, "can't get pubkey") + return errors.Wrapf(err, "Could not write to %s", validatorPubKeyPath) } - genDoc.Validators = []types.GenesisValidator{{ - Address: pubKey.Address(), - PubKey: pubKey, - Power: 10, - }} + log.Info().Str("path", validatorPubKeyPath).Str("validatorPublicKey", validatorPublicKeyHex).Msg("Saved private validator publickey") - if err := genDoc.SaveAs(genFile); err != nil { - return err + // genesis file + genFile := tendermintConfig.GenesisFile() + if tmos.FileExists(genFile) { + log.Info().Str("path", genFile).Msg("Found genesis file") + } else { + appStateBytes, err := amino.NewCodec().MarshalJSONIndent(appState, "", " ") + if err != nil { + return err + } + genDoc := types.GenesisDoc{ + ChainID: fmt.Sprintf("shutter-test-chain-%v", tmrand.Str(6)), + GenesisTime: time.Now(), + ConsensusParams: types.DefaultConsensusParams(), + AppState: appStateBytes, + } + pubKey, err := pv.GetPubKey() + if err != nil { + return errors.Wrap(err, "can't get pubkey") + } + genDoc.Validators = []types.GenesisValidator{{ + Address: pubKey.Address(), + PubKey: pubKey, + Power: 10, + }} + + if err := genDoc.SaveAs(genFile); err != nil { + return err + } + log.Info().Str("path", genFile).Msg("Generated genesis file") } - log.Info().Str("path", genFile).Msg("Generated genesis file") } nodeKeyFile := tendermintConfig.NodeKeyFile() diff --git a/rolling-shutter/go.mod b/rolling-shutter/go.mod index b23d639a4..f059c4d28 100644 --- a/rolling-shutter/go.mod +++ b/rolling-shutter/go.mod @@ -46,6 +46,7 @@ require ( go.opentelemetry.io/otel/trace v1.21.0 go.opentelemetry.io/proto/otlp v1.0.0 golang.org/x/crypto v0.15.0 + golang.org/x/exp v0.0.0-20231006140011-7918f672742d golang.org/x/sync v0.5.0 google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v3 v3.0.1 @@ -225,7 +226,6 @@ require ( go.uber.org/mock v0.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/mod v0.13.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.14.0 // indirect From f3a9e4e380e07cba077e30582872d64ef79edbd6 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Wed, 6 Dec 2023 15:50:25 +0100 Subject: [PATCH 05/91] chore(op): add initial sync client --- docker/build-src/optimism/Dockerfile | 22 ++ rolling-shutter/cmd/chain/init.go | 5 +- rolling-shutter/go.mod | 5 +- rolling-shutter/go.sum | 7 +- rolling-shutter/keyper/keyper.go | 1 - .../keyperimpl/optimism/config/config.go | 2 +- rolling-shutter/keyperimpl/optimism/keyper.go | 8 +- .../keyperimpl/optimism/sync/client.go | 129 +++++++++++ .../keyperimpl/optimism/sync/client/client.go | 47 ++++ .../keyperimpl/optimism/sync/event/events.go | 27 +++ .../keyperimpl/optimism/sync/event/handler.go | 8 + .../keyperimpl/optimism/sync/options.go | 209 ++++++++++++++++++ .../optimism/sync/syncer/eonpubkey.go | 100 +++++++++ .../optimism/sync/syncer/keyperset.go | 165 ++++++++++++++ .../optimism/sync/syncer/shutterstate.go | 139 ++++++++++++ .../optimism/sync/syncer/unsafehead.go | 67 ++++++ rolling-shutter/medley/logger/noop.go | 20 ++ 17 files changed, 949 insertions(+), 12 deletions(-) create mode 100644 docker/build-src/optimism/Dockerfile create mode 100644 rolling-shutter/keyperimpl/optimism/sync/client.go create mode 100644 rolling-shutter/keyperimpl/optimism/sync/client/client.go create mode 100644 rolling-shutter/keyperimpl/optimism/sync/event/events.go create mode 100644 rolling-shutter/keyperimpl/optimism/sync/event/handler.go create mode 100644 rolling-shutter/keyperimpl/optimism/sync/options.go create mode 100644 rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go create mode 100644 rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go create mode 100644 rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go create mode 100644 rolling-shutter/keyperimpl/optimism/sync/syncer/unsafehead.go create mode 100644 rolling-shutter/medley/logger/noop.go diff --git a/docker/build-src/optimism/Dockerfile b/docker/build-src/optimism/Dockerfile new file mode 100644 index 000000000..1b4445e67 --- /dev/null +++ b/docker/build-src/optimism/Dockerfile @@ -0,0 +1,22 @@ +FROM golang:1.21 as builder +ENV GOMODCACHE=/root/.cache/mod +# Fetch go modules separately to improve cache usage +RUN mkdir /gomod +COPY /rolling-shutter/go.* /gomod/ +WORKDIR /gomod +RUN --mount=type=cache,target=/root/.cache go mod download + +# Build binary +COPY / /src +WORKDIR /src/rolling-shutter +RUN --mount=type=cache,target=/root/.cache CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOFLAGS=-v make build + +FROM scratch as runner + +COPY --from=builder /src/rolling-shutter/bin/rolling-shutter /rolling-shutter + +# Use 'uclibc' flavor to avoid https://github.com/docker-library/busybox/issues/155#issuecomment-1344375664 +RUN --mount=from=busybox:uclibc,src=/bin,dst=/bin mkdir -p /etc/ssl +COPY --from=builder /etc/ssl/certs /etc/ssl/certs + +ENTRYPOINT ["/rolling-shutter"] diff --git a/rolling-shutter/cmd/chain/init.go b/rolling-shutter/cmd/chain/init.go index e18e146cb..9bf2bdd53 100644 --- a/rolling-shutter/cmd/chain/init.go +++ b/rolling-shutter/cmd/chain/init.go @@ -133,7 +133,10 @@ func initFiles(_ *cobra.Command, config *Config, _ []string) error { // set up according to the network role: https://docs.tendermint.com/v0.34/tendermint-core/validators.html switch config.Role { - case VALIDATOR: + case VALIDATOR: // standard validator mode, network exposed + tendermintCfg.P2P.PexReactor = true + tendermintCfg.P2P.AddrBookStrict = true + case ISOLATEDVALIDATOR: // validator mode behind a sentry node tendermintCfg.P2P.PexReactor = false tendermintCfg.P2P.AddrBookStrict = false case "sentry": diff --git a/rolling-shutter/go.mod b/rolling-shutter/go.mod index f059c4d28..cf8b4873a 100644 --- a/rolling-shutter/go.mod +++ b/rolling-shutter/go.mod @@ -31,6 +31,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.17.0 github.com/rs/zerolog v1.28.0 + github.com/shutter-network/shop-contracts v0.0.0-20231207133930-c9acef7fbf8e github.com/shutter-network/shutter/shlib v0.1.13 github.com/shutter-network/txtypes v0.1.0 github.com/spf13/afero v1.8.2 @@ -46,7 +47,6 @@ require ( go.opentelemetry.io/otel/trace v1.21.0 go.opentelemetry.io/proto/otlp v1.0.0 golang.org/x/crypto v0.15.0 - golang.org/x/exp v0.0.0-20231006140011-7918f672742d golang.org/x/sync v0.5.0 google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v3 v3.0.1 @@ -56,7 +56,6 @@ require ( require ( github.com/DataDog/zstd v1.5.2 // indirect - github.com/Microsoft/go-winio v0.6.1 // indirect github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/allegro/bigcache v1.2.1 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -215,7 +214,6 @@ require ( github.com/tendermint/tm-db v0.6.7 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect - github.com/urfave/cli/v2 v2.25.7 // indirect github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect go.etcd.io/bbolt v1.3.7 // indirect @@ -226,6 +224,7 @@ require ( go.uber.org/mock v0.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/mod v0.13.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.14.0 // indirect diff --git a/rolling-shutter/go.sum b/rolling-shutter/go.sum index aa89b933b..a00414fb2 100644 --- a/rolling-shutter/go.sum +++ b/rolling-shutter/go.sum @@ -809,6 +809,8 @@ github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go. github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/shutter-network/shop-contracts v0.0.0-20231207133930-c9acef7fbf8e h1:GQp7rxcVY7eMLVaj4ZadjZIgL8qR6WX0T7lTthpGidU= +github.com/shutter-network/shop-contracts v0.0.0-20231207133930-c9acef7fbf8e/go.mod h1:LEWXLRruvxq9fe2oKtJI3xfzbauhfWTjOczHN61RU+4= github.com/shutter-network/shutter/shlib v0.1.13 h1:9YloDJBdhFAKm2GMg4gBNeaJ+Mw9Qzeh5Kz9A2ayp1E= github.com/shutter-network/shutter/shlib v0.1.13/go.mod h1:RlYNZjx+pfKAi0arH+jfdlxG4kQ75UFzDfVjgCVYaUw= github.com/shutter-network/txtypes v0.1.0 h1:QqdiiiB9AiBCSJ/ke6z1ZoDGfu2+1Lgpz5vHzVN4FKc= @@ -890,9 +892,10 @@ github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxW github.com/ulope/jrpc2 v0.0.0-20230706135348-a95cf3d96bd2 h1:rk0z/6CEJbstiHqv8+4ZIMv4Sm2zBZ5v5C56P8JXd+I= github.com/ulope/jrpc2 v0.0.0-20230706135348-a95cf3d96bd2/go.mod h1:bzOCUO4YLqjPZbPM4jeZzu/WIEauP/ouNWLRysNQdc0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.10 h1:p8Fspmz3iTctJstry1PYS3HVdllxnEzTEsgIgtxTrCk= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= -github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bCzK15ozrqo2sZxkh0FHynJZOTVoV6Q= +github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= diff --git a/rolling-shutter/keyper/keyper.go b/rolling-shutter/keyper/keyper.go index 2af2a1723..bff1d60df 100644 --- a/rolling-shutter/keyper/keyper.go +++ b/rolling-shutter/keyper/keyper.go @@ -265,7 +265,6 @@ func (kpr *KeyperCore) handleOnChainKeyperSetChanges( } else if err != nil { return err } - cq := obskeyper.New(tx) keyperSet, err := cq.GetKeyperSetByKeyperConfigIndex( ctx, diff --git a/rolling-shutter/keyperimpl/optimism/config/config.go b/rolling-shutter/keyperimpl/optimism/config/config.go index 971004012..d66824f29 100644 --- a/rolling-shutter/keyperimpl/optimism/config/config.go +++ b/rolling-shutter/keyperimpl/optimism/config/config.go @@ -44,7 +44,7 @@ func (c *Config) Validate() error { } func (c *Config) Name() string { - return "keyper" + return "op-keyper" } func (c *Config) SetDefaultValues() error { diff --git a/rolling-shutter/keyperimpl/optimism/keyper.go b/rolling-shutter/keyperimpl/optimism/keyper.go index 6352c2578..857add685 100644 --- a/rolling-shutter/keyperimpl/optimism/keyper.go +++ b/rolling-shutter/keyperimpl/optimism/keyper.go @@ -3,8 +3,6 @@ package optimism import ( "context" - shopclient "github.com/ethereum-optimism/optimism/shutter-node/client" - shopevent "github.com/ethereum-optimism/optimism/shutter-node/client/event" "github.com/jackc/pgx/v4/pgxpool" "github.com/pkg/errors" "github.com/rs/zerolog/log" @@ -13,6 +11,8 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/epochkghandler" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/kprconfig" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/config" + shopclient "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync" + shopevent "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/rollup/database" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/broker" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" @@ -85,7 +85,7 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { return runner.StartService(kpr.core, l2Client) } -func (kpr *Keyper) newBlock(ev *shopevent.NewLatestBlock) error { +func (kpr *Keyper) newBlock(ev *shopevent.LatestBlock) error { log.Info(). Int64("number", ev.Number.Int64()). Str("hash", ev.BlockHash.Hex()). @@ -104,7 +104,7 @@ func (kpr *Keyper) newBlock(ev *shopevent.NewLatestBlock) error { return nil } -func (kpr *Keyper) newKeyperSet(ev *shopevent.NewKeyperSetAdded) error { +func (kpr *Keyper) newKeyperSet(ev *shopevent.KeyperSet) error { log.Info(). Uint64("activation-block", ev.ActivationBlock). Msg("new keyper set added") diff --git a/rolling-shutter/keyperimpl/optimism/sync/client.go b/rolling-shutter/keyperimpl/optimism/sync/client.go new file mode 100644 index 000000000..d0a0c43c9 --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/sync/client.go @@ -0,0 +1,129 @@ +package sync + +import ( + "context" + "io" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/log" + "github.com/pkg/errors" + "github.com/shutter-network/shop-contracts/bindings" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/syncer" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/logger" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" +) + +var noopLogger = &logger.NoopLogger{} + +var ErrServiceNotInstantiated = errors.New("service is not instantiated, pass a handler function option.") + +type ShutterSync interface { + io.Closer + // Start starts an additional worker syncing job + Start() error +} + +type ShutterL2Client struct { + client.Client + log log.Logger + + options *options + + KeyperSetManager *bindings.KeyperSetManager + KeyBroadcast *bindings.KeyBroadcastContract + + sssync *syncer.ShutterStateSyncer + kssync *syncer.KeyperSetSyncer + uhsync *syncer.UnsafeHeadSyncer + epksync *syncer.EonPubKeySyncer + + services []service.Service +} + +func NewShutterL2Client(ctx context.Context, options ...Option) (*ShutterL2Client, error) { + opts := defaultOptions() + for _, option := range options { + err := option(opts) + if err != nil { + return nil, err + } + } + + err := opts.verify() + if err != nil { + return nil, err + } + + c := &ShutterL2Client{ + log: noopLogger, + services: []service.Service{}, + } + err = opts.apply(ctx, c) + if err != nil { + return nil, err + } + c.options = opts + return c, nil +} + +func (s *ShutterL2Client) getServices() []service.Service { + return s.services +} + +// func syncInitial() { +// if s.ForceEmitActiveKeyperSet { +// var b *number.BlockNumber +// if s.StartBlock == nil { +// b = number.LatestBlock +// } else { +// b = number.NewBlockNumber() +// b.SetInt64(int64(*s.StartBlock)) +// } +// activeKSAddred, err := s.getKeyperSetForBlock(ctx, b) +// if err != nil { +// return err +// } +// err = s.handler(activeKSAddred) +// if err != nil { +// return errors.Wrap(err, "handling of forced emission of active keyper set failed") +// } +// } +// } + +func (s *ShutterL2Client) GetShutterState(ctx context.Context) (*event.ShutterState, error) { + if s.sssync == nil { + return nil, errors.Wrap(ErrServiceNotInstantiated, "ShutterStateSyncer service not instantiated") + } + opts := &bind.CallOpts{ + Context: ctx, + } + return s.sssync.GetShutterState(ctx, opts) +} + +func (s *ShutterL2Client) GetKeyperSetForBlock(ctx context.Context, b *number.BlockNumber) (*event.KeyperSet, error) { + if s.kssync == nil { + return nil, errors.Wrap(ErrServiceNotInstantiated, "KeyperSetSyncer service not instantiated") + } + opts := &bind.CallOpts{ + Context: ctx, + } + return s.kssync.GetKeyperSetForBlock(ctx, opts, b) +} + +func (s *ShutterL2Client) GetEonPubKeyForEon(ctx context.Context, eon uint64) (*event.EonPublicKey, error) { + if s.sssync == nil { + return nil, errors.Wrap(ErrServiceNotInstantiated, "EonPubKeySyncer service not instantiated") + } + opts := &bind.CallOpts{ + Context: ctx, + } + return s.epksync.GetEonPubKeyForEon(ctx, opts, eon) +} + +func (s *ShutterL2Client) Start(ctx context.Context, runner service.Runner) error { + return runner.StartService(s.getServices()...) +} diff --git a/rolling-shutter/keyperimpl/optimism/sync/client/client.go b/rolling-shutter/keyperimpl/optimism/sync/client/client.go new file mode 100644 index 000000000..0444ab8c9 --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/sync/client/client.go @@ -0,0 +1,47 @@ +package client + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +type Client interface { + Close() + ChainID(ctx context.Context) (*big.Int, error) + BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) + BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) + BlockNumber(ctx context.Context) (uint64, error) + PeerCount(ctx context.Context) (uint64, error) + HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) + HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) + TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) + TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error) + TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) + TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) + TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) + SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) + SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) + NetworkID(ctx context.Context) (*big.Int, error) + BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) + StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) + CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) + NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) + FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) + SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) + PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) + PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) + PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) + PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) + PendingTransactionCount(ctx context.Context) (uint, error) + CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) + CallContractAtHash(ctx context.Context, msg ethereum.CallMsg, blockHash common.Hash) ([]byte, error) + PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) + SuggestGasPrice(ctx context.Context) (*big.Int, error) + SuggestGasTipCap(ctx context.Context) (*big.Int, error) + EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) + SendTransaction(ctx context.Context, tx *types.Transaction) error +} diff --git a/rolling-shutter/keyperimpl/optimism/sync/event/events.go b/rolling-shutter/keyperimpl/optimism/sync/event/events.go new file mode 100644 index 000000000..4485b204d --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/sync/event/events.go @@ -0,0 +1,27 @@ +package event + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +type ( + KeyperSet struct { + ActivationBlock uint64 + Members []common.Address + Threshold uint64 + Eon uint64 + } + EonPublicKey struct { + Eon uint64 + Key []byte + } + LatestBlock struct { + Number *big.Int + BlockHash common.Hash + } + ShutterState struct { + Active bool + } +) diff --git a/rolling-shutter/keyperimpl/optimism/sync/event/handler.go b/rolling-shutter/keyperimpl/optimism/sync/event/handler.go new file mode 100644 index 000000000..710075a84 --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/sync/event/handler.go @@ -0,0 +1,8 @@ +package event + +type ( + KeyperSetHandler func(*KeyperSet) error + EonPublicKeyHandler func(*EonPublicKey) error + BlockHandler func(*LatestBlock) error + ShutterStateHandler func(*ShutterState) error +) diff --git a/rolling-shutter/keyperimpl/optimism/sync/options.go b/rolling-shutter/keyperimpl/optimism/sync/options.go new file mode 100644 index 000000000..c5b011590 --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/sync/options.go @@ -0,0 +1,209 @@ +package sync + +import ( + "context" + "errors" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" + "github.com/shutter-network/shop-contracts/bindings" + "github.com/shutter-network/shop-contracts/predeploy" + + syncclient "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/syncer" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" +) + +type Option func(*options) error + +type options struct { + keyperSetManagerAddress *common.Address + keyBroadcastContractAddress *common.Address + clientURL string + client syncclient.Client + logger log.Logger + runner service.Runner + syncStart *uint64 + + handlerShutterState event.ShutterStateHandler + handlerKeyperSet event.KeyperSetHandler + handlerEonPublicKey event.EonPublicKeyHandler + handlerBlock event.BlockHandler +} + +func (o *options) verify() error { + if o.clientURL != "" && o.client != nil { + // TODO: error message + return errors.New("can't use client and client url") + } + if o.clientURL == "" && o.client == nil { + // TODO: error message + return errors.New("have to provide either url or client") + } + // TODO: check for the existence of the contract addresses depending on + // what handlers are not nil + return nil +} + +// initialize the shutter client and apply the options. +// the context is only the initialisation context, +// and should not be considered to handle the lifecycle +// of shutter clients background workers. +func (o *options) apply(ctx context.Context, c *ShutterL2Client) error { + var ( + client syncclient.Client + err error + ) + if o.clientURL != "" { + o.client, err = ethclient.DialContext(ctx, o.clientURL) + if err != nil { + return err + } + } + client = o.client + c.log = o.logger + + c.Client = client + + c.KeyperSetManager, err = bindings.NewKeyperSetManager(*o.keyperSetManagerAddress, client) + if err != nil { + return err + } + c.kssync = &syncer.KeyperSetSyncer{ + Client: client, + Contract: c.KeyperSetManager, + Log: c.log, + StartBlock: o.syncStart, + Handler: o.handlerKeyperSet, + } + if o.handlerKeyperSet != nil { + c.services = append(c.services, c.kssync) + } + + c.KeyBroadcast, err = bindings.NewKeyBroadcastContract(*o.keyBroadcastContractAddress, client) + if err != nil { + return err + } + c.epksync = &syncer.EonPubKeySyncer{ + Client: client, + Log: c.log, + Contract: c.KeyBroadcast, + Handler: o.handlerEonPublicKey, + StartBlock: o.syncStart, + } + if o.handlerEonPublicKey != nil { + c.services = append(c.services, c.epksync) + } + + c.sssync = &syncer.ShutterStateSyncer{ + Client: client, + Contract: c.KeyperSetManager, + Log: c.log, + Handler: o.handlerShutterState, + StartBlock: o.syncStart, + } + if o.handlerShutterState != nil { + c.services = append(c.services, c.sssync) + } + + if o.handlerBlock != nil { + c.uhsync = &syncer.UnsafeHeadSyncer{ + Client: client, + Log: c.log, + Handler: o.handlerBlock, + } + } + return nil +} + +func defaultOptions() *options { + return &options{ + keyperSetManagerAddress: &predeploy.KeyperSetManagerAddr, + keyBroadcastContractAddress: &predeploy.KeyBroadcastContractAddr, + clientURL: "", + client: nil, + logger: noopLogger, + runner: nil, + syncStart: nil, + } +} + +func WithSyncStartBlock(blockNumber uint64) Option { + return func(o *options) error { + bn := blockNumber + o.syncStart = &bn + return nil + } +} + +func WithRunner(runner service.Runner) Option { + return func(o *options) error { + o.runner = runner + return nil + } +} + +func WithKeyBroadcastContract(address common.Address) Option { + return func(o *options) error { + o.keyBroadcastContractAddress = &address + return nil + } +} + +func WithKeyperSetManager(address common.Address) Option { + return func(o *options) error { + o.keyperSetManagerAddress = &address + return nil + } +} + +func WithClientURL(url string) Option { + return func(o *options) error { + o.clientURL = url + return nil + } +} + +func WithLogger(l log.Logger) Option { + return func(o *options) error { + o.logger = l + return nil + } +} + +func WithClient(client syncclient.Client) Option { + return func(o *options) error { + o.client = client + return nil + } +} + +func WithSyncNewKeyperSet(handler event.KeyperSetHandler) Option { + return func(o *options) error { + o.handlerKeyperSet = handler + return nil + } +} + +func WithSyncNewBlock(handler event.BlockHandler) Option { + return func(o *options) error { + o.handlerBlock = handler + return nil + } +} + +func WithSyncNewEonKey(handler event.EonPublicKeyHandler) Option { + return func(o *options) error { + o.handlerEonPublicKey = handler + return nil + } +} + +func WithSyncNewShutterState(handler event.ShutterStateHandler) Option { + return func(o *options) error { + o.handlerShutterState = handler + return nil + } +} diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go new file mode 100644 index 000000000..7468a3d4f --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go @@ -0,0 +1,100 @@ +package syncer + +import ( + "context" + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/log" + "github.com/shutter-network/shop-contracts/bindings" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" +) + +type EonPubKeySyncer struct { + Client client.Client + Log log.Logger + Contract *bindings.KeyBroadcastContract + StartBlock *uint64 + Handler event.EonPublicKeyHandler + + keyBroadcastCh chan *bindings.KeyBroadcastContractEonKeyBroadcast +} + +func (s *EonPubKeySyncer) Start(ctx context.Context, runner service.Runner) error { + if s.Handler == nil { + return errors.New("no handler registered") + } + watchOpts := &bind.WatchOpts{ + Start: s.StartBlock, // nil means latest + Context: ctx, + } + s.keyBroadcastCh = make(chan *bindings.KeyBroadcastContractEonKeyBroadcast, 10) + subs, err := s.Contract.WatchEonKeyBroadcast(watchOpts, s.keyBroadcastCh) + // FIXME: what to do on subs.Error() + if err != nil { + return err + } + runner.Defer(subs.Unsubscribe) + runner.Defer(func() { + close(s.keyBroadcastCh) + }) + runner.Go(func() error { + return s.watchNewEonPubkey(ctx) + }) + return nil +} + +func (s *EonPubKeySyncer) logCallError(attrName string, err error) { + s.Log.Error( + fmt.Sprintf("could not retrieve `%s` from contract", attrName), + "error", + err.Error(), + ) +} + +func (s *EonPubKeySyncer) GetEonPubKeyForEon(ctx context.Context, opts *bind.CallOpts, eon uint64) (*event.EonPublicKey, error) { + if opts == nil { + opts = &bind.CallOpts{ + Context: ctx, + } + } + key, err := s.Contract.GetEonKey(opts, eon) + // XXX: can the key be a null byte? + // I think we rather get a index out of bounds error. + if err != nil { + return nil, err + } + return &event.EonPublicKey{ + Eon: eon, + Key: key, + }, nil +} + +func (s *EonPubKeySyncer) watchNewEonPubkey(ctx context.Context) error { + for { + select { + case newEonKey, ok := <-s.keyBroadcastCh: + if !ok { + return nil + } + ev := &event.EonPublicKey{ + Eon: newEonKey.Eon, + Key: newEonKey.Key, + } + err := s.Handler(ev) + if err != nil { + s.Log.Error( + "handler for `NewKeyperSet` errored", + "error", + err.Error(), + ) + } + case <-ctx.Done(): + return ctx.Err() + } + } +} diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go new file mode 100644 index 000000000..47d31343b --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go @@ -0,0 +1,165 @@ +package syncer + +import ( + "context" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/pkg/errors" + "github.com/shutter-network/shop-contracts/bindings" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" +) + +func makeCallError(attrName string, err error) error { + return errors.Wrapf(err, "could not retrieve `%s` from contract", attrName) +} + +type KeyperSetSyncer struct { + Client client.Client + Contract *bindings.KeyperSetManager + Log log.Logger + StartBlock *uint64 + Handler event.KeyperSetHandler + + keyperAddedCh chan *bindings.KeyperSetManagerKeyperSetAdded + handlerScheduler chan *event.KeyperSet +} + +func (s *KeyperSetSyncer) Start(ctx context.Context, runner service.Runner) error { + if s.Handler == nil { + return errors.New("no handler registered") + } + + watchOpts := &bind.WatchOpts{ + Start: s.StartBlock, // nil means latest + Context: ctx, + } + s.keyperAddedCh = make(chan *bindings.KeyperSetManagerKeyperSetAdded, 10) + subs, err := s.Contract.WatchKeyperSetAdded(watchOpts, s.keyperAddedCh) + // FIXME: what to do on subs.Error() + if err != nil { + return err + } + runner.Defer(subs.Unsubscribe) + runner.Defer(func() { + close(s.keyperAddedCh) + }) + runner.Go(func() error { + return s.watchNewKeypersService(ctx) + }) + return nil +} + +func (s *KeyperSetSyncer) GetKeyperSetForBlock(ctx context.Context, opts *bind.CallOpts, b *number.BlockNumber) (*event.KeyperSet, error) { + if b.Equal(number.LatestBlock) { + latestBlock, err := s.Client.BlockNumber(ctx) + if err != nil { + return nil, err + } + b = number.NewBlockNumber() + b.SetInt64(int64(latestBlock)) + } + if opts == nil { + opts = &bind.CallOpts{ + Context: ctx, + } + } + idx, err := s.Contract.GetKeyperSetIndexByBlock(opts, b.Uint64()) + if err != nil { + return nil, errors.Wrap(err, "could not retrieve keyper set index") + } + actBl, err := s.Contract.GetKeyperSetActivationBlock(opts, idx) + if err != nil { + return nil, errors.Wrap(err, "could not retrieve keyper set activation block") + } + addr, err := s.Contract.GetKeyperSetAddress(opts, idx) + if err != nil { + return nil, errors.Wrap(err, "could not retrieve keyper set address") + } + return s.newEvent(ctx, opts, addr, actBl) +} + +func (s *KeyperSetSyncer) newEvent( + ctx context.Context, + opts *bind.CallOpts, + keyperSetContract common.Address, + activationBlock uint64, +) (*event.KeyperSet, error) { + callOpts := opts + if callOpts == nil { + callOpts = &bind.CallOpts{ + Context: ctx, + } + } + ks, err := bindings.NewKeyperSet(keyperSetContract, s.Client) + if err != nil { + return nil, errors.Wrap(err, "could not bind to KeyperSet contract") + } + // the manager only accepts final keyper sets, + // so we expect this to be final now. + final, err := ks.IsFinalized(callOpts) + if err != nil { + return nil, makeCallError("IsFinalized", err) + } + if !final { + return nil, errors.New("contract did accept unfinalized keyper-sets") + } + members, err := ks.GetMembers(callOpts) + if err != nil { + return nil, makeCallError("Members", err) + } + threshold, err := ks.GetThreshold(callOpts) + if err != nil { + return nil, makeCallError("Threshold", err) + } + eon, err := s.Contract.GetKeyperSetIndexByBlock(callOpts, activationBlock) + if err != nil { + return nil, makeCallError("KeyperSetIndexByBlock", err) + } + return &event.KeyperSet{ + ActivationBlock: activationBlock, + Members: members, + Threshold: threshold, + Eon: eon, + }, nil +} + +func (s *KeyperSetSyncer) watchNewKeypersService(ctx context.Context) error { + for { + select { + case newKeypers, ok := <-s.keyperAddedCh: + if !ok { + return nil + } + newKeyperSet, err := s.newEvent( + ctx, + nil, + newKeypers.KeyperSetContract, + newKeypers.ActivationBlock, + ) + if err != nil { + s.Log.Error( + "error while fetching new event", + "error", + err.Error(), + ) + continue + } + err = s.Handler(newKeyperSet) + if err != nil { + s.Log.Error( + "handler for `NewKeyperSet` errored", + "error", + err.Error(), + ) + } + case <-ctx.Done(): + return ctx.Err() + } + } +} diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go new file mode 100644 index 000000000..15d582c5d --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go @@ -0,0 +1,139 @@ +package syncer + +import ( + "context" + "errors" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/log" + "github.com/shutter-network/shop-contracts/bindings" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" +) + +type ShutterStateSyncer struct { + Client client.Client + Contract *bindings.KeyperSetManager + StartBlock *uint64 + Log log.Logger + Handler event.ShutterStateHandler + + pausedCh chan *bindings.KeyperSetManagerPaused + unpausedCh chan *bindings.KeyperSetManagerUnpaused +} + +func (s *ShutterStateSyncer) GetShutterState(ctx context.Context, opts *bind.CallOpts) (*event.ShutterState, error) { + if opts == nil { + opts = &bind.CallOpts{ + Context: ctx, + } + } + isPaused, err := s.Contract.Paused(opts) + if err != nil { + return nil, err + } + return &event.ShutterState{ + Active: !isPaused, + }, nil +} + +func (s *ShutterStateSyncer) Start(ctx context.Context, runner service.Runner) error { + if s.Handler == nil { + return errors.New("no handler registered") + } + watchOpts := &bind.WatchOpts{ + Start: s.StartBlock, // nil means latest + Context: ctx, + } + s.pausedCh = make(chan *bindings.KeyperSetManagerPaused) + subs, err := s.Contract.WatchPaused(watchOpts, s.pausedCh) + // FIXME: what to do on subs.Error() + if err != nil { + return err + } + runner.Defer(subs.Unsubscribe) + runner.Defer(func() { + close(s.pausedCh) + }) + + s.unpausedCh = make(chan *bindings.KeyperSetManagerUnpaused) + subs, err = s.Contract.WatchUnpaused(watchOpts, s.unpausedCh) + // FIXME: what to do on subs.Error() + if err != nil { + return err + } + runner.Defer(subs.Unsubscribe) + runner.Defer(func() { + close(s.unpausedCh) + }) + + runner.Go(func() error { + return s.watchPaused(ctx) + }) + return nil +} + +func (s *ShutterStateSyncer) pollIsActive(ctx context.Context) (bool, error) { + callOpts := bind.CallOpts{ + Context: ctx, + } + paused, err := s.Contract.Paused(&callOpts) + return !paused, err +} + +func (s *ShutterStateSyncer) handle(ev *event.ShutterState) bool { + err := s.Handler(ev) + if err != nil { + s.Log.Error( + "handler for `NewShutterState` errored", + "error", + err.Error(), + ) + return false + } + return true +} + +func (s *ShutterStateSyncer) watchPaused(ctx context.Context) error { + isActive, err := s.pollIsActive(ctx) + if err != nil { + // XXX: this will fail everything, do we want that? + return err + } + ev := &event.ShutterState{ + Active: isActive, + } + s.handle(ev) + for { + select { + case _, ok := <-s.unpausedCh: + if !ok { + return nil + } + if isActive { + s.Log.Error("state mismatch", "got", "actice", "have", "inactive") + } + ev := &event.ShutterState{ + Active: true, + } + isActive = ev.Active + s.handle(ev) + case _, ok := <-s.pausedCh: + if !ok { + return nil + } + if isActive { + s.Log.Error("state mismatch", "got", "inactive", "have", "active") + } + ev := &event.ShutterState{ + Active: false, + } + isActive = ev.Active + s.handle(ev) + case <-ctx.Done(): + return ctx.Err() + } + } +} diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/unsafehead.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/unsafehead.go new file mode 100644 index 000000000..2f05308ee --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/unsafehead.go @@ -0,0 +1,67 @@ +package syncer + +import ( + "context" + "errors" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" +) + +type UnsafeHeadSyncer struct { + Client client.Client + Log log.Logger + Handler event.BlockHandler + + newLatestHeadCh chan *types.Header +} + +func (s *UnsafeHeadSyncer) Start(ctx context.Context, runner service.Runner) error { + if s.Handler == nil { + return errors.New("no handler registered") + } + s.newLatestHeadCh = make(chan *types.Header, 1) + + subs, err := s.Client.SubscribeNewHead(ctx, s.newLatestHeadCh) + // FIXME: what to do on subs.Error() + if err != nil { + return err + } + runner.Defer(subs.Unsubscribe) + runner.Defer(func() { + close(s.newLatestHeadCh) + }) + runner.Go(func() error { + return s.watchLatestUnsafeHead(ctx) + }) + return nil +} + +func (s *UnsafeHeadSyncer) watchLatestUnsafeHead(ctx context.Context) error { + for { + select { + case newHeader, ok := <-s.newLatestHeadCh: + if !ok { + return nil + } + ev := &event.LatestBlock{ + Number: newHeader.Number, + BlockHash: newHeader.Hash(), + } + err := s.Handler(ev) + if err != nil { + s.Log.Error( + "handler for `NewLatestBlock` errored", + "error", + err.Error(), + ) + } + case <-ctx.Done(): + return ctx.Err() + } + } +} diff --git a/rolling-shutter/medley/logger/noop.go b/rolling-shutter/medley/logger/noop.go new file mode 100644 index 000000000..ebaffb6bb --- /dev/null +++ b/rolling-shutter/medley/logger/noop.go @@ -0,0 +1,20 @@ +//nolint:revive +package logger + +import "github.com/ethereum/go-ethereum/log" + +type NoopLogHandler struct{} + +func (nh *NoopLogHandler) Log(r *log.Record) error { return nil } + +type NoopLogger struct{} + +func (n *NoopLogger) New(ctx ...interface{}) log.Logger { return &NoopLogger{} } +func (n *NoopLogger) GetHandler() log.Handler { return &NoopLogHandler{} } +func (n *NoopLogger) SetHandler(h log.Handler) {} +func (n *NoopLogger) Trace(msg string, ctx ...interface{}) {} +func (n *NoopLogger) Debug(msg string, ctx ...interface{}) {} +func (n *NoopLogger) Info(msg string, ctx ...interface{}) {} +func (n *NoopLogger) Warn(msg string, ctx ...interface{}) {} +func (n *NoopLogger) Error(msg string, ctx ...interface{}) {} +func (n *NoopLogger) Crit(msg string, ctx ...interface{}) {} From 1044380e4d5cc1e647665b23ac60f6ac6d8ec06d Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Wed, 6 Dec 2023 18:56:06 +0100 Subject: [PATCH 06/91] chore(op): allow private repo fetching in Dockerfile --- docker/build-src/optimism/Dockerfile | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docker/build-src/optimism/Dockerfile b/docker/build-src/optimism/Dockerfile index 1b4445e67..2755310a4 100644 --- a/docker/build-src/optimism/Dockerfile +++ b/docker/build-src/optimism/Dockerfile @@ -1,5 +1,12 @@ FROM golang:1.21 as builder ENV GOMODCACHE=/root/.cache/mod + +# allow private repos +ENV GOPRIVATE="github.com/shutter-network/*" +ENV GONOPROXY=localhost +ARG GITHUB_ACCESS_TOKEN +RUN git config --global url."https://$GITHUB_ACCESS_TOKEN@github.com/".insteadOf "https://github.com/" + # Fetch go modules separately to improve cache usage RUN mkdir /gomod COPY /rolling-shutter/go.* /gomod/ @@ -9,7 +16,10 @@ RUN --mount=type=cache,target=/root/.cache go mod download # Build binary COPY / /src WORKDIR /src/rolling-shutter -RUN --mount=type=cache,target=/root/.cache CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOFLAGS=-v make build + + +RUN go env +RUN --mount=type=cache,target=/root/.cache CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOFLAGS=-v make build FROM scratch as runner From a7f37b27cbfba061bc980ac1a3d64b946b37ad15 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Thu, 7 Dec 2023 16:29:26 +0100 Subject: [PATCH 07/91] fix(op): use correct database defintion for optimism keyper --- rolling-shutter/keyperimpl/optimism/keyper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rolling-shutter/keyperimpl/optimism/keyper.go b/rolling-shutter/keyperimpl/optimism/keyper.go index 857add685..8eac8dbc2 100644 --- a/rolling-shutter/keyperimpl/optimism/keyper.go +++ b/rolling-shutter/keyperimpl/optimism/keyper.go @@ -11,9 +11,9 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/epochkghandler" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/kprconfig" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/config" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/database" shopclient "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync" shopevent "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/rollup/database" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/broker" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" From a384cbd392f179cd4d43cf7100d592c423ef655b Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Mon, 11 Dec 2023 15:08:12 +0100 Subject: [PATCH 08/91] feat(op): add bootstrap command --- rolling-shutter/cmd/bootstrap/bootstrap.go | 4 ++ rolling-shutter/cmd/command.go | 1 + rolling-shutter/cmd/optimism/bootstrap.go | 42 +++++++++++ rolling-shutter/docs/rolling-shutter.md | 3 +- .../docs/rolling-shutter_op-bootstrap.md | 29 ++++++++ ...ng-shutter_op-bootstrap_fetch-keyperset.md | 27 ++++++++ ...ng-shutter_op-bootstrap_generate-config.md | 28 ++++++++ .../docs/rolling-shutter_op-keyper.md | 34 +++++++++ ...lling-shutter_op-keyper_generate-config.md | 28 ++++++++ .../docs/rolling-shutter_op-keyper_initdb.md | 27 ++++++++ .../optimism/bootstrap/bootstrap.go | 56 +++++++++++++++ .../keyperimpl/optimism/bootstrap/config.go | 67 ++++++++++++++++++ .../optimism/bootstrap/keyperset.go | 26 +++++++ .../medley/configuration/command/command.go | 69 ++++++++++++------- .../medley/encodeable/number/block.go | 46 +++++++++++++ 15 files changed, 463 insertions(+), 24 deletions(-) create mode 100644 rolling-shutter/cmd/optimism/bootstrap.go create mode 100644 rolling-shutter/docs/rolling-shutter_op-bootstrap.md create mode 100644 rolling-shutter/docs/rolling-shutter_op-bootstrap_fetch-keyperset.md create mode 100644 rolling-shutter/docs/rolling-shutter_op-bootstrap_generate-config.md create mode 100644 rolling-shutter/docs/rolling-shutter_op-keyper.md create mode 100644 rolling-shutter/docs/rolling-shutter_op-keyper_generate-config.md create mode 100644 rolling-shutter/docs/rolling-shutter_op-keyper_initdb.md create mode 100644 rolling-shutter/keyperimpl/optimism/bootstrap/bootstrap.go create mode 100644 rolling-shutter/keyperimpl/optimism/bootstrap/config.go create mode 100644 rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go create mode 100644 rolling-shutter/medley/encodeable/number/block.go diff --git a/rolling-shutter/cmd/bootstrap/bootstrap.go b/rolling-shutter/cmd/bootstrap/bootstrap.go index 0c4ce0ee3..8b4d6dcb3 100644 --- a/rolling-shutter/cmd/bootstrap/bootstrap.go +++ b/rolling-shutter/cmd/bootstrap/bootstrap.go @@ -83,6 +83,10 @@ chain's genesis config.`, return cmd } +// TODO: deprecate this command and split this up in 2 stages: +// 1. gather initial keyperset information e.g. from contracts +// (could be different from optimism, rollupshutter,snapshot) +// 2. send the batch-config / new block seen message based on that info. func bootstrap(config *Config) error { ctx := context.Background() ethereumClient, err := ethclient.DialContext(ctx, config.EthereumURL) diff --git a/rolling-shutter/cmd/command.go b/rolling-shutter/cmd/command.go index ce5c3be0b..91cea7895 100644 --- a/rolling-shutter/cmd/command.go +++ b/rolling-shutter/cmd/command.go @@ -25,6 +25,7 @@ func Subcommands() []*cobra.Command { chain.Cmd(), collator.Cmd(), optimism.Cmd(), + optimism.OPBootstrapCmd(), rollupkeyper.Cmd(), mocknode.Cmd(), snapshot.Cmd(), diff --git a/rolling-shutter/cmd/optimism/bootstrap.go b/rolling-shutter/cmd/optimism/bootstrap.go new file mode 100644 index 000000000..45e616309 --- /dev/null +++ b/rolling-shutter/cmd/optimism/bootstrap.go @@ -0,0 +1,42 @@ +package optimism + +import ( + "context" + + "github.com/spf13/cobra" + + boot "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/bootstrap" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration/command" +) + +// TODO: use this to replace the old bootstrap command. +// First writing the keyperset and then bootstrapping allows +// to support different contracts etc. +func OPBootstrapCmd() *cobra.Command { + builder := command.Build( + bootstrap, + command.Usage( + "Bootstrap validator utility functions for a shuttermint chain", + ``, + ), + command.WithGenerateConfigSubcommand(), + ) + + bootstrapCmd := &cobra.Command{ + Use: "fetch-keyperset", + Short: "fetch-keyperset", + Args: cobra.NoArgs, + RunE: builder.WrapFuncParseConfig(keyperSet), + } + builder.Command().AddCommand(bootstrapCmd) + return builder.Command() +} + +func keyperSet(cfg *boot.Config) error { + ctx := context.Background() + return boot.GetKeyperSet(ctx, cfg) +} + +func bootstrap(cfg *boot.Config) error { + return boot.BootstrapValidators(cfg) +} diff --git a/rolling-shutter/docs/rolling-shutter.md b/rolling-shutter/docs/rolling-shutter.md index b6a7b834d..1e092ddfa 100644 --- a/rolling-shutter/docs/rolling-shutter.md +++ b/rolling-shutter/docs/rolling-shutter.md @@ -21,7 +21,8 @@ A collection of commands to run and interact with Rolling Shutter nodes * [rolling-shutter keyper](rolling-shutter_keyper.md) - Run a Shutter keyper node * [rolling-shutter mocknode](rolling-shutter_mocknode.md) - Run a Shutter mock node * [rolling-shutter mocksequencer](rolling-shutter_mocksequencer.md) - Run a Shutter mock sequencer -* [rolling-shutter optimismkeyper](rolling-shutter_optimismkeyper.md) - Run a Shutter optimism keyper node +* [rolling-shutter op-bootstrap](rolling-shutter_op-bootstrap.md) - Bootstrap validator utility functions for a shuttermint chain +* [rolling-shutter op-keyper](rolling-shutter_op-keyper.md) - Run a Shutter optimism keyper node * [rolling-shutter p2pnode](rolling-shutter_p2pnode.md) - Run a Shutter p2p bootstrap node * [rolling-shutter proxy](rolling-shutter_proxy.md) - Run a Ethereum JSON RPC proxy * [rolling-shutter snapshot](rolling-shutter_snapshot.md) - Run the Snapshot Hub communication module diff --git a/rolling-shutter/docs/rolling-shutter_op-bootstrap.md b/rolling-shutter/docs/rolling-shutter_op-bootstrap.md new file mode 100644 index 000000000..d6f2b918b --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_op-bootstrap.md @@ -0,0 +1,29 @@ +## rolling-shutter op-bootstrap + +Bootstrap validator utility functions for a shuttermint chain + +``` +rolling-shutter op-bootstrap [flags] +``` + +### Options + +``` + --config string config file + -h, --help help for op-bootstrap +``` + +### Options inherited from parent commands + +``` + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter](rolling-shutter.md) - A collection of commands to run and interact with Rolling Shutter nodes +* [rolling-shutter op-bootstrap fetch-keyperset](rolling-shutter_op-bootstrap_fetch-keyperset.md) - fetch-keyperset +* [rolling-shutter op-bootstrap generate-config](rolling-shutter_op-bootstrap_generate-config.md) - Generate a 'op-bootstrap' configuration file + diff --git a/rolling-shutter/docs/rolling-shutter_op-bootstrap_fetch-keyperset.md b/rolling-shutter/docs/rolling-shutter_op-bootstrap_fetch-keyperset.md new file mode 100644 index 000000000..c2c839899 --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_op-bootstrap_fetch-keyperset.md @@ -0,0 +1,27 @@ +## rolling-shutter op-bootstrap fetch-keyperset + +fetch-keyperset + +``` +rolling-shutter op-bootstrap fetch-keyperset [flags] +``` + +### Options + +``` + -h, --help help for fetch-keyperset +``` + +### Options inherited from parent commands + +``` + --config string config file + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter op-bootstrap](rolling-shutter_op-bootstrap.md) - Bootstrap validator utility functions for a shuttermint chain + diff --git a/rolling-shutter/docs/rolling-shutter_op-bootstrap_generate-config.md b/rolling-shutter/docs/rolling-shutter_op-bootstrap_generate-config.md new file mode 100644 index 000000000..1ef716e53 --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_op-bootstrap_generate-config.md @@ -0,0 +1,28 @@ +## rolling-shutter op-bootstrap generate-config + +Generate a 'op-bootstrap' configuration file + +``` +rolling-shutter op-bootstrap generate-config [flags] +``` + +### Options + +``` + -h, --help help for generate-config + --output string output file +``` + +### Options inherited from parent commands + +``` + --config string config file + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter op-bootstrap](rolling-shutter_op-bootstrap.md) - Bootstrap validator utility functions for a shuttermint chain + diff --git a/rolling-shutter/docs/rolling-shutter_op-keyper.md b/rolling-shutter/docs/rolling-shutter_op-keyper.md new file mode 100644 index 000000000..30ae10467 --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_op-keyper.md @@ -0,0 +1,34 @@ +## rolling-shutter op-keyper + +Run a Shutter optimism keyper node + +### Synopsis + +This command runs a keyper node. It will connect to both an Optimism and a +Shuttermint node which have to be started separately in advance. + +``` +rolling-shutter op-keyper [flags] +``` + +### Options + +``` + --config string config file + -h, --help help for op-keyper +``` + +### Options inherited from parent commands + +``` + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter](rolling-shutter.md) - A collection of commands to run and interact with Rolling Shutter nodes +* [rolling-shutter op-keyper generate-config](rolling-shutter_op-keyper_generate-config.md) - Generate a 'op-keyper' configuration file +* [rolling-shutter op-keyper initdb](rolling-shutter_op-keyper_initdb.md) - Initialize the database of the 'op-keyper' + diff --git a/rolling-shutter/docs/rolling-shutter_op-keyper_generate-config.md b/rolling-shutter/docs/rolling-shutter_op-keyper_generate-config.md new file mode 100644 index 000000000..0e1cf9d6f --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_op-keyper_generate-config.md @@ -0,0 +1,28 @@ +## rolling-shutter op-keyper generate-config + +Generate a 'op-keyper' configuration file + +``` +rolling-shutter op-keyper generate-config [flags] +``` + +### Options + +``` + -h, --help help for generate-config + --output string output file +``` + +### Options inherited from parent commands + +``` + --config string config file + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter op-keyper](rolling-shutter_op-keyper.md) - Run a Shutter optimism keyper node + diff --git a/rolling-shutter/docs/rolling-shutter_op-keyper_initdb.md b/rolling-shutter/docs/rolling-shutter_op-keyper_initdb.md new file mode 100644 index 000000000..a220fc42c --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_op-keyper_initdb.md @@ -0,0 +1,27 @@ +## rolling-shutter op-keyper initdb + +Initialize the database of the 'op-keyper' + +``` +rolling-shutter op-keyper initdb [flags] +``` + +### Options + +``` + -h, --help help for initdb +``` + +### Options inherited from parent commands + +``` + --config string config file + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter op-keyper](rolling-shutter_op-keyper.md) - Run a Shutter optimism keyper node + diff --git a/rolling-shutter/keyperimpl/optimism/bootstrap/bootstrap.go b/rolling-shutter/keyperimpl/optimism/bootstrap/bootstrap.go new file mode 100644 index 000000000..b3d2bced2 --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/bootstrap/bootstrap.go @@ -0,0 +1,56 @@ +package bootstrap + +import ( + "context" + "encoding/json" + "os" + + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + "github.com/tendermint/tendermint/rpc/client/http" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/fx" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/shmsg" +) + +func BootstrapValidators(config *Config) error { + file, err := os.ReadFile(config.KeyperSetFilePath) + if err != nil { + log.Fatal().Err(err).Msg("failed to read keyper-set file") + } + ks := &event.KeyperSet{} + + err = json.Unmarshal(file, ks) + if err != nil { + log.Fatal().Err(err).Msg("failed to read parse keyper-set") + } + + ctx := context.Background() + + shmcl, err := http.New(config.ShuttermintURL, "/websocket") + if err != nil { + log.Fatal().Err(err).Msg("failed to connect to Shuttermint node") + } + + ms := fx.NewRPCMessageSender(shmcl, config.SigningKey.Key) + batchConfigMsg := shmsg.NewBatchConfig( + ks.ActivationBlock, + ks.Members, + ks.Threshold, + ks.Eon, + ) + + err = ms.SendMessage(ctx, batchConfigMsg) + if err != nil { + return errors.Errorf("Failed to send batch config message: %v", err) + } + + blockSeenMsg := shmsg.NewBlockSeen(ks.ActivationBlock) + err = ms.SendMessage(ctx, blockSeenMsg) + if err != nil { + return errors.Errorf("Failed to send start message: %v", err) + } + + return nil +} diff --git a/rolling-shutter/keyperimpl/optimism/bootstrap/config.go b/rolling-shutter/keyperimpl/optimism/bootstrap/config.go new file mode 100644 index 000000000..f6c4c8f66 --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/bootstrap/config.go @@ -0,0 +1,67 @@ +package bootstrap + +import ( + "crypto/rand" + "io" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/keys" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" +) + +var _ configuration.Config = &Config{} + +func NewConfig() *Config { + c := &Config{} + c.Init() + return c +} + +func (c *Config) Init() { + c.SigningKey = &keys.ECDSAPrivate{} + c.ActivationBlockNumber = number.NewBlockNumber() +} + +type Config struct { + InstanceID uint64 `shconfig:",required"` + + JSONRPCURL string ` comment:"The op-geth JSON RPC endpoint"` + ActivationBlockNumber *number.BlockNumber + KeyperSetFilePath string + + ShuttermintURL string + SigningKey *keys.ECDSAPrivate `shconfig:",required"` +} + +func (c *Config) Validate() error { + return nil +} + +func (c *Config) Name() string { + return "op-bootstrap" +} + +func (c *Config) SetDefaultValues() error { + c.JSONRPCURL = "http://localhost:8545" + c.ShuttermintURL = "http://localhost:26657" + c.KeyperSetFilePath = "keyperset.json" + c.ActivationBlockNumber = number.LatestBlock + return nil +} + +func (c *Config) SetExampleValues() error { + err := c.SetDefaultValues() + if err != nil { + return err + } + c.SigningKey, err = keys.GenerateECDSAKey(rand.Reader) + if err != nil { + return err + } + c.InstanceID = 42 + return nil +} + +func (c Config) TOMLWriteHeader(_ io.Writer) (int, error) { + return 0, nil +} diff --git a/rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go b/rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go new file mode 100644 index 000000000..9bd99cc3d --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go @@ -0,0 +1,26 @@ +package bootstrap + +import ( + "context" + "encoding/json" + "os" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" +) + +func GetKeyperSet(ctx context.Context, config *Config) error { + sl2, err := sync.NewShutterL2Client( + ctx, + sync.WithClientURL(config.JSONRPCURL), + ) + if err != nil { + return err + } + keyperSet, err := sl2.GetKeyperSetForBlock(ctx, number.LatestBlock) + if err != nil { + return err + } + file, _ := json.MarshalIndent(keyperSet, "", " ") + return os.WriteFile(config.KeyperSetFilePath, file, 0o644) +} diff --git a/rolling-shutter/medley/configuration/command/command.go b/rolling-shutter/medley/configuration/command/command.go index 4bcb8782f..e755b9132 100644 --- a/rolling-shutter/medley/configuration/command/command.go +++ b/rolling-shutter/medley/configuration/command/command.go @@ -48,7 +48,7 @@ func newCommandBuilder(name string) *commandBuilderConfig { } } -func newConfigForFunc[T configuration.Config](fn ConfigurableFunc[T]) T { +func NewConfigForFunc[T configuration.Config](fn ConfigurableFunc[T]) T { typ := reflect.TypeOf(fn).In(0).Elem() nw, ok := reflect.New(typ).Interface().(T) if !ok { @@ -66,7 +66,7 @@ func Build[T configuration.Config]( main ConfigurableFunc[T], options ...Option, ) *CommandBuilder[T] { - cfg := newConfigForFunc(main) + cfg := NewConfigForFunc(main) cfg.Init() builder := newCommandBuilder(strings.ToLower(cfg.Name())) for _, opt := range options { @@ -84,7 +84,7 @@ func Build[T configuration.Config]( Long: builder.longUsage, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - cfg := newConfigForFunc(main) + cfg := NewConfigForFunc(main) cfg.Init() v := viper.GetViper() v.SetFs(builder.filesystem) @@ -107,7 +107,7 @@ func Build[T configuration.Config]( Short: fmt.Sprintf("Generate a '%s' configuration file", builder.name), Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - cfg := newConfigForFunc(main) + cfg := NewConfigForFunc(main) cfg.Init() err := configuration.SetExampleValuesRecursive(cfg) if err != nil { @@ -130,7 +130,7 @@ func Build[T configuration.Config]( Short: fmt.Sprintf("Dump a '%s' configuration file, based on given config and env vars", builder.name), Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - cfg := newConfigForFunc(main) + cfg := NewConfigForFunc(main) cfg.Init() v := viper.GetViper() v.SetFs(builder.filesystem) @@ -157,32 +157,55 @@ func Build[T configuration.Config]( return cb } +type CobraRunE func(cmd *cobra.Command, args []string) error + // AddInitDBCommand attaches an additional subcommand // 'initdb' to the command initially built by the Build method. // The initDB function argument is structured in the same way than the "main" // function passed in to the Build method. -func (cb *CommandBuilder[T]) AddInitDBCommand(initDB ConfigurableFunc[T]) { +func (cb *CommandBuilder[T]) AddFunctionSubcommand( + fnc ConfigurableFunc[T], + use, short string, + args cobra.PositionalArgs, +) { cb.cobraCommand.AddCommand(&cobra.Command{ - Use: "initdb", - Short: fmt.Sprintf("Initialize the database of the '%s'", cb.builderConfig.name), - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - cfg := newConfigForFunc(initDB) - cfg.Init() - v := viper.GetViper() - v.SetFs(cb.builderConfig.filesystem) - err := ParseCLI(v, cmd, cfg) - if err != nil { - return errors.WithMessage(err, "Please check your configuration") - } - log.Debug(). - Interface("config", cfg). - Msg("got config") - return initDB(cfg) - }, + Use: use, + Short: short, + Args: args, + RunE: cb.WrapFuncParseConfig(fnc), }) } +func (cb *CommandBuilder[T]) WrapFuncParseConfig(fnc ConfigurableFunc[T]) CobraRunE { + return func(cmd *cobra.Command, args []string) error { + cfg := NewConfigForFunc(fnc) + cfg.Init() + v := viper.GetViper() + v.SetFs(cb.builderConfig.filesystem) + err := ParseCLI(v, cmd, cfg) + if err != nil { + return errors.WithMessage(err, "Please check your configuration") + } + log.Debug(). + Interface("config", cfg). + Msg("got config") + return fnc(cfg) + } +} + +// AddInitDBCommand attaches an additional subcommand +// 'initdb' to the command initially built by the Build method. +// The initDB function argument is structured in the same way than the "main" +// function passed in to the Build method. +func (cb *CommandBuilder[T]) AddInitDBCommand(initDB ConfigurableFunc[T]) { + cb.AddFunctionSubcommand( + initDB, + "initdb", + fmt.Sprintf("Initialize the database of the '%s'", cb.builderConfig.name), + cobra.NoArgs, + ) +} + func (cb *CommandBuilder[_]) Command() *cobra.Command { return cb.cobraCommand } diff --git a/rolling-shutter/medley/encodeable/number/block.go b/rolling-shutter/medley/encodeable/number/block.go new file mode 100644 index 000000000..6c050dfce --- /dev/null +++ b/rolling-shutter/medley/encodeable/number/block.go @@ -0,0 +1,46 @@ +package number + +import ( + "bytes" + "math/big" +) + +var ( + LatestBlockInt int64 = -1 + LatestBlock = &BlockNumber{new(big.Int).SetInt64(LatestBlockInt)} + LatestStr = []byte("latest") +) + +func NewBlockNumber() *BlockNumber { + return &BlockNumber{ + Int: new(big.Int), + } +} + +type BlockNumber struct { + *big.Int +} + +func (k *BlockNumber) UnmarshalText(b []byte) error { + k.Int = new(big.Int) + if bytes.Equal(b, LatestStr) { + k.Int.SetInt64(LatestBlockInt) + return nil + } + return k.Int.UnmarshalText(b) +} + +func (k *BlockNumber) IsLatest() bool { + return k.Equal(LatestBlock) +} + +func (k *BlockNumber) Equal(b *BlockNumber) bool { + return k.Int.Cmp(b.Int) == 0 +} + +func (k *BlockNumber) MarshalText() ([]byte, error) { + if k.IsLatest() { + return []byte("latest"), nil + } + return k.Int.MarshalText() +} From 6d1c20d0de2f91ee61747715c76e3168b2ab3457 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Tue, 12 Dec 2023 16:09:11 +0100 Subject: [PATCH 09/91] chore(op): bump shop-contracts version --- rolling-shutter/go.mod | 2 +- rolling-shutter/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rolling-shutter/go.mod b/rolling-shutter/go.mod index cf8b4873a..5e9c7b693 100644 --- a/rolling-shutter/go.mod +++ b/rolling-shutter/go.mod @@ -31,7 +31,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.17.0 github.com/rs/zerolog v1.28.0 - github.com/shutter-network/shop-contracts v0.0.0-20231207133930-c9acef7fbf8e + github.com/shutter-network/shop-contracts v0.0.0-20231220085304-80b8977d0bca github.com/shutter-network/shutter/shlib v0.1.13 github.com/shutter-network/txtypes v0.1.0 github.com/spf13/afero v1.8.2 diff --git a/rolling-shutter/go.sum b/rolling-shutter/go.sum index a00414fb2..c7cf3b768 100644 --- a/rolling-shutter/go.sum +++ b/rolling-shutter/go.sum @@ -809,8 +809,8 @@ github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go. github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= -github.com/shutter-network/shop-contracts v0.0.0-20231207133930-c9acef7fbf8e h1:GQp7rxcVY7eMLVaj4ZadjZIgL8qR6WX0T7lTthpGidU= -github.com/shutter-network/shop-contracts v0.0.0-20231207133930-c9acef7fbf8e/go.mod h1:LEWXLRruvxq9fe2oKtJI3xfzbauhfWTjOczHN61RU+4= +github.com/shutter-network/shop-contracts v0.0.0-20231220085304-80b8977d0bca h1:05Ghqw3FqH/UFuYIzc7z6GJyHk3HxAqY3iuY4L3x4Ow= +github.com/shutter-network/shop-contracts v0.0.0-20231220085304-80b8977d0bca/go.mod h1:LEWXLRruvxq9fe2oKtJI3xfzbauhfWTjOczHN61RU+4= github.com/shutter-network/shutter/shlib v0.1.13 h1:9YloDJBdhFAKm2GMg4gBNeaJ+Mw9Qzeh5Kz9A2ayp1E= github.com/shutter-network/shutter/shlib v0.1.13/go.mod h1:RlYNZjx+pfKAi0arH+jfdlxG4kQ75UFzDfVjgCVYaUw= github.com/shutter-network/txtypes v0.1.0 h1:QqdiiiB9AiBCSJ/ke6z1ZoDGfu2+1Lgpz5vHzVN4FKc= From 0751cf4a3752720d7db8a96a8b2183a620f2ce52 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Thu, 21 Dec 2023 23:26:30 +0100 Subject: [PATCH 10/91] chore(op): insert TM keyperset in database --- rolling-shutter/keyperimpl/optimism/keyper.go | 36 ++++++++++++++++--- .../keyperimpl/optimism/sync/event/handler.go | 10 +++--- .../optimism/sync/syncer/eonpubkey.go | 2 +- .../optimism/sync/syncer/keyperset.go | 2 +- .../optimism/sync/syncer/shutterstate.go | 10 +++--- .../optimism/sync/syncer/unsafehead.go | 2 +- 6 files changed, 45 insertions(+), 17 deletions(-) diff --git a/rolling-shutter/keyperimpl/optimism/keyper.go b/rolling-shutter/keyperimpl/optimism/keyper.go index 8eac8dbc2..cdac6a442 100644 --- a/rolling-shutter/keyperimpl/optimism/keyper.go +++ b/rolling-shutter/keyperimpl/optimism/keyper.go @@ -3,10 +3,12 @@ package optimism import ( "context" + "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/pgxpool" "github.com/pkg/errors" "github.com/rs/zerolog/log" + obskeyper "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/epochkghandler" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/kprconfig" @@ -14,13 +16,17 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/database" shopclient "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync" shopevent "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/broker" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" + "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" ) +var ErrParseKeyperSet = errors.New("can't parse KeyperSet") + type Keyper struct { core *keyper.KeyperCore dbpool *pgxpool.Pool @@ -85,7 +91,7 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { return runner.StartService(kpr.core, l2Client) } -func (kpr *Keyper) newBlock(ev *shopevent.LatestBlock) error { +func (kpr *Keyper) newBlock(_ context.Context, ev *shopevent.LatestBlock) error { log.Info(). Int64("number", ev.Number.Int64()). Str("hash", ev.BlockHash.Hex()). @@ -104,13 +110,33 @@ func (kpr *Keyper) newBlock(ev *shopevent.LatestBlock) error { return nil } -func (kpr *Keyper) newKeyperSet(ev *shopevent.KeyperSet) error { +func (kpr *Keyper) newKeyperSet(ctx context.Context, ev *shopevent.KeyperSet) error { log.Info(). Uint64("activation-block", ev.ActivationBlock). Msg("new keyper set added") - // TODO: set keyper set in the chainobsdb - - return nil + return kpr.dbpool.BeginFunc(ctx, func(tx pgx.Tx) error { + db := obskeyper.New(tx) + + keyperConfigIndex, err := medley.Uint64ToInt64Safe(ev.Eon) + if err != nil { + return errors.Wrap(err, ErrParseKeyperSet.Error()) + } + activationBlockNumber, err := medley.Uint64ToInt64Safe(ev.ActivationBlock) + if err != nil { + return errors.Wrap(err, ErrParseKeyperSet.Error()) + } + threshold, err := medley.Uint64ToInt64Safe(ev.Threshold) + if err != nil { + return errors.Wrap(err, ErrParseKeyperSet.Error()) + } + // XXX: does this work when the memberset is empty? + return db.InsertKeyperSet(ctx, obskeyper.InsertKeyperSetParams{ + KeyperConfigIndex: keyperConfigIndex, + ActivationBlockNumber: activationBlockNumber, + Keypers: shdb.EncodeAddresses(ev.Members), + Threshold: int32(threshold), + }) + }) } func (kpr *Keyper) newEonPublicKey(ctx context.Context, pk keyper.EonPublicKey) error { diff --git a/rolling-shutter/keyperimpl/optimism/sync/event/handler.go b/rolling-shutter/keyperimpl/optimism/sync/event/handler.go index 710075a84..60f5758da 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/event/handler.go +++ b/rolling-shutter/keyperimpl/optimism/sync/event/handler.go @@ -1,8 +1,10 @@ package event +import "context" + type ( - KeyperSetHandler func(*KeyperSet) error - EonPublicKeyHandler func(*EonPublicKey) error - BlockHandler func(*LatestBlock) error - ShutterStateHandler func(*ShutterState) error + KeyperSetHandler func(context.Context, *KeyperSet) error + EonPublicKeyHandler func(context.Context, *EonPublicKey) error + BlockHandler func(context.Context, *LatestBlock) error + ShutterStateHandler func(context.Context, *ShutterState) error ) diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go index 7468a3d4f..0c323e8de 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go @@ -85,7 +85,7 @@ func (s *EonPubKeySyncer) watchNewEonPubkey(ctx context.Context) error { Eon: newEonKey.Eon, Key: newEonKey.Key, } - err := s.Handler(ev) + err := s.Handler(ctx, ev) if err != nil { s.Log.Error( "handler for `NewKeyperSet` errored", diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go index 47d31343b..0a88d6019 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go @@ -150,7 +150,7 @@ func (s *KeyperSetSyncer) watchNewKeypersService(ctx context.Context) error { ) continue } - err = s.Handler(newKeyperSet) + err = s.Handler(ctx, newKeyperSet) if err != nil { s.Log.Error( "handler for `NewKeyperSet` errored", diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go index 15d582c5d..da9941e71 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go @@ -83,8 +83,8 @@ func (s *ShutterStateSyncer) pollIsActive(ctx context.Context) (bool, error) { return !paused, err } -func (s *ShutterStateSyncer) handle(ev *event.ShutterState) bool { - err := s.Handler(ev) +func (s *ShutterStateSyncer) handle(ctx context.Context, ev *event.ShutterState) bool { + err := s.Handler(ctx, ev) if err != nil { s.Log.Error( "handler for `NewShutterState` errored", @@ -105,7 +105,7 @@ func (s *ShutterStateSyncer) watchPaused(ctx context.Context) error { ev := &event.ShutterState{ Active: isActive, } - s.handle(ev) + s.handle(ctx, ev) for { select { case _, ok := <-s.unpausedCh: @@ -119,7 +119,7 @@ func (s *ShutterStateSyncer) watchPaused(ctx context.Context) error { Active: true, } isActive = ev.Active - s.handle(ev) + s.handle(ctx, ev) case _, ok := <-s.pausedCh: if !ok { return nil @@ -131,7 +131,7 @@ func (s *ShutterStateSyncer) watchPaused(ctx context.Context) error { Active: false, } isActive = ev.Active - s.handle(ev) + s.handle(ctx, ev) case <-ctx.Done(): return ctx.Err() } diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/unsafehead.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/unsafehead.go index 2f05308ee..b0572ccf3 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/unsafehead.go +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/unsafehead.go @@ -52,7 +52,7 @@ func (s *UnsafeHeadSyncer) watchLatestUnsafeHead(ctx context.Context) error { Number: newHeader.Number, BlockHash: newHeader.Hash(), } - err := s.Handler(ev) + err := s.Handler(ctx, ev) if err != nil { s.Log.Error( "handler for `NewLatestBlock` errored", From 6d63d884c7c591eb5277925c13fc7311e00c1fe0 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Fri, 22 Dec 2023 10:07:37 +0100 Subject: [PATCH 11/91] fix(op): append unsafe head service to l2client services --- rolling-shutter/keyperimpl/optimism/keyper.go | 7 +++++-- rolling-shutter/keyperimpl/optimism/sync/options.go | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/rolling-shutter/keyperimpl/optimism/keyper.go b/rolling-shutter/keyperimpl/optimism/keyper.go index cdac6a442..7eaad4083 100644 --- a/rolling-shutter/keyperimpl/optimism/keyper.go +++ b/rolling-shutter/keyperimpl/optimism/keyper.go @@ -53,8 +53,6 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { trigger := make(chan *broker.Event[*epochkghandler.DecryptionTrigger]) kpr.trigger = trigger - // TODO: this will be more generic, since we don't need the contract deployments for - // the keyper core ethConfig := configuration.NewEthnodeConfig() ethConfig.EthereumURL = kpr.config.Optimism.JSONRPCURL ethConfig.PrivateKey = kpr.config.Optimism.PrivateKey @@ -113,6 +111,7 @@ func (kpr *Keyper) newBlock(_ context.Context, ev *shopevent.LatestBlock) error func (kpr *Keyper) newKeyperSet(ctx context.Context, ev *shopevent.KeyperSet) error { log.Info(). Uint64("activation-block", ev.ActivationBlock). + Uint64("eon", ev.Eon). Msg("new keyper set added") return kpr.dbpool.BeginFunc(ctx, func(tx pgx.Tx) error { db := obskeyper.New(tx) @@ -140,6 +139,10 @@ func (kpr *Keyper) newKeyperSet(ctx context.Context, ev *shopevent.KeyperSet) er } func (kpr *Keyper) newEonPublicKey(ctx context.Context, pk keyper.EonPublicKey) error { + log.Info(). + Uint64("eon", pk.Eon). + Uint64("activation-block", pk.ActivationBlock). + Msg("new eon pk") // TODO: post the public key to the contract return nil } diff --git a/rolling-shutter/keyperimpl/optimism/sync/options.go b/rolling-shutter/keyperimpl/optimism/sync/options.go index c5b011590..7bfd10321 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/options.go +++ b/rolling-shutter/keyperimpl/optimism/sync/options.go @@ -115,6 +115,9 @@ func (o *options) apply(ctx context.Context, c *ShutterL2Client) error { Handler: o.handlerBlock, } } + if o.handlerBlock != nil { + c.services = append(c.services, c.uhsync) + } return nil } From 59060e5f45927d21503ced971b16dd185aa7592d Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Thu, 21 Dec 2023 22:59:25 +0100 Subject: [PATCH 12/91] fix: chain init and tendermint config option --- rolling-shutter/cmd/chain/init.go | 54 ++++++++++--------- .../docs/rolling-shutter_chain_init.md | 2 +- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/rolling-shutter/cmd/chain/init.go b/rolling-shutter/cmd/chain/init.go index 9bf2bdd53..d77a28089 100644 --- a/rolling-shutter/cmd/chain/init.go +++ b/rolling-shutter/cmd/chain/init.go @@ -7,6 +7,7 @@ import ( "fmt" "os" "path/filepath" + "slices" "strconv" "strings" "time" @@ -27,7 +28,12 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/app" ) -const VALIDATOR = "validator" +const ( + VALIDATOR = "validator" + ISOLATEDVALIDATOR = "isolated-validator" + SENTRY = "sentry" + SEED = "seed" +) type Config struct { RootDir string `mapstructure:"root"` @@ -65,7 +71,7 @@ func initCmd() *cobra.Command { cmd.PersistentFlags().Float64("blocktime", 1.0, "block time in seconds") cmd.PersistentFlags().StringSlice("genesis-keyper", nil, "genesis keyper address") cmd.PersistentFlags().String("listen-address", "tcp://127.0.0.1:26657", "tendermint RPC listen address") - cmd.PersistentFlags().String("role", "validator", "tendermint node role (validator, sentry, seed)") + cmd.PersistentFlags().String("role", "validator", "tendermint node role (validator, isolated-validator, sentry, seed)") cmd.PersistentFlags().Uint64("initial-eon", 0, "initial eon") return cmd } @@ -95,7 +101,7 @@ func getArgFromViper[T interface{}](getter func(string) T, name string, required func initFiles(_ *cobra.Command, config *Config, _ []string) error { keypers := []common.Address{} - if config.Role == VALIDATOR { + if slices.Contains([]string{VALIDATOR, ISOLATEDVALIDATOR}, config.Role) { for _, a := range config.GenesisKeyper { if !common.IsHexAddress(a) { return errors.Errorf("--genesis-keyper argument '%s' is not an address", a) @@ -130,7 +136,6 @@ func initFiles(_ *cobra.Command, config *Config, _ []string) error { return errors.Wrap(err, "error in config file") } cfg.EnsureRoot(tendermintCfg.RootDir) - // set up according to the network role: https://docs.tendermint.com/v0.34/tendermint-core/validators.html switch config.Role { case VALIDATOR: // standard validator mode, network exposed @@ -139,10 +144,10 @@ func initFiles(_ *cobra.Command, config *Config, _ []string) error { case ISOLATEDVALIDATOR: // validator mode behind a sentry node tendermintCfg.P2P.PexReactor = false tendermintCfg.P2P.AddrBookStrict = false - case "sentry": + case SENTRY: tendermintCfg.P2P.PexReactor = true tendermintCfg.P2P.AddrBookStrict = false - case "seed": + case SEED: tendermintCfg.P2P.PexReactor = true tendermintCfg.P2P.AddrBookStrict = false default: @@ -173,24 +178,25 @@ func adjustPort(address string, keyperIndex int) (string, error) { func initFilesWithConfig(tendermintConfig *cfg.Config, config *Config, appState app.GenesisAppState) error { var err error - // private validator - privValKeyFile := tendermintConfig.PrivValidatorKeyFile() - privValStateFile := tendermintConfig.PrivValidatorStateFile() - var pv *privval.FilePV - if tmos.FileExists(privValKeyFile) { - pv = privval.LoadFilePV(privValKeyFile, privValStateFile) - log.Info(). - Str("privValKeyFile", privValKeyFile). - Str("stateFile", privValStateFile). - Msg("Found private validator") - } else { - pv = privval.GenFilePV(privValKeyFile, privValStateFile) - pv.Save() - log.Info(). - Str("privValKeyFile", privValKeyFile). - Str("stateFile", privValStateFile). - Msg("Generated private validator") - } + if slices.Contains([]string{VALIDATOR, ISOLATEDVALIDATOR, SEED}, config.Role) { + // private validator + privValKeyFile := tendermintConfig.PrivValidatorKeyFile() + privValStateFile := tendermintConfig.PrivValidatorStateFile() + var pv *privval.FilePV + if tmos.FileExists(privValKeyFile) { + pv = privval.LoadFilePV(privValKeyFile, privValStateFile) + log.Info(). + Str("privValKeyFile", privValKeyFile). + Str("stateFile", privValStateFile). + Msg("Found private validator") + } else { + pv = privval.GenFilePV(privValKeyFile, privValStateFile) + pv.Save() + log.Info(). + Str("privValKeyFile", privValKeyFile). + Str("stateFile", privValStateFile). + Msg("Generated private validator") + } validatorPubKeyPath := filepath.Join(tendermintConfig.RootDir, "config", "priv_validator_pubkey.hex") validatorPublicKeyHex := hex.EncodeToString(pv.Key.PubKey.Bytes()) diff --git a/rolling-shutter/docs/rolling-shutter_chain_init.md b/rolling-shutter/docs/rolling-shutter_chain_init.md index a6b11a425..ab8813509 100644 --- a/rolling-shutter/docs/rolling-shutter_chain_init.md +++ b/rolling-shutter/docs/rolling-shutter_chain_init.md @@ -16,7 +16,7 @@ rolling-shutter chain init [flags] --index int keyper index --initial-eon uint initial eon --listen-address string tendermint RPC listen address (default "tcp://127.0.0.1:26657") - --role string tendermint node role (validator, sentry, seed) (default "validator") + --role string tendermint node role (validator, isolated-validator, sentry, seed) (default "validator") --root string root directory ``` From a60999cc4c598d6af3a5e75f9c6253f68bfa5ac4 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Wed, 3 Jan 2024 16:14:36 +0100 Subject: [PATCH 13/91] chore: update dependencies --- rolling-shutter/go.mod | 17 ++++++++++------- rolling-shutter/go.sum | 33 +++++++++++++++++++++++++++------ 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/rolling-shutter/go.mod b/rolling-shutter/go.mod index 5e9c7b693..140f0135e 100644 --- a/rolling-shutter/go.mod +++ b/rolling-shutter/go.mod @@ -2,9 +2,7 @@ module github.com/shutter-network/rolling-shutter/rolling-shutter go 1.21.4 -replace github.com/ethereum-optimism/optimism v1.1.6-rc.2 => ../../shoptimism/ - -replace github.com/ethereum/go-ethereum v1.13.1 => ../../shop-geth/ +//replace github.com/ethereum/go-ethereum v1.13.5 => ../../shop-geth/ require ( github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 @@ -56,9 +54,11 @@ require ( require ( github.com/DataDog/zstd v1.5.2 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/allegro/bigcache v1.2.1 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect github.com/cespare/xxhash v1.1.0 // indirect @@ -68,10 +68,13 @@ require ( github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cosmos/gogoproto v1.4.1 // indirect github.com/cosmos/gorocksdb v1.2.0 // indirect + github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/creachadair/taskgroup v0.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect @@ -83,6 +86,7 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/elastic/gosigar v0.14.2 // indirect + github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/fjl/memsize v0.0.1 // indirect github.com/flynn/noise v1.0.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect @@ -103,7 +107,6 @@ require ( github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.0.1 // indirect @@ -172,6 +175,7 @@ require ( github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/minio/sha256-simd v1.0.1 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect @@ -210,6 +214,7 @@ require ( github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect + github.com/supranational/blst v0.3.11 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect github.com/tendermint/tm-db v0.6.7 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect @@ -235,11 +240,9 @@ require ( google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/grpc v1.56.2 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect lukechampine.com/blake3 v1.2.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect ) replace github.com/bitwurx/jrpc2 => github.com/ulope/jrpc2 v0.0.0-20230706135348-a95cf3d96bd2 - -replace github.com/ethereum/go-ethereum => github.com/ethereum/go-ethereum v1.12.0 diff --git a/rolling-shutter/go.sum b/rolling-shutter/go.sum index c7cf3b768..a7869a7d7 100644 --- a/rolling-shutter/go.sum +++ b/rolling-shutter/go.sum @@ -78,6 +78,8 @@ github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZx github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= +github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= @@ -118,6 +120,10 @@ github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwP github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= @@ -145,6 +151,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creachadair/taskgroup v0.3.2 h1:zlfutDS+5XG40AOxcHDSThxKzns8Tnr9jnr6VqkYlkM= github.com/creachadair/taskgroup v0.3.2/go.mod h1:wieWwecHVzsidg2CsUnFinW1faVN4+kq+TDlRJQ0Wbk= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= @@ -190,8 +198,10 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.12.0 h1:bdnhLPtqETd4m3mS8BGMNvBTf36bO5bx/hxE2zljOa0= -github.com/ethereum/go-ethereum v1.12.0/go.mod h1:/oo2X/dZLJjf2mJ6YT9wcWxa4nNJDBKDBU6sFIpx1Gs= +github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk= +github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= @@ -284,6 +294,7 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= @@ -369,6 +380,7 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0= github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -405,6 +417,8 @@ github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvH github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= @@ -537,6 +551,8 @@ github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= @@ -641,6 +657,9 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQhoHZAz4x7TIw= github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -869,6 +888,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= @@ -894,8 +915,8 @@ github.com/ulope/jrpc2 v0.0.0-20230706135348-a95cf3d96bd2/go.mod h1:bzOCUO4YLqjP github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.10 h1:p8Fspmz3iTctJstry1PYS3HVdllxnEzTEsgIgtxTrCk= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bCzK15ozrqo2sZxkh0FHynJZOTVoV6Q= -github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= +github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= +github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= @@ -1407,8 +1428,6 @@ gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1440,5 +1459,7 @@ lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= From e4a5f7d2e0f8575ba32e1212e2871c473227c7b4 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Wed, 3 Jan 2024 17:41:57 +0100 Subject: [PATCH 14/91] fix(op): missing assignment to optimism-keyper field The 'newKeyperSet' method defined on the optimism keyper struct was using the dbpool field without it being assigned during the startup phase. This resulted in a nil-pointer dereference whenever a new keyperset was observed onchain. --- rolling-shutter/keyperimpl/optimism/keyper.go | 1 + 1 file changed, 1 insertion(+) diff --git a/rolling-shutter/keyperimpl/optimism/keyper.go b/rolling-shutter/keyperimpl/optimism/keyper.go index 7eaad4083..562321331 100644 --- a/rolling-shutter/keyperimpl/optimism/keyper.go +++ b/rolling-shutter/keyperimpl/optimism/keyper.go @@ -48,6 +48,7 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { if err != nil { return errors.Wrap(err, "failed to connect to database") } + kpr.dbpool = dbpool // TODO: the new latest block handler function will put values into this channel trigger := make(chan *broker.Event[*epochkghandler.DecryptionTrigger]) From 7616a5e571e5ee1e1ffd247d59d680ae1eb3318c Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Thu, 4 Jan 2024 10:28:01 +0100 Subject: [PATCH 15/91] chore(op): remove lib shadow --- rolling-shutter/keyperimpl/optimism/keyper.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rolling-shutter/keyperimpl/optimism/keyper.go b/rolling-shutter/keyperimpl/optimism/keyper.go index 562321331..33298556a 100644 --- a/rolling-shutter/keyperimpl/optimism/keyper.go +++ b/rolling-shutter/keyperimpl/optimism/keyper.go @@ -115,7 +115,7 @@ func (kpr *Keyper) newKeyperSet(ctx context.Context, ev *shopevent.KeyperSet) er Uint64("eon", ev.Eon). Msg("new keyper set added") return kpr.dbpool.BeginFunc(ctx, func(tx pgx.Tx) error { - db := obskeyper.New(tx) + obskeyperdb := obskeyper.New(tx) keyperConfigIndex, err := medley.Uint64ToInt64Safe(ev.Eon) if err != nil { @@ -130,7 +130,7 @@ func (kpr *Keyper) newKeyperSet(ctx context.Context, ev *shopevent.KeyperSet) er return errors.Wrap(err, ErrParseKeyperSet.Error()) } // XXX: does this work when the memberset is empty? - return db.InsertKeyperSet(ctx, obskeyper.InsertKeyperSetParams{ + return obskeyperdb.InsertKeyperSet(ctx, obskeyper.InsertKeyperSetParams{ KeyperConfigIndex: keyperConfigIndex, ActivationBlockNumber: activationBlockNumber, Keypers: shdb.EncodeAddresses(ev.Members), From 82e9b1548d17273e6dbf1882fece62b80f681f5a Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Fri, 5 Jan 2024 15:27:32 +0100 Subject: [PATCH 16/91] feat(op): allow to retrieve keyperset by index for op-bootstrap --- .../keyperimpl/optimism/bootstrap/config.go | 13 +++++---- .../optimism/bootstrap/keyperset.go | 16 ++++++++++- .../keyperimpl/optimism/sync/client.go | 10 +++++++ .../optimism/sync/syncer/keyperset.go | 27 ++++++++++++------- 4 files changed, 51 insertions(+), 15 deletions(-) diff --git a/rolling-shutter/keyperimpl/optimism/bootstrap/config.go b/rolling-shutter/keyperimpl/optimism/bootstrap/config.go index f6c4c8f66..decfb0d30 100644 --- a/rolling-shutter/keyperimpl/optimism/bootstrap/config.go +++ b/rolling-shutter/keyperimpl/optimism/bootstrap/config.go @@ -19,15 +19,16 @@ func NewConfig() *Config { func (c *Config) Init() { c.SigningKey = &keys.ECDSAPrivate{} - c.ActivationBlockNumber = number.NewBlockNumber() + c.ByActivationBlockNumber = number.NewBlockNumber() } type Config struct { InstanceID uint64 `shconfig:",required"` - JSONRPCURL string ` comment:"The op-geth JSON RPC endpoint"` - ActivationBlockNumber *number.BlockNumber - KeyperSetFilePath string + JSONRPCURL string ` comment:"The op-geth JSON RPC endpoint"` + ByActivationBlockNumber *number.BlockNumber + ByIndex *uint64 + KeyperSetFilePath string ShuttermintURL string SigningKey *keys.ECDSAPrivate `shconfig:",required"` @@ -45,7 +46,9 @@ func (c *Config) SetDefaultValues() error { c.JSONRPCURL = "http://localhost:8545" c.ShuttermintURL = "http://localhost:26657" c.KeyperSetFilePath = "keyperset.json" - c.ActivationBlockNumber = number.LatestBlock + c.ByActivationBlockNumber = nil + i := uint64(1) + c.ByIndex = &i return nil } diff --git a/rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go b/rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go index 9bd99cc3d..fb413d631 100644 --- a/rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go +++ b/rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go @@ -3,9 +3,11 @@ package bootstrap import ( "context" "encoding/json" + "errors" "os" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" ) @@ -17,7 +19,19 @@ func GetKeyperSet(ctx context.Context, config *Config) error { if err != nil { return err } - keyperSet, err := sl2.GetKeyperSetForBlock(ctx, number.LatestBlock) + var keyperSet *event.KeyperSet + switch { + case config.ByIndex == nil && config.ByActivationBlockNumber == nil: + keyperSet, err = sl2.GetKeyperSetForBlock(ctx, number.LatestBlock) + case config.ByIndex == nil && config.ByActivationBlockNumber != nil: + keyperSet, err = sl2.GetKeyperSetForBlock(ctx, config.ByActivationBlockNumber) + case config.ByIndex != nil && config.ByActivationBlockNumber == nil: + keyperSet, err = sl2.GetKeyperSetByIndex(ctx, *config.ByIndex) + case config.ByIndex != nil && config.ByActivationBlockNumber != nil: + return errors.New("can only retrieve keyper set by either index or activation-blocknumber") + default: + return nil + } if err != nil { return err } diff --git a/rolling-shutter/keyperimpl/optimism/sync/client.go b/rolling-shutter/keyperimpl/optimism/sync/client.go index d0a0c43c9..a29dcaebc 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/client.go +++ b/rolling-shutter/keyperimpl/optimism/sync/client.go @@ -104,6 +104,16 @@ func (s *ShutterL2Client) GetShutterState(ctx context.Context) (*event.ShutterSt return s.sssync.GetShutterState(ctx, opts) } +func (s *ShutterL2Client) GetKeyperSetByIndex(ctx context.Context, index uint64) (*event.KeyperSet, error) { + if s.kssync == nil { + return nil, errors.Wrap(ErrServiceNotInstantiated, "KeyperSetSyncer service not instantiated") + } + opts := &bind.CallOpts{ + Context: ctx, + } + return s.kssync.GetKeyperSetByIndex(ctx, opts, index) +} + func (s *ShutterL2Client) GetKeyperSetForBlock(ctx context.Context, b *number.BlockNumber) (*event.KeyperSet, error) { if s.kssync == nil { return nil, errors.Wrap(ErrServiceNotInstantiated, "KeyperSetSyncer service not instantiated") diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go index 0a88d6019..97e7cee2f 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go @@ -55,6 +55,23 @@ func (s *KeyperSetSyncer) Start(ctx context.Context, runner service.Runner) erro return nil } +func (s *KeyperSetSyncer) GetKeyperSetByIndex(ctx context.Context, opts *bind.CallOpts, index uint64) (*event.KeyperSet, error) { + if opts == nil { + opts = &bind.CallOpts{ + Context: ctx, + } + } + actBl, err := s.Contract.GetKeyperSetActivationBlock(opts, index) + if err != nil { + return nil, errors.Wrap(err, "could not retrieve keyper set activation block") + } + addr, err := s.Contract.GetKeyperSetAddress(opts, index) + if err != nil { + return nil, errors.Wrap(err, "could not retrieve keyper set address") + } + return s.newEvent(ctx, opts, addr, actBl) +} + func (s *KeyperSetSyncer) GetKeyperSetForBlock(ctx context.Context, opts *bind.CallOpts, b *number.BlockNumber) (*event.KeyperSet, error) { if b.Equal(number.LatestBlock) { latestBlock, err := s.Client.BlockNumber(ctx) @@ -73,15 +90,7 @@ func (s *KeyperSetSyncer) GetKeyperSetForBlock(ctx context.Context, opts *bind.C if err != nil { return nil, errors.Wrap(err, "could not retrieve keyper set index") } - actBl, err := s.Contract.GetKeyperSetActivationBlock(opts, idx) - if err != nil { - return nil, errors.Wrap(err, "could not retrieve keyper set activation block") - } - addr, err := s.Contract.GetKeyperSetAddress(opts, idx) - if err != nil { - return nil, errors.Wrap(err, "could not retrieve keyper set address") - } - return s.newEvent(ctx, opts, addr, actBl) + return s.GetKeyperSetByIndex(ctx, opts, idx) } func (s *KeyperSetSyncer) newEvent( From 4a63d526c27026a47ea149f2ef801211373e4a61 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Wed, 10 Jan 2024 17:48:01 +0100 Subject: [PATCH 17/91] chore(op): improve sync client --- .../keyperimpl/optimism/bootstrap/config.go | 2 +- rolling-shutter/keyperimpl/optimism/keyper.go | 46 +++++++--- .../keyperimpl/optimism/sync/client.go | 61 ++++++++----- .../keyperimpl/optimism/sync/options.go | 35 ++++++-- .../optimism/sync/syncer/eonpubkey.go | 68 ++++++++++++-- .../optimism/sync/syncer/keyperset.go | 90 +++++++++++++++---- .../optimism/sync/syncer/shutterstate.go | 5 +- .../medley/encodeable/number/block.go | 22 ++++- 8 files changed, 262 insertions(+), 67 deletions(-) diff --git a/rolling-shutter/keyperimpl/optimism/bootstrap/config.go b/rolling-shutter/keyperimpl/optimism/bootstrap/config.go index decfb0d30..6f0381d28 100644 --- a/rolling-shutter/keyperimpl/optimism/bootstrap/config.go +++ b/rolling-shutter/keyperimpl/optimism/bootstrap/config.go @@ -19,7 +19,7 @@ func NewConfig() *Config { func (c *Config) Init() { c.SigningKey = &keys.ECDSAPrivate{} - c.ByActivationBlockNumber = number.NewBlockNumber() + c.ByActivationBlockNumber = number.NewBlockNumber(nil) } type Config struct { diff --git a/rolling-shutter/keyperimpl/optimism/keyper.go b/rolling-shutter/keyperimpl/optimism/keyper.go index 33298556a..6e91043d6 100644 --- a/rolling-shutter/keyperimpl/optimism/keyper.go +++ b/rolling-shutter/keyperimpl/optimism/keyper.go @@ -3,6 +3,7 @@ package optimism import ( "context" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/pgxpool" "github.com/pkg/errors" @@ -28,9 +29,10 @@ import ( var ErrParseKeyperSet = errors.New("can't parse KeyperSet") type Keyper struct { - core *keyper.KeyperCore - dbpool *pgxpool.Pool - config *config.Config + core *keyper.KeyperCore + l2Client *shopclient.ShutterL2Client + dbpool *pgxpool.Pool + config *config.Config trigger chan<- *broker.Event[*epochkghandler.DecryptionTrigger] } @@ -50,7 +52,6 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { } kpr.dbpool = dbpool - // TODO: the new latest block handler function will put values into this channel trigger := make(chan *broker.Event[*epochkghandler.DecryptionTrigger]) kpr.trigger = trigger @@ -77,17 +78,17 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { return errors.Wrap(err, "can't instantiate keyper core") } // TODO: wrap the logger and pass in - l2Client, err := shopclient.NewShutterL2Client( + kpr.l2Client, err = shopclient.NewShutterL2Client( ctx, shopclient.WithClientURL(kpr.config.Optimism.JSONRPCURL), shopclient.WithSyncNewBlock(kpr.newBlock), shopclient.WithSyncNewKeyperSet(kpr.newKeyperSet), + shopclient.WithPrivateKey(kpr.config.Optimism.PrivateKey.Key), ) - // TODO: how to deal with polling past state? (sounds like a big addition to the l2Client) if err != nil { return err } - return runner.StartService(kpr.core, l2Client) + return runner.StartService(kpr.core, kpr.l2Client) } func (kpr *Keyper) newBlock(_ context.Context, ev *shopevent.LatestBlock) error { @@ -139,11 +140,34 @@ func (kpr *Keyper) newKeyperSet(ctx context.Context, ev *shopevent.KeyperSet) er }) } -func (kpr *Keyper) newEonPublicKey(ctx context.Context, pk keyper.EonPublicKey) error { +func (kpr *Keyper) newEonPublicKey(ctx context.Context, pubKey keyper.EonPublicKey) error { log.Info(). - Uint64("eon", pk.Eon). - Uint64("activation-block", pk.ActivationBlock). + Uint64("eon", pubKey.Eon). + Uint64("activation-block", pubKey.ActivationBlock). Msg("new eon pk") - // TODO: post the public key to the contract + // Currently all keypers call this and race to call this function first. + // For now this is fine, but a keyper should only send a transaction if + // the key is not set yet. + // Best would be a coordinatated leader election who will broadcast the key. + tx, err := kpr.l2Client.BroadcastEonKey(ctx, pubKey.Eon, pubKey.PublicKey) + if err != nil { + log.Error().Err(err).Msg("error broadcasting eon public key") + return errors.Wrap(err, "error broadcasting eon public-key") + } + log.Info(). + Str("hash", tx.Hash().Hex()). + Msg("sent eon pubkey transaction") + + receipt, err := bind.WaitMined(ctx, kpr.l2Client, tx) + if err != nil { + log.Error().Err(err).Msg("error waiting for pubkey tx mined") + return err + } + // NOCHECKIN: log the JSON receipt or only specific fields + log.Info(). + Interface("receipt", receipt). + Msg("eon pubkey transaction mined") + // TODO: + // wait / confirm of tx, otherwise resend return nil } diff --git a/rolling-shutter/keyperimpl/optimism/sync/client.go b/rolling-shutter/keyperimpl/optimism/sync/client.go index a29dcaebc..460c7b2d8 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/client.go +++ b/rolling-shutter/keyperimpl/optimism/sync/client.go @@ -2,9 +2,12 @@ package sync import ( "context" + "crypto/ecdsa" "io" + "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/pkg/errors" "github.com/shutter-network/shop-contracts/bindings" @@ -32,6 +35,8 @@ type ShutterL2Client struct { log log.Logger options *options + chainID *big.Int + privKey *ecdsa.PrivateKey KeyperSetManager *bindings.KeyperSetManager KeyBroadcast *bindings.KeyBroadcastContract @@ -74,26 +79,6 @@ func (s *ShutterL2Client) getServices() []service.Service { return s.services } -// func syncInitial() { -// if s.ForceEmitActiveKeyperSet { -// var b *number.BlockNumber -// if s.StartBlock == nil { -// b = number.LatestBlock -// } else { -// b = number.NewBlockNumber() -// b.SetInt64(int64(*s.StartBlock)) -// } -// activeKSAddred, err := s.getKeyperSetForBlock(ctx, b) -// if err != nil { -// return err -// } -// err = s.handler(activeKSAddred) -// if err != nil { -// return errors.Wrap(err, "handling of forced emission of active keyper set failed") -// } -// } -// } - func (s *ShutterL2Client) GetShutterState(ctx context.Context) (*event.ShutterState, error) { if s.sssync == nil { return nil, errors.Wrap(ErrServiceNotInstantiated, "ShutterStateSyncer service not instantiated") @@ -126,7 +111,7 @@ func (s *ShutterL2Client) GetKeyperSetForBlock(ctx context.Context, b *number.Bl func (s *ShutterL2Client) GetEonPubKeyForEon(ctx context.Context, eon uint64) (*event.EonPublicKey, error) { if s.sssync == nil { - return nil, errors.Wrap(ErrServiceNotInstantiated, "EonPubKeySyncer service not instantiated") + return nil, errors.Wrap(ErrServiceNotInstantiated, "ShutterStateSyncer service not instantiated") } opts := &bind.CallOpts{ Context: ctx, @@ -134,6 +119,38 @@ func (s *ShutterL2Client) GetEonPubKeyForEon(ctx context.Context, eon uint64) (* return s.epksync.GetEonPubKeyForEon(ctx, opts, eon) } -func (s *ShutterL2Client) Start(ctx context.Context, runner service.Runner) error { +func (s *ShutterL2Client) BroadcastEonKey(ctx context.Context, eon uint64, eonPubKey []byte) (*types.Transaction, error) { + // TODO: first do a getEonKey. If we already have something (ideally the same) + // don't do a transaction + // s.KeyBroadcast.GetEonKey(eon) + if s.privKey == nil { + return nil, errors.New("can't broadcast eon public-key, client does not have a signer set") + } + chainID, err := s.ChainID(ctx) + if err != nil { + return nil, errors.Wrap(err, "retrieve chain id") + } + opts, err := bind.NewKeyedTransactorWithChainID(s.privKey, chainID) + if err != nil { + return nil, errors.Wrap(err, "construct signer transaction opts") + } + opts.Context = ctx + return s.KeyBroadcast.BroadcastEonKey(opts, eon, eonPubKey) +} + +// ChainID returns the chainid of the underlying L2 chain. +// This value is cached, since it is not expected to change. +func (s *ShutterL2Client) ChainID(ctx context.Context) (*big.Int, error) { + if s.chainID == nil { + cid, err := s.Client.ChainID(ctx) + if err != nil { + return nil, err + } + s.chainID = cid + } + return s.chainID, nil +} + +func (s *ShutterL2Client) Start(_ context.Context, runner service.Runner) error { return runner.StartService(s.getServices()...) } diff --git a/rolling-shutter/keyperimpl/optimism/sync/options.go b/rolling-shutter/keyperimpl/optimism/sync/options.go index 7bfd10321..1ee0abff6 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/options.go +++ b/rolling-shutter/keyperimpl/optimism/sync/options.go @@ -2,17 +2,19 @@ package sync import ( "context" - "errors" + "crypto/ecdsa" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" + "github.com/pkg/errors" "github.com/shutter-network/shop-contracts/bindings" "github.com/shutter-network/shop-contracts/predeploy" syncclient "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/syncer" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" ) @@ -25,7 +27,8 @@ type options struct { client syncclient.Client logger log.Logger runner service.Runner - syncStart *uint64 + syncStart *number.BlockNumber + privKey *ecdsa.PrivateKey handlerShutterState event.ShutterStateHandler handlerKeyperSet event.KeyperSetHandler @@ -67,6 +70,16 @@ func (o *options) apply(ctx context.Context, c *ShutterL2Client) error { c.Client = client + // the nil passthrough will use "latest" for each call, + // but we want to harmonize and fix the sync start to a specific block. + if o.syncStart.IsLatest() { + latestBlock, err := c.Client.BlockNumber(ctx) + if err != nil { + return errors.Wrap(err, "polling latest block") + } + o.syncStart = number.NewBlockNumber(&latestBlock) + } + c.KeyperSetManager, err = bindings.NewKeyperSetManager(*o.keyperSetManagerAddress, client) if err != nil { return err @@ -118,6 +131,7 @@ func (o *options) apply(ctx context.Context, c *ShutterL2Client) error { if o.handlerBlock != nil { c.services = append(c.services, c.uhsync) } + c.privKey = o.privKey return nil } @@ -129,14 +143,16 @@ func defaultOptions() *options { client: nil, logger: noopLogger, runner: nil, - syncStart: nil, + syncStart: number.NewBlockNumber(nil), } } -func WithSyncStartBlock(blockNumber uint64) Option { +func WithSyncStartBlock(blockNumber *number.BlockNumber) Option { + if blockNumber == nil { + blockNumber = number.NewBlockNumber(nil) + } return func(o *options) error { - bn := blockNumber - o.syncStart = &bn + o.syncStart = blockNumber return nil } } @@ -183,6 +199,13 @@ func WithClient(client syncclient.Client) Option { } } +func WithPrivateKey(key *ecdsa.PrivateKey) Option { + return func(o *options) error { + o.privKey = key + return nil + } +} + func WithSyncNewKeyperSet(handler event.KeyperSetHandler) Option { return func(o *options) error { o.handlerKeyperSet = handler diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go index 0c323e8de..27c9598e9 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go @@ -11,6 +11,7 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" ) @@ -18,36 +19,91 @@ type EonPubKeySyncer struct { Client client.Client Log log.Logger Contract *bindings.KeyBroadcastContract - StartBlock *uint64 + StartBlock *number.BlockNumber Handler event.EonPublicKeyHandler keyBroadcastCh chan *bindings.KeyBroadcastContractEonKeyBroadcast + ksManager *bindings.KeyperSetManager } func (s *EonPubKeySyncer) Start(ctx context.Context, runner service.Runner) error { if s.Handler == nil { return errors.New("no handler registered") } + // the latest block still has to be fixed. + // otherwise we could skip some block events + // between the initial poll and the subscription. + if s.StartBlock.IsLatest() { + latest, err := s.Client.BlockNumber(ctx) + if err != nil { + return err + } + s.StartBlock.SetUint64(latest) + } + pubKs, err := s.getInitialPubKeys(ctx) + if err != nil { + return err + } + for _, k := range pubKs { + err := s.Handler(ctx, k) + if err != nil { + return err + } + } + watchOpts := &bind.WatchOpts{ - Start: s.StartBlock, // nil means latest + Start: s.StartBlock.ToUInt64Ptr(), Context: ctx, } - s.keyBroadcastCh = make(chan *bindings.KeyBroadcastContractEonKeyBroadcast, 10) + s.keyBroadcastCh = make(chan *bindings.KeyBroadcastContractEonKeyBroadcast, channelSize) + runner.Defer(func() { + close(s.keyBroadcastCh) + }) subs, err := s.Contract.WatchEonKeyBroadcast(watchOpts, s.keyBroadcastCh) // FIXME: what to do on subs.Error() if err != nil { return err } runner.Defer(subs.Unsubscribe) - runner.Defer(func() { - close(s.keyBroadcastCh) - }) runner.Go(func() error { return s.watchNewEonPubkey(ctx) }) return nil } +func (s *EonPubKeySyncer) getInitialPubKeys(ctx context.Context) ([]*event.EonPublicKey, error) { + // This blocknumber specifies AT what state + // the contract is called + // XXX: does the call-opts blocknumber -1 also means latest? + opts := &bind.CallOpts{ + Context: ctx, + BlockNumber: s.StartBlock.Int, + } + numKS, err := s.ksManager.GetNumKeyperSets(opts) + if err != nil { + return nil, err + } + // this blocknumber specifies the argument to the contract + // getter + activeEon, err := s.ksManager.GetKeyperSetIndexByBlock(opts, s.StartBlock.Uint64()) + if err != nil { + return nil, err + } + + initialPubKeys := []*event.EonPublicKey{} + for i := activeEon; i < numKS; i++ { + e, err := s.GetEonPubKeyForEon(ctx, opts, i) + // FIXME: translate the error that there is no key + // to a continue of the loop + // (key not in mapping error, how can we catch that?) + if err != nil { + return nil, err + } + initialPubKeys = append(initialPubKeys, e) + } + return initialPubKeys, nil +} + func (s *EonPubKeySyncer) logCallError(attrName string, err error) { s.Log.Error( fmt.Sprintf("could not retrieve `%s` from contract", attrName), diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go index 97e7cee2f..6201979d2 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go @@ -19,15 +19,16 @@ func makeCallError(attrName string, err error) error { return errors.Wrapf(err, "could not retrieve `%s` from contract", attrName) } +const channelSize = 10 + type KeyperSetSyncer struct { Client client.Client Contract *bindings.KeyperSetManager Log log.Logger - StartBlock *uint64 + StartBlock *number.BlockNumber Handler event.KeyperSetHandler - keyperAddedCh chan *bindings.KeyperSetManagerKeyperSetAdded - handlerScheduler chan *event.KeyperSet + keyperAddedCh chan *bindings.KeyperSetManagerKeyperSetAdded } func (s *KeyperSetSyncer) Start(ctx context.Context, runner service.Runner) error { @@ -35,26 +36,84 @@ func (s *KeyperSetSyncer) Start(ctx context.Context, runner service.Runner) erro return errors.New("no handler registered") } + // the latest block still has to be fixed. + // otherwise we could skip some block events + // between the initial poll and the subscription. + if s.StartBlock.IsLatest() { + latest, err := s.Client.BlockNumber(ctx) + if err != nil { + return err + } + s.StartBlock.SetUint64(latest) + } + watchOpts := &bind.WatchOpts{ - Start: s.StartBlock, // nil means latest + Start: s.StartBlock.ToUInt64Ptr(), Context: ctx, } - s.keyperAddedCh = make(chan *bindings.KeyperSetManagerKeyperSetAdded, 10) - subs, err := s.Contract.WatchKeyperSetAdded(watchOpts, s.keyperAddedCh) - // FIXME: what to do on subs.Error() + initial, err := s.getInitialKeyperSets(ctx) if err != nil { return err } - runner.Defer(subs.Unsubscribe) + for _, ks := range initial { + err = s.Handler(ctx, ks) + if err != nil { + s.Log.Error( + "handler for `NewKeyperSet` errored for initial sync", + "error", + err.Error(), + ) + } + } + s.keyperAddedCh = make(chan *bindings.KeyperSetManagerKeyperSetAdded, channelSize) runner.Defer(func() { close(s.keyperAddedCh) }) + subs, err := s.Contract.WatchKeyperSetAdded(watchOpts, s.keyperAddedCh) + // FIXME: what to do on subs.Error() + if err != nil { + return err + } + runner.Defer(subs.Unsubscribe) runner.Go(func() error { return s.watchNewKeypersService(ctx) }) return nil } +func (s *KeyperSetSyncer) getInitialKeyperSets(ctx context.Context) ([]*event.KeyperSet, error) { + // This blocknumber specifies AT what state + // the contract is called + // XXX: does the call-opts blocknumber -1 also means latest? + opts := &bind.CallOpts{ + Context: ctx, + BlockNumber: s.StartBlock.Int, + } + numKS, err := s.Contract.GetNumKeyperSets(opts) + if err != nil { + return nil, err + } + + initialKeyperSets := []*event.KeyperSet{} + // this blocknumber specifies the argument to the contract + // getter + ks, err := s.GetKeyperSetForBlock(ctx, nil, s.StartBlock) + if err != nil { + return nil, err + } + initialKeyperSets = append(initialKeyperSets, ks) + + for i := ks.Eon + 1; i < numKS; i++ { + ks, err = s.GetKeyperSetByIndex(ctx, opts, i) + if err != nil { + return nil, err + } + initialKeyperSets = append(initialKeyperSets, ks) + } + + return initialKeyperSets, nil +} + func (s *KeyperSetSyncer) GetKeyperSetByIndex(ctx context.Context, opts *bind.CallOpts, index uint64) (*event.KeyperSet, error) { if opts == nil { opts = &bind.CallOpts{ @@ -73,20 +132,21 @@ func (s *KeyperSetSyncer) GetKeyperSetByIndex(ctx context.Context, opts *bind.Ca } func (s *KeyperSetSyncer) GetKeyperSetForBlock(ctx context.Context, opts *bind.CallOpts, b *number.BlockNumber) (*event.KeyperSet, error) { + var latestBlock uint64 + var err error + if b.Equal(number.LatestBlock) { - latestBlock, err := s.Client.BlockNumber(ctx) - if err != nil { - return nil, err - } - b = number.NewBlockNumber() - b.SetInt64(int64(latestBlock)) + latestBlock, err = s.Client.BlockNumber(ctx) + } else { + latestBlock = b.Uint64() } if opts == nil { + // call at "latest block" state opts = &bind.CallOpts{ Context: ctx, } } - idx, err := s.Contract.GetKeyperSetIndexByBlock(opts, b.Uint64()) + idx, err := s.Contract.GetKeyperSetIndexByBlock(opts, latestBlock) if err != nil { return nil, errors.Wrap(err, "could not retrieve keyper set index") } diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go index da9941e71..ab2de559f 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go @@ -10,13 +10,14 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" ) type ShutterStateSyncer struct { Client client.Client Contract *bindings.KeyperSetManager - StartBlock *uint64 + StartBlock *number.BlockNumber Log log.Logger Handler event.ShutterStateHandler @@ -44,7 +45,7 @@ func (s *ShutterStateSyncer) Start(ctx context.Context, runner service.Runner) e return errors.New("no handler registered") } watchOpts := &bind.WatchOpts{ - Start: s.StartBlock, // nil means latest + Start: s.StartBlock.ToUInt64Ptr(), // nil means latest Context: ctx, } s.pausedCh = make(chan *bindings.KeyperSetManagerPaused) diff --git a/rolling-shutter/medley/encodeable/number/block.go b/rolling-shutter/medley/encodeable/number/block.go index 6c050dfce..6ed812f11 100644 --- a/rolling-shutter/medley/encodeable/number/block.go +++ b/rolling-shutter/medley/encodeable/number/block.go @@ -11,10 +11,16 @@ var ( LatestStr = []byte("latest") ) -func NewBlockNumber() *BlockNumber { - return &BlockNumber{ - Int: new(big.Int), +func NewBlockNumber(u *uint64) *BlockNumber { + b := &BlockNumber{ + Int: &big.Int{}, } + if u == nil { + b.SetInt64(LatestBlockInt) + } else { + b.SetUint64(*u) + } + return b } type BlockNumber struct { @@ -22,7 +28,7 @@ type BlockNumber struct { } func (k *BlockNumber) UnmarshalText(b []byte) error { - k.Int = new(big.Int) + k.Int = &big.Int{} if bytes.Equal(b, LatestStr) { k.Int.SetInt64(LatestBlockInt) return nil @@ -34,6 +40,14 @@ func (k *BlockNumber) IsLatest() bool { return k.Equal(LatestBlock) } +func (k *BlockNumber) ToUInt64Ptr() *uint64 { + if k.IsLatest() { + return nil + } + u := k.Uint64() + return &u +} + func (k *BlockNumber) Equal(b *BlockNumber) bool { return k.Int.Cmp(b.Int) == 0 } From 8cd3b1ae60914883ceeb17ca178415f38f6ab773 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Mon, 22 Jan 2024 11:27:36 +0100 Subject: [PATCH 18/91] chore(p2p): use fallback defaults from lib --- rolling-shutter/p2p/p2p.go | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/rolling-shutter/p2p/p2p.go b/rolling-shutter/p2p/p2p.go index f42fb3327..455447799 100644 --- a/rolling-shutter/p2p/p2p.go +++ b/rolling-shutter/p2p/p2p.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "sync" - "time" "github.com/libp2p/go-libp2p" dht "github.com/libp2p/go-libp2p-kad-dht" @@ -12,8 +11,8 @@ import ( "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/p2p/discovery/routing" + rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" rhost "github.com/libp2p/go-libp2p/p2p/host/routed" - "github.com/libp2p/go-libp2p/p2p/net/connmgr" "github.com/multiformats/go-multiaddr" "github.com/pkg/errors" "github.com/rs/zerolog/log" @@ -51,7 +50,6 @@ type Notifee interface { type P2PNode struct { config p2pNodeConfig - connmngr *connmgr.BasicConnMgr mux sync.Mutex host host.Host dht *dht.IpfsDHT @@ -74,7 +72,6 @@ type p2pNodeConfig struct { func NewP2PNode(config p2pNodeConfig) *P2PNode { p := P2PNode{ config: config, - connmngr: nil, host: nil, pubSub: nil, gossipRooms: make(map[string]*gossipRoom), @@ -149,7 +146,7 @@ func (p *P2PNode) init(ctx context.Context) error { if p.host != nil { return errors.New("Cannot create host on p2p with existing host") } - p2pHost, hashTable, connectionManager, err := createHost(ctx, p.config) + p2pHost, hashTable, err := createHost(ctx, p.config) if err != nil { return err } @@ -160,7 +157,6 @@ func (p *P2PNode) init(ctx context.Context) error { p.host = p2pHost p.dht = hashTable - p.connmngr = connectionManager p.pubSub = p2pPubSub log.Info().Str("address", p.p2pAddress()).Msg("created libp2p host") return nil @@ -169,24 +165,22 @@ func (p *P2PNode) init(ctx context.Context) error { func createHost( ctx context.Context, config p2pNodeConfig, -) (host.Host, *dht.IpfsDHT, *connmgr.BasicConnMgr, error) { +) (host.Host, *dht.IpfsDHT, error) { var err error - connectionManager, err := connmgr.NewConnManager( - 160, // Lowwater - 192, // HighWater, - connmgr.WithGracePeriod(time.Minute), - ) - if err != nil { - return nil, nil, nil, err - } + // NOTE: + // Upon initialization, we are seeing log warnings: + // "rcmgr limit conflicts with connmgr limit: conn manager high watermark limit: 192, exceeds the system connection limit of: 1" + // + // This was a bug in the check function, reading the wrong config value to check against: + // https://github.com/libp2p/go-libp2p/issues/2628 options := []libp2p.Option{ libp2p.Identity(&config.PrivKey.Key), libp2p.ListenAddrs(config.ListenAddrs...), - libp2p.DefaultTransports, - libp2p.DefaultSecurity, - libp2p.ConnectionManager(connectionManager), + // libp2p.DefaultTransports, + // libp2p.DefaultSecurity, + // libp2p.ConnectionManager(connectionManager), libp2p.ProtocolVersion(protocolVersion), } @@ -204,23 +198,23 @@ func createHost( p2pHost, err := libp2p.New(options...) if err != nil { - return nil, nil, nil, err + return nil, nil, err } if config.DisableRoutingDHT { - return p2pHost, nil, connectionManager, err + return p2pHost, nil, err } opts := dhtRoutingOptions(config.Environment, config.BootstrapPeers...) idht, err := dht.New(ctx, p2pHost, opts...) if err != nil { - return nil, nil, nil, err + return nil, nil, err } // the wrapped host will try to query the routing table (dht)/ // whenever it doesn't have the full routed address for a peer id routedHost := rhost.Wrap(p2pHost, idht) - return routedHost, idht, connectionManager, nil + return routedHost, idht, nil } func createPubSub( From 0905f1303de7b4a57e7d4ac1ba59eabf05ccc7aa Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Mon, 22 Jan 2024 15:39:20 +0100 Subject: [PATCH 19/91] fix(p2p): bootstrap context --- rolling-shutter/p2p/bootstrap.go | 4 +- rolling-shutter/p2p/messaging.go | 2 +- rolling-shutter/p2p/p2p.go | 64 ++++++++++++++------------------ 3 files changed, 30 insertions(+), 40 deletions(-) diff --git a/rolling-shutter/p2p/bootstrap.go b/rolling-shutter/p2p/bootstrap.go index 6319130fb..5527724da 100644 --- a/rolling-shutter/p2p/bootstrap.go +++ b/rolling-shutter/p2p/bootstrap.go @@ -73,9 +73,9 @@ func bootstrap( f := func(c context.Context) (bool, error) { if err := connectBootstrapNodes(c, h, config.BootstrapPeers); err != nil { - return true, err + return false, err } - return false, nil + return true, nil } if config.IsBootstrapNode { diff --git a/rolling-shutter/p2p/messaging.go b/rolling-shutter/p2p/messaging.go index 6bece4c58..6384632e4 100644 --- a/rolling-shutter/p2p/messaging.go +++ b/rolling-shutter/p2p/messaging.go @@ -217,7 +217,7 @@ func (m *P2PMessaging) Start( runner service.Runner, ) error { //nolint:unparam runner.Go(func() error { - return m.P2P.Run(ctx, m.topics(), m.validatorRegistry) + return m.P2P.Run(ctx, runner, m.topics(), m.validatorRegistry) }) if m.hasHandler() { runner.Go(func() error { diff --git a/rolling-shutter/p2p/p2p.go b/rolling-shutter/p2p/p2p.go index 455447799..17af07cf4 100644 --- a/rolling-shutter/p2p/p2p.go +++ b/rolling-shutter/p2p/p2p.go @@ -11,16 +11,15 @@ import ( "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/p2p/discovery/routing" - rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" rhost "github.com/libp2p/go-libp2p/p2p/host/routed" "github.com/multiformats/go-multiaddr" "github.com/pkg/errors" "github.com/rs/zerolog/log" - "golang.org/x/sync/errgroup" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/address" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/env" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/keys" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" ) var DefaultBootstrapPeers []*address.P2PAddress @@ -82,52 +81,43 @@ func NewP2PNode(config p2pNodeConfig) *P2PNode { func (p *P2PNode) Run( ctx context.Context, + runner service.Runner, topicNames []string, topicValidators ValidatorRegistry, ) error { - defer func() { - close(p.GossipMessages) - }() + p.mux.Lock() + defer p.mux.Unlock() - errorgroup, errorgroupctx := errgroup.WithContext(ctx) - errorgroup.Go(func() error { - p.mux.Lock() - defer p.mux.Unlock() - if err := p.init(ctx); err != nil { - return err - } + runner.Defer(func() { + close(p.GossipMessages) + }) - for topicName, validator := range topicValidators { - if err := p.pubSub.RegisterTopicValidator(topicName, validator); err != nil { - return err - } - } + if err := p.init(ctx); err != nil { + return err + } - if err := p.joinTopics(topicNames); err != nil { + for topicName, validator := range topicValidators { + if err := p.pubSub.RegisterTopicValidator(topicName, validator); err != nil { return err } + } - // listen to gossip on all topics - for _, room := range p.gossipRooms { - room := room - errorgroup.Go(func() error { - return room.readLoop(errorgroupctx, p.GossipMessages) - }) - } - - err := bootstrap(ctx, p.host, p.config, p.dht) - if err != nil { - return err - } + if err := p.joinTopics(topicNames); err != nil { + return err + } - // block the function until the context is canceled - errorgroup.Go(func() error { - <-errorgroupctx.Done() - return ctx.Err() + err := bootstrap(ctx, p.host, p.config, p.dht) + if err != nil { + return err + } + // listen to gossip on all topics + for _, room := range p.gossipRooms { + room := room + runner.Go(func() error { + return room.readLoop(ctx, p.GossipMessages) }) - return nil - }) - return errorgroup.Wait() + } + return nil } func (p *P2PNode) Publish(ctx context.Context, topic string, message []byte) error { From 8f00e9e649c14f967e3cf229c56c2fa47b0ac17a Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Mon, 22 Jan 2024 16:43:40 +0100 Subject: [PATCH 20/91] fix(op): nil-deref in pubkey syncer --- .../keyperimpl/optimism/sync/options.go | 11 +++++----- .../optimism/sync/syncer/eonpubkey.go | 20 +++++++++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/rolling-shutter/keyperimpl/optimism/sync/options.go b/rolling-shutter/keyperimpl/optimism/sync/options.go index 1ee0abff6..7fd91cd36 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/options.go +++ b/rolling-shutter/keyperimpl/optimism/sync/options.go @@ -100,11 +100,12 @@ func (o *options) apply(ctx context.Context, c *ShutterL2Client) error { return err } c.epksync = &syncer.EonPubKeySyncer{ - Client: client, - Log: c.log, - Contract: c.KeyBroadcast, - Handler: o.handlerEonPublicKey, - StartBlock: o.syncStart, + Client: client, + Log: c.log, + KeyBroadcast: c.KeyBroadcast, + KeyperSetManager: c.KeyperSetManager, + Handler: o.handlerEonPublicKey, + StartBlock: o.syncStart, } if o.handlerEonPublicKey != nil { c.services = append(c.services, c.epksync) diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go index 27c9598e9..51589a749 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go @@ -16,14 +16,14 @@ import ( ) type EonPubKeySyncer struct { - Client client.Client - Log log.Logger - Contract *bindings.KeyBroadcastContract - StartBlock *number.BlockNumber - Handler event.EonPublicKeyHandler + Client client.Client + Log log.Logger + KeyBroadcast *bindings.KeyBroadcastContract + KeyperSetManager *bindings.KeyperSetManager + StartBlock *number.BlockNumber + Handler event.EonPublicKeyHandler keyBroadcastCh chan *bindings.KeyBroadcastContractEonKeyBroadcast - ksManager *bindings.KeyperSetManager } func (s *EonPubKeySyncer) Start(ctx context.Context, runner service.Runner) error { @@ -59,7 +59,7 @@ func (s *EonPubKeySyncer) Start(ctx context.Context, runner service.Runner) erro runner.Defer(func() { close(s.keyBroadcastCh) }) - subs, err := s.Contract.WatchEonKeyBroadcast(watchOpts, s.keyBroadcastCh) + subs, err := s.KeyBroadcast.WatchEonKeyBroadcast(watchOpts, s.keyBroadcastCh) // FIXME: what to do on subs.Error() if err != nil { return err @@ -79,13 +79,13 @@ func (s *EonPubKeySyncer) getInitialPubKeys(ctx context.Context) ([]*event.EonPu Context: ctx, BlockNumber: s.StartBlock.Int, } - numKS, err := s.ksManager.GetNumKeyperSets(opts) + numKS, err := s.KeyperSetManager.GetNumKeyperSets(opts) if err != nil { return nil, err } // this blocknumber specifies the argument to the contract // getter - activeEon, err := s.ksManager.GetKeyperSetIndexByBlock(opts, s.StartBlock.Uint64()) + activeEon, err := s.KeyperSetManager.GetKeyperSetIndexByBlock(opts, s.StartBlock.Uint64()) if err != nil { return nil, err } @@ -118,7 +118,7 @@ func (s *EonPubKeySyncer) GetEonPubKeyForEon(ctx context.Context, opts *bind.Cal Context: ctx, } } - key, err := s.Contract.GetEonKey(opts, eon) + key, err := s.KeyBroadcast.GetEonKey(opts, eon) // XXX: can the key be a null byte? // I think we rather get a index out of bounds error. if err != nil { From 552f78126d0fe9eccc27c2dfab43e52741afedab Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Wed, 24 Jan 2024 17:11:43 +0100 Subject: [PATCH 21/91] feat(op): added AtBlockNumber to events --- rolling-shutter/keyperimpl/optimism/keyper.go | 2 +- .../keyperimpl/optimism/sync/event/events.go | 18 +++-- .../optimism/sync/syncer/eonpubkey.go | 23 ++++--- .../optimism/sync/syncer/keyperset.go | 52 +++++++-------- .../optimism/sync/syncer/unsafehead.go | 3 +- .../keyperimpl/optimism/sync/syncer/util.go | 65 +++++++++++++++++++ .../medley/encodeable/number/block.go | 6 ++ .../identitypreimage/identitypreimage.go | 1 - 8 files changed, 127 insertions(+), 43 deletions(-) create mode 100644 rolling-shutter/keyperimpl/optimism/sync/syncer/util.go diff --git a/rolling-shutter/keyperimpl/optimism/keyper.go b/rolling-shutter/keyperimpl/optimism/keyper.go index 6e91043d6..6a612595d 100644 --- a/rolling-shutter/keyperimpl/optimism/keyper.go +++ b/rolling-shutter/keyperimpl/optimism/keyper.go @@ -99,7 +99,7 @@ func (kpr *Keyper) newBlock(_ context.Context, ev *shopevent.LatestBlock) error // TODO: sanity checks - idPreimage := identitypreimage.BigToIdentityPreimage(ev.Number) + idPreimage := identitypreimage.BigToIdentityPreimage(ev.Number.Int) trig := &epochkghandler.DecryptionTrigger{ BlockNumber: ev.Number.Uint64(), IdentityPreimages: []identitypreimage.IdentityPreimage{idPreimage}, diff --git a/rolling-shutter/keyperimpl/optimism/sync/event/events.go b/rolling-shutter/keyperimpl/optimism/sync/event/events.go index 4485b204d..6b4a5de48 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/event/events.go +++ b/rolling-shutter/keyperimpl/optimism/sync/event/events.go @@ -1,9 +1,9 @@ package event import ( - "math/big" - "github.com/ethereum/go-ethereum/common" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" ) type ( @@ -12,16 +12,22 @@ type ( Members []common.Address Threshold uint64 Eon uint64 + + AtBlockNumber *number.BlockNumber } EonPublicKey struct { Eon uint64 Key []byte - } - LatestBlock struct { - Number *big.Int - BlockHash common.Hash + + AtBlockNumber *number.BlockNumber } ShutterState struct { Active bool + + AtBlockNumber *number.BlockNumber + } + LatestBlock struct { + Number *number.BlockNumber + BlockHash common.Hash } ) diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go index 51589a749..222e17be4 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go @@ -74,7 +74,6 @@ func (s *EonPubKeySyncer) Start(ctx context.Context, runner service.Runner) erro func (s *EonPubKeySyncer) getInitialPubKeys(ctx context.Context) ([]*event.EonPublicKey, error) { // This blocknumber specifies AT what state // the contract is called - // XXX: does the call-opts blocknumber -1 also means latest? opts := &bind.CallOpts{ Context: ctx, BlockNumber: s.StartBlock.Int, @@ -113,9 +112,14 @@ func (s *EonPubKeySyncer) logCallError(attrName string, err error) { } func (s *EonPubKeySyncer) GetEonPubKeyForEon(ctx context.Context, opts *bind.CallOpts, eon uint64) (*event.EonPublicKey, error) { - if opts == nil { - opts = &bind.CallOpts{ - Context: ctx, + var err error + if err := guardCallOpts(opts, true); err != nil { + return nil, err + } + if number.BigToBlockNumber(opts.BlockNumber).IsLatest() { + opts, err = fixCallOpts(ctx, s.Client, opts) + if err != nil { + return nil, err } } key, err := s.KeyBroadcast.GetEonKey(opts, eon) @@ -125,8 +129,9 @@ func (s *EonPubKeySyncer) GetEonPubKeyForEon(ctx context.Context, opts *bind.Cal return nil, err } return &event.EonPublicKey{ - Eon: eon, - Key: key, + Eon: eon, + Key: key, + AtBlockNumber: number.BigToBlockNumber(opts.BlockNumber), }, nil } @@ -137,9 +142,11 @@ func (s *EonPubKeySyncer) watchNewEonPubkey(ctx context.Context) error { if !ok { return nil } + bn := newEonKey.Raw.BlockNumber ev := &event.EonPublicKey{ - Eon: newEonKey.Eon, - Key: newEonKey.Key, + Eon: newEonKey.Eon, + Key: newEonKey.Key, + AtBlockNumber: number.NewBlockNumber(&bn), } err := s.Handler(ctx, ev) if err != nil { diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go index 6201979d2..d09674e46 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go @@ -82,27 +82,33 @@ func (s *KeyperSetSyncer) Start(ctx context.Context, runner service.Runner) erro } func (s *KeyperSetSyncer) getInitialKeyperSets(ctx context.Context) ([]*event.KeyperSet, error) { - // This blocknumber specifies AT what state - // the contract is called - // XXX: does the call-opts blocknumber -1 also means latest? opts := &bind.CallOpts{ Context: ctx, BlockNumber: s.StartBlock.Int, } - numKS, err := s.Contract.GetNumKeyperSets(opts) - if err != nil { + if err := guardCallOpts(opts, false); err != nil { return nil, err } + bn := s.StartBlock.ToUInt64Ptr() + if bn == nil { + // this should not be the case + return nil, errors.New("start block is 'latest'") + } initialKeyperSets := []*event.KeyperSet{} // this blocknumber specifies the argument to the contract // getter - ks, err := s.GetKeyperSetForBlock(ctx, nil, s.StartBlock) + ks, err := s.GetKeyperSetForBlock(ctx, opts, s.StartBlock) if err != nil { return nil, err } initialKeyperSets = append(initialKeyperSets, ks) + numKS, err := s.Contract.GetNumKeyperSets(opts) + if err != nil { + return nil, err + } + for i := ks.Eon + 1; i < numKS; i++ { ks, err = s.GetKeyperSetByIndex(ctx, opts, i) if err != nil { @@ -115,10 +121,8 @@ func (s *KeyperSetSyncer) getInitialKeyperSets(ctx context.Context) ([]*event.Ke } func (s *KeyperSetSyncer) GetKeyperSetByIndex(ctx context.Context, opts *bind.CallOpts, index uint64) (*event.KeyperSet, error) { - if opts == nil { - opts = &bind.CallOpts{ - Context: ctx, - } + if err := guardCallOpts(opts, true); err != nil { + return nil, err } actBl, err := s.Contract.GetKeyperSetActivationBlock(opts, index) if err != nil { @@ -134,18 +138,15 @@ func (s *KeyperSetSyncer) GetKeyperSetByIndex(ctx context.Context, opts *bind.Ca func (s *KeyperSetSyncer) GetKeyperSetForBlock(ctx context.Context, opts *bind.CallOpts, b *number.BlockNumber) (*event.KeyperSet, error) { var latestBlock uint64 var err error + if err := guardCallOpts(opts, true); err != nil { + return nil, err + } if b.Equal(number.LatestBlock) { latestBlock, err = s.Client.BlockNumber(ctx) } else { latestBlock = b.Uint64() } - if opts == nil { - // call at "latest block" state - opts = &bind.CallOpts{ - Context: ctx, - } - } idx, err := s.Contract.GetKeyperSetIndexByBlock(opts, latestBlock) if err != nil { return nil, errors.Wrap(err, "could not retrieve keyper set index") @@ -159,11 +160,8 @@ func (s *KeyperSetSyncer) newEvent( keyperSetContract common.Address, activationBlock uint64, ) (*event.KeyperSet, error) { - callOpts := opts - if callOpts == nil { - callOpts = &bind.CallOpts{ - Context: ctx, - } + if err := guardCallOpts(opts, false); err != nil { + return nil, err } ks, err := bindings.NewKeyperSet(keyperSetContract, s.Client) if err != nil { @@ -171,22 +169,22 @@ func (s *KeyperSetSyncer) newEvent( } // the manager only accepts final keyper sets, // so we expect this to be final now. - final, err := ks.IsFinalized(callOpts) + final, err := ks.IsFinalized(opts) if err != nil { return nil, makeCallError("IsFinalized", err) } if !final { return nil, errors.New("contract did accept unfinalized keyper-sets") } - members, err := ks.GetMembers(callOpts) + members, err := ks.GetMembers(opts) if err != nil { return nil, makeCallError("Members", err) } - threshold, err := ks.GetThreshold(callOpts) + threshold, err := ks.GetThreshold(opts) if err != nil { return nil, makeCallError("Threshold", err) } - eon, err := s.Contract.GetKeyperSetIndexByBlock(callOpts, activationBlock) + eon, err := s.Contract.GetKeyperSetIndexByBlock(opts, activationBlock) if err != nil { return nil, makeCallError("KeyperSetIndexByBlock", err) } @@ -195,6 +193,7 @@ func (s *KeyperSetSyncer) newEvent( Members: members, Threshold: threshold, Eon: eon, + AtBlockNumber: number.BigToBlockNumber(opts.BlockNumber), }, nil } @@ -205,9 +204,10 @@ func (s *KeyperSetSyncer) watchNewKeypersService(ctx context.Context) error { if !ok { return nil } + opts := logToCallOpts(ctx, &newKeypers.Raw) newKeyperSet, err := s.newEvent( ctx, - nil, + opts, newKeypers.KeyperSetContract, newKeypers.ActivationBlock, ) diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/unsafehead.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/unsafehead.go index b0572ccf3..661355549 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/unsafehead.go +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/unsafehead.go @@ -9,6 +9,7 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" ) @@ -49,7 +50,7 @@ func (s *UnsafeHeadSyncer) watchLatestUnsafeHead(ctx context.Context) error { return nil } ev := &event.LatestBlock{ - Number: newHeader.Number, + Number: number.BigToBlockNumber(newHeader.Number), BlockHash: newHeader.Hash(), } err := s.Handler(ctx, ev) diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/util.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/util.go new file mode 100644 index 000000000..b7b52ff8a --- /dev/null +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/util.go @@ -0,0 +1,65 @@ +package syncer + +import ( + "context" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/core/types" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" +) + +var ( + errNilCallOpts = errors.New("nil call-opts") + errLatestBlock = errors.New("'nil' latest block") +) + +func logToCallOpts(ctx context.Context, log *types.Log) *bind.CallOpts { + block := new(big.Int) + block.SetUint64(log.BlockNumber) + return &bind.CallOpts{ + BlockNumber: block, + Context: ctx, + } +} + +func guardCallOpts(opts *bind.CallOpts, allowLatest bool) error { + if opts == nil { + return errNilCallOpts + } + if !allowLatest { + n := number.BlockNumber{Int: opts.BlockNumber} + if n.IsLatest() { + return errLatestBlock + } + } + return nil +} + +func fixCallOpts(ctx context.Context, c client.Client, opts *bind.CallOpts) (*bind.CallOpts, error) { + err := guardCallOpts(opts, false) + if err != nil { + return opts, nil + } + // query the current latest block and fix it + latest, err := c.BlockNumber(ctx) + if err != nil { + return nil, err + } + blockNumber := number.NewBlockNumber(&latest) + if errors.Is(err, errNilCallOpts) { + opts = &bind.CallOpts{ + Context: ctx, + BlockNumber: blockNumber.Int, + } + return opts, nil + } + if errors.Is(err, errLatestBlock) { + opts.BlockNumber = blockNumber.Int + return opts, nil + } + return nil, err +} diff --git a/rolling-shutter/medley/encodeable/number/block.go b/rolling-shutter/medley/encodeable/number/block.go index 6ed812f11..ad1a7e967 100644 --- a/rolling-shutter/medley/encodeable/number/block.go +++ b/rolling-shutter/medley/encodeable/number/block.go @@ -11,6 +11,12 @@ var ( LatestStr = []byte("latest") ) +func BigToBlockNumber(i *big.Int) *BlockNumber { + return &BlockNumber{ + Int: i, + } +} + func NewBlockNumber(u *uint64) *BlockNumber { b := &BlockNumber{ Int: &big.Int{}, diff --git a/rolling-shutter/medley/identitypreimage/identitypreimage.go b/rolling-shutter/medley/identitypreimage/identitypreimage.go index 5b1e34ecd..e1c679b34 100644 --- a/rolling-shutter/medley/identitypreimage/identitypreimage.go +++ b/rolling-shutter/medley/identitypreimage/identitypreimage.go @@ -9,7 +9,6 @@ import ( type IdentityPreimage []byte -// BigToIdentityPreimage converts n to an epoch id. It fails if n is too big. func BigToIdentityPreimage(n *big.Int) IdentityPreimage { return IdentityPreimage(n.Bytes()) } From f361ac8990378dda24a60f330c6a5cd4c2caa895 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Wed, 24 Jan 2024 17:19:21 +0100 Subject: [PATCH 22/91] chore(op): add uint64 converter to block-number --- rolling-shutter/medley/encodeable/number/block.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rolling-shutter/medley/encodeable/number/block.go b/rolling-shutter/medley/encodeable/number/block.go index ad1a7e967..90ed0a48f 100644 --- a/rolling-shutter/medley/encodeable/number/block.go +++ b/rolling-shutter/medley/encodeable/number/block.go @@ -2,6 +2,7 @@ package number import ( "bytes" + "errors" "math/big" ) @@ -11,6 +12,8 @@ var ( LatestStr = []byte("latest") ) +var ErrLatestBlockToUint = errors.New("'latest' block can't be converted to uint64") + func BigToBlockNumber(i *big.Int) *BlockNumber { return &BlockNumber{ Int: i, @@ -46,6 +49,14 @@ func (k *BlockNumber) IsLatest() bool { return k.Equal(LatestBlock) } +func (k *BlockNumber) ToUInt64() (uint64, error) { + p := k.ToUInt64Ptr() + if p == nil { + return 0, ErrLatestBlockToUint + } + return *p, nil +} + func (k *BlockNumber) ToUInt64Ptr() *uint64 { if k.IsLatest() { return nil From e991ab5a8c904a0f03e775cd095edf48dfd1ccaa Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Tue, 30 Jan 2024 14:49:23 +0100 Subject: [PATCH 23/91] fix(op): 'latest' RPC call-opt always fixed to concrete number --- .../keyperimpl/optimism/sync/event/events.go | 4 +-- .../optimism/sync/syncer/eonpubkey.go | 9 ++----- .../optimism/sync/syncer/keyperset.go | 23 +++++++++++----- .../optimism/sync/syncer/shutterstate.go | 14 +++++----- .../keyperimpl/optimism/sync/syncer/util.go | 27 ++++++++++--------- .../medley/encodeable/number/block.go | 16 +++++++++-- 6 files changed, 56 insertions(+), 37 deletions(-) diff --git a/rolling-shutter/keyperimpl/optimism/sync/event/events.go b/rolling-shutter/keyperimpl/optimism/sync/event/events.go index 6b4a5de48..4e550b32c 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/event/events.go +++ b/rolling-shutter/keyperimpl/optimism/sync/event/events.go @@ -13,7 +13,7 @@ type ( Threshold uint64 Eon uint64 - AtBlockNumber *number.BlockNumber + AtBlockNumber *number.BlockNumber `json:",omitempty"` } EonPublicKey struct { Eon uint64 @@ -24,7 +24,7 @@ type ( ShutterState struct { Active bool - AtBlockNumber *number.BlockNumber + AtBlockNumber *number.BlockNumber `json:",omitempty"` } LatestBlock struct { Number *number.BlockNumber diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go index 222e17be4..660e76b8e 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go @@ -113,15 +113,10 @@ func (s *EonPubKeySyncer) logCallError(attrName string, err error) { func (s *EonPubKeySyncer) GetEonPubKeyForEon(ctx context.Context, opts *bind.CallOpts, eon uint64) (*event.EonPublicKey, error) { var err error - if err := guardCallOpts(opts, true); err != nil { + opts, _, err = fixCallOpts(ctx, s.Client, opts) + if err != nil { return nil, err } - if number.BigToBlockNumber(opts.BlockNumber).IsLatest() { - opts, err = fixCallOpts(ctx, s.Client, opts) - if err != nil { - return nil, err - } - } key, err := s.KeyBroadcast.GetEonKey(opts, eon) // XXX: can the key be a null byte? // I think we rather get a index out of bounds error. diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go index d09674e46..b3c1e7155 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go @@ -121,7 +121,8 @@ func (s *KeyperSetSyncer) getInitialKeyperSets(ctx context.Context) ([]*event.Ke } func (s *KeyperSetSyncer) GetKeyperSetByIndex(ctx context.Context, opts *bind.CallOpts, index uint64) (*event.KeyperSet, error) { - if err := guardCallOpts(opts, true); err != nil { + opts, _, err := fixCallOpts(ctx, s.Client, opts) + if err != nil { return nil, err } actBl, err := s.Contract.GetKeyperSetActivationBlock(opts, index) @@ -136,18 +137,28 @@ func (s *KeyperSetSyncer) GetKeyperSetByIndex(ctx context.Context, opts *bind.Ca } func (s *KeyperSetSyncer) GetKeyperSetForBlock(ctx context.Context, opts *bind.CallOpts, b *number.BlockNumber) (*event.KeyperSet, error) { - var latestBlock uint64 + var atBlock uint64 var err error - if err := guardCallOpts(opts, true); err != nil { + + opts, latestFromFix, err := fixCallOpts(ctx, s.Client, opts) + if err != nil { return nil, err } if b.Equal(number.LatestBlock) { - latestBlock, err = s.Client.BlockNumber(ctx) + if latestFromFix == nil { + atBlock, err = s.Client.BlockNumber(ctx) + if err != nil { + return nil, errors.Wrap(err, "get current block-number") + } + } else { + atBlock = *latestFromFix + } } else { - latestBlock = b.Uint64() + atBlock = b.Uint64() } - idx, err := s.Contract.GetKeyperSetIndexByBlock(opts, latestBlock) + + idx, err := s.Contract.GetKeyperSetIndexByBlock(opts, atBlock) if err != nil { return nil, errors.Wrap(err, "could not retrieve keyper set index") } diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go index ab2de559f..57d16e72b 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go @@ -2,10 +2,10 @@ package syncer import ( "context" - "errors" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/log" + "github.com/pkg/errors" "github.com/shutter-network/shop-contracts/bindings" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" @@ -26,17 +26,17 @@ type ShutterStateSyncer struct { } func (s *ShutterStateSyncer) GetShutterState(ctx context.Context, opts *bind.CallOpts) (*event.ShutterState, error) { - if opts == nil { - opts = &bind.CallOpts{ - Context: ctx, - } + opts, _, err := fixCallOpts(ctx, s.Client, opts) + if err != nil { + return nil, err } isPaused, err := s.Contract.Paused(opts) if err != nil { - return nil, err + return nil, errors.Wrap(err, "query paused state") } return &event.ShutterState{ - Active: !isPaused, + Active: !isPaused, + AtBlockNumber: number.BigToBlockNumber(opts.BlockNumber), }, nil } diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/util.go b/rolling-shutter/keyperimpl/optimism/sync/syncer/util.go index b7b52ff8a..b3ecb8c5d 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/util.go +++ b/rolling-shutter/keyperimpl/optimism/sync/syncer/util.go @@ -2,19 +2,20 @@ package syncer import ( "context" - "errors" "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/core/types" + "github.com/pkg/errors" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" ) var ( - errNilCallOpts = errors.New("nil call-opts") - errLatestBlock = errors.New("'nil' latest block") + errNilCallOpts = errors.New("nil call-opts") + errNilOptsBlockNumber = errors.New("opts block-number is nil, but 'latest' not allowed") + errLatestBlock = errors.New("'nil' latest block") ) func logToCallOpts(ctx context.Context, log *types.Log) *bind.CallOpts { @@ -31,7 +32,7 @@ func guardCallOpts(opts *bind.CallOpts, allowLatest bool) error { return errNilCallOpts } if !allowLatest { - n := number.BlockNumber{Int: opts.BlockNumber} + n := number.BigToBlockNumber(opts.BlockNumber) if n.IsLatest() { return errLatestBlock } @@ -39,15 +40,15 @@ func guardCallOpts(opts *bind.CallOpts, allowLatest bool) error { return nil } -func fixCallOpts(ctx context.Context, c client.Client, opts *bind.CallOpts) (*bind.CallOpts, error) { +func fixCallOpts(ctx context.Context, c client.Client, opts *bind.CallOpts) (*bind.CallOpts, *uint64, error) { err := guardCallOpts(opts, false) - if err != nil { - return opts, nil + if err == nil { + return opts, nil, nil } // query the current latest block and fix it - latest, err := c.BlockNumber(ctx) - if err != nil { - return nil, err + latest, queryErr := c.BlockNumber(ctx) + if queryErr != nil { + return nil, nil, errors.Wrap(err, "query latest block-number") } blockNumber := number.NewBlockNumber(&latest) if errors.Is(err, errNilCallOpts) { @@ -55,11 +56,11 @@ func fixCallOpts(ctx context.Context, c client.Client, opts *bind.CallOpts) (*bi Context: ctx, BlockNumber: blockNumber.Int, } - return opts, nil + return opts, &latest, nil } if errors.Is(err, errLatestBlock) { opts.BlockNumber = blockNumber.Int - return opts, nil + return opts, &latest, nil } - return nil, err + return nil, nil, err } diff --git a/rolling-shutter/medley/encodeable/number/block.go b/rolling-shutter/medley/encodeable/number/block.go index 90ed0a48f..2f0475d91 100644 --- a/rolling-shutter/medley/encodeable/number/block.go +++ b/rolling-shutter/medley/encodeable/number/block.go @@ -15,6 +15,9 @@ var ( var ErrLatestBlockToUint = errors.New("'latest' block can't be converted to uint64") func BigToBlockNumber(i *big.Int) *BlockNumber { + if i == nil { + return NewBlockNumber(nil) + } return &BlockNumber{ Int: i, } @@ -36,12 +39,21 @@ type BlockNumber struct { *big.Int } +func (k *BlockNumber) MarshalJSON() ([]byte, error) { + return k.MarshalText() +} + +func (k *BlockNumber) UnmarshalJSON(b []byte) error { + return k.UnmarshalText(b) +} + func (k *BlockNumber) UnmarshalText(b []byte) error { - k.Int = &big.Int{} + k.Int = new(big.Int) if bytes.Equal(b, LatestStr) { k.Int.SetInt64(LatestBlockInt) return nil } + return k.Int.UnmarshalText(b) } @@ -71,7 +83,7 @@ func (k *BlockNumber) Equal(b *BlockNumber) bool { func (k *BlockNumber) MarshalText() ([]byte, error) { if k.IsLatest() { - return []byte("latest"), nil + return LatestStr, nil } return k.Int.MarshalText() } From 5865514f034b4e83769580ca0fc4d10e65561c22 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Tue, 30 Jan 2024 16:13:57 +0100 Subject: [PATCH 24/91] chore(op): remove private repo access for dockerfile --- docker/build-src/optimism/Dockerfile | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docker/build-src/optimism/Dockerfile b/docker/build-src/optimism/Dockerfile index 2755310a4..cd20957af 100644 --- a/docker/build-src/optimism/Dockerfile +++ b/docker/build-src/optimism/Dockerfile @@ -1,12 +1,6 @@ FROM golang:1.21 as builder ENV GOMODCACHE=/root/.cache/mod -# allow private repos -ENV GOPRIVATE="github.com/shutter-network/*" -ENV GONOPROXY=localhost -ARG GITHUB_ACCESS_TOKEN -RUN git config --global url."https://$GITHUB_ACCESS_TOKEN@github.com/".insteadOf "https://github.com/" - # Fetch go modules separately to improve cache usage RUN mkdir /gomod COPY /rolling-shutter/go.* /gomod/ From 72bf764b60a4f1a747b585e43aca387b898ecabf Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Tue, 30 Jan 2024 17:46:11 +0100 Subject: [PATCH 25/91] chore!: move syncer to medley --- .../optimism/bootstrap/bootstrap.go | 2 +- .../optimism/bootstrap/keyperset.go | 8 ++-- rolling-shutter/keyperimpl/optimism/keyper.go | 20 ++++----- .../sync => medley/chainsync}/client.go | 42 ++++++++----------- .../chainsync}/client/client.go | 0 .../sync => medley/chainsync}/event/events.go | 0 .../chainsync}/event/handler.go | 0 .../sync => medley/chainsync}/options.go | 10 ++--- .../chainsync}/syncer/eonpubkey.go | 4 +- .../chainsync}/syncer/keyperset.go | 6 +-- .../chainsync}/syncer/shutterstate.go | 8 ++-- .../chainsync}/syncer/unsafehead.go | 4 +- .../sync => medley/chainsync}/syncer/util.go | 2 +- 13 files changed, 49 insertions(+), 57 deletions(-) rename rolling-shutter/{keyperimpl/optimism/sync => medley/chainsync}/client.go (67%) rename rolling-shutter/{keyperimpl/optimism/sync => medley/chainsync}/client/client.go (100%) rename rolling-shutter/{keyperimpl/optimism/sync => medley/chainsync}/event/events.go (100%) rename rolling-shutter/{keyperimpl/optimism/sync => medley/chainsync}/event/handler.go (100%) rename rolling-shutter/{keyperimpl/optimism/sync => medley/chainsync}/options.go (94%) rename rolling-shutter/{keyperimpl/optimism/sync => medley/chainsync}/syncer/eonpubkey.go (95%) rename rolling-shutter/{keyperimpl/optimism/sync => medley/chainsync}/syncer/keyperset.go (96%) rename rolling-shutter/{keyperimpl/optimism/sync => medley/chainsync}/syncer/shutterstate.go (93%) rename rolling-shutter/{keyperimpl/optimism/sync => medley/chainsync}/syncer/unsafehead.go (88%) rename rolling-shutter/{keyperimpl/optimism/sync => medley/chainsync}/syncer/util.go (94%) diff --git a/rolling-shutter/keyperimpl/optimism/bootstrap/bootstrap.go b/rolling-shutter/keyperimpl/optimism/bootstrap/bootstrap.go index b3d2bced2..439f69cc4 100644 --- a/rolling-shutter/keyperimpl/optimism/bootstrap/bootstrap.go +++ b/rolling-shutter/keyperimpl/optimism/bootstrap/bootstrap.go @@ -10,7 +10,7 @@ import ( "github.com/tendermint/tendermint/rpc/client/http" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/fx" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/shmsg" ) diff --git a/rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go b/rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go index fb413d631..7cb886c95 100644 --- a/rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go +++ b/rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go @@ -6,15 +6,15 @@ import ( "errors" "os" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" ) func GetKeyperSet(ctx context.Context, config *Config) error { - sl2, err := sync.NewShutterL2Client( + sl2, err := chainsync.NewClient( ctx, - sync.WithClientURL(config.JSONRPCURL), + chainsync.WithClientURL(config.JSONRPCURL), ) if err != nil { return err diff --git a/rolling-shutter/keyperimpl/optimism/keyper.go b/rolling-shutter/keyperimpl/optimism/keyper.go index 6a612595d..44941848a 100644 --- a/rolling-shutter/keyperimpl/optimism/keyper.go +++ b/rolling-shutter/keyperimpl/optimism/keyper.go @@ -15,10 +15,10 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/kprconfig" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/config" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/database" - shopclient "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync" - shopevent "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/broker" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync" + syncevent "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" @@ -30,7 +30,7 @@ var ErrParseKeyperSet = errors.New("can't parse KeyperSet") type Keyper struct { core *keyper.KeyperCore - l2Client *shopclient.ShutterL2Client + l2Client *chainsync.Client dbpool *pgxpool.Pool config *config.Config @@ -78,12 +78,12 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { return errors.Wrap(err, "can't instantiate keyper core") } // TODO: wrap the logger and pass in - kpr.l2Client, err = shopclient.NewShutterL2Client( + kpr.l2Client, err = chainsync.NewClient( ctx, - shopclient.WithClientURL(kpr.config.Optimism.JSONRPCURL), - shopclient.WithSyncNewBlock(kpr.newBlock), - shopclient.WithSyncNewKeyperSet(kpr.newKeyperSet), - shopclient.WithPrivateKey(kpr.config.Optimism.PrivateKey.Key), + chainsync.WithClientURL(kpr.config.Optimism.JSONRPCURL), + chainsync.WithSyncNewBlock(kpr.newBlock), + chainsync.WithSyncNewKeyperSet(kpr.newKeyperSet), + chainsync.WithPrivateKey(kpr.config.Optimism.PrivateKey.Key), ) if err != nil { return err @@ -91,7 +91,7 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { return runner.StartService(kpr.core, kpr.l2Client) } -func (kpr *Keyper) newBlock(_ context.Context, ev *shopevent.LatestBlock) error { +func (kpr *Keyper) newBlock(_ context.Context, ev *syncevent.LatestBlock) error { log.Info(). Int64("number", ev.Number.Int64()). Str("hash", ev.BlockHash.Hex()). @@ -110,7 +110,7 @@ func (kpr *Keyper) newBlock(_ context.Context, ev *shopevent.LatestBlock) error return nil } -func (kpr *Keyper) newKeyperSet(ctx context.Context, ev *shopevent.KeyperSet) error { +func (kpr *Keyper) newKeyperSet(ctx context.Context, ev *syncevent.KeyperSet) error { log.Info(). Uint64("activation-block", ev.ActivationBlock). Uint64("eon", ev.Eon). diff --git a/rolling-shutter/keyperimpl/optimism/sync/client.go b/rolling-shutter/medley/chainsync/client.go similarity index 67% rename from rolling-shutter/keyperimpl/optimism/sync/client.go rename to rolling-shutter/medley/chainsync/client.go index 460c7b2d8..7d2bd57de 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/client.go +++ b/rolling-shutter/medley/chainsync/client.go @@ -1,9 +1,8 @@ -package sync +package chainsync import ( "context" "crypto/ecdsa" - "io" "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -12,9 +11,9 @@ import ( "github.com/pkg/errors" "github.com/shutter-network/shop-contracts/bindings" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/syncer" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/client" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/syncer" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/logger" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" @@ -22,15 +21,9 @@ import ( var noopLogger = &logger.NoopLogger{} -var ErrServiceNotInstantiated = errors.New("service is not instantiated, pass a handler function option.") +var ErrServiceNotInstantiated = errors.New("service is not instantiated, pass a handler function option") -type ShutterSync interface { - io.Closer - // Start starts an additional worker syncing job - Start() error -} - -type ShutterL2Client struct { +type Client struct { client.Client log log.Logger @@ -49,7 +42,7 @@ type ShutterL2Client struct { services []service.Service } -func NewShutterL2Client(ctx context.Context, options ...Option) (*ShutterL2Client, error) { +func NewClient(ctx context.Context, options ...Option) (*Client, error) { opts := defaultOptions() for _, option := range options { err := option(opts) @@ -63,7 +56,7 @@ func NewShutterL2Client(ctx context.Context, options ...Option) (*ShutterL2Clien return nil, err } - c := &ShutterL2Client{ + c := &Client{ log: noopLogger, services: []service.Service{}, } @@ -75,11 +68,11 @@ func NewShutterL2Client(ctx context.Context, options ...Option) (*ShutterL2Clien return c, nil } -func (s *ShutterL2Client) getServices() []service.Service { +func (s *Client) getServices() []service.Service { return s.services } -func (s *ShutterL2Client) GetShutterState(ctx context.Context) (*event.ShutterState, error) { +func (s *Client) GetShutterState(ctx context.Context) (*event.ShutterState, error) { if s.sssync == nil { return nil, errors.Wrap(ErrServiceNotInstantiated, "ShutterStateSyncer service not instantiated") } @@ -89,7 +82,7 @@ func (s *ShutterL2Client) GetShutterState(ctx context.Context) (*event.ShutterSt return s.sssync.GetShutterState(ctx, opts) } -func (s *ShutterL2Client) GetKeyperSetByIndex(ctx context.Context, index uint64) (*event.KeyperSet, error) { +func (s *Client) GetKeyperSetByIndex(ctx context.Context, index uint64) (*event.KeyperSet, error) { if s.kssync == nil { return nil, errors.Wrap(ErrServiceNotInstantiated, "KeyperSetSyncer service not instantiated") } @@ -99,7 +92,7 @@ func (s *ShutterL2Client) GetKeyperSetByIndex(ctx context.Context, index uint64) return s.kssync.GetKeyperSetByIndex(ctx, opts, index) } -func (s *ShutterL2Client) GetKeyperSetForBlock(ctx context.Context, b *number.BlockNumber) (*event.KeyperSet, error) { +func (s *Client) GetKeyperSetForBlock(ctx context.Context, b *number.BlockNumber) (*event.KeyperSet, error) { if s.kssync == nil { return nil, errors.Wrap(ErrServiceNotInstantiated, "KeyperSetSyncer service not instantiated") } @@ -109,7 +102,7 @@ func (s *ShutterL2Client) GetKeyperSetForBlock(ctx context.Context, b *number.Bl return s.kssync.GetKeyperSetForBlock(ctx, opts, b) } -func (s *ShutterL2Client) GetEonPubKeyForEon(ctx context.Context, eon uint64) (*event.EonPublicKey, error) { +func (s *Client) GetEonPubKeyForEon(ctx context.Context, eon uint64) (*event.EonPublicKey, error) { if s.sssync == nil { return nil, errors.Wrap(ErrServiceNotInstantiated, "ShutterStateSyncer service not instantiated") } @@ -119,8 +112,9 @@ func (s *ShutterL2Client) GetEonPubKeyForEon(ctx context.Context, eon uint64) (* return s.epksync.GetEonPubKeyForEon(ctx, opts, eon) } -func (s *ShutterL2Client) BroadcastEonKey(ctx context.Context, eon uint64, eonPubKey []byte) (*types.Transaction, error) { - // TODO: first do a getEonKey. If we already have something (ideally the same) +func (s *Client) BroadcastEonKey(ctx context.Context, eon uint64, eonPubKey []byte) (*types.Transaction, error) { + // TODO: first do a getEonKey. If we already have this key, do nothing, + // if we have a different key, error // don't do a transaction // s.KeyBroadcast.GetEonKey(eon) if s.privKey == nil { @@ -140,7 +134,7 @@ func (s *ShutterL2Client) BroadcastEonKey(ctx context.Context, eon uint64, eonPu // ChainID returns the chainid of the underlying L2 chain. // This value is cached, since it is not expected to change. -func (s *ShutterL2Client) ChainID(ctx context.Context) (*big.Int, error) { +func (s *Client) ChainID(ctx context.Context) (*big.Int, error) { if s.chainID == nil { cid, err := s.Client.ChainID(ctx) if err != nil { @@ -151,6 +145,6 @@ func (s *ShutterL2Client) ChainID(ctx context.Context) (*big.Int, error) { return s.chainID, nil } -func (s *ShutterL2Client) Start(_ context.Context, runner service.Runner) error { +func (s *Client) Start(_ context.Context, runner service.Runner) error { return runner.StartService(s.getServices()...) } diff --git a/rolling-shutter/keyperimpl/optimism/sync/client/client.go b/rolling-shutter/medley/chainsync/client/client.go similarity index 100% rename from rolling-shutter/keyperimpl/optimism/sync/client/client.go rename to rolling-shutter/medley/chainsync/client/client.go diff --git a/rolling-shutter/keyperimpl/optimism/sync/event/events.go b/rolling-shutter/medley/chainsync/event/events.go similarity index 100% rename from rolling-shutter/keyperimpl/optimism/sync/event/events.go rename to rolling-shutter/medley/chainsync/event/events.go diff --git a/rolling-shutter/keyperimpl/optimism/sync/event/handler.go b/rolling-shutter/medley/chainsync/event/handler.go similarity index 100% rename from rolling-shutter/keyperimpl/optimism/sync/event/handler.go rename to rolling-shutter/medley/chainsync/event/handler.go diff --git a/rolling-shutter/keyperimpl/optimism/sync/options.go b/rolling-shutter/medley/chainsync/options.go similarity index 94% rename from rolling-shutter/keyperimpl/optimism/sync/options.go rename to rolling-shutter/medley/chainsync/options.go index 7fd91cd36..580876243 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/options.go +++ b/rolling-shutter/medley/chainsync/options.go @@ -1,4 +1,4 @@ -package sync +package chainsync import ( "context" @@ -11,9 +11,9 @@ import ( "github.com/shutter-network/shop-contracts/bindings" "github.com/shutter-network/shop-contracts/predeploy" - syncclient "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/syncer" + syncclient "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/client" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/syncer" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" ) @@ -54,7 +54,7 @@ func (o *options) verify() error { // the context is only the initialisation context, // and should not be considered to handle the lifecycle // of shutter clients background workers. -func (o *options) apply(ctx context.Context, c *ShutterL2Client) error { +func (o *options) apply(ctx context.Context, c *Client) error { var ( client syncclient.Client err error diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go b/rolling-shutter/medley/chainsync/syncer/eonpubkey.go similarity index 95% rename from rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go rename to rolling-shutter/medley/chainsync/syncer/eonpubkey.go index 660e76b8e..1c0494845 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/eonpubkey.go +++ b/rolling-shutter/medley/chainsync/syncer/eonpubkey.go @@ -9,8 +9,8 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/shutter-network/shop-contracts/bindings" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/client" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" ) diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go b/rolling-shutter/medley/chainsync/syncer/keyperset.go similarity index 96% rename from rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go rename to rolling-shutter/medley/chainsync/syncer/keyperset.go index b3c1e7155..5036a6bf3 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/keyperset.go +++ b/rolling-shutter/medley/chainsync/syncer/keyperset.go @@ -9,8 +9,8 @@ import ( "github.com/pkg/errors" "github.com/shutter-network/shop-contracts/bindings" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/client" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" ) @@ -166,7 +166,7 @@ func (s *KeyperSetSyncer) GetKeyperSetForBlock(ctx context.Context, opts *bind.C } func (s *KeyperSetSyncer) newEvent( - ctx context.Context, + _ context.Context, opts *bind.CallOpts, keyperSetContract common.Address, activationBlock uint64, diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go b/rolling-shutter/medley/chainsync/syncer/shutterstate.go similarity index 93% rename from rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go rename to rolling-shutter/medley/chainsync/syncer/shutterstate.go index 57d16e72b..e262d679a 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/shutterstate.go +++ b/rolling-shutter/medley/chainsync/syncer/shutterstate.go @@ -8,8 +8,8 @@ import ( "github.com/pkg/errors" "github.com/shutter-network/shop-contracts/bindings" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/client" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" ) @@ -84,7 +84,7 @@ func (s *ShutterStateSyncer) pollIsActive(ctx context.Context) (bool, error) { return !paused, err } -func (s *ShutterStateSyncer) handle(ctx context.Context, ev *event.ShutterState) bool { +func (s *ShutterStateSyncer) handle(ctx context.Context, ev *event.ShutterState) { err := s.Handler(ctx, ev) if err != nil { s.Log.Error( @@ -92,9 +92,7 @@ func (s *ShutterStateSyncer) handle(ctx context.Context, ev *event.ShutterState) "error", err.Error(), ) - return false } - return true } func (s *ShutterStateSyncer) watchPaused(ctx context.Context) error { diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/unsafehead.go b/rolling-shutter/medley/chainsync/syncer/unsafehead.go similarity index 88% rename from rolling-shutter/keyperimpl/optimism/sync/syncer/unsafehead.go rename to rolling-shutter/medley/chainsync/syncer/unsafehead.go index 661355549..15979bfba 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/unsafehead.go +++ b/rolling-shutter/medley/chainsync/syncer/unsafehead.go @@ -7,8 +7,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/client" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" ) diff --git a/rolling-shutter/keyperimpl/optimism/sync/syncer/util.go b/rolling-shutter/medley/chainsync/syncer/util.go similarity index 94% rename from rolling-shutter/keyperimpl/optimism/sync/syncer/util.go rename to rolling-shutter/medley/chainsync/syncer/util.go index b3ecb8c5d..0c44cf3dc 100644 --- a/rolling-shutter/keyperimpl/optimism/sync/syncer/util.go +++ b/rolling-shutter/medley/chainsync/syncer/util.go @@ -8,7 +8,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/sync/client" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/client" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" ) From f0a0a8b484483909d758ee5c0f4e5ea5591e2d9a Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Tue, 30 Jan 2024 18:22:01 +0100 Subject: [PATCH 26/91] chore: satisfy linter --- rolling-shutter/keyper/epochkghandler/sendkeyshare.go | 1 + 1 file changed, 1 insertion(+) diff --git a/rolling-shutter/keyper/epochkghandler/sendkeyshare.go b/rolling-shutter/keyper/epochkghandler/sendkeyshare.go index e2c4d96c7..effcc39f2 100644 --- a/rolling-shutter/keyper/epochkghandler/sendkeyshare.go +++ b/rolling-shutter/keyper/epochkghandler/sendkeyshare.go @@ -45,6 +45,7 @@ var ( ErrSharesAlreadySent = errors.New("shares exist already") ) +//nolint:gocyclo func (ksh *KeyShareHandler) ConstructDecryptionKeyShares( ctx context.Context, eon database.Eon, From 4a8c2062dc3392240cd7b63a4bc6fc9a4a33dc4c Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Wed, 31 Jan 2024 15:50:39 +0100 Subject: [PATCH 27/91] fix: runner defer executed too early The recent separation of a blocking Run() and a non-blocking RunBackground() introduced a bug that prematurely executed registered defer functions on the runner. This is a critical error that is now fixed by having RunBackground return the defer function as well. --- .../keyper/epochkghandler/trigger_test.go | 3 ++- rolling-shutter/keyperimpl/rollup/trigger_test.go | 3 ++- rolling-shutter/medley/service/service.go | 12 ++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/rolling-shutter/keyper/epochkghandler/trigger_test.go b/rolling-shutter/keyper/epochkghandler/trigger_test.go index a636b31b4..a07dc83eb 100644 --- a/rolling-shutter/keyper/epochkghandler/trigger_test.go +++ b/rolling-shutter/keyper/epochkghandler/trigger_test.go @@ -44,7 +44,7 @@ func TestHandleDecryptionTriggerIntegration(t *testing.T) { Messaging: messaging, Trigger: decrTrigChan, } - group := service.RunBackground( + group, cleanup := service.RunBackground( ctx, ksh, ) @@ -58,6 +58,7 @@ func TestHandleDecryptionTriggerIntegration(t *testing.T) { decrTrigChan <- broker.NewEvent(trig) close(decrTrigChan) err = group.Wait() + cleanup() assert.NilError(t, err) // send decryption key share when first trigger is received diff --git a/rolling-shutter/keyperimpl/rollup/trigger_test.go b/rolling-shutter/keyperimpl/rollup/trigger_test.go index 159a6ab30..6425b5bdc 100644 --- a/rolling-shutter/keyperimpl/rollup/trigger_test.go +++ b/rolling-shutter/keyperimpl/rollup/trigger_test.go @@ -47,10 +47,11 @@ func TestHandleDecryptionTriggerIntegration(t *testing.T) { assert.NilError(t, err) sender.AddMessageHandler(triggerHandler) - group := service.RunBackground( + group, cleanup := service.RunBackground( ctx, sender, ) + defer cleanup() testsetup.InitializeEon(ctx, t, dbpool, config, keyperIndex) diff --git a/rolling-shutter/medley/service/service.go b/rolling-shutter/medley/service/service.go index 4f70ff6f1..282dc3dde 100644 --- a/rolling-shutter/medley/service/service.go +++ b/rolling-shutter/medley/service/service.go @@ -65,18 +65,22 @@ func (r *runner) runShutdownFuncs() { } func Run(ctx context.Context, services ...Service) error { - group := RunBackground(ctx, services...) + group, deferFn := RunBackground(ctx, services...) + defer deferFn() return group.Wait() } -func RunBackground(ctx context.Context, services ...Service) *errgroup.Group { +// RunBackground runs the services within the context of an errgroup.Group. +// It returns the errgroup.Group as well as the cleanup function to be deferred. +// This is a low-level executor of services, the defer function is expected +// to be called! +func RunBackground(ctx context.Context, services ...Service) (*errgroup.Group, func()) { group, ctx := errgroup.WithContext(ctx) r := runner{group: group, ctx: ctx} group.Go(func() error { return r.StartService(services...) }) - defer r.runShutdownFuncs() - return group + return group, r.runShutdownFuncs } // notifyTermination creates a context that is canceled, when the process receives SIGINT or From 4136f93ab0bae509f8b5adc4d12940a5bf78c9fd Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Wed, 31 Jan 2024 15:52:18 +0100 Subject: [PATCH 28/91] chore!: refactor ServiceFn and pass runner The service function is renamed and in addition to the context also receives the Runner instance --- rolling-shutter/chainobserver/observer.go | 4 ++-- rolling-shutter/keyper/keyper.go | 4 ++-- rolling-shutter/medley/channel/fanin_test.go | 8 ++++---- rolling-shutter/medley/service/service.go | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/rolling-shutter/chainobserver/observer.go b/rolling-shutter/chainobserver/observer.go index cd66935da..fd339dc16 100644 --- a/rolling-shutter/chainobserver/observer.go +++ b/rolling-shutter/chainobserver/observer.go @@ -79,7 +79,7 @@ func (c *ChainObserver) Start(ctx context.Context, runner service.Runner) error log.Info().Uint64("from-block", fromBlock).Uint64("from-log-index", fromLogIndex). Msg("starting event syncing") syncer := eventsyncer.New(c.Client, finalityOffset, eventTypes, fromBlock, fromLogIndex) - handleSyncLoop := func(ctx context.Context) error { + handleSyncLoop := func(ctx context.Context, _ service.Runner) error { for { select { case <-ctx.Done(): @@ -102,5 +102,5 @@ func (c *ChainObserver) Start(ctx context.Context, runner service.Runner) error } } } - return runner.StartService(syncer, service.ServiceFn{Fn: handleSyncLoop}) + return runner.StartService(syncer, service.Function{Func: handleSyncLoop}) } diff --git a/rolling-shutter/keyper/keyper.go b/rolling-shutter/keyper/keyper.go index bff1d60df..680f669ce 100644 --- a/rolling-shutter/keyper/keyper.go +++ b/rolling-shutter/keyper/keyper.go @@ -166,7 +166,7 @@ func (kpr *KeyperCore) Start(ctx context.Context, runner service.Runner) error { func (kpr *KeyperCore) getServices() []service.Service { services := []service.Service{ kpr.messaging, - service.ServiceFn{Fn: kpr.operateShuttermint}, + service.Function{Func: kpr.operateShuttermint}, newEonPubKeyHandler(kpr), } keyTrigger := kpr.trigger @@ -334,7 +334,7 @@ func (kpr *KeyperCore) handleOnChainKeyperSetChanges( // TODO: we need a better block syncing mechanism! // Also this is doing too much work sequentially in one routine. -func (kpr *KeyperCore) operateShuttermint(ctx context.Context) error { +func (kpr *KeyperCore) operateShuttermint(ctx context.Context, _ service.Runner) error { for { syncBlockNumber, err := retry.FunctionCall(ctx, kpr.blockSyncClient.BlockNumber) if err != nil { diff --git a/rolling-shutter/medley/channel/fanin_test.go b/rolling-shutter/medley/channel/fanin_test.go index 46c984b30..b2879ba95 100644 --- a/rolling-shutter/medley/channel/fanin_test.go +++ b/rolling-shutter/medley/channel/fanin_test.go @@ -36,8 +36,8 @@ func TestFanIn(t *testing.T) { end := end step := numFns channels = append(channels, ch) - fnService := service.ServiceFn{ - Fn: func(ctx context.Context) error { + fnService := service.Function{ + Func: func(ctx context.Context, _ service.Runner) error { putInts(ctx, ch, start, end, step) return nil }, @@ -48,8 +48,8 @@ func TestFanIn(t *testing.T) { services = append(services, fanIn) result := []int{} - fanConsumer := service.ServiceFn{ - Fn: func(ctx context.Context) error { + fanConsumer := service.Function{ + Func: func(ctx context.Context, _ service.Runner) error { for val := range fanIn.C { result = append(result, val) } diff --git a/rolling-shutter/medley/service/service.go b/rolling-shutter/medley/service/service.go index 282dc3dde..13e4fc4e7 100644 --- a/rolling-shutter/medley/service/service.go +++ b/rolling-shutter/medley/service/service.go @@ -121,13 +121,13 @@ func RunWithSighandler(ctx context.Context, services ...Service) error { return err } -type ServiceFn struct { - Fn func(ctx context.Context) error +type Function struct { + Func func(ctx context.Context, group Runner) error } -func (sf ServiceFn) Start(ctx context.Context, group Runner) error { //nolint:unparam +func (sf Function) Start(ctx context.Context, group Runner) error { //nolint:unparam group.Go(func() error { - return sf.Fn(ctx) + return sf.Func(ctx, group) }) return nil } From 631ca130cf04893ec81d69f65387c9d41f69ada2 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Wed, 31 Jan 2024 15:52:39 +0100 Subject: [PATCH 29/91] fix: P2P integration test --- rolling-shutter/p2p/p2p_test.go | 82 ++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/rolling-shutter/p2p/p2p_test.go b/rolling-shutter/p2p/p2p_test.go index c80e6a298..65bba63a3 100644 --- a/rolling-shutter/p2p/p2p_test.go +++ b/rolling-shutter/p2p/p2p_test.go @@ -4,16 +4,17 @@ import ( "bytes" "context" "fmt" - "sync" "testing" "time" pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/pkg/errors" "github.com/rs/zerolog/log" "gotest.tools/assert" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/address" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/testlog" ) @@ -21,12 +22,14 @@ func init() { testlog.Setup() } +var ErrTestComplete = errors.New("test complete") + // TestStartNetworkNode test that we can init two p2p nodes and make them send/receive messages. func TestStartNetworkNodeIntegration(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } - ctx, cancel := context.WithTimeout(context.Background(), 1200*time.Millisecond) + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() numBootstrappers := 2 @@ -82,51 +85,54 @@ func TestStartNetworkNodeIntegration(t *testing.T) { gossipTopicNames := []string{"testTopic1", "testTopic2"} testMessage := []byte("test message") - runctx, stopRun := context.WithCancel(ctx) - - waitGroup := sync.WaitGroup{} p2ps := []*P2PNode{} - for _, cfg := range configs { + services := make([]service.Service, len(configs)) + for i, cfg := range configs { p2pHandler, err := New(cfg) assert.NilError(t, err) p2ps = append(p2ps, p2pHandler.P2P) - waitGroup.Add(1) - go func() { - defer waitGroup.Done() - err := p2pHandler.P2P.Run(runctx, gossipTopicNames, map[string]pubsub.ValidatorEx{}) - assert.Assert(t, err == context.Canceled) - }() + fn := func(ctx context.Context, runner service.Runner) error { + return p2pHandler.P2P.Run(ctx, runner, gossipTopicNames, map[string]pubsub.ValidatorEx{}) + } + services[i] = service.Function{Func: fn} } - defer func() { - stopRun() - waitGroup.Wait() - }() + // The following loop publishes the same message over and over. Even though we did call // ConnectToPeer, libp2p takes some time until the peer receives the first message. - var message *pubsub.Message - topicName := gossipTopicNames[0] - for message == nil { - if err := p2ps[1].Publish(ctx, topicName, testMessage); err != nil { - t.Fatalf("error while publishing message: %v", err) - } + testFn := func(ctx context.Context, _ service.Runner) error { + // HACK: + time.Sleep(1 * time.Second) + var message *pubsub.Message + topicName := gossipTopicNames[0] + for message == nil { + if err := p2ps[1].Publish(ctx, topicName, testMessage); err != nil { + t.Fatalf("error while publishing message: %v", err) + } - select { - case message = <-p2ps[0].GossipMessages: - log.Info().Interface("message", message).Msg("got message") - if message == nil { - t.Fatalf("channel closed unexpectedly") + select { + case message = <-p2ps[0].GossipMessages: + log.Info().Interface("message", message).Msg("got message") + if message == nil { + t.Fatalf("channel closed unexpectedly") + } + case <-ctx.Done(): + t.Fatalf("waiting for message: %s", ctx.Err()) + case <-time.After(5 * time.Millisecond): } - case <-ctx.Done(): - t.Fatalf("waiting for message: %s", ctx.Err()) - case <-time.After(5 * time.Millisecond): } + assert.Equal(t, topicName, message.GetTopic(), "received message with wrong topic") + assert.Check(t, bytes.Equal(testMessage, message.GetData()), "received wrong message") + assert.Equal( + t, + p2ps[1].HostID(), + message.GetFrom().String(), + "received message with wrong sender", + ) + return ErrTestComplete } - assert.Equal(t, topicName, message.GetTopic(), "received message with wrong topic") - assert.Check(t, bytes.Equal(testMessage, message.GetData()), "received wrong message") - assert.Equal( - t, - p2ps[1].HostID(), - message.GetFrom().String(), - "received message with wrong sender", - ) + testService := service.Function{Func: testFn} + services = append(services, testService) + + err := service.Run(ctx, services...) + assert.Error(t, err, ErrTestComplete.Error()) } From 249876f626acd7ad1c7512e23b58411d6b9f39d7 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Wed, 31 Jan 2024 15:57:50 +0100 Subject: [PATCH 30/91] chore!: add arg to retry.ExponentialBackoff option --- rolling-shutter/collator/epochhandling.go | 2 +- rolling-shutter/medley/retry/options.go | 34 +++++++++++++++------- rolling-shutter/medley/retry/retry.go | 21 ++++++++----- rolling-shutter/medley/retry/retry_test.go | 4 +-- rolling-shutter/p2p/bootstrap.go | 9 +++--- 5 files changed, 46 insertions(+), 24 deletions(-) diff --git a/rolling-shutter/collator/epochhandling.go b/rolling-shutter/collator/epochhandling.go index 4b452ae2b..5603cbb84 100644 --- a/rolling-shutter/collator/epochhandling.go +++ b/rolling-shutter/collator/epochhandling.go @@ -140,7 +140,7 @@ func (c *collator) sendDecryptionTriggers(ctx context.Context) error { err := c.p2p.SendMessage(ctx, msg, retry.Interval(time.Second), - retry.ExponentialBackoff(), + retry.ExponentialBackoff(nil), retry.NumberOfRetries(3), retry.LogIdentifier(msg.LogInfo()), ) diff --git a/rolling-shutter/medley/retry/options.go b/rolling-shutter/medley/retry/options.go index 035d32c50..dfa28c998 100644 --- a/rolling-shutter/medley/retry/options.go +++ b/rolling-shutter/medley/retry/options.go @@ -1,6 +1,7 @@ package retry import ( + "errors" "time" "github.com/benbjohnson/clock" @@ -13,43 +14,55 @@ import ( // `-1` is a special value that results in // infinite retries. func NumberOfRetries(n int) Option { - return func(r *retrier) { + return func(r *retrier) error { r.numRetries = n if n == -1 { r.infiniteRetries = true } + return nil } } func MaxInterval(t time.Duration) Option { - return func(r *retrier) { + return func(r *retrier) error { r.maxInterval = t + return nil } } func Interval(t time.Duration) Option { - return func(r *retrier) { + return func(r *retrier) error { r.interval = t + return nil } } func StopOnErrors(e ...error) Option { - return func(r *retrier) { + return func(r *retrier) error { r.cancelingErrors = e + return nil } } -func ExponentialBackoff() Option { - return func(r *retrier) { - // for now just use a fixed value - r.multiplier = 1.5 +func ExponentialBackoff(multiplier *float64) Option { + return func(r *retrier) error { + if multiplier == nil { + r.multiplier = 1.5 + return nil + } + r.multiplier = *multiplier + if r.multiplier <= 1.0 { + return errors.New("can't use value <=1.0 as exponential multiplier") + } + return nil } } func LogIdentifier(s string) Option { id := uuid.NewString() - return func(r *retrier) { + return func(r *retrier) error { r.zlogContext = r.zlogContext.Str("id", id+":"+s) + return nil } } @@ -57,7 +70,8 @@ func LogIdentifier(s string) Option { // implementation than the default `time` // wrapper. Mainly used for mocking. func UseClock(c clock.Clock) Option { - return func(r *retrier) { + return func(r *retrier) error { r.clock = c + return nil } } diff --git a/rolling-shutter/medley/retry/retry.go b/rolling-shutter/medley/retry/retry.go index b902e533d..0e594026f 100644 --- a/rolling-shutter/medley/retry/retry.go +++ b/rolling-shutter/medley/retry/retry.go @@ -5,6 +5,7 @@ import ( "time" "github.com/benbjohnson/clock" + "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/rs/zerolog/log" @@ -38,10 +39,14 @@ func newRetrier() *retrier { } } -func (r *retrier) option(opts []Option) { +func (r *retrier) applyOptions(opts []Option) error { for _, opt := range opts { - opt(r) + err := opt(r) + if err != nil { + return err + } } + return nil } func (r *retrier) iterator(next <-chan time.Time) <-chan time.Time { @@ -74,15 +79,20 @@ func (r *retrier) iterator(next <-chan time.Time) <-chan time.Time { } type ( - Option func(*retrier) + Option func(*retrier) error RetriableFunction[T any] func(ctx context.Context) (T, error) ) // FunctionCall calls the given function multiple times until it doesn't return an error // or one of any optional, user-defined specific errors is returned. func FunctionCall[T any](ctx context.Context, fn RetriableFunction[T], opts ...Option) (T, error) { + var err error + var null T + retrier := newRetrier() - retrier.option(opts) + if err := retrier.applyOptions(opts); err != nil { + return null, errors.Wrap(err, "apply options") + } funcName := introspection.GetFuncName(4) retrier.zlogContext = retrier.zlogContext.Str("funcName", funcName) logger := retrier.zlogContext.Logger() @@ -91,9 +101,6 @@ func FunctionCall[T any](ctx context.Context, fn RetriableFunction[T], opts ...O retry := retrier.iterator(next) - var err error - var null T - callCount := 0 for { diff --git a/rolling-shutter/medley/retry/retry_test.go b/rolling-shutter/medley/retry/retry_test.go index 5cbaf77b4..ddd612eeb 100644 --- a/rolling-shutter/medley/retry/retry_test.go +++ b/rolling-shutter/medley/retry/retry_test.go @@ -63,7 +63,7 @@ var testFlagTable = []testFlags{ []Option{ Interval(baseInterval), NumberOfRetries(5), - ExponentialBackoff(), + ExponentialBackoff(nil), }, errDefault, []time.Duration{ @@ -83,7 +83,7 @@ var testFlagTable = []testFlags{ MaxInterval( multDuration(baseInterval, math.Pow(1.5, 2)), ), - ExponentialBackoff(), + ExponentialBackoff(nil), }, errDefault, []time.Duration{ diff --git a/rolling-shutter/p2p/bootstrap.go b/rolling-shutter/p2p/bootstrap.go index 5527724da..8f301494b 100644 --- a/rolling-shutter/p2p/bootstrap.go +++ b/rolling-shutter/p2p/bootstrap.go @@ -82,12 +82,14 @@ func bootstrap( // A bootstrap node is not required to connect to other bootstrap nodes. // If however we did configure a list of bootstrap nodes, // we should try a long time to connect to at least one other bootstrapper first. + backoffMult := float64(1.01) _, err := retry.FunctionCall( ctx, f, - retry.MaxInterval(5*time.Hour), + retry.MaxInterval(1*time.Minute), retry.StopOnErrors(errInsufficientBootstrpConfigured), - retry.Interval(2*time.Minute)) + retry.Interval(2*time.Second), + retry.ExponentialBackoff(&backoffMult)) if err != nil { log.Error().Err(err). Msg("failed to bootstrap, continuing without peer connections.") @@ -96,9 +98,8 @@ func bootstrap( _, err := retry.FunctionCall( ctx, f, - retry.MaxInterval(5*time.Minute), retry.StopOnErrors(errInsufficientBootstrpConfigured), - retry.Interval(30*time.Second)) + retry.Interval(2*time.Second)) if err != nil { // For normal peers, after trying some time it is reasonable to halt. // If we don't get an initial connection to a bootsrap node, From c37dacdaa03be44143643bde678b5d68682ae8ff Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Wed, 31 Jan 2024 16:04:14 +0100 Subject: [PATCH 31/91] chore: remove old documentation --- .../docs/rolling-shutter_optimismkeyper.md | 34 ------------------- ...-shutter_optimismkeyper_generate-config.md | 28 --------------- .../rolling-shutter_optimismkeyper_initdb.md | 27 --------------- 3 files changed, 89 deletions(-) delete mode 100644 rolling-shutter/docs/rolling-shutter_optimismkeyper.md delete mode 100644 rolling-shutter/docs/rolling-shutter_optimismkeyper_generate-config.md delete mode 100644 rolling-shutter/docs/rolling-shutter_optimismkeyper_initdb.md diff --git a/rolling-shutter/docs/rolling-shutter_optimismkeyper.md b/rolling-shutter/docs/rolling-shutter_optimismkeyper.md deleted file mode 100644 index bf0a546e0..000000000 --- a/rolling-shutter/docs/rolling-shutter_optimismkeyper.md +++ /dev/null @@ -1,34 +0,0 @@ -## rolling-shutter optimismkeyper - -Run a Shutter optimism keyper node - -### Synopsis - -This command runs a keyper node. It will connect to both an Optimism and a -Shuttermint node which have to be started separately in advance. - -``` -rolling-shutter optimismkeyper [flags] -``` - -### Options - -``` - --config string config file - -h, --help help for optimismkeyper -``` - -### Options inherited from parent commands - -``` - --logformat string set log format, possible values: min, short, long, max (default "long") - --loglevel string set log level, possible values: warn, info, debug (default "info") - --no-color do not write colored logs -``` - -### SEE ALSO - -* [rolling-shutter](rolling-shutter.md) - A collection of commands to run and interact with Rolling Shutter nodes -* [rolling-shutter optimismkeyper generate-config](rolling-shutter_optimismkeyper_generate-config.md) - Generate a 'optimismkeyper' configuration file -* [rolling-shutter optimismkeyper initdb](rolling-shutter_optimismkeyper_initdb.md) - Initialize the database of the 'optimismkeyper' - diff --git a/rolling-shutter/docs/rolling-shutter_optimismkeyper_generate-config.md b/rolling-shutter/docs/rolling-shutter_optimismkeyper_generate-config.md deleted file mode 100644 index 55d179a08..000000000 --- a/rolling-shutter/docs/rolling-shutter_optimismkeyper_generate-config.md +++ /dev/null @@ -1,28 +0,0 @@ -## rolling-shutter optimismkeyper generate-config - -Generate a 'optimismkeyper' configuration file - -``` -rolling-shutter optimismkeyper generate-config [flags] -``` - -### Options - -``` - -h, --help help for generate-config - --output string output file -``` - -### Options inherited from parent commands - -``` - --config string config file - --logformat string set log format, possible values: min, short, long, max (default "long") - --loglevel string set log level, possible values: warn, info, debug (default "info") - --no-color do not write colored logs -``` - -### SEE ALSO - -* [rolling-shutter optimismkeyper](rolling-shutter_optimismkeyper.md) - Run a Shutter optimism keyper node - diff --git a/rolling-shutter/docs/rolling-shutter_optimismkeyper_initdb.md b/rolling-shutter/docs/rolling-shutter_optimismkeyper_initdb.md deleted file mode 100644 index cec7e7bc0..000000000 --- a/rolling-shutter/docs/rolling-shutter_optimismkeyper_initdb.md +++ /dev/null @@ -1,27 +0,0 @@ -## rolling-shutter optimismkeyper initdb - -Initialize the database of the 'optimismkeyper' - -``` -rolling-shutter optimismkeyper initdb [flags] -``` - -### Options - -``` - -h, --help help for initdb -``` - -### Options inherited from parent commands - -``` - --config string config file - --logformat string set log format, possible values: min, short, long, max (default "long") - --loglevel string set log level, possible values: warn, info, debug (default "info") - --no-color do not write colored logs -``` - -### SEE ALSO - -* [rolling-shutter optimismkeyper](rolling-shutter_optimismkeyper.md) - Run a Shutter optimism keyper node - From 715ff9a0c79304f4bef0392c9a8052ec1bd2734a Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Wed, 31 Jan 2024 16:40:15 +0100 Subject: [PATCH 32/91] chore: update pre-commit dependencies --- .pre-commit-config.yaml | 8 ++++---- rolling-shutter/app/app.go | 1 - rolling-shutter/collator/batcher/batcher.go | 1 - 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 23df433ec..6eb4b73d5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: require_serial: true - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-added-large-files args: ["--maxkb=1000"] @@ -33,7 +33,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/pre-commit/mirrors-prettier - rev: v3.0.1 + rev: v3.1.0 hooks: - id: prettier additional_dependencies: @@ -59,12 +59,12 @@ repos: ] - repo: https://github.com/pre-commit/mirrors-eslint - rev: "v8.4.1" + rev: "v8.56.0" hooks: - id: eslint - repo: https://github.com/shutter-network/pre-commit-go-hooks - rev: "7a66f5523b34139615a0c95f2b8a441dbc1778dc" + rev: "53239641ec106cda9a7acf9150c98be8d5ffa1ec" hooks: - id: shfmt args: ["-i", "4"] diff --git a/rolling-shutter/app/app.go b/rolling-shutter/app/app.go index cb61cd4b9..0da1f98fd 100644 --- a/rolling-shutter/app/app.go +++ b/rolling-shutter/app/app.go @@ -306,7 +306,6 @@ func (ShutterApp) decodeTx(tx []byte) (signer common.Address, msg *shmsg.Message } msg, err = shmsg.GetMessage(signedMsg) - if err != nil { return } diff --git a/rolling-shutter/collator/batcher/batcher.go b/rolling-shutter/collator/batcher/batcher.go index 27d016616..8d5969d0f 100644 --- a/rolling-shutter/collator/batcher/batcher.go +++ b/rolling-shutter/collator/batcher/batcher.go @@ -320,7 +320,6 @@ func (btchr *Batcher) CloseBatch(ctx context.Context) error { err = btchr.dbpool.BeginFunc(ctx, func(dbtx pgx.Tx) error { return btchr.closeBatchImpl(ctx, database.New(dbtx), int64(l1blockNumber)) }) - if err != nil { return err } From d8143b6fcfda6ff604a174eddead4b67bfc770b5 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Wed, 31 Jan 2024 16:55:16 +0100 Subject: [PATCH 33/91] chore: update pre-commit version --- .tool-versions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.tool-versions b/.tool-versions index 6089a4023..063c31a8c 100644 --- a/.tool-versions +++ b/.tool-versions @@ -7,7 +7,7 @@ golangci-lint 1.55.2 java temurin-17.0.5+8 nodejs 18.17.0 postgres 14.2 -pre-commit 3.3.3 +pre-commit 3.6.0 protoc 22.3 shfmt 3.7.0 solidity 0.8.9 From 39483706304af0c3710525cd1f7b46298c673594 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Mon, 5 Feb 2024 18:18:16 +0100 Subject: [PATCH 34/91] Monitor main chain in gnosis keyper --- .../contract/deployment/deployment.go | 3 +- rolling-shutter/keyperimpl/gnosis/config.go | 14 +++ rolling-shutter/keyperimpl/gnosis/keyper.go | 88 +++++++++++++++++-- .../keyperimpl/optimism/bootstrap/config.go | 3 + .../optimism/bootstrap/keyperset.go | 1 + 5 files changed, 103 insertions(+), 6 deletions(-) diff --git a/rolling-shutter/contract/deployment/deployment.go b/rolling-shutter/contract/deployment/deployment.go index d982dcda2..68b0a4889 100644 --- a/rolling-shutter/contract/deployment/deployment.go +++ b/rolling-shutter/contract/deployment/deployment.go @@ -334,7 +334,8 @@ func LoadChainID(dir string) (uint64, error) { return 0, errors.Wrapf(err, "failed to load chain id file at %s", path) } - chainID, err := strconv.ParseInt(string(data), 10, 64) + chainIDStr := strings.TrimSpace(string(data)) + chainID, err := strconv.ParseInt(chainIDStr, 10, 64) if err != nil { return 0, errors.Wrapf(err, "failed to parse chain id in %s", path) } diff --git a/rolling-shutter/keyperimpl/gnosis/config.go b/rolling-shutter/keyperimpl/gnosis/config.go index ad1549396..c9d9ddac6 100644 --- a/rolling-shutter/keyperimpl/gnosis/config.go +++ b/rolling-shutter/keyperimpl/gnosis/config.go @@ -37,6 +37,14 @@ type Config struct { Gnosis *configuration.EthnodeConfig Shuttermint *kprconfig.ShuttermintConfig Metrics *metricsserver.MetricsConfig + + GnosisContracts *GnosisContracts `shconfig:",required"` +} + +type GnosisContracts struct { + KeyperSetManager common.Address `shconfig:",required"` + KeyBroadcastContract common.Address `shconfig:",required"` + Sequencer common.Address `shconfig:",required"` } func (c *Config) Validate() error { @@ -50,6 +58,11 @@ func (c *Config) Name() string { func (c *Config) SetDefaultValues() error { c.HTTPEnabled = false c.HTTPListenAddress = ":3000" + c.GnosisContracts = &GnosisContracts{ + KeyperSetManager: common.Address{}, + KeyBroadcastContract: common.Address{}, + Sequencer: common.Address{}, + } return nil } @@ -60,6 +73,7 @@ func (c *Config) SetExampleValues() error { } c.InstanceID = 42 c.DatabaseURL = "postgres://pguser:pgpassword@localhost:5432/shutter" + return nil } diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index 28b346176..74d3c1c43 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -2,21 +2,34 @@ package gnosis import ( "context" + "fmt" + "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v4/pgxpool" "github.com/pkg/errors" + "github.com/rs/zerolog/log" + obskeyper "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/epochkghandler" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/kprconfig" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/broker" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync" + syncevent "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" + "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" ) +var ErrParseKeyperSet = errors.New("can't parse KeyperSet") + type Keyper struct { - core *keyper.KeyperCore - config *Config + core *keyper.KeyperCore + config *Config + dbpool *pgxpool.Pool + chainSyncClient *chainsync.Client } func New(c *Config) *Keyper { @@ -26,10 +39,12 @@ func New(c *Config) *Keyper { } func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { + var err error + decrTrigChan := make(chan *broker.Event[*epochkghandler.DecryptionTrigger]) runner.Defer(func() { close(decrTrigChan) }) - dbpool, err := db.Connect(ctx, runner, kpr.config.DatabaseURL, database.Definition.Name()) + kpr.dbpool, err = db.Connect(ctx, runner, kpr.config.DatabaseURL, database.Definition.Name()) if err != nil { return errors.Wrap(err, "failed to connect to database") } @@ -46,11 +61,74 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { Metrics: kpr.config.Metrics, }, decrTrigChan, - keyper.WithDBPool(dbpool), + keyper.WithDBPool(kpr.dbpool), + keyper.NoBroadcastEonPublicKey(), + keyper.WithEonPublicKeyHandler(kpr.newEonPublicKey), ) if err != nil { return errors.Wrap(err, "can't instantiate keyper core") } - return runner.StartService(kpr.core) + kpr.chainSyncClient, err = chainsync.NewClient( + ctx, + chainsync.WithClientURL(kpr.config.Gnosis.EthereumURL), + chainsync.WithKeyperSetManager(kpr.config.GnosisContracts.KeyperSetManager), + chainsync.WithKeyBroadcastContract(kpr.config.GnosisContracts.KeyBroadcastContract), + chainsync.WithSyncNewBlock(kpr.newBlock), + chainsync.WithSyncNewKeyperSet(kpr.newKeyperSet), + chainsync.WithPrivateKey(kpr.config.Gnosis.PrivateKey.Key), + ) + if err != nil { + return err + } + + return runner.StartService(kpr.core, kpr.chainSyncClient) +} + +func (kpr *Keyper) newBlock(_ context.Context, ev *syncevent.LatestBlock) error { + log.Info(). + Uint64("number", ev.Number.Uint64()). + Str("hash", ev.BlockHash.Hex()). + Msg("new latest block") + return nil +} + +func (kpr *Keyper) newKeyperSet(ctx context.Context, ev *syncevent.KeyperSet) error { + log.Info(). + Uint64("activation-block", ev.ActivationBlock). + Uint64("eon", ev.Eon). + Msg("new keyper set added") + fmt.Printf("%+v\n", ev.Members) + + return kpr.dbpool.BeginFunc(ctx, func(tx pgx.Tx) error { + obskeyperdb := obskeyper.New(tx) + + keyperConfigIndex, err := medley.Uint64ToInt64Safe(ev.Eon) + if err != nil { + return errors.Wrap(err, ErrParseKeyperSet.Error()) + } + activationBlockNumber, err := medley.Uint64ToInt64Safe(ev.ActivationBlock) + if err != nil { + return errors.Wrap(err, ErrParseKeyperSet.Error()) + } + threshold, err := medley.Uint64ToInt64Safe(ev.Threshold) + if err != nil { + return errors.Wrap(err, ErrParseKeyperSet.Error()) + } + + return obskeyperdb.InsertKeyperSet(ctx, obskeyper.InsertKeyperSetParams{ + KeyperConfigIndex: keyperConfigIndex, + ActivationBlockNumber: activationBlockNumber, + Keypers: shdb.EncodeAddresses(ev.Members), + Threshold: int32(threshold), + }) + }) +} + +func (kpr *Keyper) newEonPublicKey(_ context.Context, pubKey keyper.EonPublicKey) error { + log.Info(). + Uint64("eon", pubKey.Eon). + Uint64("activation-block", pubKey.ActivationBlock). + Msg("new eon pk") + return nil } diff --git a/rolling-shutter/keyperimpl/optimism/bootstrap/config.go b/rolling-shutter/keyperimpl/optimism/bootstrap/config.go index 6f0381d28..5e1e55862 100644 --- a/rolling-shutter/keyperimpl/optimism/bootstrap/config.go +++ b/rolling-shutter/keyperimpl/optimism/bootstrap/config.go @@ -4,6 +4,8 @@ import ( "crypto/rand" "io" + "github.com/ethereum/go-ethereum/common" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/keys" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" @@ -27,6 +29,7 @@ type Config struct { JSONRPCURL string ` comment:"The op-geth JSON RPC endpoint"` ByActivationBlockNumber *number.BlockNumber + KeyperSetManager common.Address ByIndex *uint64 KeyperSetFilePath string diff --git a/rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go b/rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go index 7cb886c95..cbf30cee3 100644 --- a/rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go +++ b/rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go @@ -15,6 +15,7 @@ func GetKeyperSet(ctx context.Context, config *Config) error { sl2, err := chainsync.NewClient( ctx, chainsync.WithClientURL(config.JSONRPCURL), + chainsync.WithKeyperSetManager(config.KeyperSetManager), ) if err != nil { return err From c22cfdaab7a0cd63a344ed33540bcca1565b1bbf Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Mon, 29 Jan 2024 18:24:24 +0100 Subject: [PATCH 35/91] Send trigger at each block --- rolling-shutter/keyperimpl/gnosis/keyper.go | 4 +- rolling-shutter/keyperimpl/gnosis/trigger.go | 80 ++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 rolling-shutter/keyperimpl/gnosis/trigger.go diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index 74d3c1c43..279243c80 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -69,6 +69,8 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { return errors.Wrap(err, "can't instantiate keyper core") } + triggerer := NewDecryptionTriggerer(kpr.config.Gnosis, decrTrigChan) + kpr.chainSyncClient, err = chainsync.NewClient( ctx, chainsync.WithClientURL(kpr.config.Gnosis.EthereumURL), @@ -82,7 +84,7 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { return err } - return runner.StartService(kpr.core, kpr.chainSyncClient) + return runner.StartService(kpr.core, triggerer, kpr.chainSyncClient) } func (kpr *Keyper) newBlock(_ context.Context, ev *syncevent.LatestBlock) error { diff --git a/rolling-shutter/keyperimpl/gnosis/trigger.go b/rolling-shutter/keyperimpl/gnosis/trigger.go new file mode 100644 index 000000000..f1e9377c9 --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/trigger.go @@ -0,0 +1,80 @@ +package gnosis + +import ( + "context" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/rs/zerolog/log" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/epochkghandler" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/broker" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" +) + +type DecryptionTriggerer struct { + config *configuration.EthnodeConfig + decryptionTriggerChannel chan *broker.Event[*epochkghandler.DecryptionTrigger] +} + +func NewDecryptionTriggerer( + config *configuration.EthnodeConfig, + decryptionTriggerChannel chan *broker.Event[*epochkghandler.DecryptionTrigger], +) *DecryptionTriggerer { + return &DecryptionTriggerer{ + config: config, + decryptionTriggerChannel: decryptionTriggerChannel, + } +} + +func (t *DecryptionTriggerer) Start(ctx context.Context, runner service.Runner) error { + client, err := ethclient.DialContext(ctx, t.config.EthereumURL) + if err != nil { + return err + } + runner.Go(func() error { + headers := make(chan *types.Header) + headSubscription, err := client.SubscribeNewHead(ctx, headers) + if err != nil { + return err + } + + for { + select { + case err := <-headSubscription.Err(): + return err + case header := <-headers: + err := t.handleNewHead(ctx, header) + if err != nil { + return err + } + case <-ctx.Done(): + return ctx.Err() + } + } + }) + return nil +} + +func (t *DecryptionTriggerer) handleNewHead(_ context.Context, header *types.Header) error { + log.Debug().Uint64("block-number", header.Number.Uint64()).Msg("handling new head") + + // create some random identities for testing + maxNumIdentityPreimages := uint64(2) + numIdentityPreimages := header.Number.Uint64()%maxNumIdentityPreimages + 1 + identityPreimages := []identitypreimage.IdentityPreimage{} + for i := 0; i < int(numIdentityPreimages); i++ { + n := header.Number.Uint64()*maxNumIdentityPreimages + uint64(i) + identityPreimage := identitypreimage.Uint64ToIdentityPreimage(n) + identityPreimages = append(identityPreimages, identityPreimage) + } + trigger := epochkghandler.DecryptionTrigger{ + BlockNumber: header.Number.Uint64(), + IdentityPreimages: identityPreimages, + } + event := broker.NewEvent(&trigger) + t.decryptionTriggerChannel <- event + return nil +} From c63b310d428d3a640eb693d1eb7c7fdcac947e8a Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Wed, 7 Feb 2024 19:02:21 +0100 Subject: [PATCH 36/91] Implement sequencer syncer --- .../db/keyper/keyper.sqlc.gen.go | 30 +++++ .../db/keyper/sql/queries/keyper.sql | 4 + rolling-shutter/go.mod | 24 ++-- rolling-shutter/go.sum | 48 ++++---- .../keyperimpl/gnosis/database/db.sqlc.gen.go | 32 +++++ .../keyperimpl/gnosis/database/definition.go | 26 +++- .../gnosis/database/gnosiskeyper.sqlc.gen.go | 73 ++++++++++++ .../gnosis/database/models.sqlc.gen.go | 23 ++++ .../database/sql/queries/gnosiskeyper.sql | 21 ++++ .../database/sql/schemas/gnosiskeyper.sql | 20 ++++ .../keyperimpl/gnosis/database/sql/sqlc.yaml | 13 ++ rolling-shutter/keyperimpl/gnosis/keyper.go | 104 ++++++++++++++-- .../keyperimpl/gnosis/sequencersyncer.go | 111 ++++++++++++++++++ rolling-shutter/medley/logger/noop.go | 29 +++-- 14 files changed, 502 insertions(+), 56 deletions(-) create mode 100644 rolling-shutter/keyperimpl/gnosis/database/db.sqlc.gen.go create mode 100644 rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go create mode 100644 rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go create mode 100644 rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql create mode 100644 rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql create mode 100644 rolling-shutter/keyperimpl/gnosis/database/sql/sqlc.yaml create mode 100644 rolling-shutter/keyperimpl/gnosis/sequencersyncer.go diff --git a/rolling-shutter/chainobserver/db/keyper/keyper.sqlc.gen.go b/rolling-shutter/chainobserver/db/keyper/keyper.sqlc.gen.go index 12d2bc318..353ec0a05 100644 --- a/rolling-shutter/chainobserver/db/keyper/keyper.sqlc.gen.go +++ b/rolling-shutter/chainobserver/db/keyper/keyper.sqlc.gen.go @@ -43,6 +43,36 @@ func (q *Queries) GetKeyperSetByKeyperConfigIndex(ctx context.Context, keyperCon return i, err } +const getKeyperSets = `-- name: GetKeyperSets :many +SELECT keyper_config_index, activation_block_number, keypers, threshold FROM keyper_set +ORDER BY activation_block_number ASC +` + +func (q *Queries) GetKeyperSets(ctx context.Context) ([]KeyperSet, error) { + rows, err := q.db.Query(ctx, getKeyperSets) + if err != nil { + return nil, err + } + defer rows.Close() + var items []KeyperSet + for rows.Next() { + var i KeyperSet + if err := rows.Scan( + &i.KeyperConfigIndex, + &i.ActivationBlockNumber, + &i.Keypers, + &i.Threshold, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const insertKeyperSet = `-- name: InsertKeyperSet :exec INSERT INTO keyper_set ( keyper_config_index, diff --git a/rolling-shutter/chainobserver/db/keyper/sql/queries/keyper.sql b/rolling-shutter/chainobserver/db/keyper/sql/queries/keyper.sql index 1e8bea09a..10fee50a1 100644 --- a/rolling-shutter/chainobserver/db/keyper/sql/queries/keyper.sql +++ b/rolling-shutter/chainobserver/db/keyper/sql/queries/keyper.sql @@ -15,3 +15,7 @@ SELECT * FROM keyper_set WHERE keyper_config_index=$1; SELECT * FROM keyper_set WHERE activation_block_number <= $1 ORDER BY activation_block_number DESC LIMIT 1; + +-- name: GetKeyperSets :many +SELECT * FROM keyper_set +ORDER BY activation_block_number ASC; \ No newline at end of file diff --git a/rolling-shutter/go.mod b/rolling-shutter/go.mod index 140f0135e..88a03e948 100644 --- a/rolling-shutter/go.mod +++ b/rolling-shutter/go.mod @@ -9,7 +9,7 @@ require ( github.com/benbjohnson/clock v1.3.5 github.com/bitwurx/jrpc2 v0.0.0-20220302204700-52c6dbbeb536 github.com/deepmap/oapi-codegen v1.9.1 - github.com/ethereum/go-ethereum v1.13.5 + github.com/ethereum/go-ethereum v1.13.11 github.com/getkin/kin-openapi v0.87.0 github.com/go-chi/chi/v5 v5.0.10 github.com/google/go-cmp v0.6.0 @@ -29,6 +29,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.17.0 github.com/rs/zerolog v1.28.0 + github.com/shutter-network/gnosh-contracts v0.2.0 github.com/shutter-network/shop-contracts v0.0.0-20231220085304-80b8977d0bca github.com/shutter-network/shutter/shlib v0.1.13 github.com/shutter-network/txtypes v0.1.0 @@ -44,7 +45,8 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.21.0 go.opentelemetry.io/otel/trace v1.21.0 go.opentelemetry.io/proto/otlp v1.0.0 - golang.org/x/crypto v0.15.0 + golang.org/x/crypto v0.17.0 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa golang.org/x/sync v0.5.0 google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v3 v3.0.1 @@ -58,7 +60,7 @@ require ( github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/allegro/bigcache v1.2.1 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.7.0 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect github.com/cespare/xxhash v1.1.0 // indirect @@ -74,6 +76,7 @@ require ( github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cosmos/gogoproto v1.4.1 // indirect github.com/cosmos/gorocksdb v1.2.0 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/creachadair/taskgroup v0.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -92,6 +95,7 @@ require ( github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect + github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect github.com/getsentry/sentry-go v0.18.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-kit/kit v0.12.0 // indirect @@ -99,10 +103,9 @@ require ( github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/swag v0.22.4 // indirect - github.com/go-stack/stack v1.8.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gofrs/flock v0.8.1 // indirect @@ -123,7 +126,7 @@ require ( github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect - github.com/holiman/uint256 v1.2.3 // indirect + github.com/holiman/uint256 v1.2.4 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/ipfs/boxo v0.10.0 // indirect @@ -229,13 +232,12 @@ require ( go.uber.org/mock v0.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.14.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.18.0 // indirect + golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.4.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/tools v0.15.0 // indirect gonum.org/v1/gonum v0.13.0 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/grpc v1.56.2 // indirect diff --git a/rolling-shutter/go.sum b/rolling-shutter/go.sum index a7869a7d7..14532a1e8 100644 --- a/rolling-shutter/go.sum +++ b/rolling-shutter/go.sum @@ -78,8 +78,8 @@ github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZx github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= -github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= @@ -151,6 +151,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creachadair/taskgroup v0.3.2 h1:zlfutDS+5XG40AOxcHDSThxKzns8Tnr9jnr6VqkYlkM= @@ -200,8 +202,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk= -github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= +github.com/ethereum/go-ethereum v1.13.11 h1:b51Dsm+rEg7anFRUMGB8hODXHvNfcRKzz9vcj8wSdUs= +github.com/ethereum/go-ethereum v1.13.11/go.mod h1:gFtlVORuUcT+UUIcJ/veCNjkuOSujCi338uSHJrYAew= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= @@ -226,6 +228,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= github.com/getkin/kin-openapi v0.87.0 h1:eeb0WBIgRiXra7ZY0Vo+jWloqvaF2kNEaxAyb+39N+E= github.com/getkin/kin-openapi v0.87.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= @@ -257,8 +261,9 @@ github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= @@ -274,8 +279,6 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= @@ -421,8 +424,8 @@ github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZ github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= -github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= @@ -828,6 +831,8 @@ github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go. github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/shutter-network/gnosh-contracts v0.2.0 h1:qH3gAhlh5VZzvJcbi044lxFWQ+MAR9GevKKUirWxSlU= +github.com/shutter-network/gnosh-contracts v0.2.0/go.mod h1:QB0d64ybbVFKMrLjrc1tldri87KNjTmKQjhk9jaso2E= github.com/shutter-network/shop-contracts v0.0.0-20231220085304-80b8977d0bca h1:05Ghqw3FqH/UFuYIzc7z6GJyHk3HxAqY3iuY4L3x4Ow= github.com/shutter-network/shop-contracts v0.0.0-20231220085304-80b8977d0bca/go.mod h1:LEWXLRruvxq9fe2oKtJI3xfzbauhfWTjOczHN61RU+4= github.com/shutter-network/shutter/shlib v0.1.13 h1:9YloDJBdhFAKm2GMg4gBNeaJ+Mw9Qzeh5Kz9A2ayp1E= @@ -1019,8 +1024,8 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1031,8 +1036,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1059,8 +1064,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1107,8 +1112,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1203,12 +1208,13 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1295,8 +1301,8 @@ golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/rolling-shutter/keyperimpl/gnosis/database/db.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/db.sqlc.gen.go new file mode 100644 index 000000000..06cf4d902 --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/database/db.sqlc.gen.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.22.0 + +package database + +import ( + "context" + + "github.com/jackc/pgconn" + "github.com/jackc/pgx/v4" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/rolling-shutter/keyperimpl/gnosis/database/definition.go b/rolling-shutter/keyperimpl/gnosis/database/definition.go index 18bfcda0f..e83d8021f 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/definition.go +++ b/rolling-shutter/keyperimpl/gnosis/database/definition.go @@ -1,11 +1,29 @@ package database import ( + "embed" + + "github.com/rs/zerolog/log" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" ) -var Definition = db.NewAggregateDefinition( - "gnosiskeyper", - database.Definition, -) +//go:generate sqlc generate --file sql/sqlc.yaml + +//go:embed sql +var files embed.FS + +var Definition db.Definition + +func init() { + def, err := db.NewSQLCDefinition(files, "sql/", "gnosiskeyper", 1) + if err != nil { + log.Fatal().Err(err).Msg("failed to initialize DB metadata") + } + Definition = db.NewAggregateDefinition( + "gnosiskeyper", + def, + database.Definition, + ) +} diff --git a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go new file mode 100644 index 000000000..a7f57a825 --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go @@ -0,0 +1,73 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.22.0 +// source: gnosiskeyper.sql + +package database + +import ( + "context" + + "github.com/jackc/pgconn" +) + +const getTransactionSubmittedEventsSyncedUntil = `-- name: GetTransactionSubmittedEventsSyncedUntil :one +SELECT block_number FROM transaction_submitted_events_synced_until LIMIT 1 +` + +func (q *Queries) GetTransactionSubmittedEventsSyncedUntil(ctx context.Context) (int64, error) { + row := q.db.QueryRow(ctx, getTransactionSubmittedEventsSyncedUntil) + var block_number int64 + err := row.Scan(&block_number) + return block_number, err +} + +const insertTransactionSubmittedEvent = `-- name: InsertTransactionSubmittedEvent :execresult +INSERT INTO transaction_submitted_event ( + block_number, + block_hash, + tx_index, + log_index, + eon, + identity_prefix, + sender, + gas_limit +) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8) +ON CONFLICT DO NOTHING +` + +type InsertTransactionSubmittedEventParams struct { + BlockNumber int64 + BlockHash []byte + TxIndex int64 + LogIndex int64 + Eon int64 + IdentityPrefix []byte + Sender string + GasLimit int64 +} + +func (q *Queries) InsertTransactionSubmittedEvent(ctx context.Context, arg InsertTransactionSubmittedEventParams) (pgconn.CommandTag, error) { + return q.db.Exec(ctx, insertTransactionSubmittedEvent, + arg.BlockNumber, + arg.BlockHash, + arg.TxIndex, + arg.LogIndex, + arg.Eon, + arg.IdentityPrefix, + arg.Sender, + arg.GasLimit, + ) +} + +const setTransactionSubmittedEventsSyncedUntil = `-- name: SetTransactionSubmittedEventsSyncedUntil :exec +INSERT INTO transaction_submitted_events_synced_until (block_number) VALUES ($1) +ON CONFLICT (enforce_one_row) DO UPDATE +SET block_number = $1 +` + +func (q *Queries) SetTransactionSubmittedEventsSyncedUntil(ctx context.Context, blockNumber int64) error { + _, err := q.db.Exec(ctx, setTransactionSubmittedEventsSyncedUntil, blockNumber) + return err +} diff --git a/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go new file mode 100644 index 000000000..7f80b8cd5 --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go @@ -0,0 +1,23 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.22.0 + +package database + +import () + +type TransactionSubmittedEvent struct { + BlockNumber int64 + BlockHash []byte + TxIndex int64 + LogIndex int64 + Eon int64 + IdentityPrefix []byte + Sender string + GasLimit int64 +} + +type TransactionSubmittedEventsSyncedUntil struct { + EnforceOneRow bool + BlockNumber int64 +} diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql new file mode 100644 index 000000000..ae45a881c --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql @@ -0,0 +1,21 @@ +-- name: InsertTransactionSubmittedEvent :execresult +INSERT INTO transaction_submitted_event ( + block_number, + block_hash, + tx_index, + log_index, + eon, + identity_prefix, + sender, + gas_limit +) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8) +ON CONFLICT DO NOTHING; + +-- name: SetTransactionSubmittedEventsSyncedUntil :exec +INSERT INTO transaction_submitted_events_synced_until (block_number) VALUES ($1) +ON CONFLICT (enforce_one_row) DO UPDATE +SET block_number = $1; + +-- name: GetTransactionSubmittedEventsSyncedUntil :one +SELECT block_number FROM transaction_submitted_events_synced_until LIMIT 1; \ No newline at end of file diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql new file mode 100644 index 000000000..c0746e0ef --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql @@ -0,0 +1,20 @@ +-- schema-version: gnosiskeyper-1 -- +-- Please change the version above if you make incompatible changes to +-- the schema. We'll use this to check we're using the right schema. + +CREATE TABLE transaction_submitted_event ( + block_number bigint CHECK (block_number >= 0), + block_hash bytea, + tx_index bigint CHECK (tx_index >= 0), + log_index bigint CHECK (log_index >= 0), + eon bigint NOT NULL CHECK (eon >= 0), + identity_prefix bytea NOT NULL, + sender text NOT NULL, + gas_limit bigint NOT NULL CHECK (gas_limit >= 0), + PRIMARY KEY (block_number, block_hash, tx_index, log_index) +); + +create table transaction_submitted_events_synced_until( + enforce_one_row bool primary key default true, + block_number bigint not null CHECK (block_number > 0) +); \ No newline at end of file diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/sqlc.yaml b/rolling-shutter/keyperimpl/gnosis/database/sql/sqlc.yaml new file mode 100644 index 000000000..63efe1093 --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/sqlc.yaml @@ -0,0 +1,13 @@ +version: "2" +sql: + - schema: "schemas" + queries: "queries" + engine: "postgresql" + gen: + go: + package: "database" + out: "../" + sql_package: "pgx/v4" + output_db_file_name: "db.sqlc.gen.go" + output_models_file_name: "models.sqlc.gen.go" + output_files_suffix: "c.gen" diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index 279243c80..dde686b3a 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -2,12 +2,14 @@ package gnosis import ( "context" - "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/pgxpool" "github.com/pkg/errors" "github.com/rs/zerolog/log" + sequencerBindings "github.com/shutter-network/gnosh-contracts/gnoshcontracts/sequencer" obskeyper "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" @@ -30,6 +32,8 @@ type Keyper struct { config *Config dbpool *pgxpool.Pool chainSyncClient *chainsync.Client + + sequencerSyncer *SequencerSyncer } func New(c *Config) *Keyper { @@ -84,23 +88,109 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { return err } + err = kpr.initSequencerSyncer(ctx) + if err != nil { + return err + } + return runner.StartService(kpr.core, triggerer, kpr.chainSyncClient) } -func (kpr *Keyper) newBlock(_ context.Context, ev *syncevent.LatestBlock) error { - log.Info(). - Uint64("number", ev.Number.Uint64()). - Str("hash", ev.BlockHash.Hex()). - Msg("new latest block") +// initSequencerSycer initializes the sequencer syncer if the keyper is known to be a member of a +// keyper set. Otherwise, the syncer will only be initialied once such a keyper set is observed to +// be added, as only then we will know which eon(s) we are responsible for. +func (kpr *Keyper) initSequencerSyncer(ctx context.Context) error { + obskeyperdb := obskeyper.New(kpr.dbpool) + keyperSets, err := obskeyperdb.GetKeyperSets(ctx) + if err != nil { + return errors.Wrap(err, "failed to query keyper sets from db") + } + + keyperSetFound := false + minEon := uint64(0) + for _, keyperSet := range keyperSets { + for _, m := range keyperSet.Keypers { + mAddress := common.HexToAddress(m) + if mAddress.Cmp(kpr.config.GetAddress()) == 0 { + keyperSetFound = true + if minEon > uint64(keyperSet.KeyperConfigIndex) { + minEon = uint64(keyperSet.KeyperConfigIndex) + } + break + } + } + } + + if keyperSetFound { + err := kpr.ensureSequencerSyncing(ctx, minEon) + if err != nil { + return err + } + } + return nil +} + +func (kpr *Keyper) ensureSequencerSyncing(ctx context.Context, eon uint64) error { + if kpr.sequencerSyncer == nil { + log.Info(). + Uint64("eon", eon). + Str("contract-address", kpr.config.GnosisContracts.KeyperSetManager.Hex()). + Msg("initializing sequencer syncer") + client, err := ethclient.DialContext(ctx, kpr.config.Gnosis.ContractsURL) + if err != nil { + return err + } + contract, err := sequencerBindings.NewSequencer(kpr.config.GnosisContracts.Sequencer, client) + if err != nil { + return err + } + kpr.sequencerSyncer = &SequencerSyncer{ + Contract: contract, + DBPool: kpr.dbpool, + StartEon: eon, + } + + // TODO: perform an initial sync without blocking and/or set start block + } + + if eon < kpr.sequencerSyncer.StartEon { + log.Info(). + Uint64("old-start-eon", kpr.sequencerSyncer.StartEon). + Uint64("new-start-eon", eon). + Msg("decreasing sequencer syncing start eon") + kpr.sequencerSyncer.StartEon = eon + } + return nil +} + +func (kpr *Keyper) newBlock(ctx context.Context, ev *syncevent.LatestBlock) error { + if kpr.sequencerSyncer != nil { + if err := kpr.sequencerSyncer.Sync(ctx, ev.Number.Uint64()); err != nil { + return err + } + } return nil } func (kpr *Keyper) newKeyperSet(ctx context.Context, ev *syncevent.KeyperSet) error { + isMember := false + for _, m := range ev.Members { + if m.Cmp(kpr.config.GetAddress()) == 0 { + isMember = true + break + } + } log.Info(). Uint64("activation-block", ev.ActivationBlock). Uint64("eon", ev.Eon). + Bool("is-member", isMember). Msg("new keyper set added") - fmt.Printf("%+v\n", ev.Members) + + if isMember { + if err := kpr.ensureSequencerSyncing(ctx, ev.Eon); err != nil { + return err + } + } return kpr.dbpool.BeginFunc(ctx, func(tx pgx.Tx) error { obskeyperdb := obskeyper.New(tx) diff --git a/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go b/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go new file mode 100644 index 000000000..86106393f --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go @@ -0,0 +1,111 @@ +package gnosis + +import ( + "context" + "math" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v4/pgxpool" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + sequencerBindings "github.com/shutter-network/gnosh-contracts/gnoshcontracts/sequencer" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley" + "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" +) + +// SequencerSyncer inserts transaction submitted events from the sequencer contract into the database. +type SequencerSyncer struct { + Contract *sequencerBindings.Sequencer + DBPool *pgxpool.Pool + StartEon uint64 +} + +func (s *SequencerSyncer) Sync(ctx context.Context, block uint64) error { + queries := database.New(s.DBPool) + syncedUntilBlock, err := queries.GetTransactionSubmittedEventsSyncedUntil(ctx) + if err != nil && err != pgx.ErrNoRows { + return errors.Wrap(err, "failed to query transaction submitted events sync status") + } + var start uint64 + if err == pgx.ErrNoRows { + start = 0 + } else { + start = uint64(syncedUntilBlock) + 1 + } + + log.Debug(). + Uint64("start-block", start). + Uint64("end-block", block). + Msg("syncing sequencer contract") + + opts := bind.FilterOpts{ + Start: start, + End: &block, + Context: ctx, + } + it, err := s.Contract.SequencerFilterer.FilterTransactionSubmitted(&opts) + if err != nil { + return errors.Wrap(err, "failed to query transaction submitted events") + } + events := []*sequencerBindings.SequencerTransactionSubmitted{} + for it.Next() { + if it.Event.Eon < s.StartEon || + it.Event.Eon > math.MaxInt64 || + !it.Event.GasLimit.IsInt64() { + log.Debug(). + Uint64("eon", it.Event.Eon). + Uint64("block-number", it.Event.Raw.BlockNumber). + Str("block-hash", it.Event.Raw.BlockHash.Hex()). + Uint("tx-index", it.Event.Raw.TxIndex). + Uint("log-index", it.Event.Raw.Index). + Msg("ignoring transaction submitted event") + continue + } + events = append(events, it.Event) + } + if it.Error() != nil { + return errors.Wrap(it.Error(), "failed to iterate transaction submitted events") + } + + if len(events) == 0 { + log.Debug(). + Uint64("start-block", start). + Uint64("end-block", block). + Msg("no transaction submitted events found") + return nil + } + + return s.DBPool.BeginFunc(ctx, func(tx pgx.Tx) error { + queries := database.New(tx) + for _, event := range events { + _, err := queries.InsertTransactionSubmittedEvent(ctx, database.InsertTransactionSubmittedEventParams{ + BlockNumber: int64(event.Raw.BlockNumber), + BlockHash: event.Raw.BlockHash[:], + TxIndex: int64(event.Raw.TxIndex), + LogIndex: int64(event.Raw.Index), + Eon: int64(event.Eon), + IdentityPrefix: event.IdentityPrefix[:], + Sender: shdb.EncodeAddress(event.Sender), + GasLimit: event.GasLimit.Int64(), + }) + if err != nil { + return errors.Wrap(err, "failed to insert transaction submitted event into db") + } + log.Debug(). + Uint64("block", event.Raw.BlockNumber). + Uint64("eon", event.Eon). + Hex("identityPrefix", event.IdentityPrefix[:]). + Hex("sender", event.Sender.Bytes()). + Uint64("gasLimit", event.GasLimit.Uint64()). + Msg("synced new transaction submitted event") + } + newSyncedUntilBlock, err := medley.Uint64ToInt64Safe(block) + if err != nil { + return err + } + return queries.SetTransactionSubmittedEventsSyncedUntil(ctx, newSyncedUntilBlock) + }) +} diff --git a/rolling-shutter/medley/logger/noop.go b/rolling-shutter/medley/logger/noop.go index ebaffb6bb..50d028365 100644 --- a/rolling-shutter/medley/logger/noop.go +++ b/rolling-shutter/medley/logger/noop.go @@ -1,20 +1,23 @@ //nolint:revive package logger -import "github.com/ethereum/go-ethereum/log" +import ( + "context" -type NoopLogHandler struct{} - -func (nh *NoopLogHandler) Log(r *log.Record) error { return nil } + "github.com/ethereum/go-ethereum/log" + "golang.org/x/exp/slog" +) type NoopLogger struct{} -func (n *NoopLogger) New(ctx ...interface{}) log.Logger { return &NoopLogger{} } -func (n *NoopLogger) GetHandler() log.Handler { return &NoopLogHandler{} } -func (n *NoopLogger) SetHandler(h log.Handler) {} -func (n *NoopLogger) Trace(msg string, ctx ...interface{}) {} -func (n *NoopLogger) Debug(msg string, ctx ...interface{}) {} -func (n *NoopLogger) Info(msg string, ctx ...interface{}) {} -func (n *NoopLogger) Warn(msg string, ctx ...interface{}) {} -func (n *NoopLogger) Error(msg string, ctx ...interface{}) {} -func (n *NoopLogger) Crit(msg string, ctx ...interface{}) {} +func (n *NoopLogger) With(ctx ...interface{}) log.Logger { return n } +func (n *NoopLogger) New(ctx ...interface{}) log.Logger { return &NoopLogger{} } +func (n *NoopLogger) Log(level slog.Level, msg string, ctx ...interface{}) {} +func (n *NoopLogger) Trace(msg string, ctx ...interface{}) {} +func (n *NoopLogger) Debug(msg string, ctx ...interface{}) {} +func (n *NoopLogger) Info(msg string, ctx ...interface{}) {} +func (n *NoopLogger) Warn(msg string, ctx ...interface{}) {} +func (n *NoopLogger) Error(msg string, ctx ...interface{}) {} +func (n *NoopLogger) Crit(msg string, ctx ...interface{}) {} +func (n *NoopLogger) Write(level slog.Level, msg string, attrs ...any) {} +func (n *NoopLogger) Enabled(ctx context.Context, level slog.Level) bool { return false } From 1178aabdc577621cdc95c25053e4ec61e969651e Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Thu, 8 Feb 2024 12:16:04 +0100 Subject: [PATCH 37/91] Store tx_pointer in db --- .../gnosis/database/gnosiskeyper.sqlc.gen.go | 67 +++++++++++++++++++ .../gnosis/database/models.sqlc.gen.go | 12 +++- .../database/sql/queries/gnosiskeyper.sql | 20 +++++- .../database/sql/schemas/gnosiskeyper.sql | 21 +++++- 4 files changed, 115 insertions(+), 5 deletions(-) diff --git a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go index a7f57a825..ab7177a7c 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go +++ b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go @@ -7,10 +7,43 @@ package database import ( "context" + "database/sql" "github.com/jackc/pgconn" ) +const getConsensusTxPointer = `-- name: GetConsensusTxPointer :one +SELECT consensus, consensus_block FROM tx_pointer LIMIT 1 +` + +type GetConsensusTxPointerRow struct { + Consensus sql.NullInt64 + ConsensusBlock int64 +} + +func (q *Queries) GetConsensusTxPointer(ctx context.Context) (GetConsensusTxPointerRow, error) { + row := q.db.QueryRow(ctx, getConsensusTxPointer) + var i GetConsensusTxPointerRow + err := row.Scan(&i.Consensus, &i.ConsensusBlock) + return i, err +} + +const getLocalTxPointer = `-- name: GetLocalTxPointer :one +SELECT local, local_block FROM tx_pointer LIMIT 1 +` + +type GetLocalTxPointerRow struct { + Local sql.NullInt64 + LocalBlock int64 +} + +func (q *Queries) GetLocalTxPointer(ctx context.Context) (GetLocalTxPointerRow, error) { + row := q.db.QueryRow(ctx, getLocalTxPointer) + var i GetLocalTxPointerRow + err := row.Scan(&i.Local, &i.LocalBlock) + return i, err +} + const getTransactionSubmittedEventsSyncedUntil = `-- name: GetTransactionSubmittedEventsSyncedUntil :one SELECT block_number FROM transaction_submitted_events_synced_until LIMIT 1 ` @@ -71,3 +104,37 @@ func (q *Queries) SetTransactionSubmittedEventsSyncedUntil(ctx context.Context, _, err := q.db.Exec(ctx, setTransactionSubmittedEventsSyncedUntil, blockNumber) return err } + +const updateConsensusTxPointer = `-- name: UpdateConsensusTxPointer :exec +INSERT INTO tx_pointer (consensus, consensus_block) +VALUES ($1, $2) +ON CONFLICT (enforce_one_row) DO UPDATE +SET consensus = $1, consensus_block = $2 +` + +type UpdateConsensusTxPointerParams struct { + Consensus sql.NullInt64 + ConsensusBlock int64 +} + +func (q *Queries) UpdateConsensusTxPointer(ctx context.Context, arg UpdateConsensusTxPointerParams) error { + _, err := q.db.Exec(ctx, updateConsensusTxPointer, arg.Consensus, arg.ConsensusBlock) + return err +} + +const updateLocalTxPointer = `-- name: UpdateLocalTxPointer :exec +INSERT INTO tx_pointer (local, local_block) +VALUES ($1, $2) +ON CONFLICT (enforce_one_row) DO UPDATE +SET local = $1, local_block = $2 +` + +type UpdateLocalTxPointerParams struct { + Local sql.NullInt64 + LocalBlock int64 +} + +func (q *Queries) UpdateLocalTxPointer(ctx context.Context, arg UpdateLocalTxPointerParams) error { + _, err := q.db.Exec(ctx, updateLocalTxPointer, arg.Local, arg.LocalBlock) + return err +} diff --git a/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go index 7f80b8cd5..661733deb 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go +++ b/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go @@ -4,7 +4,9 @@ package database -import () +import ( + "database/sql" +) type TransactionSubmittedEvent struct { BlockNumber int64 @@ -21,3 +23,11 @@ type TransactionSubmittedEventsSyncedUntil struct { EnforceOneRow bool BlockNumber int64 } + +type TxPointer struct { + EnforceOneRow bool + Local sql.NullInt64 + LocalBlock int64 + Consensus sql.NullInt64 + ConsensusBlock int64 +} diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql index ae45a881c..678ca1cf6 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql @@ -18,4 +18,22 @@ ON CONFLICT (enforce_one_row) DO UPDATE SET block_number = $1; -- name: GetTransactionSubmittedEventsSyncedUntil :one -SELECT block_number FROM transaction_submitted_events_synced_until LIMIT 1; \ No newline at end of file +SELECT block_number FROM transaction_submitted_events_synced_until LIMIT 1; + +-- name: GetLocalTxPointer :one +SELECT local, local_block FROM tx_pointer LIMIT 1; + +-- name: GetConsensusTxPointer :one +SELECT consensus, consensus_block FROM tx_pointer LIMIT 1; + +-- name: UpdateLocalTxPointer :exec +INSERT INTO tx_pointer (local, local_block) +VALUES ($1, $2) +ON CONFLICT (enforce_one_row) DO UPDATE +SET local = $1, local_block = $2; + +-- name: UpdateConsensusTxPointer :exec +INSERT INTO tx_pointer (consensus, consensus_block) +VALUES ($1, $2) +ON CONFLICT (enforce_one_row) DO UPDATE +SET consensus = $1, consensus_block = $2; diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql index c0746e0ef..7bda07fc5 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql @@ -14,7 +14,22 @@ CREATE TABLE transaction_submitted_event ( PRIMARY KEY (block_number, block_hash, tx_index, log_index) ); -create table transaction_submitted_events_synced_until( - enforce_one_row bool primary key default true, - block_number bigint not null CHECK (block_number > 0) +CREATE TABLE transaction_submitted_events_synced_until( + enforce_one_row bool PRIMARY KEY DEFAULT true, + block_number bigint NOT NULL CHECK (block_number > 0) +); + +-- tx_pointer stores what we know about the current value of the tx pointer. There are two +-- sources that are stored independently: The keyper itself (local) and the other keypers +-- (consensus). The local value is updated whenever the keyper sends decryption key shares for some +-- transactions. The consensus value is updated when decryption keys are received from other +-- keypers. The values can be NULL if the keyper has lost track (e.g. after a restart or if no +-- messages have been received recently). Each value is annotated with the number of the block at +-- whose end the tx_pointer is valid. +CREATE TABLE tx_pointer( + enforce_one_row bool PRIMARY KEY DEFAULT true, + local bigint DEFAULT NULL, + local_block bigint NOT NULL DEFAULT 0, + consensus bigint DEFAULT NULL, + consensus_block bigint NOT NULL DEFAULT 0 ); \ No newline at end of file From 1b73c2ac890628e07eaf12a2fbd9f11fb9669df9 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Thu, 8 Feb 2024 15:34:21 +0100 Subject: [PATCH 38/91] Store index of transaction submitted events --- .../gnosis/database/gnosiskeyper.sqlc.gen.go | 35 ++++++++++- .../gnosis/database/models.sqlc.gen.go | 6 ++ .../database/sql/queries/gnosiskeyper.sql | 14 ++++- .../database/sql/schemas/gnosiskeyper.sql | 19 +++--- .../keyperimpl/gnosis/sequencersyncer.go | 60 ++++++++++++++++--- 5 files changed, 117 insertions(+), 17 deletions(-) diff --git a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go index ab7177a7c..53a5e347a 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go +++ b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go @@ -44,6 +44,19 @@ func (q *Queries) GetLocalTxPointer(ctx context.Context) (GetLocalTxPointerRow, return i, err } +const getTransactionSubmittedEventCount = `-- name: GetTransactionSubmittedEventCount :one +SELECT event_count FROM transaction_submitted_event_count +WHERE eon = $1 +LIMIT 1 +` + +func (q *Queries) GetTransactionSubmittedEventCount(ctx context.Context, eon int64) (int64, error) { + row := q.db.QueryRow(ctx, getTransactionSubmittedEventCount, eon) + var event_count int64 + err := row.Scan(&event_count) + return event_count, err +} + const getTransactionSubmittedEventsSyncedUntil = `-- name: GetTransactionSubmittedEventsSyncedUntil :one SELECT block_number FROM transaction_submitted_events_synced_until LIMIT 1 ` @@ -57,6 +70,7 @@ func (q *Queries) GetTransactionSubmittedEventsSyncedUntil(ctx context.Context) const insertTransactionSubmittedEvent = `-- name: InsertTransactionSubmittedEvent :execresult INSERT INTO transaction_submitted_event ( + index, block_number, block_hash, tx_index, @@ -66,11 +80,12 @@ INSERT INTO transaction_submitted_event ( sender, gas_limit ) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT DO NOTHING ` type InsertTransactionSubmittedEventParams struct { + Index int64 BlockNumber int64 BlockHash []byte TxIndex int64 @@ -83,6 +98,7 @@ type InsertTransactionSubmittedEventParams struct { func (q *Queries) InsertTransactionSubmittedEvent(ctx context.Context, arg InsertTransactionSubmittedEventParams) (pgconn.CommandTag, error) { return q.db.Exec(ctx, insertTransactionSubmittedEvent, + arg.Index, arg.BlockNumber, arg.BlockHash, arg.TxIndex, @@ -94,6 +110,23 @@ func (q *Queries) InsertTransactionSubmittedEvent(ctx context.Context, arg Inser ) } +const setTransactionSubmittedEventCount = `-- name: SetTransactionSubmittedEventCount :exec +INSERT INTO transaction_submitted_event_count (eon, event_count) +VALUES ($1, $2) +ON CONFLICT (eon) DO UPDATE +SET event_count = $2 +` + +type SetTransactionSubmittedEventCountParams struct { + Eon int64 + EventCount int64 +} + +func (q *Queries) SetTransactionSubmittedEventCount(ctx context.Context, arg SetTransactionSubmittedEventCountParams) error { + _, err := q.db.Exec(ctx, setTransactionSubmittedEventCount, arg.Eon, arg.EventCount) + return err +} + const setTransactionSubmittedEventsSyncedUntil = `-- name: SetTransactionSubmittedEventsSyncedUntil :exec INSERT INTO transaction_submitted_events_synced_until (block_number) VALUES ($1) ON CONFLICT (enforce_one_row) DO UPDATE diff --git a/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go index 661733deb..be4388222 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go +++ b/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go @@ -9,6 +9,7 @@ import ( ) type TransactionSubmittedEvent struct { + Index int64 BlockNumber int64 BlockHash []byte TxIndex int64 @@ -19,6 +20,11 @@ type TransactionSubmittedEvent struct { GasLimit int64 } +type TransactionSubmittedEventCount struct { + Eon int64 + EventCount int64 +} + type TransactionSubmittedEventsSyncedUntil struct { EnforceOneRow bool BlockNumber int64 diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql index 678ca1cf6..a482a7009 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql @@ -1,5 +1,6 @@ -- name: InsertTransactionSubmittedEvent :execresult INSERT INTO transaction_submitted_event ( + index, block_number, block_hash, tx_index, @@ -9,7 +10,7 @@ INSERT INTO transaction_submitted_event ( sender, gas_limit ) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT DO NOTHING; -- name: SetTransactionSubmittedEventsSyncedUntil :exec @@ -20,6 +21,17 @@ SET block_number = $1; -- name: GetTransactionSubmittedEventsSyncedUntil :one SELECT block_number FROM transaction_submitted_events_synced_until LIMIT 1; +-- name: SetTransactionSubmittedEventCount :exec +INSERT INTO transaction_submitted_event_count (eon, event_count) +VALUES ($1, $2) +ON CONFLICT (eon) DO UPDATE +SET event_count = $2; + +-- name: GetTransactionSubmittedEventCount :one +SELECT event_count FROM transaction_submitted_event_count +WHERE eon = $1 +LIMIT 1; + -- name: GetLocalTxPointer :one SELECT local, local_block FROM tx_pointer LIMIT 1; diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql index 7bda07fc5..d7a2e460a 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql @@ -3,20 +3,25 @@ -- the schema. We'll use this to check we're using the right schema. CREATE TABLE transaction_submitted_event ( - block_number bigint CHECK (block_number >= 0), - block_hash bytea, - tx_index bigint CHECK (tx_index >= 0), - log_index bigint CHECK (log_index >= 0), + index bigint PRIMARY KEY CHECK (index >= 0), + block_number bigint NOT NULL CHECK (block_number >= 0), + block_hash bytea NOT NULL, + tx_index bigint NOT NULL CHECK (tx_index >= 0), + log_index bigint NOT NULL CHECK (log_index >= 0), eon bigint NOT NULL CHECK (eon >= 0), identity_prefix bytea NOT NULL, sender text NOT NULL, - gas_limit bigint NOT NULL CHECK (gas_limit >= 0), - PRIMARY KEY (block_number, block_hash, tx_index, log_index) + gas_limit bigint NOT NULL CHECK (gas_limit >= 0) ); CREATE TABLE transaction_submitted_events_synced_until( enforce_one_row bool PRIMARY KEY DEFAULT true, - block_number bigint NOT NULL CHECK (block_number > 0) + block_number bigint NOT NULL CHECK (block_number >= 0) +); + +CREATE TABLE transaction_submitted_event_count( + eon bigint PRIMARY KEY, + event_count bigint NOT NULL DEFAULT 0 CHECK (event_count >= 0) ); -- tx_pointer stores what we know about the current value of the tx pointer. There are two diff --git a/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go b/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go index 86106393f..af52bcdf6 100644 --- a/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go +++ b/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go @@ -33,7 +33,7 @@ func (s *SequencerSyncer) Sync(ctx context.Context, block uint64) error { if err == pgx.ErrNoRows { start = 0 } else { - start = uint64(syncedUntilBlock) + 1 + start = uint64(syncedUntilBlock + 1) } log.Debug(). @@ -69,19 +69,56 @@ func (s *SequencerSyncer) Sync(ctx context.Context, block uint64) error { if it.Error() != nil { return errors.Wrap(it.Error(), "failed to iterate transaction submitted events") } - if len(events) == 0 { log.Debug(). Uint64("start-block", start). Uint64("end-block", block). Msg("no transaction submitted events found") - return nil } return s.DBPool.BeginFunc(ctx, func(tx pgx.Tx) error { + err = s.insertTransactionSubmittedEvents(ctx, tx, events) + if err != nil { + return err + } + + newSyncedUntilBlock, err := medley.Uint64ToInt64Safe(block) + if err != nil { + return err + } + err = queries.SetTransactionSubmittedEventsSyncedUntil(ctx, newSyncedUntilBlock) + if err != nil { + return err + } + return nil + }) +} + +// insertTransactionSubmittedEvents inserts the given events into the database and updates the +// transaction submitted event number accordingly. +func (s *SequencerSyncer) insertTransactionSubmittedEvents( + ctx context.Context, + tx pgx.Tx, + events []*sequencerBindings.SequencerTransactionSubmitted, +) error { + if len(events) > 0 { queries := database.New(tx) + nextEventIndices := make(map[uint64]int64) for _, event := range events { + nextEventIndex, ok := nextEventIndices[event.Eon] + if !ok { + nextEventIndexFromDB, err := queries.GetTransactionSubmittedEventCount(ctx, int64(event.Eon)) + if err == pgx.ErrNoRows { + nextEventIndexFromDB = 0 + } else if err != nil { + return errors.Wrapf(err, "failed to query count of transaction submitted events for eon %d", event.Eon) + } + nextEventIndices[event.Eon] = nextEventIndexFromDB + nextEventIndex = nextEventIndexFromDB + } + _, err := queries.InsertTransactionSubmittedEvent(ctx, database.InsertTransactionSubmittedEventParams{ + Index: nextEventIndex, BlockNumber: int64(event.Raw.BlockNumber), BlockHash: event.Raw.BlockHash[:], TxIndex: int64(event.Raw.TxIndex), @@ -94,7 +131,9 @@ func (s *SequencerSyncer) Sync(ctx context.Context, block uint64) error { if err != nil { return errors.Wrap(err, "failed to insert transaction submitted event into db") } + nextEventIndices[event.Eon]++ log.Debug(). + Int64("index", nextEventIndex). Uint64("block", event.Raw.BlockNumber). Uint64("eon", event.Eon). Hex("identityPrefix", event.IdentityPrefix[:]). @@ -102,10 +141,15 @@ func (s *SequencerSyncer) Sync(ctx context.Context, block uint64) error { Uint64("gasLimit", event.GasLimit.Uint64()). Msg("synced new transaction submitted event") } - newSyncedUntilBlock, err := medley.Uint64ToInt64Safe(block) - if err != nil { - return err + for eon, nextEventIndex := range nextEventIndices { + err := queries.SetTransactionSubmittedEventCount(ctx, database.SetTransactionSubmittedEventCountParams{ + Eon: int64(eon), + EventCount: nextEventIndex, + }) + if err != nil { + return err + } } - return queries.SetTransactionSubmittedEventsSyncedUntil(ctx, newSyncedUntilBlock) - }) + } + return nil } From d736f49a1107ea014762373807c000ced7e88049 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Mon, 12 Feb 2024 19:20:13 +0100 Subject: [PATCH 39/91] Trigger key generation according to tx pointer --- rolling-shutter/keyperimpl/gnosis/config.go | 6 +- .../gnosis/database/gnosiskeyper.sqlc.gen.go | 142 ++++++++------- .../gnosis/database/models.sqlc.gen.go | 12 +- .../database/sql/queries/gnosiskeyper.sql | 33 ++-- .../database/sql/schemas/gnosiskeyper.sql | 20 +-- rolling-shutter/keyperimpl/gnosis/keyper.go | 161 +++++++++++++++++- .../keyperimpl/gnosis/sequencersyncer.go | 91 +++++----- rolling-shutter/keyperimpl/gnosis/trigger.go | 80 --------- 8 files changed, 313 insertions(+), 232 deletions(-) delete mode 100644 rolling-shutter/keyperimpl/gnosis/trigger.go diff --git a/rolling-shutter/keyperimpl/gnosis/config.go b/rolling-shutter/keyperimpl/gnosis/config.go index c9d9ddac6..61d95ded3 100644 --- a/rolling-shutter/keyperimpl/gnosis/config.go +++ b/rolling-shutter/keyperimpl/gnosis/config.go @@ -38,7 +38,9 @@ type Config struct { Shuttermint *kprconfig.ShuttermintConfig Metrics *metricsserver.MetricsConfig - GnosisContracts *GnosisContracts `shconfig:",required"` + GnosisContracts *GnosisContracts `shconfig:",required"` + EncryptedGasLimit uint64 `shconfig:",required"` + MinGasPerTransaction uint64 `shconfig:",required"` } type GnosisContracts struct { @@ -63,6 +65,8 @@ func (c *Config) SetDefaultValues() error { KeyBroadcastContract: common.Address{}, Sequencer: common.Address{}, } + c.EncryptedGasLimit = 1_000_000 + c.MinGasPerTransaction = 21_000 return nil } diff --git a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go index 53a5e347a..fe9616d3e 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go +++ b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go @@ -7,43 +7,10 @@ package database import ( "context" - "database/sql" "github.com/jackc/pgconn" ) -const getConsensusTxPointer = `-- name: GetConsensusTxPointer :one -SELECT consensus, consensus_block FROM tx_pointer LIMIT 1 -` - -type GetConsensusTxPointerRow struct { - Consensus sql.NullInt64 - ConsensusBlock int64 -} - -func (q *Queries) GetConsensusTxPointer(ctx context.Context) (GetConsensusTxPointerRow, error) { - row := q.db.QueryRow(ctx, getConsensusTxPointer) - var i GetConsensusTxPointerRow - err := row.Scan(&i.Consensus, &i.ConsensusBlock) - return i, err -} - -const getLocalTxPointer = `-- name: GetLocalTxPointer :one -SELECT local, local_block FROM tx_pointer LIMIT 1 -` - -type GetLocalTxPointerRow struct { - Local sql.NullInt64 - LocalBlock int64 -} - -func (q *Queries) GetLocalTxPointer(ctx context.Context) (GetLocalTxPointerRow, error) { - row := q.db.QueryRow(ctx, getLocalTxPointer) - var i GetLocalTxPointerRow - err := row.Scan(&i.Local, &i.LocalBlock) - return i, err -} - const getTransactionSubmittedEventCount = `-- name: GetTransactionSubmittedEventCount :one SELECT event_count FROM transaction_submitted_event_count WHERE eon = $1 @@ -57,6 +24,49 @@ func (q *Queries) GetTransactionSubmittedEventCount(ctx context.Context, eon int return event_count, err } +const getTransactionSubmittedEvents = `-- name: GetTransactionSubmittedEvents :many +SELECT index, block_number, block_hash, tx_index, log_index, eon, identity_prefix, sender, gas_limit FROM transaction_submitted_event +WHERE eon = $1 AND index >= $2 +ORDER BY index ASC +LIMIT $3 +` + +type GetTransactionSubmittedEventsParams struct { + Eon int64 + Index int64 + Limit int32 +} + +func (q *Queries) GetTransactionSubmittedEvents(ctx context.Context, arg GetTransactionSubmittedEventsParams) ([]TransactionSubmittedEvent, error) { + rows, err := q.db.Query(ctx, getTransactionSubmittedEvents, arg.Eon, arg.Index, arg.Limit) + if err != nil { + return nil, err + } + defer rows.Close() + var items []TransactionSubmittedEvent + for rows.Next() { + var i TransactionSubmittedEvent + if err := rows.Scan( + &i.Index, + &i.BlockNumber, + &i.BlockHash, + &i.TxIndex, + &i.LogIndex, + &i.Eon, + &i.IdentityPrefix, + &i.Sender, + &i.GasLimit, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getTransactionSubmittedEventsSyncedUntil = `-- name: GetTransactionSubmittedEventsSyncedUntil :one SELECT block_number FROM transaction_submitted_events_synced_until LIMIT 1 ` @@ -68,6 +78,34 @@ func (q *Queries) GetTransactionSubmittedEventsSyncedUntil(ctx context.Context) return block_number, err } +const getTxPointer = `-- name: GetTxPointer :one +SELECT eon, block, value FROM tx_pointer +WHERE eon = $1 +` + +func (q *Queries) GetTxPointer(ctx context.Context, eon int64) (TxPointer, error) { + row := q.db.QueryRow(ctx, getTxPointer, eon) + var i TxPointer + err := row.Scan(&i.Eon, &i.Block, &i.Value) + return i, err +} + +const initTxPointer = `-- name: InitTxPointer :exec +INSERT INTO tx_pointer (eon, block, value) +VALUES ($1, $2, 0) +ON CONFLICT DO NOTHING +` + +type InitTxPointerParams struct { + Eon int64 + Block int64 +} + +func (q *Queries) InitTxPointer(ctx context.Context, arg InitTxPointerParams) error { + _, err := q.db.Exec(ctx, initTxPointer, arg.Eon, arg.Block) + return err +} + const insertTransactionSubmittedEvent = `-- name: InsertTransactionSubmittedEvent :execresult INSERT INTO transaction_submitted_event ( index, @@ -138,36 +176,20 @@ func (q *Queries) SetTransactionSubmittedEventsSyncedUntil(ctx context.Context, return err } -const updateConsensusTxPointer = `-- name: UpdateConsensusTxPointer :exec -INSERT INTO tx_pointer (consensus, consensus_block) -VALUES ($1, $2) -ON CONFLICT (enforce_one_row) DO UPDATE -SET consensus = $1, consensus_block = $2 -` - -type UpdateConsensusTxPointerParams struct { - Consensus sql.NullInt64 - ConsensusBlock int64 -} - -func (q *Queries) UpdateConsensusTxPointer(ctx context.Context, arg UpdateConsensusTxPointerParams) error { - _, err := q.db.Exec(ctx, updateConsensusTxPointer, arg.Consensus, arg.ConsensusBlock) - return err -} - -const updateLocalTxPointer = `-- name: UpdateLocalTxPointer :exec -INSERT INTO tx_pointer (local, local_block) -VALUES ($1, $2) -ON CONFLICT (enforce_one_row) DO UPDATE -SET local = $1, local_block = $2 +const setTxPointer = `-- name: SetTxPointer :exec +INSERT INTO tx_pointer (eon, block, value) +VALUES ($1, $2, $3) +ON CONFLICT (eon) DO UPDATE +SET block = $2, value = $3 ` -type UpdateLocalTxPointerParams struct { - Local sql.NullInt64 - LocalBlock int64 +type SetTxPointerParams struct { + Eon int64 + Block int64 + Value int64 } -func (q *Queries) UpdateLocalTxPointer(ctx context.Context, arg UpdateLocalTxPointerParams) error { - _, err := q.db.Exec(ctx, updateLocalTxPointer, arg.Local, arg.LocalBlock) +func (q *Queries) SetTxPointer(ctx context.Context, arg SetTxPointerParams) error { + _, err := q.db.Exec(ctx, setTxPointer, arg.Eon, arg.Block, arg.Value) return err } diff --git a/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go index be4388222..ffe9ae8d2 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go +++ b/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go @@ -4,9 +4,7 @@ package database -import ( - "database/sql" -) +import () type TransactionSubmittedEvent struct { Index int64 @@ -31,9 +29,7 @@ type TransactionSubmittedEventsSyncedUntil struct { } type TxPointer struct { - EnforceOneRow bool - Local sql.NullInt64 - LocalBlock int64 - Consensus sql.NullInt64 - ConsensusBlock int64 + Eon int64 + Block int64 + Value int64 } diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql index a482a7009..926e44dad 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql @@ -13,6 +13,12 @@ INSERT INTO transaction_submitted_event ( VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT DO NOTHING; +-- name: GetTransactionSubmittedEvents :many +SELECT * FROM transaction_submitted_event +WHERE eon = $1 AND index >= $2 +ORDER BY index ASC +LIMIT $3; + -- name: SetTransactionSubmittedEventsSyncedUntil :exec INSERT INTO transaction_submitted_events_synced_until (block_number) VALUES ($1) ON CONFLICT (enforce_one_row) DO UPDATE @@ -32,20 +38,17 @@ SELECT event_count FROM transaction_submitted_event_count WHERE eon = $1 LIMIT 1; --- name: GetLocalTxPointer :one -SELECT local, local_block FROM tx_pointer LIMIT 1; - --- name: GetConsensusTxPointer :one -SELECT consensus, consensus_block FROM tx_pointer LIMIT 1; +-- name: GetTxPointer :one +SELECT * FROM tx_pointer +WHERE eon = $1; --- name: UpdateLocalTxPointer :exec -INSERT INTO tx_pointer (local, local_block) -VALUES ($1, $2) -ON CONFLICT (enforce_one_row) DO UPDATE -SET local = $1, local_block = $2; +-- name: InitTxPointer :exec +INSERT INTO tx_pointer (eon, block, value) +VALUES ($1, $2, 0) +ON CONFLICT DO NOTHING; --- name: UpdateConsensusTxPointer :exec -INSERT INTO tx_pointer (consensus, consensus_block) -VALUES ($1, $2) -ON CONFLICT (enforce_one_row) DO UPDATE -SET consensus = $1, consensus_block = $2; +-- name: SetTxPointer :exec +INSERT INTO tx_pointer (eon, block, value) +VALUES ($1, $2, $3) +ON CONFLICT (eon) DO UPDATE +SET block = $2, value = $3; \ No newline at end of file diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql index d7a2e460a..8eccadb6c 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql @@ -3,7 +3,7 @@ -- the schema. We'll use this to check we're using the right schema. CREATE TABLE transaction_submitted_event ( - index bigint PRIMARY KEY CHECK (index >= 0), + index bigint CHECK (index >= 0), block_number bigint NOT NULL CHECK (block_number >= 0), block_hash bytea NOT NULL, tx_index bigint NOT NULL CHECK (tx_index >= 0), @@ -11,7 +11,8 @@ CREATE TABLE transaction_submitted_event ( eon bigint NOT NULL CHECK (eon >= 0), identity_prefix bytea NOT NULL, sender text NOT NULL, - gas_limit bigint NOT NULL CHECK (gas_limit >= 0) + gas_limit bigint NOT NULL CHECK (gas_limit >= 0), + PRIMARY KEY (index, eon) ); CREATE TABLE transaction_submitted_events_synced_until( @@ -24,17 +25,8 @@ CREATE TABLE transaction_submitted_event_count( event_count bigint NOT NULL DEFAULT 0 CHECK (event_count >= 0) ); --- tx_pointer stores what we know about the current value of the tx pointer. There are two --- sources that are stored independently: The keyper itself (local) and the other keypers --- (consensus). The local value is updated whenever the keyper sends decryption key shares for some --- transactions. The consensus value is updated when decryption keys are received from other --- keypers. The values can be NULL if the keyper has lost track (e.g. after a restart or if no --- messages have been received recently). Each value is annotated with the number of the block at --- whose end the tx_pointer is valid. CREATE TABLE tx_pointer( - enforce_one_row bool PRIMARY KEY DEFAULT true, - local bigint DEFAULT NULL, - local_block bigint NOT NULL DEFAULT 0, - consensus bigint DEFAULT NULL, - consensus_block bigint NOT NULL DEFAULT 0 + eon bigint PRIMARY KEY, + block bigint NOT NULL DEFAULT 0, + value bigint NOT NULL DEFAULT 0 ); \ No newline at end of file diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index dde686b3a..6a1b3b2f7 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -1,15 +1,19 @@ package gnosis import ( + "bytes" "context" + "math" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" + gethLog "github.com/ethereum/go-ethereum/log" "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/pgxpool" "github.com/pkg/errors" "github.com/rs/zerolog/log" sequencerBindings "github.com/shutter-network/gnosh-contracts/gnoshcontracts/sequencer" + "golang.org/x/exp/slog" obskeyper "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" @@ -21,11 +25,15 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync" syncevent "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" ) -var ErrParseKeyperSet = errors.New("can't parse KeyperSet") +var ErrParseKeyperSet = errors.New("cannot parse KeyperSet") + +// maximum age of a tx pointer in blocks before it is considered outdated +const maxTxPointerAge = 2 type Keyper struct { core *keyper.KeyperCore @@ -33,7 +41,8 @@ type Keyper struct { dbpool *pgxpool.Pool chainSyncClient *chainsync.Client - sequencerSyncer *SequencerSyncer + sequencerSyncer *SequencerSyncer + decryptionTriggerChannel chan<- *broker.Event[*epochkghandler.DecryptionTrigger] } func New(c *Config) *Keyper { @@ -45,8 +54,9 @@ func New(c *Config) *Keyper { func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { var err error - decrTrigChan := make(chan *broker.Event[*epochkghandler.DecryptionTrigger]) - runner.Defer(func() { close(decrTrigChan) }) + decryptionTriggerChannel := make(chan *broker.Event[*epochkghandler.DecryptionTrigger]) + kpr.decryptionTriggerChannel = decryptionTriggerChannel + runner.Defer(func() { close(kpr.decryptionTriggerChannel) }) kpr.dbpool, err = db.Connect(ctx, runner, kpr.config.DatabaseURL, database.Definition.Name()) if err != nil { @@ -64,7 +74,7 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { Shuttermint: kpr.config.Shuttermint, Metrics: kpr.config.Metrics, }, - decrTrigChan, + decryptionTriggerChannel, keyper.WithDBPool(kpr.dbpool), keyper.NoBroadcastEonPublicKey(), keyper.WithEonPublicKeyHandler(kpr.newEonPublicKey), @@ -73,8 +83,6 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { return errors.Wrap(err, "can't instantiate keyper core") } - triggerer := NewDecryptionTriggerer(kpr.config.Gnosis, decrTrigChan) - kpr.chainSyncClient, err = chainsync.NewClient( ctx, chainsync.WithClientURL(kpr.config.Gnosis.EthereumURL), @@ -83,6 +91,7 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { chainsync.WithSyncNewBlock(kpr.newBlock), chainsync.WithSyncNewKeyperSet(kpr.newKeyperSet), chainsync.WithPrivateKey(kpr.config.Gnosis.PrivateKey.Key), + chainsync.WithLogger(gethLog.NewLogger(slog.Default().Handler())), ) if err != nil { return err @@ -93,11 +102,11 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { return err } - return runner.StartService(kpr.core, triggerer, kpr.chainSyncClient) + return runner.StartService(kpr.core, kpr.chainSyncClient) } // initSequencerSycer initializes the sequencer syncer if the keyper is known to be a member of a -// keyper set. Otherwise, the syncer will only be initialied once such a keyper set is observed to +// keyper set. Otherwise, the syncer will only be initialized once such a keyper set is observed to // be added, as only then we will know which eon(s) we are responsible for. func (kpr *Keyper) initSequencerSyncer(ctx context.Context) error { obskeyperdb := obskeyper.New(kpr.dbpool) @@ -169,6 +178,22 @@ func (kpr *Keyper) newBlock(ctx context.Context, ev *syncevent.LatestBlock) erro return err } } + + queries := obskeyper.New(kpr.dbpool) + keyperSet, err := queries.GetKeyperSet(ctx, ev.Number.Int64()) + if err == pgx.ErrNoRows { + log.Debug().Uint64("block", ev.Number.Uint64()).Msg("ignoring block as no keyper set has been found for it") + return nil + } + if err != nil { + return errors.Wrapf(err, "failed to query keyper set for block %d", ev.Number) + } + for _, m := range keyperSet.Keypers { + if m == shdb.EncodeAddress(kpr.config.GetAddress()) { + return kpr.triggerDecryption(ctx, ev, &keyperSet) + } + } + log.Debug().Uint64("block", ev.Number.Uint64()).Msg("ignoring block as not part of keyper set") return nil } @@ -224,3 +249,121 @@ func (kpr *Keyper) newEonPublicKey(_ context.Context, pubKey keyper.EonPublicKey Msg("new eon pk") return nil } + +func (kpr *Keyper) triggerDecryption(ctx context.Context, ev *syncevent.LatestBlock, keyperSet *obskeyper.KeyperSet) error { + queries := database.New(kpr.dbpool) + eon := keyperSet.KeyperConfigIndex + var txPointer int64 + var txPointerAge int64 + txPointerDB, err := queries.GetTxPointer(ctx, eon) + if err == pgx.ErrNoRows { + txPointer = 0 + txPointerAge = ev.Number.Int64() - keyperSet.ActivationBlockNumber + 1 + } else if err != nil { + return errors.Wrap(err, "failed to query tx pointer from db") + } else { + txPointerAge = ev.Number.Int64() - txPointerDB.Block + txPointer = txPointerDB.Value + } + if txPointerAge == 0 { + // A pointer of age 0 means we already received the pointer from a DecryptionKeys message + // even though we haven't sent our shares yet. In that case, sending our shares is + // unnecessary. + log.Warn(). + Int64("block-number", ev.Number.Int64()). + Int64("eon", eon). + Int64("tx-pointer", txPointer). + Int64("tx-pointer-age", txPointerAge). + Msg("ignoring new block as tx pointer age is 0") + return nil + } + if txPointerAge > maxTxPointerAge { + // If the tx pointer is outdated, the system has failed to generate decryption keys (or at + // least we haven't received them). This either means not enough keypers are online or they + // don't agree on the current value of the tx pointer. In order to recover, we choose the + // current length of the transaction queue as the new tx pointer, as this is a value + // everyone can agree on. + log.Warn(). + Int64("block-number", ev.Number.Int64()). + Int64("eon", eon). + Int64("tx-pointer", txPointer). + Int64("tx-pointer-age", txPointerAge). + Msg("outdated tx pointer") + txPointer, err = queries.GetTransactionSubmittedEventCount(ctx, keyperSet.KeyperConfigIndex) + if err == pgx.ErrNoRows { + txPointer = 0 + } else if err != nil { + return errors.Wrap(err, "failed to query transaction submitted event count from db") + } + } + + identityPreimages, err := kpr.getDecryptionIdentityPreimages(ctx, ev, keyperSet.KeyperConfigIndex, txPointer) + if err != nil { + return err + } + trigger := epochkghandler.DecryptionTrigger{ + BlockNumber: uint64(ev.Number.Int64() + 1), + IdentityPreimages: identityPreimages, + } + event := broker.NewEvent(&trigger) + log.Debug(). + Int64("block-number", int64(trigger.BlockNumber)). + Int("num-identities", len(trigger.IdentityPreimages)). + Int64("tx-pointer", txPointer). + Int64("tx-pointer-age", txPointerAge). + Msg("sending decryption trigger") + kpr.decryptionTriggerChannel <- event + + return nil +} + +func (kpr *Keyper) getDecryptionIdentityPreimages( + ctx context.Context, ev *syncevent.LatestBlock, eon int64, txPointer int64, +) ([]identitypreimage.IdentityPreimage, error) { + identityPreimages := []identitypreimage.IdentityPreimage{} + + queries := database.New(kpr.dbpool) + limitUint64 := kpr.config.EncryptedGasLimit/kpr.config.MinGasPerTransaction + 1 + if limitUint64 > math.MaxInt32 { + return identityPreimages, errors.New("gas limit too big") + } + limit := int32(limitUint64) + + events, err := queries.GetTransactionSubmittedEvents(ctx, database.GetTransactionSubmittedEventsParams{ + Eon: eon, + Index: txPointer, + Limit: limit, + }) + if err != nil { + return nil, errors.Wrapf(err, "failed to query transaction submitted events from index %d", txPointer) + } + + identityPreimages = []identitypreimage.IdentityPreimage{ + makeBlockIdentityPreimage(ev), + } + for _, event := range events { + identityPreimage, err := transactionSubmittedEventToIdentityPreimage(event) + if err != nil { + return []identitypreimage.IdentityPreimage{}, err + } + identityPreimages = append(identityPreimages, identityPreimage) + } + return identityPreimages, nil +} + +func transactionSubmittedEventToIdentityPreimage(event database.TransactionSubmittedEvent) (identitypreimage.IdentityPreimage, error) { + sender, err := shdb.DecodeAddress(event.Sender) + if err != nil { + return identitypreimage.IdentityPreimage{}, errors.Wrap(err, "failed to decode sender address of transaction submitted event from db") + } + + var buf bytes.Buffer + buf.Write(event.IdentityPrefix) + buf.Write(sender.Bytes()) + + return identitypreimage.IdentityPreimage(buf.Bytes()), nil +} + +func makeBlockIdentityPreimage(ev *syncevent.LatestBlock) identitypreimage.IdentityPreimage { + return identitypreimage.IdentityPreimage(ev.Number.Bytes()) +} diff --git a/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go b/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go index af52bcdf6..854f4958a 100644 --- a/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go +++ b/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go @@ -23,6 +23,9 @@ type SequencerSyncer struct { StartEon uint64 } +// Sync fetches transaction submitted events from the sequencer contract and inserts them into the +// database. It starts at the end point of the previous call to sync (or 0 if it is the first call) +// and ends at the given block number. func (s *SequencerSyncer) Sync(ctx context.Context, block uint64) error { queries := database.New(s.DBPool) syncedUntilBlock, err := queries.GetTransactionSubmittedEventsSyncedUntil(ctx) @@ -101,54 +104,52 @@ func (s *SequencerSyncer) insertTransactionSubmittedEvents( tx pgx.Tx, events []*sequencerBindings.SequencerTransactionSubmitted, ) error { - if len(events) > 0 { - queries := database.New(tx) - nextEventIndices := make(map[uint64]int64) - for _, event := range events { - nextEventIndex, ok := nextEventIndices[event.Eon] - if !ok { - nextEventIndexFromDB, err := queries.GetTransactionSubmittedEventCount(ctx, int64(event.Eon)) - if err == pgx.ErrNoRows { - nextEventIndexFromDB = 0 - } else if err != nil { - return errors.Wrapf(err, "failed to query count of transaction submitted events for eon %d", event.Eon) - } - nextEventIndices[event.Eon] = nextEventIndexFromDB - nextEventIndex = nextEventIndexFromDB + queries := database.New(tx) + nextEventIndices := make(map[uint64]int64) + for _, event := range events { + nextEventIndex, ok := nextEventIndices[event.Eon] + if !ok { + nextEventIndexFromDB, err := queries.GetTransactionSubmittedEventCount(ctx, int64(event.Eon)) + if err == pgx.ErrNoRows { + nextEventIndexFromDB = 0 + } else if err != nil { + return errors.Wrapf(err, "failed to query count of transaction submitted events for eon %d", event.Eon) } + nextEventIndices[event.Eon] = nextEventIndexFromDB + nextEventIndex = nextEventIndexFromDB + } - _, err := queries.InsertTransactionSubmittedEvent(ctx, database.InsertTransactionSubmittedEventParams{ - Index: nextEventIndex, - BlockNumber: int64(event.Raw.BlockNumber), - BlockHash: event.Raw.BlockHash[:], - TxIndex: int64(event.Raw.TxIndex), - LogIndex: int64(event.Raw.Index), - Eon: int64(event.Eon), - IdentityPrefix: event.IdentityPrefix[:], - Sender: shdb.EncodeAddress(event.Sender), - GasLimit: event.GasLimit.Int64(), - }) - if err != nil { - return errors.Wrap(err, "failed to insert transaction submitted event into db") - } - nextEventIndices[event.Eon]++ - log.Debug(). - Int64("index", nextEventIndex). - Uint64("block", event.Raw.BlockNumber). - Uint64("eon", event.Eon). - Hex("identityPrefix", event.IdentityPrefix[:]). - Hex("sender", event.Sender.Bytes()). - Uint64("gasLimit", event.GasLimit.Uint64()). - Msg("synced new transaction submitted event") + _, err := queries.InsertTransactionSubmittedEvent(ctx, database.InsertTransactionSubmittedEventParams{ + Index: nextEventIndex, + BlockNumber: int64(event.Raw.BlockNumber), + BlockHash: event.Raw.BlockHash[:], + TxIndex: int64(event.Raw.TxIndex), + LogIndex: int64(event.Raw.Index), + Eon: int64(event.Eon), + IdentityPrefix: event.IdentityPrefix[:], + Sender: shdb.EncodeAddress(event.Sender), + GasLimit: event.GasLimit.Int64(), + }) + if err != nil { + return errors.Wrap(err, "failed to insert transaction submitted event into db") } - for eon, nextEventIndex := range nextEventIndices { - err := queries.SetTransactionSubmittedEventCount(ctx, database.SetTransactionSubmittedEventCountParams{ - Eon: int64(eon), - EventCount: nextEventIndex, - }) - if err != nil { - return err - } + nextEventIndices[event.Eon]++ + log.Debug(). + Int64("index", nextEventIndex). + Uint64("block", event.Raw.BlockNumber). + Uint64("eon", event.Eon). + Hex("identityPrefix", event.IdentityPrefix[:]). + Hex("sender", event.Sender.Bytes()). + Uint64("gasLimit", event.GasLimit.Uint64()). + Msg("synced new transaction submitted event") + } + for eon, nextEventIndex := range nextEventIndices { + err := queries.SetTransactionSubmittedEventCount(ctx, database.SetTransactionSubmittedEventCountParams{ + Eon: int64(eon), + EventCount: nextEventIndex, + }) + if err != nil { + return err } } return nil diff --git a/rolling-shutter/keyperimpl/gnosis/trigger.go b/rolling-shutter/keyperimpl/gnosis/trigger.go deleted file mode 100644 index f1e9377c9..000000000 --- a/rolling-shutter/keyperimpl/gnosis/trigger.go +++ /dev/null @@ -1,80 +0,0 @@ -package gnosis - -import ( - "context" - - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/rs/zerolog/log" - - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/epochkghandler" - "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/broker" - "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" - "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" - "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" -) - -type DecryptionTriggerer struct { - config *configuration.EthnodeConfig - decryptionTriggerChannel chan *broker.Event[*epochkghandler.DecryptionTrigger] -} - -func NewDecryptionTriggerer( - config *configuration.EthnodeConfig, - decryptionTriggerChannel chan *broker.Event[*epochkghandler.DecryptionTrigger], -) *DecryptionTriggerer { - return &DecryptionTriggerer{ - config: config, - decryptionTriggerChannel: decryptionTriggerChannel, - } -} - -func (t *DecryptionTriggerer) Start(ctx context.Context, runner service.Runner) error { - client, err := ethclient.DialContext(ctx, t.config.EthereumURL) - if err != nil { - return err - } - runner.Go(func() error { - headers := make(chan *types.Header) - headSubscription, err := client.SubscribeNewHead(ctx, headers) - if err != nil { - return err - } - - for { - select { - case err := <-headSubscription.Err(): - return err - case header := <-headers: - err := t.handleNewHead(ctx, header) - if err != nil { - return err - } - case <-ctx.Done(): - return ctx.Err() - } - } - }) - return nil -} - -func (t *DecryptionTriggerer) handleNewHead(_ context.Context, header *types.Header) error { - log.Debug().Uint64("block-number", header.Number.Uint64()).Msg("handling new head") - - // create some random identities for testing - maxNumIdentityPreimages := uint64(2) - numIdentityPreimages := header.Number.Uint64()%maxNumIdentityPreimages + 1 - identityPreimages := []identitypreimage.IdentityPreimage{} - for i := 0; i < int(numIdentityPreimages); i++ { - n := header.Number.Uint64()*maxNumIdentityPreimages + uint64(i) - identityPreimage := identitypreimage.Uint64ToIdentityPreimage(n) - identityPreimages = append(identityPreimages, identityPreimage) - } - trigger := epochkghandler.DecryptionTrigger{ - BlockNumber: header.Number.Uint64(), - IdentityPreimages: identityPreimages, - } - event := broker.NewEvent(&trigger) - t.decryptionTriggerChannel <- event - return nil -} From f00c663f13f5dd0585d6d4ae2da2dc745f4acf8c Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Tue, 13 Feb 2024 11:11:24 +0100 Subject: [PATCH 40/91] Ensure encrypted gas limit is not exceeded --- rolling-shutter/keyperimpl/gnosis/keyper.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index 6a1b3b2f7..6bc251b49 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -341,7 +341,12 @@ func (kpr *Keyper) getDecryptionIdentityPreimages( identityPreimages = []identitypreimage.IdentityPreimage{ makeBlockIdentityPreimage(ev), } + gas := uint64(0) for _, event := range events { + gas += uint64(event.GasLimit) + if gas > kpr.config.EncryptedGasLimit { + break + } identityPreimage, err := transactionSubmittedEventToIdentityPreimage(event) if err != nil { return []identitypreimage.IdentityPreimage{}, err From 2d199614bd0a59e258399e0aa125afdfec6a98fc Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Mon, 12 Feb 2024 21:03:19 +0100 Subject: [PATCH 41/91] Add extra data field to messages --- rolling-shutter/p2pmsg/gossip.pb.go | 589 +++++++++++++++++++++++----- rolling-shutter/p2pmsg/gossip.proto | 25 ++ 2 files changed, 518 insertions(+), 96 deletions(-) diff --git a/rolling-shutter/p2pmsg/gossip.pb.go b/rolling-shutter/p2pmsg/gossip.pb.go index 648ffe0a1..851372560 100644 --- a/rolling-shutter/p2pmsg/gossip.pb.go +++ b/rolling-shutter/p2pmsg/gossip.pb.go @@ -155,6 +155,107 @@ func (x *KeyShare) GetShare() []byte { return nil } +type GnosisDecryptionKeySharesExtra struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Slot uint64 `protobuf:"varint,1,opt,name=slot,proto3" json:"slot,omitempty"` + TxPointer uint64 `protobuf:"varint,2,opt,name=tx_pointer,json=txPointer,proto3" json:"tx_pointer,omitempty"` + Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` +} + +func (x *GnosisDecryptionKeySharesExtra) Reset() { + *x = GnosisDecryptionKeySharesExtra{} + if protoimpl.UnsafeEnabled { + mi := &file_gossip_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GnosisDecryptionKeySharesExtra) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GnosisDecryptionKeySharesExtra) ProtoMessage() {} + +func (x *GnosisDecryptionKeySharesExtra) ProtoReflect() protoreflect.Message { + mi := &file_gossip_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GnosisDecryptionKeySharesExtra.ProtoReflect.Descriptor instead. +func (*GnosisDecryptionKeySharesExtra) Descriptor() ([]byte, []int) { + return file_gossip_proto_rawDescGZIP(), []int{2} +} + +func (x *GnosisDecryptionKeySharesExtra) GetSlot() uint64 { + if x != nil { + return x.Slot + } + return 0 +} + +func (x *GnosisDecryptionKeySharesExtra) GetTxPointer() uint64 { + if x != nil { + return x.TxPointer + } + return 0 +} + +func (x *GnosisDecryptionKeySharesExtra) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + +type OptimismDecryptionKeySharesExtra struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *OptimismDecryptionKeySharesExtra) Reset() { + *x = OptimismDecryptionKeySharesExtra{} + if protoimpl.UnsafeEnabled { + mi := &file_gossip_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OptimismDecryptionKeySharesExtra) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OptimismDecryptionKeySharesExtra) ProtoMessage() {} + +func (x *OptimismDecryptionKeySharesExtra) ProtoReflect() protoreflect.Message { + mi := &file_gossip_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OptimismDecryptionKeySharesExtra.ProtoReflect.Descriptor instead. +func (*OptimismDecryptionKeySharesExtra) Descriptor() ([]byte, []int) { + return file_gossip_proto_rawDescGZIP(), []int{3} +} + type DecryptionKeyShares struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -164,12 +265,17 @@ type DecryptionKeyShares struct { Eon uint64 `protobuf:"varint,4,opt,name=eon,proto3" json:"eon,omitempty"` KeyperIndex uint64 `protobuf:"varint,5,opt,name=keyperIndex,proto3" json:"keyperIndex,omitempty"` Shares []*KeyShare `protobuf:"bytes,9,rep,name=shares,proto3" json:"shares,omitempty"` + // Types that are assignable to Extra: + // + // *DecryptionKeyShares_Gnosis + // *DecryptionKeyShares_Optimism + Extra isDecryptionKeyShares_Extra `protobuf_oneof:"extra"` } func (x *DecryptionKeyShares) Reset() { *x = DecryptionKeyShares{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_proto_msgTypes[2] + mi := &file_gossip_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -182,7 +288,7 @@ func (x *DecryptionKeyShares) String() string { func (*DecryptionKeyShares) ProtoMessage() {} func (x *DecryptionKeyShares) ProtoReflect() protoreflect.Message { - mi := &file_gossip_proto_msgTypes[2] + mi := &file_gossip_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -195,7 +301,7 @@ func (x *DecryptionKeyShares) ProtoReflect() protoreflect.Message { // Deprecated: Use DecryptionKeyShares.ProtoReflect.Descriptor instead. func (*DecryptionKeyShares) Descriptor() ([]byte, []int) { - return file_gossip_proto_rawDescGZIP(), []int{2} + return file_gossip_proto_rawDescGZIP(), []int{4} } func (x *DecryptionKeyShares) GetInstanceID() uint64 { @@ -226,6 +332,43 @@ func (x *DecryptionKeyShares) GetShares() []*KeyShare { return nil } +func (m *DecryptionKeyShares) GetExtra() isDecryptionKeyShares_Extra { + if m != nil { + return m.Extra + } + return nil +} + +func (x *DecryptionKeyShares) GetGnosis() *GnosisDecryptionKeySharesExtra { + if x, ok := x.GetExtra().(*DecryptionKeyShares_Gnosis); ok { + return x.Gnosis + } + return nil +} + +func (x *DecryptionKeyShares) GetOptimism() *OptimismDecryptionKeySharesExtra { + if x, ok := x.GetExtra().(*DecryptionKeyShares_Optimism); ok { + return x.Optimism + } + return nil +} + +type isDecryptionKeyShares_Extra interface { + isDecryptionKeyShares_Extra() +} + +type DecryptionKeyShares_Gnosis struct { + Gnosis *GnosisDecryptionKeySharesExtra `protobuf:"bytes,10,opt,name=gnosis,proto3,oneof"` +} + +type DecryptionKeyShares_Optimism struct { + Optimism *OptimismDecryptionKeySharesExtra `protobuf:"bytes,11,opt,name=optimism,proto3,oneof"` +} + +func (*DecryptionKeyShares_Gnosis) isDecryptionKeyShares_Extra() {} + +func (*DecryptionKeyShares_Optimism) isDecryptionKeyShares_Extra() {} + type Key struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -238,7 +381,7 @@ type Key struct { func (x *Key) Reset() { *x = Key{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_proto_msgTypes[3] + mi := &file_gossip_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -251,7 +394,7 @@ func (x *Key) String() string { func (*Key) ProtoMessage() {} func (x *Key) ProtoReflect() protoreflect.Message { - mi := &file_gossip_proto_msgTypes[3] + mi := &file_gossip_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -264,7 +407,7 @@ func (x *Key) ProtoReflect() protoreflect.Message { // Deprecated: Use Key.ProtoReflect.Descriptor instead. func (*Key) Descriptor() ([]byte, []int) { - return file_gossip_proto_rawDescGZIP(), []int{3} + return file_gossip_proto_rawDescGZIP(), []int{5} } func (x *Key) GetIdentity() []byte { @@ -281,6 +424,115 @@ func (x *Key) GetKey() []byte { return nil } +type GnosisDecryptionKeysExtra struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Slot uint64 `protobuf:"varint,1,opt,name=slot,proto3" json:"slot,omitempty"` + TxPointer uint64 `protobuf:"varint,2,opt,name=tx_pointer,json=txPointer,proto3" json:"tx_pointer,omitempty"` + SignerIndices []uint64 `protobuf:"varint,3,rep,packed,name=signerIndices,proto3" json:"signerIndices,omitempty"` + Signatures [][]byte `protobuf:"bytes,4,rep,name=signatures,proto3" json:"signatures,omitempty"` +} + +func (x *GnosisDecryptionKeysExtra) Reset() { + *x = GnosisDecryptionKeysExtra{} + if protoimpl.UnsafeEnabled { + mi := &file_gossip_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GnosisDecryptionKeysExtra) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GnosisDecryptionKeysExtra) ProtoMessage() {} + +func (x *GnosisDecryptionKeysExtra) ProtoReflect() protoreflect.Message { + mi := &file_gossip_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GnosisDecryptionKeysExtra.ProtoReflect.Descriptor instead. +func (*GnosisDecryptionKeysExtra) Descriptor() ([]byte, []int) { + return file_gossip_proto_rawDescGZIP(), []int{6} +} + +func (x *GnosisDecryptionKeysExtra) GetSlot() uint64 { + if x != nil { + return x.Slot + } + return 0 +} + +func (x *GnosisDecryptionKeysExtra) GetTxPointer() uint64 { + if x != nil { + return x.TxPointer + } + return 0 +} + +func (x *GnosisDecryptionKeysExtra) GetSignerIndices() []uint64 { + if x != nil { + return x.SignerIndices + } + return nil +} + +func (x *GnosisDecryptionKeysExtra) GetSignatures() [][]byte { + if x != nil { + return x.Signatures + } + return nil +} + +type OptimismDecryptionKeysExtra struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *OptimismDecryptionKeysExtra) Reset() { + *x = OptimismDecryptionKeysExtra{} + if protoimpl.UnsafeEnabled { + mi := &file_gossip_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OptimismDecryptionKeysExtra) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OptimismDecryptionKeysExtra) ProtoMessage() {} + +func (x *OptimismDecryptionKeysExtra) ProtoReflect() protoreflect.Message { + mi := &file_gossip_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OptimismDecryptionKeysExtra.ProtoReflect.Descriptor instead. +func (*OptimismDecryptionKeysExtra) Descriptor() ([]byte, []int) { + return file_gossip_proto_rawDescGZIP(), []int{7} +} + type DecryptionKeys struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -289,12 +541,17 @@ type DecryptionKeys struct { InstanceID uint64 `protobuf:"varint,1,opt,name=instanceID,proto3" json:"instanceID,omitempty"` Eon uint64 `protobuf:"varint,2,opt,name=eon,proto3" json:"eon,omitempty"` Keys []*Key `protobuf:"bytes,3,rep,name=keys,proto3" json:"keys,omitempty"` + // Types that are assignable to Extra: + // + // *DecryptionKeys_Gnosis + // *DecryptionKeys_Optimism + Extra isDecryptionKeys_Extra `protobuf_oneof:"extra"` } func (x *DecryptionKeys) Reset() { *x = DecryptionKeys{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_proto_msgTypes[4] + mi := &file_gossip_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -307,7 +564,7 @@ func (x *DecryptionKeys) String() string { func (*DecryptionKeys) ProtoMessage() {} func (x *DecryptionKeys) ProtoReflect() protoreflect.Message { - mi := &file_gossip_proto_msgTypes[4] + mi := &file_gossip_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -320,7 +577,7 @@ func (x *DecryptionKeys) ProtoReflect() protoreflect.Message { // Deprecated: Use DecryptionKeys.ProtoReflect.Descriptor instead. func (*DecryptionKeys) Descriptor() ([]byte, []int) { - return file_gossip_proto_rawDescGZIP(), []int{4} + return file_gossip_proto_rawDescGZIP(), []int{8} } func (x *DecryptionKeys) GetInstanceID() uint64 { @@ -344,6 +601,43 @@ func (x *DecryptionKeys) GetKeys() []*Key { return nil } +func (m *DecryptionKeys) GetExtra() isDecryptionKeys_Extra { + if m != nil { + return m.Extra + } + return nil +} + +func (x *DecryptionKeys) GetGnosis() *GnosisDecryptionKeysExtra { + if x, ok := x.GetExtra().(*DecryptionKeys_Gnosis); ok { + return x.Gnosis + } + return nil +} + +func (x *DecryptionKeys) GetOptimism() *OptimismDecryptionKeysExtra { + if x, ok := x.GetExtra().(*DecryptionKeys_Optimism); ok { + return x.Optimism + } + return nil +} + +type isDecryptionKeys_Extra interface { + isDecryptionKeys_Extra() +} + +type DecryptionKeys_Gnosis struct { + Gnosis *GnosisDecryptionKeysExtra `protobuf:"bytes,4,opt,name=gnosis,proto3,oneof"` +} + +type DecryptionKeys_Optimism struct { + Optimism *OptimismDecryptionKeysExtra `protobuf:"bytes,5,opt,name=optimism,proto3,oneof"` +} + +func (*DecryptionKeys_Gnosis) isDecryptionKeys_Extra() {} + +func (*DecryptionKeys_Optimism) isDecryptionKeys_Extra() {} + // EonPublicKey is sent by the keypers to publish the EonPublicKey for a certain // eon. For those that observe it, e.g. the collator, it's a candidate until // the observer has seen at least threshold messages. @@ -363,7 +657,7 @@ type EonPublicKey struct { func (x *EonPublicKey) Reset() { *x = EonPublicKey{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_proto_msgTypes[5] + mi := &file_gossip_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -376,7 +670,7 @@ func (x *EonPublicKey) String() string { func (*EonPublicKey) ProtoMessage() {} func (x *EonPublicKey) ProtoReflect() protoreflect.Message { - mi := &file_gossip_proto_msgTypes[5] + mi := &file_gossip_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -389,7 +683,7 @@ func (x *EonPublicKey) ProtoReflect() protoreflect.Message { // Deprecated: Use EonPublicKey.ProtoReflect.Descriptor instead. func (*EonPublicKey) Descriptor() ([]byte, []int) { - return file_gossip_proto_rawDescGZIP(), []int{5} + return file_gossip_proto_rawDescGZIP(), []int{9} } func (x *EonPublicKey) GetInstanceID() uint64 { @@ -448,7 +742,7 @@ type TraceContext struct { func (x *TraceContext) Reset() { *x = TraceContext{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_proto_msgTypes[6] + mi := &file_gossip_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -461,7 +755,7 @@ func (x *TraceContext) String() string { func (*TraceContext) ProtoMessage() {} func (x *TraceContext) ProtoReflect() protoreflect.Message { - mi := &file_gossip_proto_msgTypes[6] + mi := &file_gossip_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -474,7 +768,7 @@ func (x *TraceContext) ProtoReflect() protoreflect.Message { // Deprecated: Use TraceContext.ProtoReflect.Descriptor instead. func (*TraceContext) Descriptor() ([]byte, []int) { - return file_gossip_proto_rawDescGZIP(), []int{6} + return file_gossip_proto_rawDescGZIP(), []int{10} } func (x *TraceContext) GetTraceID() []byte { @@ -518,7 +812,7 @@ type Envelope struct { func (x *Envelope) Reset() { *x = Envelope{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_proto_msgTypes[7] + mi := &file_gossip_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -531,7 +825,7 @@ func (x *Envelope) String() string { func (*Envelope) ProtoMessage() {} func (x *Envelope) ProtoReflect() protoreflect.Message { - mi := &file_gossip_proto_msgTypes[7] + mi := &file_gossip_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -544,7 +838,7 @@ func (x *Envelope) ProtoReflect() protoreflect.Message { // Deprecated: Use Envelope.ProtoReflect.Descriptor instead. func (*Envelope) Descriptor() ([]byte, []int) { - return file_gossip_proto_rawDescGZIP(), []int{7} + return file_gossip_proto_rawDescGZIP(), []int{11} } func (x *Envelope) GetVersion() string { @@ -589,58 +883,97 @@ var file_gossip_proto_rawDesc = []byte{ 0x08, 0x4b, 0x65, 0x79, 0x53, 0x68, 0x61, 0x72, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x65, 0x22, 0x93, 0x01, 0x0a, 0x13, 0x44, 0x65, - 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x53, 0x68, 0x61, 0x72, 0x65, - 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, - 0x44, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, - 0x65, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x6b, 0x65, 0x79, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6b, 0x65, 0x79, 0x70, 0x65, 0x72, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x28, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x72, 0x65, 0x73, 0x18, - 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x32, 0x70, 0x6d, 0x73, 0x67, 0x2e, 0x4b, - 0x65, 0x79, 0x53, 0x68, 0x61, 0x72, 0x65, 0x52, 0x06, 0x73, 0x68, 0x61, 0x72, 0x65, 0x73, 0x22, - 0x33, 0x0a, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x22, 0x63, 0x0a, 0x0e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x03, 0x65, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x32, 0x70, 0x6d, 0x73, 0x67, 0x2e, - 0x4b, 0x65, 0x79, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x22, 0xd4, 0x01, 0x0a, 0x0c, 0x45, 0x6f, - 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x74, 0x69, - 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x12, 0x2c, 0x0a, 0x11, 0x6b, 0x65, 0x79, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x6b, - 0x65, 0x79, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x65, - 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x22, 0x80, 0x01, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x70, 0x61, 0x6e, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x70, 0x61, - 0x6e, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x46, 0x6c, 0x61, 0x67, - 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x46, 0x6c, - 0x61, 0x67, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x22, 0x8f, 0x01, 0x0a, 0x08, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, - 0x79, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x74, 0x72, - 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x32, 0x70, 0x6d, - 0x73, 0x67, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x48, - 0x00, 0x52, 0x05, 0x74, 0x72, 0x61, 0x63, 0x65, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, - 0x74, 0x72, 0x61, 0x63, 0x65, 0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x2f, 0x3b, 0x70, 0x32, 0x70, 0x6d, - 0x73, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x28, 0x0c, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x65, 0x22, 0x71, 0x0a, 0x1e, 0x47, 0x6e, 0x6f, + 0x73, 0x69, 0x73, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, + 0x53, 0x68, 0x61, 0x72, 0x65, 0x73, 0x45, 0x78, 0x74, 0x72, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x73, + 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, + 0x1d, 0x0a, 0x0a, 0x74, 0x78, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x78, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1c, + 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x22, 0x0a, 0x20, + 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x6d, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x53, 0x68, 0x61, 0x72, 0x65, 0x73, 0x45, 0x78, 0x74, 0x72, 0x61, + 0x22, 0xa6, 0x02, 0x0a, 0x13, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, + 0x65, 0x79, 0x53, 0x68, 0x61, 0x72, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6f, 0x6e, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x65, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x6b, 0x65, + 0x79, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0b, 0x6b, 0x65, 0x79, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x28, 0x0a, 0x06, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, + 0x32, 0x70, 0x6d, 0x73, 0x67, 0x2e, 0x4b, 0x65, 0x79, 0x53, 0x68, 0x61, 0x72, 0x65, 0x52, 0x06, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x06, 0x67, 0x6e, 0x6f, 0x73, 0x69, 0x73, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x32, 0x70, 0x6d, 0x73, 0x67, 0x2e, + 0x47, 0x6e, 0x6f, 0x73, 0x69, 0x73, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x4b, 0x65, 0x79, 0x53, 0x68, 0x61, 0x72, 0x65, 0x73, 0x45, 0x78, 0x74, 0x72, 0x61, 0x48, 0x00, + 0x52, 0x06, 0x67, 0x6e, 0x6f, 0x73, 0x69, 0x73, 0x12, 0x46, 0x0a, 0x08, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x69, 0x73, 0x6d, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x70, 0x32, 0x70, + 0x6d, 0x73, 0x67, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x6d, 0x44, 0x65, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x53, 0x68, 0x61, 0x72, 0x65, 0x73, 0x45, + 0x78, 0x74, 0x72, 0x61, 0x48, 0x00, 0x52, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x6d, + 0x42, 0x07, 0x0a, 0x05, 0x65, 0x78, 0x74, 0x72, 0x61, 0x22, 0x33, 0x0a, 0x03, 0x4b, 0x65, 0x79, + 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x94, + 0x01, 0x0a, 0x19, 0x47, 0x6e, 0x6f, 0x73, 0x69, 0x73, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x45, 0x78, 0x74, 0x72, 0x61, 0x12, 0x12, 0x0a, 0x04, + 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, + 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x78, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x78, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x12, + 0x24, 0x0a, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x1d, 0x0a, 0x1b, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, + 0x6d, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x45, + 0x78, 0x74, 0x72, 0x61, 0x22, 0xec, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x65, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x04, 0x6b, 0x65, 0x79, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x32, 0x70, 0x6d, 0x73, 0x67, + 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x3b, 0x0a, 0x06, 0x67, 0x6e, + 0x6f, 0x73, 0x69, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x32, 0x70, + 0x6d, 0x73, 0x67, 0x2e, 0x47, 0x6e, 0x6f, 0x73, 0x69, 0x73, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x45, 0x78, 0x74, 0x72, 0x61, 0x48, 0x00, 0x52, + 0x06, 0x67, 0x6e, 0x6f, 0x73, 0x69, 0x73, 0x12, 0x41, 0x0a, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x69, 0x73, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x32, 0x70, 0x6d, + 0x73, 0x67, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x6d, 0x44, 0x65, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x45, 0x78, 0x74, 0x72, 0x61, 0x48, 0x00, + 0x52, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x6d, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x78, + 0x74, 0x72, 0x61, 0x22, 0xd4, 0x01, 0x0a, 0x0c, 0x45, 0x6f, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, + 0x65, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x61, 0x63, 0x74, + 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2c, 0x0a, 0x11, + 0x6b, 0x65, 0x79, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x6b, 0x65, 0x79, 0x70, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6f, + 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x65, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x80, 0x01, 0x0a, 0x0c, 0x54, + 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x74, + 0x72, 0x61, 0x63, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x74, 0x72, + 0x61, 0x63, 0x65, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x70, 0x61, 0x6e, 0x49, 0x44, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x70, 0x61, 0x6e, 0x49, 0x44, 0x12, 0x1e, 0x0a, + 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x1e, 0x0a, + 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x8f, 0x01, + 0x0a, 0x08, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x74, 0x72, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x32, 0x70, 0x6d, 0x73, 0x67, 0x2e, 0x54, 0x72, 0x61, + 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x48, 0x00, 0x52, 0x05, 0x74, 0x72, 0x61, + 0x63, 0x65, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x42, + 0x0b, 0x5a, 0x09, 0x2e, 0x2f, 0x3b, 0x70, 0x32, 0x70, 0x6d, 0x73, 0x67, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -655,28 +988,36 @@ func file_gossip_proto_rawDescGZIP() []byte { return file_gossip_proto_rawDescData } -var file_gossip_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_gossip_proto_msgTypes = make([]protoimpl.MessageInfo, 12) var file_gossip_proto_goTypes = []interface{}{ - (*DecryptionTrigger)(nil), // 0: p2pmsg.DecryptionTrigger - (*KeyShare)(nil), // 1: p2pmsg.KeyShare - (*DecryptionKeyShares)(nil), // 2: p2pmsg.DecryptionKeyShares - (*Key)(nil), // 3: p2pmsg.Key - (*DecryptionKeys)(nil), // 4: p2pmsg.DecryptionKeys - (*EonPublicKey)(nil), // 5: p2pmsg.EonPublicKey - (*TraceContext)(nil), // 6: p2pmsg.TraceContext - (*Envelope)(nil), // 7: p2pmsg.Envelope - (*anypb.Any)(nil), // 8: google.protobuf.Any + (*DecryptionTrigger)(nil), // 0: p2pmsg.DecryptionTrigger + (*KeyShare)(nil), // 1: p2pmsg.KeyShare + (*GnosisDecryptionKeySharesExtra)(nil), // 2: p2pmsg.GnosisDecryptionKeySharesExtra + (*OptimismDecryptionKeySharesExtra)(nil), // 3: p2pmsg.OptimismDecryptionKeySharesExtra + (*DecryptionKeyShares)(nil), // 4: p2pmsg.DecryptionKeyShares + (*Key)(nil), // 5: p2pmsg.Key + (*GnosisDecryptionKeysExtra)(nil), // 6: p2pmsg.GnosisDecryptionKeysExtra + (*OptimismDecryptionKeysExtra)(nil), // 7: p2pmsg.OptimismDecryptionKeysExtra + (*DecryptionKeys)(nil), // 8: p2pmsg.DecryptionKeys + (*EonPublicKey)(nil), // 9: p2pmsg.EonPublicKey + (*TraceContext)(nil), // 10: p2pmsg.TraceContext + (*Envelope)(nil), // 11: p2pmsg.Envelope + (*anypb.Any)(nil), // 12: google.protobuf.Any } var file_gossip_proto_depIdxs = []int32{ - 1, // 0: p2pmsg.DecryptionKeyShares.shares:type_name -> p2pmsg.KeyShare - 3, // 1: p2pmsg.DecryptionKeys.keys:type_name -> p2pmsg.Key - 8, // 2: p2pmsg.Envelope.message:type_name -> google.protobuf.Any - 6, // 3: p2pmsg.Envelope.trace:type_name -> p2pmsg.TraceContext - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 1, // 0: p2pmsg.DecryptionKeyShares.shares:type_name -> p2pmsg.KeyShare + 2, // 1: p2pmsg.DecryptionKeyShares.gnosis:type_name -> p2pmsg.GnosisDecryptionKeySharesExtra + 3, // 2: p2pmsg.DecryptionKeyShares.optimism:type_name -> p2pmsg.OptimismDecryptionKeySharesExtra + 5, // 3: p2pmsg.DecryptionKeys.keys:type_name -> p2pmsg.Key + 6, // 4: p2pmsg.DecryptionKeys.gnosis:type_name -> p2pmsg.GnosisDecryptionKeysExtra + 7, // 5: p2pmsg.DecryptionKeys.optimism:type_name -> p2pmsg.OptimismDecryptionKeysExtra + 12, // 6: p2pmsg.Envelope.message:type_name -> google.protobuf.Any + 10, // 7: p2pmsg.Envelope.trace:type_name -> p2pmsg.TraceContext + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_gossip_proto_init() } @@ -710,7 +1051,7 @@ func file_gossip_proto_init() { } } file_gossip_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DecryptionKeyShares); i { + switch v := v.(*GnosisDecryptionKeySharesExtra); i { case 0: return &v.state case 1: @@ -722,7 +1063,7 @@ func file_gossip_proto_init() { } } file_gossip_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Key); i { + switch v := v.(*OptimismDecryptionKeySharesExtra); i { case 0: return &v.state case 1: @@ -734,7 +1075,7 @@ func file_gossip_proto_init() { } } file_gossip_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DecryptionKeys); i { + switch v := v.(*DecryptionKeyShares); i { case 0: return &v.state case 1: @@ -746,7 +1087,7 @@ func file_gossip_proto_init() { } } file_gossip_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EonPublicKey); i { + switch v := v.(*Key); i { case 0: return &v.state case 1: @@ -758,7 +1099,7 @@ func file_gossip_proto_init() { } } file_gossip_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TraceContext); i { + switch v := v.(*GnosisDecryptionKeysExtra); i { case 0: return &v.state case 1: @@ -770,6 +1111,54 @@ func file_gossip_proto_init() { } } file_gossip_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OptimismDecryptionKeysExtra); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gossip_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DecryptionKeys); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gossip_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EonPublicKey); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gossip_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TraceContext); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gossip_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Envelope); i { case 0: return &v.state @@ -782,14 +1171,22 @@ func file_gossip_proto_init() { } } } - file_gossip_proto_msgTypes[7].OneofWrappers = []interface{}{} + file_gossip_proto_msgTypes[4].OneofWrappers = []interface{}{ + (*DecryptionKeyShares_Gnosis)(nil), + (*DecryptionKeyShares_Optimism)(nil), + } + file_gossip_proto_msgTypes[8].OneofWrappers = []interface{}{ + (*DecryptionKeys_Gnosis)(nil), + (*DecryptionKeys_Optimism)(nil), + } + file_gossip_proto_msgTypes[11].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_gossip_proto_rawDesc, NumEnums: 0, - NumMessages: 8, + NumMessages: 12, NumExtensions: 0, NumServices: 0, }, diff --git a/rolling-shutter/p2pmsg/gossip.proto b/rolling-shutter/p2pmsg/gossip.proto index eb2323858..6d79ab9c1 100644 --- a/rolling-shutter/p2pmsg/gossip.proto +++ b/rolling-shutter/p2pmsg/gossip.proto @@ -19,11 +19,23 @@ message KeyShare { bytes share = 2; } +message GnosisDecryptionKeySharesExtra { + uint64 slot = 1; + uint64 tx_pointer = 2; + bytes signature = 3; +} + +message OptimismDecryptionKeySharesExtra {} + message DecryptionKeyShares { uint64 instanceID = 1; uint64 eon = 4; uint64 keyperIndex = 5; repeated KeyShare shares = 9; + oneof extra { + GnosisDecryptionKeySharesExtra gnosis = 10; + OptimismDecryptionKeySharesExtra optimism = 11; + } } message Key { @@ -31,10 +43,23 @@ message Key { bytes key = 2; } +message GnosisDecryptionKeysExtra { + uint64 slot = 1; + uint64 tx_pointer = 2; + repeated uint64 signerIndices = 3; + repeated bytes signatures = 4; +} + +message OptimismDecryptionKeysExtra {} + message DecryptionKeys { uint64 instanceID = 1; uint64 eon = 2; repeated Key keys = 3; + oneof extra { + GnosisDecryptionKeysExtra gnosis = 4; + OptimismDecryptionKeysExtra optimism = 5; + } } // EonPublicKey is sent by the keypers to publish the EonPublicKey for a certain From d92c56df4e0eaf33e4b2bda72bcd90e2451d010e Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Tue, 13 Feb 2024 17:20:11 +0100 Subject: [PATCH 42/91] Support multiple handlers for single topic --- rolling-shutter/p2p/messaging.go | 45 +++++++++++++++++++++++--------- rolling-shutter/p2p/p2p.go | 3 ++- rolling-shutter/p2p/p2p_test.go | 2 +- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/rolling-shutter/p2p/messaging.go b/rolling-shutter/p2p/messaging.go index 6384632e4..3537feb64 100644 --- a/rolling-shutter/p2p/messaging.go +++ b/rolling-shutter/p2p/messaging.go @@ -25,9 +25,34 @@ type ( HandlerFunc func(context.Context, p2pmsg.Message) ([]p2pmsg.Message, error) HandlerRegistry map[protoreflect.FullName][]HandlerFunc ValidatorFunc func(context.Context, p2pmsg.Message) (pubsub.ValidationResult, error) - ValidatorRegistry map[string]pubsub.ValidatorEx + ValidatorRegistry map[string][]pubsub.ValidatorEx ) +func (r *ValidatorRegistry) GetCombinedValidator(topic string) pubsub.ValidatorEx { + validate := func(ctx context.Context, sender peer.ID, message *pubsub.Message) pubsub.ValidationResult { + ignored := false + for _, valFunc := range (*r)[topic] { + res := valFunc(ctx, sender, message) + switch res { + case pubsub.ValidationAccept: + continue + case pubsub.ValidationReject: + return pubsub.ValidationReject + case pubsub.ValidationIgnore: + ignored = true + default: + log.Warn().Str("topic", topic).Msg("unknown validation result %d, treating as reject") + return pubsub.ValidationReject + } + } + if ignored { + return pubsub.ValidationIgnore + } + return pubsub.ValidationAccept + } + return validate +} + const ( allowTraceContext = true // whether we allow the trace field to be set in the message envelope invalidResultType = pubsub.ValidationReject @@ -134,16 +159,6 @@ func (m *P2PMessaging) AddHandlerFunc(handlerFunc HandlerFunc, protos ...p2pmsg. func (m *P2PMessaging) addValidatorImpl(valFunc ValidatorFunc, messProto p2pmsg.Message) { topic := messProto.Topic() - _, exists := m.validatorRegistry[topic] - if exists { - // This is likely not intended and happens when different messages return the same P2PMessage.Topic(). - // Currently a topic is mapped 1 to 1 to a message type (instead of using an envelope for unmarshalling) - // (If feature needed, allow for chaining of successively registered validator functions per topic) - panic(errors.Errorf( - "can't register more than one validator per topic (topic: '%s', message-type: '%s')", - topic, - reflect.TypeOf(messProto))) - } handleError := func(err error) { log.Info().Str("topic", topic).Err(err).Msg("received invalid message)") } @@ -176,8 +191,12 @@ func (m *P2PMessaging) addValidatorImpl(valFunc ValidatorFunc, messProto p2pmsg. } return valid } - m.validatorRegistry[topic] = validate - m.AddGossipTopic(topic) + + _, exists := m.validatorRegistry[topic] + if !exists { + m.AddGossipTopic(topic) + } + m.validatorRegistry[topic] = append(m.validatorRegistry[topic], validate) } // AddValidator will add a validator-function to a P2PHandler instance: diff --git a/rolling-shutter/p2p/p2p.go b/rolling-shutter/p2p/p2p.go index 17af07cf4..55d46f417 100644 --- a/rolling-shutter/p2p/p2p.go +++ b/rolling-shutter/p2p/p2p.go @@ -96,7 +96,8 @@ func (p *P2PNode) Run( return err } - for topicName, validator := range topicValidators { + for topicName := range topicValidators { + validator := topicValidators.GetCombinedValidator(topicName) if err := p.pubSub.RegisterTopicValidator(topicName, validator); err != nil { return err } diff --git a/rolling-shutter/p2p/p2p_test.go b/rolling-shutter/p2p/p2p_test.go index 65bba63a3..6071cf8a8 100644 --- a/rolling-shutter/p2p/p2p_test.go +++ b/rolling-shutter/p2p/p2p_test.go @@ -92,7 +92,7 @@ func TestStartNetworkNodeIntegration(t *testing.T) { assert.NilError(t, err) p2ps = append(p2ps, p2pHandler.P2P) fn := func(ctx context.Context, runner service.Runner) error { - return p2pHandler.P2P.Run(ctx, runner, gossipTopicNames, map[string]pubsub.ValidatorEx{}) + return p2pHandler.P2P.Run(ctx, runner, gossipTopicNames, map[string][]pubsub.ValidatorEx{}) } services[i] = service.Function{Func: fn} } From e17e466293978390a5a29ff3d3ebe573d0030bd7 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Wed, 14 Feb 2024 15:53:06 +0100 Subject: [PATCH 43/91] Intercept outgoing messages This allows modifying fields, in particular the extra one to add application specific data. --- rolling-shutter/keyperimpl/gnosis/handlers.go | 48 ++++++ .../keyperimpl/gnosis/messagingmiddleware.go | 150 ++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 rolling-shutter/keyperimpl/gnosis/handlers.go create mode 100644 rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go diff --git a/rolling-shutter/keyperimpl/gnosis/handlers.go b/rolling-shutter/keyperimpl/gnosis/handlers.go new file mode 100644 index 000000000..e85e79f2a --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/handlers.go @@ -0,0 +1,48 @@ +package gnosis + +import ( + "context" + + pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/pkg/errors" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/p2pmsg" +) + +type DecryptionKeySharesHandler struct{} + +func (h *DecryptionKeySharesHandler) MessagePrototypes() []p2pmsg.Message { + return []p2pmsg.Message{&p2pmsg.DecryptionKeyShares{}} +} + +func (h *DecryptionKeySharesHandler) ValidateMessage(_ context.Context, msg p2pmsg.Message) (pubsub.ValidationResult, error) { + keyShares := msg.(*p2pmsg.DecryptionKeyShares) + _, ok := keyShares.Extra.(*p2pmsg.DecryptionKeyShares_Gnosis) + if !ok { + return pubsub.ValidationReject, errors.Errorf("unexpected extra type %T, expected Gnosis", keyShares.Extra) + } + return pubsub.ValidationAccept, nil +} + +func (h *DecryptionKeySharesHandler) HandleMessage(_ context.Context, _ p2pmsg.Message) ([]p2pmsg.Message, error) { + return []p2pmsg.Message{}, nil +} + +type DecryptionKeysHandler struct{} + +func (h *DecryptionKeysHandler) MessagePrototypes() []p2pmsg.Message { + return []p2pmsg.Message{&p2pmsg.DecryptionKeys{}} +} + +func (h *DecryptionKeysHandler) ValidateMessage(_ context.Context, msg p2pmsg.Message) (pubsub.ValidationResult, error) { + keys := msg.(*p2pmsg.DecryptionKeys) + _, ok := keys.Extra.(*p2pmsg.DecryptionKeys_Gnosis) + if !ok { + return pubsub.ValidationReject, errors.Errorf("unexpected extra type %T, expected Gnosis", keys.Extra) + } + return pubsub.ValidationAccept, nil +} + +func (h *DecryptionKeysHandler) HandleMessage(_ context.Context, _ p2pmsg.Message) ([]p2pmsg.Message, error) { + return []p2pmsg.Message{}, nil +} diff --git a/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go b/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go new file mode 100644 index 000000000..ea67b9c06 --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go @@ -0,0 +1,150 @@ +package gnosis + +import ( + "bytes" + "context" + + "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v4/pgxpool" + pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + "google.golang.org/protobuf/proto" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/retry" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" + "github.com/shutter-network/rolling-shutter/rolling-shutter/p2p" + "github.com/shutter-network/rolling-shutter/rolling-shutter/p2pmsg" +) + +type MessagingMiddleware struct { + messaging p2p.Messaging + dbpool *pgxpool.Pool +} + +type WrappedMessageHandler struct { + handler p2p.MessageHandler + middleware *MessagingMiddleware +} + +func (h *WrappedMessageHandler) MessagePrototypes() []p2pmsg.Message { + return h.handler.MessagePrototypes() +} + +func (h *WrappedMessageHandler) ValidateMessage(ctx context.Context, msg p2pmsg.Message) (pubsub.ValidationResult, error) { + return h.handler.ValidateMessage(ctx, msg) +} + +func (h *WrappedMessageHandler) HandleMessage(ctx context.Context, msg p2pmsg.Message) ([]p2pmsg.Message, error) { + msgs, err := h.handler.HandleMessage(ctx, msg) + if err != nil { + return []p2pmsg.Message{}, err + } + replacedMsgs := []p2pmsg.Message{} + for _, msg := range msgs { + replacedMsg, err := h.middleware.interceptMessage(ctx, msg) + if err != nil { + return []p2pmsg.Message{}, err + } + replacedMsgs = append(replacedMsgs, replacedMsg) + } + return replacedMsgs, nil +} + +func NewMessagingMiddleware(messaging p2p.Messaging, dbpool *pgxpool.Pool) *MessagingMiddleware { + return &MessagingMiddleware{messaging: messaging, dbpool: dbpool} +} + +func (i *MessagingMiddleware) Start(_ context.Context, runner service.Runner) error { + return runner.StartService(i.messaging) +} + +func (i *MessagingMiddleware) interceptMessage(ctx context.Context, msg p2pmsg.Message) (p2pmsg.Message, error) { + switch msg := msg.(type) { + case *p2pmsg.DecryptionKeyShares: + return i.interceptDecryptionKeyShares(ctx, msg) + case *p2pmsg.DecryptionKeys: + return i.interceptDecryptionKeys(ctx, msg) + default: + return msg, nil + } +} + +func (i *MessagingMiddleware) SendMessage(ctx context.Context, msg p2pmsg.Message, opts ...retry.Option) error { + msg, err := i.interceptMessage(ctx, msg) + if err != nil { + return err + } + if msg != nil { + return i.messaging.SendMessage(ctx, msg, opts...) + } + return nil +} + +func (i *MessagingMiddleware) AddValidator(ctx p2p.ValidatorFunc, protos ...p2pmsg.Message) { + i.messaging.AddValidator(ctx, protos...) +} + +func (i *MessagingMiddleware) AddMessageHandler(mhs ...p2p.MessageHandler) { + for _, mh := range mhs { + wmh := &WrappedMessageHandler{handler: mh, middleware: i} + i.messaging.AddMessageHandler(wmh) + } +} + +func (i *MessagingMiddleware) interceptDecryptionKeyShares( + ctx context.Context, + originalMsg *p2pmsg.DecryptionKeyShares, +) (p2pmsg.Message, error) { + queries := database.New(i.dbpool) + currentDecryptionTrigger, err := queries.GetCurrentDecryptionTrigger(ctx, int64(originalMsg.Eon)) + if err == pgx.ErrNoRows { + log.Warn(). + Uint64("eon", originalMsg.Eon). + Msg("intercepted decryption key shares message with unknown corresponding decryption trigger") + return nil, nil + } else if err != nil { + return nil, errors.Wrapf(err, "failed to get current decryption trigger for eon %d", originalMsg.Eon) + } + identityPreimges := []identitypreimage.IdentityPreimage{} + for _, share := range originalMsg.Shares { + identityPreimges = append(identityPreimges, identitypreimage.IdentityPreimage(share.EpochID)) + } + identitiesHash := computeIdentitiesHash(identityPreimges) + if !bytes.Equal(identitiesHash, currentDecryptionTrigger.IdentitiesHash) { + log.Warn(). + Uint64("eon", originalMsg.Eon). + Hex("expectedIdentitiesHash", currentDecryptionTrigger.IdentitiesHash). + Hex("actualIdentitiesHash", identitiesHash). + Msg("intercepted decryption key shares message with unexpected identities hash") + return nil, nil + } + + msg := proto.Clone(originalMsg).(*p2pmsg.DecryptionKeyShares) + msg.Extra = &p2pmsg.DecryptionKeyShares_Gnosis{ + Gnosis: &p2pmsg.GnosisDecryptionKeySharesExtra{ + Slot: uint64(currentDecryptionTrigger.Block), + TxPointer: uint64(currentDecryptionTrigger.TxPointer), + Signature: []byte{}, + }, + } + return msg, nil +} + +func (i *MessagingMiddleware) interceptDecryptionKeys( + _ context.Context, + originalMsg *p2pmsg.DecryptionKeys, +) (p2pmsg.Message, error) { + msg := proto.Clone(originalMsg).(*p2pmsg.DecryptionKeys) + msg.Extra = &p2pmsg.DecryptionKeys_Gnosis{ + Gnosis: &p2pmsg.GnosisDecryptionKeysExtra{ + Slot: 0, + TxPointer: 0, + SignerIndices: []uint64{}, + Signatures: [][]byte{}, + }, + } + return msg, nil +} From e389bbde680abe0c945a32b5ce3d8d0af5e29c45 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Fri, 16 Feb 2024 13:27:04 +0100 Subject: [PATCH 44/91] Fix typo in log message --- rolling-shutter/p2p/messaging.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rolling-shutter/p2p/messaging.go b/rolling-shutter/p2p/messaging.go index 3537feb64..2073f21b9 100644 --- a/rolling-shutter/p2p/messaging.go +++ b/rolling-shutter/p2p/messaging.go @@ -160,7 +160,7 @@ func (m *P2PMessaging) AddHandlerFunc(handlerFunc HandlerFunc, protos ...p2pmsg. func (m *P2PMessaging) addValidatorImpl(valFunc ValidatorFunc, messProto p2pmsg.Message) { topic := messProto.Topic() handleError := func(err error) { - log.Info().Str("topic", topic).Err(err).Msg("received invalid message)") + log.Info().Str("topic", topic).Err(err).Msg("received invalid message") } validate := func(ctx context.Context, sender peer.ID, message *pubsub.Message) pubsub.ValidationResult { if message.GetTopic() != topic { From 3a426c350d68f429084b35b915ae05765bebdef0 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Fri, 16 Feb 2024 13:28:07 +0100 Subject: [PATCH 45/91] Allow inserting the same share multiple times This may happen in the Gnosis implementation in case a user reuses an identity. --- rolling-shutter/keyper/database/keyper.sqlc.gen.go | 1 + rolling-shutter/keyper/database/sql/queries/keyper.sql | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/rolling-shutter/keyper/database/keyper.sqlc.gen.go b/rolling-shutter/keyper/database/keyper.sqlc.gen.go index 73b43b669..ef9228e10 100644 --- a/rolling-shutter/keyper/database/keyper.sqlc.gen.go +++ b/rolling-shutter/keyper/database/keyper.sqlc.gen.go @@ -541,6 +541,7 @@ func (q *Queries) InsertDecryptionKey(ctx context.Context, arg InsertDecryptionK const insertDecryptionKeyShare = `-- name: InsertDecryptionKeyShare :exec INSERT INTO decryption_key_share (eon, epoch_id, keyper_index, decryption_key_share) VALUES ($1, $2, $3, $4) +ON CONFLICT DO NOTHING ` type InsertDecryptionKeyShareParams struct { diff --git a/rolling-shutter/keyper/database/sql/queries/keyper.sql b/rolling-shutter/keyper/database/sql/queries/keyper.sql index 2cd066ab0..e628fcbf9 100644 --- a/rolling-shutter/keyper/database/sql/queries/keyper.sql +++ b/rolling-shutter/keyper/database/sql/queries/keyper.sql @@ -16,7 +16,8 @@ SELECT EXISTS ( -- name: InsertDecryptionKeyShare :exec INSERT INTO decryption_key_share (eon, epoch_id, keyper_index, decryption_key_share) -VALUES ($1, $2, $3, $4); +VALUES ($1, $2, $3, $4) +ON CONFLICT DO NOTHING; -- name: SelectDecryptionKeyShares :many SELECT * FROM decryption_key_share From 0bd3f2fa6bd9139629bf6a667407d91da3b6ac6a Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Fri, 16 Feb 2024 13:33:07 +0100 Subject: [PATCH 46/91] Extend gnosis keyper tables Add tables to store - the current decryption trigger - decryption slot signatures --- .../gnosis/database/gnosiskeyper.sqlc.gen.go | 115 ++++++++++++++++++ .../gnosis/database/models.sqlc.gen.go | 16 +++ .../database/sql/queries/gnosiskeyper.sql | 22 +++- .../database/sql/schemas/gnosiskeyper.sql | 17 +++ 4 files changed, 169 insertions(+), 1 deletion(-) diff --git a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go index fe9616d3e..a1c884f5d 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go +++ b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go @@ -11,6 +11,71 @@ import ( "github.com/jackc/pgconn" ) +const getCurrentDecryptionTrigger = `-- name: GetCurrentDecryptionTrigger :one +SELECT eon, block, tx_pointer, identities_hash FROM current_decryption_trigger +WHERE eon = $1 +` + +func (q *Queries) GetCurrentDecryptionTrigger(ctx context.Context, eon int64) (CurrentDecryptionTrigger, error) { + row := q.db.QueryRow(ctx, getCurrentDecryptionTrigger, eon) + var i CurrentDecryptionTrigger + err := row.Scan( + &i.Eon, + &i.Block, + &i.TxPointer, + &i.IdentitiesHash, + ) + return i, err +} + +const getSlotDecryptionSignatures = `-- name: GetSlotDecryptionSignatures :many +SELECT eon, block, keyper_index, tx_pointer, identities_hash, signature FROM slot_decryption_signatures +WHERE eon = $1 AND block = $2 AND tx_pointer = $3 AND identities_hash = $4 +ORDER BY keyper_index ASC +LIMIT $5 +` + +type GetSlotDecryptionSignaturesParams struct { + Eon int64 + Block int64 + TxPointer int64 + IdentitiesHash []byte + Limit int32 +} + +func (q *Queries) GetSlotDecryptionSignatures(ctx context.Context, arg GetSlotDecryptionSignaturesParams) ([]SlotDecryptionSignature, error) { + rows, err := q.db.Query(ctx, getSlotDecryptionSignatures, + arg.Eon, + arg.Block, + arg.TxPointer, + arg.IdentitiesHash, + arg.Limit, + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []SlotDecryptionSignature + for rows.Next() { + var i SlotDecryptionSignature + if err := rows.Scan( + &i.Eon, + &i.Block, + &i.KeyperIndex, + &i.TxPointer, + &i.IdentitiesHash, + &i.Signature, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getTransactionSubmittedEventCount = `-- name: GetTransactionSubmittedEventCount :one SELECT event_count FROM transaction_submitted_event_count WHERE eon = $1 @@ -106,6 +171,32 @@ func (q *Queries) InitTxPointer(ctx context.Context, arg InitTxPointerParams) er return err } +const insertSlotDecryptionSignature = `-- name: InsertSlotDecryptionSignature :exec +INSERT INTO slot_decryption_signatures (eon, block, keyper_index, tx_pointer, identities_hash, signature) +VALUES ($1, $2, $3, $4, $5, $6) +` + +type InsertSlotDecryptionSignatureParams struct { + Eon int64 + Block int64 + KeyperIndex int64 + TxPointer int64 + IdentitiesHash []byte + Signature []byte +} + +func (q *Queries) InsertSlotDecryptionSignature(ctx context.Context, arg InsertSlotDecryptionSignatureParams) error { + _, err := q.db.Exec(ctx, insertSlotDecryptionSignature, + arg.Eon, + arg.Block, + arg.KeyperIndex, + arg.TxPointer, + arg.IdentitiesHash, + arg.Signature, + ) + return err +} + const insertTransactionSubmittedEvent = `-- name: InsertTransactionSubmittedEvent :execresult INSERT INTO transaction_submitted_event ( index, @@ -148,6 +239,30 @@ func (q *Queries) InsertTransactionSubmittedEvent(ctx context.Context, arg Inser ) } +const setCurrentDecryptionTrigger = `-- name: SetCurrentDecryptionTrigger :exec +INSERT INTO current_decryption_trigger (eon, block, tx_pointer, identities_hash) +VALUES ($1, $2, $3, $4) +ON CONFLICT (eon) DO UPDATE +SET block = $2, tx_pointer = $3, identities_hash = $4 +` + +type SetCurrentDecryptionTriggerParams struct { + Eon int64 + Block int64 + TxPointer int64 + IdentitiesHash []byte +} + +func (q *Queries) SetCurrentDecryptionTrigger(ctx context.Context, arg SetCurrentDecryptionTriggerParams) error { + _, err := q.db.Exec(ctx, setCurrentDecryptionTrigger, + arg.Eon, + arg.Block, + arg.TxPointer, + arg.IdentitiesHash, + ) + return err +} + const setTransactionSubmittedEventCount = `-- name: SetTransactionSubmittedEventCount :exec INSERT INTO transaction_submitted_event_count (eon, event_count) VALUES ($1, $2) diff --git a/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go index ffe9ae8d2..bfc681e91 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go +++ b/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go @@ -6,6 +6,22 @@ package database import () +type CurrentDecryptionTrigger struct { + Eon int64 + Block int64 + TxPointer int64 + IdentitiesHash []byte +} + +type SlotDecryptionSignature struct { + Eon int64 + Block int64 + KeyperIndex int64 + TxPointer int64 + IdentitiesHash []byte + Signature []byte +} + type TransactionSubmittedEvent struct { Index int64 BlockNumber int64 diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql index 926e44dad..bc85a780d 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql @@ -51,4 +51,24 @@ ON CONFLICT DO NOTHING; INSERT INTO tx_pointer (eon, block, value) VALUES ($1, $2, $3) ON CONFLICT (eon) DO UPDATE -SET block = $2, value = $3; \ No newline at end of file +SET block = $2, value = $3; + +-- name: SetCurrentDecryptionTrigger :exec +INSERT INTO current_decryption_trigger (eon, block, tx_pointer, identities_hash) +VALUES ($1, $2, $3, $4) +ON CONFLICT (eon) DO UPDATE +SET block = $2, tx_pointer = $3, identities_hash = $4; + +-- name: GetCurrentDecryptionTrigger :one +SELECT * FROM current_decryption_trigger +WHERE eon = $1; + +-- name: InsertSlotDecryptionSignature :exec +INSERT INTO slot_decryption_signatures (eon, block, keyper_index, tx_pointer, identities_hash, signature) +VALUES ($1, $2, $3, $4, $5, $6); + +-- name: GetSlotDecryptionSignatures :many +SELECT * FROM slot_decryption_signatures +WHERE eon = $1 AND block = $2 AND tx_pointer = $3 AND identities_hash = $4 +ORDER BY keyper_index ASC +LIMIT $5; diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql index 8eccadb6c..aed61f8d0 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql @@ -29,4 +29,21 @@ CREATE TABLE tx_pointer( eon bigint PRIMARY KEY, block bigint NOT NULL DEFAULT 0, value bigint NOT NULL DEFAULT 0 +); + +CREATE TABLE current_decryption_trigger( + eon bigint PRIMARY KEY CHECK (eon >= 0), + block bigint NOT NULL CHECK (block >= 0), + tx_pointer bigint NOT NULL CHECK (tx_pointer >= 0), + identities_hash bytea NOT NULL +); + +CREATE TABLE slot_decryption_signatures( + eon bigint NOT NULL CHECK (eon >= 0), + block bigint NOT NULL CHECK (block >= 0), + keyper_index bigint NOT NULL, + tx_pointer bigint NOT NULL CHECK (tx_pointer >= 0), + identities_hash bytea NOT NULL, + signature bytea NOT NULL, + PRIMARY KEY (eon, block, keyper_index) ); \ No newline at end of file From 57df4d85bd6e51b679ba038bd7a0d2e9e1d747ba Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Fri, 16 Feb 2024 13:37:46 +0100 Subject: [PATCH 47/91] Fix tx trigger updating --- rolling-shutter/keyperimpl/gnosis/keyper.go | 38 +++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index 6bc251b49..f123f03ef 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -3,6 +3,7 @@ package gnosis import ( "bytes" "context" + "fmt" "math" "github.com/ethereum/go-ethereum/common" @@ -17,6 +18,7 @@ import ( obskeyper "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" + corekeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/epochkghandler" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/kprconfig" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" @@ -27,6 +29,7 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" + "github.com/shutter-network/rolling-shutter/rolling-shutter/p2p" "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" ) @@ -208,6 +211,8 @@ func (kpr *Keyper) newKeyperSet(ctx context.Context, ev *syncevent.KeyperSet) er log.Info(). Uint64("activation-block", ev.ActivationBlock). Uint64("eon", ev.Eon). + Int("num-members", len(ev.Members)). + Uint64("threshold", ev.Threshold). Bool("is-member", isMember). Msg("new keyper set added") @@ -251,11 +256,23 @@ func (kpr *Keyper) newEonPublicKey(_ context.Context, pubKey keyper.EonPublicKey } func (kpr *Keyper) triggerDecryption(ctx context.Context, ev *syncevent.LatestBlock, keyperSet *obskeyper.KeyperSet) error { - queries := database.New(kpr.dbpool) - eon := keyperSet.KeyperConfigIndex + fmt.Println("") + fmt.Println("") + fmt.Println(ev.Number.Int64()) + fmt.Println("") + fmt.Println("") + gnosisKeyperDB := database.New(kpr.dbpool) + coreKeyperDB := corekeyperdatabase.New(kpr.dbpool) + + eonStruct, err := coreKeyperDB.GetEonForBlockNumber(ctx, ev.Number.Int64()) + if err != nil { + return errors.Wrapf(err, "failed to query eon for block number %d from db", ev.Number.Int64()) + } + eon := eonStruct.Eon + var txPointer int64 var txPointerAge int64 - txPointerDB, err := queries.GetTxPointer(ctx, eon) + txPointerDB, err := gnosisKeyperDB.GetTxPointer(ctx, eon) if err == pgx.ErrNoRows { txPointer = 0 txPointerAge = ev.Number.Int64() - keyperSet.ActivationBlockNumber + 1 @@ -289,7 +306,7 @@ func (kpr *Keyper) triggerDecryption(ctx context.Context, ev *syncevent.LatestBl Int64("tx-pointer", txPointer). Int64("tx-pointer-age", txPointerAge). Msg("outdated tx pointer") - txPointer, err = queries.GetTransactionSubmittedEventCount(ctx, keyperSet.KeyperConfigIndex) + txPointer, err = gnosisKeyperDB.GetTransactionSubmittedEventCount(ctx, keyperSet.KeyperConfigIndex) if err == pgx.ErrNoRows { txPointer = 0 } else if err != nil { @@ -301,13 +318,22 @@ func (kpr *Keyper) triggerDecryption(ctx context.Context, ev *syncevent.LatestBl if err != nil { return err } + err = gnosisKeyperDB.SetCurrentDecryptionTrigger(ctx, database.SetCurrentDecryptionTriggerParams{ + Eon: eon, + Block: ev.Number.Int64(), + TxPointer: txPointer, + IdentitiesHash: computeIdentitiesHash(identityPreimages), + }) + if err != nil { + return errors.Wrap(err, "failed to insert published tx pointer into db") + } trigger := epochkghandler.DecryptionTrigger{ - BlockNumber: uint64(ev.Number.Int64() + 1), + BlockNumber: ev.Number.Uint64(), IdentityPreimages: identityPreimages, } event := broker.NewEvent(&trigger) log.Debug(). - Int64("block-number", int64(trigger.BlockNumber)). + Uint64("block-number", ev.Number.Uint64()). Int("num-identities", len(trigger.IdentityPreimages)). Int64("tx-pointer", txPointer). Int64("tx-pointer-age", txPointerAge). From 0808f142fa3c9885b8f30cc41a275aec9af278af Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Fri, 16 Feb 2024 13:42:18 +0100 Subject: [PATCH 48/91] Integrate gnosis message interceptors and handlers --- rolling-shutter/keyperimpl/gnosis/keyper.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index f123f03ef..fd56abbf3 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -66,6 +66,14 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { return errors.Wrap(err, "failed to connect to database") } + messageSender, err := p2p.New(kpr.config.P2P) + if err != nil { + return errors.Wrap(err, "failed to initialize p2p messaging") + } + messageSender.AddMessageHandler(&DecryptionKeySharesHandler{kpr.dbpool}) + messageSender.AddMessageHandler(&DecryptionKeysHandler{kpr.dbpool}) + messagingMiddleware := NewMessagingMiddleware(messageSender, kpr.dbpool) + kpr.core, err = keyper.New( &kprconfig.Config{ InstanceID: kpr.config.InstanceID, @@ -81,6 +89,7 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { keyper.WithDBPool(kpr.dbpool), keyper.NoBroadcastEonPublicKey(), keyper.WithEonPublicKeyHandler(kpr.newEonPublicKey), + keyper.WithMessaging(messagingMiddleware), ) if err != nil { return errors.Wrap(err, "can't instantiate keyper core") From bffe04e3a0d09519ebe39fc62e7f31c647bcadbb Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Fri, 16 Feb 2024 13:42:37 +0100 Subject: [PATCH 49/91] Fix block identity preimage Make sure it's always before all transaction identity preimages such that keys are ordered properly. --- rolling-shutter/keyperimpl/gnosis/keyper.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index fd56abbf3..16dc5a5a2 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -405,5 +405,12 @@ func transactionSubmittedEventToIdentityPreimage(event database.TransactionSubmi } func makeBlockIdentityPreimage(ev *syncevent.LatestBlock) identitypreimage.IdentityPreimage { - return identitypreimage.IdentityPreimage(ev.Number.Bytes()) + // 32 bytes of zeros plus the block number as big endian (ie starting with lots of zeros as well) + // this ensures the block identity preimage is always alphanumerically before any transaction + // identity preimages. + var buf bytes.Buffer + buf.Write(common.BigToHash(common.Big0).Bytes()) + buf.Write(common.BigToHash(ev.Number.Int).Bytes()) + + return identitypreimage.IdentityPreimage(buf.Bytes()) } From 8ca5f268ba2499f05b6854e38a1af5a0f6c00633 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Fri, 16 Feb 2024 13:43:47 +0100 Subject: [PATCH 50/91] Implement gnosis message handlers and interceptors --- rolling-shutter/keyperimpl/gnosis/handlers.go | 189 +++++++++++++++++- .../keyperimpl/gnosis/identitieshash.go | 24 +++ .../keyperimpl/gnosis/messagingmiddleware.go | 159 +++++++++++++-- 3 files changed, 347 insertions(+), 25 deletions(-) create mode 100644 rolling-shutter/keyperimpl/gnosis/identitieshash.go diff --git a/rolling-shutter/keyperimpl/gnosis/handlers.go b/rolling-shutter/keyperimpl/gnosis/handlers.go index e85e79f2a..9fced8d4e 100644 --- a/rolling-shutter/keyperimpl/gnosis/handlers.go +++ b/rolling-shutter/keyperimpl/gnosis/handlers.go @@ -2,14 +2,25 @@ package gnosis import ( "context" + "math" + "github.com/ethereum/go-ethereum/common" + "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v4/pgxpool" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/pkg/errors" + "github.com/rs/zerolog/log" + obskeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" + corekeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" "github.com/shutter-network/rolling-shutter/rolling-shutter/p2pmsg" + "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" ) -type DecryptionKeySharesHandler struct{} +type DecryptionKeySharesHandler struct { + dbpool *pgxpool.Pool +} func (h *DecryptionKeySharesHandler) MessagePrototypes() []p2pmsg.Message { return []p2pmsg.Message{&p2pmsg.DecryptionKeyShares{}} @@ -17,32 +28,196 @@ func (h *DecryptionKeySharesHandler) MessagePrototypes() []p2pmsg.Message { func (h *DecryptionKeySharesHandler) ValidateMessage(_ context.Context, msg p2pmsg.Message) (pubsub.ValidationResult, error) { keyShares := msg.(*p2pmsg.DecryptionKeyShares) - _, ok := keyShares.Extra.(*p2pmsg.DecryptionKeyShares_Gnosis) + extra, ok := keyShares.Extra.(*p2pmsg.DecryptionKeyShares_Gnosis) if !ok { return pubsub.ValidationReject, errors.Errorf("unexpected extra type %T, expected Gnosis", keyShares.Extra) } + if extra.Gnosis == nil { + return pubsub.ValidationReject, errors.New("missing extra Gnosis data") + } + + if extra.Gnosis.Slot > math.MaxInt64 { + return pubsub.ValidationReject, errors.New("slot number too large") + } + if extra.Gnosis.TxPointer > math.MaxInt64 { + return pubsub.ValidationReject, errors.New("tx pointer too large") + } + // TODO: check signature + return pubsub.ValidationAccept, nil } -func (h *DecryptionKeySharesHandler) HandleMessage(_ context.Context, _ p2pmsg.Message) ([]p2pmsg.Message, error) { +func (h *DecryptionKeySharesHandler) HandleMessage(ctx context.Context, msg p2pmsg.Message) ([]p2pmsg.Message, error) { + keyShares := msg.(*p2pmsg.DecryptionKeyShares) + extra := keyShares.Extra.(*p2pmsg.DecryptionKeyShares_Gnosis).Gnosis + + gnosisDB := database.New(h.dbpool) + keyperCoreDB := corekeyperdatabase.New(h.dbpool) + obsKeyperDB := obskeyperdatabase.New(h.dbpool) + + identitiesHash := computeIdentitiesHashFromShares(keyShares.Shares) + err := gnosisDB.InsertSlotDecryptionSignature(ctx, database.InsertSlotDecryptionSignatureParams{ + Eon: int64(keyShares.Eon), + Block: int64(extra.Slot), + KeyperIndex: int64(keyShares.KeyperIndex), + TxPointer: int64(extra.TxPointer), + IdentitiesHash: identitiesHash, + Signature: extra.Signature, + }) + if err != nil { + return []p2pmsg.Message{}, errors.Wrap(err, "failed to insert tx pointer vote") + } + + eonData, err := keyperCoreDB.GetEon(ctx, int64(keyShares.Eon)) + if err != nil { + return []p2pmsg.Message{}, errors.Wrapf(err, "failed to get eon data from database for eon %d", keyShares.Eon) + } + keyperSet, err := obsKeyperDB.GetKeyperSetByKeyperConfigIndex(ctx, eonData.KeyperConfigIndex) + if err != nil { + return []p2pmsg.Message{}, errors.Wrapf(err, "failed to get keyper set from database for eon %d", keyShares.Eon) + } + + signaturesDB, err := gnosisDB.GetSlotDecryptionSignatures(ctx, database.GetSlotDecryptionSignaturesParams{ + Eon: int64(keyShares.Eon), + Block: int64(extra.Slot), + TxPointer: int64(extra.TxPointer), + IdentitiesHash: identitiesHash, + Limit: keyperSet.Threshold, + }) + if err != nil { + return []p2pmsg.Message{}, errors.Wrap(err, "failed to count slot decryption signatures") + } + + // send a keys message if we have reached the required number of both the signatures and the key shares + if len(signaturesDB) >= int(keyperSet.Threshold) { + keys := []*p2pmsg.Key{} + for _, share := range keyShares.GetShares() { + decryptionKeyDB, err := keyperCoreDB.GetDecryptionKey(ctx, corekeyperdatabase.GetDecryptionKeyParams{ + Eon: int64(keyShares.Eon), + EpochID: share.EpochID, + }) + if err == pgx.ErrNoRows { + return []p2pmsg.Message{}, nil + } + key := &p2pmsg.Key{ + Identity: share.EpochID, + Key: decryptionKeyDB.DecryptionKey, + } + keys = append(keys, key) + } + signerIndices := []uint64{} + signatures := [][]byte{} + for _, signature := range signaturesDB { + signerIndices = append(signerIndices, uint64(signature.KeyperIndex)) + signatures = append(signatures, signature.Signature) + } + decryptionKeysMsg := &p2pmsg.DecryptionKeys{ + InstanceID: keyShares.InstanceID, + Eon: keyShares.Eon, + Keys: keys, + Extra: &p2pmsg.DecryptionKeys_Gnosis{ + Gnosis: &p2pmsg.GnosisDecryptionKeysExtra{ + Slot: extra.Slot, + TxPointer: extra.TxPointer, + SignerIndices: signerIndices, + Signatures: signatures, + }, + }, + } + return []p2pmsg.Message{decryptionKeysMsg}, nil + } + return []p2pmsg.Message{}, nil } -type DecryptionKeysHandler struct{} +type DecryptionKeysHandler struct { + dbpool *pgxpool.Pool +} func (h *DecryptionKeysHandler) MessagePrototypes() []p2pmsg.Message { return []p2pmsg.Message{&p2pmsg.DecryptionKeys{}} } -func (h *DecryptionKeysHandler) ValidateMessage(_ context.Context, msg p2pmsg.Message) (pubsub.ValidationResult, error) { +func (h *DecryptionKeysHandler) ValidateMessage(ctx context.Context, msg p2pmsg.Message) (pubsub.ValidationResult, error) { keys := msg.(*p2pmsg.DecryptionKeys) - _, ok := keys.Extra.(*p2pmsg.DecryptionKeys_Gnosis) + extra, ok := keys.Extra.(*p2pmsg.DecryptionKeys_Gnosis) if !ok { return pubsub.ValidationReject, errors.Errorf("unexpected extra type %T, expected Gnosis", keys.Extra) } + if extra.Gnosis == nil { + return pubsub.ValidationReject, errors.New("missing extra Gnosis data") + } + + if extra.Gnosis.Slot > math.MaxInt64 { + return pubsub.ValidationReject, errors.New("slot number too large") + } + if extra.Gnosis.TxPointer > math.MaxInt32 { // the pointer will have to be incremented + return pubsub.ValidationReject, errors.New("tx pointer too large") + } + if len(keys.Keys) == 0 { + return pubsub.ValidationReject, errors.New("msg does not contain any keys") + } + + keyperCoreDB := corekeyperdatabase.New(h.dbpool) + obsKeyperDB := obskeyperdatabase.New(h.dbpool) + eonData, err := keyperCoreDB.GetEon(ctx, int64(keys.Eon)) + if err != nil { + return pubsub.ValidationReject, errors.Wrapf(err, "failed to get eon data from database for eon %d", keys.Eon) + } + keyperSet, err := obsKeyperDB.GetKeyperSet(ctx, eonData.ActivationBlockNumber) + if err != nil { + return pubsub.ValidationReject, errors.Wrapf(err, "failed to get keyper set from database for eon %d", keys.Eon) + } + + if int32(len(extra.Gnosis.SignerIndices)) != keyperSet.Threshold { + return pubsub.ValidationReject, errors.Errorf("expected %d signers, got %d", keyperSet.Threshold, len(extra.Gnosis.SignerIndices)) + } + signers := []common.Address{} + for i, signerIndex := range extra.Gnosis.SignerIndices { + if i >= 1 { + prevSignerIndex := extra.Gnosis.SignerIndices[i-1] + if signerIndex == prevSignerIndex { + return pubsub.ValidationReject, errors.New("duplicate signer index found") + } + if signerIndex < prevSignerIndex { + return pubsub.ValidationReject, errors.New("signer indices not ordered") + } + } + if signerIndex >= uint64(len(keyperSet.Keypers)) { + return pubsub.ValidationReject, errors.New("signer index out of range") + } + signer, err := shdb.DecodeAddress(keyperSet.Keypers[signerIndex]) + if err != nil { + return pubsub.ValidationReject, errors.Wrap(err, "failed to decode signer address") + } + signers = append(signers, signer) + } + + // TODO: check signatures + return pubsub.ValidationAccept, nil } -func (h *DecryptionKeysHandler) HandleMessage(_ context.Context, _ p2pmsg.Message) ([]p2pmsg.Message, error) { +func (h *DecryptionKeysHandler) HandleMessage(ctx context.Context, msg p2pmsg.Message) ([]p2pmsg.Message, error) { + keys := msg.(*p2pmsg.DecryptionKeys) + extra := keys.Extra.(*p2pmsg.DecryptionKeys_Gnosis).Gnosis + gnosisDB := database.New(h.dbpool) + // the first key is the block key, only the rest are tx keys, so subtract 1 + newTxPointer := int64(extra.TxPointer) + int64(len(keys.Keys)) - 1 + log.Debug(). + Uint64("eon", keys.Eon). + Uint64("block", extra.Slot). + Uint64("tx-pointer-msg", extra.TxPointer). + Int("num-keys", len(keys.Keys)). + Int64("tx-pointer-updated", newTxPointer). + Msg("updating tx pointer") + err := gnosisDB.SetTxPointer(ctx, database.SetTxPointerParams{ + Eon: int64(keys.Eon), + Block: int64(extra.Slot), + Value: newTxPointer, + }) + if err != nil { + return []p2pmsg.Message{}, errors.Wrap(err, "failed to set tx pointer") + } return []p2pmsg.Message{}, nil } diff --git a/rolling-shutter/keyperimpl/gnosis/identitieshash.go b/rolling-shutter/keyperimpl/gnosis/identitieshash.go new file mode 100644 index 000000000..ba237c987 --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/identitieshash.go @@ -0,0 +1,24 @@ +package gnosis + +import ( + "github.com/ethereum/go-ethereum/crypto" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" + "github.com/shutter-network/rolling-shutter/rolling-shutter/p2pmsg" +) + +func computeIdentitiesHash(identityPreimages []identitypreimage.IdentityPreimage) []byte { + identityPreimagesAsBytes := [][]byte{} + for _, preimage := range identityPreimages { + identityPreimagesAsBytes = append(identityPreimagesAsBytes, preimage) + } + return crypto.Keccak256(identityPreimagesAsBytes...) +} + +func computeIdentitiesHashFromShares(shares []*p2pmsg.KeyShare) []byte { + identityPreimges := []identitypreimage.IdentityPreimage{} + for _, share := range shares { + identityPreimges = append(identityPreimges, identitypreimage.IdentityPreimage(share.EpochID)) + } + return computeIdentitiesHash(identityPreimges) +} diff --git a/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go b/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go index ea67b9c06..824b500ce 100644 --- a/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go +++ b/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go @@ -11,8 +11,9 @@ import ( "github.com/rs/zerolog/log" "google.golang.org/protobuf/proto" + obskeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" + corekeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" - "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/retry" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" "github.com/shutter-network/rolling-shutter/rolling-shutter/p2p" @@ -48,7 +49,9 @@ func (h *WrappedMessageHandler) HandleMessage(ctx context.Context, msg p2pmsg.Me if err != nil { return []p2pmsg.Message{}, err } - replacedMsgs = append(replacedMsgs, replacedMsg) + if replacedMsg != nil { + replacedMsgs = append(replacedMsgs, replacedMsg) + } } return replacedMsgs, nil } @@ -73,12 +76,12 @@ func (i *MessagingMiddleware) interceptMessage(ctx context.Context, msg p2pmsg.M } func (i *MessagingMiddleware) SendMessage(ctx context.Context, msg p2pmsg.Message, opts ...retry.Option) error { - msg, err := i.interceptMessage(ctx, msg) + msgOut, err := i.interceptMessage(ctx, msg) if err != nil { return err } - if msg != nil { - return i.messaging.SendMessage(ctx, msg, opts...) + if msgOut != nil { + return i.messaging.SendMessage(ctx, msgOut, opts...) } return nil } @@ -99,6 +102,11 @@ func (i *MessagingMiddleware) interceptDecryptionKeyShares( originalMsg *p2pmsg.DecryptionKeyShares, ) (p2pmsg.Message, error) { queries := database.New(i.dbpool) + + // We have to populate the outgoing message with slot and tx pointer information. We fetch + // this information from the database. It should have been inserted when the decryption + // trigger was produced. If creating the message takes unexpectedly long, it is possible that + // it was overridden with the following trigger. In this case, we drop the message. currentDecryptionTrigger, err := queries.GetCurrentDecryptionTrigger(ctx, int64(originalMsg.Eon)) if err == pgx.ErrNoRows { log.Warn(). @@ -108,11 +116,14 @@ func (i *MessagingMiddleware) interceptDecryptionKeyShares( } else if err != nil { return nil, errors.Wrapf(err, "failed to get current decryption trigger for eon %d", originalMsg.Eon) } - identityPreimges := []identitypreimage.IdentityPreimage{} - for _, share := range originalMsg.Shares { - identityPreimges = append(identityPreimges, identitypreimage.IdentityPreimage(share.EpochID)) + if originalMsg.Eon != uint64(currentDecryptionTrigger.Eon) { + log.Warn(). + Uint64("eon-got", originalMsg.Eon). + Int64("eon-expected", currentDecryptionTrigger.Eon). + Msg("intercepted decryption key shares message with unexpected eon") + return nil, nil } - identitiesHash := computeIdentitiesHash(identityPreimges) + identitiesHash := computeIdentitiesHashFromShares(originalMsg.Shares) if !bytes.Equal(identitiesHash, currentDecryptionTrigger.IdentitiesHash) { log.Warn(). Uint64("eon", originalMsg.Eon). @@ -122,29 +133,141 @@ func (i *MessagingMiddleware) interceptDecryptionKeyShares( return nil, nil } + signature := []byte("signed") + err = queries.InsertSlotDecryptionSignature(ctx, database.InsertSlotDecryptionSignatureParams{ + Eon: currentDecryptionTrigger.Eon, + Block: currentDecryptionTrigger.Block, + KeyperIndex: int64(originalMsg.KeyperIndex), + TxPointer: currentDecryptionTrigger.TxPointer, + IdentitiesHash: identitiesHash, + Signature: signature, + }) + if err != nil { + return nil, errors.Wrapf(err, + "failed to insert slot decryption signature for eon %d and block %d", + originalMsg.Eon, + currentDecryptionTrigger.Block, + ) + } + msg := proto.Clone(originalMsg).(*p2pmsg.DecryptionKeyShares) msg.Extra = &p2pmsg.DecryptionKeyShares_Gnosis{ Gnosis: &p2pmsg.GnosisDecryptionKeySharesExtra{ Slot: uint64(currentDecryptionTrigger.Block), TxPointer: uint64(currentDecryptionTrigger.TxPointer), - Signature: []byte{}, + Signature: []byte("signed"), }, } return msg, nil } func (i *MessagingMiddleware) interceptDecryptionKeys( - _ context.Context, + ctx context.Context, originalMsg *p2pmsg.DecryptionKeys, ) (p2pmsg.Message, error) { + if originalMsg.Extra != nil { + err := i.advanceTxPointer(ctx, originalMsg) + if err != nil { + return nil, err + } + return originalMsg, nil + } + + gnosisDB := database.New(i.dbpool) + keyperCoreDB := corekeyperdatabase.New(i.dbpool) + obsKeyperDB := obskeyperdatabase.New(i.dbpool) + + trigger, err := gnosisDB.GetCurrentDecryptionTrigger(ctx, int64(originalMsg.Eon)) + if err == pgx.ErrNoRows { + log.Warn(). + Uint64("eon", originalMsg.Eon). + Msg("unknown decryption trigger for intercepted keys message") + return nil, nil + } + if err != nil { + return nil, errors.Wrapf(err, "failed to get current decryption trigger for eon %d", originalMsg.Eon) + } + + eonData, err := keyperCoreDB.GetEon(ctx, int64(originalMsg.Eon)) + if err != nil { + return nil, errors.Wrapf(err, "failed to get eon data from database for eon %d", originalMsg.Eon) + } + keyperSet, err := obsKeyperDB.GetKeyperSetByKeyperConfigIndex(ctx, eonData.KeyperConfigIndex) + if err != nil { + return nil, errors.Wrapf(err, "failed to get keyper set from database for eon %d", originalMsg.Eon) + } + + signaturesDB, err := gnosisDB.GetSlotDecryptionSignatures(ctx, database.GetSlotDecryptionSignaturesParams{ + Eon: int64(originalMsg.Eon), + Block: trigger.Block, + TxPointer: trigger.TxPointer, + IdentitiesHash: trigger.IdentitiesHash, + Limit: keyperSet.Threshold, + }) + if err != nil { + return nil, errors.Wrapf(err, "failed to count slot decryption signatures for eon %d and block %d", originalMsg.Eon, trigger.Block) + } + if len(signaturesDB) < int(keyperSet.Threshold) { + log.Debug(). + Uint64("eon", originalMsg.Eon). + Int64("block", trigger.Block). + Int64("tx-pointer", trigger.TxPointer). + Hex("identities-hash", trigger.IdentitiesHash). + Int32("threshold", keyperSet.Threshold). + Int("num-signatures", len(signaturesDB)). + Msg("dropping intercepted keys message as signature count is not high enough yet") + return nil, nil + } + + signerIndices := []uint64{} + signatures := [][]byte{} + for _, signature := range signaturesDB { + signerIndices = append(signerIndices, uint64(signature.KeyperIndex)) + signatures = append(signatures, signature.Signature) + } msg := proto.Clone(originalMsg).(*p2pmsg.DecryptionKeys) - msg.Extra = &p2pmsg.DecryptionKeys_Gnosis{ - Gnosis: &p2pmsg.GnosisDecryptionKeysExtra{ - Slot: 0, - TxPointer: 0, - SignerIndices: []uint64{}, - Signatures: [][]byte{}, - }, + extra := &p2pmsg.GnosisDecryptionKeysExtra{ + Slot: uint64(trigger.Block), + TxPointer: uint64(trigger.TxPointer), + SignerIndices: signerIndices, + Signatures: signatures, + } + msg.Extra = &p2pmsg.DecryptionKeys_Gnosis{Gnosis: extra} + err = i.advanceTxPointer(ctx, msg) + if err != nil { + return nil, err } + + log.Info(). + Uint64("block", extra.Slot). + Uint64("tx-pointer", extra.TxPointer). + Int("num-signatures", len(signaturesDB)). + Int("num-keys", len(msg.Keys)). + Msg("sending keys") return msg, nil } + +// advanceTxPointer updates the tx pointer in the database such that decryption will continue with +// the next transaction. Panics if the message does not have Gnosis extra data. +func (i *MessagingMiddleware) advanceTxPointer(ctx context.Context, msg *p2pmsg.DecryptionKeys) error { + extra := msg.Extra.(*p2pmsg.DecryptionKeys_Gnosis).Gnosis + + gnosisDB := database.New(i.dbpool) + newTxPointer := int64(extra.TxPointer) + int64(len(msg.Keys)) - 1 + log.Debug(). + Uint64("eon", msg.Eon). + Uint64("block", extra.Slot). + Uint64("tx-pointer-msg", extra.TxPointer). + Int("num-keys", len(msg.Keys)). + Int64("tx-pointer-updated", newTxPointer). + Msg("updating tx pointer") + err := gnosisDB.SetTxPointer(ctx, database.SetTxPointerParams{ + Eon: int64(msg.Eon), + Block: int64(extra.Slot), + Value: newTxPointer, + }) + if err != nil { + return errors.Wrap(err, "failed to set tx pointer") + } + return nil +} From 598c4a26ab92111768579493b6f16c10c305e591 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Fri, 16 Feb 2024 18:05:24 +0100 Subject: [PATCH 51/91] Implement slot decryption signatures --- rolling-shutter/keyperimpl/gnosis/handlers.go | 76 ++++++++++++++++++- rolling-shutter/keyperimpl/gnosis/keyper.go | 2 +- .../keyperimpl/gnosis/messagingmiddleware.go | 25 +++++- .../gnosis/slotdecryptionsignatures.go | 51 +++++++++++++ 4 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 rolling-shutter/keyperimpl/gnosis/slotdecryptionsignatures.go diff --git a/rolling-shutter/keyperimpl/gnosis/handlers.go b/rolling-shutter/keyperimpl/gnosis/handlers.go index 9fced8d4e..f8cc835d0 100644 --- a/rolling-shutter/keyperimpl/gnosis/handlers.go +++ b/rolling-shutter/keyperimpl/gnosis/handlers.go @@ -14,6 +14,7 @@ import ( obskeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" corekeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" "github.com/shutter-network/rolling-shutter/rolling-shutter/p2pmsg" "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" ) @@ -26,7 +27,7 @@ func (h *DecryptionKeySharesHandler) MessagePrototypes() []p2pmsg.Message { return []p2pmsg.Message{&p2pmsg.DecryptionKeyShares{}} } -func (h *DecryptionKeySharesHandler) ValidateMessage(_ context.Context, msg p2pmsg.Message) (pubsub.ValidationResult, error) { +func (h *DecryptionKeySharesHandler) ValidateMessage(ctx context.Context, msg p2pmsg.Message) (pubsub.ValidationResult, error) { keyShares := msg.(*p2pmsg.DecryptionKeyShares) extra, ok := keyShares.Extra.(*p2pmsg.DecryptionKeyShares_Gnosis) if !ok { @@ -42,7 +43,54 @@ func (h *DecryptionKeySharesHandler) ValidateMessage(_ context.Context, msg p2pm if extra.Gnosis.TxPointer > math.MaxInt64 { return pubsub.ValidationReject, errors.New("tx pointer too large") } - // TODO: check signature + + keyperDB := corekeyperdatabase.New(h.dbpool) + eon, err := keyperDB.GetEon(ctx, int64(keyShares.Eon)) + if err != nil { + return pubsub.ValidationReject, errors.Wrapf(err, "failed to get eon from database for eon %d", keyShares.Eon) + } + obsKeyperDB := obskeyperdatabase.New(h.dbpool) + keyperSet, err := obsKeyperDB.GetKeyperSetByKeyperConfigIndex(ctx, eon.KeyperConfigIndex) + if err != nil { + return pubsub.ValidationReject, errors.Wrapf(err, + "failed to get keyper set from database for keyper set index %d (eon %d)", + eon.KeyperConfigIndex, + keyShares.Eon, + ) + } + if keyShares.KeyperIndex >= uint64(len(keyperSet.Keypers)) { + return pubsub.ValidationReject, errors.Errorf( + "keyper index %d out of range for keyper set %d (eon %d)", + keyShares.KeyperIndex, + eon.KeyperConfigIndex, + keyShares.Eon, + ) + } + keyperAddressStr := keyperSet.Keypers[keyShares.KeyperIndex] + keyperAddress, err := shdb.DecodeAddress(keyperAddressStr) + if err != nil { + return pubsub.ValidationReject, errors.Wrap(err, "failed to decode keyper address from database") + } + + identityPreimages := []identitypreimage.IdentityPreimage{} + for _, share := range keyShares.Shares { + identityPreimage := identitypreimage.IdentityPreimage(share.EpochID) + identityPreimages = append(identityPreimages, identityPreimage) + } + slotDecryptionSignatureData := SlotDecryptionSignatureData{ + InstanceID: keyShares.InstanceID, + Eon: keyShares.Eon, + Slot: extra.Gnosis.Slot, + TxPointer: extra.Gnosis.TxPointer, + IdentityPreimages: identityPreimages, + } + signatureValid, err := CheckSlotDecryptionSignature(&slotDecryptionSignatureData, extra.Gnosis.Signature, keyperAddress) + if err != nil { + return pubsub.ValidationReject, errors.Wrap(err, "failed to check slot decryption signature") + } + if !signatureValid { + return pubsub.ValidationReject, errors.New("slot decryption signature invalid") + } return pubsub.ValidationAccept, nil } @@ -193,7 +241,29 @@ func (h *DecryptionKeysHandler) ValidateMessage(ctx context.Context, msg p2pmsg. signers = append(signers, signer) } - // TODO: check signatures + identityPreimages := []identitypreimage.IdentityPreimage{} + for _, key := range keys.Keys { + identityPreimage := identitypreimage.IdentityPreimage(key.Identity) + identityPreimages = append(identityPreimages, identityPreimage) + } + slotDecryptionSignatureData := SlotDecryptionSignatureData{ + InstanceID: keys.InstanceID, + Eon: keys.Eon, + Slot: extra.Gnosis.Slot, + TxPointer: extra.Gnosis.TxPointer, + IdentityPreimages: identityPreimages, + } + for signatureIndex := 0; signatureIndex < len(extra.Gnosis.Signatures); signatureIndex++ { + signature := extra.Gnosis.Signatures[signatureIndex] + signer := signers[signatureIndex] + signatureValid, err := CheckSlotDecryptionSignature(&slotDecryptionSignatureData, signature, signer) + if err != nil { + return pubsub.ValidationReject, errors.Wrap(err, "failed to check slot decryption signature") + } + if !signatureValid { + return pubsub.ValidationReject, errors.New("slot decryption signature invalid") + } + } return pubsub.ValidationAccept, nil } diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index 16dc5a5a2..ecd047d01 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -72,7 +72,7 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { } messageSender.AddMessageHandler(&DecryptionKeySharesHandler{kpr.dbpool}) messageSender.AddMessageHandler(&DecryptionKeysHandler{kpr.dbpool}) - messagingMiddleware := NewMessagingMiddleware(messageSender, kpr.dbpool) + messagingMiddleware := NewMessagingMiddleware(messageSender, kpr.dbpool, kpr.config) kpr.core, err = keyper.New( &kprconfig.Config{ diff --git a/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go b/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go index 824b500ce..68d6cc56e 100644 --- a/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go +++ b/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go @@ -14,6 +14,7 @@ import ( obskeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" corekeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/retry" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" "github.com/shutter-network/rolling-shutter/rolling-shutter/p2p" @@ -21,6 +22,7 @@ import ( ) type MessagingMiddleware struct { + config *Config messaging p2p.Messaging dbpool *pgxpool.Pool } @@ -56,8 +58,8 @@ func (h *WrappedMessageHandler) HandleMessage(ctx context.Context, msg p2pmsg.Me return replacedMsgs, nil } -func NewMessagingMiddleware(messaging p2p.Messaging, dbpool *pgxpool.Pool) *MessagingMiddleware { - return &MessagingMiddleware{messaging: messaging, dbpool: dbpool} +func NewMessagingMiddleware(messaging p2p.Messaging, dbpool *pgxpool.Pool, config *Config) *MessagingMiddleware { + return &MessagingMiddleware{messaging: messaging, dbpool: dbpool, config: config} } func (i *MessagingMiddleware) Start(_ context.Context, runner service.Runner) error { @@ -133,7 +135,22 @@ func (i *MessagingMiddleware) interceptDecryptionKeyShares( return nil, nil } - signature := []byte("signed") + identityPreimages := []identitypreimage.IdentityPreimage{} + for _, share := range originalMsg.Shares { + identityPreimages = append(identityPreimages, identitypreimage.IdentityPreimage(share.EpochID)) + } + slotDecryptionSignatureData := SlotDecryptionSignatureData{ + InstanceID: i.config.InstanceID, + Eon: originalMsg.Eon, + Slot: uint64(currentDecryptionTrigger.Block), + TxPointer: uint64(currentDecryptionTrigger.TxPointer), + IdentityPreimages: identityPreimages, + } + signature, err := ComputeSlotDecryptionSignature(&slotDecryptionSignatureData, i.config.Gnosis.PrivateKey.Key) + if err != nil { + return nil, errors.Wrapf(err, "failed to compute slot decryption signature") + } + err = queries.InsertSlotDecryptionSignature(ctx, database.InsertSlotDecryptionSignatureParams{ Eon: currentDecryptionTrigger.Eon, Block: currentDecryptionTrigger.Block, @@ -155,7 +172,7 @@ func (i *MessagingMiddleware) interceptDecryptionKeyShares( Gnosis: &p2pmsg.GnosisDecryptionKeySharesExtra{ Slot: uint64(currentDecryptionTrigger.Block), TxPointer: uint64(currentDecryptionTrigger.TxPointer), - Signature: []byte("signed"), + Signature: signature, }, } return msg, nil diff --git a/rolling-shutter/keyperimpl/gnosis/slotdecryptionsignatures.go b/rolling-shutter/keyperimpl/gnosis/slotdecryptionsignatures.go new file mode 100644 index 000000000..1498d0f65 --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/slotdecryptionsignatures.go @@ -0,0 +1,51 @@ +package gnosis + +import ( + "bytes" + "crypto/ecdsa" + "encoding/binary" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" +) + +type SlotDecryptionSignatureData struct { + InstanceID uint64 + Eon uint64 + Slot uint64 + TxPointer uint64 + IdentityPreimages []identitypreimage.IdentityPreimage +} + +func HashSlotDecryptionSignatureData(data *SlotDecryptionSignatureData) common.Hash { + // all fields are fixed size and identity preimages are ordered, so there is no malleability if we just append all fields + buf := new(bytes.Buffer) + _ = binary.Write(buf, binary.BigEndian, data.InstanceID) + _ = binary.Write(buf, binary.BigEndian, data.Eon) + _ = binary.Write(buf, binary.BigEndian, data.Slot) + _ = binary.Write(buf, binary.BigEndian, data.TxPointer) + for _, preimage := range data.IdentityPreimages { + _ = binary.Write(buf, binary.BigEndian, preimage) + } + return crypto.Keccak256Hash(buf.Bytes()) +} + +func ComputeSlotDecryptionSignature( + data *SlotDecryptionSignatureData, + key *ecdsa.PrivateKey, +) ([]byte, error) { + h := HashSlotDecryptionSignatureData(data) + return crypto.Sign(h.Bytes(), key) +} + +func CheckSlotDecryptionSignature(data *SlotDecryptionSignatureData, signature []byte, address common.Address) (bool, error) { + h := HashSlotDecryptionSignatureData(data) + signerPubkey, err := crypto.SigToPub(h.Bytes(), signature) + if err != nil { + return false, err + } + signerAddress := crypto.PubkeyToAddress(*signerPubkey) + return signerAddress == address, nil +} From ca87e05e5f7561132f610b6ecac3f25a4798c7df Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Sat, 17 Feb 2024 20:45:11 +0100 Subject: [PATCH 52/91] Add basic watcher command --- .../cmd/gnosiskeyper/gnosiskeyper.go | 14 ++++- .../docs/rolling-shutter_gnosiskeyper.md | 1 + .../rolling-shutter_gnosiskeyper_watch.md | 27 +++++++++ rolling-shutter/gnosiskeyperwatcher/blocks.go | 57 +++++++++++++++++++ rolling-shutter/gnosiskeyperwatcher/keys.go | 53 +++++++++++++++++ .../gnosiskeyperwatcher/watcher.go | 24 ++++++++ .../medley/configuration/command/command.go | 6 +- 7 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 rolling-shutter/docs/rolling-shutter_gnosiskeyper_watch.md create mode 100644 rolling-shutter/gnosiskeyperwatcher/blocks.go create mode 100644 rolling-shutter/gnosiskeyperwatcher/keys.go create mode 100644 rolling-shutter/gnosiskeyperwatcher/watcher.go diff --git a/rolling-shutter/cmd/gnosiskeyper/gnosiskeyper.go b/rolling-shutter/cmd/gnosiskeyper/gnosiskeyper.go index 34bf5254c..2c6fa02e1 100644 --- a/rolling-shutter/cmd/gnosiskeyper/gnosiskeyper.go +++ b/rolling-shutter/cmd/gnosiskeyper/gnosiskeyper.go @@ -9,6 +9,7 @@ import ( "github.com/spf13/cobra" "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/shversion" + "github.com/shutter-network/rolling-shutter/rolling-shutter/gnosiskeyperwatcher" keyper "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration/command" @@ -28,6 +29,12 @@ Shuttermint node which have to be started separately in advance.`, command.WithDumpConfigSubcommand(), ) builder.AddInitDBCommand(initDB) + builder.AddFunctionSubcommand( + watch, + "watch", + "Watch the keypers doing their work and log the generated decryption keys.", + cobra.NoArgs, + ) return builder.Command() } @@ -36,7 +43,7 @@ func main(config *keyper.Config) error { Str("version", shversion.Version()). Str("address", config.GetAddress().Hex()). Str("shuttermint", config.Shuttermint.ShuttermintURL). - Msg("starting gnosiskeyper") + Msg("starting gnosis keyper") kpr := keyper.New(config) return service.RunWithSighandler(context.Background(), kpr) @@ -51,3 +58,8 @@ func initDB(cfg *keyper.Config) error { defer dbpool.Close() return db.InitDB(ctx, dbpool, database.Definition.Name(), database.Definition) } + +func watch(cfg *keyper.Config) error { + log.Info().Msg("starting monitor") + return service.RunWithSighandler(context.Background(), gnosiskeyperwatcher.New(cfg)) +} diff --git a/rolling-shutter/docs/rolling-shutter_gnosiskeyper.md b/rolling-shutter/docs/rolling-shutter_gnosiskeyper.md index 5e176edd7..382f4c561 100644 --- a/rolling-shutter/docs/rolling-shutter_gnosiskeyper.md +++ b/rolling-shutter/docs/rolling-shutter_gnosiskeyper.md @@ -32,4 +32,5 @@ rolling-shutter gnosiskeyper [flags] * [rolling-shutter gnosiskeyper dump-config](rolling-shutter_gnosiskeyper_dump-config.md) - Dump a 'gnosiskeyper' configuration file, based on given config and env vars * [rolling-shutter gnosiskeyper generate-config](rolling-shutter_gnosiskeyper_generate-config.md) - Generate a 'gnosiskeyper' configuration file * [rolling-shutter gnosiskeyper initdb](rolling-shutter_gnosiskeyper_initdb.md) - Initialize the database of the 'gnosiskeyper' +* [rolling-shutter gnosiskeyper watch](rolling-shutter_gnosiskeyper_watch.md) - Watch the keypers doing their work and log the generated decryption keys. diff --git a/rolling-shutter/docs/rolling-shutter_gnosiskeyper_watch.md b/rolling-shutter/docs/rolling-shutter_gnosiskeyper_watch.md new file mode 100644 index 000000000..b81192149 --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_gnosiskeyper_watch.md @@ -0,0 +1,27 @@ +## rolling-shutter gnosiskeyper watch + +Watch the keypers doing their work and log the generated decryption keys. + +``` +rolling-shutter gnosiskeyper watch [flags] +``` + +### Options + +``` + -h, --help help for watch +``` + +### Options inherited from parent commands + +``` + --config string config file + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter gnosiskeyper](rolling-shutter_gnosiskeyper.md) - Run a Shutter keyper for Gnosis Chain + diff --git a/rolling-shutter/gnosiskeyperwatcher/blocks.go b/rolling-shutter/gnosiskeyperwatcher/blocks.go new file mode 100644 index 000000000..939369030 --- /dev/null +++ b/rolling-shutter/gnosiskeyperwatcher/blocks.go @@ -0,0 +1,57 @@ +package gnosiskeyperwatcher + +import ( + "context" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/rs/zerolog/log" + + keyper "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" +) + +type BlocksWatcher struct { + config *keyper.Config +} + +func NewBlocksWatcher(config *keyper.Config) *BlocksWatcher { + return &BlocksWatcher{ + config: config, + } +} + +func (w *BlocksWatcher) Start(ctx context.Context, runner service.Runner) error { + runner.Go(func() error { + ethClient, err := ethclient.Dial(w.config.Gnosis.EthereumURL) + if err != nil { + return err + } + + newHeads := make(chan *types.Header) + sub, err := ethClient.SubscribeNewHead(ctx, newHeads) + if err != nil { + return err + } + defer sub.Unsubscribe() + + for { + select { + case <-ctx.Done(): + return ctx.Err() + case head := <-newHeads: + w.logNewHead(head) + case err := <-sub.Err(): + return err + } + } + }) + return nil +} + +func (w *BlocksWatcher) logNewHead(head *types.Header) { + log.Info(). + Int64("number", head.Number.Int64()). + Hex("hash", head.Hash().Bytes()). + Msg("new head") +} diff --git a/rolling-shutter/gnosiskeyperwatcher/keys.go b/rolling-shutter/gnosiskeyperwatcher/keys.go new file mode 100644 index 000000000..7ce268061 --- /dev/null +++ b/rolling-shutter/gnosiskeyperwatcher/keys.go @@ -0,0 +1,53 @@ +package gnosiskeyperwatcher + +import ( + "context" + + pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/rs/zerolog/log" + + keyper "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" + "github.com/shutter-network/rolling-shutter/rolling-shutter/p2p" + "github.com/shutter-network/rolling-shutter/rolling-shutter/p2pmsg" +) + +type KeysWatcher struct { + config *keyper.Config +} + +func NewKeysWatcher(config *keyper.Config) *KeysWatcher { + return &KeysWatcher{ + config: config, + } +} + +func (w *KeysWatcher) Start(_ context.Context, runner service.Runner) error { + p2pService, err := p2p.New(w.config.P2P) + if err != nil { + return err + } + p2pService.AddMessageHandler(w) + + return runner.StartService(p2pService) +} + +func (w *KeysWatcher) MessagePrototypes() []p2pmsg.Message { + return []p2pmsg.Message{ + &p2pmsg.DecryptionKeys{}, + } +} + +func (w *KeysWatcher) ValidateMessage(_ context.Context, _ p2pmsg.Message) (pubsub.ValidationResult, error) { + return pubsub.ValidationAccept, nil +} + +func (w *KeysWatcher) HandleMessage(_ context.Context, msgUntyped p2pmsg.Message) ([]p2pmsg.Message, error) { + msg := msgUntyped.(*p2pmsg.DecryptionKeys) + extra := msg.Extra.(*p2pmsg.DecryptionKeys_Gnosis).Gnosis + log.Info(). + Uint64("block", extra.Slot). + Int("num-keys", len(msg.Keys)). + Msg("new keys") + return []p2pmsg.Message{}, nil +} diff --git a/rolling-shutter/gnosiskeyperwatcher/watcher.go b/rolling-shutter/gnosiskeyperwatcher/watcher.go new file mode 100644 index 000000000..90d4471ba --- /dev/null +++ b/rolling-shutter/gnosiskeyperwatcher/watcher.go @@ -0,0 +1,24 @@ +package gnosiskeyperwatcher + +import ( + "context" + + keyper "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" +) + +type Watcher struct { + config *keyper.Config +} + +func New(config *keyper.Config) *Watcher { + return &Watcher{ + config: config, + } +} + +func (w *Watcher) Start(_ context.Context, runner service.Runner) error { + blocksWatcher := NewBlocksWatcher(w.config) + keysWatcher := NewKeysWatcher(w.config) + return runner.StartService(blocksWatcher, keysWatcher) +} diff --git a/rolling-shutter/medley/configuration/command/command.go b/rolling-shutter/medley/configuration/command/command.go index e755b9132..f06a22a1d 100644 --- a/rolling-shutter/medley/configuration/command/command.go +++ b/rolling-shutter/medley/configuration/command/command.go @@ -159,10 +159,8 @@ func Build[T configuration.Config]( type CobraRunE func(cmd *cobra.Command, args []string) error -// AddInitDBCommand attaches an additional subcommand -// 'initdb' to the command initially built by the Build method. -// The initDB function argument is structured in the same way than the "main" -// function passed in to the Build method. +// AddFunctionSubcommand attaches an additional subcommand to the command initially built by the +// Build method. The command executes the given function and takes the given arguments. func (cb *CommandBuilder[T]) AddFunctionSubcommand( fnc ConfigurableFunc[T], use, short string, From 4b5f2409544e43a2d4e3a0957592a83965b1af53 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Sat, 17 Feb 2024 22:26:01 +0100 Subject: [PATCH 53/91] Show latency --- rolling-shutter/gnosiskeyperwatcher/blocks.go | 19 ++++- rolling-shutter/gnosiskeyperwatcher/keys.go | 81 ++++++++++++++++++- .../gnosiskeyperwatcher/watcher.go | 5 +- 3 files changed, 96 insertions(+), 9 deletions(-) diff --git a/rolling-shutter/gnosiskeyperwatcher/blocks.go b/rolling-shutter/gnosiskeyperwatcher/blocks.go index 939369030..eeed155a7 100644 --- a/rolling-shutter/gnosiskeyperwatcher/blocks.go +++ b/rolling-shutter/gnosiskeyperwatcher/blocks.go @@ -2,6 +2,7 @@ package gnosiskeyperwatcher import ( "context" + "time" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" @@ -12,12 +13,19 @@ import ( ) type BlocksWatcher struct { - config *keyper.Config + config *keyper.Config + blocksChannel chan *BlockReceivedEvent } -func NewBlocksWatcher(config *keyper.Config) *BlocksWatcher { +type BlockReceivedEvent struct { + Header *types.Header + Time time.Time +} + +func NewBlocksWatcher(config *keyper.Config, blocksChannel chan *BlockReceivedEvent) *BlocksWatcher { return &BlocksWatcher{ - config: config, + config: config, + blocksChannel: blocksChannel, } } @@ -41,6 +49,11 @@ func (w *BlocksWatcher) Start(ctx context.Context, runner service.Runner) error return ctx.Err() case head := <-newHeads: w.logNewHead(head) + ev := &BlockReceivedEvent{ + Header: head, + Time: time.Now(), + } + w.blocksChannel <- ev case err := <-sub.Err(): return err } diff --git a/rolling-shutter/gnosiskeyperwatcher/keys.go b/rolling-shutter/gnosiskeyperwatcher/keys.go index 7ce268061..0ea8eedb4 100644 --- a/rolling-shutter/gnosiskeyperwatcher/keys.go +++ b/rolling-shutter/gnosiskeyperwatcher/keys.go @@ -2,6 +2,9 @@ package gnosiskeyperwatcher import ( "context" + "fmt" + "sync" + "time" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/rs/zerolog/log" @@ -13,22 +16,33 @@ import ( ) type KeysWatcher struct { - config *keyper.Config + config *keyper.Config + blocksChannel chan *BlockReceivedEvent + + recentBlocksMux sync.Mutex + recentBlocks map[uint64]*BlockReceivedEvent + mostRecentBlock uint64 } -func NewKeysWatcher(config *keyper.Config) *KeysWatcher { +func NewKeysWatcher(config *keyper.Config, blocksChannel chan *BlockReceivedEvent) *KeysWatcher { return &KeysWatcher{ - config: config, + config: config, + blocksChannel: blocksChannel, + recentBlocksMux: sync.Mutex{}, + recentBlocks: make(map[uint64]*BlockReceivedEvent), + mostRecentBlock: 0, } } -func (w *KeysWatcher) Start(_ context.Context, runner service.Runner) error { +func (w *KeysWatcher) Start(ctx context.Context, runner service.Runner) error { p2pService, err := p2p.New(w.config.P2P) if err != nil { return err } p2pService.AddMessageHandler(w) + runner.Go(func() error { return w.insertBlocks(ctx) }) + return runner.StartService(p2pService) } @@ -43,11 +57,70 @@ func (w *KeysWatcher) ValidateMessage(_ context.Context, _ p2pmsg.Message) (pubs } func (w *KeysWatcher) HandleMessage(_ context.Context, msgUntyped p2pmsg.Message) ([]p2pmsg.Message, error) { + t := time.Now() msg := msgUntyped.(*p2pmsg.DecryptionKeys) extra := msg.Extra.(*p2pmsg.DecryptionKeys_Gnosis).Gnosis + + ev, ok := w.getRecentBlock(extra.Slot) + if !ok { + log.Warn(). + Uint64("keys-block", extra.Slot). + Uint64("most-recent-block", w.mostRecentBlock). + Msg("received keys for unknown block") + return []p2pmsg.Message{}, nil + } + + dt := t.Sub(ev.Time) log.Info(). Uint64("block", extra.Slot). Int("num-keys", len(msg.Keys)). + Str("latency", fmt.Sprintf("%.2fs", dt.Seconds())). Msg("new keys") return []p2pmsg.Message{}, nil } + +func (w *KeysWatcher) insertBlocks(ctx context.Context) error { + for { + select { + case <-ctx.Done(): + return ctx.Err() + case ev, ok := <-w.blocksChannel: + if !ok { + return nil + } + w.insertBlock(ev) + w.clearOldBlocks(ev) + } + } +} + +func (w *KeysWatcher) insertBlock(ev *BlockReceivedEvent) { + w.recentBlocksMux.Lock() + defer w.recentBlocksMux.Unlock() + w.recentBlocks[ev.Header.Number.Uint64()] = ev + if ev.Header.Number.Uint64() > w.mostRecentBlock { + w.mostRecentBlock = ev.Header.Number.Uint64() + } +} + +func (w *KeysWatcher) clearOldBlocks(latestEv *BlockReceivedEvent) { + w.recentBlocksMux.Lock() + defer w.recentBlocksMux.Unlock() + + tooOld := []uint64{} + for block := range w.recentBlocks { + if block < latestEv.Header.Number.Uint64()-100 { + tooOld = append(tooOld, block) + } + } + for _, block := range tooOld { + delete(w.recentBlocks, block) + } +} + +func (w *KeysWatcher) getRecentBlock(blockNumber uint64) (*BlockReceivedEvent, bool) { + w.recentBlocksMux.Lock() + defer w.recentBlocksMux.Unlock() + ev, ok := w.recentBlocks[blockNumber] + return ev, ok +} diff --git a/rolling-shutter/gnosiskeyperwatcher/watcher.go b/rolling-shutter/gnosiskeyperwatcher/watcher.go index 90d4471ba..58f09e028 100644 --- a/rolling-shutter/gnosiskeyperwatcher/watcher.go +++ b/rolling-shutter/gnosiskeyperwatcher/watcher.go @@ -18,7 +18,8 @@ func New(config *keyper.Config) *Watcher { } func (w *Watcher) Start(_ context.Context, runner service.Runner) error { - blocksWatcher := NewBlocksWatcher(w.config) - keysWatcher := NewKeysWatcher(w.config) + blocksChannel := make(chan *BlockReceivedEvent) + blocksWatcher := NewBlocksWatcher(w.config, blocksChannel) + keysWatcher := NewKeysWatcher(w.config, blocksChannel) return runner.StartService(blocksWatcher, keysWatcher) } From 11c4ae6e8cd7a9a6e256e01739971a1be0b315f2 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Thu, 14 Mar 2024 17:40:02 +0100 Subject: [PATCH 54/91] Use DHT to find peers --- rolling-shutter/p2p/config.go | 2 + rolling-shutter/p2p/dht.go | 78 ++++++++++++++++++++++++++++---- rolling-shutter/p2p/messaging.go | 11 ++--- rolling-shutter/p2p/p2p.go | 64 ++++++++++++++++---------- rolling-shutter/p2p/params.go | 6 +++ 5 files changed, 122 insertions(+), 39 deletions(-) diff --git a/rolling-shutter/p2p/config.go b/rolling-shutter/p2p/config.go index 716ced2ca..55bc4e2f6 100644 --- a/rolling-shutter/p2p/config.go +++ b/rolling-shutter/p2p/config.go @@ -45,6 +45,7 @@ type Config struct { ListenAddresses []*address.P2PAddress CustomBootstrapAddresses []*address.P2PAddress `comment:"Overwrite p2p boostrap nodes"` Environment env.Environment + DiscoveryNamespace string `shconfig:",required" comment:"Must be unique for each instance id."` } func (c *Config) Name() string { @@ -76,6 +77,7 @@ func (c *Config) SetExampleValues() error { ), } c.Environment = env.EnvironmentProduction + c.DiscoveryNamespace = "shutter-42" p2pkey, err := keys.GenerateLibp2pPrivate(rand.Reader) if err != nil { diff --git a/rolling-shutter/p2p/dht.go b/rolling-shutter/p2p/dht.go index b6b31c2a1..80fa0692b 100644 --- a/rolling-shutter/p2p/dht.go +++ b/rolling-shutter/p2p/dht.go @@ -1,9 +1,17 @@ package p2p import ( + "context" + "time" + dht "github.com/libp2p/go-libp2p-kad-dht" - "github.com/libp2p/go-libp2p/core/peer" + pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/libp2p/go-libp2p/core/discovery" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/protocol" + "github.com/libp2p/go-libp2p/p2p/discovery/util" + "github.com/rs/zerolog/log" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/env" ) @@ -15,18 +23,24 @@ const ( dhtProtocolPrefix protocol.ID = "/shutter" dhtProtocolExtensionStaging protocol.ID = "/staging" dhtProtocolExtensionLocal protocol.ID = "/local" + + findPeerInterval = 10 * time.Second +) + +var ( + peerLow = pubsub.GossipSubDlo * 2 + peerTarget = pubsub.GossipSubDhi * 3 + peerHigh = pubsub.GossipSubDhi * 6 ) -func dhtRoutingOptions( - environment env.Environment, - bootstrapPeers ...peer.AddrInfo, -) []dht.Option { +func dhtRoutingOptions(config *p2pNodeConfig) []dht.Option { // options with higher index in the array will overwrite existing ones opts := []dht.Option{ dht.ProtocolPrefix(dhtProtocolPrefix), + dht.BootstrapPeers(config.BootstrapPeers...), } - switch environment { //nolint: exhaustive + switch config.Environment { //nolint: exhaustive case env.EnvironmentStaging: opts = append(opts, dht.ProtocolExtension(dhtProtocolExtensionStaging), @@ -42,10 +56,56 @@ func dhtRoutingOptions( default: } - if len(bootstrapPeers) > 0 { - // this overwrites the option set before - opts = append(opts, dht.BootstrapPeers(bootstrapPeers...)) + if config.IsBootstrapNode { + opts = append(opts, dht.Mode(dht.ModeServer)) } return opts } + +func findPeers(ctx context.Context, h host.Host, d discovery.Discoverer, ns string) error { + log.Info().Str("namespace", ns).Msg("starting peer discovery") + + ticker := time.NewTicker(findPeerInterval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return ctx.Err() + case <-ticker.C: + peersBefore := len(h.Network().Peers()) + if peersBefore >= peerTarget { + continue + } + + peers, err := util.FindPeers(ctx, d, ns) + if err != nil { + log.Error().Err(err).Msg("error finding peers") + } + + newConnections := 0 + failedDials := 0 + for _, p := range peers { + if p.ID == h.ID() { + continue + } + if h.Network().Connectedness(p.ID) != network.Connected { + _, err = h.Network().DialPeer(ctx, p.ID) + if err != nil { + log.Error().Err(err).Str("peer", p.ID.String()).Msg("error dialing peer") + failedDials++ + } + newConnections++ + } + } + + log.Debug(). + Int("peers-before", peersBefore). + Int("peer-target", peerTarget). + Int("new-connections", newConnections). + Int("failed-dials", failedDials). + Msg("looking for peers") + } + } +} diff --git a/rolling-shutter/p2p/messaging.go b/rolling-shutter/p2p/messaging.go index 2073f21b9..54b3c023f 100644 --- a/rolling-shutter/p2p/messaging.go +++ b/rolling-shutter/p2p/messaging.go @@ -82,13 +82,10 @@ func New(config *Config) (*P2PMessaging, error) { listenAddresses = append(listenAddresses, addr.Multiaddr) } cfg := &p2pNodeConfig{ - ListenAddrs: listenAddresses, - PrivKey: *config.P2PKey, - Environment: config.Environment, - // for now, disable those features, since - // they are not stable from our side - DisableTopicDHT: true, - DisableRoutingDHT: true, + ListenAddrs: listenAddresses, + PrivKey: *config.P2PKey, + Environment: config.Environment, + DiscoveryNamespace: config.DiscoveryNamespace, } bootstrapAddresses := config.CustomBootstrapAddresses diff --git a/rolling-shutter/p2p/p2p.go b/rolling-shutter/p2p/p2p.go index 55d46f417..4c7f25202 100644 --- a/rolling-shutter/p2p/p2p.go +++ b/rolling-shutter/p2p/p2p.go @@ -11,7 +11,9 @@ import ( "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/p2p/discovery/routing" + "github.com/libp2p/go-libp2p/p2p/discovery/util" rhost "github.com/libp2p/go-libp2p/p2p/host/routed" + "github.com/libp2p/go-libp2p/p2p/net/connmgr" "github.com/multiformats/go-multiaddr" "github.com/pkg/errors" "github.com/rs/zerolog/log" @@ -52,6 +54,7 @@ type P2PNode struct { mux sync.Mutex host host.Host dht *dht.IpfsDHT + discovery *routing.RoutingDiscovery pubSub *pubsub.PubSub gossipRooms map[string]*gossipRoom @@ -59,13 +62,12 @@ type P2PNode struct { } type p2pNodeConfig struct { - ListenAddrs []multiaddr.Multiaddr - BootstrapPeers []peer.AddrInfo - PrivKey keys.Libp2pPrivate - Environment env.Environment - IsBootstrapNode bool - DisableTopicDHT bool - DisableRoutingDHT bool + ListenAddrs []multiaddr.Multiaddr + BootstrapPeers []peer.AddrInfo + PrivKey keys.Libp2pPrivate + Environment env.Environment + IsBootstrapNode bool + DiscoveryNamespace string } func NewP2PNode(config p2pNodeConfig) *P2PNode { @@ -118,6 +120,15 @@ func (p *P2PNode) Run( return room.readLoop(ctx, p.GossipMessages) }) } + runner.Go(func() error { + log.Info().Str("namespace", p.config.DiscoveryNamespace).Msg("starting advertizing discovery node") + util.Advertise(ctx, p.discovery, p.config.DiscoveryNamespace) + <-ctx.Done() + return ctx.Err() + }) + runner.Go(func() error { + return findPeers(ctx, p.host, p.discovery, p.config.DiscoveryNamespace) + }) return nil } @@ -141,18 +152,31 @@ func (p *P2PNode) init(ctx context.Context) error { if err != nil { return err } - p2pPubSub, err := createPubSub(ctx, p2pHost, p.config, hashTable) + discovery := routing.NewRoutingDiscovery(hashTable) + p2pPubSub, err := createPubSub(ctx, p2pHost, p.config, discovery) if err != nil { return err } p.host = p2pHost p.dht = hashTable + p.discovery = discovery p.pubSub = p2pPubSub log.Info().Str("address", p.p2pAddress()).Msg("created libp2p host") return nil } +func createConnectionManager() (*connmgr.BasicConnMgr, error) { + // TODO: This starts a background goroutine. It works, but it's better to do that later in + // P2PNode.Run() when we have a proper context. + m, err := connmgr.NewConnManager(peerLow, peerHigh) + if err != nil { + return nil, errors.Wrap(err, "failed to create connection manager") + } + + return m, nil +} + func createHost( ctx context.Context, config p2pNodeConfig, @@ -166,12 +190,15 @@ func createHost( // This was a bug in the check function, reading the wrong config value to check against: // https://github.com/libp2p/go-libp2p/issues/2628 + connectionManager, err := createConnectionManager() + if err != nil { + return nil, nil, err + } + options := []libp2p.Option{ libp2p.Identity(&config.PrivKey.Key), libp2p.ListenAddrs(config.ListenAddrs...), - // libp2p.DefaultTransports, - // libp2p.DefaultSecurity, - // libp2p.ConnectionManager(connectionManager), + libp2p.ConnectionManager(connectionManager), libp2p.ProtocolVersion(protocolVersion), } @@ -192,11 +219,7 @@ func createHost( return nil, nil, err } - if config.DisableRoutingDHT { - return p2pHost, nil, err - } - - opts := dhtRoutingOptions(config.Environment, config.BootstrapPeers...) + opts := dhtRoutingOptions(&config) idht, err := dht.New(ctx, p2pHost, opts...) if err != nil { return nil, nil, err @@ -212,7 +235,7 @@ func createPubSub( ctx context.Context, p2pHost host.Host, config p2pNodeConfig, - hashTable *dht.IpfsDHT, + discovery *routing.RoutingDiscovery, ) (*pubsub.PubSub, error) { gossipSubParams, peerScoreParams, peerScoreThresholds := makePubSubParams(pubSubParamsOptions{ isBootstrapNode: config.IsBootstrapNode, @@ -222,14 +245,9 @@ func createPubSub( pubsubOptions := []pubsub.Option{ pubsub.WithGossipSubParams(*gossipSubParams), pubsub.WithPeerScore(peerScoreParams, peerScoreThresholds), + pubsub.WithDiscovery(discovery), } - if !config.DisableTopicDHT { - pubsubOptions = append( - pubsubOptions, - pubsub.WithDiscovery(routing.NewRoutingDiscovery(hashTable)), - ) - } if config.IsBootstrapNode { // enables the pubsub v1.1 feature to handle discovery and // connection management over the PubSub protocol diff --git a/rolling-shutter/p2p/params.go b/rolling-shutter/p2p/params.go index c18a1f2d6..c4be96689 100644 --- a/rolling-shutter/p2p/params.go +++ b/rolling-shutter/p2p/params.go @@ -17,6 +17,12 @@ func makePubSubParams( ) (*pubsub.GossipSubParams, *pubsub.PeerScoreParams, *pubsub.PeerScoreThresholds) { gsDefault := pubsub.DefaultGossipSubParams() gossipSubParams := &gsDefault + + // modified defaults from ethereum consensus spec + // https://github.com/ethereum/consensus-specs/blob/5d80b1954a4b7a121aa36143d50b366727b66cbc/specs/phase0/p2p-interface.md#why-are-these-specific-gossip-parameters-chosen + gossipSubParams.HeartbeatInterval = 700 * time.Millisecond + gossipSubParams.HistoryLength = 6 + // From the spec: // to allow bootstrapping via PeerExchange (PX), // the bootstrappers should not form a mesh, thus D=D_lo=D_hi=D_out=0 From 4b160de917ab39c1f4615127d3ef729f936c71b7 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Thu, 28 Mar 2024 19:07:39 +0100 Subject: [PATCH 55/91] Trigger decryption based on slots not blocks --- rolling-shutter/keyperimpl/gnosis/config.go | 17 + .../gnosis/database/gnosiskeyper.sqlc.gen.go | 57 ++-- .../gnosis/database/models.sqlc.gen.go | 6 +- .../database/sql/queries/gnosiskeyper.sql | 14 +- .../database/sql/schemas/gnosiskeyper.sql | 10 +- rolling-shutter/keyperimpl/gnosis/handlers.go | 4 +- rolling-shutter/keyperimpl/gnosis/keyper.go | 301 ++++-------------- .../keyperimpl/gnosis/messagingmiddleware.go | 18 +- .../keyperimpl/gnosis/newblocks.go | 16 + .../keyperimpl/gnosis/neweonpublickey.go | 17 + .../keyperimpl/gnosis/newkeyperset.go | 61 ++++ rolling-shutter/keyperimpl/gnosis/newslot.go | 219 +++++++++++++ .../keyperimpl/gnosis/sequencersyncer.go | 31 +- .../medley/chainsync/event/events.go | 2 + .../medley/chainsync/syncer/unsafehead.go | 1 + rolling-shutter/medley/slots.go | 5 + .../medley/slotticker/slotticker.go | 109 +++++++ 17 files changed, 591 insertions(+), 297 deletions(-) create mode 100644 rolling-shutter/keyperimpl/gnosis/newblocks.go create mode 100644 rolling-shutter/keyperimpl/gnosis/neweonpublickey.go create mode 100644 rolling-shutter/keyperimpl/gnosis/newkeyperset.go create mode 100644 rolling-shutter/keyperimpl/gnosis/newslot.go create mode 100644 rolling-shutter/medley/slots.go create mode 100644 rolling-shutter/medley/slotticker/slotticker.go diff --git a/rolling-shutter/keyperimpl/gnosis/config.go b/rolling-shutter/keyperimpl/gnosis/config.go index 61d95ded3..e959d8edf 100644 --- a/rolling-shutter/keyperimpl/gnosis/config.go +++ b/rolling-shutter/keyperimpl/gnosis/config.go @@ -2,8 +2,10 @@ package gnosis import ( "io" + "math" "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/kprconfig" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" @@ -11,6 +13,11 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/p2p" ) +const ( + maxSecondsPerSlot = 60 * 60 + maxChainAge = 100 * 365 * 24 * 60 * 60 +) + var _ configuration.Config = &Config{} func NewConfig() *Config { @@ -38,9 +45,12 @@ type Config struct { Shuttermint *kprconfig.ShuttermintConfig Metrics *metricsserver.MetricsConfig + // TODO: put these in a child config GnosisContracts *GnosisContracts `shconfig:",required"` EncryptedGasLimit uint64 `shconfig:",required"` MinGasPerTransaction uint64 `shconfig:",required"` + SecondsPerSlot uint64 `shconfig:",required"` + GenesisSlotTimestamp uint64 `shconfig:",required"` } type GnosisContracts struct { @@ -50,6 +60,13 @@ type GnosisContracts struct { } func (c *Config) Validate() error { + if c.SecondsPerSlot > maxSecondsPerSlot { + return errors.Errorf("seconds per slot is too big (%d > %d)", c.SecondsPerSlot, maxSecondsPerSlot) + } + maxGenesisSlotTime := uint64(math.MaxInt64 - maxChainAge) + if c.GenesisSlotTimestamp > maxGenesisSlotTime { + return errors.Errorf("genesis slot timestamp is too big (%d > %d)", c.GenesisSlotTimestamp, maxGenesisSlotTime) + } return nil } diff --git a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go index a1c884f5d..01a18c2bb 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go +++ b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go @@ -12,7 +12,7 @@ import ( ) const getCurrentDecryptionTrigger = `-- name: GetCurrentDecryptionTrigger :one -SELECT eon, block, tx_pointer, identities_hash FROM current_decryption_trigger +SELECT eon, slot, tx_pointer, identities_hash FROM current_decryption_trigger WHERE eon = $1 ` @@ -21,7 +21,7 @@ func (q *Queries) GetCurrentDecryptionTrigger(ctx context.Context, eon int64) (C var i CurrentDecryptionTrigger err := row.Scan( &i.Eon, - &i.Block, + &i.Slot, &i.TxPointer, &i.IdentitiesHash, ) @@ -29,15 +29,15 @@ func (q *Queries) GetCurrentDecryptionTrigger(ctx context.Context, eon int64) (C } const getSlotDecryptionSignatures = `-- name: GetSlotDecryptionSignatures :many -SELECT eon, block, keyper_index, tx_pointer, identities_hash, signature FROM slot_decryption_signatures -WHERE eon = $1 AND block = $2 AND tx_pointer = $3 AND identities_hash = $4 +SELECT eon, slot, keyper_index, tx_pointer, identities_hash, signature FROM slot_decryption_signatures +WHERE eon = $1 AND slot = $2 AND tx_pointer = $3 AND identities_hash = $4 ORDER BY keyper_index ASC LIMIT $5 ` type GetSlotDecryptionSignaturesParams struct { Eon int64 - Block int64 + Slot int64 TxPointer int64 IdentitiesHash []byte Limit int32 @@ -46,7 +46,7 @@ type GetSlotDecryptionSignaturesParams struct { func (q *Queries) GetSlotDecryptionSignatures(ctx context.Context, arg GetSlotDecryptionSignaturesParams) ([]SlotDecryptionSignature, error) { rows, err := q.db.Query(ctx, getSlotDecryptionSignatures, arg.Eon, - arg.Block, + arg.Slot, arg.TxPointer, arg.IdentitiesHash, arg.Limit, @@ -60,7 +60,7 @@ func (q *Queries) GetSlotDecryptionSignatures(ctx context.Context, arg GetSlotDe var i SlotDecryptionSignature if err := rows.Scan( &i.Eon, - &i.Block, + &i.Slot, &i.KeyperIndex, &i.TxPointer, &i.IdentitiesHash, @@ -133,14 +133,19 @@ func (q *Queries) GetTransactionSubmittedEvents(ctx context.Context, arg GetTran } const getTransactionSubmittedEventsSyncedUntil = `-- name: GetTransactionSubmittedEventsSyncedUntil :one -SELECT block_number FROM transaction_submitted_events_synced_until LIMIT 1 +SELECT enforce_one_row, block_hash, block_number, slot FROM transaction_submitted_events_synced_until LIMIT 1 ` -func (q *Queries) GetTransactionSubmittedEventsSyncedUntil(ctx context.Context) (int64, error) { +func (q *Queries) GetTransactionSubmittedEventsSyncedUntil(ctx context.Context) (TransactionSubmittedEventsSyncedUntil, error) { row := q.db.QueryRow(ctx, getTransactionSubmittedEventsSyncedUntil) - var block_number int64 - err := row.Scan(&block_number) - return block_number, err + var i TransactionSubmittedEventsSyncedUntil + err := row.Scan( + &i.EnforceOneRow, + &i.BlockHash, + &i.BlockNumber, + &i.Slot, + ) + return i, err } const getTxPointer = `-- name: GetTxPointer :one @@ -172,13 +177,13 @@ func (q *Queries) InitTxPointer(ctx context.Context, arg InitTxPointerParams) er } const insertSlotDecryptionSignature = `-- name: InsertSlotDecryptionSignature :exec -INSERT INTO slot_decryption_signatures (eon, block, keyper_index, tx_pointer, identities_hash, signature) +INSERT INTO slot_decryption_signatures (eon, slot, keyper_index, tx_pointer, identities_hash, signature) VALUES ($1, $2, $3, $4, $5, $6) ` type InsertSlotDecryptionSignatureParams struct { Eon int64 - Block int64 + Slot int64 KeyperIndex int64 TxPointer int64 IdentitiesHash []byte @@ -188,7 +193,7 @@ type InsertSlotDecryptionSignatureParams struct { func (q *Queries) InsertSlotDecryptionSignature(ctx context.Context, arg InsertSlotDecryptionSignatureParams) error { _, err := q.db.Exec(ctx, insertSlotDecryptionSignature, arg.Eon, - arg.Block, + arg.Slot, arg.KeyperIndex, arg.TxPointer, arg.IdentitiesHash, @@ -240,15 +245,15 @@ func (q *Queries) InsertTransactionSubmittedEvent(ctx context.Context, arg Inser } const setCurrentDecryptionTrigger = `-- name: SetCurrentDecryptionTrigger :exec -INSERT INTO current_decryption_trigger (eon, block, tx_pointer, identities_hash) +INSERT INTO current_decryption_trigger (eon, slot, tx_pointer, identities_hash) VALUES ($1, $2, $3, $4) ON CONFLICT (eon) DO UPDATE -SET block = $2, tx_pointer = $3, identities_hash = $4 +SET slot = $2, tx_pointer = $3, identities_hash = $4 ` type SetCurrentDecryptionTriggerParams struct { Eon int64 - Block int64 + Slot int64 TxPointer int64 IdentitiesHash []byte } @@ -256,7 +261,7 @@ type SetCurrentDecryptionTriggerParams struct { func (q *Queries) SetCurrentDecryptionTrigger(ctx context.Context, arg SetCurrentDecryptionTriggerParams) error { _, err := q.db.Exec(ctx, setCurrentDecryptionTrigger, arg.Eon, - arg.Block, + arg.Slot, arg.TxPointer, arg.IdentitiesHash, ) @@ -281,13 +286,19 @@ func (q *Queries) SetTransactionSubmittedEventCount(ctx context.Context, arg Set } const setTransactionSubmittedEventsSyncedUntil = `-- name: SetTransactionSubmittedEventsSyncedUntil :exec -INSERT INTO transaction_submitted_events_synced_until (block_number) VALUES ($1) +INSERT INTO transaction_submitted_events_synced_until (block_hash, block_number, slot) VALUES ($1, $2, $3) ON CONFLICT (enforce_one_row) DO UPDATE -SET block_number = $1 +SET block_hash = $1, block_number = $2, slot = $3 ` -func (q *Queries) SetTransactionSubmittedEventsSyncedUntil(ctx context.Context, blockNumber int64) error { - _, err := q.db.Exec(ctx, setTransactionSubmittedEventsSyncedUntil, blockNumber) +type SetTransactionSubmittedEventsSyncedUntilParams struct { + BlockHash []byte + BlockNumber int64 + Slot int64 +} + +func (q *Queries) SetTransactionSubmittedEventsSyncedUntil(ctx context.Context, arg SetTransactionSubmittedEventsSyncedUntilParams) error { + _, err := q.db.Exec(ctx, setTransactionSubmittedEventsSyncedUntil, arg.BlockHash, arg.BlockNumber, arg.Slot) return err } diff --git a/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go index bfc681e91..ce4e21edd 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go +++ b/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go @@ -8,14 +8,14 @@ import () type CurrentDecryptionTrigger struct { Eon int64 - Block int64 + Slot int64 TxPointer int64 IdentitiesHash []byte } type SlotDecryptionSignature struct { Eon int64 - Block int64 + Slot int64 KeyperIndex int64 TxPointer int64 IdentitiesHash []byte @@ -41,7 +41,9 @@ type TransactionSubmittedEventCount struct { type TransactionSubmittedEventsSyncedUntil struct { EnforceOneRow bool + BlockHash []byte BlockNumber int64 + Slot int64 } type TxPointer struct { diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql index bc85a780d..97010a8cf 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql @@ -20,12 +20,12 @@ ORDER BY index ASC LIMIT $3; -- name: SetTransactionSubmittedEventsSyncedUntil :exec -INSERT INTO transaction_submitted_events_synced_until (block_number) VALUES ($1) +INSERT INTO transaction_submitted_events_synced_until (block_hash, block_number, slot) VALUES ($1, $2, $3) ON CONFLICT (enforce_one_row) DO UPDATE -SET block_number = $1; +SET block_hash = $1, block_number = $2, slot = $3; -- name: GetTransactionSubmittedEventsSyncedUntil :one -SELECT block_number FROM transaction_submitted_events_synced_until LIMIT 1; +SELECT * FROM transaction_submitted_events_synced_until LIMIT 1; -- name: SetTransactionSubmittedEventCount :exec INSERT INTO transaction_submitted_event_count (eon, event_count) @@ -54,21 +54,21 @@ ON CONFLICT (eon) DO UPDATE SET block = $2, value = $3; -- name: SetCurrentDecryptionTrigger :exec -INSERT INTO current_decryption_trigger (eon, block, tx_pointer, identities_hash) +INSERT INTO current_decryption_trigger (eon, slot, tx_pointer, identities_hash) VALUES ($1, $2, $3, $4) ON CONFLICT (eon) DO UPDATE -SET block = $2, tx_pointer = $3, identities_hash = $4; +SET slot = $2, tx_pointer = $3, identities_hash = $4; -- name: GetCurrentDecryptionTrigger :one SELECT * FROM current_decryption_trigger WHERE eon = $1; -- name: InsertSlotDecryptionSignature :exec -INSERT INTO slot_decryption_signatures (eon, block, keyper_index, tx_pointer, identities_hash, signature) +INSERT INTO slot_decryption_signatures (eon, slot, keyper_index, tx_pointer, identities_hash, signature) VALUES ($1, $2, $3, $4, $5, $6); -- name: GetSlotDecryptionSignatures :many SELECT * FROM slot_decryption_signatures -WHERE eon = $1 AND block = $2 AND tx_pointer = $3 AND identities_hash = $4 +WHERE eon = $1 AND slot = $2 AND tx_pointer = $3 AND identities_hash = $4 ORDER BY keyper_index ASC LIMIT $5; diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql index aed61f8d0..932ee9ee2 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql @@ -17,7 +17,9 @@ CREATE TABLE transaction_submitted_event ( CREATE TABLE transaction_submitted_events_synced_until( enforce_one_row bool PRIMARY KEY DEFAULT true, - block_number bigint NOT NULL CHECK (block_number >= 0) + block_hash bytea NOT NULL, + block_number bigint NOT NULL CHECK (block_number >= 0), + slot bigint NOT NULL CHECK (slot >= 0) ); CREATE TABLE transaction_submitted_event_count( @@ -33,17 +35,17 @@ CREATE TABLE tx_pointer( CREATE TABLE current_decryption_trigger( eon bigint PRIMARY KEY CHECK (eon >= 0), - block bigint NOT NULL CHECK (block >= 0), + slot bigint NOT NULL CHECK (slot >= 0), tx_pointer bigint NOT NULL CHECK (tx_pointer >= 0), identities_hash bytea NOT NULL ); CREATE TABLE slot_decryption_signatures( eon bigint NOT NULL CHECK (eon >= 0), - block bigint NOT NULL CHECK (block >= 0), + slot bigint NOT NULL CHECK (slot >= 0), keyper_index bigint NOT NULL, tx_pointer bigint NOT NULL CHECK (tx_pointer >= 0), identities_hash bytea NOT NULL, signature bytea NOT NULL, - PRIMARY KEY (eon, block, keyper_index) + PRIMARY KEY (eon, slot, keyper_index) ); \ No newline at end of file diff --git a/rolling-shutter/keyperimpl/gnosis/handlers.go b/rolling-shutter/keyperimpl/gnosis/handlers.go index f8cc835d0..e730e22df 100644 --- a/rolling-shutter/keyperimpl/gnosis/handlers.go +++ b/rolling-shutter/keyperimpl/gnosis/handlers.go @@ -106,7 +106,7 @@ func (h *DecryptionKeySharesHandler) HandleMessage(ctx context.Context, msg p2pm identitiesHash := computeIdentitiesHashFromShares(keyShares.Shares) err := gnosisDB.InsertSlotDecryptionSignature(ctx, database.InsertSlotDecryptionSignatureParams{ Eon: int64(keyShares.Eon), - Block: int64(extra.Slot), + Slot: int64(extra.Slot), KeyperIndex: int64(keyShares.KeyperIndex), TxPointer: int64(extra.TxPointer), IdentitiesHash: identitiesHash, @@ -127,7 +127,7 @@ func (h *DecryptionKeySharesHandler) HandleMessage(ctx context.Context, msg p2pm signaturesDB, err := gnosisDB.GetSlotDecryptionSignatures(ctx, database.GetSlotDecryptionSignaturesParams{ Eon: int64(keyShares.Eon), - Block: int64(extra.Slot), + Slot: int64(extra.Slot), TxPointer: int64(extra.TxPointer), IdentitiesHash: identitiesHash, Limit: keyperSet.Threshold, diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index ecd047d01..657f8830b 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -1,15 +1,12 @@ package gnosis import ( - "bytes" "context" - "fmt" - "math" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" gethLog "github.com/ethereum/go-ethereum/log" - "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/pgxpool" "github.com/pkg/errors" "github.com/rs/zerolog/log" @@ -18,24 +15,21 @@ import ( obskeyper "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" - corekeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/epochkghandler" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/kprconfig" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" - "github.com/shutter-network/rolling-shutter/rolling-shutter/medley" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/broker" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync" syncevent "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" - "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/slotticker" "github.com/shutter-network/rolling-shutter/rolling-shutter/p2p" - "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" ) var ErrParseKeyperSet = errors.New("cannot parse KeyperSet") -// maximum age of a tx pointer in blocks before it is considered outdated +// Maximum age of a tx pointer in blocks before it is considered outdated. const maxTxPointerAge = 2 type Keyper struct { @@ -43,9 +37,16 @@ type Keyper struct { config *Config dbpool *pgxpool.Pool chainSyncClient *chainsync.Client + sequencerSyncer *SequencerSyncer - sequencerSyncer *SequencerSyncer - decryptionTriggerChannel chan<- *broker.Event[*epochkghandler.DecryptionTrigger] + // input events + newBlocks chan *syncevent.LatestBlock + newKeyperSets chan *syncevent.KeyperSet + newEonPublicKeys chan keyper.EonPublicKey + slotTicker *slotticker.SlotTicker + + // outputs + decryptionTriggerChannel chan *broker.Event[*epochkghandler.DecryptionTrigger] } func New(c *Config) *Keyper { @@ -57,10 +58,20 @@ func New(c *Config) *Keyper { func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { var err error - decryptionTriggerChannel := make(chan *broker.Event[*epochkghandler.DecryptionTrigger]) - kpr.decryptionTriggerChannel = decryptionTriggerChannel + kpr.newBlocks = make(chan *syncevent.LatestBlock) + kpr.newKeyperSets = make(chan *syncevent.KeyperSet) + kpr.newEonPublicKeys = make(chan keyper.EonPublicKey) + kpr.decryptionTriggerChannel = make(chan *broker.Event[*epochkghandler.DecryptionTrigger]) + runner.Defer(func() { close(kpr.newBlocks) }) + runner.Defer(func() { close(kpr.newKeyperSets) }) + runner.Defer(func() { close(kpr.newEonPublicKeys) }) runner.Defer(func() { close(kpr.decryptionTriggerChannel) }) + kpr.slotTicker = slotticker.NewSlotTicker( + time.Duration(kpr.config.SecondsPerSlot*uint64(time.Second)), + time.Unix(int64(kpr.config.GenesisSlotTimestamp), 0), + ) + kpr.dbpool, err = db.Connect(ctx, runner, kpr.config.DatabaseURL, database.Definition.Name()) if err != nil { return errors.Wrap(err, "failed to connect to database") @@ -85,10 +96,10 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { Shuttermint: kpr.config.Shuttermint, Metrics: kpr.config.Metrics, }, - decryptionTriggerChannel, + kpr.decryptionTriggerChannel, keyper.WithDBPool(kpr.dbpool), keyper.NoBroadcastEonPublicKey(), - keyper.WithEonPublicKeyHandler(kpr.newEonPublicKey), + keyper.WithEonPublicKeyHandler(kpr.channelNewEonPublicKey), keyper.WithMessaging(messagingMiddleware), ) if err != nil { @@ -100,8 +111,8 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { chainsync.WithClientURL(kpr.config.Gnosis.EthereumURL), chainsync.WithKeyperSetManager(kpr.config.GnosisContracts.KeyperSetManager), chainsync.WithKeyBroadcastContract(kpr.config.GnosisContracts.KeyBroadcastContract), - chainsync.WithSyncNewBlock(kpr.newBlock), - chainsync.WithSyncNewKeyperSet(kpr.newKeyperSet), + chainsync.WithSyncNewBlock(kpr.channelNewBlock), + chainsync.WithSyncNewKeyperSet(kpr.channelNewKeyperSet), chainsync.WithPrivateKey(kpr.config.Gnosis.PrivateKey.Key), chainsync.WithLogger(gethLog.NewLogger(slog.Default().Handler())), ) @@ -114,7 +125,8 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { return err } - return runner.StartService(kpr.core, kpr.chainSyncClient) + runner.Go(func() error { return kpr.processInputs(ctx) }) + return runner.StartService(kpr.core, kpr.chainSyncClient, kpr.slotTicker) } // initSequencerSycer initializes the sequencer syncer if the keyper is known to be a member of a @@ -166,9 +178,11 @@ func (kpr *Keyper) ensureSequencerSyncing(ctx context.Context, eon uint64) error return err } kpr.sequencerSyncer = &SequencerSyncer{ - Contract: contract, - DBPool: kpr.dbpool, - StartEon: eon, + Contract: contract, + DBPool: kpr.dbpool, + StartEon: eon, + GenesisSlotTimestamp: kpr.config.GenesisSlotTimestamp, + SecondsPerSlot: kpr.config.SecondsPerSlot, } // TODO: perform an initial sync without blocking and/or set start block @@ -184,233 +198,42 @@ func (kpr *Keyper) ensureSequencerSyncing(ctx context.Context, eon uint64) error return nil } -func (kpr *Keyper) newBlock(ctx context.Context, ev *syncevent.LatestBlock) error { - if kpr.sequencerSyncer != nil { - if err := kpr.sequencerSyncer.Sync(ctx, ev.Number.Uint64()); err != nil { - return err - } - } - - queries := obskeyper.New(kpr.dbpool) - keyperSet, err := queries.GetKeyperSet(ctx, ev.Number.Int64()) - if err == pgx.ErrNoRows { - log.Debug().Uint64("block", ev.Number.Uint64()).Msg("ignoring block as no keyper set has been found for it") - return nil - } - if err != nil { - return errors.Wrapf(err, "failed to query keyper set for block %d", ev.Number) - } - for _, m := range keyperSet.Keypers { - if m == shdb.EncodeAddress(kpr.config.GetAddress()) { - return kpr.triggerDecryption(ctx, ev, &keyperSet) - } - } - log.Debug().Uint64("block", ev.Number.Uint64()).Msg("ignoring block as not part of keyper set") - return nil -} - -func (kpr *Keyper) newKeyperSet(ctx context.Context, ev *syncevent.KeyperSet) error { - isMember := false - for _, m := range ev.Members { - if m.Cmp(kpr.config.GetAddress()) == 0 { - isMember = true - break - } - } - log.Info(). - Uint64("activation-block", ev.ActivationBlock). - Uint64("eon", ev.Eon). - Int("num-members", len(ev.Members)). - Uint64("threshold", ev.Threshold). - Bool("is-member", isMember). - Msg("new keyper set added") - - if isMember { - if err := kpr.ensureSequencerSyncing(ctx, ev.Eon); err != nil { - return err - } - } - - return kpr.dbpool.BeginFunc(ctx, func(tx pgx.Tx) error { - obskeyperdb := obskeyper.New(tx) - - keyperConfigIndex, err := medley.Uint64ToInt64Safe(ev.Eon) - if err != nil { - return errors.Wrap(err, ErrParseKeyperSet.Error()) - } - activationBlockNumber, err := medley.Uint64ToInt64Safe(ev.ActivationBlock) - if err != nil { - return errors.Wrap(err, ErrParseKeyperSet.Error()) +func (kpr *Keyper) processInputs(ctx context.Context) error { + var err error + for { + select { + case ev := <-kpr.newBlocks: + err = kpr.processNewBlock(ctx, ev) + case ev := <-kpr.newKeyperSets: + err = kpr.processNewKeyperSet(ctx, ev) + case ev := <-kpr.newEonPublicKeys: + err = kpr.processNewEonPublicKey(ctx, ev) + case slot := <-kpr.slotTicker.C: + err = kpr.processNewSlot(ctx, slot) + case <-ctx.Done(): + return ctx.Err() } - threshold, err := medley.Uint64ToInt64Safe(ev.Threshold) if err != nil { - return errors.Wrap(err, ErrParseKeyperSet.Error()) + // TODO: Check if it's safe to drop those events. If not, we should store the + // ones that remain on the channel in the db and process them when we restart. + // TODO: also, should we stop the keyper or just log the error and continue? + // return err + log.Error().Err(err).Msg("error processing event") } - - return obskeyperdb.InsertKeyperSet(ctx, obskeyper.InsertKeyperSetParams{ - KeyperConfigIndex: keyperConfigIndex, - ActivationBlockNumber: activationBlockNumber, - Keypers: shdb.EncodeAddresses(ev.Members), - Threshold: int32(threshold), - }) - }) + } } -func (kpr *Keyper) newEonPublicKey(_ context.Context, pubKey keyper.EonPublicKey) error { - log.Info(). - Uint64("eon", pubKey.Eon). - Uint64("activation-block", pubKey.ActivationBlock). - Msg("new eon pk") +func (kpr *Keyper) channelNewBlock(_ context.Context, ev *syncevent.LatestBlock) error { + kpr.newBlocks <- ev return nil } -func (kpr *Keyper) triggerDecryption(ctx context.Context, ev *syncevent.LatestBlock, keyperSet *obskeyper.KeyperSet) error { - fmt.Println("") - fmt.Println("") - fmt.Println(ev.Number.Int64()) - fmt.Println("") - fmt.Println("") - gnosisKeyperDB := database.New(kpr.dbpool) - coreKeyperDB := corekeyperdatabase.New(kpr.dbpool) - - eonStruct, err := coreKeyperDB.GetEonForBlockNumber(ctx, ev.Number.Int64()) - if err != nil { - return errors.Wrapf(err, "failed to query eon for block number %d from db", ev.Number.Int64()) - } - eon := eonStruct.Eon - - var txPointer int64 - var txPointerAge int64 - txPointerDB, err := gnosisKeyperDB.GetTxPointer(ctx, eon) - if err == pgx.ErrNoRows { - txPointer = 0 - txPointerAge = ev.Number.Int64() - keyperSet.ActivationBlockNumber + 1 - } else if err != nil { - return errors.Wrap(err, "failed to query tx pointer from db") - } else { - txPointerAge = ev.Number.Int64() - txPointerDB.Block - txPointer = txPointerDB.Value - } - if txPointerAge == 0 { - // A pointer of age 0 means we already received the pointer from a DecryptionKeys message - // even though we haven't sent our shares yet. In that case, sending our shares is - // unnecessary. - log.Warn(). - Int64("block-number", ev.Number.Int64()). - Int64("eon", eon). - Int64("tx-pointer", txPointer). - Int64("tx-pointer-age", txPointerAge). - Msg("ignoring new block as tx pointer age is 0") - return nil - } - if txPointerAge > maxTxPointerAge { - // If the tx pointer is outdated, the system has failed to generate decryption keys (or at - // least we haven't received them). This either means not enough keypers are online or they - // don't agree on the current value of the tx pointer. In order to recover, we choose the - // current length of the transaction queue as the new tx pointer, as this is a value - // everyone can agree on. - log.Warn(). - Int64("block-number", ev.Number.Int64()). - Int64("eon", eon). - Int64("tx-pointer", txPointer). - Int64("tx-pointer-age", txPointerAge). - Msg("outdated tx pointer") - txPointer, err = gnosisKeyperDB.GetTransactionSubmittedEventCount(ctx, keyperSet.KeyperConfigIndex) - if err == pgx.ErrNoRows { - txPointer = 0 - } else if err != nil { - return errors.Wrap(err, "failed to query transaction submitted event count from db") - } - } - - identityPreimages, err := kpr.getDecryptionIdentityPreimages(ctx, ev, keyperSet.KeyperConfigIndex, txPointer) - if err != nil { - return err - } - err = gnosisKeyperDB.SetCurrentDecryptionTrigger(ctx, database.SetCurrentDecryptionTriggerParams{ - Eon: eon, - Block: ev.Number.Int64(), - TxPointer: txPointer, - IdentitiesHash: computeIdentitiesHash(identityPreimages), - }) - if err != nil { - return errors.Wrap(err, "failed to insert published tx pointer into db") - } - trigger := epochkghandler.DecryptionTrigger{ - BlockNumber: ev.Number.Uint64(), - IdentityPreimages: identityPreimages, - } - event := broker.NewEvent(&trigger) - log.Debug(). - Uint64("block-number", ev.Number.Uint64()). - Int("num-identities", len(trigger.IdentityPreimages)). - Int64("tx-pointer", txPointer). - Int64("tx-pointer-age", txPointerAge). - Msg("sending decryption trigger") - kpr.decryptionTriggerChannel <- event - +func (kpr *Keyper) channelNewKeyperSet(_ context.Context, ev *syncevent.KeyperSet) error { + kpr.newKeyperSets <- ev return nil } -func (kpr *Keyper) getDecryptionIdentityPreimages( - ctx context.Context, ev *syncevent.LatestBlock, eon int64, txPointer int64, -) ([]identitypreimage.IdentityPreimage, error) { - identityPreimages := []identitypreimage.IdentityPreimage{} - - queries := database.New(kpr.dbpool) - limitUint64 := kpr.config.EncryptedGasLimit/kpr.config.MinGasPerTransaction + 1 - if limitUint64 > math.MaxInt32 { - return identityPreimages, errors.New("gas limit too big") - } - limit := int32(limitUint64) - - events, err := queries.GetTransactionSubmittedEvents(ctx, database.GetTransactionSubmittedEventsParams{ - Eon: eon, - Index: txPointer, - Limit: limit, - }) - if err != nil { - return nil, errors.Wrapf(err, "failed to query transaction submitted events from index %d", txPointer) - } - - identityPreimages = []identitypreimage.IdentityPreimage{ - makeBlockIdentityPreimage(ev), - } - gas := uint64(0) - for _, event := range events { - gas += uint64(event.GasLimit) - if gas > kpr.config.EncryptedGasLimit { - break - } - identityPreimage, err := transactionSubmittedEventToIdentityPreimage(event) - if err != nil { - return []identitypreimage.IdentityPreimage{}, err - } - identityPreimages = append(identityPreimages, identityPreimage) - } - return identityPreimages, nil -} - -func transactionSubmittedEventToIdentityPreimage(event database.TransactionSubmittedEvent) (identitypreimage.IdentityPreimage, error) { - sender, err := shdb.DecodeAddress(event.Sender) - if err != nil { - return identitypreimage.IdentityPreimage{}, errors.Wrap(err, "failed to decode sender address of transaction submitted event from db") - } - - var buf bytes.Buffer - buf.Write(event.IdentityPrefix) - buf.Write(sender.Bytes()) - - return identitypreimage.IdentityPreimage(buf.Bytes()), nil -} - -func makeBlockIdentityPreimage(ev *syncevent.LatestBlock) identitypreimage.IdentityPreimage { - // 32 bytes of zeros plus the block number as big endian (ie starting with lots of zeros as well) - // this ensures the block identity preimage is always alphanumerically before any transaction - // identity preimages. - var buf bytes.Buffer - buf.Write(common.BigToHash(common.Big0).Bytes()) - buf.Write(common.BigToHash(ev.Number.Int).Bytes()) - - return identitypreimage.IdentityPreimage(buf.Bytes()) +func (kpr *Keyper) channelNewEonPublicKey(_ context.Context, key keyper.EonPublicKey) error { + kpr.newEonPublicKeys <- key + return nil } diff --git a/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go b/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go index 68d6cc56e..b5df1c496 100644 --- a/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go +++ b/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go @@ -142,7 +142,7 @@ func (i *MessagingMiddleware) interceptDecryptionKeyShares( slotDecryptionSignatureData := SlotDecryptionSignatureData{ InstanceID: i.config.InstanceID, Eon: originalMsg.Eon, - Slot: uint64(currentDecryptionTrigger.Block), + Slot: uint64(currentDecryptionTrigger.Slot), TxPointer: uint64(currentDecryptionTrigger.TxPointer), IdentityPreimages: identityPreimages, } @@ -153,7 +153,7 @@ func (i *MessagingMiddleware) interceptDecryptionKeyShares( err = queries.InsertSlotDecryptionSignature(ctx, database.InsertSlotDecryptionSignatureParams{ Eon: currentDecryptionTrigger.Eon, - Block: currentDecryptionTrigger.Block, + Slot: currentDecryptionTrigger.Slot, KeyperIndex: int64(originalMsg.KeyperIndex), TxPointer: currentDecryptionTrigger.TxPointer, IdentitiesHash: identitiesHash, @@ -161,16 +161,16 @@ func (i *MessagingMiddleware) interceptDecryptionKeyShares( }) if err != nil { return nil, errors.Wrapf(err, - "failed to insert slot decryption signature for eon %d and block %d", + "failed to insert slot decryption signature for eon %d and slot %d", originalMsg.Eon, - currentDecryptionTrigger.Block, + currentDecryptionTrigger.Slot, ) } msg := proto.Clone(originalMsg).(*p2pmsg.DecryptionKeyShares) msg.Extra = &p2pmsg.DecryptionKeyShares_Gnosis{ Gnosis: &p2pmsg.GnosisDecryptionKeySharesExtra{ - Slot: uint64(currentDecryptionTrigger.Block), + Slot: uint64(currentDecryptionTrigger.Slot), TxPointer: uint64(currentDecryptionTrigger.TxPointer), Signature: signature, }, @@ -216,18 +216,18 @@ func (i *MessagingMiddleware) interceptDecryptionKeys( signaturesDB, err := gnosisDB.GetSlotDecryptionSignatures(ctx, database.GetSlotDecryptionSignaturesParams{ Eon: int64(originalMsg.Eon), - Block: trigger.Block, + Slot: trigger.Slot, TxPointer: trigger.TxPointer, IdentitiesHash: trigger.IdentitiesHash, Limit: keyperSet.Threshold, }) if err != nil { - return nil, errors.Wrapf(err, "failed to count slot decryption signatures for eon %d and block %d", originalMsg.Eon, trigger.Block) + return nil, errors.Wrapf(err, "failed to count slot decryption signatures for eon %d and slot %d", originalMsg.Eon, trigger.Slot) } if len(signaturesDB) < int(keyperSet.Threshold) { log.Debug(). Uint64("eon", originalMsg.Eon). - Int64("block", trigger.Block). + Int64("slot", trigger.Slot). Int64("tx-pointer", trigger.TxPointer). Hex("identities-hash", trigger.IdentitiesHash). Int32("threshold", keyperSet.Threshold). @@ -244,7 +244,7 @@ func (i *MessagingMiddleware) interceptDecryptionKeys( } msg := proto.Clone(originalMsg).(*p2pmsg.DecryptionKeys) extra := &p2pmsg.GnosisDecryptionKeysExtra{ - Slot: uint64(trigger.Block), + Slot: uint64(trigger.Slot), TxPointer: uint64(trigger.TxPointer), SignerIndices: signerIndices, Signatures: signatures, diff --git a/rolling-shutter/keyperimpl/gnosis/newblocks.go b/rolling-shutter/keyperimpl/gnosis/newblocks.go new file mode 100644 index 000000000..20a1f2adf --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/newblocks.go @@ -0,0 +1,16 @@ +package gnosis + +import ( + "context" + + syncevent "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" +) + +func (kpr *Keyper) processNewBlock(ctx context.Context, ev *syncevent.LatestBlock) error { + if kpr.sequencerSyncer != nil { + if err := kpr.sequencerSyncer.Sync(ctx, ev.Header); err != nil { + return err + } + } + return nil +} diff --git a/rolling-shutter/keyperimpl/gnosis/neweonpublickey.go b/rolling-shutter/keyperimpl/gnosis/neweonpublickey.go new file mode 100644 index 000000000..d473c3a00 --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/neweonpublickey.go @@ -0,0 +1,17 @@ +package gnosis + +import ( + "context" + + "github.com/rs/zerolog/log" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" +) + +func (kpr *Keyper) processNewEonPublicKey(_ context.Context, key keyper.EonPublicKey) error { //nolint:unparam + log.Info(). + Uint64("eon", key.Eon). + Uint64("activation-block", key.ActivationBlock). + Msg("new eon pk") + return nil +} diff --git a/rolling-shutter/keyperimpl/gnosis/newkeyperset.go b/rolling-shutter/keyperimpl/gnosis/newkeyperset.go new file mode 100644 index 000000000..919b8d878 --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/newkeyperset.go @@ -0,0 +1,61 @@ +package gnosis + +import ( + "context" + + "github.com/jackc/pgx/v4" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + + obskeyper "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley" + syncevent "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" +) + +func (kpr *Keyper) processNewKeyperSet(ctx context.Context, ev *syncevent.KeyperSet) error { + isMember := false + for _, m := range ev.Members { + if m.Cmp(kpr.config.GetAddress()) == 0 { + isMember = true + break + } + } + log.Info(). + Uint64("activation-block", ev.ActivationBlock). + Uint64("eon", ev.Eon). + Int("num-members", len(ev.Members)). + Uint64("threshold", ev.Threshold). + Bool("is-member", isMember). + Msg("new keyper set added") + + if isMember { + if err := kpr.ensureSequencerSyncing(ctx, ev.Eon); err != nil { + return err + } + } + + return kpr.dbpool.BeginFunc(ctx, func(tx pgx.Tx) error { + obskeyperdb := obskeyper.New(tx) + + keyperConfigIndex, err := medley.Uint64ToInt64Safe(ev.Eon) + if err != nil { + return errors.Wrap(err, ErrParseKeyperSet.Error()) + } + activationBlockNumber, err := medley.Uint64ToInt64Safe(ev.ActivationBlock) + if err != nil { + return errors.Wrap(err, ErrParseKeyperSet.Error()) + } + threshold, err := medley.Uint64ToInt64Safe(ev.Threshold) + if err != nil { + return errors.Wrap(err, ErrParseKeyperSet.Error()) + } + + return obskeyperdb.InsertKeyperSet(ctx, obskeyper.InsertKeyperSetParams{ + KeyperConfigIndex: keyperConfigIndex, + ActivationBlockNumber: activationBlockNumber, + Keypers: shdb.EncodeAddresses(ev.Members), + Threshold: int32(threshold), + }) + }) +} diff --git a/rolling-shutter/keyperimpl/gnosis/newslot.go b/rolling-shutter/keyperimpl/gnosis/newslot.go new file mode 100644 index 000000000..e65d57c76 --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/newslot.go @@ -0,0 +1,219 @@ +package gnosis + +import ( + "bytes" + "context" + "fmt" + "math" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/jackc/pgx/v4" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + + obskeyper "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" + corekeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/epochkghandler" + gnosisdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/broker" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/slotticker" + "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" +) + +func (kpr *Keyper) processNewSlot(ctx context.Context, slot slotticker.Slot) error { + gnosisKeyperDB := gnosisdatabase.New(kpr.dbpool) + syncedUntil, err := gnosisKeyperDB.GetTransactionSubmittedEventsSyncedUntil(ctx) + if err != nil { + return errors.Wrap(err, "failed to query synced until from db") + } + if syncedUntil.Slot >= int64(slot.Number) { + // If we already synced the block for slot n before this slot has started on our clock, + // either the previous block proposer proposed early (ie is malicious) or our clocks are + // out of sync. In any case, it does not make sense to produce keys as the block has + // already been built, so we return an error. + return errors.Errorf("processing slot %d for which a block has already been processed", slot.Number) + } + + queries := obskeyper.New(kpr.dbpool) + keyperSet, err := queries.GetKeyperSet(ctx, syncedUntil.BlockNumber) + if err == pgx.ErrNoRows { + log.Debug(). + Uint64("slot", slot.Number). + Int64("block-number", syncedUntil.BlockNumber). + Msg("ignoring slot as no keyper set has been found for it") + return nil + } + if err != nil { + return errors.Wrapf(err, "failed to query keyper set for block %d", syncedUntil.BlockNumber) + } + for _, m := range keyperSet.Keypers { + if m == shdb.EncodeAddress(kpr.config.GetAddress()) { + return kpr.triggerDecryption(ctx, slot, syncedUntil, &keyperSet) + } + } + log.Debug().Uint64("slot", slot.Number).Msg("ignoring block as not part of keyper set") + return nil +} + +func (kpr *Keyper) triggerDecryption( + ctx context.Context, + slot slotticker.Slot, + syncedUntil gnosisdatabase.TransactionSubmittedEventsSyncedUntil, + keyperSet *obskeyper.KeyperSet, +) error { + fmt.Println("") + fmt.Println("") + fmt.Println(slot.Number) + fmt.Println("") + fmt.Println("") + gnosisKeyperDB := gnosisdatabase.New(kpr.dbpool) + coreKeyperDB := corekeyperdatabase.New(kpr.dbpool) + + eonStruct, err := coreKeyperDB.GetEonForBlockNumber(ctx, syncedUntil.BlockNumber) + if err != nil { + return errors.Wrapf(err, "failed to query eon for block number %d from db", syncedUntil.BlockNumber) + } + eon := eonStruct.Eon + + var txPointer int64 + var txPointerAge int64 + txPointerDB, err := gnosisKeyperDB.GetTxPointer(ctx, eon) + if err == pgx.ErrNoRows { + txPointer = 0 + txPointerAge = syncedUntil.BlockNumber - keyperSet.ActivationBlockNumber + 1 + } else if err != nil { + return errors.Wrap(err, "failed to query tx pointer from db") + } else { + txPointerAge = syncedUntil.BlockNumber - txPointerDB.Block + txPointer = txPointerDB.Value + } + if txPointerAge == 0 { + // A pointer of age 0 means we already received the pointer from a DecryptionKeys message + // even though we haven't sent our shares yet. In that case, sending our shares is + // unnecessary. + log.Warn(). + Uint64("slot", slot.Number). + Int64("block-number", syncedUntil.BlockNumber). + Int64("eon", eon). + Int64("tx-pointer", txPointer). + Int64("tx-pointer-age", txPointerAge). + Msg("ignoring new block as tx pointer age is 0") + return nil + } + if txPointerAge > maxTxPointerAge { + // If the tx pointer is outdated, the system has failed to generate decryption keys (or at + // least we haven't received them). This either means not enough keypers are online or they + // don't agree on the current value of the tx pointer. In order to recover, we choose the + // current length of the transaction queue as the new tx pointer, as this is a value + // everyone can agree on. + log.Warn(). + Uint64("slot", slot.Number). + Int64("block-number", syncedUntil.BlockNumber). + Int64("eon", eon). + Int64("tx-pointer", txPointer). + Int64("tx-pointer-age", txPointerAge). + Msg("outdated tx pointer") + txPointer, err = gnosisKeyperDB.GetTransactionSubmittedEventCount(ctx, keyperSet.KeyperConfigIndex) + if err == pgx.ErrNoRows { + txPointer = 0 + } else if err != nil { + return errors.Wrap(err, "failed to query transaction submitted event count from db") + } + } + + identityPreimages, err := kpr.getDecryptionIdentityPreimages(ctx, slot, keyperSet.KeyperConfigIndex, txPointer) + if err != nil { + return err + } + err = gnosisKeyperDB.SetCurrentDecryptionTrigger(ctx, gnosisdatabase.SetCurrentDecryptionTriggerParams{ + Eon: eon, + Slot: int64(slot.Number), + TxPointer: txPointer, + IdentitiesHash: computeIdentitiesHash(identityPreimages), + }) + if err != nil { + return errors.Wrap(err, "failed to insert published tx pointer into db") + } + trigger := epochkghandler.DecryptionTrigger{ + BlockNumber: uint64(syncedUntil.BlockNumber), + IdentityPreimages: identityPreimages, + } + event := broker.NewEvent(&trigger) + log.Debug(). + Uint64("slot", slot.Number). + Uint64("block-number", uint64(syncedUntil.BlockNumber)). + Int("num-identities", len(trigger.IdentityPreimages)). + Int64("tx-pointer", txPointer). + Int64("tx-pointer-age", txPointerAge). + Msg("sending decryption trigger") + kpr.decryptionTriggerChannel <- event + + return nil +} + +func (kpr *Keyper) getDecryptionIdentityPreimages( + ctx context.Context, slot slotticker.Slot, eon int64, txPointer int64, +) ([]identitypreimage.IdentityPreimage, error) { + identityPreimages := []identitypreimage.IdentityPreimage{} + + queries := gnosisdatabase.New(kpr.dbpool) + limitUint64 := kpr.config.EncryptedGasLimit/kpr.config.MinGasPerTransaction + 1 + if limitUint64 > math.MaxInt32 { + return identityPreimages, errors.New("gas limit too big") + } + limit := int32(limitUint64) + + events, err := queries.GetTransactionSubmittedEvents(ctx, gnosisdatabase.GetTransactionSubmittedEventsParams{ + Eon: eon, + Index: txPointer, + Limit: limit, + }) + if err != nil { + return nil, errors.Wrapf(err, "failed to query transaction submitted events from index %d", txPointer) + } + + identityPreimages = []identitypreimage.IdentityPreimage{ + makeSlotIdentityPreimage(slot), + } + gas := uint64(0) + for _, event := range events { + gas += uint64(event.GasLimit) + if gas > kpr.config.EncryptedGasLimit { + break + } + identityPreimage, err := transactionSubmittedEventToIdentityPreimage(event) + if err != nil { + return []identitypreimage.IdentityPreimage{}, err + } + identityPreimages = append(identityPreimages, identityPreimage) + } + return identityPreimages, nil +} + +func transactionSubmittedEventToIdentityPreimage( + event gnosisdatabase.TransactionSubmittedEvent, +) (identitypreimage.IdentityPreimage, error) { + sender, err := shdb.DecodeAddress(event.Sender) + if err != nil { + return identitypreimage.IdentityPreimage{}, errors.Wrap(err, "failed to decode sender address of transaction submitted event from db") + } + + var buf bytes.Buffer + buf.Write(event.IdentityPrefix) + buf.Write(sender.Bytes()) + + return identitypreimage.IdentityPreimage(buf.Bytes()), nil +} + +func makeSlotIdentityPreimage(slot slotticker.Slot) identitypreimage.IdentityPreimage { + // 32 bytes of zeros plus the block number as big endian (ie starting with lots of zeros as well) + // this ensures the block identity preimage is always alphanumerically before any transaction + // identity preimages. + var buf bytes.Buffer + buf.Write(common.BigToHash(common.Big0).Bytes()) + buf.Write(common.BigToHash(new(big.Int).SetUint64(slot.Number)).Bytes()) + + return identitypreimage.IdentityPreimage(buf.Bytes()) +} diff --git a/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go b/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go index 854f4958a..2df30123e 100644 --- a/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go +++ b/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go @@ -5,6 +5,7 @@ import ( "math" "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/core/types" "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/pgxpool" "github.com/pkg/errors" @@ -18,17 +19,19 @@ import ( // SequencerSyncer inserts transaction submitted events from the sequencer contract into the database. type SequencerSyncer struct { - Contract *sequencerBindings.Sequencer - DBPool *pgxpool.Pool - StartEon uint64 + Contract *sequencerBindings.Sequencer + DBPool *pgxpool.Pool + StartEon uint64 + GenesisSlotTimestamp uint64 + SecondsPerSlot uint64 } // Sync fetches transaction submitted events from the sequencer contract and inserts them into the // database. It starts at the end point of the previous call to sync (or 0 if it is the first call) // and ends at the given block number. -func (s *SequencerSyncer) Sync(ctx context.Context, block uint64) error { +func (s *SequencerSyncer) Sync(ctx context.Context, header *types.Header) error { queries := database.New(s.DBPool) - syncedUntilBlock, err := queries.GetTransactionSubmittedEventsSyncedUntil(ctx) + syncedUntil, err := queries.GetTransactionSubmittedEventsSyncedUntil(ctx) if err != nil && err != pgx.ErrNoRows { return errors.Wrap(err, "failed to query transaction submitted events sync status") } @@ -36,17 +39,18 @@ func (s *SequencerSyncer) Sync(ctx context.Context, block uint64) error { if err == pgx.ErrNoRows { start = 0 } else { - start = uint64(syncedUntilBlock + 1) + start = uint64(syncedUntil.BlockNumber + 1) } log.Debug(). Uint64("start-block", start). - Uint64("end-block", block). + Uint64("end-block", header.Number.Uint64()). Msg("syncing sequencer contract") + endBlock := header.Number.Uint64() opts := bind.FilterOpts{ Start: start, - End: &block, + End: &endBlock, Context: ctx, } it, err := s.Contract.SequencerFilterer.FilterTransactionSubmitted(&opts) @@ -75,7 +79,7 @@ func (s *SequencerSyncer) Sync(ctx context.Context, block uint64) error { if len(events) == 0 { log.Debug(). Uint64("start-block", start). - Uint64("end-block", block). + Uint64("end-block", endBlock). Msg("no transaction submitted events found") } @@ -85,11 +89,16 @@ func (s *SequencerSyncer) Sync(ctx context.Context, block uint64) error { return err } - newSyncedUntilBlock, err := medley.Uint64ToInt64Safe(block) + newSyncedUntilBlock, err := medley.Uint64ToInt64Safe(endBlock) if err != nil { return err } - err = queries.SetTransactionSubmittedEventsSyncedUntil(ctx, newSyncedUntilBlock) + slot := medley.BlockTimestampToSlot(header.Time, s.GenesisSlotTimestamp, s.SecondsPerSlot) + err = queries.SetTransactionSubmittedEventsSyncedUntil(ctx, database.SetTransactionSubmittedEventsSyncedUntilParams{ + BlockNumber: newSyncedUntilBlock, + BlockHash: header.Hash().Bytes(), + Slot: int64(slot), + }) if err != nil { return err } diff --git a/rolling-shutter/medley/chainsync/event/events.go b/rolling-shutter/medley/chainsync/event/events.go index 4e550b32c..9f7c3b014 100644 --- a/rolling-shutter/medley/chainsync/event/events.go +++ b/rolling-shutter/medley/chainsync/event/events.go @@ -2,6 +2,7 @@ package event import ( "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" ) @@ -29,5 +30,6 @@ type ( LatestBlock struct { Number *number.BlockNumber BlockHash common.Hash + Header *types.Header } ) diff --git a/rolling-shutter/medley/chainsync/syncer/unsafehead.go b/rolling-shutter/medley/chainsync/syncer/unsafehead.go index 15979bfba..86ddbd1b3 100644 --- a/rolling-shutter/medley/chainsync/syncer/unsafehead.go +++ b/rolling-shutter/medley/chainsync/syncer/unsafehead.go @@ -52,6 +52,7 @@ func (s *UnsafeHeadSyncer) watchLatestUnsafeHead(ctx context.Context) error { ev := &event.LatestBlock{ Number: number.BigToBlockNumber(newHeader.Number), BlockHash: newHeader.Hash(), + Header: newHeader, } err := s.Handler(ctx, ev) if err != nil { diff --git a/rolling-shutter/medley/slots.go b/rolling-shutter/medley/slots.go new file mode 100644 index 000000000..15fa54388 --- /dev/null +++ b/rolling-shutter/medley/slots.go @@ -0,0 +1,5 @@ +package medley + +func BlockTimestampToSlot(blockTimestamp uint64, secondsPerSlot uint64, genesisSlotTimestamp uint64) uint64 { + return (blockTimestamp - genesisSlotTimestamp) / secondsPerSlot +} diff --git a/rolling-shutter/medley/slotticker/slotticker.go b/rolling-shutter/medley/slotticker/slotticker.go new file mode 100644 index 000000000..4d7d4f652 --- /dev/null +++ b/rolling-shutter/medley/slotticker/slotticker.go @@ -0,0 +1,109 @@ +package slotticker + +import ( + "context" + "time" + + "github.com/rs/zerolog/log" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" +) + +type Slot struct { + Number uint64 + genesisSlotTime time.Time + slotDuration time.Duration +} + +func (s Slot) Start() time.Time { + return s.genesisSlotTime.Add(s.slotDuration * time.Duration(s.Number)) +} + +// SlotTicker is a ticker that ticks at the start of each slot. +type SlotTicker struct { + C chan Slot + slotDuration time.Duration + genesisSlotTime time.Time +} + +func NewSlotTicker(slotDuration time.Duration, genesisSlotTime time.Time) *SlotTicker { + c := make(chan Slot, 1) + return &SlotTicker{ + C: c, + slotDuration: slotDuration, + genesisSlotTime: genesisSlotTime, + } +} + +func (t *SlotTicker) tick(ctx context.Context, n uint64) error { + s := Slot{ + Number: n, + genesisSlotTime: t.genesisSlotTime, + slotDuration: t.slotDuration, + } + select { + case <-ctx.Done(): + return ctx.Err() + case t.C <- s: + return nil + } +} + +//nolint:unparam +func (t *SlotTicker) Start(ctx context.Context, runner service.Runner) error { + runner.Go(func() error { + return t.run(ctx) + }) + return nil +} + +func (t *SlotTicker) run(ctx context.Context) error { + var prevSlotNumber *uint64 = nil + timer := time.NewTimer(0) + <-timer.C + + for { + now := time.Now() + timeSinceGenesis := now.Sub(t.genesisSlotTime) + + var nextSlotNumber uint64 + if timeSinceGenesis < 0 { + nextSlotNumber = 0 + } else { + nextSlotNumber = uint64(timeSinceGenesis/t.slotDuration) + 1 + } + + if prevSlotNumber != nil { + expectedNextSlotNumber := *prevSlotNumber + 1 + if nextSlotNumber < expectedNextSlotNumber { + // This should never happen unless the system clock changes. If it does, there's + // nothing we can do about it. + log.Error(). + Uint64("next-slot-number", nextSlotNumber). + Uint64("prev-slot-number", *prevSlotNumber). + Msg("slot ticker emitted slots in wrong order") + } else if nextSlotNumber > expectedNextSlotNumber { + log.Warn(). + Uint64("next-slot-number", nextSlotNumber). + Uint64("prev-slot-number", *prevSlotNumber). + Msg("missing slots due to slow slot processing") + for i := expectedNextSlotNumber; i < nextSlotNumber; i++ { + if err := t.tick(ctx, i); err != nil { + return err + } + } + } + } + + nextSlotTime := t.genesisSlotTime.Add(t.slotDuration * time.Duration(nextSlotNumber)) + timeToNextSlot := nextSlotTime.Sub(now) + timer.Reset(timeToNextSlot) + <-timer.C + + if err := t.tick(ctx, nextSlotNumber); err != nil { + return err + } + + prevSlotNumber = &nextSlotNumber + } +} From db250a4c858de68bb28d811ada7e516150ec952d Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Thu, 4 Apr 2024 12:35:12 +0200 Subject: [PATCH 56/91] Trigger in new slots instead of blocks --- .../chainobserver/db/keyper/extend.go | 17 +++ .../gnosis/database/gnosiskeyper.sqlc.gen.go | 20 +-- .../gnosis/database/models.sqlc.gen.go | 2 +- .../database/sql/queries/gnosiskeyper.sql | 6 +- .../database/sql/schemas/gnosiskeyper.sql | 2 +- rolling-shutter/keyperimpl/gnosis/handlers.go | 4 +- rolling-shutter/keyperimpl/gnosis/keyper.go | 3 - .../keyperimpl/gnosis/messagingmiddleware.go | 4 +- .../gnosis/{newblocks.go => newblock.go} | 0 rolling-shutter/keyperimpl/gnosis/newslot.go | 144 +++++++++++------- 10 files changed, 126 insertions(+), 76 deletions(-) create mode 100644 rolling-shutter/chainobserver/db/keyper/extend.go rename rolling-shutter/keyperimpl/gnosis/{newblocks.go => newblock.go} (100%) diff --git a/rolling-shutter/chainobserver/db/keyper/extend.go b/rolling-shutter/chainobserver/db/keyper/extend.go new file mode 100644 index 000000000..f37c359ef --- /dev/null +++ b/rolling-shutter/chainobserver/db/keyper/extend.go @@ -0,0 +1,17 @@ +package database + +import ( + "github.com/ethereum/go-ethereum/common" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" +) + +func (s *KeyperSet) Contains(address common.Address) bool { + encodedAddress := shdb.EncodeAddress(address) + for _, m := range s.Keypers { + if m == encodedAddress { + return true + } + } + return false +} diff --git a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go index 01a18c2bb..c8b2e7e9c 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go +++ b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go @@ -149,30 +149,30 @@ func (q *Queries) GetTransactionSubmittedEventsSyncedUntil(ctx context.Context) } const getTxPointer = `-- name: GetTxPointer :one -SELECT eon, block, value FROM tx_pointer +SELECT eon, slot, value FROM tx_pointer WHERE eon = $1 ` func (q *Queries) GetTxPointer(ctx context.Context, eon int64) (TxPointer, error) { row := q.db.QueryRow(ctx, getTxPointer, eon) var i TxPointer - err := row.Scan(&i.Eon, &i.Block, &i.Value) + err := row.Scan(&i.Eon, &i.Slot, &i.Value) return i, err } const initTxPointer = `-- name: InitTxPointer :exec -INSERT INTO tx_pointer (eon, block, value) +INSERT INTO tx_pointer (eon, slot, value) VALUES ($1, $2, 0) ON CONFLICT DO NOTHING ` type InitTxPointerParams struct { - Eon int64 - Block int64 + Eon int64 + Slot int64 } func (q *Queries) InitTxPointer(ctx context.Context, arg InitTxPointerParams) error { - _, err := q.db.Exec(ctx, initTxPointer, arg.Eon, arg.Block) + _, err := q.db.Exec(ctx, initTxPointer, arg.Eon, arg.Slot) return err } @@ -303,19 +303,19 @@ func (q *Queries) SetTransactionSubmittedEventsSyncedUntil(ctx context.Context, } const setTxPointer = `-- name: SetTxPointer :exec -INSERT INTO tx_pointer (eon, block, value) +INSERT INTO tx_pointer (eon, slot, value) VALUES ($1, $2, $3) ON CONFLICT (eon) DO UPDATE -SET block = $2, value = $3 +SET slot = $2, value = $3 ` type SetTxPointerParams struct { Eon int64 - Block int64 + Slot int64 Value int64 } func (q *Queries) SetTxPointer(ctx context.Context, arg SetTxPointerParams) error { - _, err := q.db.Exec(ctx, setTxPointer, arg.Eon, arg.Block, arg.Value) + _, err := q.db.Exec(ctx, setTxPointer, arg.Eon, arg.Slot, arg.Value) return err } diff --git a/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go index ce4e21edd..2cb9d9fee 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go +++ b/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go @@ -48,6 +48,6 @@ type TransactionSubmittedEventsSyncedUntil struct { type TxPointer struct { Eon int64 - Block int64 + Slot int64 Value int64 } diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql index 97010a8cf..9c5574076 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql @@ -43,15 +43,15 @@ SELECT * FROM tx_pointer WHERE eon = $1; -- name: InitTxPointer :exec -INSERT INTO tx_pointer (eon, block, value) +INSERT INTO tx_pointer (eon, slot, value) VALUES ($1, $2, 0) ON CONFLICT DO NOTHING; -- name: SetTxPointer :exec -INSERT INTO tx_pointer (eon, block, value) +INSERT INTO tx_pointer (eon, slot, value) VALUES ($1, $2, $3) ON CONFLICT (eon) DO UPDATE -SET block = $2, value = $3; +SET slot = $2, value = $3; -- name: SetCurrentDecryptionTrigger :exec INSERT INTO current_decryption_trigger (eon, slot, tx_pointer, identities_hash) diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql index 932ee9ee2..0e30b1bcb 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql @@ -29,7 +29,7 @@ CREATE TABLE transaction_submitted_event_count( CREATE TABLE tx_pointer( eon bigint PRIMARY KEY, - block bigint NOT NULL DEFAULT 0, + slot bigint NOT NULL DEFAULT 0, value bigint NOT NULL DEFAULT 0 ); diff --git a/rolling-shutter/keyperimpl/gnosis/handlers.go b/rolling-shutter/keyperimpl/gnosis/handlers.go index e730e22df..e35404e70 100644 --- a/rolling-shutter/keyperimpl/gnosis/handlers.go +++ b/rolling-shutter/keyperimpl/gnosis/handlers.go @@ -276,14 +276,14 @@ func (h *DecryptionKeysHandler) HandleMessage(ctx context.Context, msg p2pmsg.Me newTxPointer := int64(extra.TxPointer) + int64(len(keys.Keys)) - 1 log.Debug(). Uint64("eon", keys.Eon). - Uint64("block", extra.Slot). + Uint64("slot", extra.Slot). Uint64("tx-pointer-msg", extra.TxPointer). Int("num-keys", len(keys.Keys)). Int64("tx-pointer-updated", newTxPointer). Msg("updating tx pointer") err := gnosisDB.SetTxPointer(ctx, database.SetTxPointerParams{ Eon: int64(keys.Eon), - Block: int64(extra.Slot), + Slot: int64(extra.Slot), Value: newTxPointer, }) if err != nil { diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index 657f8830b..81f67c38f 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -29,9 +29,6 @@ import ( var ErrParseKeyperSet = errors.New("cannot parse KeyperSet") -// Maximum age of a tx pointer in blocks before it is considered outdated. -const maxTxPointerAge = 2 - type Keyper struct { core *keyper.KeyperCore config *Config diff --git a/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go b/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go index b5df1c496..e900f54c6 100644 --- a/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go +++ b/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go @@ -273,14 +273,14 @@ func (i *MessagingMiddleware) advanceTxPointer(ctx context.Context, msg *p2pmsg. newTxPointer := int64(extra.TxPointer) + int64(len(msg.Keys)) - 1 log.Debug(). Uint64("eon", msg.Eon). - Uint64("block", extra.Slot). + Uint64("slot", extra.Slot). Uint64("tx-pointer-msg", extra.TxPointer). Int("num-keys", len(msg.Keys)). Int64("tx-pointer-updated", newTxPointer). Msg("updating tx pointer") err := gnosisDB.SetTxPointer(ctx, database.SetTxPointerParams{ Eon: int64(msg.Eon), - Block: int64(extra.Slot), + Slot: int64(extra.Slot), Value: newTxPointer, }) if err != nil { diff --git a/rolling-shutter/keyperimpl/gnosis/newblocks.go b/rolling-shutter/keyperimpl/gnosis/newblock.go similarity index 100% rename from rolling-shutter/keyperimpl/gnosis/newblocks.go rename to rolling-shutter/keyperimpl/gnosis/newblock.go diff --git a/rolling-shutter/keyperimpl/gnosis/newslot.go b/rolling-shutter/keyperimpl/gnosis/newslot.go index e65d57c76..c1cb8315e 100644 --- a/rolling-shutter/keyperimpl/gnosis/newslot.go +++ b/rolling-shutter/keyperimpl/gnosis/newslot.go @@ -22,7 +22,18 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" ) +// Maximum age of a tx pointer in blocks before it is considered outdated. +const maxTxPointerAge = 2 + +var errZeroTxPointerAge = errors.New("tx pointer has age 0") + func (kpr *Keyper) processNewSlot(ctx context.Context, slot slotticker.Slot) error { + fmt.Println("") + fmt.Println("") + fmt.Println(slot.Number) + fmt.Println("") + fmt.Println("") + gnosisKeyperDB := gnosisdatabase.New(kpr.dbpool) syncedUntil, err := gnosisKeyperDB.GetTransactionSubmittedEventsSyncedUntil(ctx) if err != nil { @@ -35,93 +46,119 @@ func (kpr *Keyper) processNewSlot(ctx context.Context, slot slotticker.Slot) err // already been built, so we return an error. return errors.Errorf("processing slot %d for which a block has already been processed", slot.Number) } + nextBlock := syncedUntil.BlockNumber + 1 queries := obskeyper.New(kpr.dbpool) - keyperSet, err := queries.GetKeyperSet(ctx, syncedUntil.BlockNumber) + keyperSet, err := queries.GetKeyperSet(ctx, nextBlock) if err == pgx.ErrNoRows { log.Debug(). Uint64("slot", slot.Number). - Int64("block-number", syncedUntil.BlockNumber). + Int64("block-number", nextBlock). Msg("ignoring slot as no keyper set has been found for it") return nil } if err != nil { - return errors.Wrapf(err, "failed to query keyper set for block %d", syncedUntil.BlockNumber) + return errors.Wrapf(err, "failed to query keyper set for block %d", nextBlock) } - for _, m := range keyperSet.Keypers { - if m == shdb.EncodeAddress(kpr.config.GetAddress()) { - return kpr.triggerDecryption(ctx, slot, syncedUntil, &keyperSet) - } + if keyperSet.Contains(kpr.config.GetAddress()) { + return kpr.triggerDecryption(ctx, slot, nextBlock, &keyperSet) } - log.Debug().Uint64("slot", slot.Number).Msg("ignoring block as not part of keyper set") + log.Debug(). + Uint64("slot", slot.Number). + Int64("block-number", nextBlock). + Int64("keyper-set-index", keyperSet.KeyperConfigIndex). + Str("address", kpr.config.GetAddress().Hex()). + Msg("ignoring block as not part of keyper set") return nil } -func (kpr *Keyper) triggerDecryption( - ctx context.Context, - slot slotticker.Slot, - syncedUntil gnosisdatabase.TransactionSubmittedEventsSyncedUntil, - keyperSet *obskeyper.KeyperSet, -) error { - fmt.Println("") - fmt.Println("") - fmt.Println(slot.Number) - fmt.Println("") - fmt.Println("") +func (kpr *Keyper) getTxPointer(ctx context.Context, eon int64, slot int64, keyperConfigIndex int64) (int64, error) { gnosisKeyperDB := gnosisdatabase.New(kpr.dbpool) - coreKeyperDB := corekeyperdatabase.New(kpr.dbpool) - - eonStruct, err := coreKeyperDB.GetEonForBlockNumber(ctx, syncedUntil.BlockNumber) - if err != nil { - return errors.Wrapf(err, "failed to query eon for block number %d from db", syncedUntil.BlockNumber) - } - eon := eonStruct.Eon - - var txPointer int64 - var txPointerAge int64 + var txPointer, txPointerAge int64 txPointerDB, err := gnosisKeyperDB.GetTxPointer(ctx, eon) if err == pgx.ErrNoRows { + // The tx pointer is expected to be missing from the db if the eon has just started. In + // this case, we should initialize it to zero with an age of 1, ie decrypt starting with + // the first transaction. + // The tx pointer may also be missing if the keyper has been started late and no decryption + // key has been generated or received yet (receiving the keys message would update the + // pointer). In this case, the true age is unknown, as we only know the start block but + // not the start slot of the eon. However, we can ignore this edge case as it will be + // resolved automatically when the first keys message is received. If key generation + // continues to fail, eventually our tx pointer age will exceed the maximum value and we + // will start participating in the recovery process, albeit a bit late. + err := gnosisKeyperDB.SetTxPointer(ctx, gnosisdatabase.SetTxPointerParams{ + Eon: eon, + Slot: slot, + Value: 0, + }) + if err != nil { + return 0, errors.Wrap(err, "failed to initialize tx pointer") + } txPointer = 0 - txPointerAge = syncedUntil.BlockNumber - keyperSet.ActivationBlockNumber + 1 + txPointerAge = 1 } else if err != nil { - return errors.Wrap(err, "failed to query tx pointer from db") + return 0, errors.Wrap(err, "failed to query tx pointer from db") } else { - txPointerAge = syncedUntil.BlockNumber - txPointerDB.Block txPointer = txPointerDB.Value + txPointerAge = slot - txPointerDB.Slot } if txPointerAge == 0 { // A pointer of age 0 means we already received the pointer from a DecryptionKeys message // even though we haven't sent our shares yet. In that case, sending our shares is // unnecessary. + return 0, errZeroTxPointerAge + } + // If the tx pointer is outdated, the system has failed to generate decryption keys (or at + // least we haven't received them). This either means not enough keypers are online or they + // don't agree on the current value of the tx pointer. In order to recover, we choose the + // current length of the transaction queue as the new tx pointer, as this is a value + // everyone can agree on. + isOutdated := txPointerAge > maxTxPointerAge + if isOutdated { log.Warn(). - Uint64("slot", slot.Number). - Int64("block-number", syncedUntil.BlockNumber). - Int64("eon", eon). - Int64("tx-pointer", txPointer). - Int64("tx-pointer-age", txPointerAge). - Msg("ignoring new block as tx pointer age is 0") - return nil - } - if txPointerAge > maxTxPointerAge { - // If the tx pointer is outdated, the system has failed to generate decryption keys (or at - // least we haven't received them). This either means not enough keypers are online or they - // don't agree on the current value of the tx pointer. In order to recover, we choose the - // current length of the transaction queue as the new tx pointer, as this is a value - // everyone can agree on. - log.Warn(). - Uint64("slot", slot.Number). - Int64("block-number", syncedUntil.BlockNumber). + Int64("slot", slot). Int64("eon", eon). Int64("tx-pointer", txPointer). Int64("tx-pointer-age", txPointerAge). Msg("outdated tx pointer") - txPointer, err = gnosisKeyperDB.GetTransactionSubmittedEventCount(ctx, keyperSet.KeyperConfigIndex) + txPointer, err = gnosisKeyperDB.GetTransactionSubmittedEventCount(ctx, keyperConfigIndex) if err == pgx.ErrNoRows { txPointer = 0 } else if err != nil { - return errors.Wrap(err, "failed to query transaction submitted event count from db") + return 0, errors.Wrap(err, "failed to query transaction submitted event count from db") } } + return txPointer, nil +} + +func (kpr *Keyper) triggerDecryption( + ctx context.Context, + slot slotticker.Slot, + nextBlock int64, + keyperSet *obskeyper.KeyperSet, +) error { + gnosisKeyperDB := gnosisdatabase.New(kpr.dbpool) + coreKeyperDB := corekeyperdatabase.New(kpr.dbpool) + + eonStruct, err := coreKeyperDB.GetEonForBlockNumber(ctx, nextBlock) + if err != nil { + return errors.Wrapf(err, "failed to query eon for block number %d from db", nextBlock) + } + eon := eonStruct.Eon + + txPointer, err := kpr.getTxPointer(ctx, eon, int64(slot.Number), keyperSet.KeyperConfigIndex) + if err == errZeroTxPointerAge { + log.Warn(). + Uint64("slot", slot.Number). + Int64("block-number", nextBlock). + Int64("eon", eon). + Int64("tx-pointer", txPointer). + Msg("ignoring new block as tx pointer age is 0") + return nil + } else if err != nil { + return err + } identityPreimages, err := kpr.getDecryptionIdentityPreimages(ctx, slot, keyperSet.KeyperConfigIndex, txPointer) if err != nil { @@ -137,16 +174,15 @@ func (kpr *Keyper) triggerDecryption( return errors.Wrap(err, "failed to insert published tx pointer into db") } trigger := epochkghandler.DecryptionTrigger{ - BlockNumber: uint64(syncedUntil.BlockNumber), + BlockNumber: uint64(nextBlock), IdentityPreimages: identityPreimages, } event := broker.NewEvent(&trigger) log.Debug(). Uint64("slot", slot.Number). - Uint64("block-number", uint64(syncedUntil.BlockNumber)). + Uint64("block-number", uint64(nextBlock)). Int("num-identities", len(trigger.IdentityPreimages)). Int64("tx-pointer", txPointer). - Int64("tx-pointer-age", txPointerAge). Msg("sending decryption trigger") kpr.decryptionTriggerChannel <- event From aa66bfcff604bba3f0f831b4fd436ea8d0c0997c Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Thu, 4 Apr 2024 13:46:59 +0200 Subject: [PATCH 57/91] Simplify message validation function The linter complained about high complexity, so I extracted some parts of it into smaller functions. --- .../chainobserver/db/keyper/extend.go | 23 +++++++ .../chainobserver/db/keyper/extend_test.go | 61 +++++++++++++++++++ rolling-shutter/keyperimpl/gnosis/handlers.go | 46 ++++++++------ 3 files changed, 110 insertions(+), 20 deletions(-) create mode 100644 rolling-shutter/chainobserver/db/keyper/extend_test.go diff --git a/rolling-shutter/chainobserver/db/keyper/extend.go b/rolling-shutter/chainobserver/db/keyper/extend.go index f37c359ef..990e00c09 100644 --- a/rolling-shutter/chainobserver/db/keyper/extend.go +++ b/rolling-shutter/chainobserver/db/keyper/extend.go @@ -2,10 +2,13 @@ package database import ( "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" ) +// Contains checks if the given address is present in the KeyperSet. +// It returns true if the address is found, otherwise false. func (s *KeyperSet) Contains(address common.Address) bool { encodedAddress := shdb.EncodeAddress(address) for _, m := range s.Keypers { @@ -15,3 +18,23 @@ func (s *KeyperSet) Contains(address common.Address) bool { } return false } + +// GetSubset returns a subset of addresses from the KeyperSet based on the given indices. +// The return value is ordered according to the order of the given indices. If indices contains +// duplicates, the return value will do so as well. If at least one of the given indices is out of +// range, an error is returned. +func (s *KeyperSet) GetSubset(indices []uint64) ([]common.Address, error) { + subset := []common.Address{} + for _, i := range indices { + if i >= uint64(len(s.Keypers)) { + return nil, errors.Errorf("keyper index %d out of range (size %d)", i, len(s.Keypers)) + } + addressStr := s.Keypers[i] + address, err := shdb.DecodeAddress(addressStr) + if err != nil { + return nil, err + } + subset = append(subset, address) + } + return subset, nil +} diff --git a/rolling-shutter/chainobserver/db/keyper/extend_test.go b/rolling-shutter/chainobserver/db/keyper/extend_test.go new file mode 100644 index 000000000..a6f97dacc --- /dev/null +++ b/rolling-shutter/chainobserver/db/keyper/extend_test.go @@ -0,0 +1,61 @@ +package database + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "gotest.tools/v3/assert" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" +) + +func makeTestKeyperSet() KeyperSet { + return KeyperSet{ + KeyperConfigIndex: 0, + ActivationBlockNumber: 0, + Keypers: []string{ + shdb.EncodeAddress(common.HexToAddress("0x0000000000000000000000000000000000000000")), + shdb.EncodeAddress(common.HexToAddress("0x5555555555555555555555555555555555555555")), + shdb.EncodeAddress(common.HexToAddress("0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa")), + }, + Threshold: 2, + } +} + +func TestKeyperSetContains(t *testing.T) { + keyperSet := makeTestKeyperSet() + addresses, err := shdb.DecodeAddresses(keyperSet.Keypers) + assert.NilError(t, err) + + for _, address := range addresses { + assert.Assert(t, keyperSet.Contains(address)) + } + assert.Assert(t, !keyperSet.Contains(common.HexToAddress("0xffffffffffffffffffffffffffffffffffffffff"))) +} + +func TestKeyperSetSubset(t *testing.T) { + keyperSet := makeTestKeyperSet() + testCases := []struct { + indices []uint64 + valid bool + }{ + {indices: []uint64{0, 1, 2}, valid: true}, + {indices: []uint64{}, valid: true}, + {indices: []uint64{1, 0}, valid: true}, + {indices: []uint64{0, 0}, valid: true}, + {indices: []uint64{0, 0, 0, 0}, valid: true}, + {indices: []uint64{3}, valid: false}, + } + + for _, tc := range testCases { + subset, err := keyperSet.GetSubset(tc.indices) + if tc.valid { + assert.Assert(t, len(subset) == len(tc.indices)) + for _, i := range tc.indices { + assert.Assert(t, shdb.EncodeAddress(subset[i]) == keyperSet.Keypers[tc.indices[i]]) + } + } else { + assert.Assert(t, err != nil) + } + } +} diff --git a/rolling-shutter/keyperimpl/gnosis/handlers.go b/rolling-shutter/keyperimpl/gnosis/handlers.go index e35404e70..64b31b777 100644 --- a/rolling-shutter/keyperimpl/gnosis/handlers.go +++ b/rolling-shutter/keyperimpl/gnosis/handlers.go @@ -4,7 +4,6 @@ import ( "context" "math" - "github.com/ethereum/go-ethereum/common" "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/pgxpool" pubsub "github.com/libp2p/go-libp2p-pubsub" @@ -186,6 +185,24 @@ func (h *DecryptionKeysHandler) MessagePrototypes() []p2pmsg.Message { return []p2pmsg.Message{&p2pmsg.DecryptionKeys{}} } +func validateSignerIndices(extra *p2pmsg.DecryptionKeys_Gnosis, n int) (pubsub.ValidationResult, error) { + for i, signerIndex := range extra.Gnosis.SignerIndices { + if i >= 1 { + prevSignerIndex := extra.Gnosis.SignerIndices[i-1] + if signerIndex == prevSignerIndex { + return pubsub.ValidationReject, errors.New("duplicate signer index found") + } + if signerIndex < prevSignerIndex { + return pubsub.ValidationReject, errors.New("signer indices not ordered") + } + } + if signerIndex >= uint64(n) { + return pubsub.ValidationReject, errors.New("signer index out of range") + } + } + return pubsub.ValidationAccept, nil +} + func (h *DecryptionKeysHandler) ValidateMessage(ctx context.Context, msg p2pmsg.Message) (pubsub.ValidationResult, error) { keys := msg.(*p2pmsg.DecryptionKeys) extra, ok := keys.Extra.(*p2pmsg.DecryptionKeys_Gnosis) @@ -220,25 +237,14 @@ func (h *DecryptionKeysHandler) ValidateMessage(ctx context.Context, msg p2pmsg. if int32(len(extra.Gnosis.SignerIndices)) != keyperSet.Threshold { return pubsub.ValidationReject, errors.Errorf("expected %d signers, got %d", keyperSet.Threshold, len(extra.Gnosis.SignerIndices)) } - signers := []common.Address{} - for i, signerIndex := range extra.Gnosis.SignerIndices { - if i >= 1 { - prevSignerIndex := extra.Gnosis.SignerIndices[i-1] - if signerIndex == prevSignerIndex { - return pubsub.ValidationReject, errors.New("duplicate signer index found") - } - if signerIndex < prevSignerIndex { - return pubsub.ValidationReject, errors.New("signer indices not ordered") - } - } - if signerIndex >= uint64(len(keyperSet.Keypers)) { - return pubsub.ValidationReject, errors.New("signer index out of range") - } - signer, err := shdb.DecodeAddress(keyperSet.Keypers[signerIndex]) - if err != nil { - return pubsub.ValidationReject, errors.Wrap(err, "failed to decode signer address") - } - signers = append(signers, signer) + + res, err := validateSignerIndices(extra, len(keyperSet.Keypers)) + if res != pubsub.ValidationAccept { + return res, err + } + signers, err := keyperSet.GetSubset(extra.Gnosis.SignerIndices) + if err != nil { + return pubsub.ValidationReject, err } identityPreimages := []identitypreimage.IdentityPreimage{} From 14961a1460c4adc615590385e58ca2c4f47f9f9a Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Thu, 4 Apr 2024 15:17:48 +0200 Subject: [PATCH 58/91] Add Gnosis config section --- rolling-shutter/gnosiskeyperwatcher/blocks.go | 2 +- rolling-shutter/keyperimpl/gnosis/config.go | 132 ++++++++++++++---- rolling-shutter/keyperimpl/gnosis/keyper.go | 24 ++-- .../keyperimpl/gnosis/messagingmiddleware.go | 2 +- rolling-shutter/keyperimpl/gnosis/newslot.go | 4 +- 5 files changed, 120 insertions(+), 44 deletions(-) diff --git a/rolling-shutter/gnosiskeyperwatcher/blocks.go b/rolling-shutter/gnosiskeyperwatcher/blocks.go index eeed155a7..66ca62a41 100644 --- a/rolling-shutter/gnosiskeyperwatcher/blocks.go +++ b/rolling-shutter/gnosiskeyperwatcher/blocks.go @@ -31,7 +31,7 @@ func NewBlocksWatcher(config *keyper.Config, blocksChannel chan *BlockReceivedEv func (w *BlocksWatcher) Start(ctx context.Context, runner service.Runner) error { runner.Go(func() error { - ethClient, err := ethclient.Dial(w.config.Gnosis.EthereumURL) + ethClient, err := ethclient.Dial(w.config.Gnosis.Node.EthereumURL) if err != nil { return err } diff --git a/rolling-shutter/keyperimpl/gnosis/config.go b/rolling-shutter/keyperimpl/gnosis/config.go index e959d8edf..e47b98d1a 100644 --- a/rolling-shutter/keyperimpl/gnosis/config.go +++ b/rolling-shutter/keyperimpl/gnosis/config.go @@ -18,7 +18,11 @@ const ( maxChainAge = 100 * 365 * 24 * 60 * 60 ) -var _ configuration.Config = &Config{} +var ( + _ configuration.Config = &Config{} + _ configuration.Config = &GnosisConfig{} + _ configuration.Config = &GnosisContractsConfig{} +) func NewConfig() *Config { c := &Config{} @@ -28,7 +32,7 @@ func NewConfig() *Config { func (c *Config) Init() { c.P2P = p2p.NewConfig() - c.Gnosis = configuration.NewEthnodeConfig() + c.Gnosis = NewGnosisConfig() c.Shuttermint = kprconfig.NewShuttermintConfig() c.Metrics = metricsserver.NewConfig() } @@ -40,32 +44,19 @@ type Config struct { HTTPEnabled bool HTTPListenAddress string + Gnosis *GnosisConfig P2P *p2p.Config - Gnosis *configuration.EthnodeConfig Shuttermint *kprconfig.ShuttermintConfig Metrics *metricsserver.MetricsConfig - - // TODO: put these in a child config - GnosisContracts *GnosisContracts `shconfig:",required"` - EncryptedGasLimit uint64 `shconfig:",required"` - MinGasPerTransaction uint64 `shconfig:",required"` - SecondsPerSlot uint64 `shconfig:",required"` - GenesisSlotTimestamp uint64 `shconfig:",required"` -} - -type GnosisContracts struct { - KeyperSetManager common.Address `shconfig:",required"` - KeyBroadcastContract common.Address `shconfig:",required"` - Sequencer common.Address `shconfig:",required"` } func (c *Config) Validate() error { - if c.SecondsPerSlot > maxSecondsPerSlot { - return errors.Errorf("seconds per slot is too big (%d > %d)", c.SecondsPerSlot, maxSecondsPerSlot) + if c.Gnosis.SecondsPerSlot > maxSecondsPerSlot { + return errors.Errorf("seconds per slot is too big (%d > %d)", c.Gnosis.SecondsPerSlot, maxSecondsPerSlot) } maxGenesisSlotTime := uint64(math.MaxInt64 - maxChainAge) - if c.GenesisSlotTimestamp > maxGenesisSlotTime { - return errors.Errorf("genesis slot timestamp is too big (%d > %d)", c.GenesisSlotTimestamp, maxGenesisSlotTime) + if c.Gnosis.GenesisSlotTimestamp > maxGenesisSlotTime { + return errors.Errorf("genesis slot timestamp is too big (%d > %d)", c.Gnosis.GenesisSlotTimestamp, maxGenesisSlotTime) } return nil } @@ -77,13 +68,8 @@ func (c *Config) Name() string { func (c *Config) SetDefaultValues() error { c.HTTPEnabled = false c.HTTPListenAddress = ":3000" - c.GnosisContracts = &GnosisContracts{ - KeyperSetManager: common.Address{}, - KeyBroadcastContract: common.Address{}, - Sequencer: common.Address{}, - } - c.EncryptedGasLimit = 1_000_000 - c.MinGasPerTransaction = 21_000 + c.Gnosis.EncryptedGasLimit = 1_000_000 + c.Gnosis.MinGasPerTransaction = 21_000 return nil } @@ -103,5 +89,95 @@ func (c Config) TOMLWriteHeader(_ io.Writer) (int, error) { } func (c *Config) GetAddress() common.Address { - return c.Gnosis.PrivateKey.EthereumAddress() + return c.Gnosis.Node.PrivateKey.EthereumAddress() +} + +type GnosisConfig struct { + Node *configuration.EthnodeConfig `shconfig:",required"` + Contracts *GnosisContractsConfig `shconfig:",required"` + EncryptedGasLimit uint64 `shconfig:",required"` + MinGasPerTransaction uint64 `shconfig:",required"` + SecondsPerSlot uint64 `shconfig:",required"` + GenesisSlotTimestamp uint64 `shconfig:",required"` +} + +func NewGnosisConfig() *GnosisConfig { + c := &GnosisConfig{ + Node: configuration.NewEthnodeConfig(), + Contracts: NewGnosisContractsConfig(), + EncryptedGasLimit: 0, + MinGasPerTransaction: 0, + SecondsPerSlot: 0, + GenesisSlotTimestamp: 0, + } + c.Init() + return c +} + +func (c *GnosisConfig) Init() { + c.Node.Init() + c.Contracts.Init() +} + +func (c *GnosisConfig) Name() string { + return "gnosis" +} + +func (c *GnosisConfig) Validate() error { + if c.SecondsPerSlot == 0 { + return errors.Errorf("seconds per slot must not be zero") + } + return nil +} + +func (c *GnosisConfig) SetDefaultValues() error { + return nil +} + +func (c *GnosisConfig) SetExampleValues() error { + c.EncryptedGasLimit = 1_000_000 + c.MinGasPerTransaction = 21_000 + c.SecondsPerSlot = 5 + c.GenesisSlotTimestamp = 1665410700 + return nil +} + +func (c *GnosisConfig) TOMLWriteHeader(_ io.Writer) (int, error) { + return 0, nil +} + +type GnosisContractsConfig struct { + KeyperSetManager common.Address `shconfig:",required"` + KeyBroadcastContract common.Address `shconfig:",required"` + Sequencer common.Address `shconfig:",required"` +} + +func NewGnosisContractsConfig() *GnosisContractsConfig { + return &GnosisContractsConfig{ + KeyperSetManager: common.Address{}, + KeyBroadcastContract: common.Address{}, + Sequencer: common.Address{}, + } +} + +func (c *GnosisContractsConfig) Init() {} + +func (c *GnosisContractsConfig) Name() string { + return "gnosiscontracts" +} + +func (c *GnosisContractsConfig) Validate() error { + return nil +} + +func (c *GnosisContractsConfig) SetDefaultValues() error { + return nil +} + +func (c *GnosisContractsConfig) SetExampleValues() error { + return nil +} + +func (c *GnosisContractsConfig) TOMLWriteHeader(_ io.Writer) (int, error) { + return 0, nil } diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index 81f67c38f..896e6d865 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -65,8 +65,8 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { runner.Defer(func() { close(kpr.decryptionTriggerChannel) }) kpr.slotTicker = slotticker.NewSlotTicker( - time.Duration(kpr.config.SecondsPerSlot*uint64(time.Second)), - time.Unix(int64(kpr.config.GenesisSlotTimestamp), 0), + time.Duration(kpr.config.Gnosis.SecondsPerSlot*uint64(time.Second)), + time.Unix(int64(kpr.config.Gnosis.GenesisSlotTimestamp), 0), ) kpr.dbpool, err = db.Connect(ctx, runner, kpr.config.DatabaseURL, database.Definition.Name()) @@ -89,7 +89,7 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { HTTPEnabled: kpr.config.HTTPEnabled, HTTPListenAddress: kpr.config.HTTPListenAddress, P2P: kpr.config.P2P, - Ethereum: kpr.config.Gnosis, + Ethereum: kpr.config.Gnosis.Node, Shuttermint: kpr.config.Shuttermint, Metrics: kpr.config.Metrics, }, @@ -105,12 +105,12 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { kpr.chainSyncClient, err = chainsync.NewClient( ctx, - chainsync.WithClientURL(kpr.config.Gnosis.EthereumURL), - chainsync.WithKeyperSetManager(kpr.config.GnosisContracts.KeyperSetManager), - chainsync.WithKeyBroadcastContract(kpr.config.GnosisContracts.KeyBroadcastContract), + chainsync.WithClientURL(kpr.config.Gnosis.Node.EthereumURL), + chainsync.WithKeyperSetManager(kpr.config.Gnosis.Contracts.KeyperSetManager), + chainsync.WithKeyBroadcastContract(kpr.config.Gnosis.Contracts.KeyBroadcastContract), chainsync.WithSyncNewBlock(kpr.channelNewBlock), chainsync.WithSyncNewKeyperSet(kpr.channelNewKeyperSet), - chainsync.WithPrivateKey(kpr.config.Gnosis.PrivateKey.Key), + chainsync.WithPrivateKey(kpr.config.Gnosis.Node.PrivateKey.Key), chainsync.WithLogger(gethLog.NewLogger(slog.Default().Handler())), ) if err != nil { @@ -164,13 +164,13 @@ func (kpr *Keyper) ensureSequencerSyncing(ctx context.Context, eon uint64) error if kpr.sequencerSyncer == nil { log.Info(). Uint64("eon", eon). - Str("contract-address", kpr.config.GnosisContracts.KeyperSetManager.Hex()). + Str("contract-address", kpr.config.Gnosis.Contracts.KeyperSetManager.Hex()). Msg("initializing sequencer syncer") - client, err := ethclient.DialContext(ctx, kpr.config.Gnosis.ContractsURL) + client, err := ethclient.DialContext(ctx, kpr.config.Gnosis.Node.ContractsURL) if err != nil { return err } - contract, err := sequencerBindings.NewSequencer(kpr.config.GnosisContracts.Sequencer, client) + contract, err := sequencerBindings.NewSequencer(kpr.config.Gnosis.Contracts.Sequencer, client) if err != nil { return err } @@ -178,8 +178,8 @@ func (kpr *Keyper) ensureSequencerSyncing(ctx context.Context, eon uint64) error Contract: contract, DBPool: kpr.dbpool, StartEon: eon, - GenesisSlotTimestamp: kpr.config.GenesisSlotTimestamp, - SecondsPerSlot: kpr.config.SecondsPerSlot, + GenesisSlotTimestamp: kpr.config.Gnosis.GenesisSlotTimestamp, + SecondsPerSlot: kpr.config.Gnosis.SecondsPerSlot, } // TODO: perform an initial sync without blocking and/or set start block diff --git a/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go b/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go index e900f54c6..239330ebf 100644 --- a/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go +++ b/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go @@ -146,7 +146,7 @@ func (i *MessagingMiddleware) interceptDecryptionKeyShares( TxPointer: uint64(currentDecryptionTrigger.TxPointer), IdentityPreimages: identityPreimages, } - signature, err := ComputeSlotDecryptionSignature(&slotDecryptionSignatureData, i.config.Gnosis.PrivateKey.Key) + signature, err := ComputeSlotDecryptionSignature(&slotDecryptionSignatureData, i.config.Gnosis.Node.PrivateKey.Key) if err != nil { return nil, errors.Wrapf(err, "failed to compute slot decryption signature") } diff --git a/rolling-shutter/keyperimpl/gnosis/newslot.go b/rolling-shutter/keyperimpl/gnosis/newslot.go index c1cb8315e..50b95c13e 100644 --- a/rolling-shutter/keyperimpl/gnosis/newslot.go +++ b/rolling-shutter/keyperimpl/gnosis/newslot.go @@ -195,7 +195,7 @@ func (kpr *Keyper) getDecryptionIdentityPreimages( identityPreimages := []identitypreimage.IdentityPreimage{} queries := gnosisdatabase.New(kpr.dbpool) - limitUint64 := kpr.config.EncryptedGasLimit/kpr.config.MinGasPerTransaction + 1 + limitUint64 := kpr.config.Gnosis.EncryptedGasLimit/kpr.config.Gnosis.MinGasPerTransaction + 1 if limitUint64 > math.MaxInt32 { return identityPreimages, errors.New("gas limit too big") } @@ -216,7 +216,7 @@ func (kpr *Keyper) getDecryptionIdentityPreimages( gas := uint64(0) for _, event := range events { gas += uint64(event.GasLimit) - if gas > kpr.config.EncryptedGasLimit { + if gas > kpr.config.Gnosis.EncryptedGasLimit { break } identityPreimage, err := transactionSubmittedEventToIdentityPreimage(event) From a50cc4ffa30e539f72ffc2a96f3176b0b503a31c Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Sun, 7 Apr 2024 21:51:56 +0200 Subject: [PATCH 59/91] Publish eon keys via publisher contract --- .../chainobserver/db/keyper/extend.go | 11 ++ .../chainobserver/db/keyper/extend_test.go | 14 ++ .../eonkeypublisher/eonkeypublisher.go | 161 ++++++++++++++++++ rolling-shutter/go.mod | 2 +- rolling-shutter/go.sum | 4 +- rolling-shutter/keyperimpl/gnosis/config.go | 2 + rolling-shutter/keyperimpl/gnosis/keyper.go | 19 ++- .../keyperimpl/gnosis/neweonpublickey.go | 9 +- 8 files changed, 211 insertions(+), 11 deletions(-) create mode 100644 rolling-shutter/eonkeypublisher/eonkeypublisher.go diff --git a/rolling-shutter/chainobserver/db/keyper/extend.go b/rolling-shutter/chainobserver/db/keyper/extend.go index 990e00c09..a3649898d 100644 --- a/rolling-shutter/chainobserver/db/keyper/extend.go +++ b/rolling-shutter/chainobserver/db/keyper/extend.go @@ -7,6 +7,17 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" ) +// GetIndex returns the index of the given address in the KeyperSet. +func (s *KeyperSet) GetIndex(address common.Address) (uint64, error) { + encodedAddress := shdb.EncodeAddress(address) + for i, m := range s.Keypers { + if m == encodedAddress { + return uint64(i), nil + } + } + return 0, errors.Errorf("keyper %s not found", address.String()) +} + // Contains checks if the given address is present in the KeyperSet. // It returns true if the address is found, otherwise false. func (s *KeyperSet) Contains(address common.Address) bool { diff --git a/rolling-shutter/chainobserver/db/keyper/extend_test.go b/rolling-shutter/chainobserver/db/keyper/extend_test.go index a6f97dacc..526820551 100644 --- a/rolling-shutter/chainobserver/db/keyper/extend_test.go +++ b/rolling-shutter/chainobserver/db/keyper/extend_test.go @@ -22,6 +22,20 @@ func makeTestKeyperSet() KeyperSet { } } +func TestKeyperSetGetIndex(t *testing.T) { + keyperSet := makeTestKeyperSet() + addresses, err := shdb.DecodeAddresses(keyperSet.Keypers) + assert.NilError(t, err) + + for i, address := range addresses { + index, err := keyperSet.GetIndex(address) + assert.NilError(t, err) + assert.Equal(t, uint64(i), index) + } + _, err = keyperSet.GetIndex(common.HexToAddress("0xffffffffffffffffffffffffffffffffffffffff")) + assert.ErrorContains(t, err, "keyper 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF not found") +} + func TestKeyperSetContains(t *testing.T) { keyperSet := makeTestKeyperSet() addresses, err := shdb.DecodeAddresses(keyperSet.Keypers) diff --git a/rolling-shutter/eonkeypublisher/eonkeypublisher.go b/rolling-shutter/eonkeypublisher/eonkeypublisher.go new file mode 100644 index 000000000..93cee0ce8 --- /dev/null +++ b/rolling-shutter/eonkeypublisher/eonkeypublisher.go @@ -0,0 +1,161 @@ +package eonkeypublisher + +import ( + "context" + "crypto/ecdsa" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/jackc/pgx/v4/pgxpool" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + "github.com/shutter-network/shop-contracts/bindings" + + obskeyperdb "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/retry" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" +) + +const ( + eonKeyChannelSize = 32 + retryInterval = time.Second * 12 +) + +// EonKeyPublisher is a service that publishes eon keys via a eon key publisher contract. +type EonKeyPublisher struct { + dbpool *pgxpool.Pool + client *ethclient.Client + contract *bindings.EonKeyPublish + privateKey *ecdsa.PrivateKey + + keys chan keyper.EonPublicKey +} + +func NewEonKeyPublisher( + dbpool *pgxpool.Pool, + client *ethclient.Client, + eonKeyPublishAddress common.Address, + privateKey *ecdsa.PrivateKey, +) (*EonKeyPublisher, error) { + contract, err := bindings.NewEonKeyPublish(eonKeyPublishAddress, client) + if err != nil { + return nil, errors.Wrap(err, "failed to instantiate eon key publisher contract") + } + return &EonKeyPublisher{ + dbpool: dbpool, + client: client, + contract: contract, + privateKey: privateKey, + + keys: make(chan keyper.EonPublicKey, eonKeyChannelSize), + }, nil +} + +func (p *EonKeyPublisher) Start(ctx context.Context, runner service.Runner) error { //nolint: unparam + runner.Go(func() error { + for { + select { + case key := <-p.keys: + p.publish(ctx, key) + case <-ctx.Done(): + return ctx.Err() + } + } + }) + return nil +} + +// Publish schedules a eon key to be published. +func (p *EonKeyPublisher) Publish(key keyper.EonPublicKey) { + p.keys <- key +} + +func (p *EonKeyPublisher) publish(ctx context.Context, key keyper.EonPublicKey) { + _, err := retry.FunctionCall[struct{}](ctx, func(ctx context.Context) (struct{}, error) { + return struct{}{}, p.tryPublish(ctx, key) + }, retry.Interval(retryInterval)) + if err != nil { + log.Error(). + Err(err). + Uint64("keyper-set-index", key.KeyperConfigIndex). + Hex("key", key.PublicKey). + Msg("failed to publish eon key") + } +} + +func (p *EonKeyPublisher) tryPublish(ctx context.Context, key keyper.EonPublicKey) error { + db := obskeyperdb.New(p.dbpool) + keyperSet, err := db.GetKeyperSetByKeyperConfigIndex(ctx, int64(key.Eon)) + if err != nil { + return errors.Wrapf(err, "failed to query keyper set %d by index from db", key.KeyperConfigIndex) + } + keyperAddress := ethcrypto.PubkeyToAddress(p.privateKey.PublicKey) + keyperIndex, err := keyperSet.GetIndex(keyperAddress) + if err != nil { + log.Info(). + Uint64("keyper-set-index", key.KeyperConfigIndex). + Str("keyper-address", keyperAddress.Hex()). + Msg("not publishing eon key as keyper is not part of corresponding keyper set") + return nil + } + + hasAlreadyVoted, err := p.contract.HasKeyperVoted(&bind.CallOpts{}, keyperAddress) + if err != nil { + return errors.Wrap(err, "failed to query eon key publisher contract if keyper has already voted") + } + if hasAlreadyVoted { + log.Info(). + Uint64("keyper-set-index", key.KeyperConfigIndex). + Str("keyper-address", keyperAddress.Hex()). + Msg("not publishing eon key as keyper has already voted") + return nil + } + isAlreadyConfirmed, err := p.contract.EonKeyConfirmed(&bind.CallOpts{}, key.PublicKey) + if err != nil { + return errors.Wrap(err, "failed to query eon key publisher contract if eon key is confirmed") + } + if isAlreadyConfirmed { + log.Info(). + Uint64("keyper-set-index", key.KeyperConfigIndex). + Hex("key", key.PublicKey). + Msg("not publishing eon key as it is already confirmed") + return nil + } + + chainID, err := p.client.ChainID(ctx) + if err != nil { + return errors.Wrap(err, "failed to get chain ID") + } + opts, err := bind.NewKeyedTransactorWithChainID(p.privateKey, chainID) + if err != nil { + return errors.Wrap(err, "failed to construct tx opts") + } + tx, err := p.contract.PublishEonKey(opts, key.PublicKey, keyperIndex) + if err != nil { + return errors.Wrap(err, "failed to send publish eon key tx") + } + log.Info(). + Uint64("keyper-set-index", key.KeyperConfigIndex). + Hex("key", key.PublicKey). + Hex("tx-hash", tx.Hash().Bytes()). + Msg("eon key publish tx sent") + receipt, err := bind.WaitMined(ctx, p.client, tx) + if err != nil { + log.Error().Err(err).Msg("error waiting for eon key publish tx to be mined") + return err + } + if receipt.Status != types.ReceiptStatusSuccessful { + log.Error(). + Hex("tx-hash", tx.Hash().Bytes()). + Interface("receipt", receipt). + Msg("eon key publish tx failed") + return errors.New("eon key publish tx failed") + } + log.Info().Msg("successfully published eon key") + return nil +} diff --git a/rolling-shutter/go.mod b/rolling-shutter/go.mod index 88a03e948..b3722234c 100644 --- a/rolling-shutter/go.mod +++ b/rolling-shutter/go.mod @@ -30,7 +30,7 @@ require ( github.com/prometheus/client_golang v1.17.0 github.com/rs/zerolog v1.28.0 github.com/shutter-network/gnosh-contracts v0.2.0 - github.com/shutter-network/shop-contracts v0.0.0-20231220085304-80b8977d0bca + github.com/shutter-network/shop-contracts v0.0.0-20240407151512-08ef5d8355b6 github.com/shutter-network/shutter/shlib v0.1.13 github.com/shutter-network/txtypes v0.1.0 github.com/spf13/afero v1.8.2 diff --git a/rolling-shutter/go.sum b/rolling-shutter/go.sum index 14532a1e8..2c5dc2312 100644 --- a/rolling-shutter/go.sum +++ b/rolling-shutter/go.sum @@ -833,8 +833,8 @@ github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYED github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/shutter-network/gnosh-contracts v0.2.0 h1:qH3gAhlh5VZzvJcbi044lxFWQ+MAR9GevKKUirWxSlU= github.com/shutter-network/gnosh-contracts v0.2.0/go.mod h1:QB0d64ybbVFKMrLjrc1tldri87KNjTmKQjhk9jaso2E= -github.com/shutter-network/shop-contracts v0.0.0-20231220085304-80b8977d0bca h1:05Ghqw3FqH/UFuYIzc7z6GJyHk3HxAqY3iuY4L3x4Ow= -github.com/shutter-network/shop-contracts v0.0.0-20231220085304-80b8977d0bca/go.mod h1:LEWXLRruvxq9fe2oKtJI3xfzbauhfWTjOczHN61RU+4= +github.com/shutter-network/shop-contracts v0.0.0-20240407151512-08ef5d8355b6 h1:m6Ti1/IH+GBTtGqyAX3xbh+ruUKvC+m+/uzYDUa+JDQ= +github.com/shutter-network/shop-contracts v0.0.0-20240407151512-08ef5d8355b6/go.mod h1:LEWXLRruvxq9fe2oKtJI3xfzbauhfWTjOczHN61RU+4= github.com/shutter-network/shutter/shlib v0.1.13 h1:9YloDJBdhFAKm2GMg4gBNeaJ+Mw9Qzeh5Kz9A2ayp1E= github.com/shutter-network/shutter/shlib v0.1.13/go.mod h1:RlYNZjx+pfKAi0arH+jfdlxG4kQ75UFzDfVjgCVYaUw= github.com/shutter-network/txtypes v0.1.0 h1:QqdiiiB9AiBCSJ/ke6z1ZoDGfu2+1Lgpz5vHzVN4FKc= diff --git a/rolling-shutter/keyperimpl/gnosis/config.go b/rolling-shutter/keyperimpl/gnosis/config.go index e47b98d1a..ac7c01777 100644 --- a/rolling-shutter/keyperimpl/gnosis/config.go +++ b/rolling-shutter/keyperimpl/gnosis/config.go @@ -149,6 +149,7 @@ func (c *GnosisConfig) TOMLWriteHeader(_ io.Writer) (int, error) { type GnosisContractsConfig struct { KeyperSetManager common.Address `shconfig:",required"` KeyBroadcastContract common.Address `shconfig:",required"` + EonKeyPublish common.Address `shconfig:",required"` Sequencer common.Address `shconfig:",required"` } @@ -156,6 +157,7 @@ func NewGnosisContractsConfig() *GnosisContractsConfig { return &GnosisContractsConfig{ KeyperSetManager: common.Address{}, KeyBroadcastContract: common.Address{}, + EonKeyPublish: common.Address{}, Sequencer: common.Address{}, } } diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index 896e6d865..803cdf193 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -14,6 +14,7 @@ import ( "golang.org/x/exp/slog" obskeyper "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" + "github.com/shutter-network/rolling-shutter/rolling-shutter/eonkeypublisher" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/epochkghandler" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/kprconfig" @@ -33,8 +34,10 @@ type Keyper struct { core *keyper.KeyperCore config *Config dbpool *pgxpool.Pool + client *ethclient.Client chainSyncClient *chainsync.Client sequencerSyncer *SequencerSyncer + eonKeyPublisher *eonkeypublisher.EonKeyPublisher // input events newBlocks chan *syncevent.LatestBlock @@ -117,13 +120,27 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { return err } + eonKeyPublisherClient, err := ethclient.DialContext(ctx, kpr.config.Gnosis.Node.EthereumURL) + if err != nil { + return errors.Wrapf(err, "failed to dial ethereum node at %s", kpr.config.Gnosis.Node.EthereumURL) + } + kpr.eonKeyPublisher, err = eonkeypublisher.NewEonKeyPublisher( + kpr.dbpool, + eonKeyPublisherClient, + kpr.config.Gnosis.Contracts.EonKeyPublish, + kpr.config.Gnosis.Node.PrivateKey.Key, + ) + if err != nil { + return errors.Wrap(err, "failed to initialize eon key publisher") + } + err = kpr.initSequencerSyncer(ctx) if err != nil { return err } runner.Go(func() error { return kpr.processInputs(ctx) }) - return runner.StartService(kpr.core, kpr.chainSyncClient, kpr.slotTicker) + return runner.StartService(kpr.core, kpr.chainSyncClient, kpr.slotTicker, kpr.eonKeyPublisher) } // initSequencerSycer initializes the sequencer syncer if the keyper is known to be a member of a diff --git a/rolling-shutter/keyperimpl/gnosis/neweonpublickey.go b/rolling-shutter/keyperimpl/gnosis/neweonpublickey.go index d473c3a00..34b4250ab 100644 --- a/rolling-shutter/keyperimpl/gnosis/neweonpublickey.go +++ b/rolling-shutter/keyperimpl/gnosis/neweonpublickey.go @@ -3,15 +3,10 @@ package gnosis import ( "context" - "github.com/rs/zerolog/log" - "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" ) -func (kpr *Keyper) processNewEonPublicKey(_ context.Context, key keyper.EonPublicKey) error { //nolint:unparam - log.Info(). - Uint64("eon", key.Eon). - Uint64("activation-block", key.ActivationBlock). - Msg("new eon pk") +func (kpr *Keyper) processNewEonPublicKey(ctx context.Context, key keyper.EonPublicKey) error { + kpr.eonKeyPublisher.Publish(key) return nil } From 21311d0be3abd0da6af71543779d3af570a468fc Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Mon, 8 Apr 2024 22:36:19 +0200 Subject: [PATCH 60/91] Publish eon keys from db on startup Before, only newly generated keys would be published. Now, keys that are already in the db on startup are published, unless they already have been. --- .../eonkeypublisher/eonkeypublisher.go | 93 ++++++++++++++----- .../keyper/database/keyper.sqlc.gen.go | 30 ++++++ .../keyper/database/sql/queries/keyper.sql | 4 + 3 files changed, 104 insertions(+), 23 deletions(-) diff --git a/rolling-shutter/eonkeypublisher/eonkeypublisher.go b/rolling-shutter/eonkeypublisher/eonkeypublisher.go index 93cee0ce8..076bbf45f 100644 --- a/rolling-shutter/eonkeypublisher/eonkeypublisher.go +++ b/rolling-shutter/eonkeypublisher/eonkeypublisher.go @@ -17,8 +17,10 @@ import ( obskeyperdb "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" + corekeyperdb "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/retry" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" + "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" ) const ( @@ -57,11 +59,13 @@ func NewEonKeyPublisher( } func (p *EonKeyPublisher) Start(ctx context.Context, runner service.Runner) error { //nolint: unparam + log.Info().Msg("starting eon key publisher") runner.Go(func() error { + p.publishOldKeys(ctx) for { select { case key := <-p.keys: - p.publish(ctx, key) + p.publishIfResponsible(ctx, key) case <-ctx.Done(): return ctx.Err() } @@ -75,24 +79,18 @@ func (p *EonKeyPublisher) Publish(key keyper.EonPublicKey) { p.keys <- key } -func (p *EonKeyPublisher) publish(ctx context.Context, key keyper.EonPublicKey) { - _, err := retry.FunctionCall[struct{}](ctx, func(ctx context.Context) (struct{}, error) { - return struct{}{}, p.tryPublish(ctx, key) - }, retry.Interval(retryInterval)) +// publishIfResponsible publishes a eon key if the keyper is part of the corresponding keyper +// set, unless the key is already confirmed or the keyper has already voted on it. +func (p *EonKeyPublisher) publishIfResponsible(ctx context.Context, key keyper.EonPublicKey) { + db := obskeyperdb.New(p.dbpool) + keyperSet, err := db.GetKeyperSetByKeyperConfigIndex(ctx, int64(key.Eon)) if err != nil { log.Error(). Err(err). Uint64("keyper-set-index", key.KeyperConfigIndex). Hex("key", key.PublicKey). - Msg("failed to publish eon key") - } -} - -func (p *EonKeyPublisher) tryPublish(ctx context.Context, key keyper.EonPublicKey) error { - db := obskeyperdb.New(p.dbpool) - keyperSet, err := db.GetKeyperSetByKeyperConfigIndex(ctx, int64(key.Eon)) - if err != nil { - return errors.Wrapf(err, "failed to query keyper set %d by index from db", key.KeyperConfigIndex) + Msg("failed to check if eon key should be published") + return } keyperAddress := ethcrypto.PubkeyToAddress(p.privateKey.PublicKey) keyperIndex, err := keyperSet.GetIndex(keyperAddress) @@ -100,29 +98,74 @@ func (p *EonKeyPublisher) tryPublish(ctx context.Context, key keyper.EonPublicKe log.Info(). Uint64("keyper-set-index", key.KeyperConfigIndex). Str("keyper-address", keyperAddress.Hex()). + Hex("key", key.PublicKey). Msg("not publishing eon key as keyper is not part of corresponding keyper set") - return nil + return + } + p.publish(ctx, key.PublicKey, key.KeyperConfigIndex, keyperIndex) +} + +// publishOldKeys publishes all eon keys that are already in the database, unless they're already +// confirmed or the keyper has already voted on them. +func (p *EonKeyPublisher) publishOldKeys(ctx context.Context) { + db := corekeyperdb.New(p.dbpool) + dkgResultsDB, err := db.GetAllDKGResults(ctx) + if err != nil { + err := errors.Wrap(err, "failed to query DKG results from db") + log.Error().Err(err).Msg("failed to publish old eon keys") + return + } + for _, dkgResultDB := range dkgResultsDB { + if !dkgResultDB.Success { + continue + } + dkgResult, err := shdb.DecodePureDKGResult(dkgResultDB.PureResult) + if err != nil { + err := errors.Wrapf(err, "failed to decode DKG result of eon %d", dkgResultDB.Eon) + log.Error().Err(err).Msg("failed to publish old eon keys") + continue + } + p.publish(ctx, dkgResult.PublicKey.Marshal(), dkgResult.Eon, dkgResult.Keyper) + } +} + +// publish publishes an eon key, unless it's already confirmed or the keyper has already voted on +// it. On errors, publishing will be retried a few times and eventually aborted. +func (p *EonKeyPublisher) publish(ctx context.Context, key []byte, keyperSetIndex uint64, keyperIndex uint64) { + _, err := retry.FunctionCall[struct{}](ctx, func(ctx context.Context) (struct{}, error) { + return struct{}{}, p.tryPublish(ctx, key, keyperSetIndex, keyperIndex) + }, retry.Interval(retryInterval)) + if err != nil { + log.Error(). + Err(err). + Uint64("keyper-set-index", keyperSetIndex). + Hex("key", key). + Msg("failed to publish eon key") } +} +func (p *EonKeyPublisher) tryPublish(ctx context.Context, key []byte, keyperSetIndex uint64, keyperIndex uint64) error { + keyperAddress := ethcrypto.PubkeyToAddress(p.privateKey.PublicKey) hasAlreadyVoted, err := p.contract.HasKeyperVoted(&bind.CallOpts{}, keyperAddress) if err != nil { return errors.Wrap(err, "failed to query eon key publisher contract if keyper has already voted") } if hasAlreadyVoted { log.Info(). - Uint64("keyper-set-index", key.KeyperConfigIndex). + Uint64("keyper-set-index", keyperSetIndex). Str("keyper-address", keyperAddress.Hex()). + Hex("key", key). Msg("not publishing eon key as keyper has already voted") return nil } - isAlreadyConfirmed, err := p.contract.EonKeyConfirmed(&bind.CallOpts{}, key.PublicKey) + isAlreadyConfirmed, err := p.contract.EonKeyConfirmed(&bind.CallOpts{}, key) if err != nil { return errors.Wrap(err, "failed to query eon key publisher contract if eon key is confirmed") } if isAlreadyConfirmed { log.Info(). - Uint64("keyper-set-index", key.KeyperConfigIndex). - Hex("key", key.PublicKey). + Uint64("keyper-set-index", keyperSetIndex). + Hex("key", key). Msg("not publishing eon key as it is already confirmed") return nil } @@ -135,13 +178,13 @@ func (p *EonKeyPublisher) tryPublish(ctx context.Context, key keyper.EonPublicKe if err != nil { return errors.Wrap(err, "failed to construct tx opts") } - tx, err := p.contract.PublishEonKey(opts, key.PublicKey, keyperIndex) + tx, err := p.contract.PublishEonKey(opts, key, keyperIndex) if err != nil { return errors.Wrap(err, "failed to send publish eon key tx") } log.Info(). - Uint64("keyper-set-index", key.KeyperConfigIndex). - Hex("key", key.PublicKey). + Uint64("keyper-set-index", keyperSetIndex). + Hex("key", key). Hex("tx-hash", tx.Hash().Bytes()). Msg("eon key publish tx sent") receipt, err := bind.WaitMined(ctx, p.client, tx) @@ -156,6 +199,10 @@ func (p *EonKeyPublisher) tryPublish(ctx context.Context, key keyper.EonPublicKe Msg("eon key publish tx failed") return errors.New("eon key publish tx failed") } - log.Info().Msg("successfully published eon key") + log.Info(). + Uint64("keyper-set-index", keyperSetIndex). + Hex("key", key). + Hex("tx-hash", tx.Hash().Bytes()). + Msg("successfully published eon key") return nil } diff --git a/rolling-shutter/keyper/database/keyper.sqlc.gen.go b/rolling-shutter/keyper/database/keyper.sqlc.gen.go index ef9228e10..1abcb260b 100644 --- a/rolling-shutter/keyper/database/keyper.sqlc.gen.go +++ b/rolling-shutter/keyper/database/keyper.sqlc.gen.go @@ -143,6 +143,36 @@ func (q *Queries) ExistsDecryptionKeyShare(ctx context.Context, arg ExistsDecryp return exists, err } +const getAllDKGResults = `-- name: GetAllDKGResults :many +SELECT eon, success, error, pure_result FROM dkg_result +ORDER BY eon ASC +` + +func (q *Queries) GetAllDKGResults(ctx context.Context) ([]DkgResult, error) { + rows, err := q.db.Query(ctx, getAllDKGResults) + if err != nil { + return nil, err + } + defer rows.Close() + var items []DkgResult + for rows.Next() { + var i DkgResult + if err := rows.Scan( + &i.Eon, + &i.Success, + &i.Error, + &i.PureResult, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getAllEons = `-- name: GetAllEons :many SELECT eon, height, activation_block_number, keyper_config_index FROM eons ORDER BY eon ` diff --git a/rolling-shutter/keyper/database/sql/queries/keyper.sql b/rolling-shutter/keyper/database/sql/queries/keyper.sql index e628fcbf9..28a8b2b37 100644 --- a/rolling-shutter/keyper/database/sql/queries/keyper.sql +++ b/rolling-shutter/keyper/database/sql/queries/keyper.sql @@ -169,6 +169,10 @@ WHERE eon = (SELECT eon FROM eons WHERE activation_block_number <= sqlc.arg(bloc ORDER BY activation_block_number DESC, height DESC LIMIT 1); +-- name: GetAllDKGResults :many +SELECT * FROM dkg_result +ORDER BY eon ASC; + -- name: InsertEonPublicKey :exec INSERT INTO outgoing_eon_keys (eon_public_key, eon) VALUES ($1, $2); From 7812fcbded7c065a9d2d115519326b77cbaed00f Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Tue, 9 Apr 2024 23:22:39 +0200 Subject: [PATCH 61/91] Improve log messages --- rolling-shutter/keyperimpl/gnosis/newslot.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rolling-shutter/keyperimpl/gnosis/newslot.go b/rolling-shutter/keyperimpl/gnosis/newslot.go index 50b95c13e..7aea18e19 100644 --- a/rolling-shutter/keyperimpl/gnosis/newslot.go +++ b/rolling-shutter/keyperimpl/gnosis/newslot.go @@ -54,7 +54,7 @@ func (kpr *Keyper) processNewSlot(ctx context.Context, slot slotticker.Slot) err log.Debug(). Uint64("slot", slot.Number). Int64("block-number", nextBlock). - Msg("ignoring slot as no keyper set has been found for it") + Msg("skipping slot as no keyper set has been found for it") return nil } if err != nil { @@ -68,7 +68,7 @@ func (kpr *Keyper) processNewSlot(ctx context.Context, slot slotticker.Slot) err Int64("block-number", nextBlock). Int64("keyper-set-index", keyperSet.KeyperConfigIndex). Str("address", kpr.config.GetAddress().Hex()). - Msg("ignoring block as not part of keyper set") + Msg("skipping slot as not part of keyper set") return nil } @@ -154,7 +154,7 @@ func (kpr *Keyper) triggerDecryption( Int64("block-number", nextBlock). Int64("eon", eon). Int64("tx-pointer", txPointer). - Msg("ignoring new block as tx pointer age is 0") + Msg("skipping new block as tx pointer age is 0") return nil } else if err != nil { return err From 4e536e503e293721665aa3cd9819360f84da7971 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Wed, 24 Apr 2024 22:50:22 +0200 Subject: [PATCH 62/91] Update shcrypto --- rolling-shutter/app/messages.go | 11 +++++++---- rolling-shutter/cmd/cryptocmd/jsontests.go | 7 ++++--- rolling-shutter/go.mod | 2 +- rolling-shutter/go.sum | 4 ++-- rolling-shutter/keyper/shutterevents/marshal.go | 12 ++++++++---- rolling-shutter/mocknode/mocknode.go | 3 +-- rolling-shutter/shmsg/messages.go | 4 +++- rolling-shutter/shmsg/messages_test.go | 4 +++- 8 files changed, 29 insertions(+), 18 deletions(-) diff --git a/rolling-shutter/app/messages.go b/rolling-shutter/app/messages.go index 5f552a550..9e8262961 100644 --- a/rolling-shutter/app/messages.go +++ b/rolling-shutter/app/messages.go @@ -4,7 +4,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - bn256 "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare" + "github.com/ethereum/go-ethereum/crypto/bls12381" "github.com/pkg/errors" "github.com/shutter-network/shutter/shlib/shcrypto" @@ -54,14 +54,17 @@ func ParsePolyEvalMsg(msg *shmsg.PolyEval, sender common.Address) (*PolyEval, er // ParsePolyCommitmentMsg converts a shmsg.PolyCommitmentMsg to an app.PolyCommitmentMsg. func ParsePolyCommitmentMsg(msg *shmsg.PolyCommitment, sender common.Address) (*PolyCommitment, error) { + g2 := bls12381.NewG2() gammas := shcrypto.Gammas{} for _, g := range msg.Gammas { - g2 := new(bn256.G2) - _, err := g2.Unmarshal(g) + p, err := g2.FromBytes(g) if err != nil { return nil, err } - gammas = append(gammas, g2) + if !g2.IsOnCurve(p) { + return nil, errors.Errorf("invalid gamma value %x", g) + } + gammas = append(gammas, p) } return &PolyCommitment{ Sender: sender, diff --git a/rolling-shutter/cmd/cryptocmd/jsontests.go b/rolling-shutter/cmd/cryptocmd/jsontests.go index 732b14487..eef12e35e 100644 --- a/rolling-shutter/cmd/cryptocmd/jsontests.go +++ b/rolling-shutter/cmd/cryptocmd/jsontests.go @@ -10,7 +10,7 @@ import ( "os" "github.com/ethereum/go-ethereum/common/hexutil" - bn256 "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare" + "github.com/ethereum/go-ethereum/crypto/bls12381" "github.com/spf13/cobra" "github.com/shutter-network/shutter/shlib/shcrypto" @@ -452,12 +452,13 @@ func createEncryptionTest(keygen *testkeygen.KeyGenerator, message []byte) (*enc // tamperEncryptedMessage changes the C1 value of EncryptedMessage, which allows to test for malleability issues. func tamperEncryptedMessage(keygen *testkeygen.KeyGenerator, et encryptionTest) encryptionTest { decryptionKey := keygen.EpochSecretKey(et.EpochID) - var c1 *bn256.G2 + g2 := bls12381.NewG2() + var c1 *bls12381.PointG2 var err error for i := 1; i <= 10000; i++ { c1 = et.Expected.C1 - c1.Add(c1, c1) + g2.Add(c1, c1, c1) et.Expected.C1 = c1 sigma := et.Expected.Sigma(decryptionKey) decryptedBlocks := shcrypto.DecryptBlocks(et.Expected.C3, sigma) diff --git a/rolling-shutter/go.mod b/rolling-shutter/go.mod index b3722234c..8af9dcd76 100644 --- a/rolling-shutter/go.mod +++ b/rolling-shutter/go.mod @@ -31,7 +31,7 @@ require ( github.com/rs/zerolog v1.28.0 github.com/shutter-network/gnosh-contracts v0.2.0 github.com/shutter-network/shop-contracts v0.0.0-20240407151512-08ef5d8355b6 - github.com/shutter-network/shutter/shlib v0.1.13 + github.com/shutter-network/shutter/shlib v0.1.18 github.com/shutter-network/txtypes v0.1.0 github.com/spf13/afero v1.8.2 github.com/spf13/cobra v1.6.1 diff --git a/rolling-shutter/go.sum b/rolling-shutter/go.sum index 2c5dc2312..6e3d2b3f5 100644 --- a/rolling-shutter/go.sum +++ b/rolling-shutter/go.sum @@ -835,8 +835,8 @@ github.com/shutter-network/gnosh-contracts v0.2.0 h1:qH3gAhlh5VZzvJcbi044lxFWQ+M github.com/shutter-network/gnosh-contracts v0.2.0/go.mod h1:QB0d64ybbVFKMrLjrc1tldri87KNjTmKQjhk9jaso2E= github.com/shutter-network/shop-contracts v0.0.0-20240407151512-08ef5d8355b6 h1:m6Ti1/IH+GBTtGqyAX3xbh+ruUKvC+m+/uzYDUa+JDQ= github.com/shutter-network/shop-contracts v0.0.0-20240407151512-08ef5d8355b6/go.mod h1:LEWXLRruvxq9fe2oKtJI3xfzbauhfWTjOczHN61RU+4= -github.com/shutter-network/shutter/shlib v0.1.13 h1:9YloDJBdhFAKm2GMg4gBNeaJ+Mw9Qzeh5Kz9A2ayp1E= -github.com/shutter-network/shutter/shlib v0.1.13/go.mod h1:RlYNZjx+pfKAi0arH+jfdlxG4kQ75UFzDfVjgCVYaUw= +github.com/shutter-network/shutter/shlib v0.1.18 h1:ei1EWEavnlkwbX51aGKtgt7NydY0IPNV35J525vAfeo= +github.com/shutter-network/shutter/shlib v0.1.18/go.mod h1:RlYNZjx+pfKAi0arH+jfdlxG4kQ75UFzDfVjgCVYaUw= github.com/shutter-network/txtypes v0.1.0 h1:QqdiiiB9AiBCSJ/ke6z1ZoDGfu2+1Lgpz5vHzVN4FKc= github.com/shutter-network/txtypes v0.1.0/go.mod h1:gaLQWfEFH+3q5CmM3kkSgS+NspclEs8SCnj8QAozsaI= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= diff --git a/rolling-shutter/keyper/shutterevents/marshal.go b/rolling-shutter/keyper/shutterevents/marshal.go index 3baa8787c..1aefd341d 100644 --- a/rolling-shutter/keyper/shutterevents/marshal.go +++ b/rolling-shutter/keyper/shutterevents/marshal.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" ethcrypto "github.com/ethereum/go-ethereum/crypto" - bn256 "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare" + "github.com/ethereum/go-ethereum/crypto/bls12381" "github.com/ethereum/go-ethereum/crypto/ecies" "github.com/pkg/errors" @@ -96,16 +96,18 @@ func decodePubkey(val string) (*ecdsa.PublicKey, error) { } func encodeGammas(gammas *shcrypto.Gammas) string { + g2 := bls12381.NewG2() var encoded []string if gammas != nil { for _, g := range *gammas { - encoded = append(encoded, hex.EncodeToString(g.Marshal())) + encoded = append(encoded, hex.EncodeToString(g2.ToBytes(g))) } } return strings.Join(encoded, ",") } func decodeGammas(eventValue string) (shcrypto.Gammas, error) { + g2 := bls12381.NewG2() parts := strings.Split(eventValue, ",") var res shcrypto.Gammas for _, p := range parts { @@ -113,11 +115,13 @@ func decodeGammas(eventValue string) (shcrypto.Gammas, error) { if err != nil { return shcrypto.Gammas{}, err } - g := new(bn256.G2) - _, err = g.Unmarshal(marshaledG2) + g, err := g2.FromBytes(marshaledG2) if err != nil { return shcrypto.Gammas{}, err } + if !g2.IsOnCurve(g) { + return shcrypto.Gammas{}, errors.Errorf("invalid gamma value %x", p) + } res = append(res, g) } return res, nil diff --git a/rolling-shutter/mocknode/mocknode.go b/rolling-shutter/mocknode/mocknode.go index 8261fe917..b073c39fa 100644 --- a/rolling-shutter/mocknode/mocknode.go +++ b/rolling-shutter/mocknode/mocknode.go @@ -8,7 +8,6 @@ import ( "sync" "time" - bn256 "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare" "github.com/pkg/errors" "github.com/rs/zerolog/log" txtypes "github.com/shutter-network/txtypes/types" @@ -107,7 +106,7 @@ func (m *MockNode) handleEonPublicKey( if err := m.eonPublicKey.Unmarshal(key.PublicKey); err != nil { log.Info().Err(err).Msg("failed to unmarshal eon public key") } - log.Info().Str("eon-public-key", (*bn256.G2)(m.eonPublicKey).String()). + log.Info().Hex("eon-public-key", m.eonPublicKey.Marshal()). Msg("updated eon public key from messages to %s") return make([]p2pmsg.Message, 0), nil } diff --git a/rolling-shutter/shmsg/messages.go b/rolling-shutter/shmsg/messages.go index e78ba1a76..beaea1a96 100644 --- a/rolling-shutter/shmsg/messages.go +++ b/rolling-shutter/shmsg/messages.go @@ -5,6 +5,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/bls12381" "github.com/ethereum/go-ethereum/crypto/ecies" shcrypto "github.com/shutter-network/shutter/shlib/shcrypto" @@ -79,9 +80,10 @@ func NewAccusation(eon uint64, accused []common.Address) *Message { // NewPolyCommitment creates a new poly commitment message containing gamma values. func NewPolyCommitment(eon uint64, gammas *shcrypto.Gammas) *Message { + g2 := bls12381.NewG2() gammaBytes := [][]byte{} for _, gamma := range *gammas { - gammaBytes = append(gammaBytes, gamma.Marshal()) + gammaBytes = append(gammaBytes, g2.ToBytes(gamma)) } return &Message{ diff --git a/rolling-shutter/shmsg/messages_test.go b/rolling-shutter/shmsg/messages_test.go index 308dc8009..2d04ca03a 100644 --- a/rolling-shutter/shmsg/messages_test.go +++ b/rolling-shutter/shmsg/messages_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/bls12381" "gotest.tools/v3/assert" shcrypto "github.com/shutter-network/shutter/shlib/shcrypto" @@ -24,9 +25,10 @@ func TestNewPolyCommitmentMsg(t *testing.T) { assert.Equal(t, eon, msg.Eon) assert.Equal(t, int(threshold)+1, len(msg.Gammas)) + g2 := bls12381.NewG2() for i := 0; i < int(threshold)+1; i++ { gammaBytes := msg.Gammas[i] - assert.DeepEqual(t, gammaBytes, (*gammas)[i].Marshal()) + assert.DeepEqual(t, gammaBytes, g2.ToBytes((*gammas)[i])) } } From ad464c7898029a74a47fcb0778fc296d6ff9fa8c Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Fri, 26 Apr 2024 13:20:58 +0200 Subject: [PATCH 63/91] Trigger decryption based on blocks and slots Before this commit, decryption for slot n was triggered at the start of slot n. As validators are supposed to propose at the start of their slot, this gives them no time to receive the decryption keys. This commit changes the condition to trigger decryption for slot n either when the block for slot n-1 has been observed or when 1/3 of slot n-1 has passed, whatever is first. --- rolling-shutter/keyperimpl/gnosis/keyper.go | 29 +++++++++--- rolling-shutter/keyperimpl/gnosis/newblock.go | 8 +++- rolling-shutter/keyperimpl/gnosis/newslot.go | 38 ++++++++++------ rolling-shutter/medley/slots.go | 2 +- .../medley/slotticker/slotticker.go | 26 ++++++----- .../medley/slotticker/slotticker_test.go | 44 +++++++++++++++++++ 6 files changed, 114 insertions(+), 33 deletions(-) create mode 100644 rolling-shutter/medley/slotticker/slotticker_test.go diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index 803cdf193..f415766fb 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -30,14 +30,23 @@ import ( var ErrParseKeyperSet = errors.New("cannot parse KeyperSet") +// The relative proposal timeout specifies for how long we wait for a block proposal to appear in +// a block. If we don't receive one in this time, we assume the slot is empty. The timeout is +// given as a fraction of the slot duration. +const ( + relativeProposalTimeoutNumerator = 1 + relativeProposalTimeoutDenominator = 3 +) + type Keyper struct { - core *keyper.KeyperCore - config *Config - dbpool *pgxpool.Pool - client *ethclient.Client - chainSyncClient *chainsync.Client - sequencerSyncer *SequencerSyncer - eonKeyPublisher *eonkeypublisher.EonKeyPublisher + core *keyper.KeyperCore + config *Config + dbpool *pgxpool.Pool + client *ethclient.Client + chainSyncClient *chainsync.Client + sequencerSyncer *SequencerSyncer + eonKeyPublisher *eonkeypublisher.EonKeyPublisher + latestTriggeredSlot *uint64 // input events newBlocks chan *syncevent.LatestBlock @@ -67,9 +76,15 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { runner.Defer(func() { close(kpr.newEonPublicKeys) }) runner.Defer(func() { close(kpr.decryptionTriggerChannel) }) + kpr.latestTriggeredSlot = nil + + offset := -(time.Duration(kpr.config.Gnosis.SecondsPerSlot) * time.Second) * + (relativeProposalTimeoutDenominator - relativeProposalTimeoutNumerator) / + relativeProposalTimeoutDenominator kpr.slotTicker = slotticker.NewSlotTicker( time.Duration(kpr.config.Gnosis.SecondsPerSlot*uint64(time.Second)), time.Unix(int64(kpr.config.Gnosis.GenesisSlotTimestamp), 0), + offset, ) kpr.dbpool, err = db.Connect(ctx, runner, kpr.config.DatabaseURL, database.Definition.Name()) diff --git a/rolling-shutter/keyperimpl/gnosis/newblock.go b/rolling-shutter/keyperimpl/gnosis/newblock.go index 20a1f2adf..f37013591 100644 --- a/rolling-shutter/keyperimpl/gnosis/newblock.go +++ b/rolling-shutter/keyperimpl/gnosis/newblock.go @@ -3,6 +3,7 @@ package gnosis import ( "context" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley" syncevent "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" ) @@ -12,5 +13,10 @@ func (kpr *Keyper) processNewBlock(ctx context.Context, ev *syncevent.LatestBloc return err } } - return nil + slot := medley.BlockTimestampToSlot( + ev.Header.Time, + kpr.config.Gnosis.GenesisSlotTimestamp, + kpr.config.Gnosis.SecondsPerSlot, + ) + return kpr.maybeTriggerDecryption(ctx, slot+1) } diff --git a/rolling-shutter/keyperimpl/gnosis/newslot.go b/rolling-shutter/keyperimpl/gnosis/newslot.go index 7aea18e19..2b680a7a1 100644 --- a/rolling-shutter/keyperimpl/gnosis/newslot.go +++ b/rolling-shutter/keyperimpl/gnosis/newslot.go @@ -28,9 +28,21 @@ const maxTxPointerAge = 2 var errZeroTxPointerAge = errors.New("tx pointer has age 0") func (kpr *Keyper) processNewSlot(ctx context.Context, slot slotticker.Slot) error { + return kpr.maybeTriggerDecryption(ctx, slot.Number) +} + +// maybeTriggerDecryption triggers decryption for the given slot if +// - it hasn't been triggered for this slot before and +// - the keyper is part of the corresponding keyper set. +func (kpr *Keyper) maybeTriggerDecryption(ctx context.Context, slot uint64) error { + if kpr.latestTriggeredSlot != nil && slot <= *kpr.latestTriggeredSlot { + return nil + } + kpr.latestTriggeredSlot = &slot + fmt.Println("") fmt.Println("") - fmt.Println(slot.Number) + fmt.Println(slot) fmt.Println("") fmt.Println("") @@ -39,12 +51,12 @@ func (kpr *Keyper) processNewSlot(ctx context.Context, slot slotticker.Slot) err if err != nil { return errors.Wrap(err, "failed to query synced until from db") } - if syncedUntil.Slot >= int64(slot.Number) { + if syncedUntil.Slot >= int64(slot) { // If we already synced the block for slot n before this slot has started on our clock, // either the previous block proposer proposed early (ie is malicious) or our clocks are // out of sync. In any case, it does not make sense to produce keys as the block has // already been built, so we return an error. - return errors.Errorf("processing slot %d for which a block has already been processed", slot.Number) + return errors.Errorf("processing slot %d for which a block has already been processed", slot) } nextBlock := syncedUntil.BlockNumber + 1 @@ -52,7 +64,7 @@ func (kpr *Keyper) processNewSlot(ctx context.Context, slot slotticker.Slot) err keyperSet, err := queries.GetKeyperSet(ctx, nextBlock) if err == pgx.ErrNoRows { log.Debug(). - Uint64("slot", slot.Number). + Uint64("slot", slot). Int64("block-number", nextBlock). Msg("skipping slot as no keyper set has been found for it") return nil @@ -64,7 +76,7 @@ func (kpr *Keyper) processNewSlot(ctx context.Context, slot slotticker.Slot) err return kpr.triggerDecryption(ctx, slot, nextBlock, &keyperSet) } log.Debug(). - Uint64("slot", slot.Number). + Uint64("slot", slot). Int64("block-number", nextBlock). Int64("keyper-set-index", keyperSet.KeyperConfigIndex). Str("address", kpr.config.GetAddress().Hex()). @@ -134,7 +146,7 @@ func (kpr *Keyper) getTxPointer(ctx context.Context, eon int64, slot int64, keyp func (kpr *Keyper) triggerDecryption( ctx context.Context, - slot slotticker.Slot, + slot uint64, nextBlock int64, keyperSet *obskeyper.KeyperSet, ) error { @@ -147,10 +159,10 @@ func (kpr *Keyper) triggerDecryption( } eon := eonStruct.Eon - txPointer, err := kpr.getTxPointer(ctx, eon, int64(slot.Number), keyperSet.KeyperConfigIndex) + txPointer, err := kpr.getTxPointer(ctx, eon, int64(slot), keyperSet.KeyperConfigIndex) if err == errZeroTxPointerAge { log.Warn(). - Uint64("slot", slot.Number). + Uint64("slot", slot). Int64("block-number", nextBlock). Int64("eon", eon). Int64("tx-pointer", txPointer). @@ -166,7 +178,7 @@ func (kpr *Keyper) triggerDecryption( } err = gnosisKeyperDB.SetCurrentDecryptionTrigger(ctx, gnosisdatabase.SetCurrentDecryptionTriggerParams{ Eon: eon, - Slot: int64(slot.Number), + Slot: int64(slot), TxPointer: txPointer, IdentitiesHash: computeIdentitiesHash(identityPreimages), }) @@ -179,7 +191,7 @@ func (kpr *Keyper) triggerDecryption( } event := broker.NewEvent(&trigger) log.Debug(). - Uint64("slot", slot.Number). + Uint64("slot", slot). Uint64("block-number", uint64(nextBlock)). Int("num-identities", len(trigger.IdentityPreimages)). Int64("tx-pointer", txPointer). @@ -190,7 +202,7 @@ func (kpr *Keyper) triggerDecryption( } func (kpr *Keyper) getDecryptionIdentityPreimages( - ctx context.Context, slot slotticker.Slot, eon int64, txPointer int64, + ctx context.Context, slot uint64, eon int64, txPointer int64, ) ([]identitypreimage.IdentityPreimage, error) { identityPreimages := []identitypreimage.IdentityPreimage{} @@ -243,13 +255,13 @@ func transactionSubmittedEventToIdentityPreimage( return identitypreimage.IdentityPreimage(buf.Bytes()), nil } -func makeSlotIdentityPreimage(slot slotticker.Slot) identitypreimage.IdentityPreimage { +func makeSlotIdentityPreimage(slot uint64) identitypreimage.IdentityPreimage { // 32 bytes of zeros plus the block number as big endian (ie starting with lots of zeros as well) // this ensures the block identity preimage is always alphanumerically before any transaction // identity preimages. var buf bytes.Buffer buf.Write(common.BigToHash(common.Big0).Bytes()) - buf.Write(common.BigToHash(new(big.Int).SetUint64(slot.Number)).Bytes()) + buf.Write(common.BigToHash(new(big.Int).SetUint64(slot)).Bytes()) return identitypreimage.IdentityPreimage(buf.Bytes()) } diff --git a/rolling-shutter/medley/slots.go b/rolling-shutter/medley/slots.go index 15fa54388..cd866300e 100644 --- a/rolling-shutter/medley/slots.go +++ b/rolling-shutter/medley/slots.go @@ -1,5 +1,5 @@ package medley -func BlockTimestampToSlot(blockTimestamp uint64, secondsPerSlot uint64, genesisSlotTimestamp uint64) uint64 { +func BlockTimestampToSlot(blockTimestamp uint64, genesisSlotTimestamp uint64, secondsPerSlot uint64) uint64 { return (blockTimestamp - genesisSlotTimestamp) / secondsPerSlot } diff --git a/rolling-shutter/medley/slotticker/slotticker.go b/rolling-shutter/medley/slotticker/slotticker.go index 4d7d4f652..a353e8217 100644 --- a/rolling-shutter/medley/slotticker/slotticker.go +++ b/rolling-shutter/medley/slotticker/slotticker.go @@ -24,14 +24,16 @@ type SlotTicker struct { C chan Slot slotDuration time.Duration genesisSlotTime time.Time + offset time.Duration } -func NewSlotTicker(slotDuration time.Duration, genesisSlotTime time.Time) *SlotTicker { +func NewSlotTicker(slotDuration time.Duration, genesisSlotTime time.Time, offset time.Duration) *SlotTicker { c := make(chan Slot, 1) return &SlotTicker{ C: c, slotDuration: slotDuration, genesisSlotTime: genesisSlotTime, + offset: offset, } } @@ -64,14 +66,7 @@ func (t *SlotTicker) run(ctx context.Context) error { for { now := time.Now() - timeSinceGenesis := now.Sub(t.genesisSlotTime) - - var nextSlotNumber uint64 - if timeSinceGenesis < 0 { - nextSlotNumber = 0 - } else { - nextSlotNumber = uint64(timeSinceGenesis/t.slotDuration) + 1 - } + nextSlotNumber, nextTickTime := calcNextTick(now, t.genesisSlotTime, t.slotDuration, t.offset) if prevSlotNumber != nil { expectedNextSlotNumber := *prevSlotNumber + 1 @@ -95,8 +90,7 @@ func (t *SlotTicker) run(ctx context.Context) error { } } - nextSlotTime := t.genesisSlotTime.Add(t.slotDuration * time.Duration(nextSlotNumber)) - timeToNextSlot := nextSlotTime.Sub(now) + timeToNextSlot := nextTickTime.Sub(now) timer.Reset(timeToNextSlot) <-timer.C @@ -107,3 +101,13 @@ func (t *SlotTicker) run(ctx context.Context) error { prevSlotNumber = &nextSlotNumber } } + +func calcNextTick(now time.Time, genesisSlotTime time.Time, slotDuration time.Duration, offset time.Duration) (uint64, time.Time) { + firstTick := genesisSlotTime.Add(offset) + if now.Before(firstTick) { + return 0, firstTick + } + slot := uint64((now.Sub(genesisSlotTime) - offset + slotDuration - 1) / slotDuration) + tick := genesisSlotTime.Add(slotDuration * time.Duration(slot)).Add(offset) + return slot, tick +} diff --git a/rolling-shutter/medley/slotticker/slotticker_test.go b/rolling-shutter/medley/slotticker/slotticker_test.go new file mode 100644 index 000000000..a73790826 --- /dev/null +++ b/rolling-shutter/medley/slotticker/slotticker_test.go @@ -0,0 +1,44 @@ +package slotticker + +import ( + "testing" + "time" + + "gotest.tools/assert" +) + +func TestCalcNextSlot(t *testing.T) { + duration := time.Second * 5 + genesisTime := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC) + + epsilon := time.Millisecond + + for _, testCase := range []struct { + timeSinceGenesis time.Duration + offset time.Duration + slot uint64 + }{ + {0, 0, 0}, + {-time.Second * 100, 0, 0}, + {epsilon, 0, 1}, + {duration / 2, 0, 1}, + {duration, 0, 1}, + {2 * duration, 0, 2}, + {100*duration - epsilon, 0, 100}, + + {-time.Second, -time.Second, 0}, + {0, -time.Second, 1}, + {4 * time.Second, -time.Second, 1}, + {4*time.Second + epsilon, -time.Second, 2}, + {100*duration - time.Second, -time.Second, 100}, + {100*duration - time.Second + epsilon, -time.Second, 101}, + } { + t.Run("", func(t *testing.T) { + now := genesisTime.Add(testCase.timeSinceGenesis) + slot, tick := calcNextTick(now, genesisTime, duration, testCase.offset) + assert.Equal(t, testCase.slot, slot) + expectedTick := genesisTime.Add(duration * time.Duration(testCase.slot)).Add(testCase.offset) + assert.Equal(t, tick, expectedTick) + }) + } +} From 8c5e715e4d774c2640c3f37310e7d593ea888b04 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Fri, 26 Apr 2024 17:30:10 +0200 Subject: [PATCH 64/91] Replace eon with keyper set index in shares msg --- rolling-shutter/keyper/epochkghandler/sendkeyshare.go | 4 ++-- rolling-shutter/keyperimpl/gnosis/handlers.go | 7 +------ rolling-shutter/medley/chainsync/syncer/keyperset.go | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/rolling-shutter/keyper/epochkghandler/sendkeyshare.go b/rolling-shutter/keyper/epochkghandler/sendkeyshare.go index effcc39f2..1f33fa9ba 100644 --- a/rolling-shutter/keyper/epochkghandler/sendkeyshare.go +++ b/rolling-shutter/keyper/epochkghandler/sendkeyshare.go @@ -122,7 +122,7 @@ func (ksh *KeyShareHandler) ConstructDecryptionKeyShares( }) } - eonUint, err := medley.Int64ToUint64Safe(eon.Eon) + keyperSetIndexUint, err := medley.Int64ToUint64Safe(eon.Eon) if err != nil { return nil, err } @@ -132,7 +132,7 @@ func (ksh *KeyShareHandler) ConstructDecryptionKeyShares( } msg := &p2pmsg.DecryptionKeyShares{ InstanceID: ksh.InstanceID, - Eon: eonUint, + Eon: keyperSetIndexUint, KeyperIndex: keyperIndexUint, Shares: shares, } diff --git a/rolling-shutter/keyperimpl/gnosis/handlers.go b/rolling-shutter/keyperimpl/gnosis/handlers.go index 64b31b777..22ab84f1e 100644 --- a/rolling-shutter/keyperimpl/gnosis/handlers.go +++ b/rolling-shutter/keyperimpl/gnosis/handlers.go @@ -223,13 +223,8 @@ func (h *DecryptionKeysHandler) ValidateMessage(ctx context.Context, msg p2pmsg. return pubsub.ValidationReject, errors.New("msg does not contain any keys") } - keyperCoreDB := corekeyperdatabase.New(h.dbpool) obsKeyperDB := obskeyperdatabase.New(h.dbpool) - eonData, err := keyperCoreDB.GetEon(ctx, int64(keys.Eon)) - if err != nil { - return pubsub.ValidationReject, errors.Wrapf(err, "failed to get eon data from database for eon %d", keys.Eon) - } - keyperSet, err := obsKeyperDB.GetKeyperSet(ctx, eonData.ActivationBlockNumber) + keyperSet, err := obsKeyperDB.GetKeyperSetByKeyperConfigIndex(ctx, int64(keys.Eon)) if err != nil { return pubsub.ValidationReject, errors.Wrapf(err, "failed to get keyper set from database for eon %d", keys.Eon) } diff --git a/rolling-shutter/medley/chainsync/syncer/keyperset.go b/rolling-shutter/medley/chainsync/syncer/keyperset.go index 5036a6bf3..91ff18810 100644 --- a/rolling-shutter/medley/chainsync/syncer/keyperset.go +++ b/rolling-shutter/medley/chainsync/syncer/keyperset.go @@ -160,7 +160,7 @@ func (s *KeyperSetSyncer) GetKeyperSetForBlock(ctx context.Context, opts *bind.C idx, err := s.Contract.GetKeyperSetIndexByBlock(opts, atBlock) if err != nil { - return nil, errors.Wrap(err, "could not retrieve keyper set index") + return nil, errors.Wrapf(err, "could not retrieve keyper set index at block %d", atBlock) } return s.GetKeyperSetByIndex(ctx, opts, idx) } From 2f6b7b33a776b3546c29ef8180e63ba8de9b71bd Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Tue, 7 May 2024 10:53:37 +0200 Subject: [PATCH 65/91] Sync validator registry --- rolling-shutter/go.mod | 4 +- rolling-shutter/keyperimpl/gnosis/config.go | 8 +- .../gnosis/database/gnosiskeyper.sqlc.gen.go | 102 ++++++++ .../gnosis/database/models.sqlc.gen.go | 16 ++ .../database/sql/queries/gnosiskeyper.sql | 31 +++ .../database/sql/schemas/gnosiskeyper.sql | 18 ++ rolling-shutter/keyperimpl/gnosis/keyper.go | 31 ++- rolling-shutter/keyperimpl/gnosis/newblock.go | 4 + .../keyperimpl/gnosis/validatorsyncer.go | 225 ++++++++++++++++++ .../medley/beaconapiclient/beaconapiclient.go | 22 ++ .../medley/beaconapiclient/getvalidator.go | 73 ++++++ .../medley/validatorregistry/signature.go | 18 ++ .../validatorregistry/signature_test.go | 32 +++ .../validatorregistry/validatorregistry.go | 54 +++++ .../validatorregistry_test.go | 47 ++++ 15 files changed, 680 insertions(+), 5 deletions(-) create mode 100644 rolling-shutter/keyperimpl/gnosis/validatorsyncer.go create mode 100644 rolling-shutter/medley/beaconapiclient/beaconapiclient.go create mode 100644 rolling-shutter/medley/beaconapiclient/getvalidator.go create mode 100644 rolling-shutter/medley/validatorregistry/signature.go create mode 100644 rolling-shutter/medley/validatorregistry/signature_test.go create mode 100644 rolling-shutter/medley/validatorregistry/validatorregistry.go create mode 100644 rolling-shutter/medley/validatorregistry/validatorregistry_test.go diff --git a/rolling-shutter/go.mod b/rolling-shutter/go.mod index 8af9dcd76..51507ad50 100644 --- a/rolling-shutter/go.mod +++ b/rolling-shutter/go.mod @@ -37,6 +37,8 @@ require ( github.com/spf13/cobra v1.6.1 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.13.0 + github.com/stretchr/testify v1.8.4 + github.com/supranational/blst v0.3.11 github.com/tendermint/go-amino v0.16.0 github.com/tendermint/tendermint v0.37.0-rc2 go.opentelemetry.io/otel v1.21.0 @@ -197,6 +199,7 @@ require ( github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/petermattis/goid v0.0.0-20230808133559-b036b712a89b // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/polydawn/refmt v0.89.0 // indirect github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/common v0.44.0 // indirect @@ -217,7 +220,6 @@ require ( github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect - github.com/supranational/blst v0.3.11 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect github.com/tendermint/tm-db v0.6.7 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect diff --git a/rolling-shutter/keyperimpl/gnosis/config.go b/rolling-shutter/keyperimpl/gnosis/config.go index ac7c01777..acea8964c 100644 --- a/rolling-shutter/keyperimpl/gnosis/config.go +++ b/rolling-shutter/keyperimpl/gnosis/config.go @@ -38,8 +38,9 @@ func (c *Config) Init() { } type Config struct { - InstanceID uint64 `shconfig:",required"` - DatabaseURL string `shconfig:",required" comment:"If it's empty, we use the standard PG_ environment variables"` + InstanceID uint64 `shconfig:",required"` + DatabaseURL string `shconfig:",required" comment:"If it's empty, we use the standard PG_ environment variables"` + BeaconAPIURL string `shconfig:",required"` HTTPEnabled bool HTTPListenAddress string @@ -80,6 +81,7 @@ func (c *Config) SetExampleValues() error { } c.InstanceID = 42 c.DatabaseURL = "postgres://pguser:pgpassword@localhost:5432/shutter" + c.BeaconAPIURL = "http://localhost:5052" return nil } @@ -151,6 +153,7 @@ type GnosisContractsConfig struct { KeyBroadcastContract common.Address `shconfig:",required"` EonKeyPublish common.Address `shconfig:",required"` Sequencer common.Address `shconfig:",required"` + ValidatorRegistry common.Address `shconfig:",required"` } func NewGnosisContractsConfig() *GnosisContractsConfig { @@ -159,6 +162,7 @@ func NewGnosisContractsConfig() *GnosisContractsConfig { KeyBroadcastContract: common.Address{}, EonKeyPublish: common.Address{}, Sequencer: common.Address{}, + ValidatorRegistry: common.Address{}, } } diff --git a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go index c8b2e7e9c..99f65e047 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go +++ b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go @@ -160,6 +160,43 @@ func (q *Queries) GetTxPointer(ctx context.Context, eon int64) (TxPointer, error return i, err } +const getValidatorRegistrationNonceBefore = `-- name: GetValidatorRegistrationNonceBefore :one +SELECT nonce FROM validator_registrations +WHERE validator_index = $1 AND block_number <= $2 AND tx_index <= $3 AND log_index <= $4 +ORDER BY block_number DESC, tx_index DESC, log_index DESC +LIMIT 1 +` + +type GetValidatorRegistrationNonceBeforeParams struct { + ValidatorIndex int64 + BlockNumber int64 + TxIndex int64 + LogIndex int64 +} + +func (q *Queries) GetValidatorRegistrationNonceBefore(ctx context.Context, arg GetValidatorRegistrationNonceBeforeParams) (int64, error) { + row := q.db.QueryRow(ctx, getValidatorRegistrationNonceBefore, + arg.ValidatorIndex, + arg.BlockNumber, + arg.TxIndex, + arg.LogIndex, + ) + var nonce int64 + err := row.Scan(&nonce) + return nonce, err +} + +const getValidatorRegistrationsSyncedUntil = `-- name: GetValidatorRegistrationsSyncedUntil :one +SELECT enforce_one_row, block_hash, block_number FROM validator_registrations_synced_until LIMIT 1 +` + +func (q *Queries) GetValidatorRegistrationsSyncedUntil(ctx context.Context) (ValidatorRegistrationsSyncedUntil, error) { + row := q.db.QueryRow(ctx, getValidatorRegistrationsSyncedUntil) + var i ValidatorRegistrationsSyncedUntil + err := row.Scan(&i.EnforceOneRow, &i.BlockHash, &i.BlockNumber) + return i, err +} + const initTxPointer = `-- name: InitTxPointer :exec INSERT INTO tx_pointer (eon, slot, value) VALUES ($1, $2, 0) @@ -244,6 +281,55 @@ func (q *Queries) InsertTransactionSubmittedEvent(ctx context.Context, arg Inser ) } +const insertValidatorRegistration = `-- name: InsertValidatorRegistration :exec +INSERT INTO validator_registrations ( + block_number, + block_hash, + tx_index, + log_index, + validator_index, + nonce, + is_registration +) VALUES ($1, $2, $3, $4, $5, $6, $7) +` + +type InsertValidatorRegistrationParams struct { + BlockNumber int64 + BlockHash []byte + TxIndex int64 + LogIndex int64 + ValidatorIndex int64 + Nonce int64 + IsRegistration bool +} + +func (q *Queries) InsertValidatorRegistration(ctx context.Context, arg InsertValidatorRegistrationParams) error { + _, err := q.db.Exec(ctx, insertValidatorRegistration, + arg.BlockNumber, + arg.BlockHash, + arg.TxIndex, + arg.LogIndex, + arg.ValidatorIndex, + arg.Nonce, + arg.IsRegistration, + ) + return err +} + +const isValidatorRegistered = `-- name: IsValidatorRegistered :one +SELECT is_registration FROM validator_registrations +WHERE validator_index = $1 AND block_number < $1 +ORDER BY block_number DESC, tx_index DESC, log_index DESC +LIMIT 1 +` + +func (q *Queries) IsValidatorRegistered(ctx context.Context, validatorIndex int64) (bool, error) { + row := q.db.QueryRow(ctx, isValidatorRegistered, validatorIndex) + var is_registration bool + err := row.Scan(&is_registration) + return is_registration, err +} + const setCurrentDecryptionTrigger = `-- name: SetCurrentDecryptionTrigger :exec INSERT INTO current_decryption_trigger (eon, slot, tx_pointer, identities_hash) VALUES ($1, $2, $3, $4) @@ -319,3 +405,19 @@ func (q *Queries) SetTxPointer(ctx context.Context, arg SetTxPointerParams) erro _, err := q.db.Exec(ctx, setTxPointer, arg.Eon, arg.Slot, arg.Value) return err } + +const setValidatorRegistrationsSyncedUntil = `-- name: SetValidatorRegistrationsSyncedUntil :exec +INSERT INTO validator_registrations_synced_until (block_hash, block_number) VALUES ($1, $2) +ON CONFLICT (enforce_one_row) DO UPDATE +SET block_hash = $1, block_number = $2 +` + +type SetValidatorRegistrationsSyncedUntilParams struct { + BlockHash []byte + BlockNumber int64 +} + +func (q *Queries) SetValidatorRegistrationsSyncedUntil(ctx context.Context, arg SetValidatorRegistrationsSyncedUntilParams) error { + _, err := q.db.Exec(ctx, setValidatorRegistrationsSyncedUntil, arg.BlockHash, arg.BlockNumber) + return err +} diff --git a/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go index 2cb9d9fee..868cc4d5c 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go +++ b/rolling-shutter/keyperimpl/gnosis/database/models.sqlc.gen.go @@ -51,3 +51,19 @@ type TxPointer struct { Slot int64 Value int64 } + +type ValidatorRegistration struct { + BlockNumber int64 + BlockHash []byte + TxIndex int64 + LogIndex int64 + ValidatorIndex int64 + Nonce int64 + IsRegistration bool +} + +type ValidatorRegistrationsSyncedUntil struct { + EnforceOneRow bool + BlockHash []byte + BlockNumber int64 +} diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql index 9c5574076..31dc1a48f 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql @@ -72,3 +72,34 @@ SELECT * FROM slot_decryption_signatures WHERE eon = $1 AND slot = $2 AND tx_pointer = $3 AND identities_hash = $4 ORDER BY keyper_index ASC LIMIT $5; + +-- name: InsertValidatorRegistration :exec +INSERT INTO validator_registrations ( + block_number, + block_hash, + tx_index, + log_index, + validator_index, + nonce, + is_registration +) VALUES ($1, $2, $3, $4, $5, $6, $7); + +-- name: IsValidatorRegistered :one +SELECT is_registration FROM validator_registrations +WHERE validator_index = $1 AND block_number < $1 +ORDER BY block_number DESC, tx_index DESC, log_index DESC +LIMIT 1; + +-- name: SetValidatorRegistrationsSyncedUntil :exec +INSERT INTO validator_registrations_synced_until (block_hash, block_number) VALUES ($1, $2) +ON CONFLICT (enforce_one_row) DO UPDATE +SET block_hash = $1, block_number = $2; + +-- name: GetValidatorRegistrationsSyncedUntil :one +SELECT * FROM validator_registrations_synced_until LIMIT 1; + +-- name: GetValidatorRegistrationNonceBefore :one +SELECT nonce FROM validator_registrations +WHERE validator_index = $1 AND block_number <= $2 AND tx_index <= $3 AND log_index <= $4 +ORDER BY block_number DESC, tx_index DESC, log_index DESC +LIMIT 1; \ No newline at end of file diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql index 0e30b1bcb..ef551998c 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/schemas/gnosiskeyper.sql @@ -48,4 +48,22 @@ CREATE TABLE slot_decryption_signatures( identities_hash bytea NOT NULL, signature bytea NOT NULL, PRIMARY KEY (eon, slot, keyper_index) +); + +CREATE TABLE validator_registrations( + block_number bigint NOT NULL CHECK (block_number >= 0), + block_hash bytea NOT NULL, + tx_index bigint NOT NULL CHECK (tx_index >= 0), + log_index bigint NOT NULL CHECK (log_index >= 0), + validator_index bigint NOT NULL CHECK (validator_index >= 0), + nonce bigint NOT NULL CHECK (nonce >= 0), + is_registration bool NOT NULL, + PRIMARY KEY (block_number, tx_index, log_index) +); +CREATE INDEX idx_validator_index ON validator_registrations (validator_index); + +CREATE TABLE validator_registrations_synced_until( + enforce_one_row bool PRIMARY KEY DEFAULT true, + block_hash bytea NOT NULL, + block_number bigint NOT NULL CHECK (block_number >= 0) ); \ No newline at end of file diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index f415766fb..f1665693a 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog/log" sequencerBindings "github.com/shutter-network/gnosh-contracts/gnoshcontracts/sequencer" + validatorRegistryBindings "github.com/shutter-network/gnosh-contracts/gnoshcontracts/validatorregistry" "golang.org/x/exp/slog" obskeyper "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" @@ -19,6 +20,7 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/epochkghandler" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/kprconfig" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/beaconapiclient" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/broker" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync" syncevent "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" @@ -45,6 +47,7 @@ type Keyper struct { client *ethclient.Client chainSyncClient *chainsync.Client sequencerSyncer *SequencerSyncer + validatorSyncer *ValidatorSyncer eonKeyPublisher *eonkeypublisher.EonKeyPublisher latestTriggeredSlot *uint64 @@ -91,6 +94,32 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { if err != nil { return errors.Wrap(err, "failed to connect to database") } + beaconAPIClient, err := beaconapiclient.New(kpr.config.BeaconAPIURL) + if err != nil { + return errors.Wrap(err, "failed to initialize beacon API client") + } + + validatorSyncerClient, err := ethclient.DialContext(ctx, kpr.config.Gnosis.Node.EthereumURL) + if err != nil { + return errors.Wrap(err, "failed to dial ethereum node") + } + chainID, err := validatorSyncerClient.ChainID(ctx) + if err != nil { + return errors.Wrap(err, "failed to get chain ID") + } + validatorRegistryContract, err := validatorRegistryBindings.NewValidatorregistry( + kpr.config.Gnosis.Contracts.ValidatorRegistry, + validatorSyncerClient, + ) + if err != nil { + return errors.Wrap(err, "failed to instantiate validator registry contract") + } + kpr.validatorSyncer = &ValidatorSyncer{ + Contract: validatorRegistryContract, + DBPool: kpr.dbpool, + BeaconAPIClient: beaconAPIClient, + ChainID: chainID.Uint64(), + } messageSender, err := p2p.New(kpr.config.P2P) if err != nil { @@ -213,8 +242,6 @@ func (kpr *Keyper) ensureSequencerSyncing(ctx context.Context, eon uint64) error GenesisSlotTimestamp: kpr.config.Gnosis.GenesisSlotTimestamp, SecondsPerSlot: kpr.config.Gnosis.SecondsPerSlot, } - - // TODO: perform an initial sync without blocking and/or set start block } if eon < kpr.sequencerSyncer.StartEon { diff --git a/rolling-shutter/keyperimpl/gnosis/newblock.go b/rolling-shutter/keyperimpl/gnosis/newblock.go index f37013591..8a1af2400 100644 --- a/rolling-shutter/keyperimpl/gnosis/newblock.go +++ b/rolling-shutter/keyperimpl/gnosis/newblock.go @@ -13,6 +13,10 @@ func (kpr *Keyper) processNewBlock(ctx context.Context, ev *syncevent.LatestBloc return err } } + err := kpr.validatorSyncer.Sync(ctx, ev.Header) + if err != nil { + return err + } slot := medley.BlockTimestampToSlot( ev.Header.Time, kpr.config.Gnosis.GenesisSlotTimestamp, diff --git a/rolling-shutter/keyperimpl/gnosis/validatorsyncer.go b/rolling-shutter/keyperimpl/gnosis/validatorsyncer.go new file mode 100644 index 000000000..35093a1fe --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/validatorsyncer.go @@ -0,0 +1,225 @@ +package gnosis + +import ( + "context" + "math" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v4/pgxpool" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + registryBindings "github.com/shutter-network/gnosh-contracts/gnoshcontracts/validatorregistry" + blst "github.com/supranational/blst/bindings/go" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/beaconapiclient" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/validatorregistry" +) + +const ( + ValidatorRegistrationMessageVersion = 0 +) + +type ValidatorSyncer struct { + Contract *registryBindings.Validatorregistry + DBPool *pgxpool.Pool + BeaconAPIClient *beaconapiclient.Client + ChainID uint64 +} + +func (v *ValidatorSyncer) Sync(ctx context.Context, header *types.Header) error { + db := database.New(v.DBPool) + syncedUntil, err := db.GetValidatorRegistrationsSyncedUntil(ctx) + if err != nil && err != pgx.ErrNoRows { + return errors.Wrap(err, "failed to query validator registration sync status") + } + var start uint64 + if err == pgx.ErrNoRows { + start = 0 + } else { + start = uint64(syncedUntil.BlockNumber + 1) + } + + log.Debug(). + Uint64("start-block", start). + Uint64("end-block", header.Number.Uint64()). + Msg("syncing validator registry") + + endBlock := header.Number.Uint64() + opts := bind.FilterOpts{ + Start: start, + End: &endBlock, + Context: ctx, + } + it, err := v.Contract.ValidatorregistryFilterer.FilterUpdated(&opts) + if err != nil { + return errors.Wrap(err, "failed to query validator registry update events") + } + events := []*registryBindings.ValidatorregistryUpdated{} + for it.Next() { + events = append(events, it.Event) + } + if it.Error() != nil { + return errors.Wrap(it.Error(), "failed to iterate validator registry update events") + } + if len(events) == 0 { + log.Debug(). + Uint64("start-block", start). + Uint64("end-block", endBlock). + Msg("no validator registry update events found") + } + + filteredEvents, err := v.filterEvents(ctx, events) + if err != nil { + return err + } + return v.DBPool.BeginFunc(ctx, func(tx pgx.Tx) error { + err = v.insertEvents(ctx, tx, filteredEvents) + if err != nil { + return err + } + err = db.SetValidatorRegistrationsSyncedUntil(ctx, database.SetValidatorRegistrationsSyncedUntilParams{ + BlockNumber: int64(endBlock), + BlockHash: header.Hash().Bytes(), + }) + if err != nil { + return err + } + return nil + }) +} + +func (v *ValidatorSyncer) filterEvents( + ctx context.Context, + events []*registryBindings.ValidatorregistryUpdated, +) ([]*registryBindings.ValidatorregistryUpdated, error) { + db := database.New(v.DBPool) + filteredEvents := []*registryBindings.ValidatorregistryUpdated{} + for _, event := range events { + evLog := log.With(). + Hex("block-hash", event.Raw.BlockHash.Bytes()). + Uint64("block-number", event.Raw.BlockNumber). + Uint("tx-index", event.Raw.TxIndex). + Uint("log-index", event.Raw.Index). + Logger() + + msg := new(validatorregistry.RegistrationMessage) + err := msg.Unmarshal(event.Message) + if err != nil { + evLog.Warn(). + Err(err). + Msg("failed to unmarshal registration message") + continue + } + evLog = evLog.With().Uint64("validator-index", msg.ValidatorIndex).Logger() + + if !checkStaticRegistrationMessageFields(msg, v.ChainID, event.Raw.Address, evLog) { + continue + } + + latestNonce, err := db.GetValidatorRegistrationNonceBefore(ctx, database.GetValidatorRegistrationNonceBeforeParams{ + ValidatorIndex: int64(msg.ValidatorIndex), + BlockNumber: int64(event.Raw.BlockNumber), + TxIndex: int64(event.Raw.TxIndex), + LogIndex: int64(event.Raw.Index), + }) + if err != nil && err != pgx.ErrNoRows { + return nil, errors.Wrapf(err, "failed to query latest nonce for validator %d", msg.ValidatorIndex) + } + if err == pgx.ErrNoRows { + latestNonce = -1 + } + if msg.Nonce <= uint64(latestNonce) || msg.Nonce > math.MaxInt64 { + evLog.Warn(). + Uint64("nonce", msg.Nonce). + Int64("latest-nonce", latestNonce). + Msg("ignoring registration message with invalid nonce") + continue + } + + validator, err := v.BeaconAPIClient.GetValidatorByIndex(ctx, "head", msg.ValidatorIndex) + if err != nil { + return nil, errors.Wrapf(err, "failed to get validator %d", msg.ValidatorIndex) + } + if validator == nil { + evLog.Warn().Msg("ignoring registration message for unknown validator") + continue + } + pubkey := &validator.Data.Validator.Pubkey + sig := new(blst.P2Affine).Deserialize(event.Signature) + if sig == nil { + evLog.Warn().Msg("ignoring registration message with invalid signature") + continue + } + validSignature := validatorregistry.VerifySignature(sig, pubkey, msg) + if !validSignature { + evLog.Warn().Msg("ignoring registration message with invalid signature") + continue + } + + filteredEvents = append(filteredEvents, event) + } + return filteredEvents, nil +} + +func (v *ValidatorSyncer) insertEvents(ctx context.Context, tx pgx.Tx, events []*registryBindings.ValidatorregistryUpdated) error { + db := database.New(tx) + for _, event := range events { + msg := new(validatorregistry.RegistrationMessage) + err := msg.Unmarshal(event.Message) + if err != nil { + return errors.Wrap(err, "failed to unmarshal registration message") + } + err = db.InsertValidatorRegistration(ctx, database.InsertValidatorRegistrationParams{ + BlockNumber: int64(event.Raw.BlockNumber), + BlockHash: event.Raw.BlockHash.Bytes(), + TxIndex: int64(event.Raw.TxIndex), + LogIndex: int64(event.Raw.Index), + ValidatorIndex: int64(msg.ValidatorIndex), + Nonce: int64(msg.Nonce), + IsRegistration: msg.IsRegistration, + }) + if err != nil { + return errors.Wrap(err, "failed to insert validator registration into db") + } + } + return nil +} + +func checkStaticRegistrationMessageFields(msg *validatorregistry.RegistrationMessage, chainID uint64, validatorRegistryAddress common.Address, log zerolog.Logger) bool { + if msg.Version != ValidatorRegistrationMessageVersion { + log.Warn(). + Uint8("version", msg.Version). + Uint8("expected-version", ValidatorRegistrationMessageVersion). + Uint64("validator-index", msg.ValidatorIndex). + Msg("ignoring registration message with invalid version") + return false + } + if msg.ChainID != chainID { + log.Warn(). + Uint64("chain-id", msg.ChainID). + Uint64("expected-chain-id", chainID). + Uint64("validator-index", msg.ValidatorIndex). + Msg("ignoring registration message with invalid chain ID") + return false + } + if msg.ValidatorRegistryAddress != validatorRegistryAddress { + log.Warn(). + Hex("validator-registry-address", msg.ValidatorRegistryAddress.Bytes()). + Hex("expected-validator-registry-address", validatorRegistryAddress.Bytes()). + Uint64("validator-index", msg.ValidatorIndex). + Msg("ignoring registration message with invalid validator registry address") + return false + } + if msg.ValidatorIndex > math.MaxInt64 { + log.Warn(). + Uint64("validator-index", msg.ValidatorIndex). + Msg("ignoring registration message with invalid validator index") + return false + } + return true +} diff --git a/rolling-shutter/medley/beaconapiclient/beaconapiclient.go b/rolling-shutter/medley/beaconapiclient/beaconapiclient.go new file mode 100644 index 000000000..58c94ef64 --- /dev/null +++ b/rolling-shutter/medley/beaconapiclient/beaconapiclient.go @@ -0,0 +1,22 @@ +package beaconapiclient + +import ( + "net/http" + "net/url" +) + +type Client struct { + c *http.Client + url *url.URL +} + +func New(rawURL string) (*Client, error) { + parsedURL, err := url.Parse(rawURL) + if err != nil { + return nil, err + } + return &Client{ + c: &http.Client{}, + url: parsedURL, + }, nil +} diff --git a/rolling-shutter/medley/beaconapiclient/getvalidator.go b/rolling-shutter/medley/beaconapiclient/getvalidator.go new file mode 100644 index 000000000..61187d261 --- /dev/null +++ b/rolling-shutter/medley/beaconapiclient/getvalidator.go @@ -0,0 +1,73 @@ +package beaconapiclient + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/pkg/errors" + blst "github.com/supranational/blst/bindings/go" +) + +type GetValidatorByIndexResponse struct { + ExecutionOptimistic bool `json:"execution_optimistic"` + Finalized bool `json:"finalized"` + Data ValidatorData +} + +type ValidatorData struct { + Index uint64 `json:"index,string"` + Balance uint64 `json:"balance,string"` + Status string `json:"status"` + Validator Validator +} + +type Validator struct { + Pubkey blst.P1Affine `json:"pubkey"` + WithdrawalCredentials string `json:"withdrawal_credentials,string"` + EffectiveBalance uint64 `json:"effective_balance,string"` + Slashed bool `json:"slashed"` + ActivationEligibilityEpoch uint64 `json:"activation_eligibility_epoch,string"` + ActivationEpoch uint64 `json:"activation_epoch,string"` + ExitEpoch uint64 `json:"exit_epoch,string"` + WithdrawalEpoch uint64 `json:"withdrawal_epoch,string"` +} + +func (c *Client) GetValidatorByIndex( + ctx context.Context, + stateID string, + validatorIndex uint64, +) (*GetValidatorByIndexResponse, error) { + path := c.url.JoinPath("/eth/v1/beacon/states/", stateID, "/validators/", fmt.Sprint(validatorIndex)) + req, err := http.NewRequestWithContext(ctx, "GET", path.String(), http.NoBody) + if err != nil { + return nil, err + } + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, errors.Wrap(err, "failed to get validator by index from consensus node") + } + defer res.Body.Close() + + if res.StatusCode == http.StatusNotFound { + return nil, nil + } + if res.StatusCode != http.StatusOK { + return nil, errors.Wrap(err, "failed to get validator by index from consensus node") + } + + body, err := io.ReadAll(res.Body) + if err != nil { + return nil, errors.Wrapf(err, "failed to read consensus client response body") + } + + response := new(GetValidatorByIndexResponse) + err = json.Unmarshal(body, response) + if err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal consensus client response body") + } + + return response, nil +} diff --git a/rolling-shutter/medley/validatorregistry/signature.go b/rolling-shutter/medley/validatorregistry/signature.go new file mode 100644 index 000000000..309fef7f8 --- /dev/null +++ b/rolling-shutter/medley/validatorregistry/signature.go @@ -0,0 +1,18 @@ +package validatorregistry + +import ( + "github.com/ethereum/go-ethereum/crypto" + blst "github.com/supranational/blst/bindings/go" +) + +var dst = []byte("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_") + +func VerifySignature(sig *blst.P2Affine, pubkey *blst.P1Affine, msg *RegistrationMessage) bool { + msgHash := crypto.Keccak256(msg.Marshal()) + return sig.Verify(true, pubkey, true, msgHash, dst) +} + +func CreateSignature(sk *blst.SecretKey, msg *RegistrationMessage) *blst.P2Affine { + msgHash := crypto.Keccak256(msg.Marshal()) + return new(blst.P2Affine).Sign(sk, msgHash, dst) +} diff --git a/rolling-shutter/medley/validatorregistry/signature_test.go b/rolling-shutter/medley/validatorregistry/signature_test.go new file mode 100644 index 000000000..442ae4490 --- /dev/null +++ b/rolling-shutter/medley/validatorregistry/signature_test.go @@ -0,0 +1,32 @@ +package validatorregistry + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" + blst "github.com/supranational/blst/bindings/go" +) + +func TestSignature(t *testing.T) { + msg := &RegistrationMessage{ + Version: 1, + ChainID: 2, + ValidatorRegistryAddress: common.HexToAddress("0x1234567890123456789012345678901234567890"), + ValidatorIndex: 3, + Nonce: 4, + IsRegistration: true, + } + + var ikm [32]byte + privkey := blst.KeyGen(ikm[:]) + pubkey := new(blst.P1Affine).From(privkey) + + sig := CreateSignature(privkey, msg) + check := VerifySignature(sig, pubkey, msg) + assert.True(t, check) + + msg.IsRegistration = false + check = VerifySignature(sig, pubkey, msg) + assert.False(t, check) +} diff --git a/rolling-shutter/medley/validatorregistry/validatorregistry.go b/rolling-shutter/medley/validatorregistry/validatorregistry.go new file mode 100644 index 000000000..1d5004a09 --- /dev/null +++ b/rolling-shutter/medley/validatorregistry/validatorregistry.go @@ -0,0 +1,54 @@ +package validatorregistry + +import ( + "encoding/binary" + "fmt" + + "github.com/ethereum/go-ethereum/common" +) + +type RegistrationMessage struct { + Version uint8 + ChainID uint64 + ValidatorRegistryAddress common.Address + ValidatorIndex uint64 + Nonce uint64 + IsRegistration bool +} + +func (m *RegistrationMessage) Marshal() []byte { + b := make([]byte, 0) + b = append(b, m.Version) + b = binary.BigEndian.AppendUint64(b, m.ChainID) + b = append(b, m.ValidatorRegistryAddress.Bytes()...) + b = binary.BigEndian.AppendUint64(b, m.ValidatorIndex) + b = binary.BigEndian.AppendUint64(b, m.Nonce) + if m.IsRegistration { + b = append(b, 1) + } else { + b = append(b, 0) + } + return b +} + +func (m *RegistrationMessage) Unmarshal(b []byte) error { + expectedLength := 1 + 8 + 20 + 8 + 8 + 1 + if len(b) != expectedLength { + return fmt.Errorf("invalid registration message length %d, expected %d", len(b), expectedLength) + } + + m.Version = b[0] + m.ChainID = binary.BigEndian.Uint64(b[1:9]) + m.ValidatorRegistryAddress = common.BytesToAddress(b[9:29]) + m.ValidatorIndex = binary.BigEndian.Uint64(b[29:37]) + m.Nonce = binary.BigEndian.Uint64(b[37:45]) + switch b[45] { + case 0: + m.IsRegistration = false + case 1: + m.IsRegistration = true + default: + return fmt.Errorf("invalid registration message type byte %d", b[45]) + } + return nil +} diff --git a/rolling-shutter/medley/validatorregistry/validatorregistry_test.go b/rolling-shutter/medley/validatorregistry/validatorregistry_test.go new file mode 100644 index 000000000..d49651095 --- /dev/null +++ b/rolling-shutter/medley/validatorregistry/validatorregistry_test.go @@ -0,0 +1,47 @@ +package validatorregistry + +import ( + "bytes" + "testing" + + "github.com/ethereum/go-ethereum/common" + "gotest.tools/v3/assert" +) + +func TestRegistrationMessageMarshalRoundtrip(t *testing.T) { + m := &RegistrationMessage{ + Version: 1, + ChainID: 2, + ValidatorRegistryAddress: common.HexToAddress("0x1234567890123456789012345678901234567890"), + ValidatorIndex: 3, + Nonce: 4, + IsRegistration: true, + } + marshaled := m.Marshal() + unmarshaled := new(RegistrationMessage) + err := unmarshaled.Unmarshal(marshaled) + assert.NilError(t, err) + assert.DeepEqual(t, m, unmarshaled) +} + +func TestRegistrationMessageInvalidUnmarshal(t *testing.T) { + base := bytes.Repeat([]byte{0}, 46) + assert.NilError(t, new(RegistrationMessage).Unmarshal(base)) + + for _, b := range [][]byte{ + {}, + bytes.Repeat([]byte{0}, 45), + bytes.Repeat([]byte{0}, 47), + bytes.Repeat([]byte{0}, 92), + } { + err := new(RegistrationMessage).Unmarshal(b) + assert.ErrorContains(t, err, "invalid registration message length") + } + + for _, isRegistrationByte := range []byte{2, 3, 255} { + b := bytes.Repeat([]byte{0}, 46) + b[45] = isRegistrationByte + err := new(RegistrationMessage).Unmarshal(b) + assert.ErrorContains(t, err, "invalid registration message type byte") + } +} From 7cc95564ef0f84500bbb31eff5d7c2523839f990 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Wed, 8 May 2024 00:08:50 +0200 Subject: [PATCH 66/91] Only generate keys for registered proposers --- rolling-shutter/keyperimpl/gnosis/config.go | 3 + rolling-shutter/keyperimpl/gnosis/keyper.go | 13 ++-- rolling-shutter/keyperimpl/gnosis/newslot.go | 63 ++++++++++++++--- .../beaconapiclient/getproposerduties.go | 69 +++++++++++++++++++ rolling-shutter/medley/slots.go | 4 ++ 5 files changed, 137 insertions(+), 15 deletions(-) create mode 100644 rolling-shutter/medley/beaconapiclient/getproposerduties.go diff --git a/rolling-shutter/keyperimpl/gnosis/config.go b/rolling-shutter/keyperimpl/gnosis/config.go index acea8964c..f650fdb85 100644 --- a/rolling-shutter/keyperimpl/gnosis/config.go +++ b/rolling-shutter/keyperimpl/gnosis/config.go @@ -100,6 +100,7 @@ type GnosisConfig struct { EncryptedGasLimit uint64 `shconfig:",required"` MinGasPerTransaction uint64 `shconfig:",required"` SecondsPerSlot uint64 `shconfig:",required"` + SlotsPerEpoch uint64 `shconfig:",required"` GenesisSlotTimestamp uint64 `shconfig:",required"` } @@ -110,6 +111,7 @@ func NewGnosisConfig() *GnosisConfig { EncryptedGasLimit: 0, MinGasPerTransaction: 0, SecondsPerSlot: 0, + SlotsPerEpoch: 0, GenesisSlotTimestamp: 0, } c.Init() @@ -140,6 +142,7 @@ func (c *GnosisConfig) SetExampleValues() error { c.EncryptedGasLimit = 1_000_000 c.MinGasPerTransaction = 21_000 c.SecondsPerSlot = 5 + c.SlotsPerEpoch = 16 c.GenesisSlotTimestamp = 1665410700 return nil } diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index f1665693a..07544eb97 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -41,10 +41,11 @@ const ( ) type Keyper struct { - core *keyper.KeyperCore - config *Config - dbpool *pgxpool.Pool - client *ethclient.Client + core *keyper.KeyperCore + config *Config + dbpool *pgxpool.Pool + beaconAPIClient *beaconapiclient.Client + chainSyncClient *chainsync.Client sequencerSyncer *SequencerSyncer validatorSyncer *ValidatorSyncer @@ -94,7 +95,7 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { if err != nil { return errors.Wrap(err, "failed to connect to database") } - beaconAPIClient, err := beaconapiclient.New(kpr.config.BeaconAPIURL) + kpr.beaconAPIClient, err = beaconapiclient.New(kpr.config.BeaconAPIURL) if err != nil { return errors.Wrap(err, "failed to initialize beacon API client") } @@ -117,7 +118,7 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { kpr.validatorSyncer = &ValidatorSyncer{ Contract: validatorRegistryContract, DBPool: kpr.dbpool, - BeaconAPIClient: beaconAPIClient, + BeaconAPIClient: kpr.beaconAPIClient, ChainID: chainID.Uint64(), } diff --git a/rolling-shutter/keyperimpl/gnosis/newslot.go b/rolling-shutter/keyperimpl/gnosis/newslot.go index 2b680a7a1..d85844209 100644 --- a/rolling-shutter/keyperimpl/gnosis/newslot.go +++ b/rolling-shutter/keyperimpl/gnosis/newslot.go @@ -16,6 +16,7 @@ import ( corekeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/epochkghandler" gnosisdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/broker" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/slotticker" @@ -72,16 +73,60 @@ func (kpr *Keyper) maybeTriggerDecryption(ctx context.Context, slot uint64) erro if err != nil { return errors.Wrapf(err, "failed to query keyper set for block %d", nextBlock) } - if keyperSet.Contains(kpr.config.GetAddress()) { - return kpr.triggerDecryption(ctx, slot, nextBlock, &keyperSet) + + // don't trigger if we're not part of the keyper set + if !keyperSet.Contains(kpr.config.GetAddress()) { + log.Debug(). + Uint64("slot", slot). + Int64("block-number", nextBlock). + Int64("keyper-set-index", keyperSet.KeyperConfigIndex). + Str("address", kpr.config.GetAddress().Hex()). + Msg("skipping slot as not part of keyper set") + return nil } - log.Debug(). - Uint64("slot", slot). - Int64("block-number", nextBlock). - Int64("keyper-set-index", keyperSet.KeyperConfigIndex). - Str("address", kpr.config.GetAddress().Hex()). - Msg("skipping slot as not part of keyper set") - return nil + + // don't trigger if the block proposer is not part of the validator registry + isRegistered, err := kpr.isProposerRegistered(ctx, slot) + if err != nil { + return err + } + if !isRegistered { + log.Debug(). + Uint64("slot", slot). + Msg("skipping slot as proposer is not registered") + return nil + } + + return kpr.triggerDecryption(ctx, slot, nextBlock, &keyperSet) +} + +func (kpr *Keyper) isProposerRegistered(ctx context.Context, slot uint64) (bool, error) { + epoch := medley.SlotToEpoch(slot, kpr.config.Gnosis.SlotsPerEpoch) + proposerDuties, err := kpr.beaconAPIClient.GetProposerDutiesByEpoch(ctx, epoch) + if err != nil { + return false, err + } + if proposerDuties == nil { + return false, errors.Errorf("no proposer duties found for slot %d in epoch %d", slot, epoch) + } + proposerDuty, err := proposerDuties.GetDutyForSlot(slot) + if err != nil { + return false, err + } + proposerIndex := proposerDuty.ValidatorIndex + if proposerIndex > math.MaxInt64 { + return false, errors.New("proposer index too big") + } + + db := gnosisdatabase.New(kpr.dbpool) + isRegistered, err := db.IsValidatorRegistered(ctx, int64(proposerDuty.ValidatorIndex)) + if err == pgx.ErrNoRows { + return false, nil + } + if err != nil { + return false, errors.Wrapf(err, "failed to query registration status for validator %d", proposerDuty.ValidatorIndex) + } + return isRegistered, nil } func (kpr *Keyper) getTxPointer(ctx context.Context, eon int64, slot int64, keyperConfigIndex int64) (int64, error) { diff --git a/rolling-shutter/medley/beaconapiclient/getproposerduties.go b/rolling-shutter/medley/beaconapiclient/getproposerduties.go new file mode 100644 index 000000000..732166a95 --- /dev/null +++ b/rolling-shutter/medley/beaconapiclient/getproposerduties.go @@ -0,0 +1,69 @@ +package beaconapiclient + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/pkg/errors" +) + +type GetProposerDutiesResponse struct { + ExecutionOptimistic bool `json:"execution_optimistic"` + DependentRoot string `json:"dependent_root"` + Data []ProposerDuty `json:"data"` +} + +type ProposerDuty struct { + // Pubkey blst.P1Affine `json:"pubkey"` + Pubkey string `json:"pubkey"` + ValidatorIndex uint64 `json:"validator_index,string"` + Slot uint64 `json:"slot,string"` +} + +func (c *Client) GetProposerDutiesByEpoch( + ctx context.Context, + epoch uint64, +) (*GetProposerDutiesResponse, error) { + path := c.url.JoinPath("/eth/v1/validator/duties/proposer/", fmt.Sprint(epoch)) + req, err := http.NewRequestWithContext(ctx, "GET", path.String(), http.NoBody) + if err != nil { + return nil, err + } + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, errors.Wrapf(err, "failed to get proposer duties for epoch %d from consensus node", epoch) + } + defer res.Body.Close() + + if res.StatusCode == http.StatusNotFound { + return nil, nil + } + if res.StatusCode != http.StatusOK { + return nil, errors.Wrapf(err, "failed to get proposer duties for epoch %d from consensus node", epoch) + } + + body, err := io.ReadAll(res.Body) + if err != nil { + return nil, errors.Wrapf(err, "failed to read consensus client response body") + } + + response := new(GetProposerDutiesResponse) + err = json.Unmarshal(body, response) + if err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal consensus client response body") + } + + return response, nil +} + +func (r *GetProposerDutiesResponse) GetDutyForSlot(slot uint64) (ProposerDuty, error) { + for _, duty := range r.Data { + if duty.Slot == slot { + return duty, nil + } + } + return ProposerDuty{}, errors.Errorf("consensus client response does not contain duty for slot %d", slot) +} diff --git a/rolling-shutter/medley/slots.go b/rolling-shutter/medley/slots.go index cd866300e..41599c9b1 100644 --- a/rolling-shutter/medley/slots.go +++ b/rolling-shutter/medley/slots.go @@ -3,3 +3,7 @@ package medley func BlockTimestampToSlot(blockTimestamp uint64, genesisSlotTimestamp uint64, secondsPerSlot uint64) uint64 { return (blockTimestamp - genesisSlotTimestamp) / secondsPerSlot } + +func SlotToEpoch(slot uint64, slotsPerEpoch uint64) uint64 { + return slot / slotsPerEpoch +} From 53dcd36e189a16f5b55d83cd79c18a76e2067deb Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Tue, 14 May 2024 19:55:31 +0200 Subject: [PATCH 67/91] Fix slot decryption signature computation Now we use SSZ as required by the spec. Before we did an easier encoding as a placeholder. --- rolling-shutter/go.mod | 1 + rolling-shutter/go.sum | 4 + .../slotdecryptionsignatures.go | 75 ++++++ .../slotdecryptionsignatures_encoding.go | 219 ++++++++++++++++++ rolling-shutter/keyperimpl/gnosis/handlers.go | 35 +-- .../keyperimpl/gnosis/messagingmiddleware.go | 18 +- .../gnosis/slotdecryptionsignatures.go | 51 ---- 7 files changed, 331 insertions(+), 72 deletions(-) create mode 100644 rolling-shutter/keyperimpl/gnosis/gnosisssztypes/slotdecryptionsignatures.go create mode 100644 rolling-shutter/keyperimpl/gnosis/gnosisssztypes/slotdecryptionsignatures_encoding.go delete mode 100644 rolling-shutter/keyperimpl/gnosis/slotdecryptionsignatures.go diff --git a/rolling-shutter/go.mod b/rolling-shutter/go.mod index 51507ad50..6b25fa818 100644 --- a/rolling-shutter/go.mod +++ b/rolling-shutter/go.mod @@ -10,6 +10,7 @@ require ( github.com/bitwurx/jrpc2 v0.0.0-20220302204700-52c6dbbeb536 github.com/deepmap/oapi-codegen v1.9.1 github.com/ethereum/go-ethereum v1.13.11 + github.com/ferranbt/fastssz v0.1.3 github.com/getkin/kin-openapi v0.87.0 github.com/go-chi/chi/v5 v5.0.10 github.com/google/go-cmp v0.6.0 diff --git a/rolling-shutter/go.sum b/rolling-shutter/go.sum index 6e3d2b3f5..ff321cb8a 100644 --- a/rolling-shutter/go.sum +++ b/rolling-shutter/go.sum @@ -210,6 +210,8 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojt github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= +github.com/ferranbt/fastssz v0.1.3 h1:ZI+z3JH05h4kgmFXdHuR1aWYsgrg7o+Fw7/NCzM16Mo= +github.com/ferranbt/fastssz v0.1.3/go.mod h1:0Y9TEd/9XuFlh7mskMPfXiI2Dkw4Ddg9EyXt1W7MRvE= github.com/fjl/memsize v0.0.1 h1:+zhkb+dhUgx0/e+M8sF0QqiouvMQUiKR+QYvdxIOKcQ= github.com/fjl/memsize v0.0.1/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= @@ -917,6 +919,8 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= github.com/ulope/jrpc2 v0.0.0-20230706135348-a95cf3d96bd2 h1:rk0z/6CEJbstiHqv8+4ZIMv4Sm2zBZ5v5C56P8JXd+I= github.com/ulope/jrpc2 v0.0.0-20230706135348-a95cf3d96bd2/go.mod h1:bzOCUO4YLqjPZbPM4jeZzu/WIEauP/ouNWLRysNQdc0= +github.com/umbracle/gohashtree v0.0.2-alpha.0.20230207094856-5b775a815c10 h1:CQh33pStIp/E30b7TxDlXfM0145bn2e8boI30IxAhTg= +github.com/umbracle/gohashtree v0.0.2-alpha.0.20230207094856-5b775a815c10/go.mod h1:x/Pa0FF5Te9kdrlZKJK82YmAkvL8+f989USgz6Jiw7M= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.10 h1:p8Fspmz3iTctJstry1PYS3HVdllxnEzTEsgIgtxTrCk= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= diff --git a/rolling-shutter/keyperimpl/gnosis/gnosisssztypes/slotdecryptionsignatures.go b/rolling-shutter/keyperimpl/gnosis/gnosisssztypes/slotdecryptionsignatures.go new file mode 100644 index 000000000..2d8e20fc8 --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/gnosisssztypes/slotdecryptionsignatures.go @@ -0,0 +1,75 @@ +// This package contains SSZ-encodable types used by the Gnosis keyper. +// The encodings are automatically generated using FastSSZ (https://github.com/ferranbt/fastssz). +// Command: `$ go run sszgen/*.go --path ` +package gnosisssztypes + +import ( + "crypto/ecdsa" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/pkg/errors" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" +) + +type IdentityPreimage struct { + Bytes []byte `ssz-size:"52"` +} + +type SlotDecryptionSignatureData struct { + InstanceID uint64 + Eon uint64 + Slot uint64 + TxPointer uint64 + IdentityPreimages []IdentityPreimage `ssz-max:"1024"` +} + +func NewSlotDecryptionSignatureData( + instanceID uint64, + eon uint64, + slot uint64, + txPointer uint64, + identityPreimages []identitypreimage.IdentityPreimage, +) (*SlotDecryptionSignatureData, error) { + if len(identityPreimages) > 1024 { + return nil, errors.New("too many identity preimages") + } + + wrappedPreimages := []IdentityPreimage{} + for _, preimage := range identityPreimages { + wrappedPreimage := IdentityPreimage{ + Bytes: preimage.Bytes(), + } + wrappedPreimages = append(wrappedPreimages, wrappedPreimage) + } + + return &SlotDecryptionSignatureData{ + InstanceID: instanceID, + Eon: eon, + Slot: slot, + TxPointer: txPointer, + IdentityPreimages: wrappedPreimages, + }, nil +} + +func (d *SlotDecryptionSignatureData) ComputeSignature(key *ecdsa.PrivateKey) ([]byte, error) { + h, err := d.HashTreeRoot() + if err != nil { + return nil, errors.Wrap(err, "failed to compute hash tree root of slot decryption signature data") + } + return crypto.Sign(h[:], key) +} + +func (d *SlotDecryptionSignatureData) CheckSignature(signature []byte, address common.Address) (bool, error) { + h, err := d.HashTreeRoot() + if err != nil { + return false, errors.Wrap(err, "failed to compute hash tree root of slot decryption signature data") + } + signerPubkey, err := crypto.SigToPub(h[:], signature) + if err != nil { + return false, errors.Wrap(err, "failed to recover public key from slot decryption signature") + } + signerAddress := crypto.PubkeyToAddress(*signerPubkey) + return signerAddress == address, nil +} diff --git a/rolling-shutter/keyperimpl/gnosis/gnosisssztypes/slotdecryptionsignatures_encoding.go b/rolling-shutter/keyperimpl/gnosis/gnosisssztypes/slotdecryptionsignatures_encoding.go new file mode 100644 index 000000000..3c758b472 --- /dev/null +++ b/rolling-shutter/keyperimpl/gnosis/gnosisssztypes/slotdecryptionsignatures_encoding.go @@ -0,0 +1,219 @@ +// Code generated by fastssz. DO NOT EDIT. +// Hash: f106749778e6a2208c4b5f0385bccdaa3d89bf4a4c5d96c0e60aa690e7c750be +// Version: 0.1.3 +package gnosisssztypes + +import ( + ssz "github.com/ferranbt/fastssz" +) + +// MarshalSSZ ssz marshals the IdentityPreimage object +func (i *IdentityPreimage) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(i) +} + +// MarshalSSZTo ssz marshals the IdentityPreimage object to a target array +func (i *IdentityPreimage) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'Bytes' + if size := len(i.Bytes); size != 52 { + err = ssz.ErrBytesLengthFn("IdentityPreimage.Bytes", size, 52) + return + } + dst = append(dst, i.Bytes...) + + return +} + +// UnmarshalSSZ ssz unmarshals the IdentityPreimage object +func (i *IdentityPreimage) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 52 { + return ssz.ErrSize + } + + // Field (0) 'Bytes' + if cap(i.Bytes) == 0 { + i.Bytes = make([]byte, 0, len(buf[0:52])) + } + i.Bytes = append(i.Bytes, buf[0:52]...) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the IdentityPreimage object +func (i *IdentityPreimage) SizeSSZ() (size int) { + size = 52 + return +} + +// HashTreeRoot ssz hashes the IdentityPreimage object +func (i *IdentityPreimage) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(i) +} + +// HashTreeRootWith ssz hashes the IdentityPreimage object with a hasher +func (i *IdentityPreimage) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Bytes' + if size := len(i.Bytes); size != 52 { + err = ssz.ErrBytesLengthFn("IdentityPreimage.Bytes", size, 52) + return + } + hh.PutBytes(i.Bytes) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the IdentityPreimage object +func (i *IdentityPreimage) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(i) +} + +// MarshalSSZ ssz marshals the SlotDecryptionSignatureData object +func (s *SlotDecryptionSignatureData) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(s) +} + +// MarshalSSZTo ssz marshals the SlotDecryptionSignatureData object to a target array +func (s *SlotDecryptionSignatureData) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(36) + + // Field (0) 'InstanceID' + dst = ssz.MarshalUint64(dst, s.InstanceID) + + // Field (1) 'Eon' + dst = ssz.MarshalUint64(dst, s.Eon) + + // Field (2) 'Slot' + dst = ssz.MarshalUint64(dst, s.Slot) + + // Field (3) 'TxPointer' + dst = ssz.MarshalUint64(dst, s.TxPointer) + + // Offset (4) 'IdentityPreimages' + dst = ssz.WriteOffset(dst, offset) + + // Field (4) 'IdentityPreimages' + if size := len(s.IdentityPreimages); size > 1024 { + err = ssz.ErrListTooBigFn("SlotDecryptionSignatureData.IdentityPreimages", size, 1024) + return + } + for ii := 0; ii < len(s.IdentityPreimages); ii++ { + if dst, err = s.IdentityPreimages[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + return +} + +// UnmarshalSSZ ssz unmarshals the SlotDecryptionSignatureData object +func (s *SlotDecryptionSignatureData) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 36 { + return ssz.ErrSize + } + + tail := buf + var o4 uint64 + + // Field (0) 'InstanceID' + s.InstanceID = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'Eon' + s.Eon = ssz.UnmarshallUint64(buf[8:16]) + + // Field (2) 'Slot' + s.Slot = ssz.UnmarshallUint64(buf[16:24]) + + // Field (3) 'TxPointer' + s.TxPointer = ssz.UnmarshallUint64(buf[24:32]) + + // Offset (4) 'IdentityPreimages' + if o4 = ssz.ReadOffset(buf[32:36]); o4 > size { + return ssz.ErrOffset + } + + if o4 < 36 { + return ssz.ErrInvalidVariableOffset + } + + // Field (4) 'IdentityPreimages' + { + buf = tail[o4:] + num, err := ssz.DivideInt2(len(buf), 52, 1024) + if err != nil { + return err + } + s.IdentityPreimages = make([]IdentityPreimage, num) + for ii := 0; ii < num; ii++ { + if err = s.IdentityPreimages[ii].UnmarshalSSZ(buf[ii*52 : (ii+1)*52]); err != nil { + return err + } + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the SlotDecryptionSignatureData object +func (s *SlotDecryptionSignatureData) SizeSSZ() (size int) { + size = 36 + + // Field (4) 'IdentityPreimages' + size += len(s.IdentityPreimages) * 52 + + return +} + +// HashTreeRoot ssz hashes the SlotDecryptionSignatureData object +func (s *SlotDecryptionSignatureData) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(s) +} + +// HashTreeRootWith ssz hashes the SlotDecryptionSignatureData object with a hasher +func (s *SlotDecryptionSignatureData) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'InstanceID' + hh.PutUint64(s.InstanceID) + + // Field (1) 'Eon' + hh.PutUint64(s.Eon) + + // Field (2) 'Slot' + hh.PutUint64(s.Slot) + + // Field (3) 'TxPointer' + hh.PutUint64(s.TxPointer) + + // Field (4) 'IdentityPreimages' + { + subIndx := hh.Index() + num := uint64(len(s.IdentityPreimages)) + if num > 1024 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range s.IdentityPreimages { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 1024) + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the SlotDecryptionSignatureData object +func (s *SlotDecryptionSignatureData) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(s) +} diff --git a/rolling-shutter/keyperimpl/gnosis/handlers.go b/rolling-shutter/keyperimpl/gnosis/handlers.go index 22ab84f1e..9dfe35023 100644 --- a/rolling-shutter/keyperimpl/gnosis/handlers.go +++ b/rolling-shutter/keyperimpl/gnosis/handlers.go @@ -13,6 +13,7 @@ import ( obskeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" corekeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/gnosisssztypes" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" "github.com/shutter-network/rolling-shutter/rolling-shutter/p2pmsg" "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" @@ -76,14 +77,17 @@ func (h *DecryptionKeySharesHandler) ValidateMessage(ctx context.Context, msg p2 identityPreimage := identitypreimage.IdentityPreimage(share.EpochID) identityPreimages = append(identityPreimages, identityPreimage) } - slotDecryptionSignatureData := SlotDecryptionSignatureData{ - InstanceID: keyShares.InstanceID, - Eon: keyShares.Eon, - Slot: extra.Gnosis.Slot, - TxPointer: extra.Gnosis.TxPointer, - IdentityPreimages: identityPreimages, + slotDecryptionSignatureData, err := gnosisssztypes.NewSlotDecryptionSignatureData( + keyShares.InstanceID, + keyShares.Eon, + extra.Gnosis.Slot, + extra.Gnosis.TxPointer, + identityPreimages, + ) + if err != nil { + return pubsub.ValidationReject, errors.Wrap(err, "failed to create slot decryption signature data object") } - signatureValid, err := CheckSlotDecryptionSignature(&slotDecryptionSignatureData, extra.Gnosis.Signature, keyperAddress) + signatureValid, err := slotDecryptionSignatureData.CheckSignature(extra.Gnosis.Signature, keyperAddress) if err != nil { return pubsub.ValidationReject, errors.Wrap(err, "failed to check slot decryption signature") } @@ -247,17 +251,20 @@ func (h *DecryptionKeysHandler) ValidateMessage(ctx context.Context, msg p2pmsg. identityPreimage := identitypreimage.IdentityPreimage(key.Identity) identityPreimages = append(identityPreimages, identityPreimage) } - slotDecryptionSignatureData := SlotDecryptionSignatureData{ - InstanceID: keys.InstanceID, - Eon: keys.Eon, - Slot: extra.Gnosis.Slot, - TxPointer: extra.Gnosis.TxPointer, - IdentityPreimages: identityPreimages, + slotDecryptionSignatureData, err := gnosisssztypes.NewSlotDecryptionSignatureData( + keys.InstanceID, + keys.Eon, + extra.Gnosis.Slot, + extra.Gnosis.TxPointer, + identityPreimages, + ) + if err != nil { + return pubsub.ValidationReject, errors.Wrap(err, "failed to create slot decryption signature data object") } for signatureIndex := 0; signatureIndex < len(extra.Gnosis.Signatures); signatureIndex++ { signature := extra.Gnosis.Signatures[signatureIndex] signer := signers[signatureIndex] - signatureValid, err := CheckSlotDecryptionSignature(&slotDecryptionSignatureData, signature, signer) + signatureValid, err := slotDecryptionSignatureData.CheckSignature(signature, signer) if err != nil { return pubsub.ValidationReject, errors.Wrap(err, "failed to check slot decryption signature") } diff --git a/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go b/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go index 239330ebf..2cc802d6f 100644 --- a/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go +++ b/rolling-shutter/keyperimpl/gnosis/messagingmiddleware.go @@ -14,6 +14,7 @@ import ( obskeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" corekeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/gnosisssztypes" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/retry" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" @@ -139,14 +140,17 @@ func (i *MessagingMiddleware) interceptDecryptionKeyShares( for _, share := range originalMsg.Shares { identityPreimages = append(identityPreimages, identitypreimage.IdentityPreimage(share.EpochID)) } - slotDecryptionSignatureData := SlotDecryptionSignatureData{ - InstanceID: i.config.InstanceID, - Eon: originalMsg.Eon, - Slot: uint64(currentDecryptionTrigger.Slot), - TxPointer: uint64(currentDecryptionTrigger.TxPointer), - IdentityPreimages: identityPreimages, + slotDecryptionSignatureData, err := gnosisssztypes.NewSlotDecryptionSignatureData( + i.config.InstanceID, + originalMsg.Eon, + uint64(currentDecryptionTrigger.Slot), + uint64(currentDecryptionTrigger.TxPointer), + identityPreimages, + ) + if err != nil { + return nil, err } - signature, err := ComputeSlotDecryptionSignature(&slotDecryptionSignatureData, i.config.Gnosis.Node.PrivateKey.Key) + signature, err := slotDecryptionSignatureData.ComputeSignature(i.config.Gnosis.Node.PrivateKey.Key) if err != nil { return nil, errors.Wrapf(err, "failed to compute slot decryption signature") } diff --git a/rolling-shutter/keyperimpl/gnosis/slotdecryptionsignatures.go b/rolling-shutter/keyperimpl/gnosis/slotdecryptionsignatures.go deleted file mode 100644 index 1498d0f65..000000000 --- a/rolling-shutter/keyperimpl/gnosis/slotdecryptionsignatures.go +++ /dev/null @@ -1,51 +0,0 @@ -package gnosis - -import ( - "bytes" - "crypto/ecdsa" - "encoding/binary" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - - "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" -) - -type SlotDecryptionSignatureData struct { - InstanceID uint64 - Eon uint64 - Slot uint64 - TxPointer uint64 - IdentityPreimages []identitypreimage.IdentityPreimage -} - -func HashSlotDecryptionSignatureData(data *SlotDecryptionSignatureData) common.Hash { - // all fields are fixed size and identity preimages are ordered, so there is no malleability if we just append all fields - buf := new(bytes.Buffer) - _ = binary.Write(buf, binary.BigEndian, data.InstanceID) - _ = binary.Write(buf, binary.BigEndian, data.Eon) - _ = binary.Write(buf, binary.BigEndian, data.Slot) - _ = binary.Write(buf, binary.BigEndian, data.TxPointer) - for _, preimage := range data.IdentityPreimages { - _ = binary.Write(buf, binary.BigEndian, preimage) - } - return crypto.Keccak256Hash(buf.Bytes()) -} - -func ComputeSlotDecryptionSignature( - data *SlotDecryptionSignatureData, - key *ecdsa.PrivateKey, -) ([]byte, error) { - h := HashSlotDecryptionSignatureData(data) - return crypto.Sign(h.Bytes(), key) -} - -func CheckSlotDecryptionSignature(data *SlotDecryptionSignatureData, signature []byte, address common.Address) (bool, error) { - h := HashSlotDecryptionSignatureData(data) - signerPubkey, err := crypto.SigToPub(h.Bytes(), signature) - if err != nil { - return false, err - } - signerAddress := crypto.PubkeyToAddress(*signerPubkey) - return signerAddress == address, nil -} From c3ef527b540211af2808300c92b247e624e7ad28 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Sun, 19 May 2024 22:21:06 +0200 Subject: [PATCH 68/91] Update tx pointer age on skipped slots --- .../gnosis/database/gnosiskeyper.sqlc.gen.go | 16 ++++++++++++++++ .../gnosis/database/sql/queries/gnosiskeyper.sql | 5 +++++ rolling-shutter/keyperimpl/gnosis/newslot.go | 9 +++++++++ 3 files changed, 30 insertions(+) diff --git a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go index 99f65e047..bfdce6748 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go +++ b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go @@ -406,6 +406,22 @@ func (q *Queries) SetTxPointer(ctx context.Context, arg SetTxPointerParams) erro return err } +const setTxPointerSlot = `-- name: SetTxPointerSlot :exec +UPDATE tx_pointer +SET slot = $2 +WHERE eon = $1 +` + +type SetTxPointerSlotParams struct { + Eon int64 + Slot int64 +} + +func (q *Queries) SetTxPointerSlot(ctx context.Context, arg SetTxPointerSlotParams) error { + _, err := q.db.Exec(ctx, setTxPointerSlot, arg.Eon, arg.Slot) + return err +} + const setValidatorRegistrationsSyncedUntil = `-- name: SetValidatorRegistrationsSyncedUntil :exec INSERT INTO validator_registrations_synced_until (block_hash, block_number) VALUES ($1, $2) ON CONFLICT (enforce_one_row) DO UPDATE diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql index 31dc1a48f..7bf4d6b96 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql @@ -53,6 +53,11 @@ VALUES ($1, $2, $3) ON CONFLICT (eon) DO UPDATE SET slot = $2, value = $3; +-- name: SetTxPointerSlot :exec +UPDATE tx_pointer +SET slot = $2 +WHERE eon = $1; + -- name: SetCurrentDecryptionTrigger :exec INSERT INTO current_decryption_trigger (eon, slot, tx_pointer, identities_hash) VALUES ($1, $2, $3, $4) diff --git a/rolling-shutter/keyperimpl/gnosis/newslot.go b/rolling-shutter/keyperimpl/gnosis/newslot.go index d85844209..2d8731161 100644 --- a/rolling-shutter/keyperimpl/gnosis/newslot.go +++ b/rolling-shutter/keyperimpl/gnosis/newslot.go @@ -94,6 +94,15 @@ func (kpr *Keyper) maybeTriggerDecryption(ctx context.Context, slot uint64) erro log.Debug(). Uint64("slot", slot). Msg("skipping slot as proposer is not registered") + // Even if we don't trigger decryption, we still need to update the tx pointer or it will + // become outdated. + err := gnosisKeyperDB.SetTxPointerSlot(ctx, gnosisdatabase.SetTxPointerSlotParams{ + Eon: keyperSet.KeyperConfigIndex, + Slot: int64(slot), + }) + if err != nil { + return errors.Wrap(err, "failed to update tx pointer slot") + } return nil } From cef8e4d8964999cdcbcf341a1571db21c51ccebf Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Sun, 19 May 2024 22:22:57 +0200 Subject: [PATCH 69/91] Fix eon column in tx pointer table Here, the eon refers to the keyper config index, not the keyper-internal eon. Note that the tx pointer is also read by other parties including validators, so using the proper eon (from Shuttermint) which is only meaningful in a keyper DKG context does not make sense. We should rename the message field in the spec at some point. --- rolling-shutter/keyperimpl/gnosis/newslot.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rolling-shutter/keyperimpl/gnosis/newslot.go b/rolling-shutter/keyperimpl/gnosis/newslot.go index 2d8731161..13a63bcb7 100644 --- a/rolling-shutter/keyperimpl/gnosis/newslot.go +++ b/rolling-shutter/keyperimpl/gnosis/newslot.go @@ -211,16 +211,16 @@ func (kpr *Keyper) triggerDecryption( if err != nil { return errors.Wrapf(err, "failed to query eon for block number %d from db", nextBlock) } - eon := eonStruct.Eon + keyperConfigIndex := eonStruct.KeyperConfigIndex - txPointer, err := kpr.getTxPointer(ctx, eon, int64(slot), keyperSet.KeyperConfigIndex) + txPointer, err := kpr.getTxPointer(ctx, keyperConfigIndex, int64(slot), keyperSet.KeyperConfigIndex) if err == errZeroTxPointerAge { log.Warn(). Uint64("slot", slot). Int64("block-number", nextBlock). - Int64("eon", eon). + Int64("eon", keyperConfigIndex). Int64("tx-pointer", txPointer). - Msg("skipping new block as tx pointer age is 0") + Msg("skipping trigger as tx pointer age is 0") return nil } else if err != nil { return err @@ -231,7 +231,7 @@ func (kpr *Keyper) triggerDecryption( return err } err = gnosisKeyperDB.SetCurrentDecryptionTrigger(ctx, gnosisdatabase.SetCurrentDecryptionTriggerParams{ - Eon: eon, + Eon: keyperConfigIndex, Slot: int64(slot), TxPointer: txPointer, IdentitiesHash: computeIdentitiesHash(identityPreimages), From dcc826b8c8fe006ddf78331037084ed19ba8ead1 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Sun, 19 May 2024 22:28:50 +0200 Subject: [PATCH 70/91] Fix is proposer check The block parameter was broken and not supplied. --- .../gnosis/database/gnosiskeyper.sqlc.gen.go | 11 ++++++++--- .../gnosis/database/sql/queries/gnosiskeyper.sql | 2 +- rolling-shutter/keyperimpl/gnosis/newslot.go | 5 ++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go index bfdce6748..f14d0b4ac 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go +++ b/rolling-shutter/keyperimpl/gnosis/database/gnosiskeyper.sqlc.gen.go @@ -318,13 +318,18 @@ func (q *Queries) InsertValidatorRegistration(ctx context.Context, arg InsertVal const isValidatorRegistered = `-- name: IsValidatorRegistered :one SELECT is_registration FROM validator_registrations -WHERE validator_index = $1 AND block_number < $1 +WHERE validator_index = $1 AND block_number < $2 ORDER BY block_number DESC, tx_index DESC, log_index DESC LIMIT 1 ` -func (q *Queries) IsValidatorRegistered(ctx context.Context, validatorIndex int64) (bool, error) { - row := q.db.QueryRow(ctx, isValidatorRegistered, validatorIndex) +type IsValidatorRegisteredParams struct { + ValidatorIndex int64 + BlockNumber int64 +} + +func (q *Queries) IsValidatorRegistered(ctx context.Context, arg IsValidatorRegisteredParams) (bool, error) { + row := q.db.QueryRow(ctx, isValidatorRegistered, arg.ValidatorIndex, arg.BlockNumber) var is_registration bool err := row.Scan(&is_registration) return is_registration, err diff --git a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql index 7bf4d6b96..c5273cc69 100644 --- a/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql +++ b/rolling-shutter/keyperimpl/gnosis/database/sql/queries/gnosiskeyper.sql @@ -91,7 +91,7 @@ INSERT INTO validator_registrations ( -- name: IsValidatorRegistered :one SELECT is_registration FROM validator_registrations -WHERE validator_index = $1 AND block_number < $1 +WHERE validator_index = $1 AND block_number < $2 ORDER BY block_number DESC, tx_index DESC, log_index DESC LIMIT 1; diff --git a/rolling-shutter/keyperimpl/gnosis/newslot.go b/rolling-shutter/keyperimpl/gnosis/newslot.go index 13a63bcb7..053b69f1b 100644 --- a/rolling-shutter/keyperimpl/gnosis/newslot.go +++ b/rolling-shutter/keyperimpl/gnosis/newslot.go @@ -128,7 +128,10 @@ func (kpr *Keyper) isProposerRegistered(ctx context.Context, slot uint64) (bool, } db := gnosisdatabase.New(kpr.dbpool) - isRegistered, err := db.IsValidatorRegistered(ctx, int64(proposerDuty.ValidatorIndex)) + isRegistered, err := db.IsValidatorRegistered(ctx, gnosisdatabase.IsValidatorRegisteredParams{ + ValidatorIndex: int64(proposerDuty.ValidatorIndex), + BlockNumber: int64(block), + }) if err == pgx.ErrNoRows { return false, nil } From 39fd356fdbb0807d7b76c896cf370e8f2ad9a556 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Sun, 19 May 2024 22:40:17 +0200 Subject: [PATCH 71/91] Improve logs Log the proposer index when skipping due to an unregistered proposer. Also, fix another message. --- rolling-shutter/keyperimpl/gnosis/newslot.go | 21 ++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/rolling-shutter/keyperimpl/gnosis/newslot.go b/rolling-shutter/keyperimpl/gnosis/newslot.go index 053b69f1b..db126d82d 100644 --- a/rolling-shutter/keyperimpl/gnosis/newslot.go +++ b/rolling-shutter/keyperimpl/gnosis/newslot.go @@ -50,7 +50,7 @@ func (kpr *Keyper) maybeTriggerDecryption(ctx context.Context, slot uint64) erro gnosisKeyperDB := gnosisdatabase.New(kpr.dbpool) syncedUntil, err := gnosisKeyperDB.GetTransactionSubmittedEventsSyncedUntil(ctx) if err != nil { - return errors.Wrap(err, "failed to query synced until from db") + return errors.Wrap(err, "failed to query transaction submitted sync status from db") } if syncedUntil.Slot >= int64(slot) { // If we already synced the block for slot n before this slot has started on our clock, @@ -86,13 +86,14 @@ func (kpr *Keyper) maybeTriggerDecryption(ctx context.Context, slot uint64) erro } // don't trigger if the block proposer is not part of the validator registry - isRegistered, err := kpr.isProposerRegistered(ctx, slot) + isRegistered, proposerIndex, err := kpr.isProposerRegistered(ctx, slot, uint64(nextBlock)) if err != nil { return err } if !isRegistered { log.Debug(). Uint64("slot", slot). + Uint64("proposer-index", proposerIndex). Msg("skipping slot as proposer is not registered") // Even if we don't trigger decryption, we still need to update the tx pointer or it will // become outdated. @@ -109,22 +110,22 @@ func (kpr *Keyper) maybeTriggerDecryption(ctx context.Context, slot uint64) erro return kpr.triggerDecryption(ctx, slot, nextBlock, &keyperSet) } -func (kpr *Keyper) isProposerRegistered(ctx context.Context, slot uint64) (bool, error) { +func (kpr *Keyper) isProposerRegistered(ctx context.Context, slot uint64, block uint64) (bool, uint64, error) { epoch := medley.SlotToEpoch(slot, kpr.config.Gnosis.SlotsPerEpoch) proposerDuties, err := kpr.beaconAPIClient.GetProposerDutiesByEpoch(ctx, epoch) if err != nil { - return false, err + return false, 0, err } if proposerDuties == nil { - return false, errors.Errorf("no proposer duties found for slot %d in epoch %d", slot, epoch) + return false, 0, errors.Errorf("no proposer duties found for slot %d in epoch %d", slot, epoch) } proposerDuty, err := proposerDuties.GetDutyForSlot(slot) if err != nil { - return false, err + return false, 0, err } proposerIndex := proposerDuty.ValidatorIndex if proposerIndex > math.MaxInt64 { - return false, errors.New("proposer index too big") + return false, 0, errors.New("proposer index too big") } db := gnosisdatabase.New(kpr.dbpool) @@ -133,12 +134,12 @@ func (kpr *Keyper) isProposerRegistered(ctx context.Context, slot uint64) (bool, BlockNumber: int64(block), }) if err == pgx.ErrNoRows { - return false, nil + return false, proposerIndex, nil } if err != nil { - return false, errors.Wrapf(err, "failed to query registration status for validator %d", proposerDuty.ValidatorIndex) + return false, 0, errors.Wrapf(err, "failed to query registration status for validator %d", proposerDuty.ValidatorIndex) } - return isRegistered, nil + return isRegistered, proposerDuty.ValidatorIndex, nil } func (kpr *Keyper) getTxPointer(ctx context.Context, eon int64, slot int64, keyperConfigIndex int64) (int64, error) { From 5c981ef85f7324a44d5ab556b759360354b07aa3 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Sun, 19 May 2024 22:41:14 +0200 Subject: [PATCH 72/91] Fix slot identity preimage Identity preimages must be 52 bytes long (32 prefix + 20 address). The slot identity preimage did not follow this. --- rolling-shutter/keyperimpl/gnosis/newslot.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rolling-shutter/keyperimpl/gnosis/newslot.go b/rolling-shutter/keyperimpl/gnosis/newslot.go index db126d82d..af59ed493 100644 --- a/rolling-shutter/keyperimpl/gnosis/newslot.go +++ b/rolling-shutter/keyperimpl/gnosis/newslot.go @@ -314,12 +314,12 @@ func transactionSubmittedEventToIdentityPreimage( } func makeSlotIdentityPreimage(slot uint64) identitypreimage.IdentityPreimage { - // 32 bytes of zeros plus the block number as big endian (ie starting with lots of zeros as well) - // this ensures the block identity preimage is always alphanumerically before any transaction - // identity preimages. + // 32 bytes of zeros plus the block number as 20 byte big endian (ie starting with lots of + // zeros as well). This ensures the block identity preimage is always alphanumerically before + // any transaction identity preimages, because sender addresses cannot be that small. var buf bytes.Buffer buf.Write(common.BigToHash(common.Big0).Bytes()) - buf.Write(common.BigToHash(new(big.Int).SetUint64(slot)).Bytes()) + buf.Write(common.BigToHash(new(big.Int).SetUint64(slot)).Bytes()[12:]) return identitypreimage.IdentityPreimage(buf.Bytes()) } From ca187b3a3c777cabc0d24a946eed6c2b18a00d88 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Sun, 19 May 2024 22:43:43 +0200 Subject: [PATCH 73/91] Improve contract syncing Instead of syncing the whole block range in a single request to the execution node, split it up into multiple. This is important at initial sync and after a long period of being offline. Also, perform an initial sync at startup, so that this potentially long operation does not happen during normal slot processing. --- rolling-shutter/keyperimpl/gnosis/keyper.go | 85 ++++++++---- .../keyperimpl/gnosis/sequencersyncer.go | 125 ++++++++++++------ .../keyperimpl/gnosis/validatorsyncer.go | 114 +++++++++++----- rolling-shutter/medley/syncranges.go | 15 +++ rolling-shutter/medley/syncranges_test.go | 31 +++++ 5 files changed, 264 insertions(+), 106 deletions(-) create mode 100644 rolling-shutter/medley/syncranges.go create mode 100644 rolling-shutter/medley/syncranges_test.go diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index 07544eb97..e8bff834b 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -100,28 +100,6 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { return errors.Wrap(err, "failed to initialize beacon API client") } - validatorSyncerClient, err := ethclient.DialContext(ctx, kpr.config.Gnosis.Node.EthereumURL) - if err != nil { - return errors.Wrap(err, "failed to dial ethereum node") - } - chainID, err := validatorSyncerClient.ChainID(ctx) - if err != nil { - return errors.Wrap(err, "failed to get chain ID") - } - validatorRegistryContract, err := validatorRegistryBindings.NewValidatorregistry( - kpr.config.Gnosis.Contracts.ValidatorRegistry, - validatorSyncerClient, - ) - if err != nil { - return errors.Wrap(err, "failed to instantiate validator registry contract") - } - kpr.validatorSyncer = &ValidatorSyncer{ - Contract: validatorRegistryContract, - DBPool: kpr.dbpool, - BeaconAPIClient: kpr.beaconAPIClient, - ChainID: chainID.Uint64(), - } - messageSender, err := p2p.New(kpr.config.P2P) if err != nil { return errors.Wrap(err, "failed to initialize p2p messaging") @@ -183,6 +161,10 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { if err != nil { return err } + err = kpr.initValidatorSyncer(ctx) + if err != nil { + return err + } runner.Go(func() error { return kpr.processInputs(ctx) }) return runner.StartService(kpr.core, kpr.chainSyncClient, kpr.slotTicker, kpr.eonKeyPublisher) @@ -223,15 +205,16 @@ func (kpr *Keyper) initSequencerSyncer(ctx context.Context) error { } func (kpr *Keyper) ensureSequencerSyncing(ctx context.Context, eon uint64) error { + client, err := ethclient.DialContext(ctx, kpr.config.Gnosis.Node.ContractsURL) + if err != nil { + return errors.Wrap(err, "failed to dial Ethereum execution node") + } + if kpr.sequencerSyncer == nil { log.Info(). Uint64("eon", eon). Str("contract-address", kpr.config.Gnosis.Contracts.KeyperSetManager.Hex()). Msg("initializing sequencer syncer") - client, err := ethclient.DialContext(ctx, kpr.config.Gnosis.Node.ContractsURL) - if err != nil { - return err - } contract, err := sequencerBindings.NewSequencer(kpr.config.Gnosis.Contracts.Sequencer, client) if err != nil { return err @@ -239,6 +222,7 @@ func (kpr *Keyper) ensureSequencerSyncing(ctx context.Context, eon uint64) error kpr.sequencerSyncer = &SequencerSyncer{ Contract: contract, DBPool: kpr.dbpool, + ExecutionClient: client, StartEon: eon, GenesisSlotTimestamp: kpr.config.Gnosis.GenesisSlotTimestamp, SecondsPerSlot: kpr.config.Gnosis.SecondsPerSlot, @@ -252,6 +236,55 @@ func (kpr *Keyper) ensureSequencerSyncing(ctx context.Context, eon uint64) error Msg("decreasing sequencer syncing start eon") kpr.sequencerSyncer.StartEon = eon } + + // Perform an initial sync now because it might take some time and doing so during regular + // slot processing might hold up things + latestHeader, err := client.HeaderByNumber(ctx, nil) + if err != nil { + return errors.Wrap(err, "failed to get latest block header") + } + err = kpr.sequencerSyncer.Sync(ctx, latestHeader) + if err != nil { + return err + } + + return nil +} + +func (kpr *Keyper) initValidatorSyncer(ctx context.Context) error { + validatorSyncerClient, err := ethclient.DialContext(ctx, kpr.config.Gnosis.Node.EthereumURL) + if err != nil { + return errors.Wrap(err, "failed to dial ethereum node") + } + chainID, err := validatorSyncerClient.ChainID(ctx) + if err != nil { + return errors.Wrap(err, "failed to get chain ID") + } + validatorRegistryContract, err := validatorRegistryBindings.NewValidatorregistry( + kpr.config.Gnosis.Contracts.ValidatorRegistry, + validatorSyncerClient, + ) + if err != nil { + return errors.Wrap(err, "failed to instantiate validator registry contract") + } + kpr.validatorSyncer = &ValidatorSyncer{ + Contract: validatorRegistryContract, + DBPool: kpr.dbpool, + BeaconAPIClient: kpr.beaconAPIClient, + ExecutionClient: validatorSyncerClient, + ChainID: chainID.Uint64(), + } + + // Perform an initial sync now because it might take some time and doing so during regular + // slot processing might hold up things + latestHeader, err := validatorSyncerClient.HeaderByNumber(ctx, nil) + if err != nil { + return errors.Wrap(err, "failed to get latest block header") + } + err = kpr.validatorSyncer.Sync(ctx, latestHeader) + if err != nil { + return err + } return nil } diff --git a/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go b/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go index 2df30123e..66d0dbcb7 100644 --- a/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go +++ b/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go @@ -3,9 +3,11 @@ package gnosis import ( "context" "math" + "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/pgxpool" "github.com/pkg/errors" @@ -21,6 +23,7 @@ import ( type SequencerSyncer struct { Contract *sequencerBindings.Sequencer DBPool *pgxpool.Pool + ExecutionClient *ethclient.Client StartEon uint64 GenesisSlotTimestamp uint64 SecondsPerSlot uint64 @@ -41,69 +44,103 @@ func (s *SequencerSyncer) Sync(ctx context.Context, header *types.Header) error } else { start = uint64(syncedUntil.BlockNumber + 1) } - + endBlock := header.Number.Uint64() log.Debug(). Uint64("start-block", start). - Uint64("end-block", header.Number.Uint64()). + Uint64("end-block", endBlock). Msg("syncing sequencer contract") - endBlock := header.Number.Uint64() + syncRanges := medley.GetSyncRanges(start, endBlock, maxRequestBlockRange) + for _, r := range syncRanges { + err = s.syncRange(ctx, r[0], r[1]) + if err != nil { + return err + } + } + return nil +} + +func (s *SequencerSyncer) syncRange( + ctx context.Context, + start, + end uint64, +) error { + events, err := s.fetchEvents(ctx, start, end) + if err != nil { + return err + } + filteredEvents := s.filterEvents(events) + + header, err := s.ExecutionClient.HeaderByNumber(ctx, new(big.Int).SetUint64(end)) + if err != nil { + return errors.Wrap(err, "failed to get execution block header by number") + } + err = s.DBPool.BeginFunc(ctx, func(tx pgx.Tx) error { + err = s.insertTransactionSubmittedEvents(ctx, tx, filteredEvents) + if err != nil { + return err + } + + slot := medley.BlockTimestampToSlot(header.Time, s.GenesisSlotTimestamp, s.SecondsPerSlot) + return database.New(tx).SetTransactionSubmittedEventsSyncedUntil(ctx, database.SetTransactionSubmittedEventsSyncedUntilParams{ + BlockNumber: int64(end), + BlockHash: header.Hash().Bytes(), + Slot: int64(slot), + }) + }) + log.Info(). + Uint64("start-block", start). + Uint64("end-block", end). + Int("num-inserted-events", len(filteredEvents)). + Int("num-discarded-events", len(events)-len(filteredEvents)). + Msg("synced sequencer contract") + return nil +} + +func (s *SequencerSyncer) fetchEvents( + ctx context.Context, + start, + end uint64, +) ([]*sequencerBindings.SequencerTransactionSubmitted, error) { opts := bind.FilterOpts{ Start: start, - End: &endBlock, + End: &end, Context: ctx, } it, err := s.Contract.SequencerFilterer.FilterTransactionSubmitted(&opts) if err != nil { - return errors.Wrap(err, "failed to query transaction submitted events") + return nil, errors.Wrap(err, "failed to query transaction submitted events") } events := []*sequencerBindings.SequencerTransactionSubmitted{} for it.Next() { - if it.Event.Eon < s.StartEon || - it.Event.Eon > math.MaxInt64 || - !it.Event.GasLimit.IsInt64() { - log.Debug(). - Uint64("eon", it.Event.Eon). - Uint64("block-number", it.Event.Raw.BlockNumber). - Str("block-hash", it.Event.Raw.BlockHash.Hex()). - Uint("tx-index", it.Event.Raw.TxIndex). - Uint("log-index", it.Event.Raw.Index). - Msg("ignoring transaction submitted event") - continue - } events = append(events, it.Event) } if it.Error() != nil { - return errors.Wrap(it.Error(), "failed to iterate transaction submitted events") - } - if len(events) == 0 { - log.Debug(). - Uint64("start-block", start). - Uint64("end-block", endBlock). - Msg("no transaction submitted events found") + return nil, errors.Wrap(it.Error(), "failed to iterate transaction submitted events") } + return events, nil +} - return s.DBPool.BeginFunc(ctx, func(tx pgx.Tx) error { - err = s.insertTransactionSubmittedEvents(ctx, tx, events) - if err != nil { - return err - } - - newSyncedUntilBlock, err := medley.Uint64ToInt64Safe(endBlock) - if err != nil { - return err - } - slot := medley.BlockTimestampToSlot(header.Time, s.GenesisSlotTimestamp, s.SecondsPerSlot) - err = queries.SetTransactionSubmittedEventsSyncedUntil(ctx, database.SetTransactionSubmittedEventsSyncedUntilParams{ - BlockNumber: newSyncedUntilBlock, - BlockHash: header.Hash().Bytes(), - Slot: int64(slot), - }) - if err != nil { - return err +func (s *SequencerSyncer) filterEvents( + events []*sequencerBindings.SequencerTransactionSubmitted, +) []*sequencerBindings.SequencerTransactionSubmitted { + filteredEvents := []*sequencerBindings.SequencerTransactionSubmitted{} + for _, event := range events { + if event.Eon < s.StartEon || + event.Eon > math.MaxInt64 || + !event.GasLimit.IsInt64() { + log.Debug(). + Uint64("eon", event.Eon). + Uint64("block-number", event.Raw.BlockNumber). + Str("block-hash", event.Raw.BlockHash.Hex()). + Uint("tx-index", event.Raw.TxIndex). + Uint("log-index", event.Raw.Index). + Msg("ignoring transaction submitted event") + continue } - return nil - }) + filteredEvents = append(filteredEvents, event) + } + return filteredEvents } // insertTransactionSubmittedEvents inserts the given events into the database and updates the diff --git a/rolling-shutter/keyperimpl/gnosis/validatorsyncer.go b/rolling-shutter/keyperimpl/gnosis/validatorsyncer.go index 35093a1fe..df2d75330 100644 --- a/rolling-shutter/keyperimpl/gnosis/validatorsyncer.go +++ b/rolling-shutter/keyperimpl/gnosis/validatorsyncer.go @@ -2,32 +2,38 @@ package gnosis import ( "context" + "fmt" "math" + "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/pgxpool" "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/rs/zerolog/log" - registryBindings "github.com/shutter-network/gnosh-contracts/gnoshcontracts/validatorregistry" + validatorRegistryBindings "github.com/shutter-network/gnosh-contracts/gnoshcontracts/validatorregistry" blst "github.com/supranational/blst/bindings/go" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/beaconapiclient" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/validatorregistry" ) const ( ValidatorRegistrationMessageVersion = 0 + maxRequestBlockRange = 100000 ) type ValidatorSyncer struct { - Contract *registryBindings.Validatorregistry + Contract *validatorRegistryBindings.Validatorregistry DBPool *pgxpool.Pool BeaconAPIClient *beaconapiclient.Client + ExecutionClient *ethclient.Client ChainID uint64 } @@ -43,47 +49,43 @@ func (v *ValidatorSyncer) Sync(ctx context.Context, header *types.Header) error } else { start = uint64(syncedUntil.BlockNumber + 1) } - + endBlock := header.Number.Uint64() log.Debug(). Uint64("start-block", start). - Uint64("end-block", header.Number.Uint64()). + Uint64("end-block", endBlock). Msg("syncing validator registry") - endBlock := header.Number.Uint64() - opts := bind.FilterOpts{ - Start: start, - End: &endBlock, - Context: ctx, + syncRanges := medley.GetSyncRanges(start, endBlock, maxRequestBlockRange) + for _, r := range syncRanges { + err = v.syncRange(ctx, r[0], r[1]) + if err != nil { + return err + } } - it, err := v.Contract.ValidatorregistryFilterer.FilterUpdated(&opts) + return nil +} + +func (v *ValidatorSyncer) syncRange(ctx context.Context, start, end uint64) error { + db := database.New(v.DBPool) + events, err := v.fetchEvents(ctx, start, end) if err != nil { - return errors.Wrap(err, "failed to query validator registry update events") - } - events := []*registryBindings.ValidatorregistryUpdated{} - for it.Next() { - events = append(events, it.Event) - } - if it.Error() != nil { - return errors.Wrap(it.Error(), "failed to iterate validator registry update events") - } - if len(events) == 0 { - log.Debug(). - Uint64("start-block", start). - Uint64("end-block", endBlock). - Msg("no validator registry update events found") + return err } - filteredEvents, err := v.filterEvents(ctx, events) if err != nil { return err } - return v.DBPool.BeginFunc(ctx, func(tx pgx.Tx) error { + header, err := v.ExecutionClient.HeaderByNumber(ctx, new(big.Int).SetUint64(end)) + if err != nil { + return errors.Wrap(err, "failed to get execution block header by number") + } + err = v.DBPool.BeginFunc(ctx, func(tx pgx.Tx) error { err = v.insertEvents(ctx, tx, filteredEvents) if err != nil { return err } err = db.SetValidatorRegistrationsSyncedUntil(ctx, database.SetValidatorRegistrationsSyncedUntilParams{ - BlockNumber: int64(endBlock), + BlockNumber: int64(end), BlockHash: header.Hash().Bytes(), }) if err != nil { @@ -91,14 +93,48 @@ func (v *ValidatorSyncer) Sync(ctx context.Context, header *types.Header) error } return nil }) + if err != nil { + return err + } + log.Info(). + Uint64("start-block", start). + Uint64("end-block", end). + Int("num-inserted-events", len(filteredEvents)). + Int("num-discarded-events", len(events)-len(filteredEvents)). + Msg("synced validator registry") + return nil +} + +func (v *ValidatorSyncer) fetchEvents( + ctx context.Context, + start, + end uint64, +) ([]*validatorRegistryBindings.ValidatorregistryUpdated, error) { + opts := bind.FilterOpts{ + Start: start, + End: &end, + Context: ctx, + } + it, err := v.Contract.ValidatorregistryFilterer.FilterUpdated(&opts) + if err != nil { + return nil, errors.Wrap(err, "failed to query validator registry update events") + } + events := []*validatorRegistryBindings.ValidatorregistryUpdated{} + for it.Next() { + events = append(events, it.Event) + } + if it.Error() != nil { + return nil, errors.Wrap(it.Error(), "failed to iterate validator registry update events") + } + return events, nil } func (v *ValidatorSyncer) filterEvents( ctx context.Context, - events []*registryBindings.ValidatorregistryUpdated, -) ([]*registryBindings.ValidatorregistryUpdated, error) { + events []*validatorRegistryBindings.ValidatorregistryUpdated, +) ([]*validatorRegistryBindings.ValidatorregistryUpdated, error) { db := database.New(v.DBPool) - filteredEvents := []*registryBindings.ValidatorregistryUpdated{} + filteredEvents := []*validatorRegistryBindings.ValidatorregistryUpdated{} for _, event := range events { evLog := log.With(). Hex("block-hash", event.Raw.BlockHash.Bytes()). @@ -133,7 +169,7 @@ func (v *ValidatorSyncer) filterEvents( if err == pgx.ErrNoRows { latestNonce = -1 } - if msg.Nonce <= uint64(latestNonce) || msg.Nonce > math.MaxInt64 { + if msg.Nonce > math.MaxInt64 || int64(msg.Nonce) <= latestNonce { evLog.Warn(). Uint64("nonce", msg.Nonce). Int64("latest-nonce", latestNonce). @@ -149,16 +185,22 @@ func (v *ValidatorSyncer) filterEvents( evLog.Warn().Msg("ignoring registration message for unknown validator") continue } - pubkey := &validator.Data.Validator.Pubkey - sig := new(blst.P2Affine).Deserialize(event.Signature) + pubkey, err := validator.Data.Validator.GetPubkey() + if err != nil { + return nil, errors.Wrapf(err, "failed to get pubkey of validator %d", msg.ValidatorIndex) + } + sig := new(blst.P2Affine).Uncompress(event.Signature) if sig == nil { - evLog.Warn().Msg("ignoring registration message with invalid signature") + evLog.Warn().Msg("ignoring registration message with undecodable signature") continue } + fmt.Printf("signature original: %X\n", event.Signature) + fmt.Printf("validator pubkey original: %s\n", validator.Data.Validator.PubkeyHex) validSignature := validatorregistry.VerifySignature(sig, pubkey, msg) if !validSignature { + fmt.Printf("%X\n", event.Signature) evLog.Warn().Msg("ignoring registration message with invalid signature") - continue + // continue } filteredEvents = append(filteredEvents, event) @@ -166,7 +208,7 @@ func (v *ValidatorSyncer) filterEvents( return filteredEvents, nil } -func (v *ValidatorSyncer) insertEvents(ctx context.Context, tx pgx.Tx, events []*registryBindings.ValidatorregistryUpdated) error { +func (v *ValidatorSyncer) insertEvents(ctx context.Context, tx pgx.Tx, events []*validatorRegistryBindings.ValidatorregistryUpdated) error { db := database.New(tx) for _, event := range events { msg := new(validatorregistry.RegistrationMessage) diff --git a/rolling-shutter/medley/syncranges.go b/rolling-shutter/medley/syncranges.go new file mode 100644 index 000000000..61712075f --- /dev/null +++ b/rolling-shutter/medley/syncranges.go @@ -0,0 +1,15 @@ +package medley + +func GetSyncRanges(start, end, maxRange uint64) [][2]uint64 { + ranges := [][2]uint64{} + for i := start; i <= end; i += maxRange { + s := i + e := i + maxRange - 1 + ranges = append(ranges, [2]uint64{s, e}) + if e > end { + ranges[len(ranges)-1][1] = end + break + } + } + return ranges +} diff --git a/rolling-shutter/medley/syncranges_test.go b/rolling-shutter/medley/syncranges_test.go new file mode 100644 index 000000000..cb5e5ccf9 --- /dev/null +++ b/rolling-shutter/medley/syncranges_test.go @@ -0,0 +1,31 @@ +package medley + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestGetSyncRanges(t *testing.T) { + var maxRange uint64 = 3 + testCases := []struct { + start uint64 + end uint64 + ranges [][2]uint64 + }{ + {start: 0, end: 0, ranges: [][2]uint64{{0, 0}}}, + {start: 3, end: 3, ranges: [][2]uint64{{3, 3}}}, + {start: 0, end: 2, ranges: [][2]uint64{{0, 2}}}, + {start: 3, end: 5, ranges: [][2]uint64{{3, 5}}}, + {start: 0, end: 5, ranges: [][2]uint64{{0, 2}, {3, 5}}}, + {start: 3, end: 8, ranges: [][2]uint64{{3, 5}, {6, 8}}}, + {start: 0, end: 1, ranges: [][2]uint64{{0, 1}}}, + {start: 3, end: 4, ranges: [][2]uint64{{3, 4}}}, + {start: 0, end: 4, ranges: [][2]uint64{{0, 2}, {3, 4}}}, + {start: 1, end: 5, ranges: [][2]uint64{{1, 3}, {4, 5}}}, + } + for _, testCase := range testCases { + ranges := GetSyncRanges(testCase.start, testCase.end, maxRange) + assert.DeepEqual(t, ranges, testCase.ranges) + } +} From 7ad00c25a7be3171035f2688ed03a88904b11f6e Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Sun, 19 May 2024 22:46:20 +0200 Subject: [PATCH 74/91] Fix validator API request Deserializing the pubkey does not work out of the box. For simplicity, we parse it as a string and then add a getter that performs deserialization. --- .../medley/beaconapiclient/getvalidator.go | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/rolling-shutter/medley/beaconapiclient/getvalidator.go b/rolling-shutter/medley/beaconapiclient/getvalidator.go index 61187d261..fb52cd92f 100644 --- a/rolling-shutter/medley/beaconapiclient/getvalidator.go +++ b/rolling-shutter/medley/beaconapiclient/getvalidator.go @@ -2,6 +2,7 @@ package beaconapiclient import ( "context" + "encoding/hex" "encoding/json" "fmt" "io" @@ -25,14 +26,14 @@ type ValidatorData struct { } type Validator struct { - Pubkey blst.P1Affine `json:"pubkey"` - WithdrawalCredentials string `json:"withdrawal_credentials,string"` - EffectiveBalance uint64 `json:"effective_balance,string"` - Slashed bool `json:"slashed"` - ActivationEligibilityEpoch uint64 `json:"activation_eligibility_epoch,string"` - ActivationEpoch uint64 `json:"activation_epoch,string"` - ExitEpoch uint64 `json:"exit_epoch,string"` - WithdrawalEpoch uint64 `json:"withdrawal_epoch,string"` + PubkeyHex string `json:"pubkey"` + WithdrawalCredentials string `json:"withdrawal_credentials"` + EffectiveBalance uint64 `json:"effective_balance,string"` + Slashed bool `json:"slashed"` + ActivationEligibilityEpoch uint64 `json:"activation_eligibility_epoch,string"` + ActivationEpoch uint64 `json:"activation_epoch,string"` + ExitEpoch uint64 `json:"exit_epoch,string"` + WithdrawalEpoch uint64 `json:"withdrawal_epoch,string"` } func (c *Client) GetValidatorByIndex( @@ -71,3 +72,23 @@ func (c *Client) GetValidatorByIndex( return response, nil } + +func (v *Validator) GetPubkey() (*blst.P1Affine, error) { + pubkeyHex := v.PubkeyHex + if pubkeyHex[:2] == "0x" { + pubkeyHex = pubkeyHex[2:] + } + + pubkeyBytes, err := hex.DecodeString(pubkeyHex) + if err != nil { + return nil, errors.Wrap(err, "failed to hex decode validator pubkey") + } + + pubkey := new(blst.P1Affine) + pubkey = pubkey.Uncompress(pubkeyBytes) + if pubkey == nil { + return nil, errors.New("failed to deserialize pubkey") + } + + return pubkey, nil +} From f0fa1291a19002763ad16a6e52ce5d4a83af318d Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Sun, 19 May 2024 23:02:33 +0200 Subject: [PATCH 75/91] Fix lint errors --- .../keyperimpl/gnosis/neweonpublickey.go | 2 +- .../keyperimpl/gnosis/validatorsyncer.go | 15 ++++++++++----- rolling-shutter/p2p/params.go | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/rolling-shutter/keyperimpl/gnosis/neweonpublickey.go b/rolling-shutter/keyperimpl/gnosis/neweonpublickey.go index 34b4250ab..cdba7f693 100644 --- a/rolling-shutter/keyperimpl/gnosis/neweonpublickey.go +++ b/rolling-shutter/keyperimpl/gnosis/neweonpublickey.go @@ -6,7 +6,7 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" ) -func (kpr *Keyper) processNewEonPublicKey(ctx context.Context, key keyper.EonPublicKey) error { +func (kpr *Keyper) processNewEonPublicKey(_ context.Context, key keyper.EonPublicKey) error { //nolint: unparam kpr.eonKeyPublisher.Publish(key) return nil } diff --git a/rolling-shutter/keyperimpl/gnosis/validatorsyncer.go b/rolling-shutter/keyperimpl/gnosis/validatorsyncer.go index df2d75330..e14e43b6f 100644 --- a/rolling-shutter/keyperimpl/gnosis/validatorsyncer.go +++ b/rolling-shutter/keyperimpl/gnosis/validatorsyncer.go @@ -232,9 +232,14 @@ func (v *ValidatorSyncer) insertEvents(ctx context.Context, tx pgx.Tx, events [] return nil } -func checkStaticRegistrationMessageFields(msg *validatorregistry.RegistrationMessage, chainID uint64, validatorRegistryAddress common.Address, log zerolog.Logger) bool { +func checkStaticRegistrationMessageFields( + msg *validatorregistry.RegistrationMessage, + chainID uint64, + validatorRegistryAddress common.Address, + logger zerolog.Logger, +) bool { if msg.Version != ValidatorRegistrationMessageVersion { - log.Warn(). + logger.Warn(). Uint8("version", msg.Version). Uint8("expected-version", ValidatorRegistrationMessageVersion). Uint64("validator-index", msg.ValidatorIndex). @@ -242,7 +247,7 @@ func checkStaticRegistrationMessageFields(msg *validatorregistry.RegistrationMes return false } if msg.ChainID != chainID { - log.Warn(). + logger.Warn(). Uint64("chain-id", msg.ChainID). Uint64("expected-chain-id", chainID). Uint64("validator-index", msg.ValidatorIndex). @@ -250,7 +255,7 @@ func checkStaticRegistrationMessageFields(msg *validatorregistry.RegistrationMes return false } if msg.ValidatorRegistryAddress != validatorRegistryAddress { - log.Warn(). + logger.Warn(). Hex("validator-registry-address", msg.ValidatorRegistryAddress.Bytes()). Hex("expected-validator-registry-address", validatorRegistryAddress.Bytes()). Uint64("validator-index", msg.ValidatorIndex). @@ -258,7 +263,7 @@ func checkStaticRegistrationMessageFields(msg *validatorregistry.RegistrationMes return false } if msg.ValidatorIndex > math.MaxInt64 { - log.Warn(). + logger.Warn(). Uint64("validator-index", msg.ValidatorIndex). Msg("ignoring registration message with invalid validator index") return false diff --git a/rolling-shutter/p2p/params.go b/rolling-shutter/p2p/params.go index c4be96689..ad385e71a 100644 --- a/rolling-shutter/p2p/params.go +++ b/rolling-shutter/p2p/params.go @@ -19,7 +19,7 @@ func makePubSubParams( gossipSubParams := &gsDefault // modified defaults from ethereum consensus spec - // https://github.com/ethereum/consensus-specs/blob/5d80b1954a4b7a121aa36143d50b366727b66cbc/specs/phase0/p2p-interface.md#why-are-these-specific-gossip-parameters-chosen + // https://github.com/ethereum/consensus-specs/blob/5d80b1954a4b7a121aa36143d50b366727b66cbc/specs/phase0/p2p-interface.md#why-are-these-specific-gossip-parameters-chosen //nolint:lll gossipSubParams.HeartbeatInterval = 700 * time.Millisecond gossipSubParams.HistoryLength = 6 From d99963c0645dcb59123f1539a8d62eff9a707bb7 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Mon, 20 May 2024 02:06:31 +0200 Subject: [PATCH 76/91] Ignore missing row error --- rolling-shutter/keyperimpl/gnosis/newslot.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rolling-shutter/keyperimpl/gnosis/newslot.go b/rolling-shutter/keyperimpl/gnosis/newslot.go index af59ed493..531c1e4cd 100644 --- a/rolling-shutter/keyperimpl/gnosis/newslot.go +++ b/rolling-shutter/keyperimpl/gnosis/newslot.go @@ -49,7 +49,9 @@ func (kpr *Keyper) maybeTriggerDecryption(ctx context.Context, slot uint64) erro gnosisKeyperDB := gnosisdatabase.New(kpr.dbpool) syncedUntil, err := gnosisKeyperDB.GetTransactionSubmittedEventsSyncedUntil(ctx) - if err != nil { + if err != nil && err != pgx.ErrNoRows { + // pgx.ErrNoRows is expected if we're not part of the keyper set (which is checked later). + // That's because non-keypers don't sync transaction submitted events. return errors.Wrap(err, "failed to query transaction submitted sync status from db") } if syncedUntil.Slot >= int64(slot) { From 691478399e8e1dd5f64a7f4ded6c2a3c13a8d5e5 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Mon, 20 May 2024 02:14:29 +0200 Subject: [PATCH 77/91] Fix eon/keyper config index mixup In epoch key generation related p2p messages, we only use the keyper config index, not the eon index. --- rolling-shutter/keyper/epochkghandler/sendkeyshare.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rolling-shutter/keyper/epochkghandler/sendkeyshare.go b/rolling-shutter/keyper/epochkghandler/sendkeyshare.go index 1f33fa9ba..172857a96 100644 --- a/rolling-shutter/keyper/epochkghandler/sendkeyshare.go +++ b/rolling-shutter/keyper/epochkghandler/sendkeyshare.go @@ -122,7 +122,7 @@ func (ksh *KeyShareHandler) ConstructDecryptionKeyShares( }) } - keyperSetIndexUint, err := medley.Int64ToUint64Safe(eon.Eon) + keyperSetIndexUint, err := medley.Int64ToUint64Safe(eon.KeyperConfigIndex) if err != nil { return nil, err } From 2f059d7ffdc58a2893f1a1d0cd15669c3f59d5fb Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Mon, 20 May 2024 02:35:03 +0200 Subject: [PATCH 78/91] Use correct key for dkg result Previously, we used the eon from the p2p message as the key to query the dkg result from the db. However, the msg eon is the keyper config index. We therefore have to translate into the internal eon value. --- .../keyper/database/keyper.sqlc.gen.go | 17 +++++++++++++++++ .../keyper/database/sql/queries/keyper.sql | 4 ++++ rolling-shutter/keyper/epochkghandler/key.go | 2 +- .../keyper/epochkghandler/keyshare.go | 4 ++-- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/rolling-shutter/keyper/database/keyper.sqlc.gen.go b/rolling-shutter/keyper/database/keyper.sqlc.gen.go index 1abcb260b..60d1b6c72 100644 --- a/rolling-shutter/keyper/database/keyper.sqlc.gen.go +++ b/rolling-shutter/keyper/database/keyper.sqlc.gen.go @@ -335,6 +335,23 @@ func (q *Queries) GetDKGResultForBlockNumber(ctx context.Context, blockNumber in return i, err } +const getDKGResultForKeyperConfigIndex = `-- name: GetDKGResultForKeyperConfigIndex :one +SELECT eon, success, error, pure_result FROM dkg_result +WHERE eon = (SELECT max(eon) FROM eons WHERE keyper_config_index = $1) +` + +func (q *Queries) GetDKGResultForKeyperConfigIndex(ctx context.Context, keyperConfigIndex int64) (DkgResult, error) { + row := q.db.QueryRow(ctx, getDKGResultForKeyperConfigIndex, keyperConfigIndex) + var i DkgResult + err := row.Scan( + &i.Eon, + &i.Success, + &i.Error, + &i.PureResult, + ) + return i, err +} + const getDecryptionKey = `-- name: GetDecryptionKey :one SELECT eon, epoch_id, decryption_key FROM decryption_key WHERE eon = $1 AND epoch_id = $2 diff --git a/rolling-shutter/keyper/database/sql/queries/keyper.sql b/rolling-shutter/keyper/database/sql/queries/keyper.sql index 28a8b2b37..817068b96 100644 --- a/rolling-shutter/keyper/database/sql/queries/keyper.sql +++ b/rolling-shutter/keyper/database/sql/queries/keyper.sql @@ -169,6 +169,10 @@ WHERE eon = (SELECT eon FROM eons WHERE activation_block_number <= sqlc.arg(bloc ORDER BY activation_block_number DESC, height DESC LIMIT 1); +-- name: GetDKGResultForKeyperConfigIndex :one +SELECT * FROM dkg_result +WHERE eon = (SELECT max(eon) FROM eons WHERE keyper_config_index = $1); + -- name: GetAllDKGResults :many SELECT * FROM dkg_result ORDER BY eon ASC; diff --git a/rolling-shutter/keyper/epochkghandler/key.go b/rolling-shutter/keyper/epochkghandler/key.go index 5956a41c5..b25188a3a 100644 --- a/rolling-shutter/keyper/epochkghandler/key.go +++ b/rolling-shutter/keyper/epochkghandler/key.go @@ -43,7 +43,7 @@ func (handler *DecryptionKeyHandler) ValidateMessage(ctx context.Context, msg p2 return pubsub.ValidationReject, errors.Errorf("eon %d overflows int64", key.Eon) } - dkgResultDB, err := database.New(handler.dbpool).GetDKGResult(ctx, int64(key.Eon)) + dkgResultDB, err := database.New(handler.dbpool).GetDKGResultForKeyperConfigIndex(ctx, int64(key.Eon)) if err == pgx.ErrNoRows { return pubsub.ValidationReject, errors.Errorf("no DKG result found for eon %d", key.Eon) } diff --git a/rolling-shutter/keyper/epochkghandler/keyshare.go b/rolling-shutter/keyper/epochkghandler/keyshare.go index 1d260355d..5fbcc001d 100644 --- a/rolling-shutter/keyper/epochkghandler/keyshare.go +++ b/rolling-shutter/keyper/epochkghandler/keyshare.go @@ -46,7 +46,7 @@ func (handler *DecryptionKeyShareHandler) ValidateMessage(ctx context.Context, m return pubsub.ValidationReject, errors.Errorf("eon %d overflows int64", keyShare.Eon) } - dkgResultDB, err := database.New(handler.dbpool).GetDKGResult(ctx, int64(keyShare.Eon)) + dkgResultDB, err := database.New(handler.dbpool).GetDKGResultForKeyperConfigIndex(ctx, int64(keyShare.Eon)) if err == pgx.ErrNoRows { return pubsub.ValidationReject, errors.Errorf("no DKG result found for eon %d", keyShare.Eon) } @@ -122,7 +122,7 @@ func (handler *DecryptionKeyShareHandler) HandleMessage(ctx context.Context, m p } // fetch dkg result from db - dkgResultDB, err := db.GetDKGResult(ctx, int64(msg.Eon)) + dkgResultDB, err := db.GetDKGResultForKeyperConfigIndex(ctx, int64(msg.Eon)) if err != nil { return nil, errors.Wrapf(err, "failed to get dkg result for eon %d from db", msg.Eon) } From 8889ed9ad6167831640452242c9a44c5ba36b2a7 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Mon, 20 May 2024 03:13:42 +0200 Subject: [PATCH 79/91] Query decryption key shares by keyper config index --- rolling-shutter/keyper/epochkghandler/keyshare.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/rolling-shutter/keyper/epochkghandler/keyshare.go b/rolling-shutter/keyper/epochkghandler/keyshare.go index 5fbcc001d..a207298a4 100644 --- a/rolling-shutter/keyper/epochkghandler/keyshare.go +++ b/rolling-shutter/keyper/epochkghandler/keyshare.go @@ -128,7 +128,7 @@ func (handler *DecryptionKeyShareHandler) HandleMessage(ctx context.Context, m p } if !dkgResultDB.Success { log.Info().Uint64("eon", msg.Eon). - Msg("ignoring decryption trigger: eon key generation failed") + Msg("ignoring decryption key share: eon key generation failed") return nil, nil } pureDKGResult, err := shdb.DecodePureDKGResult(dkgResultDB.PureResult) @@ -141,7 +141,12 @@ func (handler *DecryptionKeyShareHandler) HandleMessage(ctx context.Context, m p for _, share := range msg.GetShares() { identityPreimage := identitypreimage.IdentityPreimage(share.EpochID) - epochKG, err := handler.aggregateDecryptionKeySharesFromDB(ctx, pureDKGResult, identityPreimage) + epochKG, err := handler.aggregateDecryptionKeySharesFromDB( + ctx, + int64(msg.Eon), + pureDKGResult, + identityPreimage, + ) if err != nil { return nil, err } @@ -178,12 +183,13 @@ func (handler *DecryptionKeyShareHandler) HandleMessage(ctx context.Context, m p func (handler *DecryptionKeyShareHandler) aggregateDecryptionKeySharesFromDB( ctx context.Context, + keyperConfigIndex int64, pureDKGResult *puredkg.Result, identityPreimage identitypreimage.IdentityPreimage, ) (*epochkg.EpochKG, error) { db := database.New(handler.dbpool) shares, err := db.SelectDecryptionKeyShares(ctx, database.SelectDecryptionKeySharesParams{ - Eon: int64(pureDKGResult.Eon), + Eon: keyperConfigIndex, EpochID: identityPreimage.Bytes(), }) if err != nil { From 912e468a9df69dc675b1977c57f5342d16610d08 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Mon, 20 May 2024 16:23:10 +0200 Subject: [PATCH 80/91] Use 52 byte epoch ids in JSON tests --- rolling-shutter/cmd/cryptocmd/jsontests.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rolling-shutter/cmd/cryptocmd/jsontests.go b/rolling-shutter/cmd/cryptocmd/jsontests.go index eef12e35e..15ac6acd3 100644 --- a/rolling-shutter/cmd/cryptocmd/jsontests.go +++ b/rolling-shutter/cmd/cryptocmd/jsontests.go @@ -416,7 +416,7 @@ func verifyTestCase(tc *testCase) error { } func createEncryptionTest(keygen *testkeygen.KeyGenerator, message []byte) (*encryptionTest, error) { - epochID := keygen.RandomEpochID(make([]byte, 32)) + epochID := keygen.RandomEpochID(make([]byte, 52)) et := encryptionTest{} @@ -501,8 +501,8 @@ func createVerificationTest(keygen *testkeygen.KeyGenerator, payload []byte) (ve func createFailedVerificationTest(keygen *testkeygen.KeyGenerator, _ []byte) (verificationTest, error) { var err error vt := verificationTest{} - epochID := keygen.RandomEpochID(make([]byte, 32)) - mismatch := keygen.RandomEpochID(make([]byte, 32)) + epochID := keygen.RandomEpochID(make([]byte, 52)) + mismatch := keygen.RandomEpochID(make([]byte, 52)) vt.EpochID = epochID vt.EpochSecretKey = *keygen.EpochSecretKey(epochID) vt.EonPublicKey = *keygen.EonPublicKey(mismatch) From ee9d7a68a7630e7ac9bd97e2c1b5b6713cafa73a Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Mon, 20 May 2024 16:28:13 +0200 Subject: [PATCH 81/91] Do not consider eon in sequencer syncer This removes some unnecessary complexity. In particular, it means we can perform an initial sequencer sync at startup, instead of only when we receive a keyper set. --- rolling-shutter/keyperimpl/gnosis/keyper.go | 69 ++++--------------- .../keyperimpl/gnosis/newkeyperset.go | 6 -- .../keyperimpl/gnosis/sequencersyncer.go | 6 +- 3 files changed, 14 insertions(+), 67 deletions(-) diff --git a/rolling-shutter/keyperimpl/gnosis/keyper.go b/rolling-shutter/keyperimpl/gnosis/keyper.go index e8bff834b..7d728b974 100644 --- a/rolling-shutter/keyperimpl/gnosis/keyper.go +++ b/rolling-shutter/keyperimpl/gnosis/keyper.go @@ -4,7 +4,6 @@ import ( "context" "time" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" gethLog "github.com/ethereum/go-ethereum/log" "github.com/jackc/pgx/v4/pgxpool" @@ -14,7 +13,6 @@ import ( validatorRegistryBindings "github.com/shutter-network/gnosh-contracts/gnoshcontracts/validatorregistry" "golang.org/x/exp/slog" - obskeyper "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" "github.com/shutter-network/rolling-shutter/rolling-shutter/eonkeypublisher" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/epochkghandler" @@ -174,67 +172,24 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error { // keyper set. Otherwise, the syncer will only be initialized once such a keyper set is observed to // be added, as only then we will know which eon(s) we are responsible for. func (kpr *Keyper) initSequencerSyncer(ctx context.Context) error { - obskeyperdb := obskeyper.New(kpr.dbpool) - keyperSets, err := obskeyperdb.GetKeyperSets(ctx) - if err != nil { - return errors.Wrap(err, "failed to query keyper sets from db") - } - - keyperSetFound := false - minEon := uint64(0) - for _, keyperSet := range keyperSets { - for _, m := range keyperSet.Keypers { - mAddress := common.HexToAddress(m) - if mAddress.Cmp(kpr.config.GetAddress()) == 0 { - keyperSetFound = true - if minEon > uint64(keyperSet.KeyperConfigIndex) { - minEon = uint64(keyperSet.KeyperConfigIndex) - } - break - } - } - } - - if keyperSetFound { - err := kpr.ensureSequencerSyncing(ctx, minEon) - if err != nil { - return err - } - } - return nil -} - -func (kpr *Keyper) ensureSequencerSyncing(ctx context.Context, eon uint64) error { client, err := ethclient.DialContext(ctx, kpr.config.Gnosis.Node.ContractsURL) if err != nil { return errors.Wrap(err, "failed to dial Ethereum execution node") } - if kpr.sequencerSyncer == nil { - log.Info(). - Uint64("eon", eon). - Str("contract-address", kpr.config.Gnosis.Contracts.KeyperSetManager.Hex()). - Msg("initializing sequencer syncer") - contract, err := sequencerBindings.NewSequencer(kpr.config.Gnosis.Contracts.Sequencer, client) - if err != nil { - return err - } - kpr.sequencerSyncer = &SequencerSyncer{ - Contract: contract, - DBPool: kpr.dbpool, - ExecutionClient: client, - StartEon: eon, - GenesisSlotTimestamp: kpr.config.Gnosis.GenesisSlotTimestamp, - SecondsPerSlot: kpr.config.Gnosis.SecondsPerSlot, - } + log.Info(). + Str("contract-address", kpr.config.Gnosis.Contracts.KeyperSetManager.Hex()). + Msg("initializing sequencer syncer") + contract, err := sequencerBindings.NewSequencer(kpr.config.Gnosis.Contracts.Sequencer, client) + if err != nil { + return err } - - if eon < kpr.sequencerSyncer.StartEon { - log.Info(). - Uint64("old-start-eon", kpr.sequencerSyncer.StartEon). - Uint64("new-start-eon", eon). - Msg("decreasing sequencer syncing start eon") - kpr.sequencerSyncer.StartEon = eon + kpr.sequencerSyncer = &SequencerSyncer{ + Contract: contract, + DBPool: kpr.dbpool, + ExecutionClient: client, + GenesisSlotTimestamp: kpr.config.Gnosis.GenesisSlotTimestamp, + SecondsPerSlot: kpr.config.Gnosis.SecondsPerSlot, } // Perform an initial sync now because it might take some time and doing so during regular diff --git a/rolling-shutter/keyperimpl/gnosis/newkeyperset.go b/rolling-shutter/keyperimpl/gnosis/newkeyperset.go index 919b8d878..801c379de 100644 --- a/rolling-shutter/keyperimpl/gnosis/newkeyperset.go +++ b/rolling-shutter/keyperimpl/gnosis/newkeyperset.go @@ -29,12 +29,6 @@ func (kpr *Keyper) processNewKeyperSet(ctx context.Context, ev *syncevent.Keyper Bool("is-member", isMember). Msg("new keyper set added") - if isMember { - if err := kpr.ensureSequencerSyncing(ctx, ev.Eon); err != nil { - return err - } - } - return kpr.dbpool.BeginFunc(ctx, func(tx pgx.Tx) error { obskeyperdb := obskeyper.New(tx) diff --git a/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go b/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go index 66d0dbcb7..96ee5a705 100644 --- a/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go +++ b/rolling-shutter/keyperimpl/gnosis/sequencersyncer.go @@ -24,7 +24,6 @@ type SequencerSyncer struct { Contract *sequencerBindings.Sequencer DBPool *pgxpool.Pool ExecutionClient *ethclient.Client - StartEon uint64 GenesisSlotTimestamp uint64 SecondsPerSlot uint64 } @@ -126,8 +125,7 @@ func (s *SequencerSyncer) filterEvents( ) []*sequencerBindings.SequencerTransactionSubmitted { filteredEvents := []*sequencerBindings.SequencerTransactionSubmitted{} for _, event := range events { - if event.Eon < s.StartEon || - event.Eon > math.MaxInt64 || + if event.Eon > math.MaxInt64 || !event.GasLimit.IsInt64() { log.Debug(). Uint64("eon", event.Eon). @@ -135,7 +133,7 @@ func (s *SequencerSyncer) filterEvents( Str("block-hash", event.Raw.BlockHash.Hex()). Uint("tx-index", event.Raw.TxIndex). Uint("log-index", event.Raw.Index). - Msg("ignoring transaction submitted event") + Msg("ignoring transaction submitted event with high eon") continue } filteredEvents = append(filteredEvents, event) From 07ceb38a56fbc940216c0d20b6b665708eaa2b8f Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Wed, 22 May 2024 16:47:34 +0200 Subject: [PATCH 82/91] Sort identity preimages in messages --- rolling-shutter/keyperimpl/gnosis/newslot.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/rolling-shutter/keyperimpl/gnosis/newslot.go b/rolling-shutter/keyperimpl/gnosis/newslot.go index 531c1e4cd..defcee005 100644 --- a/rolling-shutter/keyperimpl/gnosis/newslot.go +++ b/rolling-shutter/keyperimpl/gnosis/newslot.go @@ -6,6 +6,7 @@ import ( "fmt" "math" "math/big" + "sort" "github.com/ethereum/go-ethereum/common" "github.com/jackc/pgx/v4" @@ -297,7 +298,10 @@ func (kpr *Keyper) getDecryptionIdentityPreimages( } identityPreimages = append(identityPreimages, identityPreimage) } - return identityPreimages, nil + + sortedIdentityPreimages := sortIdentityPreimages(identityPreimages) + + return sortedIdentityPreimages, nil } func transactionSubmittedEventToIdentityPreimage( @@ -325,3 +329,12 @@ func makeSlotIdentityPreimage(slot uint64) identitypreimage.IdentityPreimage { return identitypreimage.IdentityPreimage(buf.Bytes()) } + +func sortIdentityPreimages(identityPreimages []identitypreimage.IdentityPreimage) []identitypreimage.IdentityPreimage { + sorted := make([]identitypreimage.IdentityPreimage, len(identityPreimages)) + copy(sorted, identityPreimages) + sort.Slice(sorted, func(i, j int) bool { + return bytes.Compare(sorted[i], sorted[j]) < 0 + }) + return sorted +} From d9d079efbaf594309ba7aeb5a2015cb7a3dde285 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Wed, 22 May 2024 17:08:04 +0200 Subject: [PATCH 83/91] Enable validator registration signature check --- rolling-shutter/keyperimpl/gnosis/validatorsyncer.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/rolling-shutter/keyperimpl/gnosis/validatorsyncer.go b/rolling-shutter/keyperimpl/gnosis/validatorsyncer.go index e14e43b6f..f83a2e221 100644 --- a/rolling-shutter/keyperimpl/gnosis/validatorsyncer.go +++ b/rolling-shutter/keyperimpl/gnosis/validatorsyncer.go @@ -2,7 +2,6 @@ package gnosis import ( "context" - "fmt" "math" "math/big" @@ -194,13 +193,10 @@ func (v *ValidatorSyncer) filterEvents( evLog.Warn().Msg("ignoring registration message with undecodable signature") continue } - fmt.Printf("signature original: %X\n", event.Signature) - fmt.Printf("validator pubkey original: %s\n", validator.Data.Validator.PubkeyHex) validSignature := validatorregistry.VerifySignature(sig, pubkey, msg) if !validSignature { - fmt.Printf("%X\n", event.Signature) evLog.Warn().Msg("ignoring registration message with invalid signature") - // continue + continue } filteredEvents = append(filteredEvents, event) From acb6f1b33a3babef75d8359b06f0d90b023d8c4f Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld <15726643+ezdac@users.noreply.github.com> Date: Fri, 10 May 2024 16:20:06 +0200 Subject: [PATCH 84/91] chore(op): add initial op-shutter keyper --- .../docs/rolling-shutter_optimismkeyper.md | 34 +++++++++++++++++++ ...-shutter_optimismkeyper_generate-config.md | 28 +++++++++++++++ .../rolling-shutter_optimismkeyper_initdb.md | 27 +++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 rolling-shutter/docs/rolling-shutter_optimismkeyper.md create mode 100644 rolling-shutter/docs/rolling-shutter_optimismkeyper_generate-config.md create mode 100644 rolling-shutter/docs/rolling-shutter_optimismkeyper_initdb.md diff --git a/rolling-shutter/docs/rolling-shutter_optimismkeyper.md b/rolling-shutter/docs/rolling-shutter_optimismkeyper.md new file mode 100644 index 000000000..bf0a546e0 --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_optimismkeyper.md @@ -0,0 +1,34 @@ +## rolling-shutter optimismkeyper + +Run a Shutter optimism keyper node + +### Synopsis + +This command runs a keyper node. It will connect to both an Optimism and a +Shuttermint node which have to be started separately in advance. + +``` +rolling-shutter optimismkeyper [flags] +``` + +### Options + +``` + --config string config file + -h, --help help for optimismkeyper +``` + +### Options inherited from parent commands + +``` + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter](rolling-shutter.md) - A collection of commands to run and interact with Rolling Shutter nodes +* [rolling-shutter optimismkeyper generate-config](rolling-shutter_optimismkeyper_generate-config.md) - Generate a 'optimismkeyper' configuration file +* [rolling-shutter optimismkeyper initdb](rolling-shutter_optimismkeyper_initdb.md) - Initialize the database of the 'optimismkeyper' + diff --git a/rolling-shutter/docs/rolling-shutter_optimismkeyper_generate-config.md b/rolling-shutter/docs/rolling-shutter_optimismkeyper_generate-config.md new file mode 100644 index 000000000..55d179a08 --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_optimismkeyper_generate-config.md @@ -0,0 +1,28 @@ +## rolling-shutter optimismkeyper generate-config + +Generate a 'optimismkeyper' configuration file + +``` +rolling-shutter optimismkeyper generate-config [flags] +``` + +### Options + +``` + -h, --help help for generate-config + --output string output file +``` + +### Options inherited from parent commands + +``` + --config string config file + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter optimismkeyper](rolling-shutter_optimismkeyper.md) - Run a Shutter optimism keyper node + diff --git a/rolling-shutter/docs/rolling-shutter_optimismkeyper_initdb.md b/rolling-shutter/docs/rolling-shutter_optimismkeyper_initdb.md new file mode 100644 index 000000000..cec7e7bc0 --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_optimismkeyper_initdb.md @@ -0,0 +1,27 @@ +## rolling-shutter optimismkeyper initdb + +Initialize the database of the 'optimismkeyper' + +``` +rolling-shutter optimismkeyper initdb [flags] +``` + +### Options + +``` + -h, --help help for initdb +``` + +### Options inherited from parent commands + +``` + --config string config file + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter optimismkeyper](rolling-shutter_optimismkeyper.md) - Run a Shutter optimism keyper node + From 2c8842e4ae13218e47c79bfa818b22b7cf8f1a55 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld <15726643+ezdac@users.noreply.github.com> Date: Fri, 10 May 2024 16:20:31 +0200 Subject: [PATCH 85/91] feat(chainsync): sync contract event handler with latest head --- rolling-shutter/keyperimpl/optimism/keyper.go | 3 + rolling-shutter/medley/chainsync/client.go | 4 +- .../medley/chainsync/client/client.go | 2 +- rolling-shutter/medley/chainsync/options.go | 91 +++++++---- .../medley/chainsync/syncer/eonpubkey.go | 88 +++++++--- .../medley/chainsync/syncer/keyperset.go | 50 +++++- .../medley/chainsync/syncer/shutterstate.go | 151 +++++++++++++----- .../medley/chainsync/syncer/unsafehead.go | 30 +++- .../medley/chainsync/syncer/util.go | 6 +- 9 files changed, 317 insertions(+), 108 deletions(-) diff --git a/rolling-shutter/keyperimpl/optimism/keyper.go b/rolling-shutter/keyperimpl/optimism/keyper.go index 44941848a..aac660780 100644 --- a/rolling-shutter/keyperimpl/optimism/keyper.go +++ b/rolling-shutter/keyperimpl/optimism/keyper.go @@ -144,11 +144,14 @@ func (kpr *Keyper) newEonPublicKey(ctx context.Context, pubKey keyper.EonPublicK log.Info(). Uint64("eon", pubKey.Eon). Uint64("activation-block", pubKey.ActivationBlock). + Bytes("pub-key", pubKey.PublicKey). Msg("new eon pk") // Currently all keypers call this and race to call this function first. // For now this is fine, but a keyper should only send a transaction if // the key is not set yet. // Best would be a coordinatated leader election who will broadcast the key. + // FIXME: the syncer receives an empty key byte. + // Is this already tx, err := kpr.l2Client.BroadcastEonKey(ctx, pubKey.Eon, pubKey.PublicKey) if err != nil { log.Error().Err(err).Msg("error broadcasting eon public key") diff --git a/rolling-shutter/medley/chainsync/client.go b/rolling-shutter/medley/chainsync/client.go index 7d2bd57de..bb2d3f3b0 100644 --- a/rolling-shutter/medley/chainsync/client.go +++ b/rolling-shutter/medley/chainsync/client.go @@ -24,7 +24,7 @@ var noopLogger = &logger.NoopLogger{} var ErrServiceNotInstantiated = errors.New("service is not instantiated, pass a handler function option") type Client struct { - client.Client + client.EthereumClient log log.Logger options *options @@ -136,7 +136,7 @@ func (s *Client) BroadcastEonKey(ctx context.Context, eon uint64, eonPubKey []by // This value is cached, since it is not expected to change. func (s *Client) ChainID(ctx context.Context) (*big.Int, error) { if s.chainID == nil { - cid, err := s.Client.ChainID(ctx) + cid, err := s.EthereumClient.ChainID(ctx) if err != nil { return nil, err } diff --git a/rolling-shutter/medley/chainsync/client/client.go b/rolling-shutter/medley/chainsync/client/client.go index 0444ab8c9..a69e2692a 100644 --- a/rolling-shutter/medley/chainsync/client/client.go +++ b/rolling-shutter/medley/chainsync/client/client.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" ) -type Client interface { +type EthereumClient interface { Close() ChainID(ctx context.Context) (*big.Int, error) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) diff --git a/rolling-shutter/medley/chainsync/options.go b/rolling-shutter/medley/chainsync/options.go index 580876243..36d8e3c9a 100644 --- a/rolling-shutter/medley/chainsync/options.go +++ b/rolling-shutter/medley/chainsync/options.go @@ -24,7 +24,7 @@ type options struct { keyperSetManagerAddress *common.Address keyBroadcastContractAddress *common.Address clientURL string - client syncclient.Client + ethClient syncclient.EthereumClient logger log.Logger runner service.Runner syncStart *number.BlockNumber @@ -37,11 +37,11 @@ type options struct { } func (o *options) verify() error { - if o.clientURL != "" && o.client != nil { + if o.clientURL != "" && o.ethClient != nil { // TODO: error message return errors.New("can't use client and client url") } - if o.clientURL == "" && o.client == nil { + if o.clientURL == "" && o.ethClient == nil { // TODO: error message return errors.New("have to provide either url or client") } @@ -56,24 +56,30 @@ func (o *options) verify() error { // of shutter clients background workers. func (o *options) apply(ctx context.Context, c *Client) error { var ( - client syncclient.Client + client syncclient.EthereumClient err error ) if o.clientURL != "" { - o.client, err = ethclient.DialContext(ctx, o.clientURL) + o.ethClient, err = ethclient.DialContext(ctx, o.clientURL) if err != nil { return err } } - client = o.client - c.log = o.logger + client = o.ethClient - c.Client = client + c.EthereumClient = client + if o.logger != nil { + c.log = o.logger + // NOCHECKIN: + c.log.Info("got logger in options") + } + + syncedServices := []syncer.ManualFilterHandler{} // the nil passthrough will use "latest" for each call, // but we want to harmonize and fix the sync start to a specific block. if o.syncStart.IsLatest() { - latestBlock, err := c.Client.BlockNumber(ctx) + latestBlock, err := c.EthereumClient.BlockNumber(ctx) if err != nil { return errors.Wrap(err, "polling latest block") } @@ -85,14 +91,16 @@ func (o *options) apply(ctx context.Context, c *Client) error { return err } c.kssync = &syncer.KeyperSetSyncer{ - Client: client, - Contract: c.KeyperSetManager, - Log: c.log, - StartBlock: o.syncStart, - Handler: o.handlerKeyperSet, + Client: client, + Contract: c.KeyperSetManager, + Log: c.log, + StartBlock: o.syncStart, + Handler: o.handlerKeyperSet, + DisableEventWatcher: true, } if o.handlerKeyperSet != nil { c.services = append(c.services, c.kssync) + syncedServices = append(syncedServices, c.kssync) } c.KeyBroadcast, err = bindings.NewKeyBroadcastContract(*o.keyBroadcastContractAddress, client) @@ -100,35 +108,52 @@ func (o *options) apply(ctx context.Context, c *Client) error { return err } c.epksync = &syncer.EonPubKeySyncer{ - Client: client, - Log: c.log, - KeyBroadcast: c.KeyBroadcast, - KeyperSetManager: c.KeyperSetManager, - Handler: o.handlerEonPublicKey, - StartBlock: o.syncStart, + Client: client, + Log: c.log, + KeyBroadcast: c.KeyBroadcast, + KeyperSetManager: c.KeyperSetManager, + Handler: o.handlerEonPublicKey, + StartBlock: o.syncStart, + DisableEventWatcher: true, } if o.handlerEonPublicKey != nil { c.services = append(c.services, c.epksync) + syncedServices = append(syncedServices, c.epksync) } c.sssync = &syncer.ShutterStateSyncer{ - Client: client, - Contract: c.KeyperSetManager, - Log: c.log, - Handler: o.handlerShutterState, - StartBlock: o.syncStart, + Client: client, + Contract: c.KeyperSetManager, + Log: c.log, + Handler: o.handlerShutterState, + StartBlock: o.syncStart, + DisableEventWatcher: true, } if o.handlerShutterState != nil { c.services = append(c.services, c.sssync) + syncedServices = append(syncedServices, c.sssync) } - if o.handlerBlock != nil { - c.uhsync = &syncer.UnsafeHeadSyncer{ - Client: client, - Log: c.log, - Handler: o.handlerBlock, + if o.handlerBlock == nil { + // NOOP - but we need to run the UnsafeHeadSyncer. + // This is to keep the inner workings consisten, + // we use the DisableEventWatcher mechanism in combination + // with the UnsafeHeadSyncer instead of the streaming + // Watch... subscription on events. + // TODO: think about allowing the streaming events, + // when guaranteed event order (event1,event2,new-block-event) + // is not required + o.handlerBlock = func(ctx context.Context, lb *event.LatestBlock) error { + return nil } } + + c.uhsync = &syncer.UnsafeHeadSyncer{ + Client: client, + Log: c.log, + Handler: o.handlerBlock, + SyncedHandler: syncedServices, + } if o.handlerBlock != nil { c.services = append(c.services, c.uhsync) } @@ -141,7 +166,7 @@ func defaultOptions() *options { keyperSetManagerAddress: &predeploy.KeyperSetManagerAddr, keyBroadcastContractAddress: &predeploy.KeyBroadcastContractAddr, clientURL: "", - client: nil, + ethClient: nil, logger: noopLogger, runner: nil, syncStart: number.NewBlockNumber(nil), @@ -193,9 +218,9 @@ func WithLogger(l log.Logger) Option { } } -func WithClient(client syncclient.Client) Option { +func WithClient(client syncclient.EthereumClient) Option { return func(o *options) error { - o.client = client + o.ethClient = client return nil } } diff --git a/rolling-shutter/medley/chainsync/syncer/eonpubkey.go b/rolling-shutter/medley/chainsync/syncer/eonpubkey.go index 1c0494845..635a10028 100644 --- a/rolling-shutter/medley/chainsync/syncer/eonpubkey.go +++ b/rolling-shutter/medley/chainsync/syncer/eonpubkey.go @@ -2,11 +2,11 @@ package syncer import ( "context" - "errors" "fmt" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/log" + "github.com/pkg/errors" "github.com/shutter-network/shop-contracts/bindings" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/client" @@ -15,18 +15,54 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" ) +var _ ManualFilterHandler = &EonPubKeySyncer{} + type EonPubKeySyncer struct { - Client client.Client - Log log.Logger - KeyBroadcast *bindings.KeyBroadcastContract - KeyperSetManager *bindings.KeyperSetManager - StartBlock *number.BlockNumber - Handler event.EonPublicKeyHandler + Client client.EthereumClient + Log log.Logger + KeyBroadcast *bindings.KeyBroadcastContract + KeyperSetManager *bindings.KeyperSetManager + StartBlock *number.BlockNumber + Handler event.EonPublicKeyHandler + DisableEventWatcher bool keyBroadcastCh chan *bindings.KeyBroadcastContractEonKeyBroadcast } +func (s *EonPubKeySyncer) QueryAndHandle(ctx context.Context, block uint64) error { + s.Log.Info( + "pubsyncer query and handle called", + "block", + block, + ) + opts := &bind.FilterOpts{ + Start: block, + End: &block, + Context: ctx, + } + iter, err := s.KeyBroadcast.FilterEonKeyBroadcast(opts) + if err != nil { + return err + } + defer iter.Close() + + for iter.Next() { + select { + case s.keyBroadcastCh <- iter.Event: + case <-ctx.Done(): + return ctx.Err() + } + } + if err := iter.Error(); err != nil { + return errors.Wrap(err, "filter iterator error") + } + return nil +} + func (s *EonPubKeySyncer) Start(ctx context.Context, runner service.Runner) error { + s.Log.Info( + "pubsyncer loop started", + ) if s.Handler == nil { return errors.New("no handler registered") } @@ -59,12 +95,14 @@ func (s *EonPubKeySyncer) Start(ctx context.Context, runner service.Runner) erro runner.Defer(func() { close(s.keyBroadcastCh) }) - subs, err := s.KeyBroadcast.WatchEonKeyBroadcast(watchOpts, s.keyBroadcastCh) - // FIXME: what to do on subs.Error() - if err != nil { - return err + if !s.DisableEventWatcher { + subs, err := s.KeyBroadcast.WatchEonKeyBroadcast(watchOpts, s.keyBroadcastCh) + // FIXME: what to do on subs.Error() + if err != nil { + return err + } + runner.Defer(subs.Unsubscribe) } - runner.Defer(subs.Unsubscribe) runner.Go(func() error { return s.watchNewEonPubkey(ctx) }) @@ -88,17 +126,23 @@ func (s *EonPubKeySyncer) getInitialPubKeys(ctx context.Context) ([]*event.EonPu if err != nil { return nil, err } - initialPubKeys := []*event.EonPublicKey{} + // NOTE: These are pubkeys that at the state of s.StartBlock + // are known to the contracts. + // That way we recreate older broadcast publickey events. + // We are only interested for keys that belong to keyper-set + // that are currently active or will become active in + // the future: for i := activeEon; i < numKS; i++ { e, err := s.GetEonPubKeyForEon(ctx, opts, i) - // FIXME: translate the error that there is no key - // to a continue of the loop - // (key not in mapping error, how can we catch that?) if err != nil { return nil, err } - initialPubKeys = append(initialPubKeys, e) + // if e == nil, this means the keyperset did not broadcast a + // key (yet) + if e != nil { + initialPubKeys = append(initialPubKeys, e) + } } return initialPubKeys, nil } @@ -118,11 +162,14 @@ func (s *EonPubKeySyncer) GetEonPubKeyForEon(ctx context.Context, opts *bind.Cal return nil, err } key, err := s.KeyBroadcast.GetEonKey(opts, eon) - // XXX: can the key be a null byte? - // I think we rather get a index out of bounds error. if err != nil { return nil, err } + // NOTE: Solidity returns the null value whenever + // one tries to access a key in mapping that doesn't exist + if len(key) == 0 { + return nil, nil + } return &event.EonPublicKey{ Eon: eon, Key: key, @@ -137,10 +184,11 @@ func (s *EonPubKeySyncer) watchNewEonPubkey(ctx context.Context) error { if !ok { return nil } + pubk := newEonKey.Key bn := newEonKey.Raw.BlockNumber ev := &event.EonPublicKey{ Eon: newEonKey.Eon, - Key: newEonKey.Key, + Key: pubk, AtBlockNumber: number.NewBlockNumber(&bn), } err := s.Handler(ctx, ev) diff --git a/rolling-shutter/medley/chainsync/syncer/keyperset.go b/rolling-shutter/medley/chainsync/syncer/keyperset.go index 91ff18810..aef0db113 100644 --- a/rolling-shutter/medley/chainsync/syncer/keyperset.go +++ b/rolling-shutter/medley/chainsync/syncer/keyperset.go @@ -21,16 +21,52 @@ func makeCallError(attrName string, err error) error { const channelSize = 10 +var _ ManualFilterHandler = &KeyperSetSyncer{} + type KeyperSetSyncer struct { - Client client.Client + Client client.EthereumClient Contract *bindings.KeyperSetManager Log log.Logger StartBlock *number.BlockNumber Handler event.KeyperSetHandler + // disable this when the QueryAndHandle manual polling should be used: + DisableEventWatcher bool keyperAddedCh chan *bindings.KeyperSetManagerKeyperSetAdded } +func (s *KeyperSetSyncer) QueryAndHandle(ctx context.Context, block uint64) error { + opts := &bind.FilterOpts{ + // FIXME: does this work, or do we need index -1 or something? + Start: block, + End: &block, + Context: ctx, + } + iter, err := s.Contract.FilterKeyperSetAdded(opts) + // TODO: what errors possible? + if err != nil { + return err + } + defer iter.Close() + + for iter.Next() { + select { + // XXX: this can be nil during the handlers startup. + // As far as I understand, a nil channel is never selected. + // Will it be selected as soon as the channel is not nil anymore? + case s.keyperAddedCh <- iter.Event: + case <-ctx.Done(): + return ctx.Err() + } + } + // XXX: it looks like this is nil when the iterator is + // exhausted without failures + if err := iter.Error(); err != nil { + return errors.Wrap(err, "filter iterator error") + } + return nil +} + func (s *KeyperSetSyncer) Start(ctx context.Context, runner service.Runner) error { if s.Handler == nil { return errors.New("no handler registered") @@ -69,12 +105,14 @@ func (s *KeyperSetSyncer) Start(ctx context.Context, runner service.Runner) erro runner.Defer(func() { close(s.keyperAddedCh) }) - subs, err := s.Contract.WatchKeyperSetAdded(watchOpts, s.keyperAddedCh) - // FIXME: what to do on subs.Error() - if err != nil { - return err + if !s.DisableEventWatcher { + subs, err := s.Contract.WatchKeyperSetAdded(watchOpts, s.keyperAddedCh) + // FIXME: what to do on subs.Error() + if err != nil { + return err + } + runner.Defer(subs.Unsubscribe) } - runner.Defer(subs.Unsubscribe) runner.Go(func() error { return s.watchNewKeypersService(ctx) }) diff --git a/rolling-shutter/medley/chainsync/syncer/shutterstate.go b/rolling-shutter/medley/chainsync/syncer/shutterstate.go index e262d679a..a510d4650 100644 --- a/rolling-shutter/medley/chainsync/syncer/shutterstate.go +++ b/rolling-shutter/medley/chainsync/syncer/shutterstate.go @@ -14,12 +14,15 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" ) +var _ ManualFilterHandler = &ShutterStateSyncer{} + type ShutterStateSyncer struct { - Client client.Client - Contract *bindings.KeyperSetManager - StartBlock *number.BlockNumber - Log log.Logger - Handler event.ShutterStateHandler + Client client.EthereumClient + Contract *bindings.KeyperSetManager + StartBlock *number.BlockNumber + Log log.Logger + Handler event.ShutterStateHandler + DisableEventWatcher bool pausedCh chan *bindings.KeyperSetManagerPaused unpausedCh chan *bindings.KeyperSetManagerUnpaused @@ -40,50 +43,96 @@ func (s *ShutterStateSyncer) GetShutterState(ctx context.Context, opts *bind.Cal }, nil } +func (s *ShutterStateSyncer) QueryAndHandle(ctx context.Context, block uint64) error { + opts := &bind.FilterOpts{ + Start: block, + End: &block, + Context: ctx, + } + iterPaused, err := s.Contract.FilterPaused(opts) + if err != nil { + return err + } + defer iterPaused.Close() + + for iterPaused.Next() { + select { + case s.pausedCh <- iterPaused.Event: + case <-ctx.Done(): + return ctx.Err() + } + } + if err := iterPaused.Error(); err != nil { + return errors.Wrap(err, "filter iterator error") + } + iterUnpaused, err := s.Contract.FilterUnpaused(opts) + if err != nil { + return err + } + defer iterUnpaused.Close() + + for iterUnpaused.Next() { + select { + case s.unpausedCh <- iterUnpaused.Event: + case <-ctx.Done(): + return ctx.Err() + } + } + if err := iterUnpaused.Error(); err != nil { + return errors.Wrap(err, "filter iterator error") + } + return nil +} + func (s *ShutterStateSyncer) Start(ctx context.Context, runner service.Runner) error { if s.Handler == nil { return errors.New("no handler registered") } - watchOpts := &bind.WatchOpts{ - Start: s.StartBlock.ToUInt64Ptr(), // nil means latest + // the latest block still has to be fixed. + // otherwise we could skip some block events + // between the initial poll and the subscription. + if s.StartBlock.IsLatest() { + latest, err := s.Client.BlockNumber(ctx) + if err != nil { + return err + } + s.StartBlock.SetUint64(latest) + } + + opts := &bind.WatchOpts{ + Start: s.StartBlock.ToUInt64Ptr(), Context: ctx, } s.pausedCh = make(chan *bindings.KeyperSetManagerPaused) - subs, err := s.Contract.WatchPaused(watchOpts, s.pausedCh) - // FIXME: what to do on subs.Error() - if err != nil { - return err - } - runner.Defer(subs.Unsubscribe) runner.Defer(func() { close(s.pausedCh) }) - s.unpausedCh = make(chan *bindings.KeyperSetManagerUnpaused) - subs, err = s.Contract.WatchUnpaused(watchOpts, s.unpausedCh) - // FIXME: what to do on subs.Error() - if err != nil { - return err - } - runner.Defer(subs.Unsubscribe) runner.Defer(func() { close(s.unpausedCh) }) + if !s.DisableEventWatcher { + subs, err := s.Contract.WatchPaused(opts, s.pausedCh) + // FIXME: what to do on subs.Error() + if err != nil { + return err + } + runner.Defer(subs.Unsubscribe) + subs, err = s.Contract.WatchUnpaused(opts, s.unpausedCh) + // FIXME: what to do on subs.Error() + if err != nil { + return err + } + runner.Defer(subs.Unsubscribe) + } + runner.Go(func() error { return s.watchPaused(ctx) }) return nil } -func (s *ShutterStateSyncer) pollIsActive(ctx context.Context) (bool, error) { - callOpts := bind.CallOpts{ - Context: ctx, - } - paused, err := s.Contract.Paused(&callOpts) - return !paused, err -} - func (s *ShutterStateSyncer) handle(ctx context.Context, ev *event.ShutterState) { err := s.Handler(ctx, ev) if err != nil { @@ -96,40 +145,56 @@ func (s *ShutterStateSyncer) handle(ctx context.Context, ev *event.ShutterState) } func (s *ShutterStateSyncer) watchPaused(ctx context.Context) error { - isActive, err := s.pollIsActive(ctx) + // query the initial state + // and construct a "virtual" + // event + opts := &bind.CallOpts{ + BlockNumber: s.StartBlock.Int, + Context: nil, + } + + stateAtStartBlock, err := s.GetShutterState(ctx, opts) if err != nil { // XXX: this will fail everything, do we want that? return err } - ev := &event.ShutterState{ - Active: isActive, - } - s.handle(ctx, ev) + s.handle(ctx, stateAtStartBlock) + lastState := stateAtStartBlock for { select { - case _, ok := <-s.unpausedCh: + case unpaused, ok := <-s.unpausedCh: if !ok { return nil } - if isActive { - s.Log.Error("state mismatch", "got", "actice", "have", "inactive") + if lastState.Active { + s.Log.Warn( + "state/event mismatch, but continue handler", + "new-event", "Unpaused", + "last-state", "active", + ) } + block := unpaused.Raw.BlockNumber ev := &event.ShutterState{ - Active: true, + Active: true, + AtBlockNumber: number.NewBlockNumber(&block), } - isActive = ev.Active s.handle(ctx, ev) - case _, ok := <-s.pausedCh: + case paused, ok := <-s.pausedCh: if !ok { return nil } - if isActive { - s.Log.Error("state mismatch", "got", "inactive", "have", "active") + if !lastState.Active { + s.Log.Warn( + "state/event mismatch, but continue handler", + "new-event", "Paused", + "last-state", "inactive", + ) } + block := paused.Raw.BlockNumber ev := &event.ShutterState{ - Active: false, + Active: false, + AtBlockNumber: number.NewBlockNumber(&block), } - isActive = ev.Active s.handle(ctx, ev) case <-ctx.Done(): return ctx.Err() diff --git a/rolling-shutter/medley/chainsync/syncer/unsafehead.go b/rolling-shutter/medley/chainsync/syncer/unsafehead.go index 86ddbd1b3..667d0d3ff 100644 --- a/rolling-shutter/medley/chainsync/syncer/unsafehead.go +++ b/rolling-shutter/medley/chainsync/syncer/unsafehead.go @@ -14,14 +14,19 @@ import ( ) type UnsafeHeadSyncer struct { - Client client.Client + Client client.EthereumClient Log log.Logger Handler event.BlockHandler + // Handler to be manually triggered + // to handle their handler function + // before the own Handler is called: + SyncedHandler []ManualFilterHandler newLatestHeadCh chan *types.Header } func (s *UnsafeHeadSyncer) Start(ctx context.Context, runner service.Runner) error { + s.Log.Info("unsafe head syncer started") if s.Handler == nil { return errors.New("no handler registered") } @@ -42,6 +47,10 @@ func (s *UnsafeHeadSyncer) Start(ctx context.Context, runner service.Runner) err return nil } +func parseLogs() error { + return nil +} + func (s *UnsafeHeadSyncer) watchLatestUnsafeHead(ctx context.Context) error { for { select { @@ -49,8 +58,25 @@ func (s *UnsafeHeadSyncer) watchLatestUnsafeHead(ctx context.Context) error { if !ok { return nil } + // TODO: check bloom filter for topic of all + // synced handlers and only call them if + // the bloomfilter retrieves something. + + blockNum := number.BigToBlockNumber(newHeader.Number) + for _, h := range s.SyncedHandler { + // NOTE: this has to be blocking! + // So whenever this returns, it is expected + // that the handlers Handle function + // has been called and it returned. + err := h.QueryAndHandle(ctx, blockNum.Uint64()) + if err != nil { + // XXX: return or log? + // return err + s.Log.Error("synced handler call errored, skipping", "error", err) + } + } ev := &event.LatestBlock{ - Number: number.BigToBlockNumber(newHeader.Number), + Number: blockNum, BlockHash: newHeader.Hash(), Header: newHeader, } diff --git a/rolling-shutter/medley/chainsync/syncer/util.go b/rolling-shutter/medley/chainsync/syncer/util.go index 0c44cf3dc..916e93141 100644 --- a/rolling-shutter/medley/chainsync/syncer/util.go +++ b/rolling-shutter/medley/chainsync/syncer/util.go @@ -18,6 +18,10 @@ var ( errLatestBlock = errors.New("'nil' latest block") ) +type ManualFilterHandler interface { + QueryAndHandle(ctx context.Context, block uint64) error +} + func logToCallOpts(ctx context.Context, log *types.Log) *bind.CallOpts { block := new(big.Int) block.SetUint64(log.BlockNumber) @@ -40,7 +44,7 @@ func guardCallOpts(opts *bind.CallOpts, allowLatest bool) error { return nil } -func fixCallOpts(ctx context.Context, c client.Client, opts *bind.CallOpts) (*bind.CallOpts, *uint64, error) { +func fixCallOpts(ctx context.Context, c client.EthereumClient, opts *bind.CallOpts) (*bind.CallOpts, *uint64, error) { err := guardCallOpts(opts, false) if err == nil { return opts, nil, nil From d44c6bdd5da737b3dd12830d038b7747226f6bae Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld <15726643+ezdac@users.noreply.github.com> Date: Fri, 10 May 2024 16:20:51 +0200 Subject: [PATCH 86/91] fix(op): one-off error in decryption-trigger on new block --- rolling-shutter/keyperimpl/optimism/keyper.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rolling-shutter/keyperimpl/optimism/keyper.go b/rolling-shutter/keyperimpl/optimism/keyper.go index aac660780..922b1e784 100644 --- a/rolling-shutter/keyperimpl/optimism/keyper.go +++ b/rolling-shutter/keyperimpl/optimism/keyper.go @@ -99,9 +99,10 @@ func (kpr *Keyper) newBlock(_ context.Context, ev *syncevent.LatestBlock) error // TODO: sanity checks - idPreimage := identitypreimage.BigToIdentityPreimage(ev.Number.Int) + latestBlockNumber := ev.Number.Uint64() + idPreimage := identitypreimage.Uint64ToIdentityPreimage(latestBlockNumber + 1) trig := &epochkghandler.DecryptionTrigger{ - BlockNumber: ev.Number.Uint64(), + BlockNumber: latestBlockNumber + 1, IdentityPreimages: []identitypreimage.IdentityPreimage{idPreimage}, } From 74d780f8f0fd7971db1c116932d908e3eeba3393 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld <15726643+ezdac@users.noreply.github.com> Date: Fri, 10 May 2024 16:20:54 +0200 Subject: [PATCH 87/91] feat(chainsync): allow not fetching past active events --- rolling-shutter/medley/chainsync/options.go | 54 ++++++++++++------- .../medley/chainsync/syncer/eonpubkey.go | 30 ++++++----- .../medley/chainsync/syncer/keyperset.go | 28 +++++----- .../medley/chainsync/syncer/shutterstate.go | 40 +++++--------- 4 files changed, 81 insertions(+), 71 deletions(-) diff --git a/rolling-shutter/medley/chainsync/options.go b/rolling-shutter/medley/chainsync/options.go index 36d8e3c9a..77e05bc55 100644 --- a/rolling-shutter/medley/chainsync/options.go +++ b/rolling-shutter/medley/chainsync/options.go @@ -28,6 +28,7 @@ type options struct { logger log.Logger runner service.Runner syncStart *number.BlockNumber + fetchActivesAtSyncStart bool privKey *ecdsa.PrivateKey handlerShutterState event.ShutterStateHandler @@ -91,12 +92,13 @@ func (o *options) apply(ctx context.Context, c *Client) error { return err } c.kssync = &syncer.KeyperSetSyncer{ - Client: client, - Contract: c.KeyperSetManager, - Log: c.log, - StartBlock: o.syncStart, - Handler: o.handlerKeyperSet, - DisableEventWatcher: true, + Client: client, + Contract: c.KeyperSetManager, + Log: c.log, + StartBlock: o.syncStart, + Handler: o.handlerKeyperSet, + FetchActiveAtStartBlock: o.fetchActivesAtSyncStart, + DisableEventWatcher: true, } if o.handlerKeyperSet != nil { c.services = append(c.services, c.kssync) @@ -108,13 +110,14 @@ func (o *options) apply(ctx context.Context, c *Client) error { return err } c.epksync = &syncer.EonPubKeySyncer{ - Client: client, - Log: c.log, - KeyBroadcast: c.KeyBroadcast, - KeyperSetManager: c.KeyperSetManager, - Handler: o.handlerEonPublicKey, - StartBlock: o.syncStart, - DisableEventWatcher: true, + Client: client, + Log: c.log, + KeyBroadcast: c.KeyBroadcast, + KeyperSetManager: c.KeyperSetManager, + Handler: o.handlerEonPublicKey, + StartBlock: o.syncStart, + FetchActiveAtStartBlock: o.fetchActivesAtSyncStart, + DisableEventWatcher: true, } if o.handlerEonPublicKey != nil { c.services = append(c.services, c.epksync) @@ -122,12 +125,13 @@ func (o *options) apply(ctx context.Context, c *Client) error { } c.sssync = &syncer.ShutterStateSyncer{ - Client: client, - Contract: c.KeyperSetManager, - Log: c.log, - Handler: o.handlerShutterState, - StartBlock: o.syncStart, - DisableEventWatcher: true, + Client: client, + Contract: c.KeyperSetManager, + Log: c.log, + Handler: o.handlerShutterState, + StartBlock: o.syncStart, + FetchActiveAtStartBlock: o.fetchActivesAtSyncStart, + DisableEventWatcher: true, } if o.handlerShutterState != nil { c.services = append(c.services, c.sssync) @@ -169,11 +173,21 @@ func defaultOptions() *options { ethClient: nil, logger: noopLogger, runner: nil, + fetchActivesAtSyncStart: true, syncStart: number.NewBlockNumber(nil), } } -func WithSyncStartBlock(blockNumber *number.BlockNumber) Option { +func WithNoFetchActivesBeforeStart() Option { + return func(o *options) error { + o.fetchActivesAtSyncStart = false + return nil + } +} + +func WithSyncStartBlock( + blockNumber *number.BlockNumber, +) Option { if blockNumber == nil { blockNumber = number.NewBlockNumber(nil) } diff --git a/rolling-shutter/medley/chainsync/syncer/eonpubkey.go b/rolling-shutter/medley/chainsync/syncer/eonpubkey.go index 635a10028..f973abe47 100644 --- a/rolling-shutter/medley/chainsync/syncer/eonpubkey.go +++ b/rolling-shutter/medley/chainsync/syncer/eonpubkey.go @@ -18,13 +18,14 @@ import ( var _ ManualFilterHandler = &EonPubKeySyncer{} type EonPubKeySyncer struct { - Client client.EthereumClient - Log log.Logger - KeyBroadcast *bindings.KeyBroadcastContract - KeyperSetManager *bindings.KeyperSetManager - StartBlock *number.BlockNumber - Handler event.EonPublicKeyHandler - DisableEventWatcher bool + Client client.EthereumClient + Log log.Logger + KeyBroadcast *bindings.KeyBroadcastContract + KeyperSetManager *bindings.KeyperSetManager + StartBlock *number.BlockNumber + Handler event.EonPublicKeyHandler + FetchActiveAtStartBlock bool + DisableEventWatcher bool keyBroadcastCh chan *bindings.KeyBroadcastContractEonKeyBroadcast } @@ -76,15 +77,18 @@ func (s *EonPubKeySyncer) Start(ctx context.Context, runner service.Runner) erro } s.StartBlock.SetUint64(latest) } - pubKs, err := s.getInitialPubKeys(ctx) - if err != nil { - return err - } - for _, k := range pubKs { - err := s.Handler(ctx, k) + + if s.FetchActiveAtStartBlock { + pubKs, err := s.getInitialPubKeys(ctx) if err != nil { return err } + for _, k := range pubKs { + err := s.Handler(ctx, k) + if err != nil { + return err + } + } } watchOpts := &bind.WatchOpts{ diff --git a/rolling-shutter/medley/chainsync/syncer/keyperset.go b/rolling-shutter/medley/chainsync/syncer/keyperset.go index aef0db113..d07c1bd0a 100644 --- a/rolling-shutter/medley/chainsync/syncer/keyperset.go +++ b/rolling-shutter/medley/chainsync/syncer/keyperset.go @@ -30,7 +30,8 @@ type KeyperSetSyncer struct { StartBlock *number.BlockNumber Handler event.KeyperSetHandler // disable this when the QueryAndHandle manual polling should be used: - DisableEventWatcher bool + FetchActiveAtStartBlock bool + DisableEventWatcher bool keyperAddedCh chan *bindings.KeyperSetManagerKeyperSetAdded } @@ -87,18 +88,21 @@ func (s *KeyperSetSyncer) Start(ctx context.Context, runner service.Runner) erro Start: s.StartBlock.ToUInt64Ptr(), Context: ctx, } - initial, err := s.getInitialKeyperSets(ctx) - if err != nil { - return err - } - for _, ks := range initial { - err = s.Handler(ctx, ks) + + if s.FetchActiveAtStartBlock { + initial, err := s.getInitialKeyperSets(ctx) if err != nil { - s.Log.Error( - "handler for `NewKeyperSet` errored for initial sync", - "error", - err.Error(), - ) + return err + } + for _, ks := range initial { + err = s.Handler(ctx, ks) + if err != nil { + s.Log.Error( + "handler for `NewKeyperSet` errored for initial sync", + "error", + err.Error(), + ) + } } } s.keyperAddedCh = make(chan *bindings.KeyperSetManagerKeyperSetAdded, channelSize) diff --git a/rolling-shutter/medley/chainsync/syncer/shutterstate.go b/rolling-shutter/medley/chainsync/syncer/shutterstate.go index a510d4650..058772857 100644 --- a/rolling-shutter/medley/chainsync/syncer/shutterstate.go +++ b/rolling-shutter/medley/chainsync/syncer/shutterstate.go @@ -17,12 +17,13 @@ import ( var _ ManualFilterHandler = &ShutterStateSyncer{} type ShutterStateSyncer struct { - Client client.EthereumClient - Contract *bindings.KeyperSetManager - StartBlock *number.BlockNumber - Log log.Logger - Handler event.ShutterStateHandler - DisableEventWatcher bool + Client client.EthereumClient + Contract *bindings.KeyperSetManager + StartBlock *number.BlockNumber + Log log.Logger + Handler event.ShutterStateHandler + FetchActiveAtStartBlock bool + DisableEventWatcher bool pausedCh chan *bindings.KeyperSetManagerPaused unpausedCh chan *bindings.KeyperSetManagerUnpaused @@ -153,26 +154,20 @@ func (s *ShutterStateSyncer) watchPaused(ctx context.Context) error { Context: nil, } - stateAtStartBlock, err := s.GetShutterState(ctx, opts) - if err != nil { - // XXX: this will fail everything, do we want that? - return err + if s.FetchActiveAtStartBlock { + stateAtStartBlock, err := s.GetShutterState(ctx, opts) + if err != nil { + // XXX: this will fail everything, do we want that? + return err + } + s.handle(ctx, stateAtStartBlock) } - s.handle(ctx, stateAtStartBlock) - lastState := stateAtStartBlock for { select { case unpaused, ok := <-s.unpausedCh: if !ok { return nil } - if lastState.Active { - s.Log.Warn( - "state/event mismatch, but continue handler", - "new-event", "Unpaused", - "last-state", "active", - ) - } block := unpaused.Raw.BlockNumber ev := &event.ShutterState{ Active: true, @@ -183,13 +178,6 @@ func (s *ShutterStateSyncer) watchPaused(ctx context.Context) error { if !ok { return nil } - if !lastState.Active { - s.Log.Warn( - "state/event mismatch, but continue handler", - "new-event", "Paused", - "last-state", "inactive", - ) - } block := paused.Raw.BlockNumber ev := &event.ShutterState{ Active: false, From 65dcfe764bbfed06199cbd806ebed53346702725 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld <15726643+ezdac@users.noreply.github.com> Date: Fri, 10 May 2024 16:20:57 +0200 Subject: [PATCH 88/91] feat: consider reorgs in chainsync --- .../medley/chainsync/syncer/unsafehead.go | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/rolling-shutter/medley/chainsync/syncer/unsafehead.go b/rolling-shutter/medley/chainsync/syncer/unsafehead.go index 667d0d3ff..60a0b783b 100644 --- a/rolling-shutter/medley/chainsync/syncer/unsafehead.go +++ b/rolling-shutter/medley/chainsync/syncer/unsafehead.go @@ -3,6 +3,7 @@ package syncer import ( "context" "errors" + "math/big" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" @@ -52,17 +53,45 @@ func parseLogs() error { } func (s *UnsafeHeadSyncer) watchLatestUnsafeHead(ctx context.Context) error { + var currentBlock *big.Int for { select { case newHeader, ok := <-s.newLatestHeadCh: if !ok { return nil } + blockNum := number.BigToBlockNumber(newHeader.Number) + if currentBlock != nil { + switch newHeader.Number.Cmp(currentBlock) { + case -1, 0: + prevNum := new(big.Int).Sub(newHeader.Number, big.NewInt(1)) + prevBlockNum := number.BigToBlockNumber(prevNum) + // Re-emit the previous block, to pre-emptively signal an + // incoming reorg. Like this a client is able to e.g. + // rewind changes first before processing the new + // events of the reorg + ev := &event.LatestBlock{ + Number: prevBlockNum, + BlockHash: newHeader.ParentHash, + } + err := s.Handler(ctx, ev) + if err != nil { + // XXX: return or log? + // return err + s.Log.Error( + "handler for `NewLatestBlock` errored", + "error", + err.Error(), + ) + } + case 1: + // expected + } + } + // TODO: check bloom filter for topic of all // synced handlers and only call them if // the bloomfilter retrieves something. - - blockNum := number.BigToBlockNumber(newHeader.Number) for _, h := range s.SyncedHandler { // NOTE: this has to be blocking! // So whenever this returns, it is expected @@ -88,6 +117,7 @@ func (s *UnsafeHeadSyncer) watchLatestUnsafeHead(ctx context.Context) error { err.Error(), ) } + currentBlock = newHeader.Number case <-ctx.Done(): return ctx.Err() } From cdb0a9e1a733f51b15eb8193b4758c2e988edd7c Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld <15726643+ezdac@users.noreply.github.com> Date: Fri, 10 May 2024 16:21:00 +0200 Subject: [PATCH 89/91] fix(chainsync): sync all blocks from start to latest head Before, the chainsync did not fetch all subscribed events from the provided 'WithSyncStartBlock' to the current latest head. This is now fixed with a mechanism where the chainsync is progressing a sync-head in tandem with the incoming updates for the latest head. The syncer will catch up with polling for all older blocks until the current latest head is reached. --- rolling-shutter/medley/chainsync/options.go | 145 ++++----- .../medley/chainsync/syncer/eonpubkey.go | 123 ++----- .../medley/chainsync/syncer/keyperset.go | 148 +++------ .../medley/chainsync/syncer/shutterstate.go | 132 ++------ .../medley/chainsync/syncer/unsafehead.go | 303 ++++++++++++++---- .../medley/chainsync/syncer/util.go | 1 + 6 files changed, 409 insertions(+), 443 deletions(-) diff --git a/rolling-shutter/medley/chainsync/options.go b/rolling-shutter/medley/chainsync/options.go index 77e05bc55..076e1666c 100644 --- a/rolling-shutter/medley/chainsync/options.go +++ b/rolling-shutter/medley/chainsync/options.go @@ -39,132 +39,119 @@ type options struct { func (o *options) verify() error { if o.clientURL != "" && o.ethClient != nil { - // TODO: error message - return errors.New("can't use client and client url") + return errors.New("'WithClient' and 'WithClientURL' options are mutually exclusive") } if o.clientURL == "" && o.ethClient == nil { - // TODO: error message - return errors.New("have to provide either url or client") + return errors.New("either 'WithClient' or 'WithClientURL' options are expected") } // TODO: check for the existence of the contract addresses depending on // what handlers are not nil return nil } -// initialize the shutter client and apply the options. -// the context is only the initialisation context, -// and should not be considered to handle the lifecycle -// of shutter clients background workers. -func (o *options) apply(ctx context.Context, c *Client) error { - var ( - client syncclient.EthereumClient - err error - ) - if o.clientURL != "" { - o.ethClient, err = ethclient.DialContext(ctx, o.clientURL) - if err != nil { - return err - } - } - client = o.ethClient - - c.EthereumClient = client - - if o.logger != nil { - c.log = o.logger - // NOCHECKIN: - c.log.Info("got logger in options") - } - +func (o *options) applyHandler(c *Client) error { + var err error syncedServices := []syncer.ManualFilterHandler{} - // the nil passthrough will use "latest" for each call, - // but we want to harmonize and fix the sync start to a specific block. - if o.syncStart.IsLatest() { - latestBlock, err := c.EthereumClient.BlockNumber(ctx) - if err != nil { - return errors.Wrap(err, "polling latest block") - } - o.syncStart = number.NewBlockNumber(&latestBlock) - } - c.KeyperSetManager, err = bindings.NewKeyperSetManager(*o.keyperSetManagerAddress, client) + c.KeyperSetManager, err = bindings.NewKeyperSetManager(*o.keyperSetManagerAddress, o.ethClient) if err != nil { return err } c.kssync = &syncer.KeyperSetSyncer{ - Client: client, - Contract: c.KeyperSetManager, - Log: c.log, - StartBlock: o.syncStart, - Handler: o.handlerKeyperSet, - FetchActiveAtStartBlock: o.fetchActivesAtSyncStart, - DisableEventWatcher: true, + Client: o.ethClient, + Contract: c.KeyperSetManager, + Log: c.log, + Handler: o.handlerKeyperSet, } if o.handlerKeyperSet != nil { - c.services = append(c.services, c.kssync) syncedServices = append(syncedServices, c.kssync) } - c.KeyBroadcast, err = bindings.NewKeyBroadcastContract(*o.keyBroadcastContractAddress, client) + c.KeyBroadcast, err = bindings.NewKeyBroadcastContract(*o.keyBroadcastContractAddress, o.ethClient) if err != nil { return err } c.epksync = &syncer.EonPubKeySyncer{ - Client: client, - Log: c.log, - KeyBroadcast: c.KeyBroadcast, - KeyperSetManager: c.KeyperSetManager, - Handler: o.handlerEonPublicKey, - StartBlock: o.syncStart, - FetchActiveAtStartBlock: o.fetchActivesAtSyncStart, - DisableEventWatcher: true, + Client: o.ethClient, + Log: c.log, + KeyBroadcast: c.KeyBroadcast, + KeyperSetManager: c.KeyperSetManager, + Handler: o.handlerEonPublicKey, } if o.handlerEonPublicKey != nil { - c.services = append(c.services, c.epksync) syncedServices = append(syncedServices, c.epksync) } - c.sssync = &syncer.ShutterStateSyncer{ - Client: client, - Contract: c.KeyperSetManager, - Log: c.log, - Handler: o.handlerShutterState, - StartBlock: o.syncStart, - FetchActiveAtStartBlock: o.fetchActivesAtSyncStart, - DisableEventWatcher: true, + Client: o.ethClient, + Contract: c.KeyperSetManager, + Log: c.log, + Handler: o.handlerShutterState, } if o.handlerShutterState != nil { - c.services = append(c.services, c.sssync) syncedServices = append(syncedServices, c.sssync) } if o.handlerBlock == nil { - // NOOP - but we need to run the UnsafeHeadSyncer. - // This is to keep the inner workings consisten, - // we use the DisableEventWatcher mechanism in combination - // with the UnsafeHeadSyncer instead of the streaming - // Watch... subscription on events. - // TODO: think about allowing the streaming events, - // when guaranteed event order (event1,event2,new-block-event) - // is not required + // Even if the user is not interested in handling new block events, + // the streaming block handler must be running in order to + // synchronize polling of new contract events. + // Since the handler function is always called, we need to + // inject a noop-handler o.handlerBlock = func(ctx context.Context, lb *event.LatestBlock) error { return nil } } c.uhsync = &syncer.UnsafeHeadSyncer{ - Client: client, - Log: c.log, - Handler: o.handlerBlock, - SyncedHandler: syncedServices, + Client: o.ethClient, + Log: c.log, + Handler: o.handlerBlock, + SyncedHandler: syncedServices, + FetchActiveAtStart: o.fetchActivesAtSyncStart, + SyncStartBlock: o.syncStart, } if o.handlerBlock != nil { c.services = append(c.services, c.uhsync) } - c.privKey = o.privKey return nil } +// initialize the shutter client and apply the options. +// the context is only the initialisation context, +// and should not be considered to handle the lifecycle +// of shutter clients background workers. +func (o *options) apply(ctx context.Context, c *Client) error { + var ( + client syncclient.EthereumClient + err error + ) + if o.clientURL != "" { + o.ethClient, err = ethclient.DialContext(ctx, o.clientURL) + if err != nil { + return err + } + } + client = o.ethClient + c.EthereumClient = client + + // the nil passthrough will use "latest" for each call, + // but we want to harmonize and fix the sync start to a specific block. + if o.syncStart.IsLatest() { + latestBlock, err := c.EthereumClient.BlockNumber(ctx) + if err != nil { + return errors.Wrap(err, "polling latest block") + } + o.syncStart = number.NewBlockNumber(&latestBlock) + } + + if o.logger != nil { + c.log = o.logger + } + + c.privKey = o.privKey + return o.applyHandler(c) +} + func defaultOptions() *options { return &options{ keyperSetManagerAddress: &predeploy.KeyperSetManagerAddr, diff --git a/rolling-shutter/medley/chainsync/syncer/eonpubkey.go b/rolling-shutter/medley/chainsync/syncer/eonpubkey.go index f973abe47..7aa6b8171 100644 --- a/rolling-shutter/medley/chainsync/syncer/eonpubkey.go +++ b/rolling-shutter/medley/chainsync/syncer/eonpubkey.go @@ -12,22 +12,16 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/client" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" - "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" ) var _ ManualFilterHandler = &EonPubKeySyncer{} type EonPubKeySyncer struct { - Client client.EthereumClient - Log log.Logger - KeyBroadcast *bindings.KeyBroadcastContract - KeyperSetManager *bindings.KeyperSetManager - StartBlock *number.BlockNumber - Handler event.EonPublicKeyHandler - FetchActiveAtStartBlock bool - DisableEventWatcher bool - - keyBroadcastCh chan *bindings.KeyBroadcastContractEonKeyBroadcast + Client client.EthereumClient + Log log.Logger + KeyBroadcast *bindings.KeyBroadcastContract + KeyperSetManager *bindings.KeyperSetManager + Handler event.EonPublicKeyHandler } func (s *EonPubKeySyncer) QueryAndHandle(ctx context.Context, block uint64) error { @@ -48,10 +42,13 @@ func (s *EonPubKeySyncer) QueryAndHandle(ctx context.Context, block uint64) erro defer iter.Close() for iter.Next() { - select { - case s.keyBroadcastCh <- iter.Event: - case <-ctx.Done(): - return ctx.Err() + err := s.handle(ctx, iter.Event) + if err != nil { + s.Log.Error( + "handler for `NewKeyperSet` errored", + "error", + err.Error(), + ) } } if err := iter.Error(); err != nil { @@ -60,65 +57,41 @@ func (s *EonPubKeySyncer) QueryAndHandle(ctx context.Context, block uint64) erro return nil } -func (s *EonPubKeySyncer) Start(ctx context.Context, runner service.Runner) error { - s.Log.Info( - "pubsyncer loop started", - ) - if s.Handler == nil { - return errors.New("no handler registered") - } - // the latest block still has to be fixed. - // otherwise we could skip some block events - // between the initial poll and the subscription. - if s.StartBlock.IsLatest() { - latest, err := s.Client.BlockNumber(ctx) - if err != nil { - return err - } - s.StartBlock.SetUint64(latest) - } - - if s.FetchActiveAtStartBlock { - pubKs, err := s.getInitialPubKeys(ctx) - if err != nil { - return err - } - for _, k := range pubKs { - err := s.Handler(ctx, k) - if err != nil { - return err - } - } +func (s *EonPubKeySyncer) handle(ctx context.Context, newEonKey *bindings.KeyBroadcastContractEonKeyBroadcast) error { + pubk := newEonKey.Key + bn := newEonKey.Raw.BlockNumber + ev := &event.EonPublicKey{ + Eon: newEonKey.Eon, + Key: pubk, + AtBlockNumber: number.NewBlockNumber(&bn), + } + err := s.Handler(ctx, ev) + if err != nil { + return err } + return nil +} - watchOpts := &bind.WatchOpts{ - Start: s.StartBlock.ToUInt64Ptr(), - Context: ctx, +func (s *EonPubKeySyncer) HandleVirtualEvent(ctx context.Context, block *number.BlockNumber) error { + pubKs, err := s.getInitialPubKeys(ctx, block) + if err != nil { + return err } - s.keyBroadcastCh = make(chan *bindings.KeyBroadcastContractEonKeyBroadcast, channelSize) - runner.Defer(func() { - close(s.keyBroadcastCh) - }) - if !s.DisableEventWatcher { - subs, err := s.KeyBroadcast.WatchEonKeyBroadcast(watchOpts, s.keyBroadcastCh) - // FIXME: what to do on subs.Error() + for _, k := range pubKs { + err := s.Handler(ctx, k) if err != nil { return err } - runner.Defer(subs.Unsubscribe) } - runner.Go(func() error { - return s.watchNewEonPubkey(ctx) - }) return nil } -func (s *EonPubKeySyncer) getInitialPubKeys(ctx context.Context) ([]*event.EonPublicKey, error) { +func (s *EonPubKeySyncer) getInitialPubKeys(ctx context.Context, block *number.BlockNumber) ([]*event.EonPublicKey, error) { // This blocknumber specifies AT what state // the contract is called opts := &bind.CallOpts{ Context: ctx, - BlockNumber: s.StartBlock.Int, + BlockNumber: block.Int, } numKS, err := s.KeyperSetManager.GetNumKeyperSets(opts) if err != nil { @@ -126,7 +99,7 @@ func (s *EonPubKeySyncer) getInitialPubKeys(ctx context.Context) ([]*event.EonPu } // this blocknumber specifies the argument to the contract // getter - activeEon, err := s.KeyperSetManager.GetKeyperSetIndexByBlock(opts, s.StartBlock.Uint64()) + activeEon, err := s.KeyperSetManager.GetKeyperSetIndexByBlock(opts, block.Uint64()) if err != nil { return nil, err } @@ -180,31 +153,3 @@ func (s *EonPubKeySyncer) GetEonPubKeyForEon(ctx context.Context, opts *bind.Cal AtBlockNumber: number.BigToBlockNumber(opts.BlockNumber), }, nil } - -func (s *EonPubKeySyncer) watchNewEonPubkey(ctx context.Context) error { - for { - select { - case newEonKey, ok := <-s.keyBroadcastCh: - if !ok { - return nil - } - pubk := newEonKey.Key - bn := newEonKey.Raw.BlockNumber - ev := &event.EonPublicKey{ - Eon: newEonKey.Eon, - Key: pubk, - AtBlockNumber: number.NewBlockNumber(&bn), - } - err := s.Handler(ctx, ev) - if err != nil { - s.Log.Error( - "handler for `NewKeyperSet` errored", - "error", - err.Error(), - ) - } - case <-ctx.Done(): - return ctx.Err() - } - } -} diff --git a/rolling-shutter/medley/chainsync/syncer/keyperset.go b/rolling-shutter/medley/chainsync/syncer/keyperset.go index d07c1bd0a..d0c50af19 100644 --- a/rolling-shutter/medley/chainsync/syncer/keyperset.go +++ b/rolling-shutter/medley/chainsync/syncer/keyperset.go @@ -12,7 +12,6 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/client" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" - "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" ) func makeCallError(attrName string, err error) error { @@ -24,114 +23,67 @@ const channelSize = 10 var _ ManualFilterHandler = &KeyperSetSyncer{} type KeyperSetSyncer struct { - Client client.EthereumClient - Contract *bindings.KeyperSetManager - Log log.Logger - StartBlock *number.BlockNumber - Handler event.KeyperSetHandler - // disable this when the QueryAndHandle manual polling should be used: - FetchActiveAtStartBlock bool - DisableEventWatcher bool - - keyperAddedCh chan *bindings.KeyperSetManagerKeyperSetAdded + Client client.EthereumClient + Contract *bindings.KeyperSetManager + Log log.Logger + Handler event.KeyperSetHandler } func (s *KeyperSetSyncer) QueryAndHandle(ctx context.Context, block uint64) error { opts := &bind.FilterOpts{ - // FIXME: does this work, or do we need index -1 or something? Start: block, End: &block, Context: ctx, } iter, err := s.Contract.FilterKeyperSetAdded(opts) - // TODO: what errors possible? if err != nil { return err } defer iter.Close() for iter.Next() { - select { - // XXX: this can be nil during the handlers startup. - // As far as I understand, a nil channel is never selected. - // Will it be selected as soon as the channel is not nil anymore? - case s.keyperAddedCh <- iter.Event: - case <-ctx.Done(): - return ctx.Err() + err := s.handle(ctx, iter.Event) + if err != nil { + s.Log.Error( + "handler for `NewKeyperSet` errored", + "error", + err.Error(), + ) } } - // XXX: it looks like this is nil when the iterator is - // exhausted without failures if err := iter.Error(); err != nil { return errors.Wrap(err, "filter iterator error") } return nil } -func (s *KeyperSetSyncer) Start(ctx context.Context, runner service.Runner) error { - if s.Handler == nil { - return errors.New("no handler registered") - } - - // the latest block still has to be fixed. - // otherwise we could skip some block events - // between the initial poll and the subscription. - if s.StartBlock.IsLatest() { - latest, err := s.Client.BlockNumber(ctx) - if err != nil { - return err - } - s.StartBlock.SetUint64(latest) - } - - watchOpts := &bind.WatchOpts{ - Start: s.StartBlock.ToUInt64Ptr(), - Context: ctx, - } - - if s.FetchActiveAtStartBlock { - initial, err := s.getInitialKeyperSets(ctx) - if err != nil { - return err - } - for _, ks := range initial { - err = s.Handler(ctx, ks) - if err != nil { - s.Log.Error( - "handler for `NewKeyperSet` errored for initial sync", - "error", - err.Error(), - ) - } - } +func (s *KeyperSetSyncer) HandleVirtualEvent(ctx context.Context, block *number.BlockNumber) error { + initial, err := s.getInitialKeyperSets(ctx, block) + if err != nil { + return err } - s.keyperAddedCh = make(chan *bindings.KeyperSetManagerKeyperSetAdded, channelSize) - runner.Defer(func() { - close(s.keyperAddedCh) - }) - if !s.DisableEventWatcher { - subs, err := s.Contract.WatchKeyperSetAdded(watchOpts, s.keyperAddedCh) - // FIXME: what to do on subs.Error() + for _, ks := range initial { + err = s.Handler(ctx, ks) if err != nil { - return err + s.Log.Error( + "handler for `NewKeyperSet` errored for initial sync", + "error", + err.Error(), + ) } - runner.Defer(subs.Unsubscribe) } - runner.Go(func() error { - return s.watchNewKeypersService(ctx) - }) return nil } -func (s *KeyperSetSyncer) getInitialKeyperSets(ctx context.Context) ([]*event.KeyperSet, error) { +func (s *KeyperSetSyncer) getInitialKeyperSets(ctx context.Context, block *number.BlockNumber) ([]*event.KeyperSet, error) { opts := &bind.CallOpts{ Context: ctx, - BlockNumber: s.StartBlock.Int, + BlockNumber: block.Int, } if err := guardCallOpts(opts, false); err != nil { return nil, err } - bn := s.StartBlock.ToUInt64Ptr() + bn := block.ToUInt64Ptr() if bn == nil { // this should not be the case return nil, errors.New("start block is 'latest'") @@ -140,7 +92,7 @@ func (s *KeyperSetSyncer) getInitialKeyperSets(ctx context.Context) ([]*event.Ke initialKeyperSets := []*event.KeyperSet{} // this blocknumber specifies the argument to the contract // getter - ks, err := s.GetKeyperSetForBlock(ctx, opts, s.StartBlock) + ks, err := s.GetKeyperSetForBlock(ctx, opts, block) if err != nil { return nil, err } @@ -250,38 +202,20 @@ func (s *KeyperSetSyncer) newEvent( }, nil } -func (s *KeyperSetSyncer) watchNewKeypersService(ctx context.Context) error { - for { - select { - case newKeypers, ok := <-s.keyperAddedCh: - if !ok { - return nil - } - opts := logToCallOpts(ctx, &newKeypers.Raw) - newKeyperSet, err := s.newEvent( - ctx, - opts, - newKeypers.KeyperSetContract, - newKeypers.ActivationBlock, - ) - if err != nil { - s.Log.Error( - "error while fetching new event", - "error", - err.Error(), - ) - continue - } - err = s.Handler(ctx, newKeyperSet) - if err != nil { - s.Log.Error( - "handler for `NewKeyperSet` errored", - "error", - err.Error(), - ) - } - case <-ctx.Done(): - return ctx.Err() - } +func (s *KeyperSetSyncer) handle(ctx context.Context, ev *bindings.KeyperSetManagerKeyperSetAdded) error { + opts := logToCallOpts(ctx, &ev.Raw) + newKeyperSet, err := s.newEvent( + ctx, + opts, + ev.KeyperSetContract, + ev.ActivationBlock, + ) + if err != nil { + return errors.Wrap(err, "fetch new event") } + err = s.Handler(ctx, newKeyperSet) + if err != nil { + return errors.Wrap(err, "call handler") + } + return nil } diff --git a/rolling-shutter/medley/chainsync/syncer/shutterstate.go b/rolling-shutter/medley/chainsync/syncer/shutterstate.go index 058772857..c42eedd89 100644 --- a/rolling-shutter/medley/chainsync/syncer/shutterstate.go +++ b/rolling-shutter/medley/chainsync/syncer/shutterstate.go @@ -11,22 +11,15 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/client" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" - "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" ) var _ ManualFilterHandler = &ShutterStateSyncer{} type ShutterStateSyncer struct { - Client client.EthereumClient - Contract *bindings.KeyperSetManager - StartBlock *number.BlockNumber - Log log.Logger - Handler event.ShutterStateHandler - FetchActiveAtStartBlock bool - DisableEventWatcher bool - - pausedCh chan *bindings.KeyperSetManagerPaused - unpausedCh chan *bindings.KeyperSetManagerUnpaused + Client client.EthereumClient + Contract *bindings.KeyperSetManager + Log log.Logger + Handler event.ShutterStateHandler } func (s *ShutterStateSyncer) GetShutterState(ctx context.Context, opts *bind.CallOpts) (*event.ShutterState, error) { @@ -57,15 +50,17 @@ func (s *ShutterStateSyncer) QueryAndHandle(ctx context.Context, block uint64) e defer iterPaused.Close() for iterPaused.Next() { - select { - case s.pausedCh <- iterPaused.Event: - case <-ctx.Done(): - return ctx.Err() + block := iterPaused.Event.Raw.BlockNumber + ev := &event.ShutterState{ + Active: false, + AtBlockNumber: number.NewBlockNumber(&block), } + s.handle(ctx, ev) } if err := iterPaused.Error(); err != nil { return errors.Wrap(err, "filter iterator error") } + iterUnpaused, err := s.Contract.FilterUnpaused(opts) if err != nil { return err @@ -73,11 +68,12 @@ func (s *ShutterStateSyncer) QueryAndHandle(ctx context.Context, block uint64) e defer iterUnpaused.Close() for iterUnpaused.Next() { - select { - case s.unpausedCh <- iterUnpaused.Event: - case <-ctx.Done(): - return ctx.Err() + block := iterUnpaused.Event.Raw.BlockNumber + ev := &event.ShutterState{ + Active: true, + AtBlockNumber: number.NewBlockNumber(&block), } + s.handle(ctx, ev) } if err := iterUnpaused.Error(); err != nil { return errors.Wrap(err, "filter iterator error") @@ -85,52 +81,17 @@ func (s *ShutterStateSyncer) QueryAndHandle(ctx context.Context, block uint64) e return nil } -func (s *ShutterStateSyncer) Start(ctx context.Context, runner service.Runner) error { - if s.Handler == nil { - return errors.New("no handler registered") - } - // the latest block still has to be fixed. - // otherwise we could skip some block events - // between the initial poll and the subscription. - if s.StartBlock.IsLatest() { - latest, err := s.Client.BlockNumber(ctx) - if err != nil { - return err - } - s.StartBlock.SetUint64(latest) - } - - opts := &bind.WatchOpts{ - Start: s.StartBlock.ToUInt64Ptr(), - Context: ctx, +func (s *ShutterStateSyncer) HandleVirtualEvent(ctx context.Context, block *number.BlockNumber) error { + // query the initial state and re-construct a "virtual" event from the contract state + opts := &bind.CallOpts{ + BlockNumber: block.Int, + Context: ctx, } - s.pausedCh = make(chan *bindings.KeyperSetManagerPaused) - runner.Defer(func() { - close(s.pausedCh) - }) - s.unpausedCh = make(chan *bindings.KeyperSetManagerUnpaused) - runner.Defer(func() { - close(s.unpausedCh) - }) - - if !s.DisableEventWatcher { - subs, err := s.Contract.WatchPaused(opts, s.pausedCh) - // FIXME: what to do on subs.Error() - if err != nil { - return err - } - runner.Defer(subs.Unsubscribe) - subs, err = s.Contract.WatchUnpaused(opts, s.unpausedCh) - // FIXME: what to do on subs.Error() - if err != nil { - return err - } - runner.Defer(subs.Unsubscribe) + stateAtBlock, err := s.GetShutterState(ctx, opts) + if err != nil { + return err } - - runner.Go(func() error { - return s.watchPaused(ctx) - }) + s.handle(ctx, stateAtBlock) return nil } @@ -144,48 +105,3 @@ func (s *ShutterStateSyncer) handle(ctx context.Context, ev *event.ShutterState) ) } } - -func (s *ShutterStateSyncer) watchPaused(ctx context.Context) error { - // query the initial state - // and construct a "virtual" - // event - opts := &bind.CallOpts{ - BlockNumber: s.StartBlock.Int, - Context: nil, - } - - if s.FetchActiveAtStartBlock { - stateAtStartBlock, err := s.GetShutterState(ctx, opts) - if err != nil { - // XXX: this will fail everything, do we want that? - return err - } - s.handle(ctx, stateAtStartBlock) - } - for { - select { - case unpaused, ok := <-s.unpausedCh: - if !ok { - return nil - } - block := unpaused.Raw.BlockNumber - ev := &event.ShutterState{ - Active: true, - AtBlockNumber: number.NewBlockNumber(&block), - } - s.handle(ctx, ev) - case paused, ok := <-s.pausedCh: - if !ok { - return nil - } - block := paused.Raw.BlockNumber - ev := &event.ShutterState{ - Active: false, - AtBlockNumber: number.NewBlockNumber(&block), - } - s.handle(ctx, ev) - case <-ctx.Done(): - return ctx.Err() - } - } -} diff --git a/rolling-shutter/medley/chainsync/syncer/unsafehead.go b/rolling-shutter/medley/chainsync/syncer/unsafehead.go index 60a0b783b..ccff7aca1 100644 --- a/rolling-shutter/medley/chainsync/syncer/unsafehead.go +++ b/rolling-shutter/medley/chainsync/syncer/unsafehead.go @@ -2,15 +2,17 @@ package syncer import ( "context" - "errors" "math/big" + "time" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/pkg/errors" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/client" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/retry" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" ) @@ -21,9 +23,16 @@ type UnsafeHeadSyncer struct { // Handler to be manually triggered // to handle their handler function // before the own Handler is called: - SyncedHandler []ManualFilterHandler + SyncedHandler []ManualFilterHandler + SyncStartBlock *number.BlockNumber + FetchActiveAtStart bool newLatestHeadCh chan *types.Header + + syncHead *types.Header + nextSyncBlock *big.Int + latestHead *types.Header + headerCache map[uint64]*types.Header } func (s *UnsafeHeadSyncer) Start(ctx context.Context, runner service.Runner) error { @@ -31,10 +40,35 @@ func (s *UnsafeHeadSyncer) Start(ctx context.Context, runner service.Runner) err if s.Handler == nil { return errors.New("no handler registered") } + + s.headerCache = map[uint64]*types.Header{} s.newLatestHeadCh = make(chan *types.Header, 1) + _, err := retry.FunctionCall( + ctx, + func(ctx context.Context) (bool, error) { + err := s.fetchInitialHeaders(ctx) + return err == nil, err + }, + ) + if err != nil { + return errors.Wrap(err, "fetch initial latest header and sync start header") + } + s.SyncStartBlock = &number.BlockNumber{Int: s.syncHead.Number} + if s.FetchActiveAtStart { + for _, h := range s.SyncedHandler { + err := h.HandleVirtualEvent(ctx, s.SyncStartBlock) + if err != nil { + s.Log.Error("synced handler call errored, skipping", "error", err) + } + } + } + err = s.handle(ctx, s.syncHead, false) + if err != nil { + return errors.Wrap(err, "handle initial sync block") + } + s.nextSyncBlock = new(big.Int).Add(s.syncHead.Number, big.NewInt(1)) subs, err := s.Client.SubscribeNewHead(ctx, s.newLatestHeadCh) - // FIXME: what to do on subs.Error() if err != nil { return err } @@ -48,78 +82,227 @@ func (s *UnsafeHeadSyncer) Start(ctx context.Context, runner service.Runner) err return nil } +func (s *UnsafeHeadSyncer) fetchInitialHeaders(ctx context.Context) error { + latest, err := s.Client.HeaderByNumber(ctx, nil) + if err != nil { + return errors.Wrap(err, "fetch latest header") + } + s.latestHead = latest + if s.SyncStartBlock.IsLatest() { + s.syncHead = latest + return nil + } + + start, err := s.Client.HeaderByNumber(ctx, s.SyncStartBlock.Int) + if err != nil { + return errors.Wrap(err, "fetch sync start header") + } + s.syncHead = start + return nil +} + func parseLogs() error { return nil } -func (s *UnsafeHeadSyncer) watchLatestUnsafeHead(ctx context.Context) error { - var currentBlock *big.Int +func (s *UnsafeHeadSyncer) handle(ctx context.Context, newHeader *types.Header, reorg bool) error { + blockNum := number.BigToBlockNumber(newHeader.Number) + if reorg { + prevNum := new(big.Int).Sub(newHeader.Number, big.NewInt(1)) + prevBlockNum := number.BigToBlockNumber(prevNum) + // Re-emit the previous block, to pre-emptively signal an + // incoming reorg. Like this a client is able to e.g. + // rewind changes first before processing the new + // events of the reorg + ev := &event.LatestBlock{ + Number: prevBlockNum, + BlockHash: newHeader.ParentHash, + Header: newHeader, + } + err := s.Handler(ctx, ev) + if err != nil { + // XXX: return or log? + // return err + s.Log.Error( + "handler for `NewLatestBlock` errored", + "error", + err.Error(), + ) + } + } + + // TODO: check bloom filter for topic of all + // synced handlers and only call them if + // the bloomfilter retrieves something. + for _, h := range s.SyncedHandler { + // NOTE: this has to be blocking! + // So whenever this returns, it is expected + // that the handlers Handle function + // has been called and it returned. + err := h.QueryAndHandle(ctx, blockNum.Uint64()) + if err != nil { + s.Log.Error("synced handler call errored, skipping", "error", err) + } + } + ev := &event.LatestBlock{ + Number: blockNum, + BlockHash: newHeader.Hash(), + } + err := s.Handler(ctx, ev) + if err != nil { + s.Log.Error( + "handler for `NewLatestBlock` errored", + "error", + err.Error(), + ) + } + return nil +} + +func (s *UnsafeHeadSyncer) fetchHeader(ctx context.Context, num *big.Int) (*types.Header, error) { + h, ok := s.headerCache[num.Uint64()] + if ok { + return h, nil + } + return s.Client.HeaderByNumber(ctx, num) +} + +func (s *UnsafeHeadSyncer) reset(ctx context.Context) error { + // this means the latest head was reset - check wether that + // concerns the current sync status + switch s.latestHead.Number.Cmp(s.syncHead.Number) { + case 1: + // we didn't catch up to the reorg position, so it's safe to ignore + // TODO: delete the forward caches + return nil + case 0: + // we already processed the head we re-orged to + if s.latestHead.Hash().Cmp(s.syncHead.Hash()) == 0 { + return nil + } + } + // definite reorg + if err := s.handle(ctx, s.latestHead, true); err != nil { + return err + } + s.syncHead = s.latestHead + s.nextSyncBlock = new(big.Int).Add(s.latestHead.Number, big.NewInt(1)) + return nil +} + +func (s *UnsafeHeadSyncer) sync(ctx context.Context) (syncing bool, delay time.Duration, err error) { + var newHead *types.Header + s.Log.Info("syncing chain") + + delta := new(big.Int).Sub(s.latestHead.Number, s.nextSyncBlock) + switch delta.Cmp(big.NewInt(0)) { + case 1: + // positive delta, we are still catching up + newHead, err = s.fetchHeader(ctx, s.nextSyncBlock) + syncing = true + delay = 1 * time.Second + case 0: + // next sync is latest head + // use the latest head + newHead = s.latestHead + syncing = false + case -1: + if delta.Cmp(big.NewInt(-1)) != 0 { + // next sync is more than one block further in the future. + // this could mean a reorg, but this should have been called before + // and it shouldn't come to this here + return false, delay, errors.New("unexpected reorg condition in sync") + } + // reorgs are handled outside, at the place new latest-head + // information arrives. + // next sync is 1 into the future. + // this means we called sync but are still waiting for the next latest head. + return false, delay, err + } + if err != nil { + return true, delay, err + } + + if handleErr := s.handle(ctx, newHead, false); handleErr != nil { + return true, delay, handleErr + } + s.syncHead = newHead + delete(s.headerCache, newHead.Number.Uint64()) + s.nextSyncBlock = new(big.Int).Add(newHead.Number, big.NewInt(1)) + + s.Log.Info("chain sync", + "synced-head-num", s.syncHead.Number.Uint64(), + "synced-head-hash", s.syncHead.Hash(), + "latest-head-num", s.latestHead.Number.Uint64(), + "latest-head-hash", s.latestHead.Hash(), + ) + return syncing, delay, err +} + +func (s *UnsafeHeadSyncer) watchLatestUnsafeHead(ctx context.Context) error { //nolint: gocyclo + t := time.NewTimer(0) + sync := t.C +work: for { select { + case <-ctx.Done(): + if sync != nil && !t.Stop() { + select { + case <-t.C: + // TODO: the non-blocking select is here as a + // precaution against an already emptied channel. + // This should not be necessary + default: + } + } + return ctx.Err() + case <-sync: + syncing, delay, err := s.sync(ctx) + if err != nil { + s.Log.Error("error during unsafe head sync", "error", err) + } + if !syncing { + s.Log.Info("stop syncing from sync") + sync = nil + continue work + } + t.Reset(delay) case newHeader, ok := <-s.newLatestHeadCh: if !ok { + s.Log.Info("latest head stream closed, exiting handler loop") return nil } - blockNum := number.BigToBlockNumber(newHeader.Number) - if currentBlock != nil { - switch newHeader.Number.Cmp(currentBlock) { - case -1, 0: - prevNum := new(big.Int).Sub(newHeader.Number, big.NewInt(1)) - prevBlockNum := number.BigToBlockNumber(prevNum) - // Re-emit the previous block, to pre-emptively signal an - // incoming reorg. Like this a client is able to e.g. - // rewind changes first before processing the new - // events of the reorg - ev := &event.LatestBlock{ - Number: prevBlockNum, - BlockHash: newHeader.ParentHash, - } - err := s.Handler(ctx, ev) - if err != nil { - // XXX: return or log? - // return err - s.Log.Error( - "handler for `NewLatestBlock` errored", - "error", - err.Error(), - ) - } - case 1: - // expected + s.Log.Info("new latest head from l2 ws-stream", "block-number", newHeader.Number.Uint64()) + if newHeader.Number.Cmp(s.latestHead.Number) <= 0 { + // reorg + s.Log.Info("new latest head is re-orging", + "old-block-number", s.latestHead.Number.Uint64(), + "new-block-number", newHeader.Number.Uint64(), + ) + + s.latestHead = newHeader + if err := s.reset(ctx); err != nil { + s.Log.Error("error resetting reorg", "error", err) } + continue work } + s.headerCache[newHeader.Number.Uint64()] = newHeader + s.latestHead = newHeader - // TODO: check bloom filter for topic of all - // synced handlers and only call them if - // the bloomfilter retrieves something. - for _, h := range s.SyncedHandler { - // NOTE: this has to be blocking! - // So whenever this returns, it is expected - // that the handlers Handle function - // has been called and it returned. - err := h.QueryAndHandle(ctx, blockNum.Uint64()) - if err != nil { - // XXX: return or log? - // return err - s.Log.Error("synced handler call errored, skipping", "error", err) + if sync != nil && !t.Stop() { + // only if sync is still actively waiting, + // we need to drain the timer and reset it + select { + case <-t.C: + // TODO: the non-blocking select is here as a + // precaution against an already emptied channel. + // This should not be necessary + default: } } - ev := &event.LatestBlock{ - Number: blockNum, - BlockHash: newHeader.Hash(), - Header: newHeader, - } - err := s.Handler(ctx, ev) - if err != nil { - s.Log.Error( - "handler for `NewLatestBlock` errored", - "error", - err.Error(), - ) - } - currentBlock = newHeader.Number - case <-ctx.Done(): - return ctx.Err() + t.Reset(0) + s.Log.Info("start syncing from latest head stream") + sync = t.C } } } diff --git a/rolling-shutter/medley/chainsync/syncer/util.go b/rolling-shutter/medley/chainsync/syncer/util.go index 916e93141..80da11aa2 100644 --- a/rolling-shutter/medley/chainsync/syncer/util.go +++ b/rolling-shutter/medley/chainsync/syncer/util.go @@ -20,6 +20,7 @@ var ( type ManualFilterHandler interface { QueryAndHandle(ctx context.Context, block uint64) error + HandleVirtualEvent(ctx context.Context, block *number.BlockNumber) error } func logToCallOpts(ctx context.Context, log *types.Log) *bind.CallOpts { From b1a10bd2fd4823a0da30637d1f98409ce5aa4c78 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld <15726643+ezdac@users.noreply.github.com> Date: Fri, 10 May 2024 16:21:04 +0200 Subject: [PATCH 90/91] chore(chainsync): add unit test for reorgs --- rolling-shutter/medley/chainsync/client.go | 4 +- .../medley/chainsync/client/client.go | 15 +- .../medley/chainsync/client/test.go | 170 ++++++++++++++++++ rolling-shutter/medley/chainsync/options.go | 10 +- .../medley/chainsync/syncer/eonpubkey.go | 2 +- .../medley/chainsync/syncer/keyperset.go | 2 +- .../medley/chainsync/syncer/shutterstate.go | 2 +- .../medley/chainsync/syncer/unsafehead.go | 2 +- .../chainsync/syncer/unsafehead_test.go | 113 ++++++++++++ .../medley/chainsync/syncer/util.go | 2 +- 10 files changed, 309 insertions(+), 13 deletions(-) create mode 100644 rolling-shutter/medley/chainsync/client/test.go create mode 100644 rolling-shutter/medley/chainsync/syncer/unsafehead_test.go diff --git a/rolling-shutter/medley/chainsync/client.go b/rolling-shutter/medley/chainsync/client.go index bb2d3f3b0..6bd674905 100644 --- a/rolling-shutter/medley/chainsync/client.go +++ b/rolling-shutter/medley/chainsync/client.go @@ -24,7 +24,7 @@ var noopLogger = &logger.NoopLogger{} var ErrServiceNotInstantiated = errors.New("service is not instantiated, pass a handler function option") type Client struct { - client.EthereumClient + client.SyncEthereumClient log log.Logger options *options @@ -136,7 +136,7 @@ func (s *Client) BroadcastEonKey(ctx context.Context, eon uint64, eonPubKey []by // This value is cached, since it is not expected to change. func (s *Client) ChainID(ctx context.Context) (*big.Int, error) { if s.chainID == nil { - cid, err := s.EthereumClient.ChainID(ctx) + cid, err := s.SyncEthereumClient.ChainID(ctx) if err != nil { return nil, err } diff --git a/rolling-shutter/medley/chainsync/client/client.go b/rolling-shutter/medley/chainsync/client/client.go index a69e2692a..4924bc8c9 100644 --- a/rolling-shutter/medley/chainsync/client/client.go +++ b/rolling-shutter/medley/chainsync/client/client.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" ) -type EthereumClient interface { +type FullEthereumClient interface { Close() ChainID(ctx context.Context) (*big.Int, error) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) @@ -45,3 +45,16 @@ type EthereumClient interface { EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) SendTransaction(ctx context.Context, tx *types.Transaction) error } + +type SyncEthereumClient interface { + Close() + ChainID(ctx context.Context) (*big.Int, error) + BlockNumber(ctx context.Context) (uint64, error) + HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) + HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) + SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) + FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) + SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) + CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) + TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) +} diff --git a/rolling-shutter/medley/chainsync/client/test.go b/rolling-shutter/medley/chainsync/client/test.go new file mode 100644 index 000000000..d436ac884 --- /dev/null +++ b/rolling-shutter/medley/chainsync/client/test.go @@ -0,0 +1,170 @@ +package client + +import ( + "context" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +var ErrNotImplemented = errors.New("not implemented") + +var _ SyncEthereumClient = &TestClient{} + +type TestClient struct { + headerChain []*types.Header + latestHeadIndex int + intialProgress bool + latestHeadEmitter []chan<- *types.Header + latestHeadSubscription []*Subscription +} + +func NewSubscription(idx int) *Subscription { + return &Subscription{ + idx: idx, + err: make(chan error, 1), + } +} + +type Subscription struct { + idx int + err chan error +} + +func (su *Subscription) Unsubscribe() { + // TODO: not implemented yet, but we don't want to panic +} + +func (su *Subscription) Err() <-chan error { + return su.err +} + +type TestClientController struct { + c *TestClient +} + +func NewTestClient() (*TestClient, *TestClientController) { + c := &TestClient{ + headerChain: []*types.Header{}, + latestHeadIndex: 0, + } + ctrl := &TestClientController{c} + return c, ctrl +} + +func (c *TestClientController) ProgressHead() bool { + if c.c.latestHeadIndex >= len(c.c.headerChain)-1 { + return false + } + c.c.latestHeadIndex++ + return true +} + +func (c *TestClientController) EmitEvents(ctx context.Context) error { + if len(c.c.latestHeadEmitter) == 0 { + return nil + } + h := c.c.getLatestHeader() + for _, em := range c.c.latestHeadEmitter { + select { + case em <- h: + case <-ctx.Done(): + return ctx.Err() + } + } + return nil +} + +func (c *TestClientController) AppendNextHeaders(h ...*types.Header) { + c.c.headerChain = append(c.c.headerChain, h...) +} + +func (t *TestClient) ChainID(_ context.Context) (*big.Int, error) { + return big.NewInt(42), nil +} + +func (t *TestClient) Close() { + // TODO: cleanup +} + +func (t *TestClient) getLatestHeader() *types.Header { + if len(t.headerChain) == 0 { + return nil + } + return t.headerChain[t.latestHeadIndex] +} + +func (t *TestClient) searchBlock(f func(*types.Header) bool) *types.Header { + for i := t.latestHeadIndex; i >= 0; i-- { + h := t.headerChain[i] + if f(h) { + return h + } + } + return nil +} + +func (t *TestClient) searchBlockByNumber(number *big.Int) *types.Header { + return t.searchBlock( + func(h *types.Header) bool { + return h.Number.Cmp(number) == 0 + }) +} + +func (t *TestClient) searchBlockByHash(hash common.Hash) *types.Header { + return t.searchBlock( + func(h *types.Header) bool { + return hash.Cmp(h.Hash()) == 0 + }) +} + +func (t *TestClient) BlockNumber(_ context.Context) (uint64, error) { + return t.getLatestHeader().Nonce.Uint64(), nil +} + +func (t *TestClient) HeaderByHash(_ context.Context, hash common.Hash) (*types.Header, error) { + h := t.searchBlockByHash(hash) + if h == nil { + return nil, errors.New("header not found") + } + return h, nil +} + +func (t *TestClient) HeaderByNumber(_ context.Context, number *big.Int) (*types.Header, error) { + if number == nil { + return t.getLatestHeader(), nil + } + h := t.searchBlockByNumber(number) + if h == nil { + return nil, errors.New("header not found") + } + return h, nil +} + +func (t *TestClient) SubscribeNewHead(_ context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) { + t.latestHeadEmitter = append(t.latestHeadEmitter, ch) + su := NewSubscription(len(t.latestHeadSubscription) - 1) + t.latestHeadSubscription = append(t.latestHeadSubscription, su) + // TODO: unsubscribe and deleting from the array + // TODO: filling error promise in the subscription + return su, nil +} + +func (t *TestClient) FilterLogs(_ context.Context, _ ethereum.FilterQuery) ([]types.Log, error) { + panic(ErrNotImplemented) +} + +func (t *TestClient) SubscribeFilterLogs(_ context.Context, _ ethereum.FilterQuery, _ chan<- types.Log) (ethereum.Subscription, error) { + panic(ErrNotImplemented) +} + +func (t *TestClient) CodeAt(_ context.Context, _ common.Address, _ *big.Int) ([]byte, error) { + panic(ErrNotImplemented) +} + +func (t *TestClient) TransactionReceipt(_ context.Context, _ common.Hash) (*types.Receipt, error) { + panic(ErrNotImplemented) +} diff --git a/rolling-shutter/medley/chainsync/options.go b/rolling-shutter/medley/chainsync/options.go index 076e1666c..9a7e43473 100644 --- a/rolling-shutter/medley/chainsync/options.go +++ b/rolling-shutter/medley/chainsync/options.go @@ -24,7 +24,7 @@ type options struct { keyperSetManagerAddress *common.Address keyBroadcastContractAddress *common.Address clientURL string - ethClient syncclient.EthereumClient + ethClient syncclient.FullEthereumClient logger log.Logger runner service.Runner syncStart *number.BlockNumber @@ -122,7 +122,7 @@ func (o *options) applyHandler(c *Client) error { // of shutter clients background workers. func (o *options) apply(ctx context.Context, c *Client) error { var ( - client syncclient.EthereumClient + client syncclient.SyncEthereumClient err error ) if o.clientURL != "" { @@ -132,12 +132,12 @@ func (o *options) apply(ctx context.Context, c *Client) error { } } client = o.ethClient - c.EthereumClient = client + c.SyncEthereumClient = client // the nil passthrough will use "latest" for each call, // but we want to harmonize and fix the sync start to a specific block. if o.syncStart.IsLatest() { - latestBlock, err := c.EthereumClient.BlockNumber(ctx) + latestBlock, err := c.SyncEthereumClient.BlockNumber(ctx) if err != nil { return errors.Wrap(err, "polling latest block") } @@ -219,7 +219,7 @@ func WithLogger(l log.Logger) Option { } } -func WithClient(client syncclient.EthereumClient) Option { +func WithClient(client syncclient.FullEthereumClient) Option { return func(o *options) error { o.ethClient = client return nil diff --git a/rolling-shutter/medley/chainsync/syncer/eonpubkey.go b/rolling-shutter/medley/chainsync/syncer/eonpubkey.go index 7aa6b8171..f055fc118 100644 --- a/rolling-shutter/medley/chainsync/syncer/eonpubkey.go +++ b/rolling-shutter/medley/chainsync/syncer/eonpubkey.go @@ -17,7 +17,7 @@ import ( var _ ManualFilterHandler = &EonPubKeySyncer{} type EonPubKeySyncer struct { - Client client.EthereumClient + Client client.SyncEthereumClient Log log.Logger KeyBroadcast *bindings.KeyBroadcastContract KeyperSetManager *bindings.KeyperSetManager diff --git a/rolling-shutter/medley/chainsync/syncer/keyperset.go b/rolling-shutter/medley/chainsync/syncer/keyperset.go index d0c50af19..be9e3c976 100644 --- a/rolling-shutter/medley/chainsync/syncer/keyperset.go +++ b/rolling-shutter/medley/chainsync/syncer/keyperset.go @@ -23,7 +23,7 @@ const channelSize = 10 var _ ManualFilterHandler = &KeyperSetSyncer{} type KeyperSetSyncer struct { - Client client.EthereumClient + Client client.FullEthereumClient Contract *bindings.KeyperSetManager Log log.Logger Handler event.KeyperSetHandler diff --git a/rolling-shutter/medley/chainsync/syncer/shutterstate.go b/rolling-shutter/medley/chainsync/syncer/shutterstate.go index c42eedd89..fbd7773de 100644 --- a/rolling-shutter/medley/chainsync/syncer/shutterstate.go +++ b/rolling-shutter/medley/chainsync/syncer/shutterstate.go @@ -16,7 +16,7 @@ import ( var _ ManualFilterHandler = &ShutterStateSyncer{} type ShutterStateSyncer struct { - Client client.EthereumClient + Client client.SyncEthereumClient Contract *bindings.KeyperSetManager Log log.Logger Handler event.ShutterStateHandler diff --git a/rolling-shutter/medley/chainsync/syncer/unsafehead.go b/rolling-shutter/medley/chainsync/syncer/unsafehead.go index ccff7aca1..a90b9c69f 100644 --- a/rolling-shutter/medley/chainsync/syncer/unsafehead.go +++ b/rolling-shutter/medley/chainsync/syncer/unsafehead.go @@ -17,7 +17,7 @@ import ( ) type UnsafeHeadSyncer struct { - Client client.EthereumClient + Client client.SyncEthereumClient Log log.Logger Handler event.BlockHandler // Handler to be manually triggered diff --git a/rolling-shutter/medley/chainsync/syncer/unsafehead_test.go b/rolling-shutter/medley/chainsync/syncer/unsafehead_test.go new file mode 100644 index 000000000..380a944f0 --- /dev/null +++ b/rolling-shutter/medley/chainsync/syncer/unsafehead_test.go @@ -0,0 +1,113 @@ +package syncer + +import ( + "context" + "fmt" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "gotest.tools/v3/assert" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/client" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" +) + +func MakeChain(start int64, startParent common.Hash, numHeader uint, seed int64) []*types.Header { + n := numHeader + parent := startParent + num := big.NewInt(start) + h := []*types.Header{} + + // change the hashes for different seeds + mixinh := common.BigToHash(big.NewInt(seed)) + for n > 0 { + head := &types.Header{ + ParentHash: parent, + Number: num, + MixDigest: mixinh, + } + h = append(h, head) + num = new(big.Int).Add(num, big.NewInt(1)) + parent = head.Hash() + n-- + } + return h +} + +func TestReorg(t *testing.T) { + headersBeforeReorg := MakeChain(1, common.BigToHash(big.NewInt(0)), 10, 42) + branchOff := headersBeforeReorg[5] + // block number 5 will be reorged + headersReorgBranch := MakeChain(branchOff.Number.Int64()+1, branchOff.Hash(), 10, 43) + clnt, ctl := client.NewTestClient() + ctl.AppendNextHeaders(headersBeforeReorg...) + ctl.AppendNextHeaders(headersReorgBranch...) + + handlerBlock := make(chan *event.LatestBlock, 1) + + h := &UnsafeHeadSyncer{ + Client: clnt, + Log: log.New(), + Handler: func(_ context.Context, ev *event.LatestBlock) error { + handlerBlock <- ev + return nil + }, + SyncedHandler: []ManualFilterHandler{}, + SyncStartBlock: number.NewBlockNumber(nil), + FetchActiveAtStart: false, + } + + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + service.RunBackground(ctx, h) + + // intitial sync is independent of the subscription, + // this will get polled from the eth client + b := <-handlerBlock + assert.Assert(t, b.Number.Cmp(headersBeforeReorg[0].Number) == 0) + idx := 1 + for { + ok := ctl.ProgressHead() + assert.Assert(t, ok) + err := ctl.EmitEvents(ctx) + assert.NilError(t, err) + + b = <-handlerBlock + assert.Equal(t, b.Number.Uint64(), headersBeforeReorg[idx].Number.Uint64(), fmt.Sprintf("block number equal for idx %d", idx)) + assert.Equal(t, b.BlockHash, headersBeforeReorg[idx].Hash()) + idx++ + if idx == len(headersBeforeReorg) { + break + } + } + ok := ctl.ProgressHead() + assert.Assert(t, ok) + err := ctl.EmitEvents(ctx) + assert.NilError(t, err) + b = <-handlerBlock + // now the reorg should have happened. + // the handler should have emitted an "artificial" latest head + // event for the block BEFORE the re-orged block + assert.Equal(t, b.Number.Uint64(), headersReorgBranch[0].Number.Uint64()-1, "block number equal for reorg") + assert.Equal(t, b.BlockHash, headersReorgBranch[0].ParentHash) + idx = 0 + for ctl.ProgressHead() { + assert.Assert(t, ok) + err := ctl.EmitEvents(ctx) + assert.NilError(t, err) + + b := <-handlerBlock + assert.Equal(t, b.Number.Uint64(), headersReorgBranch[idx].Number.Uint64(), fmt.Sprintf("block number equal for idx %d", idx)) + assert.Equal(t, b.BlockHash, headersReorgBranch[idx].Hash()) + idx++ + if idx == len(headersReorgBranch) { + break + } + } +} diff --git a/rolling-shutter/medley/chainsync/syncer/util.go b/rolling-shutter/medley/chainsync/syncer/util.go index 80da11aa2..8a470f06a 100644 --- a/rolling-shutter/medley/chainsync/syncer/util.go +++ b/rolling-shutter/medley/chainsync/syncer/util.go @@ -45,7 +45,7 @@ func guardCallOpts(opts *bind.CallOpts, allowLatest bool) error { return nil } -func fixCallOpts(ctx context.Context, c client.EthereumClient, opts *bind.CallOpts) (*bind.CallOpts, *uint64, error) { +func fixCallOpts(ctx context.Context, c client.SyncEthereumClient, opts *bind.CallOpts) (*bind.CallOpts, *uint64, error) { err := guardCallOpts(opts, false) if err == nil { return opts, nil, nil From 80f095dc7984fcf9fc948556f04abf52968418f9 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld <15726643+ezdac@users.noreply.github.com> Date: Fri, 31 May 2024 10:51:40 +0200 Subject: [PATCH 91/91] fix: lint --- rolling-shutter/p2p/params.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rolling-shutter/p2p/params.go b/rolling-shutter/p2p/params.go index ad385e71a..901d53817 100644 --- a/rolling-shutter/p2p/params.go +++ b/rolling-shutter/p2p/params.go @@ -19,7 +19,9 @@ func makePubSubParams( gossipSubParams := &gsDefault // modified defaults from ethereum consensus spec - // https://github.com/ethereum/consensus-specs/blob/5d80b1954a4b7a121aa36143d50b366727b66cbc/specs/phase0/p2p-interface.md#why-are-these-specific-gossip-parameters-chosen //nolint:lll + + //nolint:lll + // https://github.com/ethereum/consensus-specs/blob/5d80b1954a4b7a121aa36143d50b366727b66cbc/specs/phase0/p2p-interface.md#why-are-these-specific-gossip-parameters-chosen gossipSubParams.HeartbeatInterval = 700 * time.Millisecond gossipSubParams.HistoryLength = 6