From e768d240f32a0baa7ae20d9a6a49937b294ad897 Mon Sep 17 00:00:00 2001 From: Aman Sanghi Date: Thu, 18 Apr 2024 20:12:35 +0530 Subject: [PATCH 01/41] Add a check to make sure we don't allow accidentally downgrading ArbOS --- cmd/nitro/init.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index 72c767d00f..6921d431aa 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -155,6 +155,10 @@ func validateBlockChain(blockChain *core.BlockChain, chainConfig *params.ChainCo return fmt.Errorf("invalid chain config, not compatible with previous: %w", err) } } + // Add a check to make sure we don't allow accidentally downgrading ArbOS + if currentArbosState.ArbOSVersion() > chainConfig.ArbitrumChainParams.InitialArbOSVersion { + return fmt.Errorf("attempted to launch node with ArbOS version %v on ArbOS state with version %v", chainConfig.ArbitrumChainParams.InitialArbOSVersion, currentArbosState.ArbOSVersion()) + } return nil } From 8572000887c6c64dcd53f26a91dd5da85ac920cd Mon Sep 17 00:00:00 2001 From: Aman Sanghi Date: Thu, 18 Apr 2024 21:29:50 +0530 Subject: [PATCH 02/41] minor fix --- cmd/nitro/init.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index 58a75b0345..54a4eeea81 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -155,7 +155,7 @@ func validateBlockChain(blockChain *core.BlockChain, chainConfig *params.ChainCo return fmt.Errorf("invalid chain config, not compatible with previous: %w", err) } } - // Add a check to make sure we don't allow accidentally downgrading ArbOS + // Make sure we don't allow accidentally downgrading ArbOS if currentArbosState.ArbOSVersion() > chainConfig.ArbitrumChainParams.InitialArbOSVersion { return fmt.Errorf("attempted to launch node with ArbOS version %v on ArbOS state with version %v", chainConfig.ArbitrumChainParams.InitialArbOSVersion, currentArbosState.ArbOSVersion()) } From ad6cd1d6a4edcc77e9ea74c393b0956f72219514 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Thu, 18 Apr 2024 14:50:38 -0500 Subject: [PATCH 03/41] Merge v1.13.6 --- cmd/daserver/daserver.go | 13 ++- cmd/deploy/deploy.go | 6 +- cmd/genericconf/config.go | 8 +- cmd/genericconf/filehandler_test.go | 7 +- cmd/genericconf/logging.go | 99 +++++++++++--------- cmd/nitro-val/nitro_val.go | 5 +- cmd/nitro/nitro.go | 5 +- cmd/relay/relay.go | 12 ++- cmd/replay/main.go | 8 +- das/aggregator_test.go | 8 +- execution/nodeInterface/virtual-contracts.go | 4 +- go-ethereum | 2 +- go.mod | 17 ++-- go.sum | 31 +++--- precompiles/precompile_test.go | 8 +- staker/challenge_test.go | 8 +- system_tests/common_test.go | 16 ++-- system_tests/das_test.go | 9 +- system_tests/debugapi_test.go | 6 +- system_tests/estimation_test.go | 3 +- system_tests/full_challenge_impl_test.go | 5 +- system_tests/retryable_test.go | 9 +- system_tests/triedb_race_test.go | 2 +- util/testhelpers/testhelpers.go | 39 +++++--- 24 files changed, 198 insertions(+), 132 deletions(-) diff --git a/cmd/daserver/daserver.go b/cmd/daserver/daserver.go index 07481651b2..3e96412648 100644 --- a/cmd/daserver/daserver.go +++ b/cmd/daserver/daserver.go @@ -7,12 +7,15 @@ import ( "context" "errors" "fmt" + "io" "net/http" "os" "os/signal" "syscall" "time" + "golang.org/x/exp/slog" + koanfjson "github.com/knadh/koanf/parsers/json" flag "github.com/spf13/pflag" @@ -182,14 +185,14 @@ func startup() error { confighelpers.PrintErrorAndExit(errors.New("please specify at least one of --enable-rest or --enable-rpc"), printSampleUsage) } - logFormat, err := genericconf.ParseLogType(serverConfig.LogType) + handler, err := genericconf.HandlerFromLogType(serverConfig.LogType, io.Writer(os.Stderr)) if err != nil { flag.Usage() - panic(fmt.Sprintf("Error parsing log type: %v", err)) + return fmt.Errorf("error parsing log type when creating handler: %w", err) } - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, logFormat)) - glogger.Verbosity(log.Lvl(serverConfig.LogLevel)) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler(handler) + glogger.Verbosity(slog.Level(serverConfig.LogLevel)) + log.SetDefault(log.NewLogger(glogger)) if err := startMetrics(serverConfig); err != nil { return err diff --git a/cmd/deploy/deploy.go b/cmd/deploy/deploy.go index 1c8b858106..d8c0aeeac4 100644 --- a/cmd/deploy/deploy.go +++ b/cmd/deploy/deploy.go @@ -8,6 +8,7 @@ import ( "encoding/json" "flag" "fmt" + "io" "math/big" "os" "strings" @@ -30,9 +31,10 @@ import ( ) func main() { - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) glogger.Verbosity(log.LvlDebug) - log.Root().SetHandler(glogger) + log.SetDefault(log.NewLogger(glogger)) log.Info("deploying rollup") ctx := context.Background() diff --git a/cmd/genericconf/config.go b/cmd/genericconf/config.go index 50aafbe223..06e1fcd12d 100644 --- a/cmd/genericconf/config.go +++ b/cmd/genericconf/config.go @@ -5,11 +5,13 @@ package genericconf import ( "errors" + "io" "time" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" flag "github.com/spf13/pflag" + "golang.org/x/exp/slog" ) type ConfConfig struct { @@ -63,11 +65,11 @@ var DefaultS3Config = S3Config{ SecretKey: "", } -func ParseLogType(logType string) (log.Format, error) { +func HandlerFromLogType(logType string, output io.Writer) (slog.Handler, error) { if logType == "plaintext" { - return log.TerminalFormat(false), nil + return log.NewTerminalHandler(output, false), nil } else if logType == "json" { - return log.JSONFormat(), nil + return log.JSONHandler(output), nil } return nil, errors.New("invalid log type") } diff --git a/cmd/genericconf/filehandler_test.go b/cmd/genericconf/filehandler_test.go index 7ea0668229..daa9ed397c 100644 --- a/cmd/genericconf/filehandler_test.go +++ b/cmd/genericconf/filehandler_test.go @@ -72,9 +72,10 @@ func testFileHandler(t *testing.T, testCompressed bool) { config.MaxSize = 1 config.Compress = testCompressed config.File = testFile - fileHandler := globalFileHandlerFactory.newHandler(log.JSONFormat(), &config, testFile) - defer func() { testhelpers.RequireImpl(t, globalFileHandlerFactory.close()) }() - log.Root().SetHandler(fileHandler) + handler, err := HandlerFromLogType("json", globalFileLoggerFactory.newFileWriter(&config, testFile)) + defer func() { testhelpers.RequireImpl(t, globalFileLoggerFactory.close()) }() + testhelpers.RequireImpl(t, err) + log.SetDefault(log.NewLogger(handler)) expected := []string{"dead", "beef", "ate", "bad", "beef"} for _, e := range expected { log.Warn(e) diff --git a/cmd/genericconf/logging.go b/cmd/genericconf/logging.go index a50dfa3190..d77071a0bf 100644 --- a/cmd/genericconf/logging.go +++ b/cmd/genericconf/logging.go @@ -4,22 +4,47 @@ import ( "context" "flag" "fmt" + "io" "os" + "sync" "github.com/ethereum/go-ethereum/log" + "golang.org/x/exp/slog" "gopkg.in/natefinch/lumberjack.v2" ) -var globalFileHandlerFactory = fileHandlerFactory{} +var globalFileLoggerFactory = fileLoggerFactory{} -type fileHandlerFactory struct { - writer *lumberjack.Logger - records chan *log.Record - cancel context.CancelFunc +type fileLoggerFactory struct { + // writerMutex is to avoid parallel writes to the file-logger + writerMutex sync.Mutex + writer *lumberjack.Logger + + cancel context.CancelFunc + + // writeStartPing and writeDonePing are used to simulate sending of data via a buffered channel + // when Write is called and receiving it on another go-routine to write it to the io.Writer. + writeStartPing chan struct{} + writeDonePing chan struct{} +} + +// Write is essentially a wrapper for filewriter or lumberjack.Logger's Write method to implement +// config.BufSize functionality, data is dropped when l.writeStartPing channel (of size config.BuffSize) is full +func (l *fileLoggerFactory) Write(p []byte) (n int, err error) { + select { + case l.writeStartPing <- struct{}{}: + // Write data to the filelogger + l.writerMutex.Lock() + _, _ = l.writer.Write(p) + l.writerMutex.Unlock() + l.writeDonePing <- struct{}{} + default: + } + return len(p), nil } -// newHandler is not threadsafe -func (l *fileHandlerFactory) newHandler(logFormat log.Format, config *FileLoggingConfig, filename string) log.Handler { +// newFileWriter is not threadsafe +func (l *fileLoggerFactory) newFileWriter(config *FileLoggingConfig, filename string) io.Writer { l.close() l.writer = &lumberjack.Logger{ Filename: filename, @@ -28,40 +53,29 @@ func (l *fileHandlerFactory) newHandler(logFormat log.Format, config *FileLoggin MaxAge: config.MaxAge, Compress: config.Compress, } - // capture copy of the pointer - writer := l.writer - // lumberjack.Logger already locks on Write, no need for SyncHandler proxy which is used in StreamHandler - unsafeStreamHandler := log.LazyHandler(log.FuncHandler(func(r *log.Record) error { - _, err := writer.Write(logFormat.Format(r)) - return err - })) - l.records = make(chan *log.Record, config.BufSize) + l.writeStartPing = make(chan struct{}, config.BufSize) + l.writeDonePing = make(chan struct{}, config.BufSize) // capture copy - records := l.records + writeStartPing := l.writeStartPing + writeDonePing := l.writeDonePing var consumerCtx context.Context consumerCtx, l.cancel = context.WithCancel(context.Background()) go func() { + // writeStartPing channel signals Write operations to correctly implement config.BufSize functionality for { select { - case r := <-records: - _ = unsafeStreamHandler.Log(r) + case <-writeStartPing: + <-writeDonePing case <-consumerCtx.Done(): return } } }() - return log.FuncHandler(func(r *log.Record) error { - select { - case records <- r: - return nil - default: - return fmt.Errorf("Buffer overflow, dropping record") - } - }) + return l } // close is not threadsafe -func (l *fileHandlerFactory) close() error { +func (l *fileLoggerFactory) close() error { if l.cancel != nil { l.cancel() l.cancel = nil @@ -76,28 +90,29 @@ func (l *fileHandlerFactory) close() error { } // initLog is not threadsafe -func InitLog(logType string, logLevel log.Lvl, fileLoggingConfig *FileLoggingConfig, pathResolver func(string) string) error { - logFormat, err := ParseLogType(logType) - if err != nil { - flag.Usage() - return fmt.Errorf("error parsing log type: %w", err) - } +func InitLog(logType string, logLevel slog.Level, fileLoggingConfig *FileLoggingConfig, pathResolver func(string) string) error { var glogger *log.GlogHandler // always close previous instance of file logger - if err := globalFileHandlerFactory.close(); err != nil { + if err := globalFileLoggerFactory.close(); err != nil { return fmt.Errorf("failed to close file writer: %w", err) } + var output io.Writer if fileLoggingConfig.Enable { - glogger = log.NewGlogHandler( - log.MultiHandler( - log.StreamHandler(os.Stderr, logFormat), - // on overflow records are dropped silently as MultiHandler ignores errors - globalFileHandlerFactory.newHandler(logFormat, fileLoggingConfig, pathResolver(fileLoggingConfig.File)), - )) + output = io.MultiWriter( + io.Writer(os.Stderr), + // on overflow writeStartPing are dropped silently + globalFileLoggerFactory.newFileWriter(fileLoggingConfig, pathResolver(fileLoggingConfig.File)), + ) } else { - glogger = log.NewGlogHandler(log.StreamHandler(os.Stderr, logFormat)) + output = io.Writer(os.Stderr) + } + handler, err := HandlerFromLogType(logType, output) + if err != nil { + flag.Usage() + return fmt.Errorf("error parsing log type when creating handler: %w", err) } + glogger = log.NewGlogHandler(handler) glogger.Verbosity(logLevel) - log.Root().SetHandler(glogger) + log.SetDefault(log.NewLogger(glogger)) return nil } diff --git a/cmd/nitro-val/nitro_val.go b/cmd/nitro-val/nitro_val.go index 3671c7ea8d..4e543f7953 100644 --- a/cmd/nitro-val/nitro_val.go +++ b/cmd/nitro-val/nitro_val.go @@ -22,6 +22,7 @@ import ( "github.com/offchainlabs/nitro/cmd/util/confighelpers" _ "github.com/offchainlabs/nitro/execution/nodeInterface" "github.com/offchainlabs/nitro/validator/valnode" + "golang.org/x/exp/slog" ) func printSampleUsage(name string) { @@ -89,7 +90,7 @@ func mainImpl() int { } } - err = genericconf.InitLog(nodeConfig.LogType, log.Lvl(nodeConfig.LogLevel), &nodeConfig.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) + err = genericconf.InitLog(nodeConfig.LogType, slog.Level(nodeConfig.LogLevel), &nodeConfig.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) if err != nil { fmt.Fprintf(os.Stderr, "Error initializing logging: %v\n", err) return 1 @@ -108,7 +109,7 @@ func mainImpl() int { liveNodeConfig := genericconf.NewLiveConfig[*ValidationNodeConfig](args, nodeConfig, ParseNode) liveNodeConfig.SetOnReloadHook(func(oldCfg *ValidationNodeConfig, newCfg *ValidationNodeConfig) error { - return genericconf.InitLog(newCfg.LogType, log.Lvl(newCfg.LogLevel), &newCfg.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) + return genericconf.InitLog(newCfg.LogType, slog.Level(newCfg.LogLevel), &newCfg.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) }) valnode.EnsureValidationExposedViaAuthRPC(&stackConf) diff --git a/cmd/nitro/nitro.go b/cmd/nitro/nitro.go index 79ecd51ac2..df0feca8ee 100644 --- a/cmd/nitro/nitro.go +++ b/cmd/nitro/nitro.go @@ -63,6 +63,7 @@ import ( "github.com/offchainlabs/nitro/util/signature" "github.com/offchainlabs/nitro/validator/server_common" "github.com/offchainlabs/nitro/validator/valnode" + "golang.org/x/exp/slog" ) func printSampleUsage(name string) { @@ -207,7 +208,7 @@ func mainImpl() int { } stackConf.JWTSecret = filename } - err = genericconf.InitLog(nodeConfig.LogType, log.Lvl(nodeConfig.LogLevel), &nodeConfig.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) + err = genericconf.InitLog(nodeConfig.LogType, slog.Level(nodeConfig.LogLevel), &nodeConfig.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) if err != nil { fmt.Fprintf(os.Stderr, "Error initializing logging: %v\n", err) return 1 @@ -599,7 +600,7 @@ func mainImpl() int { } liveNodeConfig.SetOnReloadHook(func(oldCfg *NodeConfig, newCfg *NodeConfig) error { - if err := genericconf.InitLog(newCfg.LogType, log.Lvl(newCfg.LogLevel), &newCfg.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)); err != nil { + if err := genericconf.InitLog(newCfg.LogType, slog.Level(newCfg.LogLevel), &newCfg.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)); err != nil { return fmt.Errorf("failed to re-init logging: %w", err) } return currentNode.OnConfigReload(&oldCfg.Node, &newCfg.Node) diff --git a/cmd/relay/relay.go b/cmd/relay/relay.go index 40f4f26eec..5a7499e691 100644 --- a/cmd/relay/relay.go +++ b/cmd/relay/relay.go @@ -6,6 +6,7 @@ package main import ( "context" "fmt" + "io" "os" "os/signal" "syscall" @@ -19,6 +20,7 @@ import ( "github.com/offchainlabs/nitro/cmd/genericconf" "github.com/offchainlabs/nitro/cmd/util/confighelpers" "github.com/offchainlabs/nitro/relay" + "golang.org/x/exp/slog" ) func main() { @@ -62,14 +64,14 @@ func startup() error { confighelpers.PrintErrorAndExit(err, printSampleUsage) } - logFormat, err := genericconf.ParseLogType(relayConfig.LogType) + handler, err := genericconf.HandlerFromLogType(relayConfig.LogType, io.Writer(os.Stderr)) if err != nil { flag.Usage() - panic(fmt.Sprintf("Error parsing log type: %v", err)) + return fmt.Errorf("error parsing log type when creating handler: %w", err) } - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, logFormat)) - glogger.Verbosity(log.Lvl(relayConfig.LogLevel)) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler(handler) + glogger.Verbosity(slog.Level(relayConfig.LogLevel)) + log.SetDefault(log.NewLogger(glogger)) vcsRevision, _, vcsTime := confighelpers.GetVersion() log.Info("Running Arbitrum nitro relay", "revision", vcsRevision, "vcs.time", vcsTime) diff --git a/cmd/replay/main.go b/cmd/replay/main.go index 5369495324..3348d0b431 100644 --- a/cmd/replay/main.go +++ b/cmd/replay/main.go @@ -9,6 +9,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "io" "os" "github.com/ethereum/go-ethereum/common" @@ -172,9 +173,10 @@ func main() { wavmio.StubInit() gethhook.RequireHookedGeth() - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.LvlError) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) + glogger.Verbosity(log.LevelError) + log.SetDefault(log.NewLogger(glogger)) populateEcdsaCaches() diff --git a/das/aggregator_test.go b/das/aggregator_test.go index 776af3975b..ef8ef5327a 100644 --- a/das/aggregator_test.go +++ b/das/aggregator_test.go @@ -8,6 +8,7 @@ import ( "context" "errors" "fmt" + "io" "math/rand" "os" "strconv" @@ -158,9 +159,10 @@ func min(a, b int) int { } func enableLogging() { - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.LvlTrace) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) + glogger.Verbosity(log.LevelTrace) + log.SetDefault(log.NewLogger(glogger)) } func testConfigurableStorageFailures(t *testing.T, shouldFailAggregation bool) { diff --git a/execution/nodeInterface/virtual-contracts.go b/execution/nodeInterface/virtual-contracts.go index 3a863e31b5..d72ad0da8e 100644 --- a/execution/nodeInterface/virtual-contracts.go +++ b/execution/nodeInterface/virtual-contracts.go @@ -88,7 +88,7 @@ func init() { return msg, nil, nil } - evm, vmError := backend.GetEVM(ctx, msg, statedb, header, &vm.Config{NoBaseFee: true}, blockCtx) + evm := backend.GetEVM(ctx, msg, statedb, header, &vm.Config{NoBaseFee: true}, blockCtx) go func() { <-ctx.Done() evm.Cancel() @@ -110,7 +110,7 @@ func init() { ReturnData: output, ScheduledTxes: nil, } - return msg, res, vmError() + return msg, res, statedb.Error() } return msg, nil, nil } diff --git a/go-ethereum b/go-ethereum index daccadb06c..9830725715 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit daccadb06c7bd9ad7e86c74f33ea39d897f0ece4 +Subproject commit 9830725715f92cd4ed1809b3d069af2ef25ae6e6 diff --git a/go.mod b/go.mod index e48d99f489..ded1fced70 100644 --- a/go.mod +++ b/go.mod @@ -21,12 +21,12 @@ require ( github.com/codeclysm/extract/v3 v3.0.2 github.com/dgraph-io/badger/v4 v4.2.0 github.com/enescakir/emoji v1.0.0 - github.com/ethereum/go-ethereum v1.10.26 + github.com/ethereum/go-ethereum v1.13.14 github.com/fatih/structtag v1.2.0 github.com/gdamore/tcell/v2 v2.6.0 github.com/google/go-cmp v0.6.0 github.com/hashicorp/golang-lru/v2 v2.0.2 - github.com/holiman/uint256 v1.2.3 + github.com/holiman/uint256 v1.2.4 github.com/ipfs/go-cid v0.4.1 github.com/ipfs/go-libipfs v0.6.2 github.com/ipfs/interface-go-ipfs-core v0.11.0 @@ -41,9 +41,10 @@ require ( github.com/spf13/pflag v1.0.5 github.com/wealdtech/go-merkletree v1.0.0 golang.org/x/crypto v0.21.0 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa golang.org/x/sys v0.18.0 golang.org/x/term v0.18.0 - golang.org/x/tools v0.13.0 + golang.org/x/tools v0.15.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) @@ -73,7 +74,7 @@ require ( github.com/aws/smithy-go v1.15.0 // indirect github.com/benbjohnson/clock v1.3.0 // 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/blang/semver/v4 v4.0.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect @@ -90,6 +91,7 @@ require ( github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // 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/cskr/pubsub v1.0.2 // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect @@ -109,6 +111,7 @@ require ( github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gammazero/deque v0.2.1 // indirect + github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect github.com/gdamore/encoding v1.0.0 // indirect github.com/getsentry/sentry-go v0.18.0 // indirect github.com/go-logr/logr v1.2.3 // indirect @@ -280,10 +283,9 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect go4.org v0.0.0-20200411211856-f5505b9728dd // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/mod v0.12.0 // indirect + golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.22.0 // indirect - golang.org/x/sync v0.3.0 // indirect + golang.org/x/sync v0.5.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect @@ -316,7 +318,6 @@ require ( github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-redis/redis/v8 v8.11.4 - github.com/go-stack/stack v1.8.1 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/uuid v1.3.1 github.com/gorilla/websocket v1.5.0 // indirect diff --git a/go.sum b/go.sum index 484805a064..8be44da749 100644 --- a/go.sum +++ b/go.sum @@ -171,8 +171,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -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/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= @@ -274,6 +274,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHH github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 h1:HVTnpeuvF6Owjd5mniCL8DEXo7uYXdQEmOP4FJbV5tg= github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= +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/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= @@ -380,6 +382,8 @@ github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZ github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/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/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/tcell/v2 v2.6.0 h1:OKbluoP9VYmJwZwq/iLb4BxwKcwGthaa1YNBJIyCySg= @@ -429,8 +433,6 @@ github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyL github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 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= @@ -537,6 +539,7 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= @@ -646,8 +649,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/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= @@ -1812,8 +1815,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-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +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= @@ -1838,8 +1841,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/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.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +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-20180406214816-61147c48b25b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1922,8 +1925,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.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.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-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2112,8 +2115,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +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-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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= diff --git a/precompiles/precompile_test.go b/precompiles/precompile_test.go index 975856bced..376bfd7161 100644 --- a/precompiles/precompile_test.go +++ b/precompiles/precompile_test.go @@ -5,6 +5,7 @@ package precompiles import ( "fmt" + "io" "math/big" "os" "testing" @@ -181,9 +182,10 @@ func TestEventCosts(t *testing.T) { func TestPrecompilesPerArbosVersion(t *testing.T) { // Set up a logger in case log.Crit is called by Precompiles() - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.LvlWarn) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) + glogger.Verbosity(log.LevelWarn) + log.SetDefault(log.NewLogger(glogger)) expectedNewMethodsPerArbosVersion := map[uint64]int{ 0: 89, diff --git a/staker/challenge_test.go b/staker/challenge_test.go index c21ebcdecd..f74e18b63d 100644 --- a/staker/challenge_test.go +++ b/staker/challenge_test.go @@ -5,6 +5,7 @@ package staker import ( "context" + "io" "math/big" "os" "path" @@ -116,9 +117,10 @@ func runChallengeTest( testTimeout bool, maxInboxMessage uint64, ) { - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.LvlDebug) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) + glogger.Verbosity(log.LevelDebug) + log.SetDefault(log.NewLogger(glogger)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/system_tests/common_test.go b/system_tests/common_test.go index 7f9f4844fd..9fcbb605ed 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -8,6 +8,7 @@ import ( "context" "encoding/hex" "encoding/json" + "io" "math/big" "net" "os" @@ -64,6 +65,7 @@ import ( "github.com/offchainlabs/nitro/solgen/go/upgrade_executorgen" "github.com/offchainlabs/nitro/statetransfer" "github.com/offchainlabs/nitro/util/testhelpers" + "golang.org/x/exp/slog" ) type info = *BlockchainTestInfo @@ -590,7 +592,8 @@ func createTestL1BlockChainWithConfig(t *testing.T, l1info info, stackConfig *no nodeConf := ethconfig.Defaults nodeConf.NetworkId = chainConfig.ChainID.Uint64() - l1Genesis := core.DeveloperGenesisBlock(15_000_000, l1info.GetAddress("Faucet")) + faucetAddr := l1info.GetAddress("Faucet") + l1Genesis := core.DeveloperGenesisBlock(15_000_000, &faucetAddr) infoGenesis := l1info.GetGenesisAlloc() for acct, info := range infoGenesis { l1Genesis.Alloc[acct] = info @@ -1114,13 +1117,14 @@ func deploySimple( func TestMain(m *testing.M) { logLevelEnv := os.Getenv("TEST_LOGLEVEL") if logLevelEnv != "" { - logLevel, err := strconv.ParseUint(logLevelEnv, 10, 32) - if err != nil || logLevel > uint64(log.LvlTrace) { + logLevel, err := strconv.ParseInt(logLevelEnv, 10, 32) + if err != nil || logLevel > int64(log.LevelCrit) { log.Warn("TEST_LOGLEVEL exists but out of bound, ignoring", "logLevel", logLevelEnv, "max", log.LvlTrace) } - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.Lvl(logLevel)) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) + glogger.Verbosity(slog.Level(logLevel)) + log.SetDefault(log.NewLogger(glogger)) } code := m.Run() os.Exit(code) diff --git a/system_tests/das_test.go b/system_tests/das_test.go index c4a3c453d8..bb09cc9880 100644 --- a/system_tests/das_test.go +++ b/system_tests/das_test.go @@ -7,6 +7,7 @@ import ( "context" "encoding/base64" "encoding/json" + "io" "math/big" "net" "net/http" @@ -32,6 +33,7 @@ import ( "github.com/offchainlabs/nitro/solgen/go/precompilesgen" "github.com/offchainlabs/nitro/util/headerreader" "github.com/offchainlabs/nitro/util/signature" + "golang.org/x/exp/slog" ) func startLocalDASServer( @@ -356,9 +358,10 @@ func TestDASComplexConfigAndRestMirror(t *testing.T) { } func enableLogging(logLvl int) { - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.Lvl(logLvl)) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) + glogger.Verbosity(slog.Level(logLvl)) + log.SetDefault(log.NewLogger(glogger)) } func initTest(t *testing.T) { diff --git a/system_tests/debugapi_test.go b/system_tests/debugapi_test.go index 52a6bb25c4..30a2bee03e 100644 --- a/system_tests/debugapi_test.go +++ b/system_tests/debugapi_test.go @@ -2,15 +2,15 @@ package arbtest import ( "context" - "github.com/ethereum/go-ethereum/eth/tracers" + "encoding/json" "testing" - "encoding/json" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/rpc" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" ) @@ -34,7 +34,7 @@ func TestDebugAPI(t *testing.T) { err = l2rpc.CallContext(ctx, &badBlocks, "debug_getBadBlocks") Require(t, err) - var dumpIt state.IteratorDump + var dumpIt state.Dump err = l2rpc.CallContext(ctx, &dumpIt, "debug_accountRange", rpc.LatestBlockNumber, hexutil.Bytes{}, 10, true, true, false) Require(t, err) err = l2rpc.CallContext(ctx, &dumpIt, "debug_accountRange", rpc.PendingBlockNumber, hexutil.Bytes{}, 10, true, true, false) diff --git a/system_tests/estimation_test.go b/system_tests/estimation_test.go index 6f47c14f1f..e7f00ca94e 100644 --- a/system_tests/estimation_test.go +++ b/system_tests/estimation_test.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/gasestimator" "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/solgen/go/mocksgen" @@ -285,7 +286,7 @@ func TestComponentEstimate(t *testing.T) { l2Used := receipt.GasUsed - receipt.GasUsedForL1 colors.PrintMint("True ", receipt.GasUsed, " - ", receipt.GasUsedForL1, " = ", l2Used) - if l2Estimate != l2Used { + if float64(l2Estimate-l2Used) > float64(gasEstimateForL1+l2Used)*gasestimator.EstimateGasErrorRatio { Fatal(t, l2Estimate, l2Used) } } diff --git a/system_tests/full_challenge_impl_test.go b/system_tests/full_challenge_impl_test.go index 03b6d690f1..8c8ca40805 100644 --- a/system_tests/full_challenge_impl_test.go +++ b/system_tests/full_challenge_impl_test.go @@ -249,9 +249,10 @@ func createL2Nodes(t *testing.T, ctx context.Context, conf *arbnode.Config, chai } func RunChallengeTest(t *testing.T, asserterIsCorrect bool, useStubs bool, challengeMsgIdx int64) { - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) glogger.Verbosity(log.LvlInfo) - log.Root().SetHandler(glogger) + log.SetDefault(log.NewLogger(glogger)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/system_tests/retryable_test.go b/system_tests/retryable_test.go index b0691db173..132f2e7555 100644 --- a/system_tests/retryable_test.go +++ b/system_tests/retryable_test.go @@ -14,6 +14,7 @@ import ( "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/eth/gasestimator" "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbnode" "github.com/offchainlabs/nitro/arbos" @@ -158,8 +159,12 @@ func TestSubmitRetryableImmediateSuccess(t *testing.T) { Require(t, err, "failed to estimate retryable submission") estimate := tx.Gas() expectedEstimate := params.TxGas + params.TxDataNonZeroGasEIP2028*4 - if estimate != expectedEstimate { - t.Errorf("estimated retryable ticket at %v gas but expected %v", estimate, expectedEstimate) + if float64(estimate) > float64(expectedEstimate)*(1+gasestimator.EstimateGasErrorRatio) { + t.Errorf("estimated retryable ticket at %v gas but expected %v, with error margin of %v", + estimate, + expectedEstimate, + gasestimator.EstimateGasErrorRatio, + ) } // submit & auto redeem the retryable using the gas estimate diff --git a/system_tests/triedb_race_test.go b/system_tests/triedb_race_test.go index 6d9415df83..9f14f08898 100644 --- a/system_tests/triedb_race_test.go +++ b/system_tests/triedb_race_test.go @@ -14,7 +14,7 @@ import ( ) func TestTrieDBCommitRace(t *testing.T) { - _ = testhelpers.InitTestLog(t, log.LvlError) + _ = testhelpers.InitTestLog(t, log.LevelError) ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/util/testhelpers/testhelpers.go b/util/testhelpers/testhelpers.go index bccc269171..eafd0eda7e 100644 --- a/util/testhelpers/testhelpers.go +++ b/util/testhelpers/testhelpers.go @@ -4,7 +4,9 @@ package testhelpers import ( + "context" "crypto/rand" + "io" "os" "regexp" "sync" @@ -13,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/util/colors" + "golang.org/x/exp/slog" ) // Fail a test should an error occur @@ -43,19 +46,29 @@ func RandomAddress() common.Address { } type LogHandler struct { - mutex sync.Mutex - t *testing.T - records []log.Record - streamHandler log.Handler + mutex sync.Mutex + t *testing.T + records []slog.Record + terminalHandler *log.TerminalHandler } -func (h *LogHandler) Log(record *log.Record) error { - if err := h.streamHandler.Log(record); err != nil { +func (h *LogHandler) Enabled(_ context.Context, level slog.Level) bool { + return h.terminalHandler.Enabled(context.Background(), level) +} +func (h *LogHandler) WithGroup(name string) slog.Handler { + return h.terminalHandler.WithGroup(name) +} +func (h *LogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + return h.terminalHandler.WithAttrs(attrs) +} + +func (h *LogHandler) Handle(_ context.Context, record slog.Record) error { + if err := h.terminalHandler.Handle(context.Background(), record); err != nil { return err } h.mutex.Lock() defer h.mutex.Unlock() - h.records = append(h.records, *record) + h.records = append(h.records, record) return nil } @@ -65,7 +78,7 @@ func (h *LogHandler) WasLogged(pattern string) bool { h.mutex.Lock() defer h.mutex.Unlock() for _, record := range h.records { - if re.MatchString(record.Msg) { + if re.MatchString(record.Message) { return true } } @@ -74,16 +87,16 @@ func (h *LogHandler) WasLogged(pattern string) bool { func newLogHandler(t *testing.T) *LogHandler { return &LogHandler{ - t: t, - records: make([]log.Record, 0), - streamHandler: log.StreamHandler(os.Stderr, log.TerminalFormat(false)), + t: t, + records: make([]slog.Record, 0), + terminalHandler: log.NewTerminalHandler(io.Writer(os.Stderr), false), } } -func InitTestLog(t *testing.T, level log.Lvl) *LogHandler { +func InitTestLog(t *testing.T, level slog.Level) *LogHandler { handler := newLogHandler(t) glogger := log.NewGlogHandler(handler) glogger.Verbosity(level) - log.Root().SetHandler(glogger) + log.SetDefault(log.NewLogger(glogger)) return handler } From b2b84a6f3802906645a79c5188d1d7ec427cfc75 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Thu, 18 Apr 2024 16:34:29 -0500 Subject: [PATCH 04/41] increase ConditionalOptions TimestampMax to fix TestSendRawTransactionConditionalBasic --- system_tests/conditionaltx_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system_tests/conditionaltx_test.go b/system_tests/conditionaltx_test.go index 438e42d37e..5099fc6c01 100644 --- a/system_tests/conditionaltx_test.go +++ b/system_tests/conditionaltx_test.go @@ -101,7 +101,7 @@ func getOptions(address common.Address, rootHash common.Hash, slotValueMap map[c } func getFulfillableBlockTimeLimits(t *testing.T, blockNumber uint64, timestamp uint64) []*arbitrum_types.ConditionalOptions { - future := math.HexOrDecimal64(timestamp + 40) + future := math.HexOrDecimal64(timestamp + 70) past := math.HexOrDecimal64(timestamp - 1) futureBlockNumber := math.HexOrDecimal64(blockNumber + 1000) currentBlockNumber := math.HexOrDecimal64(blockNumber) From 7aff250bb80689088d99d2d18c7c5a610aa77d19 Mon Sep 17 00:00:00 2001 From: Aman Sanghi Date: Fri, 19 Apr 2024 11:13:38 +0530 Subject: [PATCH 05/41] Changes based on offline discussion --- arbos/arbosState/arbosstate.go | 48 +++++++++++++++++++++------------- cmd/nitro/init.go | 11 ++++++-- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/arbos/arbosState/arbosstate.go b/arbos/arbosState/arbosstate.go index 9e3b90532e..f7b7f0e7f6 100644 --- a/arbos/arbosState/arbosstate.go +++ b/arbos/arbosState/arbosstate.go @@ -36,24 +36,26 @@ import ( // persisted beyond the end of the test.) type ArbosState struct { - arbosVersion uint64 // version of the ArbOS storage format and semantics - upgradeVersion storage.StorageBackedUint64 // version we're planning to upgrade to, or 0 if not planning to upgrade - upgradeTimestamp storage.StorageBackedUint64 // when to do the planned upgrade - networkFeeAccount storage.StorageBackedAddress - l1PricingState *l1pricing.L1PricingState - l2PricingState *l2pricing.L2PricingState - retryableState *retryables.RetryableState - addressTable *addressTable.AddressTable - chainOwners *addressSet.AddressSet - sendMerkle *merkleAccumulator.MerkleAccumulator - blockhashes *blockhash.Blockhashes - chainId storage.StorageBackedBigInt - chainConfig storage.StorageBackedBytes - genesisBlockNum storage.StorageBackedUint64 - infraFeeAccount storage.StorageBackedAddress - brotliCompressionLevel storage.StorageBackedUint64 // brotli compression level used for pricing - backingStorage *storage.Storage - Burner burn.Burner + arbosVersion uint64 // version of the ArbOS storage format and semantics + maxArbosVersionSupported uint64 // maximum ArbOS version supported by this code + maxDebugArbosVersionSupported uint64 // maximum ArbOS version supported by this code in debug mode + upgradeVersion storage.StorageBackedUint64 // version we're planning to upgrade to, or 0 if not planning to upgrade + upgradeTimestamp storage.StorageBackedUint64 // when to do the planned upgrade + networkFeeAccount storage.StorageBackedAddress + l1PricingState *l1pricing.L1PricingState + l2PricingState *l2pricing.L2PricingState + retryableState *retryables.RetryableState + addressTable *addressTable.AddressTable + chainOwners *addressSet.AddressSet + sendMerkle *merkleAccumulator.MerkleAccumulator + blockhashes *blockhash.Blockhashes + chainId storage.StorageBackedBigInt + chainConfig storage.StorageBackedBytes + genesisBlockNum storage.StorageBackedUint64 + infraFeeAccount storage.StorageBackedAddress + brotliCompressionLevel storage.StorageBackedUint64 // brotli compression level used for pricing + backingStorage *storage.Storage + Burner burn.Burner } var ErrUninitializedArbOS = errors.New("ArbOS uninitialized") @@ -70,6 +72,8 @@ func OpenArbosState(stateDB vm.StateDB, burner burn.Burner) (*ArbosState, error) } return &ArbosState{ arbosVersion, + 20, + 20, backingStorage.OpenStorageBackedUint64(uint64(upgradeVersionOffset)), backingStorage.OpenStorageBackedUint64(uint64(upgradeTimestampOffset)), backingStorage.OpenStorageBackedAddress(uint64(networkFeeAccountOffset)), @@ -400,6 +404,14 @@ func (state *ArbosState) RetryableState() *retryables.RetryableState { return state.retryableState } +func (state *ArbosState) MaxArbosVersionSupported() uint64 { + return state.maxArbosVersionSupported +} + +func (state *ArbosState) MaxDebugArbosVersionSupported() uint64 { + return state.maxDebugArbosVersionSupported +} + func (state *ArbosState) L1PricingState() *l1pricing.L1PricingState { return state.l1PricingState } diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index 54a4eeea81..a45ec054a1 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -156,8 +156,15 @@ func validateBlockChain(blockChain *core.BlockChain, chainConfig *params.ChainCo } } // Make sure we don't allow accidentally downgrading ArbOS - if currentArbosState.ArbOSVersion() > chainConfig.ArbitrumChainParams.InitialArbOSVersion { - return fmt.Errorf("attempted to launch node with ArbOS version %v on ArbOS state with version %v", chainConfig.ArbitrumChainParams.InitialArbOSVersion, currentArbosState.ArbOSVersion()) + if chainConfig.DebugMode() { + if currentArbosState.ArbOSVersion() > currentArbosState.MaxDebugArbosVersionSupported() { + return fmt.Errorf("attempted to launch node in debug mode with ArbOS version %v on ArbOS state with version %v", currentArbosState.MaxDebugArbosVersionSupported(), currentArbosState.ArbOSVersion()) + } + } else { + if currentArbosState.ArbOSVersion() > currentArbosState.MaxArbosVersionSupported() { + return fmt.Errorf("attempted to launch node with ArbOS version %v on ArbOS state with version %v", currentArbosState.MaxArbosVersionSupported(), currentArbosState.ArbOSVersion()) + } + } return nil From fe3d330e2f65241ba769cada721f5424ad38246d Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Fri, 19 Apr 2024 13:58:07 +0200 Subject: [PATCH 06/41] Populate PrecompiledContractsArbOS30 in geth hook --- gethhook/geth-hook.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gethhook/geth-hook.go b/gethhook/geth-hook.go index dcd1788710..fa41edd179 100644 --- a/gethhook/geth-hook.go +++ b/gethhook/geth-hook.go @@ -67,6 +67,14 @@ func init() { vm.PrecompiledAddressesArbitrum = append(vm.PrecompiledAddressesArbitrum, addr) } + for addr, precompile := range vm.PrecompiledContractsArbitrum { + vm.PrecompiledContractsArbOS30[addr] = precompile + } + for addr, precompile := range vm.PrecompiledContractsP256Verify { + vm.PrecompiledContractsArbOS30[addr] = precompile + vm.PrecompiledAddressesArbOS30 = append(vm.PrecompiledAddressesArbOS30, addr) + } + core.RenderRPCError = func(data []byte) error { if len(data) < 4 { return nil From 42891b39be05d378e3231bd43deeb307fd67da7f Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Fri, 19 Apr 2024 13:58:54 +0200 Subject: [PATCH 07/41] Change geth pin --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index 22399a74e2..6d749bf837 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 22399a74e2b413e99a4f0d06c65862ced0d021c7 +Subproject commit 6d749bf837c32b9fad59d53b1335f33d0afc824d From 8f0729de3cc6c85222f095cd6318c50f261632ab Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Fri, 19 Apr 2024 16:40:02 +0200 Subject: [PATCH 08/41] Second draft of pubsub in nitro --- linters/linters.go | 3 +- pubsub/consumer.go | 14 ++- pubsub/producer.go | 16 ++- pubsub/pubsub_test.go | 14 +-- staker/block_validator.go | 26 ++-- staker/stateless_block_validator.go | 8 ++ validator/server_api/redisconsumer.go | 64 ++++++++++ validator/server_api/redisproducer.go | 116 ++++++++++++++++++ validator/server_api/validation/validation.go | 51 ++++++++ validator/server_api/validation_api.go | 11 ++ validator/server_arb/validator_spawner.go | 11 +- 11 files changed, 305 insertions(+), 29 deletions(-) create mode 100644 validator/server_api/redisconsumer.go create mode 100644 validator/server_api/redisproducer.go create mode 100644 validator/server_api/validation/validation.go diff --git a/linters/linters.go b/linters/linters.go index a6c9f6d55e..bf12b4d7c6 100644 --- a/linters/linters.go +++ b/linters/linters.go @@ -1,7 +1,6 @@ package main import ( - "github.com/offchainlabs/nitro/linters/koanf" "github.com/offchainlabs/nitro/linters/pointercheck" "github.com/offchainlabs/nitro/linters/rightshift" "github.com/offchainlabs/nitro/linters/structinit" @@ -10,7 +9,7 @@ import ( func main() { multichecker.Main( - koanf.Analyzer, + // koanf.Analyzer, pointercheck.Analyzer, rightshift.Analyzer, structinit.Analyzer, diff --git a/pubsub/consumer.go b/pubsub/consumer.go index 3de313f120..e899c458fe 100644 --- a/pubsub/consumer.go +++ b/pubsub/consumer.go @@ -29,6 +29,16 @@ type ConsumerConfig struct { RedisGroup string `koanf:"redis-group"` } +func (c ConsumerConfig) Clone() ConsumerConfig { + return ConsumerConfig{ + ResponseEntryTimeout: c.ResponseEntryTimeout, + KeepAliveTimeout: c.KeepAliveTimeout, + RedisURL: c.RedisURL, + RedisStream: c.RedisStream, + RedisGroup: c.RedisGroup, + } +} + var DefaultConsumerConfig = &ConsumerConfig{ ResponseEntryTimeout: time.Hour, KeepAliveTimeout: 5 * time.Minute, @@ -36,7 +46,7 @@ var DefaultConsumerConfig = &ConsumerConfig{ RedisGroup: "", } -var DefaultTestConsumerConfig = &ConsumerConfig{ +var TestConsumerConfig = &ConsumerConfig{ RedisStream: "", RedisGroup: "", ResponseEntryTimeout: time.Minute, @@ -65,7 +75,7 @@ type Message[Request any] struct { Value Request } -func NewConsumer[Request any, Response any](ctx context.Context, cfg *ConsumerConfig) (*Consumer[Request, Response], error) { +func NewConsumer[Request any, Response any](cfg *ConsumerConfig) (*Consumer[Request, Response], error) { if cfg.RedisURL == "" { return nil, fmt.Errorf("redis url cannot be empty") } diff --git a/pubsub/producer.go b/pubsub/producer.go index 13a4553e2f..a0353c717e 100644 --- a/pubsub/producer.go +++ b/pubsub/producer.go @@ -65,7 +65,19 @@ type ProducerConfig struct { RedisGroup string `koanf:"redis-group"` } -var DefaultProducerConfig = &ProducerConfig{ +func (c ProducerConfig) Clone() ProducerConfig { + return ProducerConfig{ + EnableReproduce: c.EnableReproduce, + RedisURL: c.RedisURL, + RedisStream: c.RedisStream, + CheckPendingInterval: c.CheckPendingInterval, + KeepAliveTimeout: c.KeepAliveTimeout, + CheckResultInterval: c.CheckResultInterval, + RedisGroup: c.RedisGroup, + } +} + +var DefaultProducerConfig = ProducerConfig{ EnableReproduce: true, RedisStream: "", RedisGroup: "", @@ -74,7 +86,7 @@ var DefaultProducerConfig = &ProducerConfig{ CheckResultInterval: 5 * time.Second, } -var DefaultTestProducerConfig = &ProducerConfig{ +var TestProducerConfig = ProducerConfig{ EnableReproduce: true, RedisStream: "", RedisGroup: "", diff --git a/pubsub/pubsub_test.go b/pubsub/pubsub_test.go index c8968b4e45..b574c1a68d 100644 --- a/pubsub/pubsub_test.go +++ b/pubsub/pubsub_test.go @@ -54,17 +54,17 @@ func (e *disableReproduce) apply(_ *ConsumerConfig, prodCfg *ProducerConfig) { func producerCfg() *ProducerConfig { return &ProducerConfig{ - EnableReproduce: DefaultTestProducerConfig.EnableReproduce, - CheckPendingInterval: DefaultTestProducerConfig.CheckPendingInterval, - KeepAliveTimeout: DefaultTestProducerConfig.KeepAliveTimeout, - CheckResultInterval: DefaultTestProducerConfig.CheckResultInterval, + EnableReproduce: TestProducerConfig.EnableReproduce, + CheckPendingInterval: TestProducerConfig.CheckPendingInterval, + KeepAliveTimeout: TestProducerConfig.KeepAliveTimeout, + CheckResultInterval: TestProducerConfig.CheckResultInterval, } } func consumerCfg() *ConsumerConfig { return &ConsumerConfig{ - ResponseEntryTimeout: DefaultTestConsumerConfig.ResponseEntryTimeout, - KeepAliveTimeout: DefaultTestConsumerConfig.KeepAliveTimeout, + ResponseEntryTimeout: TestConsumerConfig.ResponseEntryTimeout, + KeepAliveTimeout: TestConsumerConfig.KeepAliveTimeout, } } @@ -87,7 +87,7 @@ func newProducerConsumers(ctx context.Context, t *testing.T, opts ...configOpt) var consumers []*Consumer[testRequest, testResponse] for i := 0; i < consumersCount; i++ { - c, err := NewConsumer[testRequest, testResponse](ctx, consCfg) + c, err := NewConsumer[testRequest, testResponse](consCfg) if err != nil { t.Fatalf("Error creating new consumer: %v", err) } diff --git a/staker/block_validator.go b/staker/block_validator.go index 56cd5307d8..a65adbeff0 100644 --- a/staker/block_validator.go +++ b/staker/block_validator.go @@ -27,6 +27,7 @@ import ( "github.com/offchainlabs/nitro/util/rpcclient" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" + "github.com/offchainlabs/nitro/validator/server_api" ) var ( @@ -83,18 +84,19 @@ type BlockValidator struct { } type BlockValidatorConfig struct { - Enable bool `koanf:"enable"` - ValidationServer rpcclient.ClientConfig `koanf:"validation-server" reload:"hot"` - ValidationServerConfigs []rpcclient.ClientConfig `koanf:"validation-server-configs" reload:"hot"` - ValidationPoll time.Duration `koanf:"validation-poll" reload:"hot"` - PrerecordedBlocks uint64 `koanf:"prerecorded-blocks" reload:"hot"` - ForwardBlocks uint64 `koanf:"forward-blocks" reload:"hot"` - CurrentModuleRoot string `koanf:"current-module-root"` // TODO(magic) requires reinitialization on hot reload - PendingUpgradeModuleRoot string `koanf:"pending-upgrade-module-root"` // TODO(magic) requires StatelessBlockValidator recreation on hot reload - FailureIsFatal bool `koanf:"failure-is-fatal" reload:"hot"` - Dangerous BlockValidatorDangerousConfig `koanf:"dangerous"` - MemoryFreeLimit string `koanf:"memory-free-limit" reload:"hot"` - ValidationServerConfigsList string `koanf:"validation-server-configs-list" reload:"hot"` + Enable bool `koanf:"enable"` + ValidationServer rpcclient.ClientConfig `koanf:"validation-server" reload:"hot"` + RedisValidationClientConfig server_api.RedisValidationClientConfig `koanf:"redis-validation-client-config"` + ValidationServerConfigs []rpcclient.ClientConfig `koanf:"validation-server-configs" reload:"hot"` + ValidationPoll time.Duration `koanf:"validation-poll" reload:"hot"` + PrerecordedBlocks uint64 `koanf:"prerecorded-blocks" reload:"hot"` + ForwardBlocks uint64 `koanf:"forward-blocks" reload:"hot"` + CurrentModuleRoot string `koanf:"current-module-root"` // TODO(magic) requires reinitialization on hot reload + PendingUpgradeModuleRoot string `koanf:"pending-upgrade-module-root"` // TODO(magic) requires StatelessBlockValidator recreation on hot reload + FailureIsFatal bool `koanf:"failure-is-fatal" reload:"hot"` + Dangerous BlockValidatorDangerousConfig `koanf:"dangerous"` + MemoryFreeLimit string `koanf:"memory-free-limit" reload:"hot"` + ValidationServerConfigsList string `koanf:"validation-server-configs-list" reload:"hot"` memoryFreeLimit int } diff --git a/staker/stateless_block_validator.go b/staker/stateless_block_validator.go index abfc08ec33..cfccc793ad 100644 --- a/staker/stateless_block_validator.go +++ b/staker/stateless_block_validator.go @@ -194,11 +194,19 @@ func NewStatelessBlockValidator( config func() *BlockValidatorConfig, stack *node.Node, ) (*StatelessBlockValidator, error) { + validationSpawners := make([]validator.ValidationSpawner, len(config().ValidationServerConfigs)) for i, serverConfig := range config().ValidationServerConfigs { valConfFetcher := func() *rpcclient.ClientConfig { return &serverConfig } validationSpawners[i] = server_api.NewValidationClient(valConfFetcher, stack) } + redisValClient, err := server_api.NewRedisValidationClient(&config().RedisValidationClientConfig) + if err != nil { + log.Error("Creating redis validation client", "error", err) + } else { + validationSpawners = append(validationSpawners, redisValClient) + } + valConfFetcher := func() *rpcclient.ClientConfig { return &config().ValidationServerConfigs[0] } execClient := server_api.NewExecutionClient(valConfFetcher, stack) validator := &StatelessBlockValidator{ diff --git a/validator/server_api/redisconsumer.go b/validator/server_api/redisconsumer.go new file mode 100644 index 0000000000..bba8404ba3 --- /dev/null +++ b/validator/server_api/redisconsumer.go @@ -0,0 +1,64 @@ +package server_api + +import ( + "context" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/nitro/pubsub" + "github.com/offchainlabs/nitro/util/stopwaiter" + "github.com/offchainlabs/nitro/validator" + "github.com/offchainlabs/nitro/validator/server_api/validation" +) + +// RedisValidationServer implements consumer for the requests originated from +// RedisValidationClient producers. +type RedisValidationServer struct { + stopwaiter.StopWaiter + spawner validator.ValidationSpawner + + // consumers stores moduleRoot to consumer mapping. + consumers map[common.Hash]*pubsub.Consumer[*validator.ValidationInput, validator.GoGlobalState] +} + +func NewRedisValidationServer(cfg *validation.RedisValidationServerConfig) (*RedisValidationServer, error) { + res := &RedisValidationServer{} + for _, mr := range cfg.ModuleRoots { + conf := cfg.ConsumerConfig.Clone() + conf.RedisStream, conf.RedisGroup = redisStreamForRoot(mr), redisGroupForRoot(mr) + c, err := pubsub.NewConsumer[*validator.ValidationInput, validator.GoGlobalState](&conf) + if err != nil { + return nil, fmt.Errorf("creating consumer for validation: %w", err) + } + res.consumers[mr] = c + } + return res, nil +} + +func (s *RedisValidationServer) Start(ctx_in context.Context) { + s.StopWaiter.Start(ctx_in, s) + for moduleRoot, c := range s.consumers { + c := c + c.Start(ctx_in) + s.StopWaiter.CallIteratively(func(ctx context.Context) time.Duration { + req, err := c.Consume(ctx) + if err != nil { + log.Error("Consuming request", "error", err) + return 0 + } + valRun := s.spawner.Launch(req.Value, moduleRoot) + res, err := valRun.Await(ctx) + if err != nil { + log.Error("Error validating", "input", "request value", req.Value, "error", err) + return 0 + } + if err := c.SetResult(ctx, req.ID, res); err != nil { + log.Error("Error setting result for request", "id", req.ID, "result", res, "error", err) + return 0 + } + return time.Second + }) + } +} diff --git a/validator/server_api/redisproducer.go b/validator/server_api/redisproducer.go new file mode 100644 index 0000000000..cda3948421 --- /dev/null +++ b/validator/server_api/redisproducer.go @@ -0,0 +1,116 @@ +package server_api + +import ( + "context" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/offchainlabs/nitro/pubsub" + "github.com/offchainlabs/nitro/util/containers" + "github.com/offchainlabs/nitro/util/stopwaiter" + "github.com/offchainlabs/nitro/validator" + "github.com/offchainlabs/nitro/validator/server_common" + "github.com/spf13/pflag" +) + +type RedisValidationClientConfig struct { + Name string `koanf:"name"` + Room int32 `koanf:"room"` + ProducerConfig pubsub.ProducerConfig `koanf:"producer-config"` + // Supported wasm module roots. + ModuleRoots []common.Hash `koanf:"module-roots"` +} + +var DefaultRedisValidationClientConfig = &RedisValidationClientConfig{ + Name: "redis validation client", + Room: 2, + ProducerConfig: pubsub.DefaultProducerConfig, +} + +var TestRedisValidationClientConfig = &RedisValidationClientConfig{ + Name: "test redis validation client", + Room: 2, + ProducerConfig: pubsub.TestProducerConfig, +} + +func RedisValidationClientConfigAddOptions(prefix string, f *pflag.FlagSet) { + f.String(prefix+".name", DefaultRedisValidationClientConfig.Name, "validation client name") + f.Uint64(prefix+".room", uint64(DefaultRedisValidationClientConfig.Room), "validation client room") + pubsub.ProducerAddConfigAddOptions(prefix+".producer-config", f) + // TODO(anodar): initialize module roots here. +} + +// RedisValidationClient implements validation client through redis streams. +type RedisValidationClient struct { + stopwaiter.StopWaiter + name string + room int32 + // producers stores moduleRoot to producer mapping. + producers map[common.Hash]*pubsub.Producer[*validator.ValidationInput, validator.GoGlobalState] +} + +func redisGroupForRoot(moduleRoot common.Hash) string { + return fmt.Sprintf("group:%s", moduleRoot.Hex()) +} + +func redisStreamForRoot(moduleRoot common.Hash) string { + return fmt.Sprintf("group:%s", moduleRoot.Hex()) +} + +func NewRedisValidationClient(cfg *RedisValidationClientConfig) (*RedisValidationClient, error) { + res := &RedisValidationClient{ + name: cfg.Name, + room: cfg.Room, + producers: make(map[common.Hash]*pubsub.Producer[*validator.ValidationInput, validator.GoGlobalState]), + } + for _, mr := range cfg.ModuleRoots { + c := cfg.ProducerConfig.Clone() + c.RedisStream, c.RedisGroup = redisGroupForRoot(mr), redisStreamForRoot(mr) + p, err := pubsub.NewProducer[*validator.ValidationInput, validator.GoGlobalState](&c) + if err != nil { + return nil, fmt.Errorf("creating producer for validation: %w", err) + } + res.producers[mr] = p + } + return res, nil +} + +func (c *RedisValidationClient) Launch(entry *validator.ValidationInput, moduleRoot common.Hash) validator.ValidationRun { + producer, found := c.producers[moduleRoot] + if !found { + errPromise := containers.NewReadyPromise(validator.GoGlobalState{}, fmt.Errorf("no validation is configured for wasm root %v", moduleRoot)) + return server_common.NewValRun(errPromise, moduleRoot) + } + promise, err := producer.Produce(c.GetContext(), entry) + if err != nil { + errPromise := containers.NewReadyPromise(validator.GoGlobalState{}, fmt.Errorf("error producing input: %w", err)) + return server_common.NewValRun(errPromise, moduleRoot) + } + return server_common.NewValRun(promise, moduleRoot) +} + +func (c *RedisValidationClient) Start(ctx_in context.Context) error { + for _, p := range c.producers { + p.Start(ctx_in) + } + c.StopWaiter.Start(ctx_in, c) + return nil +} + +func (c *RedisValidationClient) Stop() { + for _, p := range c.producers { + p.StopAndWait() + } + c.StopWaiter.StopAndWait() +} + +func (c *RedisValidationClient) Name() string { + if c.Started() { + return c.name + } + return "(not started)" +} + +func (c *RedisValidationClient) Room() int { + return int(c.room) +} diff --git a/validator/server_api/validation/validation.go b/validator/server_api/validation/validation.go new file mode 100644 index 0000000000..75276f511c --- /dev/null +++ b/validator/server_api/validation/validation.go @@ -0,0 +1,51 @@ +// Package validation is introduced to avoid cyclic depenency between validation +// client and validation api. +package validation + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/offchainlabs/nitro/arbutil" + "github.com/offchainlabs/nitro/pubsub" + "github.com/offchainlabs/nitro/util/jsonapi" + "github.com/offchainlabs/nitro/validator" + "github.com/spf13/pflag" +) + +type Request struct { + Input *InputJSON + ModuleRoot common.Hash +} + +type InputJSON struct { + Id uint64 + HasDelayedMsg bool + DelayedMsgNr uint64 + PreimagesB64 map[arbutil.PreimageType]*jsonapi.PreimagesMapJson + BatchInfo []BatchInfoJson + DelayedMsgB64 string + StartState validator.GoGlobalState +} + +type BatchInfoJson struct { + Number uint64 + DataB64 string +} + +type RedisValidationServerConfig struct { + ConsumerConfig pubsub.ConsumerConfig `koanf:"consumer-config"` + // Supported wasm module roots. + ModuleRoots []common.Hash `koanf:"module-roots"` +} + +var DefaultRedisValidationServerConfig = &RedisValidationServerConfig{ + ConsumerConfig: *pubsub.DefaultConsumerConfig, +} + +var TestRedisValidationServerConfig = &RedisValidationServerConfig{ + ConsumerConfig: *pubsub.TestConsumerConfig, +} + +func RedisValidationServerConfigAddOptions(prefix string, f *pflag.FlagSet) { + pubsub.ProducerAddConfigAddOptions(prefix+".producer-config", f) + // TODO(anodar): initialize module roots here. +} diff --git a/validator/server_api/validation_api.go b/validator/server_api/validation_api.go index ca5aafcee2..2cdaea931b 100644 --- a/validator/server_api/validation_api.go +++ b/validator/server_api/validation_api.go @@ -9,6 +9,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" @@ -57,15 +58,22 @@ type ExecServerAPI struct { runIdLock sync.Mutex nextId uint64 runs map[uint64]*execRunEntry + + redisConsumer *RedisValidationServer } func NewExecutionServerAPI(valSpawner validator.ValidationSpawner, execution validator.ExecutionSpawner, config server_arb.ArbitratorSpawnerConfigFecher) *ExecServerAPI { + redisConsumer, err := NewRedisValidationServer(&config().RedisValidationServerConfig) + if err != nil { + log.Error("Creating new redis validation server", "error", err) + } return &ExecServerAPI{ ValidationServerAPI: *NewValidationServerAPI(valSpawner), execSpawner: execution, nextId: rand.Uint64(), // good-enough to aver reusing ids after reboot runs: make(map[uint64]*execRunEntry), config: config, + redisConsumer: redisConsumer, } } @@ -105,6 +113,9 @@ func (a *ExecServerAPI) removeOldRuns(ctx context.Context) time.Duration { func (a *ExecServerAPI) Start(ctx_in context.Context) { a.StopWaiter.Start(ctx_in, a) a.CallIteratively(a.removeOldRuns) + if a.redisConsumer != nil { + a.redisConsumer.Start(ctx_in) + } } func (a *ExecServerAPI) WriteToFile(ctx context.Context, jsonInput *ValidationInputJson, expOut validator.GoGlobalState, moduleRoot common.Hash) error { diff --git a/validator/server_arb/validator_spawner.go b/validator/server_arb/validator_spawner.go index 67aa5477eb..9366487793 100644 --- a/validator/server_arb/validator_spawner.go +++ b/validator/server_arb/validator_spawner.go @@ -17,6 +17,7 @@ import ( "github.com/offchainlabs/nitro/util/containers" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" + "github.com/offchainlabs/nitro/validator/server_api/validation" "github.com/offchainlabs/nitro/validator/server_common" "github.com/ethereum/go-ethereum/common" @@ -27,10 +28,11 @@ import ( var arbitratorValidationSteps = metrics.NewRegisteredHistogram("arbitrator/validation/steps", nil, metrics.NewBoundedHistogramSample()) type ArbitratorSpawnerConfig struct { - Workers int `koanf:"workers" reload:"hot"` - OutputPath string `koanf:"output-path" reload:"hot"` - Execution MachineCacheConfig `koanf:"execution" reload:"hot"` // hot reloading for new executions only - ExecutionRunTimeout time.Duration `koanf:"execution-run-timeout" reload:"hot"` + Workers int `koanf:"workers" reload:"hot"` + OutputPath string `koanf:"output-path" reload:"hot"` + Execution MachineCacheConfig `koanf:"execution" reload:"hot"` // hot reloading for new executions only + ExecutionRunTimeout time.Duration `koanf:"execution-run-timeout" reload:"hot"` + RedisValidationServerConfig validation.RedisValidationServerConfig `koanf:"redis-validation-server-config"` } type ArbitratorSpawnerConfigFecher func() *ArbitratorSpawnerConfig @@ -47,6 +49,7 @@ func ArbitratorSpawnerConfigAddOptions(prefix string, f *flag.FlagSet) { f.Duration(prefix+".execution-run-timeout", DefaultArbitratorSpawnerConfig.ExecutionRunTimeout, "timeout before discarding execution run") f.String(prefix+".output-path", DefaultArbitratorSpawnerConfig.OutputPath, "path to write machines to") MachineCacheConfigConfigAddOptions(prefix+".execution", f) + validation.RedisValidationServerConfigAddOptions(prefix+".redis-validation-server-config", f) } func DefaultArbitratorSpawnerConfigFetcher() *ArbitratorSpawnerConfig { From 22d59eff2378c861f96947b5ec8d343000a96446 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Fri, 19 Apr 2024 10:15:16 -0500 Subject: [PATCH 09/41] update pin to latest commit --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index 9830725715..018bd54e2e 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 9830725715f92cd4ed1809b3d069af2ef25ae6e6 +Subproject commit 018bd54e2ecdf494dce8f59e29cc083af9bdd74c From 46749920a122bc8861c8e3b945f2786d8fba3fb3 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Fri, 19 Apr 2024 17:53:32 +0200 Subject: [PATCH 10/41] Move RedisValidationServer to ValidationNode --- linters/linters.go | 3 ++- pubsub/consumer.go | 4 ++-- validator/server_api/json.go | 24 ++++--------------- validator/server_api/redisconsumer.go | 3 ++- validator/server_api/redisproducer.go | 16 ++++++++----- validator/server_api/validation/validation.go | 16 +++++++------ validator/server_api/validation_api.go | 18 ++++---------- validator/server_arb/validator_spawner.go | 9 +++---- validator/valnode/valnode.go | 12 +++++++++- 9 files changed, 50 insertions(+), 55 deletions(-) diff --git a/linters/linters.go b/linters/linters.go index bf12b4d7c6..a6c9f6d55e 100644 --- a/linters/linters.go +++ b/linters/linters.go @@ -1,6 +1,7 @@ package main import ( + "github.com/offchainlabs/nitro/linters/koanf" "github.com/offchainlabs/nitro/linters/pointercheck" "github.com/offchainlabs/nitro/linters/rightshift" "github.com/offchainlabs/nitro/linters/structinit" @@ -9,7 +10,7 @@ import ( func main() { multichecker.Main( - // koanf.Analyzer, + koanf.Analyzer, pointercheck.Analyzer, rightshift.Analyzer, structinit.Analyzer, diff --git a/pubsub/consumer.go b/pubsub/consumer.go index e899c458fe..92094edbdc 100644 --- a/pubsub/consumer.go +++ b/pubsub/consumer.go @@ -39,14 +39,14 @@ func (c ConsumerConfig) Clone() ConsumerConfig { } } -var DefaultConsumerConfig = &ConsumerConfig{ +var DefaultConsumerConfig = ConsumerConfig{ ResponseEntryTimeout: time.Hour, KeepAliveTimeout: 5 * time.Minute, RedisStream: "", RedisGroup: "", } -var TestConsumerConfig = &ConsumerConfig{ +var TestConsumerConfig = ConsumerConfig{ RedisStream: "", RedisGroup: "", ResponseEntryTimeout: time.Minute, diff --git a/validator/server_api/json.go b/validator/server_api/json.go index 2029741989..c1e4729571 100644 --- a/validator/server_api/json.go +++ b/validator/server_api/json.go @@ -10,29 +10,15 @@ import ( "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/util/jsonapi" "github.com/offchainlabs/nitro/validator" + "github.com/offchainlabs/nitro/validator/server_api/validation" ) -type BatchInfoJson struct { - Number uint64 - DataB64 string -} - -type ValidationInputJson struct { - Id uint64 - HasDelayedMsg bool - DelayedMsgNr uint64 - PreimagesB64 map[arbutil.PreimageType]*jsonapi.PreimagesMapJson - BatchInfo []BatchInfoJson - DelayedMsgB64 string - StartState validator.GoGlobalState -} - -func ValidationInputToJson(entry *validator.ValidationInput) *ValidationInputJson { +func ValidationInputToJson(entry *validator.ValidationInput) *validation.InputJSON { jsonPreimagesMap := make(map[arbutil.PreimageType]*jsonapi.PreimagesMapJson) for ty, preimages := range entry.Preimages { jsonPreimagesMap[ty] = jsonapi.NewPreimagesMapJson(preimages) } - res := &ValidationInputJson{ + res := &validation.InputJSON{ Id: entry.Id, HasDelayedMsg: entry.HasDelayedMsg, DelayedMsgNr: entry.DelayedMsgNr, @@ -42,12 +28,12 @@ func ValidationInputToJson(entry *validator.ValidationInput) *ValidationInputJso } for _, binfo := range entry.BatchInfo { encData := base64.StdEncoding.EncodeToString(binfo.Data) - res.BatchInfo = append(res.BatchInfo, BatchInfoJson{binfo.Number, encData}) + res.BatchInfo = append(res.BatchInfo, validation.BatchInfoJson{Number: binfo.Number, DataB64: encData}) } return res } -func ValidationInputFromJson(entry *ValidationInputJson) (*validator.ValidationInput, error) { +func ValidationInputFromJson(entry *validation.InputJSON) (*validator.ValidationInput, error) { preimages := make(map[arbutil.PreimageType]map[common.Hash][]byte) for ty, jsonPreimages := range entry.PreimagesB64 { preimages[ty] = jsonPreimages.Map diff --git a/validator/server_api/redisconsumer.go b/validator/server_api/redisconsumer.go index bba8404ba3..5053020a69 100644 --- a/validator/server_api/redisconsumer.go +++ b/validator/server_api/redisconsumer.go @@ -25,7 +25,8 @@ type RedisValidationServer struct { func NewRedisValidationServer(cfg *validation.RedisValidationServerConfig) (*RedisValidationServer, error) { res := &RedisValidationServer{} - for _, mr := range cfg.ModuleRoots { + for _, hash := range cfg.ModuleRoots { + mr := common.HexToHash(hash) conf := cfg.ConsumerConfig.Clone() conf.RedisStream, conf.RedisGroup = redisStreamForRoot(mr), redisGroupForRoot(mr) c, err := pubsub.NewConsumer[*validator.ValidationInput, validator.GoGlobalState](&conf) diff --git a/validator/server_api/redisproducer.go b/validator/server_api/redisproducer.go index cda3948421..0daab53b07 100644 --- a/validator/server_api/redisproducer.go +++ b/validator/server_api/redisproducer.go @@ -3,6 +3,7 @@ package server_api import ( "context" "fmt" + "sync/atomic" "github.com/ethereum/go-ethereum/common" "github.com/offchainlabs/nitro/pubsub" @@ -18,16 +19,16 @@ type RedisValidationClientConfig struct { Room int32 `koanf:"room"` ProducerConfig pubsub.ProducerConfig `koanf:"producer-config"` // Supported wasm module roots. - ModuleRoots []common.Hash `koanf:"module-roots"` + ModuleRoots []string `koanf:"module-roots"` } -var DefaultRedisValidationClientConfig = &RedisValidationClientConfig{ +var DefaultRedisValidationClientConfig = RedisValidationClientConfig{ Name: "redis validation client", Room: 2, ProducerConfig: pubsub.DefaultProducerConfig, } -var TestRedisValidationClientConfig = &RedisValidationClientConfig{ +var TestRedisValidationClientConfig = RedisValidationClientConfig{ Name: "test redis validation client", Room: 2, ProducerConfig: pubsub.TestProducerConfig, @@ -37,7 +38,7 @@ func RedisValidationClientConfigAddOptions(prefix string, f *pflag.FlagSet) { f.String(prefix+".name", DefaultRedisValidationClientConfig.Name, "validation client name") f.Uint64(prefix+".room", uint64(DefaultRedisValidationClientConfig.Room), "validation client room") pubsub.ProducerAddConfigAddOptions(prefix+".producer-config", f) - // TODO(anodar): initialize module roots here. + f.StringSlice(prefix+".module-roots", nil, "Supported module root hashes") } // RedisValidationClient implements validation client through redis streams. @@ -54,7 +55,7 @@ func redisGroupForRoot(moduleRoot common.Hash) string { } func redisStreamForRoot(moduleRoot common.Hash) string { - return fmt.Sprintf("group:%s", moduleRoot.Hex()) + return fmt.Sprintf("stream:%s", moduleRoot.Hex()) } func NewRedisValidationClient(cfg *RedisValidationClientConfig) (*RedisValidationClient, error) { @@ -63,7 +64,8 @@ func NewRedisValidationClient(cfg *RedisValidationClientConfig) (*RedisValidatio room: cfg.Room, producers: make(map[common.Hash]*pubsub.Producer[*validator.ValidationInput, validator.GoGlobalState]), } - for _, mr := range cfg.ModuleRoots { + for _, hash := range cfg.ModuleRoots { + mr := common.HexToHash(hash) c := cfg.ProducerConfig.Clone() c.RedisStream, c.RedisGroup = redisGroupForRoot(mr), redisStreamForRoot(mr) p, err := pubsub.NewProducer[*validator.ValidationInput, validator.GoGlobalState](&c) @@ -76,6 +78,8 @@ func NewRedisValidationClient(cfg *RedisValidationClientConfig) (*RedisValidatio } func (c *RedisValidationClient) Launch(entry *validator.ValidationInput, moduleRoot common.Hash) validator.ValidationRun { + atomic.AddInt32(&c.room, -1) + defer atomic.AddInt32(&c.room, 1) producer, found := c.producers[moduleRoot] if !found { errPromise := containers.NewReadyPromise(validator.GoGlobalState{}, fmt.Errorf("no validation is configured for wasm root %v", moduleRoot)) diff --git a/validator/server_api/validation/validation.go b/validator/server_api/validation/validation.go index 75276f511c..324de2d10b 100644 --- a/validator/server_api/validation/validation.go +++ b/validator/server_api/validation/validation.go @@ -34,18 +34,20 @@ type BatchInfoJson struct { type RedisValidationServerConfig struct { ConsumerConfig pubsub.ConsumerConfig `koanf:"consumer-config"` // Supported wasm module roots. - ModuleRoots []common.Hash `koanf:"module-roots"` + ModuleRoots []string `koanf:"module-roots"` } -var DefaultRedisValidationServerConfig = &RedisValidationServerConfig{ - ConsumerConfig: *pubsub.DefaultConsumerConfig, +var DefaultRedisValidationServerConfig = RedisValidationServerConfig{ + ConsumerConfig: pubsub.DefaultConsumerConfig, + ModuleRoots: []string{}, } -var TestRedisValidationServerConfig = &RedisValidationServerConfig{ - ConsumerConfig: *pubsub.TestConsumerConfig, +var TestRedisValidationServerConfig = RedisValidationServerConfig{ + ConsumerConfig: pubsub.TestConsumerConfig, + ModuleRoots: []string{}, } func RedisValidationServerConfigAddOptions(prefix string, f *pflag.FlagSet) { - pubsub.ProducerAddConfigAddOptions(prefix+".producer-config", f) - // TODO(anodar): initialize module roots here. + pubsub.ConsumerConfigAddOptions(prefix+".consumer-config", f) + f.StringSlice(prefix+".module-roots", nil, "Supported module root hashes") } diff --git a/validator/server_api/validation_api.go b/validator/server_api/validation_api.go index 2cdaea931b..076e1ef79c 100644 --- a/validator/server_api/validation_api.go +++ b/validator/server_api/validation_api.go @@ -9,10 +9,10 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" + "github.com/offchainlabs/nitro/validator/server_api/validation" "github.com/offchainlabs/nitro/validator/server_arb" ) @@ -30,7 +30,7 @@ func (a *ValidationServerAPI) Room() int { return a.spawner.Room() } -func (a *ValidationServerAPI) Validate(ctx context.Context, entry *ValidationInputJson, moduleRoot common.Hash) (validator.GoGlobalState, error) { +func (a *ValidationServerAPI) Validate(ctx context.Context, entry *validation.InputJSON, moduleRoot common.Hash) (validator.GoGlobalState, error) { valInput, err := ValidationInputFromJson(entry) if err != nil { return validator.GoGlobalState{}, err @@ -58,26 +58,19 @@ type ExecServerAPI struct { runIdLock sync.Mutex nextId uint64 runs map[uint64]*execRunEntry - - redisConsumer *RedisValidationServer } func NewExecutionServerAPI(valSpawner validator.ValidationSpawner, execution validator.ExecutionSpawner, config server_arb.ArbitratorSpawnerConfigFecher) *ExecServerAPI { - redisConsumer, err := NewRedisValidationServer(&config().RedisValidationServerConfig) - if err != nil { - log.Error("Creating new redis validation server", "error", err) - } return &ExecServerAPI{ ValidationServerAPI: *NewValidationServerAPI(valSpawner), execSpawner: execution, nextId: rand.Uint64(), // good-enough to aver reusing ids after reboot runs: make(map[uint64]*execRunEntry), config: config, - redisConsumer: redisConsumer, } } -func (a *ExecServerAPI) CreateExecutionRun(ctx context.Context, wasmModuleRoot common.Hash, jsonInput *ValidationInputJson) (uint64, error) { +func (a *ExecServerAPI) CreateExecutionRun(ctx context.Context, wasmModuleRoot common.Hash, jsonInput *validation.InputJSON) (uint64, error) { input, err := ValidationInputFromJson(jsonInput) if err != nil { return 0, err @@ -113,12 +106,9 @@ func (a *ExecServerAPI) removeOldRuns(ctx context.Context) time.Duration { func (a *ExecServerAPI) Start(ctx_in context.Context) { a.StopWaiter.Start(ctx_in, a) a.CallIteratively(a.removeOldRuns) - if a.redisConsumer != nil { - a.redisConsumer.Start(ctx_in) - } } -func (a *ExecServerAPI) WriteToFile(ctx context.Context, jsonInput *ValidationInputJson, expOut validator.GoGlobalState, moduleRoot common.Hash) error { +func (a *ExecServerAPI) WriteToFile(ctx context.Context, jsonInput *validation.InputJSON, expOut validator.GoGlobalState, moduleRoot common.Hash) error { input, err := ValidationInputFromJson(jsonInput) if err != nil { return err diff --git a/validator/server_arb/validator_spawner.go b/validator/server_arb/validator_spawner.go index 9366487793..a20a8d0e27 100644 --- a/validator/server_arb/validator_spawner.go +++ b/validator/server_arb/validator_spawner.go @@ -38,10 +38,11 @@ type ArbitratorSpawnerConfig struct { type ArbitratorSpawnerConfigFecher func() *ArbitratorSpawnerConfig var DefaultArbitratorSpawnerConfig = ArbitratorSpawnerConfig{ - Workers: 0, - OutputPath: "./target/output", - Execution: DefaultMachineCacheConfig, - ExecutionRunTimeout: time.Minute * 15, + Workers: 0, + OutputPath: "./target/output", + Execution: DefaultMachineCacheConfig, + ExecutionRunTimeout: time.Minute * 15, + RedisValidationServerConfig: validation.DefaultRedisValidationServerConfig, } func ArbitratorSpawnerConfigAddOptions(prefix string, f *flag.FlagSet) { diff --git a/validator/valnode/valnode.go b/validator/valnode/valnode.go index ca954094ff..5b4986f9da 100644 --- a/validator/valnode/valnode.go +++ b/validator/valnode/valnode.go @@ -5,6 +5,7 @@ import ( "github.com/offchainlabs/nitro/validator" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rpc" flag "github.com/spf13/pflag" @@ -75,6 +76,8 @@ type ValidationNode struct { config ValidationConfigFetcher arbSpawner *server_arb.ArbitratorSpawner jitSpawner *server_jit.JitSpawner + + redisConsumer *server_api.RedisValidationServer } func EnsureValidationExposedViaAuthRPC(stackConf *node.Config) { @@ -116,6 +119,10 @@ func CreateValidationNode(configFetcher ValidationConfigFetcher, stack *node.Nod } else { serverAPI = server_api.NewExecutionServerAPI(arbSpawner, arbSpawner, arbConfigFetcher) } + redisConsumer, err := server_api.NewRedisValidationServer(&arbConfigFetcher().RedisValidationServerConfig) + if err != nil { + log.Error("Creating new redis validation server", "error", err) + } valAPIs := []rpc.API{{ Namespace: server_api.Namespace, Version: "1.0", @@ -125,7 +132,7 @@ func CreateValidationNode(configFetcher ValidationConfigFetcher, stack *node.Nod }} stack.RegisterAPIs(valAPIs) - return &ValidationNode{configFetcher, arbSpawner, jitSpawner}, nil + return &ValidationNode{configFetcher, arbSpawner, jitSpawner, redisConsumer}, nil } func (v *ValidationNode) Start(ctx context.Context) error { @@ -137,6 +144,9 @@ func (v *ValidationNode) Start(ctx context.Context) error { return err } } + if v.redisConsumer != nil { + v.redisConsumer.Start(ctx) + } return nil } From 51d4666b8ae86123e9d671c6ac614fb96945499b Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Fri, 19 Apr 2024 18:55:09 +0200 Subject: [PATCH 11/41] Move redisURL and redisStream out from producer and consumer, pass it to the constructor instead --- pubsub/consumer.go | 53 +++++---------- pubsub/producer.go | 65 +++++++------------ pubsub/pubsub_test.go | 28 ++++---- validator/server_api/redisconsumer.go | 20 ++++-- validator/server_api/redisproducer.go | 26 +++++--- validator/server_api/validation/validation.go | 6 ++ 6 files changed, 90 insertions(+), 108 deletions(-) diff --git a/pubsub/consumer.go b/pubsub/consumer.go index 92094edbdc..7f8ca3a988 100644 --- a/pubsub/consumer.go +++ b/pubsub/consumer.go @@ -10,7 +10,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/go-redis/redis/v8" "github.com/google/uuid" - "github.com/offchainlabs/nitro/util/redisutil" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/spf13/pflag" ) @@ -21,34 +20,21 @@ type ConsumerConfig struct { // Duration after which consumer is considered to be dead if heartbeat // is not updated. KeepAliveTimeout time.Duration `koanf:"keepalive-timeout"` - // Redis url for Redis streams and locks. - RedisURL string `koanf:"redis-url"` - // Redis stream name. - RedisStream string `koanf:"redis-stream"` - // Redis consumer group name. - RedisGroup string `koanf:"redis-group"` } func (c ConsumerConfig) Clone() ConsumerConfig { return ConsumerConfig{ ResponseEntryTimeout: c.ResponseEntryTimeout, KeepAliveTimeout: c.KeepAliveTimeout, - RedisURL: c.RedisURL, - RedisStream: c.RedisStream, - RedisGroup: c.RedisGroup, } } var DefaultConsumerConfig = ConsumerConfig{ ResponseEntryTimeout: time.Hour, KeepAliveTimeout: 5 * time.Minute, - RedisStream: "", - RedisGroup: "", } var TestConsumerConfig = ConsumerConfig{ - RedisStream: "", - RedisGroup: "", ResponseEntryTimeout: time.Minute, KeepAliveTimeout: 30 * time.Millisecond, } @@ -56,18 +42,17 @@ var TestConsumerConfig = ConsumerConfig{ func ConsumerConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Duration(prefix+".response-entry-timeout", DefaultConsumerConfig.ResponseEntryTimeout, "timeout for response entry") f.Duration(prefix+".keepalive-timeout", DefaultConsumerConfig.KeepAliveTimeout, "timeout after which consumer is considered inactive if heartbeat wasn't performed") - f.String(prefix+".redis-url", DefaultConsumerConfig.RedisURL, "redis url for redis stream") - f.String(prefix+".redis-stream", DefaultConsumerConfig.RedisStream, "redis stream name to read from") - f.String(prefix+".redis-group", DefaultConsumerConfig.RedisGroup, "redis stream consumer group name") } // Consumer implements a consumer for redis stream provides heartbeat to // indicate it is alive. type Consumer[Request any, Response any] struct { stopwaiter.StopWaiter - id string - client redis.UniversalClient - cfg *ConsumerConfig + id string + client redis.UniversalClient + redisStream string + redisGroup string + cfg *ConsumerConfig } type Message[Request any] struct { @@ -75,24 +60,16 @@ type Message[Request any] struct { Value Request } -func NewConsumer[Request any, Response any](cfg *ConsumerConfig) (*Consumer[Request, Response], error) { - if cfg.RedisURL == "" { - return nil, fmt.Errorf("redis url cannot be empty") - } - if cfg.RedisStream == "" { +func NewConsumer[Request any, Response any](client redis.UniversalClient, streamName string, cfg *ConsumerConfig) (*Consumer[Request, Response], error) { + if streamName == "" { return nil, fmt.Errorf("redis stream name cannot be empty") } - if cfg.RedisGroup == "" { - return nil, fmt.Errorf("redis group name cannot be emtpy") - } - c, err := redisutil.RedisClientFromURL(cfg.RedisURL) - if err != nil { - return nil, err - } consumer := &Consumer[Request, Response]{ - id: uuid.NewString(), - client: c, - cfg: cfg, + id: uuid.NewString(), + client: client, + redisStream: streamName, + redisGroup: streamName, // There is 1-1 mapping of redis stream and consumer group. + cfg: cfg, } return consumer, nil } @@ -135,11 +112,11 @@ func (c *Consumer[Request, Response]) heartBeat(ctx context.Context) { // unresponsive consumer, if not then reads from the stream. func (c *Consumer[Request, Response]) Consume(ctx context.Context) (*Message[Request], error) { res, err := c.client.XReadGroup(ctx, &redis.XReadGroupArgs{ - Group: c.cfg.RedisGroup, + Group: c.redisGroup, Consumer: c.id, // Receive only messages that were never delivered to any other consumer, // that is, only new messages. - Streams: []string{c.cfg.RedisStream, ">"}, + Streams: []string{c.redisStream, ">"}, Count: 1, Block: time.Millisecond, // 0 seems to block the read instead of immediately returning }).Result() @@ -180,7 +157,7 @@ func (c *Consumer[Request, Response]) SetResult(ctx context.Context, messageID s if err != nil || !acquired { return fmt.Errorf("setting result for message: %v, error: %w", messageID, err) } - if _, err := c.client.XAck(ctx, c.cfg.RedisStream, c.cfg.RedisGroup, messageID).Result(); err != nil { + if _, err := c.client.XAck(ctx, c.redisStream, c.redisGroup, messageID).Result(); err != nil { return fmt.Errorf("acking message: %v, error: %w", messageID, err) } return nil diff --git a/pubsub/producer.go b/pubsub/producer.go index a0353c717e..7f7f05389b 100644 --- a/pubsub/producer.go +++ b/pubsub/producer.go @@ -20,7 +20,6 @@ import ( "github.com/go-redis/redis/v8" "github.com/google/uuid" "github.com/offchainlabs/nitro/util/containers" - "github.com/offchainlabs/nitro/util/redisutil" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/spf13/pflag" ) @@ -32,9 +31,11 @@ const ( type Producer[Request any, Response any] struct { stopwaiter.StopWaiter - id string - client redis.UniversalClient - cfg *ProducerConfig + id string + client redis.UniversalClient + redisStream string + redisGroup string + cfg *ProducerConfig promisesLock sync.RWMutex promises map[string]*containers.Promise[Response] @@ -49,10 +50,7 @@ type ProducerConfig struct { // When enabled, messages that are sent to consumers that later die before // processing them, will be re-inserted into the stream to be proceesed by // another consumer - EnableReproduce bool `koanf:"enable-reproduce"` - RedisURL string `koanf:"redis-url"` - // Redis stream name. - RedisStream string `koanf:"redis-stream"` + EnableReproduce bool `koanf:"enable-reproduce"` // Interval duration in which producer checks for pending messages delivered // to the consumers that are currently inactive. CheckPendingInterval time.Duration `koanf:"check-pending-interval"` @@ -61,26 +59,19 @@ type ProducerConfig struct { KeepAliveTimeout time.Duration `koanf:"keepalive-timeout"` // Interval duration for checking the result set by consumers. CheckResultInterval time.Duration `koanf:"check-result-interval"` - // Redis consumer group name. - RedisGroup string `koanf:"redis-group"` } func (c ProducerConfig) Clone() ProducerConfig { return ProducerConfig{ EnableReproduce: c.EnableReproduce, - RedisURL: c.RedisURL, - RedisStream: c.RedisStream, CheckPendingInterval: c.CheckPendingInterval, KeepAliveTimeout: c.KeepAliveTimeout, CheckResultInterval: c.CheckResultInterval, - RedisGroup: c.RedisGroup, } } var DefaultProducerConfig = ProducerConfig{ EnableReproduce: true, - RedisStream: "", - RedisGroup: "", CheckPendingInterval: time.Second, KeepAliveTimeout: 5 * time.Minute, CheckResultInterval: 5 * time.Second, @@ -88,8 +79,6 @@ var DefaultProducerConfig = ProducerConfig{ var TestProducerConfig = ProducerConfig{ EnableReproduce: true, - RedisStream: "", - RedisGroup: "", CheckPendingInterval: 10 * time.Millisecond, KeepAliveTimeout: 100 * time.Millisecond, CheckResultInterval: 5 * time.Millisecond, @@ -97,32 +86,24 @@ var TestProducerConfig = ProducerConfig{ func ProducerAddConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".enable-reproduce", DefaultProducerConfig.EnableReproduce, "when enabled, messages with dead consumer will be re-inserted into the stream") - f.String(prefix+".redis-url", DefaultProducerConfig.RedisURL, "redis url for redis stream") f.Duration(prefix+".check-pending-interval", DefaultProducerConfig.CheckPendingInterval, "interval in which producer checks pending messages whether consumer processing them is inactive") f.Duration(prefix+".keepalive-timeout", DefaultProducerConfig.KeepAliveTimeout, "timeout after which consumer is considered inactive if heartbeat wasn't performed") - f.String(prefix+".redis-stream", DefaultProducerConfig.RedisStream, "redis stream name to read from") - f.String(prefix+".redis-group", DefaultProducerConfig.RedisGroup, "redis stream consumer group name") } -func NewProducer[Request any, Response any](cfg *ProducerConfig) (*Producer[Request, Response], error) { - if cfg.RedisURL == "" { - return nil, fmt.Errorf("redis url cannot be empty") +func NewProducer[Request any, Response any](client redis.UniversalClient, streamName string, cfg *ProducerConfig) (*Producer[Request, Response], error) { + if client == nil { + return nil, fmt.Errorf("redis client cannot be nil") } - if cfg.RedisStream == "" { - return nil, fmt.Errorf("redis stream cannot be emtpy") - } - if cfg.RedisGroup == "" { - return nil, fmt.Errorf("redis group cannot be empty") - } - c, err := redisutil.RedisClientFromURL(cfg.RedisURL) - if err != nil { - return nil, err + if streamName == "" { + return nil, fmt.Errorf("stream name cannot be empty") } return &Producer[Request, Response]{ - id: uuid.NewString(), - client: c, - cfg: cfg, - promises: make(map[string]*containers.Promise[Response]), + id: uuid.NewString(), + client: client, + redisStream: streamName, + redisGroup: streamName, // There is 1-1 mapping of redis stream and consumer group. + cfg: cfg, + promises: make(map[string]*containers.Promise[Response]), }, nil } @@ -154,7 +135,7 @@ func (p *Producer[Request, Response]) checkAndReproduce(ctx context.Context) tim } acked := make(map[string]Request) for _, msg := range msgs { - if _, err := p.client.XAck(ctx, p.cfg.RedisStream, p.cfg.RedisGroup, msg.ID).Result(); err != nil { + if _, err := p.client.XAck(ctx, p.redisStream, p.redisGroup, msg.ID).Result(); err != nil { log.Error("ACKing message", "error", err) continue } @@ -212,7 +193,7 @@ func (p *Producer[Request, Response]) reproduce(ctx context.Context, value Reque return nil, fmt.Errorf("marshaling value: %w", err) } id, err := p.client.XAdd(ctx, &redis.XAddArgs{ - Stream: p.cfg.RedisStream, + Stream: p.redisStream, Values: map[string]any{messageKey: val}, }).Result() if err != nil { @@ -260,8 +241,8 @@ func (p *Producer[Request, Response]) havePromiseFor(messageID string) bool { func (p *Producer[Request, Response]) checkPending(ctx context.Context) ([]*Message[Request], error) { pendingMessages, err := p.client.XPendingExt(ctx, &redis.XPendingExtArgs{ - Stream: p.cfg.RedisStream, - Group: p.cfg.RedisGroup, + Stream: p.redisStream, + Group: p.redisGroup, Start: "-", End: "+", Count: 100, @@ -297,8 +278,8 @@ func (p *Producer[Request, Response]) checkPending(ctx context.Context) ([]*Mess } log.Info("Attempting to claim", "messages", ids) claimedMsgs, err := p.client.XClaim(ctx, &redis.XClaimArgs{ - Stream: p.cfg.RedisStream, - Group: p.cfg.RedisGroup, + Stream: p.redisStream, + Group: p.redisGroup, Consumer: p.id, MinIdle: p.cfg.KeepAliveTimeout, Messages: ids, diff --git a/pubsub/pubsub_test.go b/pubsub/pubsub_test.go index b574c1a68d..949e532343 100644 --- a/pubsub/pubsub_test.go +++ b/pubsub/pubsub_test.go @@ -28,16 +28,17 @@ type testResponse struct { Response string } -func createGroup(ctx context.Context, t *testing.T, streamName, groupName string, client redis.UniversalClient) { +func createGroup(ctx context.Context, t *testing.T, streamName string, client redis.UniversalClient) { t.Helper() - if _, err := client.XGroupCreateMkStream(ctx, streamName, groupName, "$").Result(); err != nil { + // Stream name and group name are the same. + if _, err := client.XGroupCreateMkStream(ctx, streamName, streamName, "$").Result(); err != nil { t.Fatalf("Error creating stream group: %v", err) } } -func destroyGroup(ctx context.Context, t *testing.T, streamName, groupName string, client redis.UniversalClient) { +func destroyGroup(ctx context.Context, t *testing.T, streamName string, client redis.UniversalClient) { t.Helper() - if _, err := client.XGroupDestroy(ctx, streamName, groupName).Result(); err != nil { + if _, err := client.XGroupDestroy(ctx, streamName, streamName).Result(); err != nil { log.Debug("Error destroying a stream group", "error", err) } } @@ -70,33 +71,32 @@ func consumerCfg() *ConsumerConfig { func newProducerConsumers(ctx context.Context, t *testing.T, opts ...configOpt) (*Producer[testRequest, testResponse], []*Consumer[testRequest, testResponse]) { t.Helper() - redisURL := redisutil.CreateTestRedis(ctx, t) + redisClient, err := redisutil.RedisClientFromURL(redisutil.CreateTestRedis(ctx, t)) + if err != nil { + t.Fatalf("RedisClientFromURL() unexpected error: %v", err) + } prodCfg, consCfg := producerCfg(), consumerCfg() - prodCfg.RedisURL, consCfg.RedisURL = redisURL, redisURL - streamName := uuid.NewString() - groupName := fmt.Sprintf("group_%s", streamName) - prodCfg.RedisGroup, consCfg.RedisGroup = groupName, groupName - prodCfg.RedisStream, consCfg.RedisStream = streamName, streamName + streamName := fmt.Sprintf("stream:%s", uuid.NewString()) for _, o := range opts { o.apply(consCfg, prodCfg) } - producer, err := NewProducer[testRequest, testResponse](prodCfg) + producer, err := NewProducer[testRequest, testResponse](redisClient, streamName, prodCfg) if err != nil { t.Fatalf("Error creating new producer: %v", err) } var consumers []*Consumer[testRequest, testResponse] for i := 0; i < consumersCount; i++ { - c, err := NewConsumer[testRequest, testResponse](consCfg) + c, err := NewConsumer[testRequest, testResponse](redisClient, streamName, consCfg) if err != nil { t.Fatalf("Error creating new consumer: %v", err) } consumers = append(consumers, c) } - createGroup(ctx, t, streamName, groupName, producer.client) + createGroup(ctx, t, streamName, producer.client) t.Cleanup(func() { ctx := context.Background() - destroyGroup(ctx, t, streamName, groupName, producer.client) + destroyGroup(ctx, t, streamName, producer.client) var keys []string for _, c := range consumers { keys = append(keys, c.heartBeatKey()) diff --git a/validator/server_api/redisconsumer.go b/validator/server_api/redisconsumer.go index 5053020a69..bc40d19d73 100644 --- a/validator/server_api/redisconsumer.go +++ b/validator/server_api/redisconsumer.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/pubsub" + "github.com/offchainlabs/nitro/util/redisutil" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" "github.com/offchainlabs/nitro/validator/server_api/validation" @@ -24,18 +25,25 @@ type RedisValidationServer struct { } func NewRedisValidationServer(cfg *validation.RedisValidationServerConfig) (*RedisValidationServer, error) { - res := &RedisValidationServer{} + if cfg.RedisURL == "" { + return nil, fmt.Errorf("redis url cannot be empty") + } + redisClient, err := redisutil.RedisClientFromURL(cfg.RedisURL) + if err != nil { + return nil, err + } + consumers := make(map[common.Hash]*pubsub.Consumer[*validator.ValidationInput, validator.GoGlobalState]) for _, hash := range cfg.ModuleRoots { mr := common.HexToHash(hash) - conf := cfg.ConsumerConfig.Clone() - conf.RedisStream, conf.RedisGroup = redisStreamForRoot(mr), redisGroupForRoot(mr) - c, err := pubsub.NewConsumer[*validator.ValidationInput, validator.GoGlobalState](&conf) + c, err := pubsub.NewConsumer[*validator.ValidationInput, validator.GoGlobalState](redisClient, redisStreamForRoot(mr), &cfg.ConsumerConfig) if err != nil { return nil, fmt.Errorf("creating consumer for validation: %w", err) } - res.consumers[mr] = c + consumers[mr] = c } - return res, nil + return &RedisValidationServer{ + consumers: consumers, + }, nil } func (s *RedisValidationServer) Start(ctx_in context.Context) { diff --git a/validator/server_api/redisproducer.go b/validator/server_api/redisproducer.go index 0daab53b07..5540cd169f 100644 --- a/validator/server_api/redisproducer.go +++ b/validator/server_api/redisproducer.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/offchainlabs/nitro/pubsub" "github.com/offchainlabs/nitro/util/containers" + "github.com/offchainlabs/nitro/util/redisutil" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" "github.com/offchainlabs/nitro/validator/server_common" @@ -17,6 +18,8 @@ import ( type RedisValidationClientConfig struct { Name string `koanf:"name"` Room int32 `koanf:"room"` + RedisURL string `koanf:"redis-url"` + RedisStream string `koanf:"redis-stream"` ProducerConfig pubsub.ProducerConfig `koanf:"producer-config"` // Supported wasm module roots. ModuleRoots []string `koanf:"module-roots"` @@ -25,18 +28,23 @@ type RedisValidationClientConfig struct { var DefaultRedisValidationClientConfig = RedisValidationClientConfig{ Name: "redis validation client", Room: 2, + RedisURL: "", + RedisStream: "", ProducerConfig: pubsub.DefaultProducerConfig, } var TestRedisValidationClientConfig = RedisValidationClientConfig{ Name: "test redis validation client", Room: 2, + RedisURL: "", + RedisStream: "", ProducerConfig: pubsub.TestProducerConfig, } func RedisValidationClientConfigAddOptions(prefix string, f *pflag.FlagSet) { f.String(prefix+".name", DefaultRedisValidationClientConfig.Name, "validation client name") - f.Uint64(prefix+".room", uint64(DefaultRedisValidationClientConfig.Room), "validation client room") + f.Int32(prefix+".room", DefaultRedisValidationClientConfig.Room, "validation client room") + f.String(prefix+".redis-stream", DefaultRedisValidationClientConfig.RedisStream, "redis stream name") pubsub.ProducerAddConfigAddOptions(prefix+".producer-config", f) f.StringSlice(prefix+".module-roots", nil, "Supported module root hashes") } @@ -50,10 +58,6 @@ type RedisValidationClient struct { producers map[common.Hash]*pubsub.Producer[*validator.ValidationInput, validator.GoGlobalState] } -func redisGroupForRoot(moduleRoot common.Hash) string { - return fmt.Sprintf("group:%s", moduleRoot.Hex()) -} - func redisStreamForRoot(moduleRoot common.Hash) string { return fmt.Sprintf("stream:%s", moduleRoot.Hex()) } @@ -64,11 +68,17 @@ func NewRedisValidationClient(cfg *RedisValidationClientConfig) (*RedisValidatio room: cfg.Room, producers: make(map[common.Hash]*pubsub.Producer[*validator.ValidationInput, validator.GoGlobalState]), } + if cfg.RedisURL == "" { + return nil, fmt.Errorf("redis url cannot be empty") + } + redisClient, err := redisutil.RedisClientFromURL(cfg.RedisURL) + if err != nil { + return nil, err + } for _, hash := range cfg.ModuleRoots { mr := common.HexToHash(hash) - c := cfg.ProducerConfig.Clone() - c.RedisStream, c.RedisGroup = redisGroupForRoot(mr), redisStreamForRoot(mr) - p, err := pubsub.NewProducer[*validator.ValidationInput, validator.GoGlobalState](&c) + p, err := pubsub.NewProducer[*validator.ValidationInput, validator.GoGlobalState]( + redisClient, redisStreamForRoot(mr), &cfg.ProducerConfig) if err != nil { return nil, fmt.Errorf("creating producer for validation: %w", err) } diff --git a/validator/server_api/validation/validation.go b/validator/server_api/validation/validation.go index 324de2d10b..9cab29bde7 100644 --- a/validator/server_api/validation/validation.go +++ b/validator/server_api/validation/validation.go @@ -32,17 +32,23 @@ type BatchInfoJson struct { } type RedisValidationServerConfig struct { + RedisURL string `koanf:"redis-url"` + RedisStream string `koanf:"redis-stream"` ConsumerConfig pubsub.ConsumerConfig `koanf:"consumer-config"` // Supported wasm module roots. ModuleRoots []string `koanf:"module-roots"` } var DefaultRedisValidationServerConfig = RedisValidationServerConfig{ + RedisURL: "", + RedisStream: "", ConsumerConfig: pubsub.DefaultConsumerConfig, ModuleRoots: []string{}, } var TestRedisValidationServerConfig = RedisValidationServerConfig{ + RedisURL: "", + RedisStream: "", ConsumerConfig: pubsub.TestConsumerConfig, ModuleRoots: []string{}, } From 849667989ff601832a558b2493092a7fab76db06 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Fri, 19 Apr 2024 22:39:09 +0200 Subject: [PATCH 12/41] Implement system tests --- arbnode/node.go | 2 +- pubsub/consumer.go | 3 +- pubsub/producer.go | 1 + staker/block_validator.go | 8 +++- staker/stateless_block_validator.go | 34 ++++++++------ system_tests/block_validator_test.go | 31 +++++++++--- system_tests/common_test.go | 47 +++++++++++++++++-- validator/server_api/redisconsumer.go | 9 +++- validator/server_api/redisproducer.go | 13 +++-- validator/server_api/validation/validation.go | 3 -- validator/server_api/validation_client.go | 13 +++-- validator/valnode/valnode.go | 2 +- 12 files changed, 120 insertions(+), 46 deletions(-) diff --git a/arbnode/node.go b/arbnode/node.go index 7a7a99ba88..43a05155fe 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -540,7 +540,7 @@ func createNodeImpl( txStreamer.SetInboxReaders(inboxReader, delayedBridge) var statelessBlockValidator *staker.StatelessBlockValidator - if config.BlockValidator.ValidationServerConfigs[0].URL != "" { + if config.BlockValidator.RedisValidationClientConfig.Enabled() || config.BlockValidator.ValidationServerConfigs[0].URL != "" { statelessBlockValidator, err = staker.NewStatelessBlockValidator( inboxReader, inboxTracker, diff --git a/pubsub/consumer.go b/pubsub/consumer.go index 7f8ca3a988..5385b33979 100644 --- a/pubsub/consumer.go +++ b/pubsub/consumer.go @@ -129,7 +129,6 @@ func (c *Consumer[Request, Response]) Consume(ctx context.Context) (*Message[Req if len(res) != 1 || len(res[0].Messages) != 1 { return nil, fmt.Errorf("redis returned entries: %+v, for querying single message", res) } - log.Debug(fmt.Sprintf("Consumer: %s consuming message: %s", c.id, res[0].Messages[0].ID)) var ( value = res[0].Messages[0].Values[messageKey] data, ok = (value).(string) @@ -141,7 +140,7 @@ func (c *Consumer[Request, Response]) Consume(ctx context.Context) (*Message[Req if err := json.Unmarshal([]byte(data), &req); err != nil { return nil, fmt.Errorf("unmarshaling value: %v, error: %w", value, err) } - + log.Debug("Redis stream consuming", "consumer_id", c.id, "message_id", res[0].Messages[0].ID) return &Message[Request]{ ID: res[0].Messages[0].ID, Value: req, diff --git a/pubsub/producer.go b/pubsub/producer.go index 7f7f05389b..debea81365 100644 --- a/pubsub/producer.go +++ b/pubsub/producer.go @@ -217,6 +217,7 @@ func (p *Producer[Request, Response]) reproduce(ctx context.Context, value Reque } func (p *Producer[Request, Response]) Produce(ctx context.Context, value Request) (*containers.Promise[Response], error) { + log.Debug("Redis stream producing", "value", value) p.once.Do(func() { p.StopWaiter.CallIteratively(p.checkAndReproduce) p.StopWaiter.CallIteratively(p.checkResponses) diff --git a/staker/block_validator.go b/staker/block_validator.go index a65adbeff0..1ec160c558 100644 --- a/staker/block_validator.go +++ b/staker/block_validator.go @@ -111,18 +111,19 @@ func (c *BlockValidatorConfig) Validate() error { } c.memoryFreeLimit = limit } + streamsEnabled := c.RedisValidationClientConfig.Enabled() if c.ValidationServerConfigs == nil { if c.ValidationServerConfigsList == "default" { c.ValidationServerConfigs = []rpcclient.ClientConfig{c.ValidationServer} } else { var validationServersConfigs []rpcclient.ClientConfig - if err := json.Unmarshal([]byte(c.ValidationServerConfigsList), &validationServersConfigs); err != nil { + if err := json.Unmarshal([]byte(c.ValidationServerConfigsList), &validationServersConfigs); err != nil && !streamsEnabled { return fmt.Errorf("failed to parse block-validator validation-server-configs-list string: %w", err) } c.ValidationServerConfigs = validationServersConfigs } } - if len(c.ValidationServerConfigs) == 0 { + if len(c.ValidationServerConfigs) == 0 && !streamsEnabled { return fmt.Errorf("block-validator validation-server-configs is empty, need at least one validation server config") } for _, serverConfig := range c.ValidationServerConfigs { @@ -1032,6 +1033,9 @@ func (v *BlockValidator) Reorg(ctx context.Context, count arbutil.MessageIndex) // Initialize must be called after SetCurrentWasmModuleRoot sets the current one func (v *BlockValidator) Initialize(ctx context.Context) error { config := v.config() + if config.RedisValidationClientConfig.Enabled() && v.execSpawner == nil { + return nil + } currentModuleRoot := config.CurrentModuleRoot switch currentModuleRoot { case "latest": diff --git a/staker/stateless_block_validator.go b/staker/stateless_block_validator.go index cfccc793ad..25d64fae35 100644 --- a/staker/stateless_block_validator.go +++ b/staker/stateless_block_validator.go @@ -194,24 +194,20 @@ func NewStatelessBlockValidator( config func() *BlockValidatorConfig, stack *node.Node, ) (*StatelessBlockValidator, error) { - - validationSpawners := make([]validator.ValidationSpawner, len(config().ValidationServerConfigs)) - for i, serverConfig := range config().ValidationServerConfigs { - valConfFetcher := func() *rpcclient.ClientConfig { return &serverConfig } - validationSpawners[i] = server_api.NewValidationClient(valConfFetcher, stack) - } + var validationSpawners []validator.ValidationSpawner redisValClient, err := server_api.NewRedisValidationClient(&config().RedisValidationClientConfig) if err != nil { log.Error("Creating redis validation client", "error", err) } else { validationSpawners = append(validationSpawners, redisValClient) } + for _, serverConfig := range config().ValidationServerConfigs { + valConfFetcher := func() *rpcclient.ClientConfig { return &serverConfig } + validationSpawners = append(validationSpawners, server_api.NewValidationClient(valConfFetcher, stack)) + } - valConfFetcher := func() *rpcclient.ClientConfig { return &config().ValidationServerConfigs[0] } - execClient := server_api.NewExecutionClient(valConfFetcher, stack) validator := &StatelessBlockValidator{ config: config(), - execSpawner: execClient, recorder: recorder, validationSpawners: validationSpawners, inboxReader: inboxReader, @@ -221,6 +217,12 @@ func NewStatelessBlockValidator( daService: das, blobReader: blobReader, } + if len(config().ValidationServerConfigs) != 0 { + valConfFetcher := func() *rpcclient.ClientConfig { + return &config().ValidationServerConfigs[0] + } + validator.execSpawner = server_api.NewExecutionClient(valConfFetcher, stack) + } return validator, nil } @@ -425,15 +427,17 @@ func (v *StatelessBlockValidator) OverrideRecorder(t *testing.T, recorder execut } func (v *StatelessBlockValidator) Start(ctx_in context.Context) error { - err := v.execSpawner.Start(ctx_in) - if err != nil { - return err - } for _, spawner := range v.validationSpawners { if err := spawner.Start(ctx_in); err != nil { return err } } + if v.execSpawner == nil { + return nil + } + if err := v.execSpawner.Start(ctx_in); err != nil { + return err + } if v.config.PendingUpgradeModuleRoot != "" { if v.config.PendingUpgradeModuleRoot == "latest" { latest, err := v.execSpawner.LatestWasmModuleRoot().Await(ctx_in) @@ -453,7 +457,9 @@ func (v *StatelessBlockValidator) Start(ctx_in context.Context) error { } func (v *StatelessBlockValidator) Stop() { - v.execSpawner.Stop() + if v.execSpawner != nil { + v.execSpawner.Stop() + } for _, spawner := range v.validationSpawners { spawner.Stop() } diff --git a/system_tests/block_validator_test.go b/system_tests/block_validator_test.go index 1fcf2bab34..fa2fd238d5 100644 --- a/system_tests/block_validator_test.go +++ b/system_tests/block_validator_test.go @@ -26,6 +26,8 @@ import ( "github.com/offchainlabs/nitro/solgen/go/mocksgen" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" "github.com/offchainlabs/nitro/util/arbmath" + "github.com/offchainlabs/nitro/util/redisutil" + "github.com/offchainlabs/nitro/validator/server_api" ) type workloadType uint @@ -37,7 +39,9 @@ const ( upgradeArbOs ) -func testBlockValidatorSimple(t *testing.T, dasModeString string, workloadLoops int, workload workloadType, arbitrator bool) { +var moduleRoot = "0xe5059c8450e490232bf1ffe02b7cf056349dccea517c8ac7c6d28a0e91ae68cd" + +func testBlockValidatorSimple(t *testing.T, dasModeString string, workloadLoops int, workload workloadType, arbitrator bool, useRedisStreams bool) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -67,7 +71,18 @@ func testBlockValidatorSimple(t *testing.T, dasModeString string, workloadLoops validatorConfig.BlockValidator.Enable = true validatorConfig.DataAvailability = l1NodeConfigA.DataAvailability validatorConfig.DataAvailability.RPCAggregator.Enable = false - AddDefaultValNode(t, ctx, validatorConfig, !arbitrator) + redisURL := "" + if useRedisStreams { + redisURL = redisutil.CreateTestRedis(ctx, t) + validatorConfig.BlockValidator.RedisValidationClientConfig = server_api.DefaultRedisValidationClientConfig + validatorConfig.BlockValidator.RedisValidationClientConfig.ModuleRoots = []string{moduleRoot} + stream := server_api.RedisStreamForRoot(common.HexToHash(moduleRoot)) + validatorConfig.BlockValidator.RedisValidationClientConfig.RedisStream = stream + validatorConfig.BlockValidator.RedisValidationClientConfig.RedisURL = redisURL + } + + AddDefaultValNode(t, ctx, validatorConfig, !arbitrator, redisURL) + testClientB, cleanupB := builder.Build2ndNode(t, &SecondNodeParams{nodeConfig: validatorConfig}) defer cleanupB() builder.L2Info.GenerateAccount("User2") @@ -239,17 +254,21 @@ func testBlockValidatorSimple(t *testing.T, dasModeString string, workloadLoops } func TestBlockValidatorSimpleOnchainUpgradeArbOs(t *testing.T) { - testBlockValidatorSimple(t, "onchain", 1, upgradeArbOs, true) + testBlockValidatorSimple(t, "onchain", 1, upgradeArbOs, true, false) } func TestBlockValidatorSimpleOnchain(t *testing.T) { - testBlockValidatorSimple(t, "onchain", 1, ethSend, true) + testBlockValidatorSimple(t, "onchain", 1, ethSend, true, false) +} + +func TestBlockValidatorSimpleOnchainWithRedisStreams(t *testing.T) { + testBlockValidatorSimple(t, "onchain", 1, ethSend, true, true) } func TestBlockValidatorSimpleLocalDAS(t *testing.T) { - testBlockValidatorSimple(t, "files", 1, ethSend, true) + testBlockValidatorSimple(t, "files", 1, ethSend, true, false) } func TestBlockValidatorSimpleJITOnchain(t *testing.T) { - testBlockValidatorSimple(t, "files", 8, smallContract, false) + testBlockValidatorSimple(t, "files", 8, smallContract, false, false) } diff --git a/system_tests/common_test.go b/system_tests/common_test.go index cd65cd2edc..6008f57ed2 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -16,6 +16,7 @@ import ( "testing" "time" + "github.com/go-redis/redis/v8" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbos/util" "github.com/offchainlabs/nitro/arbstate" @@ -27,8 +28,10 @@ import ( "github.com/offchainlabs/nitro/execution/gethexec" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/headerreader" + "github.com/offchainlabs/nitro/util/redisutil" "github.com/offchainlabs/nitro/util/signature" "github.com/offchainlabs/nitro/validator/server_api" + "github.com/offchainlabs/nitro/validator/server_api/validation" "github.com/offchainlabs/nitro/validator/server_common" "github.com/offchainlabs/nitro/validator/valnode" @@ -504,6 +507,24 @@ func createStackConfigForTest(dataDir string) *node.Config { return &stackConf } +func createGroup(ctx context.Context, t *testing.T, streamName string, client redis.UniversalClient) { + t.Helper() + // Stream name and group name are the same. + if _, err := client.XGroupCreateMkStream(ctx, streamName, streamName, "$").Result(); err != nil { + log.Debug("Error creating stream group: %v", err) + } +} + +func destroyGroup(ctx context.Context, t *testing.T, streamName string, client redis.UniversalClient) { + t.Helper() + if client == nil { + return + } + if _, err := client.XGroupDestroy(ctx, streamName, streamName).Result(); err != nil { + log.Debug("Error destroying a stream group", "error", err) + } +} + func createTestValidationNode(t *testing.T, ctx context.Context, config *valnode.Config) (*valnode.ValidationNode, *node.Node) { stackConf := node.DefaultConfig stackConf.HTTPPort = 0 @@ -556,19 +577,35 @@ func StaticFetcherFrom[T any](t *testing.T, config *T) func() *T { } func configByValidationNode(t *testing.T, clientConfig *arbnode.Config, valStack *node.Node) { + if len(clientConfig.BlockValidator.ValidationServerConfigs) == 0 { + return + } clientConfig.BlockValidator.ValidationServerConfigs[0].URL = valStack.WSEndpoint() clientConfig.BlockValidator.ValidationServerConfigs[0].JWTSecret = "" } -func AddDefaultValNode(t *testing.T, ctx context.Context, nodeConfig *arbnode.Config, useJit bool) { +func AddDefaultValNode(t *testing.T, ctx context.Context, nodeConfig *arbnode.Config, useJit bool, redisURL string) { if !nodeConfig.ValidatorRequired() { return } - if nodeConfig.BlockValidator.ValidationServerConfigs[0].URL != "" { + if len(nodeConfig.BlockValidator.ValidationServerConfigs) > 0 && nodeConfig.BlockValidator.ValidationServerConfigs[0].URL != "" { return } conf := valnode.TestValidationConfig conf.UseJit = useJit + // Enable redis streams when URL is specified + if redisURL != "" { + conf.Arbitrator.RedisValidationServerConfig = validation.DefaultRedisValidationServerConfig + redisStream := server_api.RedisStreamForRoot(common.HexToHash(moduleRoot)) + redisClient, err := redisutil.RedisClientFromURL(redisURL) + if err != nil { + t.Fatalf("Error creating redis coordinator: %v", err) + } + createGroup(ctx, t, redisStream, redisClient) + conf.Arbitrator.RedisValidationServerConfig.RedisURL = redisURL + conf.Arbitrator.RedisValidationServerConfig.ModuleRoots = []string{moduleRoot} + t.Cleanup(func() { destroyGroup(ctx, t, redisStream, redisClient) }) + } _, valStack := createTestValidationNode(t, ctx, &conf) configByValidationNode(t, nodeConfig, valStack) } @@ -798,7 +835,7 @@ func createTestNodeWithL1( execConfig.Sequencer.Enable = false } - AddDefaultValNode(t, ctx, nodeConfig, true) + AddDefaultValNode(t, ctx, nodeConfig, true, "") Require(t, execConfig.Validate()) execConfigFetcher := func() *gethexec.Config { return execConfig } @@ -833,7 +870,7 @@ func createTestNode( feedErrChan := make(chan error, 10) - AddDefaultValNode(t, ctx, nodeConfig, true) + AddDefaultValNode(t, ctx, nodeConfig, true, "") l2info, stack, chainDb, arbDb, blockchain := createL2BlockChain(t, l2Info, "", chainConfig, &execConfig.Caching) @@ -939,7 +976,7 @@ func Create2ndNodeWithConfig( l2blockchain, err := gethexec.WriteOrTestBlockChain(l2chainDb, coreCacheConfig, initReader, chainConfig, initMessage, gethexec.ConfigDefaultTest().TxLookupLimit, 0) Require(t, err) - AddDefaultValNode(t, ctx, nodeConfig, true) + AddDefaultValNode(t, ctx, nodeConfig, true, "") Require(t, execConfig.Validate()) Require(t, nodeConfig.Validate()) diff --git a/validator/server_api/redisconsumer.go b/validator/server_api/redisconsumer.go index bc40d19d73..45ae842287 100644 --- a/validator/server_api/redisconsumer.go +++ b/validator/server_api/redisconsumer.go @@ -24,7 +24,7 @@ type RedisValidationServer struct { consumers map[common.Hash]*pubsub.Consumer[*validator.ValidationInput, validator.GoGlobalState] } -func NewRedisValidationServer(cfg *validation.RedisValidationServerConfig) (*RedisValidationServer, error) { +func NewRedisValidationServer(cfg *validation.RedisValidationServerConfig, spawner validator.ValidationSpawner) (*RedisValidationServer, error) { if cfg.RedisURL == "" { return nil, fmt.Errorf("redis url cannot be empty") } @@ -35,7 +35,7 @@ func NewRedisValidationServer(cfg *validation.RedisValidationServerConfig) (*Red consumers := make(map[common.Hash]*pubsub.Consumer[*validator.ValidationInput, validator.GoGlobalState]) for _, hash := range cfg.ModuleRoots { mr := common.HexToHash(hash) - c, err := pubsub.NewConsumer[*validator.ValidationInput, validator.GoGlobalState](redisClient, redisStreamForRoot(mr), &cfg.ConsumerConfig) + c, err := pubsub.NewConsumer[*validator.ValidationInput, validator.GoGlobalState](redisClient, RedisStreamForRoot(mr), &cfg.ConsumerConfig) if err != nil { return nil, fmt.Errorf("creating consumer for validation: %w", err) } @@ -43,6 +43,7 @@ func NewRedisValidationServer(cfg *validation.RedisValidationServerConfig) (*Red } return &RedisValidationServer{ consumers: consumers, + spawner: spawner, }, nil } @@ -57,6 +58,10 @@ func (s *RedisValidationServer) Start(ctx_in context.Context) { log.Error("Consuming request", "error", err) return 0 } + if req == nil { + // There's nothing in the queue. + return time.Second + } valRun := s.spawner.Launch(req.Value, moduleRoot) res, err := valRun.Await(ctx) if err != nil { diff --git a/validator/server_api/redisproducer.go b/validator/server_api/redisproducer.go index 5540cd169f..99c9bcce9a 100644 --- a/validator/server_api/redisproducer.go +++ b/validator/server_api/redisproducer.go @@ -21,10 +21,14 @@ type RedisValidationClientConfig struct { RedisURL string `koanf:"redis-url"` RedisStream string `koanf:"redis-stream"` ProducerConfig pubsub.ProducerConfig `koanf:"producer-config"` - // Supported wasm module roots. + // Supported wasm module roots, when the list is empty this is disabled. ModuleRoots []string `koanf:"module-roots"` } +func (c RedisValidationClientConfig) Enabled() bool { + return len(c.ModuleRoots) > 0 +} + var DefaultRedisValidationClientConfig = RedisValidationClientConfig{ Name: "redis validation client", Room: 2, @@ -58,7 +62,7 @@ type RedisValidationClient struct { producers map[common.Hash]*pubsub.Producer[*validator.ValidationInput, validator.GoGlobalState] } -func redisStreamForRoot(moduleRoot common.Hash) string { +func RedisStreamForRoot(moduleRoot common.Hash) string { return fmt.Sprintf("stream:%s", moduleRoot.Hex()) } @@ -75,10 +79,13 @@ func NewRedisValidationClient(cfg *RedisValidationClientConfig) (*RedisValidatio if err != nil { return nil, err } + if len(cfg.ModuleRoots) == 0 { + return nil, fmt.Errorf("moduleRoots must be specified to enable redis streams") + } for _, hash := range cfg.ModuleRoots { mr := common.HexToHash(hash) p, err := pubsub.NewProducer[*validator.ValidationInput, validator.GoGlobalState]( - redisClient, redisStreamForRoot(mr), &cfg.ProducerConfig) + redisClient, RedisStreamForRoot(mr), &cfg.ProducerConfig) if err != nil { return nil, fmt.Errorf("creating producer for validation: %w", err) } diff --git a/validator/server_api/validation/validation.go b/validator/server_api/validation/validation.go index 9cab29bde7..08d92085d6 100644 --- a/validator/server_api/validation/validation.go +++ b/validator/server_api/validation/validation.go @@ -33,7 +33,6 @@ type BatchInfoJson struct { type RedisValidationServerConfig struct { RedisURL string `koanf:"redis-url"` - RedisStream string `koanf:"redis-stream"` ConsumerConfig pubsub.ConsumerConfig `koanf:"consumer-config"` // Supported wasm module roots. ModuleRoots []string `koanf:"module-roots"` @@ -41,14 +40,12 @@ type RedisValidationServerConfig struct { var DefaultRedisValidationServerConfig = RedisValidationServerConfig{ RedisURL: "", - RedisStream: "", ConsumerConfig: pubsub.DefaultConsumerConfig, ModuleRoots: []string{}, } var TestRedisValidationServerConfig = RedisValidationServerConfig{ RedisURL: "", - RedisStream: "", ConsumerConfig: pubsub.TestConsumerConfig, ModuleRoots: []string{}, } diff --git a/validator/server_api/validation_client.go b/validator/server_api/validation_client.go index d6143ca917..0148eac0dd 100644 --- a/validator/server_api/validation_client.go +++ b/validator/server_api/validation_client.go @@ -48,21 +48,20 @@ func (c *ValidationClient) Launch(entry *validator.ValidationInput, moduleRoot c func (c *ValidationClient) Start(ctx_in context.Context) error { c.StopWaiter.Start(ctx_in, c) ctx := c.GetContext() - err := c.client.Start(ctx) - if err != nil { - return err + if c.client != nil { + if err := c.client.Start(ctx); err != nil { + return err + } } var name string - err = c.client.CallContext(ctx, &name, Namespace+"_name") - if err != nil { + if err := c.client.CallContext(ctx, &name, Namespace+"_name"); err != nil { return err } if len(name) == 0 { return errors.New("couldn't read name from server") } var room int - err = c.client.CallContext(c.GetContext(), &room, Namespace+"_room") - if err != nil { + if err := c.client.CallContext(c.GetContext(), &room, Namespace+"_room"); err != nil { return err } if room < 2 { diff --git a/validator/valnode/valnode.go b/validator/valnode/valnode.go index 5b4986f9da..e42acd8ae4 100644 --- a/validator/valnode/valnode.go +++ b/validator/valnode/valnode.go @@ -119,7 +119,7 @@ func CreateValidationNode(configFetcher ValidationConfigFetcher, stack *node.Nod } else { serverAPI = server_api.NewExecutionServerAPI(arbSpawner, arbSpawner, arbConfigFetcher) } - redisConsumer, err := server_api.NewRedisValidationServer(&arbConfigFetcher().RedisValidationServerConfig) + redisConsumer, err := server_api.NewRedisValidationServer(&arbConfigFetcher().RedisValidationServerConfig, arbSpawner) if err != nil { log.Error("Creating new redis validation server", "error", err) } From db855d5d5cf5918d00f42472786c2bd39072afb7 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Fri, 19 Apr 2024 22:58:47 +0200 Subject: [PATCH 13/41] Move moduleRoot to common_test since block_validator_test isn't compiled in race mode --- system_tests/block_validator_test.go | 9 +++++---- system_tests/common_test.go | 6 ++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/system_tests/block_validator_test.go b/system_tests/block_validator_test.go index fa2fd238d5..ebc9ec9b92 100644 --- a/system_tests/block_validator_test.go +++ b/system_tests/block_validator_test.go @@ -39,8 +39,6 @@ const ( upgradeArbOs ) -var moduleRoot = "0xe5059c8450e490232bf1ffe02b7cf056349dccea517c8ac7c6d28a0e91ae68cd" - func testBlockValidatorSimple(t *testing.T, dasModeString string, workloadLoops int, workload workloadType, arbitrator bool, useRedisStreams bool) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) @@ -75,8 +73,8 @@ func testBlockValidatorSimple(t *testing.T, dasModeString string, workloadLoops if useRedisStreams { redisURL = redisutil.CreateTestRedis(ctx, t) validatorConfig.BlockValidator.RedisValidationClientConfig = server_api.DefaultRedisValidationClientConfig - validatorConfig.BlockValidator.RedisValidationClientConfig.ModuleRoots = []string{moduleRoot} - stream := server_api.RedisStreamForRoot(common.HexToHash(moduleRoot)) + validatorConfig.BlockValidator.RedisValidationClientConfig.ModuleRoots = []string{wasmModuleRoot} + stream := server_api.RedisStreamForRoot(common.HexToHash(wasmModuleRoot)) validatorConfig.BlockValidator.RedisValidationClientConfig.RedisStream = stream validatorConfig.BlockValidator.RedisValidationClientConfig.RedisURL = redisURL } @@ -84,6 +82,9 @@ func testBlockValidatorSimple(t *testing.T, dasModeString string, workloadLoops AddDefaultValNode(t, ctx, validatorConfig, !arbitrator, redisURL) testClientB, cleanupB := builder.Build2ndNode(t, &SecondNodeParams{nodeConfig: validatorConfig}) + if useRedisStreams { + testClientB.ConsensusNode.BlockValidator.SetCurrentWasmModuleRoot(common.HexToHash(wasmModuleRoot)) + } defer cleanupB() builder.L2Info.GenerateAccount("User2") diff --git a/system_tests/common_test.go b/system_tests/common_test.go index 6008f57ed2..ac33043917 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -72,6 +72,8 @@ import ( type info = *BlockchainTestInfo type client = arbutil.L1Interface +const wasmModuleRoot = "0xe5059c8450e490232bf1ffe02b7cf056349dccea517c8ac7c6d28a0e91ae68cd" + type SecondNodeParams struct { nodeConfig *arbnode.Config execConfig *gethexec.Config @@ -596,14 +598,14 @@ func AddDefaultValNode(t *testing.T, ctx context.Context, nodeConfig *arbnode.Co // Enable redis streams when URL is specified if redisURL != "" { conf.Arbitrator.RedisValidationServerConfig = validation.DefaultRedisValidationServerConfig - redisStream := server_api.RedisStreamForRoot(common.HexToHash(moduleRoot)) + redisStream := server_api.RedisStreamForRoot(common.HexToHash(wasmModuleRoot)) redisClient, err := redisutil.RedisClientFromURL(redisURL) if err != nil { t.Fatalf("Error creating redis coordinator: %v", err) } createGroup(ctx, t, redisStream, redisClient) conf.Arbitrator.RedisValidationServerConfig.RedisURL = redisURL - conf.Arbitrator.RedisValidationServerConfig.ModuleRoots = []string{moduleRoot} + conf.Arbitrator.RedisValidationServerConfig.ModuleRoots = []string{wasmModuleRoot} t.Cleanup(func() { destroyGroup(ctx, t, redisStream, redisClient) }) } _, valStack := createTestValidationNode(t, ctx, &conf) From 2f9cc14470caaf95d145326141e998ab7477fc01 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Fri, 19 Apr 2024 23:13:44 +0200 Subject: [PATCH 14/41] Fix linter error --- system_tests/block_validator_test.go | 4 +++- system_tests/common_test.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/system_tests/block_validator_test.go b/system_tests/block_validator_test.go index ebc9ec9b92..79eb735bec 100644 --- a/system_tests/block_validator_test.go +++ b/system_tests/block_validator_test.go @@ -83,7 +83,9 @@ func testBlockValidatorSimple(t *testing.T, dasModeString string, workloadLoops testClientB, cleanupB := builder.Build2ndNode(t, &SecondNodeParams{nodeConfig: validatorConfig}) if useRedisStreams { - testClientB.ConsensusNode.BlockValidator.SetCurrentWasmModuleRoot(common.HexToHash(wasmModuleRoot)) + if err := testClientB.ConsensusNode.BlockValidator.SetCurrentWasmModuleRoot(common.HexToHash(wasmModuleRoot)); err != nil { + t.Fatalf("Error setting wasm module root: %v", err) + } } defer cleanupB() builder.L2Info.GenerateAccount("User2") diff --git a/system_tests/common_test.go b/system_tests/common_test.go index ac33043917..e279337007 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -72,7 +72,7 @@ import ( type info = *BlockchainTestInfo type client = arbutil.L1Interface -const wasmModuleRoot = "0xe5059c8450e490232bf1ffe02b7cf056349dccea517c8ac7c6d28a0e91ae68cd" +const wasmModuleRoot = "0x0e5403827cef82bcbb6f4ba1b6f3d84edc5b4b8991b164f623ff2eacda768e35" type SecondNodeParams struct { nodeConfig *arbnode.Config From b990e16e6b6cedfc1fd96120d2c071a1d736f5d1 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Sat, 20 Apr 2024 00:17:38 +0200 Subject: [PATCH 15/41] Drop RedisStream from RedisValidationClientConfig, it's dereived from module roots --- system_tests/block_validator_test.go | 10 ++-------- system_tests/common_test.go | 16 ++++++++++------ validator/server_api/redisconsumer.go | 2 +- validator/server_api/redisproducer.go | 4 ---- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/system_tests/block_validator_test.go b/system_tests/block_validator_test.go index 79eb735bec..b4b6e0134e 100644 --- a/system_tests/block_validator_test.go +++ b/system_tests/block_validator_test.go @@ -73,20 +73,14 @@ func testBlockValidatorSimple(t *testing.T, dasModeString string, workloadLoops if useRedisStreams { redisURL = redisutil.CreateTestRedis(ctx, t) validatorConfig.BlockValidator.RedisValidationClientConfig = server_api.DefaultRedisValidationClientConfig - validatorConfig.BlockValidator.RedisValidationClientConfig.ModuleRoots = []string{wasmModuleRoot} - stream := server_api.RedisStreamForRoot(common.HexToHash(wasmModuleRoot)) - validatorConfig.BlockValidator.RedisValidationClientConfig.RedisStream = stream + validatorConfig.BlockValidator.RedisValidationClientConfig.ModuleRoots = wasmModuleRoots validatorConfig.BlockValidator.RedisValidationClientConfig.RedisURL = redisURL + validatorConfig.BlockValidator.PendingUpgradeModuleRoot = wasmModuleRoots[0] } AddDefaultValNode(t, ctx, validatorConfig, !arbitrator, redisURL) testClientB, cleanupB := builder.Build2ndNode(t, &SecondNodeParams{nodeConfig: validatorConfig}) - if useRedisStreams { - if err := testClientB.ConsensusNode.BlockValidator.SetCurrentWasmModuleRoot(common.HexToHash(wasmModuleRoot)); err != nil { - t.Fatalf("Error setting wasm module root: %v", err) - } - } defer cleanupB() builder.L2Info.GenerateAccount("User2") diff --git a/system_tests/common_test.go b/system_tests/common_test.go index e279337007..a6db78a6c6 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -72,7 +72,9 @@ import ( type info = *BlockchainTestInfo type client = arbutil.L1Interface -const wasmModuleRoot = "0x0e5403827cef82bcbb6f4ba1b6f3d84edc5b4b8991b164f623ff2eacda768e35" +var wasmModuleRoots = []string{ + "0x0e5403827cef82bcbb6f4ba1b6f3d84edc5b4b8991b164f623ff2eacda768e35", +} type SecondNodeParams struct { nodeConfig *arbnode.Config @@ -598,15 +600,17 @@ func AddDefaultValNode(t *testing.T, ctx context.Context, nodeConfig *arbnode.Co // Enable redis streams when URL is specified if redisURL != "" { conf.Arbitrator.RedisValidationServerConfig = validation.DefaultRedisValidationServerConfig - redisStream := server_api.RedisStreamForRoot(common.HexToHash(wasmModuleRoot)) redisClient, err := redisutil.RedisClientFromURL(redisURL) if err != nil { t.Fatalf("Error creating redis coordinator: %v", err) } - createGroup(ctx, t, redisStream, redisClient) - conf.Arbitrator.RedisValidationServerConfig.RedisURL = redisURL - conf.Arbitrator.RedisValidationServerConfig.ModuleRoots = []string{wasmModuleRoot} - t.Cleanup(func() { destroyGroup(ctx, t, redisStream, redisClient) }) + for _, rootModule := range wasmModuleRoots { + redisStream := server_api.RedisStreamForRoot(common.HexToHash(rootModule)) + createGroup(ctx, t, redisStream, redisClient) + conf.Arbitrator.RedisValidationServerConfig.RedisURL = redisURL + t.Cleanup(func() { destroyGroup(ctx, t, redisStream, redisClient) }) + } + conf.Arbitrator.RedisValidationServerConfig.ModuleRoots = wasmModuleRoots } _, valStack := createTestValidationNode(t, ctx, &conf) configByValidationNode(t, nodeConfig, valStack) diff --git a/validator/server_api/redisconsumer.go b/validator/server_api/redisconsumer.go index 45ae842287..d87914380e 100644 --- a/validator/server_api/redisconsumer.go +++ b/validator/server_api/redisconsumer.go @@ -65,7 +65,7 @@ func (s *RedisValidationServer) Start(ctx_in context.Context) { valRun := s.spawner.Launch(req.Value, moduleRoot) res, err := valRun.Await(ctx) if err != nil { - log.Error("Error validating", "input", "request value", req.Value, "error", err) + log.Error("Error validating", "request value", req.Value, "error", err) return 0 } if err := c.SetResult(ctx, req.ID, res); err != nil { diff --git a/validator/server_api/redisproducer.go b/validator/server_api/redisproducer.go index 99c9bcce9a..cafef7e778 100644 --- a/validator/server_api/redisproducer.go +++ b/validator/server_api/redisproducer.go @@ -19,7 +19,6 @@ type RedisValidationClientConfig struct { Name string `koanf:"name"` Room int32 `koanf:"room"` RedisURL string `koanf:"redis-url"` - RedisStream string `koanf:"redis-stream"` ProducerConfig pubsub.ProducerConfig `koanf:"producer-config"` // Supported wasm module roots, when the list is empty this is disabled. ModuleRoots []string `koanf:"module-roots"` @@ -33,7 +32,6 @@ var DefaultRedisValidationClientConfig = RedisValidationClientConfig{ Name: "redis validation client", Room: 2, RedisURL: "", - RedisStream: "", ProducerConfig: pubsub.DefaultProducerConfig, } @@ -41,14 +39,12 @@ var TestRedisValidationClientConfig = RedisValidationClientConfig{ Name: "test redis validation client", Room: 2, RedisURL: "", - RedisStream: "", ProducerConfig: pubsub.TestProducerConfig, } func RedisValidationClientConfigAddOptions(prefix string, f *pflag.FlagSet) { f.String(prefix+".name", DefaultRedisValidationClientConfig.Name, "validation client name") f.Int32(prefix+".room", DefaultRedisValidationClientConfig.Room, "validation client room") - f.String(prefix+".redis-stream", DefaultRedisValidationClientConfig.RedisStream, "redis stream name") pubsub.ProducerAddConfigAddOptions(prefix+".producer-config", f) f.StringSlice(prefix+".module-roots", nil, "Supported module root hashes") } From 5b98d6f290f7a487051601f988c4aa5a64b7d650 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Sat, 20 Apr 2024 00:40:22 +0200 Subject: [PATCH 16/41] Error out when currentModuleRoot is latest and execSpanner isn't initialized --- staker/block_validator.go | 7 ++++--- system_tests/block_validator_test.go | 2 +- system_tests/common_test.go | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/staker/block_validator.go b/staker/block_validator.go index 1ec160c558..5cff19ba31 100644 --- a/staker/block_validator.go +++ b/staker/block_validator.go @@ -1033,12 +1033,13 @@ func (v *BlockValidator) Reorg(ctx context.Context, count arbutil.MessageIndex) // Initialize must be called after SetCurrentWasmModuleRoot sets the current one func (v *BlockValidator) Initialize(ctx context.Context) error { config := v.config() - if config.RedisValidationClientConfig.Enabled() && v.execSpawner == nil { - return nil - } + currentModuleRoot := config.CurrentModuleRoot switch currentModuleRoot { case "latest": + if v.execSpawner == nil { + return fmt.Errorf(`execution spawner is nil while current module root is "latest"`) + } latest, err := v.execSpawner.LatestWasmModuleRoot().Await(ctx) if err != nil { return err diff --git a/system_tests/block_validator_test.go b/system_tests/block_validator_test.go index b4b6e0134e..b472ec2a3c 100644 --- a/system_tests/block_validator_test.go +++ b/system_tests/block_validator_test.go @@ -75,7 +75,7 @@ func testBlockValidatorSimple(t *testing.T, dasModeString string, workloadLoops validatorConfig.BlockValidator.RedisValidationClientConfig = server_api.DefaultRedisValidationClientConfig validatorConfig.BlockValidator.RedisValidationClientConfig.ModuleRoots = wasmModuleRoots validatorConfig.BlockValidator.RedisValidationClientConfig.RedisURL = redisURL - validatorConfig.BlockValidator.PendingUpgradeModuleRoot = wasmModuleRoots[0] + validatorConfig.BlockValidator.CurrentModuleRoot = wasmModuleRoots[0] } AddDefaultValNode(t, ctx, validatorConfig, !arbitrator, redisURL) diff --git a/system_tests/common_test.go b/system_tests/common_test.go index a6db78a6c6..91b08fdeaf 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -73,7 +73,8 @@ type info = *BlockchainTestInfo type client = arbutil.L1Interface var wasmModuleRoots = []string{ - "0x0e5403827cef82bcbb6f4ba1b6f3d84edc5b4b8991b164f623ff2eacda768e35", + "0xb1e1f56cdcb7453d9416e9b242ded14aa4324674f1173e86fec9b85e923284e7", + // "0x0e5403827cef82bcbb6f4ba1b6f3d84edc5b4b8991b164f623ff2eacda768e35", } type SecondNodeParams struct { From fb897bbe70e869a971c8d6da2ca9bd2befb302ce Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Sat, 20 Apr 2024 01:22:45 +0200 Subject: [PATCH 17/41] Set rootModule dynamically --- system_tests/block_validator_test.go | 3 +-- system_tests/common_test.go | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/system_tests/block_validator_test.go b/system_tests/block_validator_test.go index b472ec2a3c..68fcaa5ba4 100644 --- a/system_tests/block_validator_test.go +++ b/system_tests/block_validator_test.go @@ -73,9 +73,8 @@ func testBlockValidatorSimple(t *testing.T, dasModeString string, workloadLoops if useRedisStreams { redisURL = redisutil.CreateTestRedis(ctx, t) validatorConfig.BlockValidator.RedisValidationClientConfig = server_api.DefaultRedisValidationClientConfig - validatorConfig.BlockValidator.RedisValidationClientConfig.ModuleRoots = wasmModuleRoots + validatorConfig.BlockValidator.RedisValidationClientConfig.ModuleRoots = []string{currentRootModule(t).Hex()} validatorConfig.BlockValidator.RedisValidationClientConfig.RedisURL = redisURL - validatorConfig.BlockValidator.CurrentModuleRoot = wasmModuleRoots[0] } AddDefaultValNode(t, ctx, validatorConfig, !arbitrator, redisURL) diff --git a/system_tests/common_test.go b/system_tests/common_test.go index 91b08fdeaf..fb82ca5fa2 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -72,11 +72,6 @@ import ( type info = *BlockchainTestInfo type client = arbutil.L1Interface -var wasmModuleRoots = []string{ - "0xb1e1f56cdcb7453d9416e9b242ded14aa4324674f1173e86fec9b85e923284e7", - // "0x0e5403827cef82bcbb6f4ba1b6f3d84edc5b4b8991b164f623ff2eacda768e35", -} - type SecondNodeParams struct { nodeConfig *arbnode.Config execConfig *gethexec.Config @@ -589,6 +584,15 @@ func configByValidationNode(t *testing.T, clientConfig *arbnode.Config, valStack clientConfig.BlockValidator.ValidationServerConfigs[0].JWTSecret = "" } +func currentRootModule(t *testing.T) common.Hash { + t.Helper() + locator, err := server_common.NewMachineLocator("") + if err != nil { + t.Fatalf("Error creating machine locator: %v", err) + } + return locator.LatestWasmModuleRoot() +} + func AddDefaultValNode(t *testing.T, ctx context.Context, nodeConfig *arbnode.Config, useJit bool, redisURL string) { if !nodeConfig.ValidatorRequired() { return @@ -605,13 +609,11 @@ func AddDefaultValNode(t *testing.T, ctx context.Context, nodeConfig *arbnode.Co if err != nil { t.Fatalf("Error creating redis coordinator: %v", err) } - for _, rootModule := range wasmModuleRoots { - redisStream := server_api.RedisStreamForRoot(common.HexToHash(rootModule)) - createGroup(ctx, t, redisStream, redisClient) - conf.Arbitrator.RedisValidationServerConfig.RedisURL = redisURL - t.Cleanup(func() { destroyGroup(ctx, t, redisStream, redisClient) }) - } - conf.Arbitrator.RedisValidationServerConfig.ModuleRoots = wasmModuleRoots + redisStream := server_api.RedisStreamForRoot(currentRootModule(t)) + createGroup(ctx, t, redisStream, redisClient) + conf.Arbitrator.RedisValidationServerConfig.RedisURL = redisURL + t.Cleanup(func() { destroyGroup(ctx, t, redisStream, redisClient) }) + conf.Arbitrator.RedisValidationServerConfig.ModuleRoots = []string{currentRootModule(t).Hex()} } _, valStack := createTestValidationNode(t, ctx, &conf) configByValidationNode(t, nodeConfig, valStack) From 738f04dcb70018fe748e69e4de84447cad340c62 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Sat, 20 Apr 2024 10:21:17 +0200 Subject: [PATCH 18/41] Introduce ExecutionServerConfig to BlockValidatorConfig, restructure validator packages --- pubsub/consumer.go | 7 -- pubsub/producer.go | 9 -- pubsub/pubsub_test.go | 8 +- staker/block_validator.go | 46 ++++---- staker/stateless_block_validator.go | 33 +++--- system_tests/block_validator_test.go | 6 +- system_tests/common_test.go | 25 ++--- system_tests/full_challenge_impl_test.go | 2 +- system_tests/validation_mock_test.go | 13 ++- .../{server_api => client}/redisproducer.go | 9 +- .../validation_client.go | 51 ++++++--- validator/server_api/json.go | 104 +++++++++--------- validator/server_api/validation/validation.go | 56 ---------- validator/server_arb/validator_spawner.go | 8 +- .../{server_api => valnode}/redisconsumer.go | 8 +- .../{server_api => valnode}/validation_api.go | 48 ++++++-- validator/valnode/valnode.go | 10 +- 17 files changed, 208 insertions(+), 235 deletions(-) rename validator/{server_api => client}/redisproducer.go (95%) rename validator/{server_api => client}/validation_client.go (72%) delete mode 100644 validator/server_api/validation/validation.go rename validator/{server_api => valnode}/redisconsumer.go (90%) rename validator/{server_api => valnode}/validation_api.go (76%) diff --git a/pubsub/consumer.go b/pubsub/consumer.go index 5385b33979..7a5078ee00 100644 --- a/pubsub/consumer.go +++ b/pubsub/consumer.go @@ -22,13 +22,6 @@ type ConsumerConfig struct { KeepAliveTimeout time.Duration `koanf:"keepalive-timeout"` } -func (c ConsumerConfig) Clone() ConsumerConfig { - return ConsumerConfig{ - ResponseEntryTimeout: c.ResponseEntryTimeout, - KeepAliveTimeout: c.KeepAliveTimeout, - } -} - var DefaultConsumerConfig = ConsumerConfig{ ResponseEntryTimeout: time.Hour, KeepAliveTimeout: 5 * time.Minute, diff --git a/pubsub/producer.go b/pubsub/producer.go index debea81365..b00eec7f64 100644 --- a/pubsub/producer.go +++ b/pubsub/producer.go @@ -61,15 +61,6 @@ type ProducerConfig struct { CheckResultInterval time.Duration `koanf:"check-result-interval"` } -func (c ProducerConfig) Clone() ProducerConfig { - return ProducerConfig{ - EnableReproduce: c.EnableReproduce, - CheckPendingInterval: c.CheckPendingInterval, - KeepAliveTimeout: c.KeepAliveTimeout, - CheckResultInterval: c.CheckResultInterval, - } -} - var DefaultProducerConfig = ProducerConfig{ EnableReproduce: true, CheckPendingInterval: time.Second, diff --git a/pubsub/pubsub_test.go b/pubsub/pubsub_test.go index 949e532343..31f6d9e20a 100644 --- a/pubsub/pubsub_test.go +++ b/pubsub/pubsub_test.go @@ -28,7 +28,7 @@ type testResponse struct { Response string } -func createGroup(ctx context.Context, t *testing.T, streamName string, client redis.UniversalClient) { +func createRedisGroup(ctx context.Context, t *testing.T, streamName string, client redis.UniversalClient) { t.Helper() // Stream name and group name are the same. if _, err := client.XGroupCreateMkStream(ctx, streamName, streamName, "$").Result(); err != nil { @@ -36,7 +36,7 @@ func createGroup(ctx context.Context, t *testing.T, streamName string, client re } } -func destroyGroup(ctx context.Context, t *testing.T, streamName string, client redis.UniversalClient) { +func destroyRedisGroup(ctx context.Context, t *testing.T, streamName string, client redis.UniversalClient) { t.Helper() if _, err := client.XGroupDestroy(ctx, streamName, streamName).Result(); err != nil { log.Debug("Error destroying a stream group", "error", err) @@ -93,10 +93,10 @@ func newProducerConsumers(ctx context.Context, t *testing.T, opts ...configOpt) } consumers = append(consumers, c) } - createGroup(ctx, t, streamName, producer.client) + createRedisGroup(ctx, t, streamName, producer.client) t.Cleanup(func() { ctx := context.Background() - destroyGroup(ctx, t, streamName, producer.client) + destroyRedisGroup(ctx, t, streamName, producer.client) var keys []string for _, c := range consumers { keys = append(keys, c.heartBeatKey()) diff --git a/staker/block_validator.go b/staker/block_validator.go index 5cff19ba31..cd89ccf650 100644 --- a/staker/block_validator.go +++ b/staker/block_validator.go @@ -14,8 +14,6 @@ import ( "testing" "time" - flag "github.com/spf13/pflag" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" @@ -27,7 +25,9 @@ import ( "github.com/offchainlabs/nitro/util/rpcclient" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" - "github.com/offchainlabs/nitro/validator/server_api" + "github.com/spf13/pflag" + + validatorclient "github.com/offchainlabs/nitro/validator/client" ) var ( @@ -84,19 +84,20 @@ type BlockValidator struct { } type BlockValidatorConfig struct { - Enable bool `koanf:"enable"` - ValidationServer rpcclient.ClientConfig `koanf:"validation-server" reload:"hot"` - RedisValidationClientConfig server_api.RedisValidationClientConfig `koanf:"redis-validation-client-config"` - ValidationServerConfigs []rpcclient.ClientConfig `koanf:"validation-server-configs" reload:"hot"` - ValidationPoll time.Duration `koanf:"validation-poll" reload:"hot"` - PrerecordedBlocks uint64 `koanf:"prerecorded-blocks" reload:"hot"` - ForwardBlocks uint64 `koanf:"forward-blocks" reload:"hot"` - CurrentModuleRoot string `koanf:"current-module-root"` // TODO(magic) requires reinitialization on hot reload - PendingUpgradeModuleRoot string `koanf:"pending-upgrade-module-root"` // TODO(magic) requires StatelessBlockValidator recreation on hot reload - FailureIsFatal bool `koanf:"failure-is-fatal" reload:"hot"` - Dangerous BlockValidatorDangerousConfig `koanf:"dangerous"` - MemoryFreeLimit string `koanf:"memory-free-limit" reload:"hot"` - ValidationServerConfigsList string `koanf:"validation-server-configs-list" reload:"hot"` + Enable bool `koanf:"enable"` + ValidationServer rpcclient.ClientConfig `koanf:"validation-server" reload:"hot"` + RedisValidationClientConfig validatorclient.RedisValidationClientConfig `koanf:"redis-validation-client-config"` + ValidationServerConfigs []rpcclient.ClientConfig `koanf:"validation-server-configs" reload:"hot"` + ExecutionServerConfig rpcclient.ClientConfig `koanf:"execution-server-config" reload:"hot"` + ValidationPoll time.Duration `koanf:"validation-poll" reload:"hot"` + PrerecordedBlocks uint64 `koanf:"prerecorded-blocks" reload:"hot"` + ForwardBlocks uint64 `koanf:"forward-blocks" reload:"hot"` + CurrentModuleRoot string `koanf:"current-module-root"` // TODO(magic) requires reinitialization on hot reload + PendingUpgradeModuleRoot string `koanf:"pending-upgrade-module-root"` // TODO(magic) requires StatelessBlockValidator recreation on hot reload + FailureIsFatal bool `koanf:"failure-is-fatal" reload:"hot"` + Dangerous BlockValidatorDangerousConfig `koanf:"dangerous"` + MemoryFreeLimit string `koanf:"memory-free-limit" reload:"hot"` + ValidationServerConfigsList string `koanf:"validation-server-configs-list" reload:"hot"` memoryFreeLimit int } @@ -113,9 +114,8 @@ func (c *BlockValidatorConfig) Validate() error { } streamsEnabled := c.RedisValidationClientConfig.Enabled() if c.ValidationServerConfigs == nil { - if c.ValidationServerConfigsList == "default" { - c.ValidationServerConfigs = []rpcclient.ClientConfig{c.ValidationServer} - } else { + c.ValidationServerConfigs = []rpcclient.ClientConfig{c.ValidationServer} + if c.ValidationServerConfigsList != "default" { var validationServersConfigs []rpcclient.ClientConfig if err := json.Unmarshal([]byte(c.ValidationServerConfigsList), &validationServersConfigs); err != nil && !streamsEnabled { return fmt.Errorf("failed to parse block-validator validation-server-configs-list string: %w", err) @@ -131,6 +131,9 @@ func (c *BlockValidatorConfig) Validate() error { return fmt.Errorf("failed to validate one of the block-validator validation-server-configs. url: %s, err: %w", serverConfig.URL, err) } } + if err := c.ExecutionServerConfig.Validate(); err != nil { + return fmt.Errorf("validating execution server config: %w", err) + } return nil } @@ -140,7 +143,7 @@ type BlockValidatorDangerousConfig struct { type BlockValidatorConfigFetcher func() *BlockValidatorConfig -func BlockValidatorConfigAddOptions(prefix string, f *flag.FlagSet) { +func BlockValidatorConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".enable", DefaultBlockValidatorConfig.Enable, "enable block-by-block validation") rpcclient.RPCClientAddOptions(prefix+".validation-server", f, &DefaultBlockValidatorConfig.ValidationServer) f.String(prefix+".validation-server-configs-list", DefaultBlockValidatorConfig.ValidationServerConfigsList, "array of validation rpc configs given as a json string. time duration should be supplied in number indicating nanoseconds") @@ -154,7 +157,7 @@ func BlockValidatorConfigAddOptions(prefix string, f *flag.FlagSet) { f.String(prefix+".memory-free-limit", DefaultBlockValidatorConfig.MemoryFreeLimit, "minimum free-memory limit after reaching which the blockvalidator pauses validation. Enabled by default as 1GB, to disable provide empty string") } -func BlockValidatorDangerousConfigAddOptions(prefix string, f *flag.FlagSet) { +func BlockValidatorDangerousConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".reset-block-validation", DefaultBlockValidatorDangerousConfig.ResetBlockValidation, "resets block-by-block validation, starting again at genesis") } @@ -176,6 +179,7 @@ var TestBlockValidatorConfig = BlockValidatorConfig{ Enable: false, ValidationServer: rpcclient.TestClientConfig, ValidationServerConfigs: []rpcclient.ClientConfig{rpcclient.TestClientConfig}, + ExecutionServerConfig: rpcclient.TestClientConfig, ValidationPoll: 100 * time.Millisecond, ForwardBlocks: 128, PrerecordedBlocks: uint64(2 * runtime.NumCPU()), diff --git a/staker/stateless_block_validator.go b/staker/stateless_block_validator.go index 25d64fae35..eaa2bfb133 100644 --- a/staker/stateless_block_validator.go +++ b/staker/stateless_block_validator.go @@ -11,19 +11,19 @@ import ( "sync" "testing" - "github.com/offchainlabs/nitro/execution" - "github.com/offchainlabs/nitro/util/rpcclient" - "github.com/offchainlabs/nitro/validator/server_api" - - "github.com/offchainlabs/nitro/arbutil" - "github.com/offchainlabs/nitro/validator" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" + "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbutil" + "github.com/offchainlabs/nitro/execution" + "github.com/offchainlabs/nitro/util/rpcclient" + "github.com/offchainlabs/nitro/validator" + + validatorclient "github.com/offchainlabs/nitro/validator/client" ) type StatelessBlockValidator struct { @@ -195,7 +195,7 @@ func NewStatelessBlockValidator( stack *node.Node, ) (*StatelessBlockValidator, error) { var validationSpawners []validator.ValidationSpawner - redisValClient, err := server_api.NewRedisValidationClient(&config().RedisValidationClientConfig) + redisValClient, err := validatorclient.NewRedisValidationClient(&config().RedisValidationClientConfig) if err != nil { log.Error("Creating redis validation client", "error", err) } else { @@ -203,7 +203,7 @@ func NewStatelessBlockValidator( } for _, serverConfig := range config().ValidationServerConfigs { valConfFetcher := func() *rpcclient.ClientConfig { return &serverConfig } - validationSpawners = append(validationSpawners, server_api.NewValidationClient(valConfFetcher, stack)) + validationSpawners = append(validationSpawners, validatorclient.NewValidationClient(valConfFetcher, stack)) } validator := &StatelessBlockValidator{ @@ -217,12 +217,10 @@ func NewStatelessBlockValidator( daService: das, blobReader: blobReader, } - if len(config().ValidationServerConfigs) != 0 { - valConfFetcher := func() *rpcclient.ClientConfig { - return &config().ValidationServerConfigs[0] - } - validator.execSpawner = server_api.NewExecutionClient(valConfFetcher, stack) + valConfFetcher := func() *rpcclient.ClientConfig { + return &config().ExecutionServerConfig } + validator.execSpawner = validatorclient.NewExecutionClient(valConfFetcher, stack) return validator, nil } @@ -432,9 +430,6 @@ func (v *StatelessBlockValidator) Start(ctx_in context.Context) error { return err } } - if v.execSpawner == nil { - return nil - } if err := v.execSpawner.Start(ctx_in); err != nil { return err } @@ -457,9 +452,7 @@ func (v *StatelessBlockValidator) Start(ctx_in context.Context) error { } func (v *StatelessBlockValidator) Stop() { - if v.execSpawner != nil { - v.execSpawner.Stop() - } + v.execSpawner.Stop() for _, spawner := range v.validationSpawners { spawner.Stop() } diff --git a/system_tests/block_validator_test.go b/system_tests/block_validator_test.go index 68fcaa5ba4..ed8438eb78 100644 --- a/system_tests/block_validator_test.go +++ b/system_tests/block_validator_test.go @@ -27,7 +27,8 @@ import ( "github.com/offchainlabs/nitro/solgen/go/precompilesgen" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/redisutil" - "github.com/offchainlabs/nitro/validator/server_api" + + validatorclient "github.com/offchainlabs/nitro/validator/client" ) type workloadType uint @@ -72,9 +73,10 @@ func testBlockValidatorSimple(t *testing.T, dasModeString string, workloadLoops redisURL := "" if useRedisStreams { redisURL = redisutil.CreateTestRedis(ctx, t) - validatorConfig.BlockValidator.RedisValidationClientConfig = server_api.DefaultRedisValidationClientConfig + validatorConfig.BlockValidator.RedisValidationClientConfig = validatorclient.DefaultRedisValidationClientConfig validatorConfig.BlockValidator.RedisValidationClientConfig.ModuleRoots = []string{currentRootModule(t).Hex()} validatorConfig.BlockValidator.RedisValidationClientConfig.RedisURL = redisURL + validatorConfig.BlockValidator.ValidationServerConfigs = nil } AddDefaultValNode(t, ctx, validatorConfig, !arbitrator, redisURL) diff --git a/system_tests/common_test.go b/system_tests/common_test.go index fb82ca5fa2..54e40219f3 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -31,7 +31,6 @@ import ( "github.com/offchainlabs/nitro/util/redisutil" "github.com/offchainlabs/nitro/util/signature" "github.com/offchainlabs/nitro/validator/server_api" - "github.com/offchainlabs/nitro/validator/server_api/validation" "github.com/offchainlabs/nitro/validator/server_common" "github.com/offchainlabs/nitro/validator/valnode" @@ -507,7 +506,7 @@ func createStackConfigForTest(dataDir string) *node.Config { return &stackConf } -func createGroup(ctx context.Context, t *testing.T, streamName string, client redis.UniversalClient) { +func createRedisGroup(ctx context.Context, t *testing.T, streamName string, client redis.UniversalClient) { t.Helper() // Stream name and group name are the same. if _, err := client.XGroupCreateMkStream(ctx, streamName, streamName, "$").Result(); err != nil { @@ -515,7 +514,7 @@ func createGroup(ctx context.Context, t *testing.T, streamName string, client re } } -func destroyGroup(ctx context.Context, t *testing.T, streamName string, client redis.UniversalClient) { +func destroyRedisGroup(ctx context.Context, t *testing.T, streamName string, client redis.UniversalClient) { t.Helper() if client == nil { return @@ -576,12 +575,9 @@ func StaticFetcherFrom[T any](t *testing.T, config *T) func() *T { return func() *T { return &tCopy } } -func configByValidationNode(t *testing.T, clientConfig *arbnode.Config, valStack *node.Node) { - if len(clientConfig.BlockValidator.ValidationServerConfigs) == 0 { - return - } - clientConfig.BlockValidator.ValidationServerConfigs[0].URL = valStack.WSEndpoint() - clientConfig.BlockValidator.ValidationServerConfigs[0].JWTSecret = "" +func configByValidationNode(clientConfig *arbnode.Config, valStack *node.Node) { + clientConfig.BlockValidator.ExecutionServerConfig.URL = valStack.WSEndpoint() + clientConfig.BlockValidator.ExecutionServerConfig.JWTSecret = "" } func currentRootModule(t *testing.T) common.Hash { @@ -597,26 +593,23 @@ func AddDefaultValNode(t *testing.T, ctx context.Context, nodeConfig *arbnode.Co if !nodeConfig.ValidatorRequired() { return } - if len(nodeConfig.BlockValidator.ValidationServerConfigs) > 0 && nodeConfig.BlockValidator.ValidationServerConfigs[0].URL != "" { - return - } conf := valnode.TestValidationConfig conf.UseJit = useJit // Enable redis streams when URL is specified if redisURL != "" { - conf.Arbitrator.RedisValidationServerConfig = validation.DefaultRedisValidationServerConfig + conf.Arbitrator.RedisValidationServerConfig = server_api.DefaultRedisValidationServerConfig redisClient, err := redisutil.RedisClientFromURL(redisURL) if err != nil { t.Fatalf("Error creating redis coordinator: %v", err) } redisStream := server_api.RedisStreamForRoot(currentRootModule(t)) - createGroup(ctx, t, redisStream, redisClient) + createRedisGroup(ctx, t, redisStream, redisClient) conf.Arbitrator.RedisValidationServerConfig.RedisURL = redisURL - t.Cleanup(func() { destroyGroup(ctx, t, redisStream, redisClient) }) + t.Cleanup(func() { destroyRedisGroup(ctx, t, redisStream, redisClient) }) conf.Arbitrator.RedisValidationServerConfig.ModuleRoots = []string{currentRootModule(t).Hex()} } _, valStack := createTestValidationNode(t, ctx, &conf) - configByValidationNode(t, nodeConfig, valStack) + configByValidationNode(nodeConfig, valStack) } func createTestL1BlockChainWithConfig(t *testing.T, l1info info, stackConfig *node.Config) (info, *ethclient.Client, *eth.Ethereum, *node.Node) { diff --git a/system_tests/full_challenge_impl_test.go b/system_tests/full_challenge_impl_test.go index 03b6d690f1..af790c9a17 100644 --- a/system_tests/full_challenge_impl_test.go +++ b/system_tests/full_challenge_impl_test.go @@ -277,7 +277,7 @@ func RunChallengeTest(t *testing.T, asserterIsCorrect bool, useStubs bool, chall } else { _, valStack = createTestValidationNode(t, ctx, &valnode.TestValidationConfig) } - configByValidationNode(t, conf, valStack) + configByValidationNode(conf, valStack) fatalErrChan := make(chan error, 10) asserterRollupAddresses, initMessage := DeployOnTestL1(t, ctx, l1Info, l1Backend, chainConfig) diff --git a/system_tests/validation_mock_test.go b/system_tests/validation_mock_test.go index d9c302b33f..2deb99b09a 100644 --- a/system_tests/validation_mock_test.go +++ b/system_tests/validation_mock_test.go @@ -21,6 +21,9 @@ import ( "github.com/offchainlabs/nitro/validator" "github.com/offchainlabs/nitro/validator/server_api" "github.com/offchainlabs/nitro/validator/server_arb" + "github.com/offchainlabs/nitro/validator/valnode" + + validatorclient "github.com/offchainlabs/nitro/validator/client" ) type mockSpawner struct { @@ -150,7 +153,7 @@ func createMockValidationNode(t *testing.T, ctx context.Context, config *server_ } configFetcher := func() *server_arb.ArbitratorSpawnerConfig { return config } spawner := &mockSpawner{} - serverAPI := server_api.NewExecutionServerAPI(spawner, spawner, configFetcher) + serverAPI := valnode.NewExecutionServerAPI(spawner, spawner, configFetcher) valAPIs := []rpc.API{{ Namespace: server_api.Namespace, @@ -181,7 +184,7 @@ func TestValidationServerAPI(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() _, validationDefault := createMockValidationNode(t, ctx, nil) - client := server_api.NewExecutionClient(StaticFetcherFrom(t, &rpcclient.TestClientConfig), validationDefault) + client := validatorclient.NewExecutionClient(StaticFetcherFrom(t, &rpcclient.TestClientConfig), validationDefault) err := client.Start(ctx) Require(t, err) @@ -247,7 +250,7 @@ func TestValidationClientRoom(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() mockSpawner, spawnerStack := createMockValidationNode(t, ctx, nil) - client := server_api.NewExecutionClient(StaticFetcherFrom(t, &rpcclient.TestClientConfig), spawnerStack) + client := validatorclient.NewExecutionClient(StaticFetcherFrom(t, &rpcclient.TestClientConfig), spawnerStack) err := client.Start(ctx) Require(t, err) @@ -334,10 +337,10 @@ func TestExecutionKeepAlive(t *testing.T) { _, validationShortTO := createMockValidationNode(t, ctx, &shortTimeoutConfig) configFetcher := StaticFetcherFrom(t, &rpcclient.TestClientConfig) - clientDefault := server_api.NewExecutionClient(configFetcher, validationDefault) + clientDefault := validatorclient.NewExecutionClient(configFetcher, validationDefault) err := clientDefault.Start(ctx) Require(t, err) - clientShortTO := server_api.NewExecutionClient(configFetcher, validationShortTO) + clientShortTO := validatorclient.NewExecutionClient(configFetcher, validationShortTO) err = clientShortTO.Start(ctx) Require(t, err) diff --git a/validator/server_api/redisproducer.go b/validator/client/redisproducer.go similarity index 95% rename from validator/server_api/redisproducer.go rename to validator/client/redisproducer.go index cafef7e778..cfe738f649 100644 --- a/validator/server_api/redisproducer.go +++ b/validator/client/redisproducer.go @@ -1,4 +1,4 @@ -package server_api +package client import ( "context" @@ -11,6 +11,7 @@ import ( "github.com/offchainlabs/nitro/util/redisutil" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" + "github.com/offchainlabs/nitro/validator/server_api" "github.com/offchainlabs/nitro/validator/server_common" "github.com/spf13/pflag" ) @@ -58,10 +59,6 @@ type RedisValidationClient struct { producers map[common.Hash]*pubsub.Producer[*validator.ValidationInput, validator.GoGlobalState] } -func RedisStreamForRoot(moduleRoot common.Hash) string { - return fmt.Sprintf("stream:%s", moduleRoot.Hex()) -} - func NewRedisValidationClient(cfg *RedisValidationClientConfig) (*RedisValidationClient, error) { res := &RedisValidationClient{ name: cfg.Name, @@ -81,7 +78,7 @@ func NewRedisValidationClient(cfg *RedisValidationClientConfig) (*RedisValidatio for _, hash := range cfg.ModuleRoots { mr := common.HexToHash(hash) p, err := pubsub.NewProducer[*validator.ValidationInput, validator.GoGlobalState]( - redisClient, RedisStreamForRoot(mr), &cfg.ProducerConfig) + redisClient, server_api.RedisStreamForRoot(mr), &cfg.ProducerConfig) if err != nil { return nil, fmt.Errorf("creating producer for validation: %w", err) } diff --git a/validator/server_api/validation_client.go b/validator/client/validation_client.go similarity index 72% rename from validator/server_api/validation_client.go rename to validator/client/validation_client.go index 0148eac0dd..ffa6ca9bd6 100644 --- a/validator/server_api/validation_client.go +++ b/validator/client/validation_client.go @@ -1,4 +1,4 @@ -package server_api +package client import ( "context" @@ -7,12 +7,15 @@ import ( "sync/atomic" "time" + "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/validator" "github.com/offchainlabs/nitro/util/containers" + "github.com/offchainlabs/nitro/util/jsonapi" "github.com/offchainlabs/nitro/util/rpcclient" "github.com/offchainlabs/nitro/util/stopwaiter" + "github.com/offchainlabs/nitro/validator/server_api" "github.com/offchainlabs/nitro/validator/server_common" "github.com/ethereum/go-ethereum/common" @@ -38,7 +41,7 @@ func (c *ValidationClient) Launch(entry *validator.ValidationInput, moduleRoot c promise := stopwaiter.LaunchPromiseThread[validator.GoGlobalState](c, func(ctx context.Context) (validator.GoGlobalState, error) { input := ValidationInputToJson(entry) var res validator.GoGlobalState - err := c.client.CallContext(ctx, &res, Namespace+"_validate", input, moduleRoot) + err := c.client.CallContext(ctx, &res, server_api.Namespace+"_validate", input, moduleRoot) atomic.AddInt32(&c.room, 1) return res, err }) @@ -54,14 +57,14 @@ func (c *ValidationClient) Start(ctx_in context.Context) error { } } var name string - if err := c.client.CallContext(ctx, &name, Namespace+"_name"); err != nil { + if err := c.client.CallContext(ctx, &name, server_api.Namespace+"_name"); err != nil { return err } if len(name) == 0 { return errors.New("couldn't read name from server") } var room int - if err := c.client.CallContext(c.GetContext(), &room, Namespace+"_room"); err != nil { + if err := c.client.CallContext(c.GetContext(), &room, server_api.Namespace+"_room"); err != nil { return err } if room < 2 { @@ -110,7 +113,7 @@ func NewExecutionClient(config rpcclient.ClientConfigFetcher, stack *node.Node) func (c *ExecutionClient) CreateExecutionRun(wasmModuleRoot common.Hash, input *validator.ValidationInput) containers.PromiseInterface[validator.ExecutionRun] { return stopwaiter.LaunchPromiseThread[validator.ExecutionRun](c, func(ctx context.Context) (validator.ExecutionRun, error) { var res uint64 - err := c.client.CallContext(ctx, &res, Namespace+"_createExecutionRun", wasmModuleRoot, ValidationInputToJson(input)) + err := c.client.CallContext(ctx, &res, server_api.Namespace+"_createExecutionRun", wasmModuleRoot, ValidationInputToJson(input)) if err != nil { return nil, err } @@ -132,7 +135,7 @@ type ExecutionClientRun struct { func (c *ExecutionClient) LatestWasmModuleRoot() containers.PromiseInterface[common.Hash] { return stopwaiter.LaunchPromiseThread[common.Hash](c, func(ctx context.Context) (common.Hash, error) { var res common.Hash - err := c.client.CallContext(ctx, &res, Namespace+"_latestWasmModuleRoot") + err := c.client.CallContext(ctx, &res, server_api.Namespace+"_latestWasmModuleRoot") if err != nil { return common.Hash{}, err } @@ -143,13 +146,13 @@ func (c *ExecutionClient) LatestWasmModuleRoot() containers.PromiseInterface[com func (c *ExecutionClient) WriteToFile(input *validator.ValidationInput, expOut validator.GoGlobalState, moduleRoot common.Hash) containers.PromiseInterface[struct{}] { jsonInput := ValidationInputToJson(input) return stopwaiter.LaunchPromiseThread[struct{}](c, func(ctx context.Context) (struct{}, error) { - err := c.client.CallContext(ctx, nil, Namespace+"_writeToFile", jsonInput, expOut, moduleRoot) + err := c.client.CallContext(ctx, nil, server_api.Namespace+"_writeToFile", jsonInput, expOut, moduleRoot) return struct{}{}, err }) } func (r *ExecutionClientRun) SendKeepAlive(ctx context.Context) time.Duration { - err := r.client.client.CallContext(ctx, nil, Namespace+"_execKeepAlive", r.id) + err := r.client.client.CallContext(ctx, nil, server_api.Namespace+"_execKeepAlive", r.id) if err != nil { log.Error("execution run keepalive failed", "err", err) } @@ -163,12 +166,12 @@ func (r *ExecutionClientRun) Start(ctx_in context.Context) { func (r *ExecutionClientRun) GetStepAt(pos uint64) containers.PromiseInterface[*validator.MachineStepResult] { return stopwaiter.LaunchPromiseThread[*validator.MachineStepResult](r, func(ctx context.Context) (*validator.MachineStepResult, error) { - var resJson MachineStepResultJson - err := r.client.client.CallContext(ctx, &resJson, Namespace+"_getStepAt", r.id, pos) + var resJson server_api.MachineStepResultJson + err := r.client.client.CallContext(ctx, &resJson, server_api.Namespace+"_getStepAt", r.id, pos) if err != nil { return nil, err } - res, err := MachineStepResultFromJson(&resJson) + res, err := server_api.MachineStepResultFromJson(&resJson) if err != nil { return nil, err } @@ -179,7 +182,7 @@ func (r *ExecutionClientRun) GetStepAt(pos uint64) containers.PromiseInterface[* func (r *ExecutionClientRun) GetProofAt(pos uint64) containers.PromiseInterface[[]byte] { return stopwaiter.LaunchPromiseThread[[]byte](r, func(ctx context.Context) ([]byte, error) { var resString string - err := r.client.client.CallContext(ctx, &resString, Namespace+"_getProofAt", r.id, pos) + err := r.client.client.CallContext(ctx, &resString, server_api.Namespace+"_getProofAt", r.id, pos) if err != nil { return nil, err } @@ -193,7 +196,7 @@ func (r *ExecutionClientRun) GetLastStep() containers.PromiseInterface[*validato func (r *ExecutionClientRun) PrepareRange(start, end uint64) containers.PromiseInterface[struct{}] { return stopwaiter.LaunchPromiseThread[struct{}](r, func(ctx context.Context) (struct{}, error) { - err := r.client.client.CallContext(ctx, nil, Namespace+"_prepareRange", r.id, start, end) + err := r.client.client.CallContext(ctx, nil, server_api.Namespace+"_prepareRange", r.id, start, end) if err != nil && ctx.Err() == nil { log.Warn("prepare execution got error", "err", err) } @@ -204,9 +207,29 @@ func (r *ExecutionClientRun) PrepareRange(start, end uint64) containers.PromiseI func (r *ExecutionClientRun) Close() { r.StopOnly() r.LaunchUntrackedThread(func() { - err := r.client.client.CallContext(r.GetParentContext(), nil, Namespace+"_closeExec", r.id) + err := r.client.client.CallContext(r.GetParentContext(), nil, server_api.Namespace+"_closeExec", r.id) if err != nil { log.Warn("closing execution client run got error", "err", err, "client", r.client.Name(), "id", r.id) } }) } + +func ValidationInputToJson(entry *validator.ValidationInput) *server_api.InputJSON { + jsonPreimagesMap := make(map[arbutil.PreimageType]*jsonapi.PreimagesMapJson) + for ty, preimages := range entry.Preimages { + jsonPreimagesMap[ty] = jsonapi.NewPreimagesMapJson(preimages) + } + res := &server_api.InputJSON{ + Id: entry.Id, + HasDelayedMsg: entry.HasDelayedMsg, + DelayedMsgNr: entry.DelayedMsgNr, + DelayedMsgB64: base64.StdEncoding.EncodeToString(entry.DelayedMsg), + StartState: entry.StartState, + PreimagesB64: jsonPreimagesMap, + } + for _, binfo := range entry.BatchInfo { + encData := base64.StdEncoding.EncodeToString(binfo.Data) + res.BatchInfo = append(res.BatchInfo, server_api.BatchInfoJson{Number: binfo.Number, DataB64: encData}) + } + return res +} diff --git a/validator/server_api/json.go b/validator/server_api/json.go index c1e4729571..e1729b53aa 100644 --- a/validator/server_api/json.go +++ b/validator/server_api/json.go @@ -4,65 +4,17 @@ package server_api import ( - "encoding/base64" + "fmt" "github.com/ethereum/go-ethereum/common" "github.com/offchainlabs/nitro/arbutil" + "github.com/offchainlabs/nitro/pubsub" "github.com/offchainlabs/nitro/util/jsonapi" "github.com/offchainlabs/nitro/validator" - "github.com/offchainlabs/nitro/validator/server_api/validation" + "github.com/spf13/pflag" ) -func ValidationInputToJson(entry *validator.ValidationInput) *validation.InputJSON { - jsonPreimagesMap := make(map[arbutil.PreimageType]*jsonapi.PreimagesMapJson) - for ty, preimages := range entry.Preimages { - jsonPreimagesMap[ty] = jsonapi.NewPreimagesMapJson(preimages) - } - res := &validation.InputJSON{ - Id: entry.Id, - HasDelayedMsg: entry.HasDelayedMsg, - DelayedMsgNr: entry.DelayedMsgNr, - DelayedMsgB64: base64.StdEncoding.EncodeToString(entry.DelayedMsg), - StartState: entry.StartState, - PreimagesB64: jsonPreimagesMap, - } - for _, binfo := range entry.BatchInfo { - encData := base64.StdEncoding.EncodeToString(binfo.Data) - res.BatchInfo = append(res.BatchInfo, validation.BatchInfoJson{Number: binfo.Number, DataB64: encData}) - } - return res -} - -func ValidationInputFromJson(entry *validation.InputJSON) (*validator.ValidationInput, error) { - preimages := make(map[arbutil.PreimageType]map[common.Hash][]byte) - for ty, jsonPreimages := range entry.PreimagesB64 { - preimages[ty] = jsonPreimages.Map - } - valInput := &validator.ValidationInput{ - Id: entry.Id, - HasDelayedMsg: entry.HasDelayedMsg, - DelayedMsgNr: entry.DelayedMsgNr, - StartState: entry.StartState, - Preimages: preimages, - } - delayed, err := base64.StdEncoding.DecodeString(entry.DelayedMsgB64) - if err != nil { - return nil, err - } - valInput.DelayedMsg = delayed - for _, binfo := range entry.BatchInfo { - data, err := base64.StdEncoding.DecodeString(binfo.DataB64) - if err != nil { - return nil, err - } - decInfo := validator.BatchInfo{ - Number: binfo.Number, - Data: data, - } - valInput.BatchInfo = append(valInput.BatchInfo, decInfo) - } - return valInput, nil -} +const Namespace string = "validation" type MachineStepResultJson struct { Hash common.Hash @@ -89,3 +41,51 @@ func MachineStepResultFromJson(resultJson *MachineStepResultJson) (*validator.Ma GlobalState: resultJson.GlobalState, }, nil } + +func RedisStreamForRoot(moduleRoot common.Hash) string { + return fmt.Sprintf("stream:%s", moduleRoot.Hex()) +} + +type Request struct { + Input *InputJSON + ModuleRoot common.Hash +} + +type InputJSON struct { + Id uint64 + HasDelayedMsg bool + DelayedMsgNr uint64 + PreimagesB64 map[arbutil.PreimageType]*jsonapi.PreimagesMapJson + BatchInfo []BatchInfoJson + DelayedMsgB64 string + StartState validator.GoGlobalState +} + +type BatchInfoJson struct { + Number uint64 + DataB64 string +} + +type RedisValidationServerConfig struct { + RedisURL string `koanf:"redis-url"` + ConsumerConfig pubsub.ConsumerConfig `koanf:"consumer-config"` + // Supported wasm module roots. + ModuleRoots []string `koanf:"module-roots"` +} + +var DefaultRedisValidationServerConfig = RedisValidationServerConfig{ + RedisURL: "", + ConsumerConfig: pubsub.DefaultConsumerConfig, + ModuleRoots: []string{}, +} + +var TestRedisValidationServerConfig = RedisValidationServerConfig{ + RedisURL: "", + ConsumerConfig: pubsub.TestConsumerConfig, + ModuleRoots: []string{}, +} + +func RedisValidationServerConfigAddOptions(prefix string, f *pflag.FlagSet) { + pubsub.ConsumerConfigAddOptions(prefix+".consumer-config", f) + f.StringSlice(prefix+".module-roots", nil, "Supported module root hashes") +} diff --git a/validator/server_api/validation/validation.go b/validator/server_api/validation/validation.go deleted file mode 100644 index 08d92085d6..0000000000 --- a/validator/server_api/validation/validation.go +++ /dev/null @@ -1,56 +0,0 @@ -// Package validation is introduced to avoid cyclic depenency between validation -// client and validation api. -package validation - -import ( - "github.com/ethereum/go-ethereum/common" - "github.com/offchainlabs/nitro/arbutil" - "github.com/offchainlabs/nitro/pubsub" - "github.com/offchainlabs/nitro/util/jsonapi" - "github.com/offchainlabs/nitro/validator" - "github.com/spf13/pflag" -) - -type Request struct { - Input *InputJSON - ModuleRoot common.Hash -} - -type InputJSON struct { - Id uint64 - HasDelayedMsg bool - DelayedMsgNr uint64 - PreimagesB64 map[arbutil.PreimageType]*jsonapi.PreimagesMapJson - BatchInfo []BatchInfoJson - DelayedMsgB64 string - StartState validator.GoGlobalState -} - -type BatchInfoJson struct { - Number uint64 - DataB64 string -} - -type RedisValidationServerConfig struct { - RedisURL string `koanf:"redis-url"` - ConsumerConfig pubsub.ConsumerConfig `koanf:"consumer-config"` - // Supported wasm module roots. - ModuleRoots []string `koanf:"module-roots"` -} - -var DefaultRedisValidationServerConfig = RedisValidationServerConfig{ - RedisURL: "", - ConsumerConfig: pubsub.DefaultConsumerConfig, - ModuleRoots: []string{}, -} - -var TestRedisValidationServerConfig = RedisValidationServerConfig{ - RedisURL: "", - ConsumerConfig: pubsub.TestConsumerConfig, - ModuleRoots: []string{}, -} - -func RedisValidationServerConfigAddOptions(prefix string, f *pflag.FlagSet) { - pubsub.ConsumerConfigAddOptions(prefix+".consumer-config", f) - f.StringSlice(prefix+".module-roots", nil, "Supported module root hashes") -} diff --git a/validator/server_arb/validator_spawner.go b/validator/server_arb/validator_spawner.go index a20a8d0e27..bc607d1088 100644 --- a/validator/server_arb/validator_spawner.go +++ b/validator/server_arb/validator_spawner.go @@ -17,7 +17,7 @@ import ( "github.com/offchainlabs/nitro/util/containers" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" - "github.com/offchainlabs/nitro/validator/server_api/validation" + "github.com/offchainlabs/nitro/validator/server_api" "github.com/offchainlabs/nitro/validator/server_common" "github.com/ethereum/go-ethereum/common" @@ -32,7 +32,7 @@ type ArbitratorSpawnerConfig struct { OutputPath string `koanf:"output-path" reload:"hot"` Execution MachineCacheConfig `koanf:"execution" reload:"hot"` // hot reloading for new executions only ExecutionRunTimeout time.Duration `koanf:"execution-run-timeout" reload:"hot"` - RedisValidationServerConfig validation.RedisValidationServerConfig `koanf:"redis-validation-server-config"` + RedisValidationServerConfig server_api.RedisValidationServerConfig `koanf:"redis-validation-server-config"` } type ArbitratorSpawnerConfigFecher func() *ArbitratorSpawnerConfig @@ -42,7 +42,7 @@ var DefaultArbitratorSpawnerConfig = ArbitratorSpawnerConfig{ OutputPath: "./target/output", Execution: DefaultMachineCacheConfig, ExecutionRunTimeout: time.Minute * 15, - RedisValidationServerConfig: validation.DefaultRedisValidationServerConfig, + RedisValidationServerConfig: server_api.DefaultRedisValidationServerConfig, } func ArbitratorSpawnerConfigAddOptions(prefix string, f *flag.FlagSet) { @@ -50,7 +50,7 @@ func ArbitratorSpawnerConfigAddOptions(prefix string, f *flag.FlagSet) { f.Duration(prefix+".execution-run-timeout", DefaultArbitratorSpawnerConfig.ExecutionRunTimeout, "timeout before discarding execution run") f.String(prefix+".output-path", DefaultArbitratorSpawnerConfig.OutputPath, "path to write machines to") MachineCacheConfigConfigAddOptions(prefix+".execution", f) - validation.RedisValidationServerConfigAddOptions(prefix+".redis-validation-server-config", f) + server_api.RedisValidationServerConfigAddOptions(prefix+".redis-validation-server-config", f) } func DefaultArbitratorSpawnerConfigFetcher() *ArbitratorSpawnerConfig { diff --git a/validator/server_api/redisconsumer.go b/validator/valnode/redisconsumer.go similarity index 90% rename from validator/server_api/redisconsumer.go rename to validator/valnode/redisconsumer.go index d87914380e..d90868fb9e 100644 --- a/validator/server_api/redisconsumer.go +++ b/validator/valnode/redisconsumer.go @@ -1,4 +1,4 @@ -package server_api +package valnode import ( "context" @@ -11,7 +11,7 @@ import ( "github.com/offchainlabs/nitro/util/redisutil" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" - "github.com/offchainlabs/nitro/validator/server_api/validation" + "github.com/offchainlabs/nitro/validator/server_api" ) // RedisValidationServer implements consumer for the requests originated from @@ -24,7 +24,7 @@ type RedisValidationServer struct { consumers map[common.Hash]*pubsub.Consumer[*validator.ValidationInput, validator.GoGlobalState] } -func NewRedisValidationServer(cfg *validation.RedisValidationServerConfig, spawner validator.ValidationSpawner) (*RedisValidationServer, error) { +func NewRedisValidationServer(cfg *server_api.RedisValidationServerConfig, spawner validator.ValidationSpawner) (*RedisValidationServer, error) { if cfg.RedisURL == "" { return nil, fmt.Errorf("redis url cannot be empty") } @@ -35,7 +35,7 @@ func NewRedisValidationServer(cfg *validation.RedisValidationServerConfig, spawn consumers := make(map[common.Hash]*pubsub.Consumer[*validator.ValidationInput, validator.GoGlobalState]) for _, hash := range cfg.ModuleRoots { mr := common.HexToHash(hash) - c, err := pubsub.NewConsumer[*validator.ValidationInput, validator.GoGlobalState](redisClient, RedisStreamForRoot(mr), &cfg.ConsumerConfig) + c, err := pubsub.NewConsumer[*validator.ValidationInput, validator.GoGlobalState](redisClient, server_api.RedisStreamForRoot(mr), &cfg.ConsumerConfig) if err != nil { return nil, fmt.Errorf("creating consumer for validation: %w", err) } diff --git a/validator/server_api/validation_api.go b/validator/valnode/validation_api.go similarity index 76% rename from validator/server_api/validation_api.go rename to validator/valnode/validation_api.go index 076e1ef79c..432e5eedd9 100644 --- a/validator/server_api/validation_api.go +++ b/validator/valnode/validation_api.go @@ -1,4 +1,4 @@ -package server_api +package valnode import ( "context" @@ -10,14 +10,13 @@ import ( "github.com/ethereum/go-ethereum/common" + "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" - "github.com/offchainlabs/nitro/validator/server_api/validation" + "github.com/offchainlabs/nitro/validator/server_api" "github.com/offchainlabs/nitro/validator/server_arb" ) -const Namespace string = "validation" - type ValidationServerAPI struct { spawner validator.ValidationSpawner } @@ -30,7 +29,7 @@ func (a *ValidationServerAPI) Room() int { return a.spawner.Room() } -func (a *ValidationServerAPI) Validate(ctx context.Context, entry *validation.InputJSON, moduleRoot common.Hash) (validator.GoGlobalState, error) { +func (a *ValidationServerAPI) Validate(ctx context.Context, entry *server_api.InputJSON, moduleRoot common.Hash) (validator.GoGlobalState, error) { valInput, err := ValidationInputFromJson(entry) if err != nil { return validator.GoGlobalState{}, err @@ -70,7 +69,7 @@ func NewExecutionServerAPI(valSpawner validator.ValidationSpawner, execution val } } -func (a *ExecServerAPI) CreateExecutionRun(ctx context.Context, wasmModuleRoot common.Hash, jsonInput *validation.InputJSON) (uint64, error) { +func (a *ExecServerAPI) CreateExecutionRun(ctx context.Context, wasmModuleRoot common.Hash, jsonInput *server_api.InputJSON) (uint64, error) { input, err := ValidationInputFromJson(jsonInput) if err != nil { return 0, err @@ -108,7 +107,7 @@ func (a *ExecServerAPI) Start(ctx_in context.Context) { a.CallIteratively(a.removeOldRuns) } -func (a *ExecServerAPI) WriteToFile(ctx context.Context, jsonInput *validation.InputJSON, expOut validator.GoGlobalState, moduleRoot common.Hash) error { +func (a *ExecServerAPI) WriteToFile(ctx context.Context, jsonInput *server_api.InputJSON, expOut validator.GoGlobalState, moduleRoot common.Hash) error { input, err := ValidationInputFromJson(jsonInput) if err != nil { return err @@ -130,7 +129,7 @@ func (a *ExecServerAPI) getRun(id uint64) (validator.ExecutionRun, error) { return entry.run, nil } -func (a *ExecServerAPI) GetStepAt(ctx context.Context, execid uint64, position uint64) (*MachineStepResultJson, error) { +func (a *ExecServerAPI) GetStepAt(ctx context.Context, execid uint64, position uint64) (*server_api.MachineStepResultJson, error) { run, err := a.getRun(execid) if err != nil { return nil, err @@ -140,7 +139,7 @@ func (a *ExecServerAPI) GetStepAt(ctx context.Context, execid uint64, position u if err != nil { return nil, err } - return MachineStepResultToJson(res), nil + return server_api.MachineStepResultToJson(res), nil } func (a *ExecServerAPI) GetProofAt(ctx context.Context, execid uint64, position uint64) (string, error) { @@ -183,3 +182,34 @@ func (a *ExecServerAPI) CloseExec(execid uint64) { run.run.Close() delete(a.runs, execid) } + +func ValidationInputFromJson(entry *server_api.InputJSON) (*validator.ValidationInput, error) { + preimages := make(map[arbutil.PreimageType]map[common.Hash][]byte) + for ty, jsonPreimages := range entry.PreimagesB64 { + preimages[ty] = jsonPreimages.Map + } + valInput := &validator.ValidationInput{ + Id: entry.Id, + HasDelayedMsg: entry.HasDelayedMsg, + DelayedMsgNr: entry.DelayedMsgNr, + StartState: entry.StartState, + Preimages: preimages, + } + delayed, err := base64.StdEncoding.DecodeString(entry.DelayedMsgB64) + if err != nil { + return nil, err + } + valInput.DelayedMsg = delayed + for _, binfo := range entry.BatchInfo { + data, err := base64.StdEncoding.DecodeString(binfo.DataB64) + if err != nil { + return nil, err + } + decInfo := validator.BatchInfo{ + Number: binfo.Number, + Data: data, + } + valInput.BatchInfo = append(valInput.BatchInfo, decInfo) + } + return valInput, nil +} diff --git a/validator/valnode/valnode.go b/validator/valnode/valnode.go index e42acd8ae4..bbb680087a 100644 --- a/validator/valnode/valnode.go +++ b/validator/valnode/valnode.go @@ -77,7 +77,7 @@ type ValidationNode struct { arbSpawner *server_arb.ArbitratorSpawner jitSpawner *server_jit.JitSpawner - redisConsumer *server_api.RedisValidationServer + redisConsumer *RedisValidationServer } func EnsureValidationExposedViaAuthRPC(stackConf *node.Config) { @@ -106,7 +106,7 @@ func CreateValidationNode(configFetcher ValidationConfigFetcher, stack *node.Nod if err != nil { return nil, err } - var serverAPI *server_api.ExecServerAPI + var serverAPI *ExecServerAPI var jitSpawner *server_jit.JitSpawner if config.UseJit { jitConfigFetcher := func() *server_jit.JitSpawnerConfig { return &configFetcher().Jit } @@ -115,11 +115,11 @@ func CreateValidationNode(configFetcher ValidationConfigFetcher, stack *node.Nod if err != nil { return nil, err } - serverAPI = server_api.NewExecutionServerAPI(jitSpawner, arbSpawner, arbConfigFetcher) + serverAPI = NewExecutionServerAPI(jitSpawner, arbSpawner, arbConfigFetcher) } else { - serverAPI = server_api.NewExecutionServerAPI(arbSpawner, arbSpawner, arbConfigFetcher) + serverAPI = NewExecutionServerAPI(arbSpawner, arbSpawner, arbConfigFetcher) } - redisConsumer, err := server_api.NewRedisValidationServer(&arbConfigFetcher().RedisValidationServerConfig, arbSpawner) + redisConsumer, err := NewRedisValidationServer(&arbConfigFetcher().RedisValidationServerConfig, arbSpawner) if err != nil { log.Error("Creating new redis validation server", "error", err) } From 005f4410ada241c393d4cd143160ac72830d1ac1 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Sat, 20 Apr 2024 11:08:56 +0200 Subject: [PATCH 19/41] Fix TestChallengeManagerFullAsserterCorrect test --- staker/stateless_block_validator.go | 16 +++++++++------- system_tests/common_test.go | 4 ++++ system_tests/validation_mock_test.go | 10 ++++++---- validator/client/redisproducer.go | 2 +- validator/client/validation_client.go | 6 ++---- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/staker/stateless_block_validator.go b/staker/stateless_block_validator.go index eaa2bfb133..8386d0b80c 100644 --- a/staker/stateless_block_validator.go +++ b/staker/stateless_block_validator.go @@ -195,10 +195,12 @@ func NewStatelessBlockValidator( stack *node.Node, ) (*StatelessBlockValidator, error) { var validationSpawners []validator.ValidationSpawner - redisValClient, err := validatorclient.NewRedisValidationClient(&config().RedisValidationClientConfig) - if err != nil { - log.Error("Creating redis validation client", "error", err) - } else { + if config().RedisValidationClientConfig.Enabled() { + redisValClient, err := validatorclient.NewRedisValidationClient(&config().RedisValidationClientConfig) + if err != nil { + return nil, fmt.Errorf("creating new redis validation client: %w", err) + // log.Error("Creating redis validation client, redis validator disabled", "error", err) + } validationSpawners = append(validationSpawners, redisValClient) } for _, serverConfig := range config().ValidationServerConfigs { @@ -427,17 +429,17 @@ func (v *StatelessBlockValidator) OverrideRecorder(t *testing.T, recorder execut func (v *StatelessBlockValidator) Start(ctx_in context.Context) error { for _, spawner := range v.validationSpawners { if err := spawner.Start(ctx_in); err != nil { - return err + return fmt.Errorf("starting validation spawner: %w", err) } } if err := v.execSpawner.Start(ctx_in); err != nil { - return err + return fmt.Errorf("starting execution spawner: %w", err) } if v.config.PendingUpgradeModuleRoot != "" { if v.config.PendingUpgradeModuleRoot == "latest" { latest, err := v.execSpawner.LatestWasmModuleRoot().Await(ctx_in) if err != nil { - return err + return fmt.Errorf("getting latest wasm module root: %w", err) } v.pendingWasmModuleRoot = latest } else { diff --git a/system_tests/common_test.go b/system_tests/common_test.go index 54e40219f3..ebf903cfa8 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -578,6 +578,10 @@ func StaticFetcherFrom[T any](t *testing.T, config *T) func() *T { func configByValidationNode(clientConfig *arbnode.Config, valStack *node.Node) { clientConfig.BlockValidator.ExecutionServerConfig.URL = valStack.WSEndpoint() clientConfig.BlockValidator.ExecutionServerConfig.JWTSecret = "" + if len(clientConfig.BlockValidator.ValidationServerConfigs) != 0 { + clientConfig.BlockValidator.ValidationServerConfigs[0].URL = valStack.WSEndpoint() + clientConfig.BlockValidator.ValidationServerConfigs[0].JWTSecret = "" + } } func currentRootModule(t *testing.T) common.Hash { diff --git a/system_tests/validation_mock_test.go b/system_tests/validation_mock_test.go index 2deb99b09a..788dfc5d7a 100644 --- a/system_tests/validation_mock_test.go +++ b/system_tests/validation_mock_test.go @@ -67,10 +67,12 @@ func (s *mockSpawner) Launch(entry *validator.ValidationInput, moduleRoot common var mockWasmModuleRoot common.Hash = common.HexToHash("0xa5a5a5") -func (s *mockSpawner) Start(context.Context) error { return nil } -func (s *mockSpawner) Stop() {} -func (s *mockSpawner) Name() string { return "mock" } -func (s *mockSpawner) Room() int { return 4 } +func (s *mockSpawner) Start(context.Context) error { + return nil +} +func (s *mockSpawner) Stop() {} +func (s *mockSpawner) Name() string { return "mock" } +func (s *mockSpawner) Room() int { return 4 } func (s *mockSpawner) CreateExecutionRun(wasmModuleRoot common.Hash, input *validator.ValidationInput) containers.PromiseInterface[validator.ExecutionRun] { s.ExecSpawned = append(s.ExecSpawned, input.Id) diff --git a/validator/client/redisproducer.go b/validator/client/redisproducer.go index cfe738f649..a2a9d28eb5 100644 --- a/validator/client/redisproducer.go +++ b/validator/client/redisproducer.go @@ -26,7 +26,7 @@ type RedisValidationClientConfig struct { } func (c RedisValidationClientConfig) Enabled() bool { - return len(c.ModuleRoots) > 0 + return c.RedisURL != "" } var DefaultRedisValidationClientConfig = RedisValidationClientConfig{ diff --git a/validator/client/validation_client.go b/validator/client/validation_client.go index ffa6ca9bd6..24e51230d6 100644 --- a/validator/client/validation_client.go +++ b/validator/client/validation_client.go @@ -51,10 +51,8 @@ func (c *ValidationClient) Launch(entry *validator.ValidationInput, moduleRoot c func (c *ValidationClient) Start(ctx_in context.Context) error { c.StopWaiter.Start(ctx_in, c) ctx := c.GetContext() - if c.client != nil { - if err := c.client.Start(ctx); err != nil { - return err - } + if err := c.client.Start(ctx); err != nil { + return err } var name string if err := c.client.CallContext(ctx, &name, server_api.Namespace+"_name"); err != nil { From a0268fe9e196b148705d7abf0484868154046c92 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Mon, 22 Apr 2024 16:06:28 +0200 Subject: [PATCH 20/41] Add config validation --- staker/block_validator.go | 6 ++++++ staker/stateless_block_validator.go | 13 ++++++------- validator/client/redisproducer.go | 29 ++++++++++++++++++++++++++--- 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/staker/block_validator.go b/staker/block_validator.go index cd89ccf650..806e5d44a1 100644 --- a/staker/block_validator.go +++ b/staker/block_validator.go @@ -134,6 +134,9 @@ func (c *BlockValidatorConfig) Validate() error { if err := c.ExecutionServerConfig.Validate(); err != nil { return fmt.Errorf("validating execution server config: %w", err) } + if err := c.RedisValidationClientConfig.Validate(); err != nil { + return fmt.Errorf("validating redis validation client configuration: %w", err) + } return nil } @@ -146,6 +149,8 @@ type BlockValidatorConfigFetcher func() *BlockValidatorConfig func BlockValidatorConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".enable", DefaultBlockValidatorConfig.Enable, "enable block-by-block validation") rpcclient.RPCClientAddOptions(prefix+".validation-server", f, &DefaultBlockValidatorConfig.ValidationServer) + rpcclient.RPCClientAddOptions(prefix+".execution-server-config", f, &DefaultBlockValidatorConfig.ExecutionServerConfig) + validatorclient.RedisValidationClientConfigAddOptions(prefix+"redis-validation-client-config", f) f.String(prefix+".validation-server-configs-list", DefaultBlockValidatorConfig.ValidationServerConfigsList, "array of validation rpc configs given as a json string. time duration should be supplied in number indicating nanoseconds") f.Duration(prefix+".validation-poll", DefaultBlockValidatorConfig.ValidationPoll, "poll time to check validations") f.Uint64(prefix+".forward-blocks", DefaultBlockValidatorConfig.ForwardBlocks, "prepare entries for up to that many blocks ahead of validation (small footprint)") @@ -165,6 +170,7 @@ var DefaultBlockValidatorConfig = BlockValidatorConfig{ Enable: false, ValidationServerConfigsList: "default", ValidationServer: rpcclient.DefaultClientConfig, + ExecutionServerConfig: rpcclient.DefaultClientConfig, ValidationPoll: time.Second, ForwardBlocks: 1024, PrerecordedBlocks: uint64(2 * runtime.NumCPU()), diff --git a/staker/stateless_block_validator.go b/staker/stateless_block_validator.go index 8386d0b80c..74b87f0291 100644 --- a/staker/stateless_block_validator.go +++ b/staker/stateless_block_validator.go @@ -208,7 +208,10 @@ func NewStatelessBlockValidator( validationSpawners = append(validationSpawners, validatorclient.NewValidationClient(valConfFetcher, stack)) } - validator := &StatelessBlockValidator{ + valConfFetcher := func() *rpcclient.ClientConfig { + return &config().ExecutionServerConfig + } + return &StatelessBlockValidator{ config: config(), recorder: recorder, validationSpawners: validationSpawners, @@ -218,12 +221,8 @@ func NewStatelessBlockValidator( db: arbdb, daService: das, blobReader: blobReader, - } - valConfFetcher := func() *rpcclient.ClientConfig { - return &config().ExecutionServerConfig - } - validator.execSpawner = validatorclient.NewExecutionClient(valConfFetcher, stack) - return validator, nil + execSpawner: validatorclient.NewExecutionClient(valConfFetcher, stack), + }, nil } func (v *StatelessBlockValidator) GetModuleRootsToValidate() []common.Hash { diff --git a/validator/client/redisproducer.go b/validator/client/redisproducer.go index a2a9d28eb5..50e58c4e66 100644 --- a/validator/client/redisproducer.go +++ b/validator/client/redisproducer.go @@ -6,6 +6,7 @@ import ( "sync/atomic" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/pubsub" "github.com/offchainlabs/nitro/util/containers" "github.com/offchainlabs/nitro/util/redisutil" @@ -23,12 +24,35 @@ type RedisValidationClientConfig struct { ProducerConfig pubsub.ProducerConfig `koanf:"producer-config"` // Supported wasm module roots, when the list is empty this is disabled. ModuleRoots []string `koanf:"module-roots"` + moduleRoots []common.Hash } func (c RedisValidationClientConfig) Enabled() bool { return c.RedisURL != "" } +func (c *RedisValidationClientConfig) Validate() error { + m := make(map[string]bool) + // Add all moduleRoot hashes in case Validate is called twice so that we + // don't add duplicate moduleRoots again. + for _, mr := range c.moduleRoots { + m[mr.Hex()] = true + } + for _, mr := range c.ModuleRoots { + if _, exists := m[mr]; exists { + log.Warn("Duplicate module root", "hash", mr) + continue + } + h := common.HexToHash(mr) + if h == (common.Hash{}) { + return fmt.Errorf("invalid module root hash: %q", mr) + } + m[mr] = true + c.moduleRoots = append(c.moduleRoots, h) + } + return nil +} + var DefaultRedisValidationClientConfig = RedisValidationClientConfig{ Name: "redis validation client", Room: 2, @@ -72,11 +96,10 @@ func NewRedisValidationClient(cfg *RedisValidationClientConfig) (*RedisValidatio if err != nil { return nil, err } - if len(cfg.ModuleRoots) == 0 { + if len(cfg.moduleRoots) == 0 { return nil, fmt.Errorf("moduleRoots must be specified to enable redis streams") } - for _, hash := range cfg.ModuleRoots { - mr := common.HexToHash(hash) + for _, mr := range cfg.moduleRoots { p, err := pubsub.NewProducer[*validator.ValidationInput, validator.GoGlobalState]( redisClient, server_api.RedisStreamForRoot(mr), &cfg.ProducerConfig) if err != nil { From 4e837508ebe925cfe5d7d33d07f1c41b2920efef Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Mon, 22 Apr 2024 18:49:21 +0200 Subject: [PATCH 21/41] Fix config defaults --- pubsub/producer.go | 1 + staker/block_validator.go | 28 +++++++++++++++------------- validator/client/redisproducer.go | 2 ++ 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/pubsub/producer.go b/pubsub/producer.go index b00eec7f64..074670ca0f 100644 --- a/pubsub/producer.go +++ b/pubsub/producer.go @@ -78,6 +78,7 @@ var TestProducerConfig = ProducerConfig{ func ProducerAddConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".enable-reproduce", DefaultProducerConfig.EnableReproduce, "when enabled, messages with dead consumer will be re-inserted into the stream") f.Duration(prefix+".check-pending-interval", DefaultProducerConfig.CheckPendingInterval, "interval in which producer checks pending messages whether consumer processing them is inactive") + f.Duration(prefix+".check-result-interval", DefaultProducerConfig.CheckResultInterval, "interval in which producer checks pending messages whether consumer processing them is inactive") f.Duration(prefix+".keepalive-timeout", DefaultProducerConfig.KeepAliveTimeout, "timeout after which consumer is considered inactive if heartbeat wasn't performed") } diff --git a/staker/block_validator.go b/staker/block_validator.go index 806e5d44a1..b66fcea44c 100644 --- a/staker/block_validator.go +++ b/staker/block_validator.go @@ -150,7 +150,7 @@ func BlockValidatorConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".enable", DefaultBlockValidatorConfig.Enable, "enable block-by-block validation") rpcclient.RPCClientAddOptions(prefix+".validation-server", f, &DefaultBlockValidatorConfig.ValidationServer) rpcclient.RPCClientAddOptions(prefix+".execution-server-config", f, &DefaultBlockValidatorConfig.ExecutionServerConfig) - validatorclient.RedisValidationClientConfigAddOptions(prefix+"redis-validation-client-config", f) + validatorclient.RedisValidationClientConfigAddOptions(prefix+".redis-validation-client-config", f) f.String(prefix+".validation-server-configs-list", DefaultBlockValidatorConfig.ValidationServerConfigsList, "array of validation rpc configs given as a json string. time duration should be supplied in number indicating nanoseconds") f.Duration(prefix+".validation-poll", DefaultBlockValidatorConfig.ValidationPoll, "poll time to check validations") f.Uint64(prefix+".forward-blocks", DefaultBlockValidatorConfig.ForwardBlocks, "prepare entries for up to that many blocks ahead of validation (small footprint)") @@ -171,6 +171,7 @@ var DefaultBlockValidatorConfig = BlockValidatorConfig{ ValidationServerConfigsList: "default", ValidationServer: rpcclient.DefaultClientConfig, ExecutionServerConfig: rpcclient.DefaultClientConfig, + RedisValidationClientConfig: validatorclient.DefaultRedisValidationClientConfig, ValidationPoll: time.Second, ForwardBlocks: 1024, PrerecordedBlocks: uint64(2 * runtime.NumCPU()), @@ -182,18 +183,19 @@ var DefaultBlockValidatorConfig = BlockValidatorConfig{ } var TestBlockValidatorConfig = BlockValidatorConfig{ - Enable: false, - ValidationServer: rpcclient.TestClientConfig, - ValidationServerConfigs: []rpcclient.ClientConfig{rpcclient.TestClientConfig}, - ExecutionServerConfig: rpcclient.TestClientConfig, - ValidationPoll: 100 * time.Millisecond, - ForwardBlocks: 128, - PrerecordedBlocks: uint64(2 * runtime.NumCPU()), - CurrentModuleRoot: "latest", - PendingUpgradeModuleRoot: "latest", - FailureIsFatal: true, - Dangerous: DefaultBlockValidatorDangerousConfig, - MemoryFreeLimit: "default", + Enable: false, + ValidationServer: rpcclient.TestClientConfig, + ValidationServerConfigs: []rpcclient.ClientConfig{rpcclient.TestClientConfig}, + RedisValidationClientConfig: validatorclient.TestRedisValidationClientConfig, + ExecutionServerConfig: rpcclient.TestClientConfig, + ValidationPoll: 100 * time.Millisecond, + ForwardBlocks: 128, + PrerecordedBlocks: uint64(2 * runtime.NumCPU()), + CurrentModuleRoot: "latest", + PendingUpgradeModuleRoot: "latest", + FailureIsFatal: true, + Dangerous: DefaultBlockValidatorDangerousConfig, + MemoryFreeLimit: "default", } var DefaultBlockValidatorDangerousConfig = BlockValidatorDangerousConfig{ diff --git a/validator/client/redisproducer.go b/validator/client/redisproducer.go index 50e58c4e66..bfc083daf9 100644 --- a/validator/client/redisproducer.go +++ b/validator/client/redisproducer.go @@ -58,6 +58,7 @@ var DefaultRedisValidationClientConfig = RedisValidationClientConfig{ Room: 2, RedisURL: "", ProducerConfig: pubsub.DefaultProducerConfig, + ModuleRoots: []string{}, } var TestRedisValidationClientConfig = RedisValidationClientConfig{ @@ -65,6 +66,7 @@ var TestRedisValidationClientConfig = RedisValidationClientConfig{ Room: 2, RedisURL: "", ProducerConfig: pubsub.TestProducerConfig, + ModuleRoots: []string{}, } func RedisValidationClientConfigAddOptions(prefix string, f *pflag.FlagSet) { From 123023e4e8947d6b03de46827e08bcdfc6aa0802 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Mon, 22 Apr 2024 21:15:16 +0200 Subject: [PATCH 22/41] drop moduleRoots from config and initialize from block_validator instead --- staker/block_validator.go | 6 +-- staker/stateless_block_validator.go | 14 +++++- system_tests/block_validator_test.go | 1 - validator/client/redisproducer.go | 66 ++++++++++------------------ 4 files changed, 40 insertions(+), 47 deletions(-) diff --git a/staker/block_validator.go b/staker/block_validator.go index b66fcea44c..1a601db8a9 100644 --- a/staker/block_validator.go +++ b/staker/block_validator.go @@ -134,9 +134,6 @@ func (c *BlockValidatorConfig) Validate() error { if err := c.ExecutionServerConfig.Validate(); err != nil { return fmt.Errorf("validating execution server config: %w", err) } - if err := c.RedisValidationClientConfig.Validate(); err != nil { - return fmt.Errorf("validating redis validation client configuration: %w", err) - } return nil } @@ -1068,6 +1065,9 @@ func (v *BlockValidator) Initialize(ctx context.Context) error { } } log.Info("BlockValidator initialized", "current", v.currentWasmModuleRoot, "pending", v.pendingWasmModuleRoot) + if err := v.StatelessBlockValidator.Initialize([]common.Hash{v.currentWasmModuleRoot, v.pendingWasmModuleRoot}); err != nil { + return fmt.Errorf("initializing block validator with module roots: %w", err) + } return nil } diff --git a/staker/stateless_block_validator.go b/staker/stateless_block_validator.go index 74b87f0291..4f71e39545 100644 --- a/staker/stateless_block_validator.go +++ b/staker/stateless_block_validator.go @@ -199,7 +199,6 @@ func NewStatelessBlockValidator( redisValClient, err := validatorclient.NewRedisValidationClient(&config().RedisValidationClientConfig) if err != nil { return nil, fmt.Errorf("creating new redis validation client: %w", err) - // log.Error("Creating redis validation client, redis validator disabled", "error", err) } validationSpawners = append(validationSpawners, redisValClient) } @@ -225,6 +224,19 @@ func NewStatelessBlockValidator( }, nil } +func (v *StatelessBlockValidator) Initialize(moduleRoots []common.Hash) error { + if len(v.validationSpawners) == 0 { + return nil + } + // First spawner is always RedisValidationClient if RedisStreams are enabled. + if v, ok := v.validationSpawners[0].(*validatorclient.RedisValidationClient); ok { + if err := v.Initialize(moduleRoots); err != nil { + return fmt.Errorf("initializing redis validation client module roots: %w", err) + } + } + return nil +} + func (v *StatelessBlockValidator) GetModuleRootsToValidate() []common.Hash { v.moduleMutex.Lock() defer v.moduleMutex.Unlock() diff --git a/system_tests/block_validator_test.go b/system_tests/block_validator_test.go index ed8438eb78..a7c85bf5e4 100644 --- a/system_tests/block_validator_test.go +++ b/system_tests/block_validator_test.go @@ -74,7 +74,6 @@ func testBlockValidatorSimple(t *testing.T, dasModeString string, workloadLoops if useRedisStreams { redisURL = redisutil.CreateTestRedis(ctx, t) validatorConfig.BlockValidator.RedisValidationClientConfig = validatorclient.DefaultRedisValidationClientConfig - validatorConfig.BlockValidator.RedisValidationClientConfig.ModuleRoots = []string{currentRootModule(t).Hex()} validatorConfig.BlockValidator.RedisValidationClientConfig.RedisURL = redisURL validatorConfig.BlockValidator.ValidationServerConfigs = nil } diff --git a/validator/client/redisproducer.go b/validator/client/redisproducer.go index bfc083daf9..07569d51b6 100644 --- a/validator/client/redisproducer.go +++ b/validator/client/redisproducer.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "github.com/go-redis/redis/v8" "github.com/offchainlabs/nitro/pubsub" "github.com/offchainlabs/nitro/util/containers" "github.com/offchainlabs/nitro/util/redisutil" @@ -22,43 +23,17 @@ type RedisValidationClientConfig struct { Room int32 `koanf:"room"` RedisURL string `koanf:"redis-url"` ProducerConfig pubsub.ProducerConfig `koanf:"producer-config"` - // Supported wasm module roots, when the list is empty this is disabled. - ModuleRoots []string `koanf:"module-roots"` - moduleRoots []common.Hash } func (c RedisValidationClientConfig) Enabled() bool { return c.RedisURL != "" } -func (c *RedisValidationClientConfig) Validate() error { - m := make(map[string]bool) - // Add all moduleRoot hashes in case Validate is called twice so that we - // don't add duplicate moduleRoots again. - for _, mr := range c.moduleRoots { - m[mr.Hex()] = true - } - for _, mr := range c.ModuleRoots { - if _, exists := m[mr]; exists { - log.Warn("Duplicate module root", "hash", mr) - continue - } - h := common.HexToHash(mr) - if h == (common.Hash{}) { - return fmt.Errorf("invalid module root hash: %q", mr) - } - m[mr] = true - c.moduleRoots = append(c.moduleRoots, h) - } - return nil -} - var DefaultRedisValidationClientConfig = RedisValidationClientConfig{ Name: "redis validation client", Room: 2, RedisURL: "", ProducerConfig: pubsub.DefaultProducerConfig, - ModuleRoots: []string{}, } var TestRedisValidationClientConfig = RedisValidationClientConfig{ @@ -66,14 +41,12 @@ var TestRedisValidationClientConfig = RedisValidationClientConfig{ Room: 2, RedisURL: "", ProducerConfig: pubsub.TestProducerConfig, - ModuleRoots: []string{}, } func RedisValidationClientConfigAddOptions(prefix string, f *pflag.FlagSet) { f.String(prefix+".name", DefaultRedisValidationClientConfig.Name, "validation client name") f.Int32(prefix+".room", DefaultRedisValidationClientConfig.Room, "validation client room") pubsub.ProducerAddConfigAddOptions(prefix+".producer-config", f) - f.StringSlice(prefix+".module-roots", nil, "Supported module root hashes") } // RedisValidationClient implements validation client through redis streams. @@ -82,15 +55,12 @@ type RedisValidationClient struct { name string room int32 // producers stores moduleRoot to producer mapping. - producers map[common.Hash]*pubsub.Producer[*validator.ValidationInput, validator.GoGlobalState] + producers map[common.Hash]*pubsub.Producer[*validator.ValidationInput, validator.GoGlobalState] + producerConfig pubsub.ProducerConfig + redisClient redis.UniversalClient } func NewRedisValidationClient(cfg *RedisValidationClientConfig) (*RedisValidationClient, error) { - res := &RedisValidationClient{ - name: cfg.Name, - room: cfg.Room, - producers: make(map[common.Hash]*pubsub.Producer[*validator.ValidationInput, validator.GoGlobalState]), - } if cfg.RedisURL == "" { return nil, fmt.Errorf("redis url cannot be empty") } @@ -98,18 +68,30 @@ func NewRedisValidationClient(cfg *RedisValidationClientConfig) (*RedisValidatio if err != nil { return nil, err } - if len(cfg.moduleRoots) == 0 { - return nil, fmt.Errorf("moduleRoots must be specified to enable redis streams") - } - for _, mr := range cfg.moduleRoots { + return &RedisValidationClient{ + name: cfg.Name, + room: cfg.Room, + producers: make(map[common.Hash]*pubsub.Producer[*validator.ValidationInput, validator.GoGlobalState]), + producerConfig: cfg.ProducerConfig, + redisClient: redisClient, + }, nil +} + +func (c *RedisValidationClient) Initialize(moduleRoots []common.Hash) error { + for _, mr := range moduleRoots { + if _, exists := c.producers[mr]; exists { + log.Warn("Producer already existsw for module root", "hash", mr) + continue + } p, err := pubsub.NewProducer[*validator.ValidationInput, validator.GoGlobalState]( - redisClient, server_api.RedisStreamForRoot(mr), &cfg.ProducerConfig) + c.redisClient, server_api.RedisStreamForRoot(mr), &c.producerConfig) if err != nil { - return nil, fmt.Errorf("creating producer for validation: %w", err) + return fmt.Errorf("creating producer for validation: %w", err) } - res.producers[mr] = p + p.Start(c.GetContext()) + c.producers[mr] = p } - return res, nil + return nil } func (c *RedisValidationClient) Launch(entry *validator.ValidationInput, moduleRoot common.Hash) validator.ValidationRun { From 400492dfb706d65fea8ed6418d3ec202b7316988 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Mon, 22 Apr 2024 16:00:34 -0500 Subject: [PATCH 23/41] Merge v1.13.10 --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index 018bd54e2e..22a573ce54 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 018bd54e2ecdf494dce8f59e29cc083af9bdd74c +Subproject commit 22a573ce5463a305ab2787473518a7575f0ec796 From 27edb42dc00ec8d3176dacaf94e332cd75d8f459 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Tue, 23 Apr 2024 11:18:33 +0200 Subject: [PATCH 24/41] Cast bytes to fixed size array instead of copying --- gethhook/geth-hook.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gethhook/geth-hook.go b/gethhook/geth-hook.go index dcd1788710..08b96b384f 100644 --- a/gethhook/geth-hook.go +++ b/gethhook/geth-hook.go @@ -58,9 +58,7 @@ func init() { precompileErrors := make(map[[4]byte]abi.Error) for addr, precompile := range precompiles.Precompiles() { for _, errABI := range precompile.Precompile().GetErrorABIs() { - var id [4]byte - copy(id[:], errABI.ID[:4]) - precompileErrors[id] = errABI + precompileErrors[[4]byte(errABI.ID.Bytes())] = errABI } var wrapped vm.AdvancedPrecompile = ArbosPrecompileWrapper{precompile} vm.PrecompiledContractsArbitrum[addr] = wrapped From 9dfe3d179a2059881408caa2bdeb2d4fe17d98f0 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Tue, 23 Apr 2024 12:02:42 +0200 Subject: [PATCH 25/41] Factor out redisproducer and redisconumer --- staker/block_validator.go | 43 ++++++++++--------- staker/stateless_block_validator.go | 5 ++- system_tests/block_validator_test.go | 5 +-- system_tests/common_test.go | 3 +- .../{redisproducer.go => redis/producer.go} | 36 ++++++++-------- validator/server_api/json.go | 26 ----------- validator/server_arb/validator_spawner.go | 20 ++++----- .../{redisconsumer.go => redis/consumer.go} | 37 +++++++++++++--- validator/valnode/valnode.go | 12 +++--- 9 files changed, 95 insertions(+), 92 deletions(-) rename validator/client/{redisproducer.go => redis/producer.go} (73%) rename validator/valnode/{redisconsumer.go => redis/consumer.go} (64%) diff --git a/staker/block_validator.go b/staker/block_validator.go index 1a601db8a9..0cde4423c0 100644 --- a/staker/block_validator.go +++ b/staker/block_validator.go @@ -25,9 +25,8 @@ import ( "github.com/offchainlabs/nitro/util/rpcclient" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" + "github.com/offchainlabs/nitro/validator/client/redis" "github.com/spf13/pflag" - - validatorclient "github.com/offchainlabs/nitro/validator/client" ) var ( @@ -84,20 +83,20 @@ type BlockValidator struct { } type BlockValidatorConfig struct { - Enable bool `koanf:"enable"` - ValidationServer rpcclient.ClientConfig `koanf:"validation-server" reload:"hot"` - RedisValidationClientConfig validatorclient.RedisValidationClientConfig `koanf:"redis-validation-client-config"` - ValidationServerConfigs []rpcclient.ClientConfig `koanf:"validation-server-configs" reload:"hot"` - ExecutionServerConfig rpcclient.ClientConfig `koanf:"execution-server-config" reload:"hot"` - ValidationPoll time.Duration `koanf:"validation-poll" reload:"hot"` - PrerecordedBlocks uint64 `koanf:"prerecorded-blocks" reload:"hot"` - ForwardBlocks uint64 `koanf:"forward-blocks" reload:"hot"` - CurrentModuleRoot string `koanf:"current-module-root"` // TODO(magic) requires reinitialization on hot reload - PendingUpgradeModuleRoot string `koanf:"pending-upgrade-module-root"` // TODO(magic) requires StatelessBlockValidator recreation on hot reload - FailureIsFatal bool `koanf:"failure-is-fatal" reload:"hot"` - Dangerous BlockValidatorDangerousConfig `koanf:"dangerous"` - MemoryFreeLimit string `koanf:"memory-free-limit" reload:"hot"` - ValidationServerConfigsList string `koanf:"validation-server-configs-list" reload:"hot"` + Enable bool `koanf:"enable"` + ValidationServer rpcclient.ClientConfig `koanf:"validation-server" reload:"hot"` + RedisValidationClientConfig redis.ValidationClientConfig `koanf:"redis-validation-client-config"` + ValidationServerConfigs []rpcclient.ClientConfig `koanf:"validation-server-configs" reload:"hot"` + ExecutionServerConfig rpcclient.ClientConfig `koanf:"execution-server-config" reload:"hot"` + ValidationPoll time.Duration `koanf:"validation-poll" reload:"hot"` + PrerecordedBlocks uint64 `koanf:"prerecorded-blocks" reload:"hot"` + ForwardBlocks uint64 `koanf:"forward-blocks" reload:"hot"` + CurrentModuleRoot string `koanf:"current-module-root"` // TODO(magic) requires reinitialization on hot reload + PendingUpgradeModuleRoot string `koanf:"pending-upgrade-module-root"` // TODO(magic) requires StatelessBlockValidator recreation on hot reload + FailureIsFatal bool `koanf:"failure-is-fatal" reload:"hot"` + Dangerous BlockValidatorDangerousConfig `koanf:"dangerous"` + MemoryFreeLimit string `koanf:"memory-free-limit" reload:"hot"` + ValidationServerConfigsList string `koanf:"validation-server-configs-list" reload:"hot"` memoryFreeLimit int } @@ -147,7 +146,7 @@ func BlockValidatorConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".enable", DefaultBlockValidatorConfig.Enable, "enable block-by-block validation") rpcclient.RPCClientAddOptions(prefix+".validation-server", f, &DefaultBlockValidatorConfig.ValidationServer) rpcclient.RPCClientAddOptions(prefix+".execution-server-config", f, &DefaultBlockValidatorConfig.ExecutionServerConfig) - validatorclient.RedisValidationClientConfigAddOptions(prefix+".redis-validation-client-config", f) + redis.ValidationClientConfigAddOptions(prefix+".redis-validation-client-config", f) f.String(prefix+".validation-server-configs-list", DefaultBlockValidatorConfig.ValidationServerConfigsList, "array of validation rpc configs given as a json string. time duration should be supplied in number indicating nanoseconds") f.Duration(prefix+".validation-poll", DefaultBlockValidatorConfig.ValidationPoll, "poll time to check validations") f.Uint64(prefix+".forward-blocks", DefaultBlockValidatorConfig.ForwardBlocks, "prepare entries for up to that many blocks ahead of validation (small footprint)") @@ -168,7 +167,7 @@ var DefaultBlockValidatorConfig = BlockValidatorConfig{ ValidationServerConfigsList: "default", ValidationServer: rpcclient.DefaultClientConfig, ExecutionServerConfig: rpcclient.DefaultClientConfig, - RedisValidationClientConfig: validatorclient.DefaultRedisValidationClientConfig, + RedisValidationClientConfig: redis.DefaultValidationClientConfig, ValidationPoll: time.Second, ForwardBlocks: 1024, PrerecordedBlocks: uint64(2 * runtime.NumCPU()), @@ -183,7 +182,7 @@ var TestBlockValidatorConfig = BlockValidatorConfig{ Enable: false, ValidationServer: rpcclient.TestClientConfig, ValidationServerConfigs: []rpcclient.ClientConfig{rpcclient.TestClientConfig}, - RedisValidationClientConfig: validatorclient.TestRedisValidationClientConfig, + RedisValidationClientConfig: redis.TestValidationClientConfig, ExecutionServerConfig: rpcclient.TestClientConfig, ValidationPoll: 100 * time.Millisecond, ForwardBlocks: 128, @@ -1065,7 +1064,11 @@ func (v *BlockValidator) Initialize(ctx context.Context) error { } } log.Info("BlockValidator initialized", "current", v.currentWasmModuleRoot, "pending", v.pendingWasmModuleRoot) - if err := v.StatelessBlockValidator.Initialize([]common.Hash{v.currentWasmModuleRoot, v.pendingWasmModuleRoot}); err != nil { + moduleRoots := []common.Hash{v.currentWasmModuleRoot} + if v.pendingWasmModuleRoot != v.currentWasmModuleRoot { + moduleRoots = append(moduleRoots, v.pendingWasmModuleRoot) + } + if err := v.StatelessBlockValidator.Initialize(moduleRoots); err != nil { return fmt.Errorf("initializing block validator with module roots: %w", err) } return nil diff --git a/staker/stateless_block_validator.go b/staker/stateless_block_validator.go index 4f71e39545..f8e30329a7 100644 --- a/staker/stateless_block_validator.go +++ b/staker/stateless_block_validator.go @@ -22,6 +22,7 @@ import ( "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/util/rpcclient" "github.com/offchainlabs/nitro/validator" + "github.com/offchainlabs/nitro/validator/client/redis" validatorclient "github.com/offchainlabs/nitro/validator/client" ) @@ -196,7 +197,7 @@ func NewStatelessBlockValidator( ) (*StatelessBlockValidator, error) { var validationSpawners []validator.ValidationSpawner if config().RedisValidationClientConfig.Enabled() { - redisValClient, err := validatorclient.NewRedisValidationClient(&config().RedisValidationClientConfig) + redisValClient, err := redis.NewValidationClient(&config().RedisValidationClientConfig) if err != nil { return nil, fmt.Errorf("creating new redis validation client: %w", err) } @@ -229,7 +230,7 @@ func (v *StatelessBlockValidator) Initialize(moduleRoots []common.Hash) error { return nil } // First spawner is always RedisValidationClient if RedisStreams are enabled. - if v, ok := v.validationSpawners[0].(*validatorclient.RedisValidationClient); ok { + if v, ok := v.validationSpawners[0].(*redis.ValidationClient); ok { if err := v.Initialize(moduleRoots); err != nil { return fmt.Errorf("initializing redis validation client module roots: %w", err) } diff --git a/system_tests/block_validator_test.go b/system_tests/block_validator_test.go index a7c85bf5e4..c64fe22f54 100644 --- a/system_tests/block_validator_test.go +++ b/system_tests/block_validator_test.go @@ -27,8 +27,7 @@ import ( "github.com/offchainlabs/nitro/solgen/go/precompilesgen" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/redisutil" - - validatorclient "github.com/offchainlabs/nitro/validator/client" + "github.com/offchainlabs/nitro/validator/client/redis" ) type workloadType uint @@ -73,7 +72,7 @@ func testBlockValidatorSimple(t *testing.T, dasModeString string, workloadLoops redisURL := "" if useRedisStreams { redisURL = redisutil.CreateTestRedis(ctx, t) - validatorConfig.BlockValidator.RedisValidationClientConfig = validatorclient.DefaultRedisValidationClientConfig + validatorConfig.BlockValidator.RedisValidationClientConfig = redis.DefaultValidationClientConfig validatorConfig.BlockValidator.RedisValidationClientConfig.RedisURL = redisURL validatorConfig.BlockValidator.ValidationServerConfigs = nil } diff --git a/system_tests/common_test.go b/system_tests/common_test.go index ebf903cfa8..5ad8aae08d 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -33,6 +33,7 @@ import ( "github.com/offchainlabs/nitro/validator/server_api" "github.com/offchainlabs/nitro/validator/server_common" "github.com/offchainlabs/nitro/validator/valnode" + rediscons "github.com/offchainlabs/nitro/validator/valnode/redis" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -601,7 +602,7 @@ func AddDefaultValNode(t *testing.T, ctx context.Context, nodeConfig *arbnode.Co conf.UseJit = useJit // Enable redis streams when URL is specified if redisURL != "" { - conf.Arbitrator.RedisValidationServerConfig = server_api.DefaultRedisValidationServerConfig + conf.Arbitrator.RedisValidationServerConfig = rediscons.DefaultValidationServerConfig redisClient, err := redisutil.RedisClientFromURL(redisURL) if err != nil { t.Fatalf("Error creating redis coordinator: %v", err) diff --git a/validator/client/redisproducer.go b/validator/client/redis/producer.go similarity index 73% rename from validator/client/redisproducer.go rename to validator/client/redis/producer.go index 07569d51b6..da184e3c16 100644 --- a/validator/client/redisproducer.go +++ b/validator/client/redis/producer.go @@ -1,4 +1,4 @@ -package client +package redis import ( "context" @@ -18,39 +18,39 @@ import ( "github.com/spf13/pflag" ) -type RedisValidationClientConfig struct { +type ValidationClientConfig struct { Name string `koanf:"name"` Room int32 `koanf:"room"` RedisURL string `koanf:"redis-url"` ProducerConfig pubsub.ProducerConfig `koanf:"producer-config"` } -func (c RedisValidationClientConfig) Enabled() bool { +func (c ValidationClientConfig) Enabled() bool { return c.RedisURL != "" } -var DefaultRedisValidationClientConfig = RedisValidationClientConfig{ +var DefaultValidationClientConfig = ValidationClientConfig{ Name: "redis validation client", Room: 2, RedisURL: "", ProducerConfig: pubsub.DefaultProducerConfig, } -var TestRedisValidationClientConfig = RedisValidationClientConfig{ +var TestValidationClientConfig = ValidationClientConfig{ Name: "test redis validation client", Room: 2, RedisURL: "", ProducerConfig: pubsub.TestProducerConfig, } -func RedisValidationClientConfigAddOptions(prefix string, f *pflag.FlagSet) { - f.String(prefix+".name", DefaultRedisValidationClientConfig.Name, "validation client name") - f.Int32(prefix+".room", DefaultRedisValidationClientConfig.Room, "validation client room") +func ValidationClientConfigAddOptions(prefix string, f *pflag.FlagSet) { + f.String(prefix+".name", DefaultValidationClientConfig.Name, "validation client name") + f.Int32(prefix+".room", DefaultValidationClientConfig.Room, "validation client room") pubsub.ProducerAddConfigAddOptions(prefix+".producer-config", f) } -// RedisValidationClient implements validation client through redis streams. -type RedisValidationClient struct { +// ValidationClient implements validation client through redis streams. +type ValidationClient struct { stopwaiter.StopWaiter name string room int32 @@ -60,7 +60,7 @@ type RedisValidationClient struct { redisClient redis.UniversalClient } -func NewRedisValidationClient(cfg *RedisValidationClientConfig) (*RedisValidationClient, error) { +func NewValidationClient(cfg *ValidationClientConfig) (*ValidationClient, error) { if cfg.RedisURL == "" { return nil, fmt.Errorf("redis url cannot be empty") } @@ -68,7 +68,7 @@ func NewRedisValidationClient(cfg *RedisValidationClientConfig) (*RedisValidatio if err != nil { return nil, err } - return &RedisValidationClient{ + return &ValidationClient{ name: cfg.Name, room: cfg.Room, producers: make(map[common.Hash]*pubsub.Producer[*validator.ValidationInput, validator.GoGlobalState]), @@ -77,7 +77,7 @@ func NewRedisValidationClient(cfg *RedisValidationClientConfig) (*RedisValidatio }, nil } -func (c *RedisValidationClient) Initialize(moduleRoots []common.Hash) error { +func (c *ValidationClient) Initialize(moduleRoots []common.Hash) error { for _, mr := range moduleRoots { if _, exists := c.producers[mr]; exists { log.Warn("Producer already existsw for module root", "hash", mr) @@ -94,7 +94,7 @@ func (c *RedisValidationClient) Initialize(moduleRoots []common.Hash) error { return nil } -func (c *RedisValidationClient) Launch(entry *validator.ValidationInput, moduleRoot common.Hash) validator.ValidationRun { +func (c *ValidationClient) Launch(entry *validator.ValidationInput, moduleRoot common.Hash) validator.ValidationRun { atomic.AddInt32(&c.room, -1) defer atomic.AddInt32(&c.room, 1) producer, found := c.producers[moduleRoot] @@ -110,7 +110,7 @@ func (c *RedisValidationClient) Launch(entry *validator.ValidationInput, moduleR return server_common.NewValRun(promise, moduleRoot) } -func (c *RedisValidationClient) Start(ctx_in context.Context) error { +func (c *ValidationClient) Start(ctx_in context.Context) error { for _, p := range c.producers { p.Start(ctx_in) } @@ -118,20 +118,20 @@ func (c *RedisValidationClient) Start(ctx_in context.Context) error { return nil } -func (c *RedisValidationClient) Stop() { +func (c *ValidationClient) Stop() { for _, p := range c.producers { p.StopAndWait() } c.StopWaiter.StopAndWait() } -func (c *RedisValidationClient) Name() string { +func (c *ValidationClient) Name() string { if c.Started() { return c.name } return "(not started)" } -func (c *RedisValidationClient) Room() int { +func (c *ValidationClient) Room() int { return int(c.room) } diff --git a/validator/server_api/json.go b/validator/server_api/json.go index e1729b53aa..8c80768b14 100644 --- a/validator/server_api/json.go +++ b/validator/server_api/json.go @@ -8,10 +8,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/offchainlabs/nitro/arbutil" - "github.com/offchainlabs/nitro/pubsub" "github.com/offchainlabs/nitro/util/jsonapi" "github.com/offchainlabs/nitro/validator" - "github.com/spf13/pflag" ) const Namespace string = "validation" @@ -65,27 +63,3 @@ type BatchInfoJson struct { Number uint64 DataB64 string } - -type RedisValidationServerConfig struct { - RedisURL string `koanf:"redis-url"` - ConsumerConfig pubsub.ConsumerConfig `koanf:"consumer-config"` - // Supported wasm module roots. - ModuleRoots []string `koanf:"module-roots"` -} - -var DefaultRedisValidationServerConfig = RedisValidationServerConfig{ - RedisURL: "", - ConsumerConfig: pubsub.DefaultConsumerConfig, - ModuleRoots: []string{}, -} - -var TestRedisValidationServerConfig = RedisValidationServerConfig{ - RedisURL: "", - ConsumerConfig: pubsub.TestConsumerConfig, - ModuleRoots: []string{}, -} - -func RedisValidationServerConfigAddOptions(prefix string, f *pflag.FlagSet) { - pubsub.ConsumerConfigAddOptions(prefix+".consumer-config", f) - f.StringSlice(prefix+".module-roots", nil, "Supported module root hashes") -} diff --git a/validator/server_arb/validator_spawner.go b/validator/server_arb/validator_spawner.go index bc607d1088..e315b6a7fb 100644 --- a/validator/server_arb/validator_spawner.go +++ b/validator/server_arb/validator_spawner.go @@ -11,14 +11,14 @@ import ( "sync/atomic" "time" - flag "github.com/spf13/pflag" + "github.com/spf13/pflag" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/util/containers" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" - "github.com/offchainlabs/nitro/validator/server_api" "github.com/offchainlabs/nitro/validator/server_common" + "github.com/offchainlabs/nitro/validator/valnode/redis" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" @@ -28,11 +28,11 @@ import ( var arbitratorValidationSteps = metrics.NewRegisteredHistogram("arbitrator/validation/steps", nil, metrics.NewBoundedHistogramSample()) type ArbitratorSpawnerConfig struct { - Workers int `koanf:"workers" reload:"hot"` - OutputPath string `koanf:"output-path" reload:"hot"` - Execution MachineCacheConfig `koanf:"execution" reload:"hot"` // hot reloading for new executions only - ExecutionRunTimeout time.Duration `koanf:"execution-run-timeout" reload:"hot"` - RedisValidationServerConfig server_api.RedisValidationServerConfig `koanf:"redis-validation-server-config"` + Workers int `koanf:"workers" reload:"hot"` + OutputPath string `koanf:"output-path" reload:"hot"` + Execution MachineCacheConfig `koanf:"execution" reload:"hot"` // hot reloading for new executions only + ExecutionRunTimeout time.Duration `koanf:"execution-run-timeout" reload:"hot"` + RedisValidationServerConfig redis.ValidationServerConfig `koanf:"redis-validation-server-config"` } type ArbitratorSpawnerConfigFecher func() *ArbitratorSpawnerConfig @@ -42,15 +42,15 @@ var DefaultArbitratorSpawnerConfig = ArbitratorSpawnerConfig{ OutputPath: "./target/output", Execution: DefaultMachineCacheConfig, ExecutionRunTimeout: time.Minute * 15, - RedisValidationServerConfig: server_api.DefaultRedisValidationServerConfig, + RedisValidationServerConfig: redis.DefaultValidationServerConfig, } -func ArbitratorSpawnerConfigAddOptions(prefix string, f *flag.FlagSet) { +func ArbitratorSpawnerConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Int(prefix+".workers", DefaultArbitratorSpawnerConfig.Workers, "number of concurrent validation threads") f.Duration(prefix+".execution-run-timeout", DefaultArbitratorSpawnerConfig.ExecutionRunTimeout, "timeout before discarding execution run") f.String(prefix+".output-path", DefaultArbitratorSpawnerConfig.OutputPath, "path to write machines to") MachineCacheConfigConfigAddOptions(prefix+".execution", f) - server_api.RedisValidationServerConfigAddOptions(prefix+".redis-validation-server-config", f) + redis.ValidationServerConfigAddOptions(prefix+".redis-validation-server-config", f) } func DefaultArbitratorSpawnerConfigFetcher() *ArbitratorSpawnerConfig { diff --git a/validator/valnode/redisconsumer.go b/validator/valnode/redis/consumer.go similarity index 64% rename from validator/valnode/redisconsumer.go rename to validator/valnode/redis/consumer.go index d90868fb9e..1187474211 100644 --- a/validator/valnode/redisconsumer.go +++ b/validator/valnode/redis/consumer.go @@ -1,4 +1,4 @@ -package valnode +package redis import ( "context" @@ -12,11 +12,12 @@ import ( "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" "github.com/offchainlabs/nitro/validator/server_api" + "github.com/spf13/pflag" ) -// RedisValidationServer implements consumer for the requests originated from +// ValidationServer implements consumer for the requests originated from // RedisValidationClient producers. -type RedisValidationServer struct { +type ValidationServer struct { stopwaiter.StopWaiter spawner validator.ValidationSpawner @@ -24,7 +25,7 @@ type RedisValidationServer struct { consumers map[common.Hash]*pubsub.Consumer[*validator.ValidationInput, validator.GoGlobalState] } -func NewRedisValidationServer(cfg *server_api.RedisValidationServerConfig, spawner validator.ValidationSpawner) (*RedisValidationServer, error) { +func NewValidationServer(cfg *ValidationServerConfig, spawner validator.ValidationSpawner) (*ValidationServer, error) { if cfg.RedisURL == "" { return nil, fmt.Errorf("redis url cannot be empty") } @@ -41,13 +42,13 @@ func NewRedisValidationServer(cfg *server_api.RedisValidationServerConfig, spawn } consumers[mr] = c } - return &RedisValidationServer{ + return &ValidationServer{ consumers: consumers, spawner: spawner, }, nil } -func (s *RedisValidationServer) Start(ctx_in context.Context) { +func (s *ValidationServer) Start(ctx_in context.Context) { s.StopWaiter.Start(ctx_in, s) for moduleRoot, c := range s.consumers { c := c @@ -76,3 +77,27 @@ func (s *RedisValidationServer) Start(ctx_in context.Context) { }) } } + +type ValidationServerConfig struct { + RedisURL string `koanf:"redis-url"` + ConsumerConfig pubsub.ConsumerConfig `koanf:"consumer-config"` + // Supported wasm module roots. + ModuleRoots []string `koanf:"module-roots"` +} + +var DefaultValidationServerConfig = ValidationServerConfig{ + RedisURL: "", + ConsumerConfig: pubsub.DefaultConsumerConfig, + ModuleRoots: []string{}, +} + +var TestValidationServerConfig = ValidationServerConfig{ + RedisURL: "", + ConsumerConfig: pubsub.TestConsumerConfig, + ModuleRoots: []string{}, +} + +func ValidationServerConfigAddOptions(prefix string, f *pflag.FlagSet) { + pubsub.ConsumerConfigAddOptions(prefix+".consumer-config", f) + f.StringSlice(prefix+".module-roots", nil, "Supported module root hashes") +} diff --git a/validator/valnode/valnode.go b/validator/valnode/valnode.go index bbb680087a..fab4531cba 100644 --- a/validator/valnode/valnode.go +++ b/validator/valnode/valnode.go @@ -8,12 +8,12 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rpc" - flag "github.com/spf13/pflag" - "github.com/offchainlabs/nitro/validator/server_api" "github.com/offchainlabs/nitro/validator/server_arb" "github.com/offchainlabs/nitro/validator/server_common" "github.com/offchainlabs/nitro/validator/server_jit" + "github.com/offchainlabs/nitro/validator/valnode/redis" + "github.com/spf13/pflag" ) type WasmConfig struct { @@ -22,7 +22,7 @@ type WasmConfig struct { AllowedWasmModuleRoots []string `koanf:"allowed-wasm-module-roots"` } -func WasmConfigAddOptions(prefix string, f *flag.FlagSet) { +func WasmConfigAddOptions(prefix string, f *pflag.FlagSet) { f.String(prefix+".root-path", DefaultWasmConfig.RootPath, "path to machine folders, each containing wasm files (machine.wavm.br, replay.wasm)") f.Bool(prefix+".enable-wasmroots-check", DefaultWasmConfig.EnableWasmrootsCheck, "enable check for compatibility of on-chain WASM module root with node") f.StringSlice(prefix+".allowed-wasm-module-roots", DefaultWasmConfig.AllowedWasmModuleRoots, "list of WASM module roots to check if the on-chain WASM module root belongs to on node startup") @@ -63,7 +63,7 @@ var TestValidationConfig = Config{ Wasm: DefaultWasmConfig, } -func ValidationConfigAddOptions(prefix string, f *flag.FlagSet) { +func ValidationConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".use-jit", DefaultValidationConfig.UseJit, "use jit for validation") f.Bool(prefix+".api-auth", DefaultValidationConfig.ApiAuth, "validate is an authenticated API") f.Bool(prefix+".api-public", DefaultValidationConfig.ApiPublic, "validate is a public API") @@ -77,7 +77,7 @@ type ValidationNode struct { arbSpawner *server_arb.ArbitratorSpawner jitSpawner *server_jit.JitSpawner - redisConsumer *RedisValidationServer + redisConsumer *redis.ValidationServer } func EnsureValidationExposedViaAuthRPC(stackConf *node.Config) { @@ -119,7 +119,7 @@ func CreateValidationNode(configFetcher ValidationConfigFetcher, stack *node.Nod } else { serverAPI = NewExecutionServerAPI(arbSpawner, arbSpawner, arbConfigFetcher) } - redisConsumer, err := NewRedisValidationServer(&arbConfigFetcher().RedisValidationServerConfig, arbSpawner) + redisConsumer, err := redis.NewValidationServer(&arbConfigFetcher().RedisValidationServerConfig, arbSpawner) if err != nil { log.Error("Creating new redis validation server", "error", err) } From c35284e7daf707e7b669002f13c72e772b4fbd87 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Tue, 23 Apr 2024 13:10:03 +0200 Subject: [PATCH 26/41] Implement reading all wasm module roots in machine locator --- validator/server_common/machine_locator.go | 82 ++++++++++++++----- .../server_common/machine_locator_test.go | 36 ++++++++ .../module-root.txt | 1 + .../module-root.txt | 1 + .../module-root.txt | 1 + validator/server_common/testdata/latest | 1 + 6 files changed, 101 insertions(+), 21 deletions(-) create mode 100644 validator/server_common/machine_locator_test.go create mode 100644 validator/server_common/testdata/0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4/module-root.txt create mode 100644 validator/server_common/testdata/0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4/module-root.txt create mode 100644 validator/server_common/testdata/0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a/module-root.txt create mode 120000 validator/server_common/testdata/latest diff --git a/validator/server_common/machine_locator.go b/validator/server_common/machine_locator.go index 4c25448dda..da9767a505 100644 --- a/validator/server_common/machine_locator.go +++ b/validator/server_common/machine_locator.go @@ -8,21 +8,20 @@ import ( "strings" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" ) type MachineLocator struct { - rootPath string - latest common.Hash + rootPath string + latest common.Hash + moduleRoots []common.Hash } var ErrMachineNotFound = errors.New("machine not found") func NewMachineLocator(rootPath string) (*MachineLocator, error) { - var places []string - - if rootPath != "" { - places = append(places, rootPath) - } else { + dirs := []string{rootPath} + if rootPath == "" { // Check the project dir: /arbnode/node.go => ../../target/machines _, thisFile, _, ok := runtime.Caller(0) if !ok { @@ -30,7 +29,7 @@ func NewMachineLocator(rootPath string) (*MachineLocator, error) { } projectDir := filepath.Dir(filepath.Dir(filepath.Dir(thisFile))) projectPath := filepath.Join(filepath.Join(projectDir, "target"), "machines") - places = append(places, projectPath) + dirs = append(dirs, projectPath) // Check the working directory: ./machines and ./target/machines workDir, err := os.Getwd() @@ -39,8 +38,8 @@ func NewMachineLocator(rootPath string) (*MachineLocator, error) { } workPath1 := filepath.Join(workDir, "machines") workPath2 := filepath.Join(filepath.Join(workDir, "target"), "machines") - places = append(places, workPath1) - places = append(places, workPath2) + dirs = append(dirs, workPath1) + dirs = append(dirs, workPath2) // Check above the executable: => ../../machines execfile, err := os.Executable() @@ -48,22 +47,59 @@ func NewMachineLocator(rootPath string) (*MachineLocator, error) { return nil, err } execPath := filepath.Join(filepath.Dir(filepath.Dir(execfile)), "machines") - places = append(places, execPath) + dirs = append(dirs, execPath) } - for _, place := range places { - if _, err := os.Stat(place); err == nil { - var latestModuleRoot common.Hash - latestModuleRootPath := filepath.Join(place, "latest", "module-root.txt") - fileBytes, err := os.ReadFile(latestModuleRootPath) - if err == nil { - s := strings.TrimSpace(string(fileBytes)) - latestModuleRoot = common.HexToHash(s) + var ( + moduleRoots = make(map[common.Hash]bool) + latestModuleRoot common.Hash + ) + + for _, dir := range dirs { + fInfo, err := os.Stat(dir) + if err != nil { + log.Warn("Getting file info", "error", err) + continue + } + if !fInfo.IsDir() { + // Skip files that are not directories. + continue + } + files, err := os.ReadDir(dir) + if err != nil { + log.Warn("Reading directory", "dir", dir, "error", err) + } + for _, file := range files { + mrFile := filepath.Join(dir, file.Name(), "module-root.txt") + if _, err := os.Stat(mrFile); errors.Is(err, os.ErrNotExist) { + // Skip if module-roots file does not exist. + continue + } + mrContent, err := os.ReadFile(mrFile) + if err != nil { + log.Warn("Reading module roots file", "file path", mrFile, "error", err) + continue + } + moduleRoot := common.HexToHash(strings.TrimSpace(string(mrContent))) + if moduleRoot == (common.Hash{}) { + log.Warn("Malformed module root hash in module-root file", "hash", string(mrContent)) + continue + } + moduleRoots[moduleRoot] = true + if file.Name() == "latest" { + latestModuleRoot = moduleRoot } - return &MachineLocator{place, latestModuleRoot}, nil } } - return nil, ErrMachineNotFound + var roots []common.Hash + for k := range moduleRoots { + roots = append(roots, k) + } + return &MachineLocator{ + rootPath: rootPath, + latest: latestModuleRoot, + moduleRoots: roots, + }, nil } func (l MachineLocator) GetMachinePath(moduleRoot common.Hash) string { @@ -81,3 +117,7 @@ func (l MachineLocator) LatestWasmModuleRoot() common.Hash { func (l MachineLocator) RootPath() string { return l.rootPath } + +func (l MachineLocator) ModuleRoots() []common.Hash { + return l.moduleRoots +} diff --git a/validator/server_common/machine_locator_test.go b/validator/server_common/machine_locator_test.go new file mode 100644 index 0000000000..7c1575871c --- /dev/null +++ b/validator/server_common/machine_locator_test.go @@ -0,0 +1,36 @@ +package server_common + +import ( + "sort" + "testing" + + "github.com/google/go-cmp/cmp" +) + +var ( + wantLatestModuleRoot = "0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a" + wantModuleRoots = []string{ + "0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4", + "0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4", + "0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a", + } +) + +func TestNewMachineLocator(t *testing.T) { + ml, err := NewMachineLocator("testdata") + if err != nil { + t.Fatalf("Error creating new machine locator: %v", err) + } + if ml.latest.Hex() != wantLatestModuleRoot { + t.Errorf("NewMachineLocator() got latestModuleRoot: %v, want: %v", ml.latest, wantLatestModuleRoot) + } + var got []string + for _, s := range ml.ModuleRoots() { + got = append(got, s.Hex()) + } + sort.Strings(got) + sort.Strings(wantModuleRoots) + if diff := cmp.Diff(got, wantModuleRoots); diff != "" { + t.Errorf("NewMachineLocator() unexpected diff (-want +got):\n%s", diff) + } +} diff --git a/validator/server_common/testdata/0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4/module-root.txt b/validator/server_common/testdata/0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4/module-root.txt new file mode 100644 index 0000000000..067f2db9f5 --- /dev/null +++ b/validator/server_common/testdata/0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4/module-root.txt @@ -0,0 +1 @@ +0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4 diff --git a/validator/server_common/testdata/0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4/module-root.txt b/validator/server_common/testdata/0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4/module-root.txt new file mode 100644 index 0000000000..ad3a905ab7 --- /dev/null +++ b/validator/server_common/testdata/0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4/module-root.txt @@ -0,0 +1 @@ +0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4 diff --git a/validator/server_common/testdata/0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a/module-root.txt b/validator/server_common/testdata/0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a/module-root.txt new file mode 100644 index 0000000000..1a359ae1cd --- /dev/null +++ b/validator/server_common/testdata/0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a/module-root.txt @@ -0,0 +1 @@ +0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a diff --git a/validator/server_common/testdata/latest b/validator/server_common/testdata/latest new file mode 120000 index 0000000000..42d98792a0 --- /dev/null +++ b/validator/server_common/testdata/latest @@ -0,0 +1 @@ +0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a \ No newline at end of file From 11871b1b59bab35dbd5ebfb5e9defeeae87f67d1 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Tue, 23 Apr 2024 17:30:52 +0200 Subject: [PATCH 27/41] Allow rootHash with default value --- validator/server_common/machine_locator.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/validator/server_common/machine_locator.go b/validator/server_common/machine_locator.go index da9767a505..539189fa91 100644 --- a/validator/server_common/machine_locator.go +++ b/validator/server_common/machine_locator.go @@ -81,10 +81,6 @@ func NewMachineLocator(rootPath string) (*MachineLocator, error) { continue } moduleRoot := common.HexToHash(strings.TrimSpace(string(mrContent))) - if moduleRoot == (common.Hash{}) { - log.Warn("Malformed module root hash in module-root file", "hash", string(mrContent)) - continue - } moduleRoots[moduleRoot] = true if file.Name() == "latest" { latestModuleRoot = moduleRoot From 0303869facff20471b8594c804751aedba918e87 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Tue, 23 Apr 2024 17:55:04 +0200 Subject: [PATCH 28/41] Set corret root path dir --- validator/server_arb/nitro_machine.go | 1 + validator/server_common/machine_locator.go | 1 + 2 files changed, 2 insertions(+) diff --git a/validator/server_arb/nitro_machine.go b/validator/server_arb/nitro_machine.go index acaf3b10e6..d8a0363938 100644 --- a/validator/server_arb/nitro_machine.go +++ b/validator/server_arb/nitro_machine.go @@ -23,6 +23,7 @@ import ( ) func createArbMachine(ctx context.Context, locator *server_common.MachineLocator, config *ArbitratorMachineConfig, moduleRoot common.Hash) (*arbMachines, error) { + fmt.Errorf("anodar moduleRoot: %v", moduleRoot) binPath := filepath.Join(locator.GetMachinePath(moduleRoot), config.WavmBinaryPath) cBinPath := C.CString(binPath) defer C.free(unsafe.Pointer(cBinPath)) diff --git a/validator/server_common/machine_locator.go b/validator/server_common/machine_locator.go index 539189fa91..ac00c40dea 100644 --- a/validator/server_common/machine_locator.go +++ b/validator/server_common/machine_locator.go @@ -84,6 +84,7 @@ func NewMachineLocator(rootPath string) (*MachineLocator, error) { moduleRoots[moduleRoot] = true if file.Name() == "latest" { latestModuleRoot = moduleRoot + rootPath = dir } } } From 72399c18c559bb4b8813a180b9e083b7dafc797e Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Tue, 23 Apr 2024 18:04:59 +0200 Subject: [PATCH 29/41] Drop remnant logging --- validator/server_arb/nitro_machine.go | 1 - 1 file changed, 1 deletion(-) diff --git a/validator/server_arb/nitro_machine.go b/validator/server_arb/nitro_machine.go index d8a0363938..acaf3b10e6 100644 --- a/validator/server_arb/nitro_machine.go +++ b/validator/server_arb/nitro_machine.go @@ -23,7 +23,6 @@ import ( ) func createArbMachine(ctx context.Context, locator *server_common.MachineLocator, config *ArbitratorMachineConfig, moduleRoot common.Hash) (*arbMachines, error) { - fmt.Errorf("anodar moduleRoot: %v", moduleRoot) binPath := filepath.Join(locator.GetMachinePath(moduleRoot), config.WavmBinaryPath) cBinPath := C.CString(binPath) defer C.free(unsafe.Pointer(cBinPath)) From db2eaf0340ff7e5f158254b294bd87c744f7afd3 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Tue, 23 Apr 2024 16:59:00 -0600 Subject: [PATCH 30/41] valnode: only start redis validation if enabled --- validator/valnode/redis/consumer.go | 4 ++++ validator/valnode/valnode.go | 10 +++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/validator/valnode/redis/consumer.go b/validator/valnode/redis/consumer.go index 1187474211..1cadaf7c9a 100644 --- a/validator/valnode/redis/consumer.go +++ b/validator/valnode/redis/consumer.go @@ -101,3 +101,7 @@ func ValidationServerConfigAddOptions(prefix string, f *pflag.FlagSet) { pubsub.ConsumerConfigAddOptions(prefix+".consumer-config", f) f.StringSlice(prefix+".module-roots", nil, "Supported module root hashes") } + +func (cfg *ValidationServerConfig) Enabled() bool { + return cfg.RedisURL != "" +} diff --git a/validator/valnode/valnode.go b/validator/valnode/valnode.go index fab4531cba..93a5b37238 100644 --- a/validator/valnode/valnode.go +++ b/validator/valnode/valnode.go @@ -119,9 +119,13 @@ func CreateValidationNode(configFetcher ValidationConfigFetcher, stack *node.Nod } else { serverAPI = NewExecutionServerAPI(arbSpawner, arbSpawner, arbConfigFetcher) } - redisConsumer, err := redis.NewValidationServer(&arbConfigFetcher().RedisValidationServerConfig, arbSpawner) - if err != nil { - log.Error("Creating new redis validation server", "error", err) + var redisConsumer *redis.ValidationServer + redisValidationConfig := arbConfigFetcher().RedisValidationServerConfig + if redisValidationConfig.Enabled() { + redisConsumer, err = redis.NewValidationServer(&redisValidationConfig, arbSpawner) + if err != nil { + log.Error("Creating new redis validation server", "error", err) + } } valAPIs := []rpc.API{{ Namespace: server_api.Namespace, From 58fd8bb994b25d52adf54961242e6ad93c032f1a Mon Sep 17 00:00:00 2001 From: Tuckson <105675159+TucksonDev@users.noreply.github.com> Date: Wed, 24 Apr 2024 12:40:23 +0100 Subject: [PATCH 31/41] chore: update ArbGasInfo precompile comments This PR updates the comment for ArbGasInfo.GetPricingInertia() and adds some missing comments. All new comments are taken from the nitro-contracts sol [interface file](https://github.com/OffchainLabs/nitro-contracts/blob/9a6bfad2363322099d399698751551ff044c7a72/src/precompiles/ArbGasInfo.sol) --- precompiles/ArbGasInfo.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/precompiles/ArbGasInfo.go b/precompiles/ArbGasInfo.go index cb0045c49f..ccf97857b8 100644 --- a/precompiles/ArbGasInfo.go +++ b/precompiles/ArbGasInfo.go @@ -187,7 +187,7 @@ func (con ArbGasInfo) GetGasBacklog(c ctx, evm mech) (uint64, error) { return c.State.L2PricingState().GasBacklog() } -// GetPricingInertia gets the L2 basefee in response to backlogged gas +// GetPricingInertia gets how slowly ArbOS updates the L2 basefee in response to backlogged gas func (con ArbGasInfo) GetPricingInertia(c ctx, evm mech) (uint64, error) { return c.State.L2PricingState().PricingInertia() } @@ -197,6 +197,7 @@ func (con ArbGasInfo) GetGasBacklogTolerance(c ctx, evm mech) (uint64, error) { return c.State.L2PricingState().BacklogTolerance() } +// GetL1PricingSurplus gets the surplus of funds for L1 batch posting payments (may be negative) func (con ArbGasInfo) GetL1PricingSurplus(c ctx, evm mech) (*big.Int, error) { if c.State.ArbOSVersion() < 10 { return con._preversion10_GetL1PricingSurplus(c, evm) @@ -220,34 +221,42 @@ func (con ArbGasInfo) _preversion10_GetL1PricingSurplus(c ctx, evm mech) (*big.I return arbmath.BigSub(haveFunds, needFunds), nil } +// GetPerBatchGasCharge gets the base charge (in L1 gas) attributed to each data batch in the calldata pricer func (con ArbGasInfo) GetPerBatchGasCharge(c ctx, evm mech) (int64, error) { return c.State.L1PricingState().PerBatchGasCost() } +// GetAmortizedCostCapBips gets the cost amortization cap in basis points func (con ArbGasInfo) GetAmortizedCostCapBips(c ctx, evm mech) (uint64, error) { return c.State.L1PricingState().AmortizedCostCapBips() } +// GetL1FeesAvailable gets the available funds from L1 fees func (con ArbGasInfo) GetL1FeesAvailable(c ctx, evm mech) (huge, error) { return c.State.L1PricingState().L1FeesAvailable() } +// GetL1PricingEquilibrationUnits gets the equilibration units parameter for L1 price adjustment algorithm func (con ArbGasInfo) GetL1PricingEquilibrationUnits(c ctx, evm mech) (*big.Int, error) { return c.State.L1PricingState().EquilibrationUnits() } +// GetLastL1PricingUpdateTime gets the last time the L1 calldata pricer was updated func (con ArbGasInfo) GetLastL1PricingUpdateTime(c ctx, evm mech) (uint64, error) { return c.State.L1PricingState().LastUpdateTime() } +// GetL1PricingFundsDueForRewards gets the amount of L1 calldata payments due for rewards (per the L1 reward rate) func (con ArbGasInfo) GetL1PricingFundsDueForRewards(c ctx, evm mech) (*big.Int, error) { return c.State.L1PricingState().FundsDueForRewards() } +// GetL1PricingUnitsSinceUpdate gets the amount of L1 calldata posted since the last update func (con ArbGasInfo) GetL1PricingUnitsSinceUpdate(c ctx, evm mech) (uint64, error) { return c.State.L1PricingState().UnitsSinceUpdate() } +// GetLastL1PricingSurplus gets the L1 pricing surplus as of the last update (may be negative) func (con ArbGasInfo) GetLastL1PricingSurplus(c ctx, evm mech) (*big.Int, error) { return c.State.L1PricingState().LastSurplus() } From 555b8abe664eae430df547a0cbc640f749fca033 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Wed, 24 Apr 2024 09:44:57 -0700 Subject: [PATCH 32/41] update geth pin to latest --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index 018bd54e2e..9317fb4911 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 018bd54e2ecdf494dce8f59e29cc083af9bdd74c +Subproject commit 9317fb4911ce83b330a1f27b976e0d991d329fa0 From 213993102fb1d84e466e4f0a5144d840c305b5d1 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 24 Apr 2024 15:21:43 -0500 Subject: [PATCH 33/41] Fix PrecompiledAddressesArbOS30 --- gethhook/geth-hook.go | 1 + 1 file changed, 1 insertion(+) diff --git a/gethhook/geth-hook.go b/gethhook/geth-hook.go index fa41edd179..4f4ed66235 100644 --- a/gethhook/geth-hook.go +++ b/gethhook/geth-hook.go @@ -69,6 +69,7 @@ func init() { for addr, precompile := range vm.PrecompiledContractsArbitrum { vm.PrecompiledContractsArbOS30[addr] = precompile + vm.PrecompiledAddressesArbOS30 = append(vm.PrecompiledAddressesArbOS30, addr) } for addr, precompile := range vm.PrecompiledContractsP256Verify { vm.PrecompiledContractsArbOS30[addr] = precompile From 66a2454120629897be518d8ccb223e33ed0eb348 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 24 Apr 2024 15:58:17 -0500 Subject: [PATCH 34/41] Clean up retryable escrow accounts in ArbOS 30 --- arbos/arbosState/arbosstate.go | 13 +++++++++++++ arbos/tx_processor.go | 2 ++ arbos/util/transfer.go | 4 ++++ system_tests/common_test.go | 7 +++++++ system_tests/retryable_test.go | 27 +++++++++++++++++++-------- 5 files changed, 45 insertions(+), 8 deletions(-) diff --git a/arbos/arbosState/arbosstate.go b/arbos/arbosState/arbosstate.go index 9e3b90532e..b753797822 100644 --- a/arbos/arbosState/arbosstate.go +++ b/arbos/arbosState/arbosstate.go @@ -320,9 +320,22 @@ func (state *ArbosState) UpgradeArbosVersion( case 20: // Update Brotli compression level for fast compression from 0 to 1 ensure(state.SetBrotliCompressionLevel(1)) + // ArbOS versions 21 through 29 are left to Orbit chains for custom upgrades. + case 30: + if !chainConfig.DebugMode() { + // This upgrade isn't finalized so we only want to support it for testing + return fmt.Errorf( + "the chain is upgrading to unsupported ArbOS version %v, %w", + nextArbosVersion, + ErrFatalNodeOutOfDate, + ) + } + // no state changes needed default: if nextArbosVersion >= 12 && nextArbosVersion <= 19 { // ArbOS versions 12 through 19 are left to Orbit chains for custom upgrades. + } else if nextArbosVersion >= 21 && nextArbosVersion <= 29 { + // ArbOS versions 21 through 29 are left to Orbit chains for custom upgrades. } else { return fmt.Errorf( "the chain is upgrading to unsupported ArbOS version %v, %w", diff --git a/arbos/tx_processor.go b/arbos/tx_processor.go index 569edb7c63..06ea51bcb9 100644 --- a/arbos/tx_processor.go +++ b/arbos/tx_processor.go @@ -143,6 +143,8 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r // We intentionally use the variant here that doesn't do tracing, // because this transfer is represented as the outer eth transaction. // This transfer is necessary because we don't actually invoke the EVM. + // Since MintBalance already called AddBalance on `from`, + // we don't have EIP-161 concerns around not touching `from`. core.Transfer(evm.StateDB, from, *to, value) return true, 0, nil, nil case *types.ArbitrumInternalTx: diff --git a/arbos/util/transfer.go b/arbos/util/transfer.go index 3a81181200..dd6a0807db 100644 --- a/arbos/util/transfer.go +++ b/arbos/util/transfer.go @@ -33,6 +33,10 @@ func TransferBalance( return fmt.Errorf("%w: addr %v have %v want %v", vm.ErrInsufficientBalance, *from, balance, amount) } evm.StateDB.SubBalance(*from, amount) + if evm.Context.ArbOSVersion >= 30 { + // ensure the from account is "touched" for EIP-161 + evm.StateDB.AddBalance(*from, common.Big0) + } } if to != nil { evm.StateDB.AddBalance(*to, amount) diff --git a/system_tests/common_test.go b/system_tests/common_test.go index a0bed2785d..af9cac2801 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -186,6 +186,13 @@ func (b *NodeBuilder) DefaultConfig(t *testing.T, withL1 bool) *NodeBuilder { return b } +func (b *NodeBuilder) WithArbOSVersion(arbosVersion uint64) *NodeBuilder { + newChainConfig := *b.chainConfig + newChainConfig.ArbitrumChainParams.InitialArbOSVersion = arbosVersion + b.chainConfig = &newChainConfig + return b +} + func (b *NodeBuilder) Build(t *testing.T) func() { if b.execConfig.RPC.MaxRecreateStateDepth == arbitrum.UninitializedMaxRecreateStateDepth { if b.execConfig.Caching.Archive { diff --git a/system_tests/retryable_test.go b/system_tests/retryable_test.go index b0691db173..f7c7cec54c 100644 --- a/system_tests/retryable_test.go +++ b/system_tests/retryable_test.go @@ -31,7 +31,7 @@ import ( "github.com/offchainlabs/nitro/util/colors" ) -func retryableSetup(t *testing.T) ( +func retryableSetup(t *testing.T, modifyNodeConfig ...func(*NodeBuilder)) ( *NodeBuilder, *bridgegen.Inbox, func(*types.Receipt) *types.Transaction, @@ -40,6 +40,9 @@ func retryableSetup(t *testing.T) ( ) { ctx, cancel := context.WithCancel(context.Background()) builder := NewNodeBuilder(ctx).DefaultConfig(t, true) + for _, f := range modifyNodeConfig { + f(builder) + } builder.Build(t) builder.L2Info.GenerateAccount("User2") @@ -200,9 +203,11 @@ func TestSubmitRetryableImmediateSuccess(t *testing.T) { } } -func TestSubmitRetryableEmptyEscrow(t *testing.T) { +func testSubmitRetryableEmptyEscrow(t *testing.T, arbosVersion uint64) { t.Parallel() - builder, delayedInbox, lookupL2Tx, ctx, teardown := retryableSetup(t) + builder, delayedInbox, lookupL2Tx, ctx, teardown := retryableSetup(t, func(builder *NodeBuilder) { + builder.WithArbOSVersion(arbosVersion) + }) defer teardown() user2Address := builder.L2Info.GetAddress("User2") @@ -273,14 +278,20 @@ func TestSubmitRetryableEmptyEscrow(t *testing.T) { escrowAccount := retryables.RetryableEscrowAddress(l2Tx.Hash()) state, err := builder.L2.ExecNode.ArbInterface.BlockChain().State() Require(t, err) - escrowCodeHash := state.GetCodeHash(escrowAccount) - if escrowCodeHash == (common.Hash{}) { - Fatal(t, "Escrow account deleted (or not created)") - } else if escrowCodeHash != types.EmptyCodeHash { - Fatal(t, "Escrow account has unexpected code hash", escrowCodeHash) + escrowExists := state.Exist(escrowAccount) + if escrowExists != (arbosVersion < 30) { + Fatal(t, "Escrow account existance", escrowExists, "doesn't correspond to ArbOS version", arbosVersion) } } +func TestSubmitRetryableEmptyEscrowArbOS20(t *testing.T) { + testSubmitRetryableEmptyEscrow(t, 20) +} + +func TestSubmitRetryableEmptyEscrowArbOS30(t *testing.T) { + testSubmitRetryableEmptyEscrow(t, 30) +} + func TestSubmitRetryableFailThenRetry(t *testing.T) { t.Parallel() builder, delayedInbox, lookupL2Tx, ctx, teardown := retryableSetup(t) From 8dc2806e87dca5143c22635254d9ce422f096d60 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 24 Apr 2024 18:07:42 -0500 Subject: [PATCH 35/41] Pull in geth fix for stopping the flat call tracer --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index daccadb06c..73a00015ac 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit daccadb06c7bd9ad7e86c74f33ea39d897f0ece4 +Subproject commit 73a00015ac5e4c856f10167226823cd355897832 From 634495e809549f58d93452fe6ca4d60df5e06060 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 24 Apr 2024 18:21:31 -0500 Subject: [PATCH 36/41] Bump pin to geth master --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index 73a00015ac..19f8227480 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 73a00015ac5e4c856f10167226823cd355897832 +Subproject commit 19f82274804e2e21fbbb3379a02502910413b46c From 25c3bf6efbff5278af78c51b4b3d3836c9794bf5 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Fri, 26 Apr 2024 10:52:47 -0700 Subject: [PATCH 37/41] update geth pin --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index 9317fb4911..5b7a4020d8 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 9317fb4911ce83b330a1f27b976e0d991d329fa0 +Subproject commit 5b7a4020d8ef3d81fe2c645ec66c91cdb8719002 From bc4906e71711df83f0167436f94b7d221cf12275 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Fri, 26 Apr 2024 15:37:05 -0700 Subject: [PATCH 38/41] update geth pin to merge-v1.13.11 --- arbnode/inbox_test.go | 2 +- arbos/arbosState/arbosstate.go | 2 +- arbos/arbosState/initialization_test.go | 2 +- arbos/arbosState/initialize.go | 7 ++++--- arbos/l1pricing/l1PricingOldVersions.go | 21 +++++++++++---------- arbos/l1pricing_test.go | 11 ++++++----- arbos/retryables/retryable.go | 2 +- arbos/tx_processor.go | 7 ++++--- arbos/util/tracing.go | 4 ++-- arbos/util/transfer.go | 9 +++++---- execution/gethexec/tx_pre_checker.go | 2 +- gethhook/geth_test.go | 2 +- go-ethereum | 2 +- go.mod | 2 +- go.sum | 4 +++- precompiles/ArbGasInfo.go | 2 +- precompiles/ArbInfo.go | 2 +- precompiles/ArbOwner.go | 2 +- precompiles/ArbOwner_test.go | 3 ++- system_tests/seqinbox_test.go | 6 +++--- 20 files changed, 51 insertions(+), 43 deletions(-) diff --git a/arbnode/inbox_test.go b/arbnode/inbox_test.go index e979979dea..5c879743a4 100644 --- a/arbnode/inbox_test.go +++ b/arbnode/inbox_test.go @@ -233,7 +233,7 @@ func TestTransactionStreamer(t *testing.T) { Fail(t, "error getting block state", err) } haveBalance := state.GetBalance(acct) - if balance.Cmp(haveBalance) != 0 { + if balance.Cmp(haveBalance.ToBig()) != 0 { t.Error("unexpected balance for account", acct, "; expected", balance, "got", haveBalance) } } diff --git a/arbos/arbosState/arbosstate.go b/arbos/arbosState/arbosstate.go index 9e3b90532e..ac1f5e8662 100644 --- a/arbos/arbosState/arbosstate.go +++ b/arbos/arbosState/arbosstate.go @@ -299,7 +299,7 @@ func (state *ArbosState) UpgradeArbosVersion( case 10: ensure(state.l1PricingState.SetL1FeesAvailable(stateDB.GetBalance( l1pricing.L1PricerFundsPoolAddress, - ))) + ).ToBig())) case 11: // Update the PerBatchGasCost to a more accurate value compared to the old v6 default. ensure(state.l1PricingState.SetPerBatchGasCost(l1pricing.InitialPerBatchGasCostV12)) diff --git a/arbos/arbosState/initialization_test.go b/arbos/arbosState/initialization_test.go index 3de1fc5d38..0ef9cea4c5 100644 --- a/arbos/arbosState/initialization_test.go +++ b/arbos/arbosState/initialization_test.go @@ -151,7 +151,7 @@ func checkAccounts(db *state.StateDB, arbState *ArbosState, accts []statetransfe if db.GetNonce(addr) != acct.Nonce { t.Fatal() } - if db.GetBalance(addr).Cmp(acct.EthBalance) != 0 { + if db.GetBalance(addr).ToBig().Cmp(acct.EthBalance) != 0 { t.Fatal() } if acct.ContractInfo != nil { diff --git a/arbos/arbosState/initialize.go b/arbos/arbosState/initialize.go index 56d8172ee8..486c6ae33a 100644 --- a/arbos/arbosState/initialize.go +++ b/arbos/arbosState/initialize.go @@ -15,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" + "github.com/holiman/uint256" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbos/burn" "github.com/offchainlabs/nitro/arbos/l2pricing" @@ -142,7 +143,7 @@ func InitializeArbosInDatabase(db ethdb.Database, initData statetransfer.InitDat if err != nil { return common.Hash{}, err } - statedb.SetBalance(account.Addr, account.EthBalance) + statedb.SetBalance(account.Addr, uint256.MustFromBig(account.EthBalance)) statedb.SetNonce(account.Addr, account.Nonce) if account.ContractInfo != nil { statedb.SetCode(account.Addr, account.ContractInfo.Code) @@ -173,7 +174,7 @@ func initializeRetryables(statedb *state.StateDB, rs *retryables.RetryableState, return err } if r.Timeout <= currentTimestamp { - statedb.AddBalance(r.Beneficiary, r.Callvalue) + statedb.AddBalance(r.Beneficiary, uint256.MustFromBig(r.Callvalue)) continue } retryablesList = append(retryablesList, r) @@ -192,7 +193,7 @@ func initializeRetryables(statedb *state.StateDB, rs *retryables.RetryableState, addr := r.To to = &addr } - statedb.AddBalance(retryables.RetryableEscrowAddress(r.Id), r.Callvalue) + statedb.AddBalance(retryables.RetryableEscrowAddress(r.Id), uint256.MustFromBig(r.Callvalue)) _, err := rs.CreateRetryable(r.Id, r.Timeout, r.From, to, r.Callvalue, r.Beneficiary, r.Calldata) if err != nil { return err diff --git a/arbos/l1pricing/l1PricingOldVersions.go b/arbos/l1pricing/l1PricingOldVersions.go index 5c6b6ab7d6..821d743e7d 100644 --- a/arbos/l1pricing/l1PricingOldVersions.go +++ b/arbos/l1pricing/l1PricingOldVersions.go @@ -4,12 +4,13 @@ package l1pricing import ( + "math" + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/offchainlabs/nitro/arbos/util" am "github.com/offchainlabs/nitro/util/arbmath" - "math" - "math/big" ) func (ps *L1PricingState) _preversion10_UpdateForBatchPosterSpending( @@ -105,8 +106,8 @@ func (ps *L1PricingState) _preversion10_UpdateForBatchPosterSpending( // pay rewards, as much as possible paymentForRewards := am.BigMulByUint(am.UintToBig(perUnitReward), unitsAllocated) availableFunds := statedb.GetBalance(L1PricerFundsPoolAddress) - if am.BigLessThan(availableFunds, paymentForRewards) { - paymentForRewards = availableFunds + if am.BigLessThan(availableFunds.ToBig(), paymentForRewards) { + paymentForRewards = availableFunds.ToBig() } fundsDueForRewards = am.BigSub(fundsDueForRewards, paymentForRewards) if err := ps.SetFundsDueForRewards(fundsDueForRewards); err != nil { @@ -130,8 +131,8 @@ func (ps *L1PricingState) _preversion10_UpdateForBatchPosterSpending( return err } balanceToTransfer := balanceDueToPoster - if am.BigLessThan(availableFunds, balanceToTransfer) { - balanceToTransfer = availableFunds + if am.BigLessThan(availableFunds.ToBig(), balanceToTransfer) { + balanceToTransfer = availableFunds.ToBig() } if balanceToTransfer.Sign() > 0 { addrToPay, err := posterState.PayTo() @@ -166,7 +167,7 @@ func (ps *L1PricingState) _preversion10_UpdateForBatchPosterSpending( if err != nil { return err } - surplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress), am.BigAdd(totalFundsDue, fundsDueForRewards)) + surplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress).ToBig(), am.BigAdd(totalFundsDue, fundsDueForRewards)) inertia, err := ps.Inertia() if err != nil { @@ -230,7 +231,7 @@ func (ps *L1PricingState) _preVersion2_UpdateForBatchPosterSpending( if err != nil { return err } - oldSurplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress), am.BigAdd(totalFundsDue, fundsDueForRewards)) + oldSurplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress).ToBig(), am.BigAdd(totalFundsDue, fundsDueForRewards)) // compute allocation fraction -- will allocate updateTimeDelta/timeDelta fraction of units and funds to this update lastUpdateTime, err := ps.LastUpdateTime() @@ -280,7 +281,7 @@ func (ps *L1PricingState) _preVersion2_UpdateForBatchPosterSpending( // allocate funds to this update collectedSinceUpdate := statedb.GetBalance(L1PricerFundsPoolAddress) - availableFunds := am.BigDivByUint(am.BigMulByUint(collectedSinceUpdate, allocationNumerator), allocationDenominator) + availableFunds := am.BigDivByUint(am.BigMulByUint(collectedSinceUpdate.ToBig(), allocationNumerator), allocationDenominator) // pay rewards, as much as possible paymentForRewards := am.BigMulByUint(am.UintToBig(perUnitReward), unitsAllocated) @@ -356,7 +357,7 @@ func (ps *L1PricingState) _preVersion2_UpdateForBatchPosterSpending( if err != nil { return err } - surplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress), am.BigAdd(totalFundsDue, fundsDueForRewards)) + surplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress).ToBig(), am.BigAdd(totalFundsDue, fundsDueForRewards)) inertia, err := ps.Inertia() if err != nil { diff --git a/arbos/l1pricing_test.go b/arbos/l1pricing_test.go index b23c1747ab..6e2b1b7eec 100644 --- a/arbos/l1pricing_test.go +++ b/arbos/l1pricing_test.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" + "github.com/holiman/uint256" "github.com/offchainlabs/nitro/arbos/arbosState" "github.com/offchainlabs/nitro/arbos/l1pricing" "github.com/offchainlabs/nitro/arbos/util" @@ -171,7 +172,7 @@ func _testL1PricingFundsDue(t *testing.T, testParams *l1PricingTest, expectedRes // create some fake collection balanceAdded := big.NewInt(int64(testParams.fundsCollectedPerSecond * 3)) unitsAdded := testParams.unitsPerSecond * 3 - evm.StateDB.AddBalance(l1pricing.L1PricerFundsPoolAddress, balanceAdded) + evm.StateDB.AddBalance(l1pricing.L1PricerFundsPoolAddress, uint256.MustFromBig(balanceAdded)) err = l1p.SetL1FeesAvailable(balanceAdded) Require(t, err) err = l1p.SetUnitsSinceUpdate(unitsAdded) @@ -187,7 +188,7 @@ func _testL1PricingFundsDue(t *testing.T, testParams *l1PricingTest, expectedRes ) Require(t, err) rewardRecipientBalance := evm.StateDB.GetBalance(rewardAddress) - if !arbmath.BigEquals(rewardRecipientBalance, expectedResults.rewardRecipientBalance) { + if !arbmath.BigEquals(rewardRecipientBalance.ToBig(), expectedResults.rewardRecipientBalance) { Fail(t, rewardRecipientBalance, expectedResults.rewardRecipientBalance) } unitsRemaining, err := l1p.UnitsSinceUpdate() @@ -196,16 +197,16 @@ func _testL1PricingFundsDue(t *testing.T, testParams *l1PricingTest, expectedRes Fail(t, unitsRemaining, expectedResults.unitsRemaining) } fundsReceived := evm.StateDB.GetBalance(firstPayTo) - if !arbmath.BigEquals(fundsReceived, expectedResults.fundsReceived) { + if !arbmath.BigEquals(fundsReceived.ToBig(), expectedResults.fundsReceived) { Fail(t, fundsReceived, expectedResults.fundsReceived) } fundsStillHeld := evm.StateDB.GetBalance(l1pricing.L1PricerFundsPoolAddress) - if !arbmath.BigEquals(fundsStillHeld, expectedResults.fundsStillHeld) { + if !arbmath.BigEquals(fundsStillHeld.ToBig(), expectedResults.fundsStillHeld) { Fail(t, fundsStillHeld, expectedResults.fundsStillHeld) } fundsAvail, err := l1p.L1FeesAvailable() Require(t, err) - if fundsStillHeld.Cmp(fundsAvail) != 0 { + if fundsStillHeld.ToBig().Cmp(fundsAvail) != 0 { Fail(t, fundsStillHeld, fundsAvail) } } diff --git a/arbos/retryables/retryable.go b/arbos/retryables/retryable.go index 6984e41904..e1cfe48bcf 100644 --- a/arbos/retryables/retryable.go +++ b/arbos/retryables/retryable.go @@ -145,7 +145,7 @@ func (rs *RetryableState) DeleteRetryable(id common.Hash, evm *vm.EVM, scenario escrowAddress := RetryableEscrowAddress(id) beneficiaryAddress := common.BytesToAddress(beneficiary[:]) amount := evm.StateDB.GetBalance(escrowAddress) - err = util.TransferBalance(&escrowAddress, &beneficiaryAddress, amount, evm, scenario, "escrow") + err = util.TransferBalance(&escrowAddress, &beneficiaryAddress, amount.ToBig(), evm, scenario, "escrow") if err != nil { return false, err } diff --git a/arbos/tx_processor.go b/arbos/tx_processor.go index 569edb7c63..3a34965834 100644 --- a/arbos/tx_processor.go +++ b/arbos/tx_processor.go @@ -8,6 +8,7 @@ import ( "fmt" "math/big" + "github.com/holiman/uint256" "github.com/offchainlabs/nitro/arbos/l1pricing" "github.com/offchainlabs/nitro/arbos/util" @@ -143,7 +144,7 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r // We intentionally use the variant here that doesn't do tracing, // because this transfer is represented as the outer eth transaction. // This transfer is necessary because we don't actually invoke the EVM. - core.Transfer(evm.StateDB, from, *to, value) + core.Transfer(evm.StateDB, from, *to, uint256.MustFromBig(value)) return true, 0, nil, nil case *types.ArbitrumInternalTx: defer (startTracer())() @@ -172,7 +173,7 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r // check that the user has enough balance to pay for the max submission fee balanceAfterMint := evm.StateDB.GetBalance(tx.From) - if balanceAfterMint.Cmp(tx.MaxSubmissionFee) < 0 { + if balanceAfterMint.ToBig().Cmp(tx.MaxSubmissionFee) < 0 { err := fmt.Errorf( "insufficient funds for max submission fee: address %v have %v want %v", tx.From, balanceAfterMint, tx.MaxSubmissionFee, @@ -256,7 +257,7 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r maxGasCost := arbmath.BigMulByUint(tx.GasFeeCap, usergas) maxFeePerGasTooLow := arbmath.BigLessThan(tx.GasFeeCap, effectiveBaseFee) - if arbmath.BigLessThan(balance, maxGasCost) || usergas < params.TxGas || maxFeePerGasTooLow { + if arbmath.BigLessThan(balance.ToBig(), maxGasCost) || usergas < params.TxGas || maxFeePerGasTooLow { // User either specified too low of a gas fee cap, didn't have enough balance to pay for gas, // or the specified gas limit is below the minimum transaction gas cost. // Either way, attempt to refund the gas costs, since we're not doing the auto-redeem. diff --git a/arbos/util/tracing.go b/arbos/util/tracing.go index e4cde0f42b..49b82d6d64 100644 --- a/arbos/util/tracing.go +++ b/arbos/util/tracing.go @@ -42,7 +42,7 @@ func NewTracingInfo(evm *vm.EVM, from, to common.Address, scenario TracingScenar return &TracingInfo{ Tracer: evm.Config.Tracer, Scenario: scenario, - Contract: vm.NewContract(addressHolder{to}, addressHolder{from}, big.NewInt(0), 0), + Contract: vm.NewContract(addressHolder{to}, addressHolder{from}, uint256.NewInt(0), 0), Depth: evm.Depth(), } } @@ -79,7 +79,7 @@ func (info *TracingInfo) MockCall(input []byte, gas uint64, from, to common.Addr tracer := info.Tracer depth := info.Depth - contract := vm.NewContract(addressHolder{to}, addressHolder{from}, amount, gas) + contract := vm.NewContract(addressHolder{to}, addressHolder{from}, uint256.MustFromBig(amount), gas) scope := &vm.ScopeContext{ Memory: TracingMemoryFromBytes(input), diff --git a/arbos/util/transfer.go b/arbos/util/transfer.go index 3a81181200..919d098d09 100644 --- a/arbos/util/transfer.go +++ b/arbos/util/transfer.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/log" + "github.com/holiman/uint256" "github.com/offchainlabs/nitro/util/arbmath" ) @@ -29,13 +30,13 @@ func TransferBalance( } if from != nil { balance := evm.StateDB.GetBalance(*from) - if arbmath.BigLessThan(balance, amount) { + if arbmath.BigLessThan(balance.ToBig(), amount) { return fmt.Errorf("%w: addr %v have %v want %v", vm.ErrInsufficientBalance, *from, balance, amount) } - evm.StateDB.SubBalance(*from, amount) + evm.StateDB.SubBalance(*from, uint256.MustFromBig(amount)) } if to != nil { - evm.StateDB.AddBalance(*to, amount) + evm.StateDB.AddBalance(*to, uint256.MustFromBig(amount)) } if tracer := evm.Config.Tracer; tracer != nil { if evm.Depth() != 0 && scenario != TracingDuringEVM { @@ -59,7 +60,7 @@ func TransferBalance( info := &TracingInfo{ Tracer: evm.Config.Tracer, Scenario: scenario, - Contract: vm.NewContract(addressHolder{*to}, addressHolder{*from}, big.NewInt(0), 0), + Contract: vm.NewContract(addressHolder{*to}, addressHolder{*from}, uint256.NewInt(0), 0), Depth: evm.Depth(), } info.MockCall([]byte{}, 0, *from, *to, amount) diff --git a/execution/gethexec/tx_pre_checker.go b/execution/gethexec/tx_pre_checker.go index cff8b04d32..1a48d75fda 100644 --- a/execution/gethexec/tx_pre_checker.go +++ b/execution/gethexec/tx_pre_checker.go @@ -187,7 +187,7 @@ func PreCheckTx(bc *core.BlockChain, chainConfig *params.ChainConfig, header *ty } balance := statedb.GetBalance(sender) cost := tx.Cost() - if arbmath.BigLessThan(balance, cost) { + if arbmath.BigLessThan(balance.ToBig(), cost) { return fmt.Errorf("%w: address %v have %v want %v", core.ErrInsufficientFunds, sender, balance, cost) } if config.Strictness >= TxPreCheckerStrictnessFullValidation && tx.Nonce() > stateNonce { diff --git a/gethhook/geth_test.go b/gethhook/geth_test.go index 6274a54119..99bfa4ae1c 100644 --- a/gethhook/geth_test.go +++ b/gethhook/geth_test.go @@ -110,7 +110,7 @@ func TestEthDepositMessage(t *testing.T) { RunMessagesThroughAPI(t, [][]byte{serialized, serialized2}, statedb) - balanceAfter := statedb.GetBalance(addr) + balanceAfter := statedb.GetBalance(addr).ToBig() if balanceAfter.Cmp(new(big.Int).Add(balance.Big(), balance2.Big())) != 0 { Fail(t) } diff --git a/go-ethereum b/go-ethereum index 22a573ce54..64ea2d1d5d 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 22a573ce5463a305ab2787473518a7575f0ec796 +Subproject commit 64ea2d1d5dc56f657dda10f48273513d0df371b5 diff --git a/go.mod b/go.mod index ded1fced70..652c5ed02d 100644 --- a/go.mod +++ b/go.mod @@ -316,7 +316,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect - github.com/go-ole/go-ole v1.2.5 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-redis/redis/v8 v8.11.4 github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/uuid v1.3.1 diff --git a/go.sum b/go.sum index 8be44da749..72d78ba497 100644 --- a/go.sum +++ b/go.sum @@ -418,8 +418,9 @@ github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV 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.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/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-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= @@ -2023,6 +2024,7 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc 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.0.0-20221010170243-090e33056c14/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= diff --git a/precompiles/ArbGasInfo.go b/precompiles/ArbGasInfo.go index cb0045c49f..4492fb28cf 100644 --- a/precompiles/ArbGasInfo.go +++ b/precompiles/ArbGasInfo.go @@ -217,7 +217,7 @@ func (con ArbGasInfo) _preversion10_GetL1PricingSurplus(c ctx, evm mech) (*big.I } haveFunds := evm.StateDB.GetBalance(l1pricing.L1PricerFundsPoolAddress) needFunds := arbmath.BigAdd(fundsDueForRefunds, fundsDueForRewards) - return arbmath.BigSub(haveFunds, needFunds), nil + return arbmath.BigSub(haveFunds.ToBig(), needFunds), nil } func (con ArbGasInfo) GetPerBatchGasCharge(c ctx, evm mech) (int64, error) { diff --git a/precompiles/ArbInfo.go b/precompiles/ArbInfo.go index a260f7e7a4..9f8cf34532 100644 --- a/precompiles/ArbInfo.go +++ b/precompiles/ArbInfo.go @@ -18,7 +18,7 @@ func (con ArbInfo) GetBalance(c ctx, evm mech, account addr) (huge, error) { if err := c.Burn(params.BalanceGasEIP1884); err != nil { return nil, err } - return evm.StateDB.GetBalance(account), nil + return evm.StateDB.GetBalance(account).ToBig(), nil } // GetCode retrieves a contract's deployed code diff --git a/precompiles/ArbOwner.go b/precompiles/ArbOwner.go index 166768940b..f718a99f3e 100644 --- a/precompiles/ArbOwner.go +++ b/precompiles/ArbOwner.go @@ -153,7 +153,7 @@ func (con ArbOwner) ReleaseL1PricerSurplusFunds(c ctx, evm mech, maxWeiToRelease if err != nil { return nil, err } - weiToTransfer := new(big.Int).Sub(balance, recognized) + weiToTransfer := new(big.Int).Sub(balance.ToBig(), recognized) if weiToTransfer.Sign() < 0 { return common.Big0, nil } diff --git a/precompiles/ArbOwner_test.go b/precompiles/ArbOwner_test.go index ab128a8cb2..1f8c7ae4cd 100644 --- a/precompiles/ArbOwner_test.go +++ b/precompiles/ArbOwner_test.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" "github.com/offchainlabs/nitro/arbos/arbosState" "github.com/offchainlabs/nitro/arbos/burn" @@ -113,7 +114,7 @@ func TestArbOwner(t *testing.T) { Fail(t, avail) } deposited := big.NewInt(1000000) - evm.StateDB.AddBalance(l1pricing.L1PricerFundsPoolAddress, deposited) + evm.StateDB.AddBalance(l1pricing.L1PricerFundsPoolAddress, uint256.MustFromBig(deposited)) avail, err = gasInfo.GetL1FeesAvailable(callCtx, evm) Require(t, err) if avail.Sign() != 0 { diff --git a/system_tests/seqinbox_test.go b/system_tests/seqinbox_test.go index 81dd2ad0d7..1b2701c2df 100644 --- a/system_tests/seqinbox_test.go +++ b/system_tests/seqinbox_test.go @@ -171,7 +171,7 @@ func testSequencerInboxReaderImpl(t *testing.T, validator bool) { var blockStates []blockTestState blockStates = append(blockStates, blockTestState{ balances: map[common.Address]*big.Int{ - ownerAddress: startOwnerBalance, + ownerAddress: startOwnerBalance.ToBig(), }, nonces: map[common.Address]uint64{ ownerAddress: startOwnerNonce, @@ -392,7 +392,7 @@ func testSequencerInboxReaderImpl(t *testing.T, validator bool) { } if batchCount.Cmp(big.NewInt(int64(len(blockStates)))) == 0 { break - } else if i >= 100 { + } else if i >= 140 { Fatal(t, "timed out waiting for l1 batch count update; have", batchCount, "want", len(blockStates)-1) } time.Sleep(10 * time.Millisecond) @@ -433,7 +433,7 @@ func testSequencerInboxReaderImpl(t *testing.T, validator bool) { Require(t, err) for acct, expectedBalance := range state.balances { haveBalance := stateDb.GetBalance(acct) - if expectedBalance.Cmp(haveBalance) < 0 { + if expectedBalance.Cmp(haveBalance.ToBig()) < 0 { Fatal(t, "unexpected balance for account", acct, "; expected", expectedBalance, "got", haveBalance) } } From e1e7d44d0027b8b125707c54372e883e22403183 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Sun, 28 Apr 2024 11:57:17 -0500 Subject: [PATCH 39/41] Fix data poster creating nonce gap --- arbnode/dataposter/data_poster.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index b0e3061330..614711249b 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -845,7 +845,7 @@ func (p *DataPoster) sendTx(ctx context.Context, prevTx *storage.QueuedTransacti return fmt.Errorf("couldn't get preceding tx in DataPoster to check if should send tx with nonce %d: %w", newTx.FullTx.Nonce(), err) } if precedingTx != nil && // precedingTx == nil -> the actual preceding tx was already confirmed - precedingTx.FullTx.Type() != newTx.FullTx.Type() { + (precedingTx.FullTx.Type() != newTx.FullTx.Type() || !precedingTx.Sent) { latestBlockNumber, err := p.client.BlockNumber(ctx) if err != nil { return fmt.Errorf("couldn't get block number in DataPoster to check if should send tx with nonce %d: %w", newTx.FullTx.Nonce(), err) @@ -857,7 +857,7 @@ func (p *DataPoster) sendTx(ctx context.Context, prevTx *storage.QueuedTransacti } if precedingTx.FullTx.Nonce() > reorgResistantNonce { - log.Info("DataPoster is holding off on sending a transaction of different type to the previous transaction until the previous transaction has been included in a reorg resistant block (it remains queued and will be retried)", "nonce", newTx.FullTx.Nonce(), "prevType", precedingTx.FullTx.Type(), "type", newTx.FullTx.Type()) + log.Info("DataPoster is avoiding creating a mempool nonce gap (the tx remains queued and will be retried)", "nonce", newTx.FullTx.Nonce(), "prevType", precedingTx.FullTx.Type(), "type", newTx.FullTx.Type(), "prevSent", precedingTx.Sent) return nil } } From b5b12793b38470d0de9276d7249e62f6d8eaf025 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Mon, 29 Apr 2024 18:40:27 +0200 Subject: [PATCH 40/41] Ignore module roots that aren't in the same directory as the hash --- validator/server_common/machine_locator.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/validator/server_common/machine_locator.go b/validator/server_common/machine_locator.go index ac00c40dea..c8b4d9a165 100644 --- a/validator/server_common/machine_locator.go +++ b/validator/server_common/machine_locator.go @@ -81,6 +81,9 @@ func NewMachineLocator(rootPath string) (*MachineLocator, error) { continue } moduleRoot := common.HexToHash(strings.TrimSpace(string(mrContent))) + if file.Name() != "latest" && file.Name() != moduleRoot.Hex() { + continue + } moduleRoots[moduleRoot] = true if file.Name() == "latest" { latestModuleRoot = moduleRoot From 4813caaa6c70d3082d5eda1583bc28c9562cc0c6 Mon Sep 17 00:00:00 2001 From: Tristan Wilson Date: Tue, 30 Apr 2024 13:37:02 +0100 Subject: [PATCH 41/41] Update geth pin --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index 64ea2d1d5d..cc9e427d63 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 64ea2d1d5dc56f657dda10f48273513d0df371b5 +Subproject commit cc9e427d63c377677b97cdb60af89859bd9c48cd