From 7985488ab71341ea4ac7ff4e82ba7b4ec8b9b595 Mon Sep 17 00:00:00 2001 From: ganeshvanahalli Date: Wed, 13 Dec 2023 19:42:57 +0530 Subject: [PATCH 01/68] Pause nitro node block validator validation when memory is running low --- .../resourcemanager/resource_management.go | 32 ++++++------- .../resource_management_test.go | 8 ++-- staker/block_validator.go | 47 +++++++++++++++++++ 3 files changed, 67 insertions(+), 20 deletions(-) diff --git a/arbnode/resourcemanager/resource_management.go b/arbnode/resourcemanager/resource_management.go index cb1ae9d6ea..aba823cc25 100644 --- a/arbnode/resourcemanager/resource_management.go +++ b/arbnode/resourcemanager/resource_management.go @@ -39,14 +39,14 @@ func Init(conf *Config) error { return nil } - limit, err := parseMemLimit(conf.MemFreeLimit) + limit, err := ParseMemLimit(conf.MemFreeLimit) if err != nil { return err } node.WrapHTTPHandler = func(srv http.Handler) (http.Handler, error) { - var c limitChecker - c, err := newCgroupsMemoryLimitCheckerIfSupported(limit) + var c LimitChecker + c, err := NewCgroupsMemoryLimitCheckerIfSupported(limit) if errors.Is(err, errNotSupported) { log.Error("No method for determining memory usage and limits was discovered, disabled memory limit RPC throttling") c = &trivialLimitChecker{} @@ -57,7 +57,7 @@ func Init(conf *Config) error { return nil } -func parseMemLimit(limitStr string) (int, error) { +func ParseMemLimit(limitStr string) (int, error) { var ( limit int = 1 s string @@ -105,10 +105,10 @@ func ConfigAddOptions(prefix string, f *pflag.FlagSet) { // limit check. type httpServer struct { inner http.Handler - c limitChecker + c LimitChecker } -func newHttpServer(inner http.Handler, c limitChecker) *httpServer { +func newHttpServer(inner http.Handler, c LimitChecker) *httpServer { return &httpServer{inner: inner, c: c} } @@ -116,7 +116,7 @@ func newHttpServer(inner http.Handler, c limitChecker) *httpServer { // limit is exceeded, in which case it returns a HTTP 429 error. func (s *httpServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { start := time.Now() - exceeded, err := s.c.isLimitExceeded() + exceeded, err := s.c.IsLimitExceeded() limitCheckDurationHistogram.Update(time.Since(start).Nanoseconds()) if err != nil { log.Error("Error checking memory limit", "err", err, "checker", s.c.String()) @@ -130,19 +130,19 @@ func (s *httpServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { s.inner.ServeHTTP(w, req) } -type limitChecker interface { - isLimitExceeded() (bool, error) +type LimitChecker interface { + IsLimitExceeded() (bool, error) String() string } -func isSupported(c limitChecker) bool { - _, err := c.isLimitExceeded() +func isSupported(c LimitChecker) bool { + _, err := c.IsLimitExceeded() return err == nil } -// newCgroupsMemoryLimitCheckerIfSupported attempts to auto-discover whether +// NewCgroupsMemoryLimitCheckerIfSupported attempts to auto-discover whether // Cgroups V1 or V2 is supported for checking system memory limits. -func newCgroupsMemoryLimitCheckerIfSupported(memLimitBytes int) (*cgroupsMemoryLimitChecker, error) { +func NewCgroupsMemoryLimitCheckerIfSupported(memLimitBytes int) (*cgroupsMemoryLimitChecker, error) { c := newCgroupsMemoryLimitChecker(cgroupsV1MemoryFiles, memLimitBytes) if isSupported(c) { log.Info("Cgroups v1 detected, enabling memory limit RPC throttling") @@ -161,7 +161,7 @@ func newCgroupsMemoryLimitCheckerIfSupported(memLimitBytes int) (*cgroupsMemoryL // trivialLimitChecker checks no limits, so its limits are never exceeded. type trivialLimitChecker struct{} -func (_ trivialLimitChecker) isLimitExceeded() (bool, error) { +func (_ trivialLimitChecker) IsLimitExceeded() (bool, error) { return false, nil } @@ -202,7 +202,7 @@ func newCgroupsMemoryLimitChecker(files cgroupsMemoryFiles, memLimitBytes int) * } } -// isLimitExceeded checks if the system memory free is less than the limit. +// IsLimitExceeded checks if the system memory free is less than the limit. // It returns true if the limit is exceeded. // // container_memory_working_set_bytes in prometheus is calculated as @@ -223,7 +223,7 @@ func newCgroupsMemoryLimitChecker(files cgroupsMemoryFiles, memLimitBytes int) * // free memory for the page cache, to avoid cache thrashing on chain state // access. How much "reasonable" is will depend on access patterns, state // size, and your application's tolerance for latency. -func (c *cgroupsMemoryLimitChecker) isLimitExceeded() (bool, error) { +func (c *cgroupsMemoryLimitChecker) IsLimitExceeded() (bool, error) { var limit, usage, active, inactive int var err error if limit, err = readIntFromFile(c.files.limitFile); err != nil { diff --git a/arbnode/resourcemanager/resource_management_test.go b/arbnode/resourcemanager/resource_management_test.go index 4f52ad017e..4495396063 100644 --- a/arbnode/resourcemanager/resource_management_test.go +++ b/arbnode/resourcemanager/resource_management_test.go @@ -52,7 +52,7 @@ func makeCgroupsTestDir(cgroupDir string) cgroupsMemoryFiles { func TestCgroupsFailIfCantOpen(t *testing.T) { testFiles := makeCgroupsTestDir(t.TempDir()) c := newCgroupsMemoryLimitChecker(testFiles, 1024*1024*512) - if _, err := c.isLimitExceeded(); err == nil { + if _, err := c.IsLimitExceeded(); err == nil { t.Fatal("Should fail open if can't read files") } } @@ -124,7 +124,7 @@ func TestCgroupsMemoryLimit(t *testing.T) { } { t.Run(tc.desc, func(t *testing.T) { testFiles := makeCgroupsTestDir(t.TempDir()) - memLimit, err := parseMemLimit(tc.memLimit) + memLimit, err := ParseMemLimit(tc.memLimit) if err != nil { t.Fatalf("Parsing memory limit failed: %v", err) } @@ -132,12 +132,12 @@ func TestCgroupsMemoryLimit(t *testing.T) { if err := updateFakeCgroupFiles(c, tc.sysLimit, tc.usage, tc.inactive, tc.active); err != nil { t.Fatalf("Updating cgroup files: %v", err) } - exceeded, err := c.isLimitExceeded() + exceeded, err := c.IsLimitExceeded() if err != nil { t.Fatalf("Checking if limit exceeded: %v", err) } if exceeded != tc.want { - t.Errorf("isLimitExceeded() = %t, want %t", exceeded, tc.want) + t.Errorf("IsLimitExceeded() = %t, want %t", exceeded, tc.want) } }, ) diff --git a/staker/block_validator.go b/staker/block_validator.go index 61e5ed519b..d54fa60c11 100644 --- a/staker/block_validator.go +++ b/staker/block_validator.go @@ -20,6 +20,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" + "github.com/offchainlabs/nitro/arbnode/resourcemanager" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/util/containers" "github.com/offchainlabs/nitro/util/rpcclient" @@ -75,6 +76,8 @@ type BlockValidator struct { testingProgressMadeChan chan struct{} fatalErr chan<- error + + MemoryFreeLimitChecker resourcemanager.LimitChecker } type BlockValidatorConfig struct { @@ -87,6 +90,7 @@ type BlockValidatorConfig struct { 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"` } func (c *BlockValidatorConfig) Validate() error { @@ -109,6 +113,7 @@ func BlockValidatorConfigAddOptions(prefix string, f *flag.FlagSet) { f.String(prefix+".pending-upgrade-module-root", DefaultBlockValidatorConfig.PendingUpgradeModuleRoot, "pending upgrade wasm module root to additionally validate (hash, 'latest' or empty)") f.Bool(prefix+".failure-is-fatal", DefaultBlockValidatorConfig.FailureIsFatal, "failing a validation is treated as a fatal error") BlockValidatorDangerousConfigAddOptions(prefix+".dangerous", f) + f.String(prefix+".memory-free-limit", DefaultBlockValidatorConfig.MemoryFreeLimit, "minimum free-memory limit after reaching which the blockvalidator pauses validation") } func BlockValidatorDangerousConfigAddOptions(prefix string, f *flag.FlagSet) { @@ -125,6 +130,7 @@ var DefaultBlockValidatorConfig = BlockValidatorConfig{ PendingUpgradeModuleRoot: "latest", FailureIsFatal: true, Dangerous: DefaultBlockValidatorDangerousConfig, + MemoryFreeLimit: "1GB", } var TestBlockValidatorConfig = BlockValidatorConfig{ @@ -137,6 +143,7 @@ var TestBlockValidatorConfig = BlockValidatorConfig{ PendingUpgradeModuleRoot: "latest", FailureIsFatal: true, Dangerous: DefaultBlockValidatorDangerousConfig, + MemoryFreeLimit: "1GB", } var DefaultBlockValidatorDangerousConfig = BlockValidatorDangerousConfig{ @@ -215,6 +222,19 @@ func NewBlockValidator( } streamer.SetBlockValidator(ret) inbox.SetBlockValidator(ret) + if config().MemoryFreeLimit != "" { + limit, err := resourcemanager.ParseMemLimit(config().MemoryFreeLimit) + if err != nil { + return nil, fmt.Errorf("failed to parse MemoryFreeLimit string from config: %w", err) + } + limtchecker, err := resourcemanager.NewCgroupsMemoryLimitCheckerIfSupported(limit) + if err != nil { + log.Warn("failed to create MemoryFreeLimitChecker, Cgroups V1 or V2 is unsupported") + } + if limtchecker != nil { + ret.MemoryFreeLimitChecker = limtchecker + } + } return ret, nil } @@ -521,6 +541,15 @@ func (v *BlockValidator) iterativeValidationEntryCreator(ctx context.Context, ig } func (v *BlockValidator) sendNextRecordRequests(ctx context.Context) (bool, error) { + if v.MemoryFreeLimitChecker != nil { + exceeded, err := v.MemoryFreeLimitChecker.IsLimitExceeded() + if err != nil { + log.Error("error checking if free-memory limit exceeded using MemoryFreeLimitChecker", "err", err) + } + if exceeded { + return false, nil + } + } v.reorgMutex.RLock() pos := v.recordSent() created := v.created() @@ -550,6 +579,15 @@ func (v *BlockValidator) sendNextRecordRequests(ctx context.Context) (bool, erro return true, nil } for pos <= recordUntil { + if v.MemoryFreeLimitChecker != nil { + exceeded, err := v.MemoryFreeLimitChecker.IsLimitExceeded() + if err != nil { + log.Error("error checking if free-memory limit exceeded using MemoryFreeLimitChecker", "err", err) + } + if exceeded { + return false, nil + } + } validationStatus, found := v.validations.Load(pos) if !found { return false, fmt.Errorf("not found entry for pos %d", pos) @@ -699,6 +737,15 @@ validationsLoop: log.Trace("advanceValidations: no more room", "pos", pos) return nil, nil } + if v.MemoryFreeLimitChecker != nil { + exceeded, err := v.MemoryFreeLimitChecker.IsLimitExceeded() + if err != nil { + log.Error("error checking if free-memory limit exceeded using MemoryFreeLimitChecker", "err", err) + } + if exceeded { + return nil, nil + } + } if currentStatus == Prepared { input, err := validationStatus.Entry.ToInput() if err != nil && ctx.Err() == nil { From 7e4e4e7f77e8fb4c7b473dc0eab746e2aab95cd9 Mon Sep 17 00:00:00 2001 From: ganeshvanahalli Date: Fri, 15 Dec 2023 15:36:01 +0530 Subject: [PATCH 02/68] address PR comments --- staker/block_validator.go | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/staker/block_validator.go b/staker/block_validator.go index d54fa60c11..e649b112b5 100644 --- a/staker/block_validator.go +++ b/staker/block_validator.go @@ -91,9 +91,25 @@ type BlockValidatorConfig struct { FailureIsFatal bool `koanf:"failure-is-fatal" reload:"hot"` Dangerous BlockValidatorDangerousConfig `koanf:"dangerous"` MemoryFreeLimit string `koanf:"memory-free-limit" reload:"hot"` + + memoryFreeLimit int } func (c *BlockValidatorConfig) Validate() error { + if c.MemoryFreeLimit == "default" { + c.memoryFreeLimit = 1073741824 // 1GB + _, err := resourcemanager.NewCgroupsMemoryLimitCheckerIfSupported(c.memoryFreeLimit) + if err != nil { + log.Warn("Cgroups V1 or V2 is unsupported, memory-free-limit feature inside block-validator is disabled") + c.MemoryFreeLimit = "" + } + } else if c.MemoryFreeLimit != "" { + limit, err := resourcemanager.ParseMemLimit(c.MemoryFreeLimit) + if err != nil { + return fmt.Errorf("failed to parse block-validator config memory-free-limit string: %w", err) + } + c.memoryFreeLimit = limit + } return c.ValidationServer.Validate() } @@ -113,7 +129,7 @@ func BlockValidatorConfigAddOptions(prefix string, f *flag.FlagSet) { f.String(prefix+".pending-upgrade-module-root", DefaultBlockValidatorConfig.PendingUpgradeModuleRoot, "pending upgrade wasm module root to additionally validate (hash, 'latest' or empty)") f.Bool(prefix+".failure-is-fatal", DefaultBlockValidatorConfig.FailureIsFatal, "failing a validation is treated as a fatal error") BlockValidatorDangerousConfigAddOptions(prefix+".dangerous", f) - f.String(prefix+".memory-free-limit", DefaultBlockValidatorConfig.MemoryFreeLimit, "minimum free-memory limit after reaching which the blockvalidator pauses validation") + 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) { @@ -130,7 +146,7 @@ var DefaultBlockValidatorConfig = BlockValidatorConfig{ PendingUpgradeModuleRoot: "latest", FailureIsFatal: true, Dangerous: DefaultBlockValidatorDangerousConfig, - MemoryFreeLimit: "1GB", + MemoryFreeLimit: "default", } var TestBlockValidatorConfig = BlockValidatorConfig{ @@ -143,7 +159,7 @@ var TestBlockValidatorConfig = BlockValidatorConfig{ PendingUpgradeModuleRoot: "latest", FailureIsFatal: true, Dangerous: DefaultBlockValidatorDangerousConfig, - MemoryFreeLimit: "1GB", + MemoryFreeLimit: "default", } var DefaultBlockValidatorDangerousConfig = BlockValidatorDangerousConfig{ @@ -223,17 +239,11 @@ func NewBlockValidator( streamer.SetBlockValidator(ret) inbox.SetBlockValidator(ret) if config().MemoryFreeLimit != "" { - limit, err := resourcemanager.ParseMemLimit(config().MemoryFreeLimit) - if err != nil { - return nil, fmt.Errorf("failed to parse MemoryFreeLimit string from config: %w", err) - } - limtchecker, err := resourcemanager.NewCgroupsMemoryLimitCheckerIfSupported(limit) + limtchecker, err := resourcemanager.NewCgroupsMemoryLimitCheckerIfSupported(config().memoryFreeLimit) if err != nil { - log.Warn("failed to create MemoryFreeLimitChecker, Cgroups V1 or V2 is unsupported") - } - if limtchecker != nil { - ret.MemoryFreeLimitChecker = limtchecker + return nil, fmt.Errorf("failed to create MemoryFreeLimitChecker, Cgroups V1 or V2 is unsupported") } + ret.MemoryFreeLimitChecker = limtchecker } return ret, nil } From fa44e50c84d2ddf4984555da39d58a104975eafb Mon Sep 17 00:00:00 2001 From: Maciej Kulawik Date: Thu, 14 Dec 2023 23:42:22 +0000 Subject: [PATCH 03/68] add prunning test --- cmd/conf/init.go | 57 +++++++ cmd/nitro/init.go | 288 +--------------------------------- cmd/nitro/nitro.go | 6 +- cmd/prunning/prunning.go | 250 +++++++++++++++++++++++++++++ go-ethereum | 2 +- system_tests/common_test.go | 4 +- system_tests/prunning_test.go | 121 ++++++++++++++ 7 files changed, 439 insertions(+), 289 deletions(-) create mode 100644 cmd/conf/init.go create mode 100644 cmd/prunning/prunning.go create mode 100644 system_tests/prunning_test.go diff --git a/cmd/conf/init.go b/cmd/conf/init.go new file mode 100644 index 0000000000..f228891b90 --- /dev/null +++ b/cmd/conf/init.go @@ -0,0 +1,57 @@ +package conf + +import ( + "time" + + "github.com/spf13/pflag" +) + +type InitConfig struct { + Force bool `koanf:"force"` + Url string `koanf:"url"` + DownloadPath string `koanf:"download-path"` + DownloadPoll time.Duration `koanf:"download-poll"` + DevInit bool `koanf:"dev-init"` + DevInitAddress string `koanf:"dev-init-address"` + DevInitBlockNum uint64 `koanf:"dev-init-blocknum"` + Empty bool `koanf:"empty"` + AccountsPerSync uint `koanf:"accounts-per-sync"` + ImportFile string `koanf:"import-file"` + ThenQuit bool `koanf:"then-quit"` + Prune string `koanf:"prune"` + PruneBloomSize uint64 `koanf:"prune-bloom-size"` + ResetToMessage int64 `koanf:"reset-to-message"` +} + +var InitConfigDefault = InitConfig{ + Force: false, + Url: "", + DownloadPath: "/tmp/", + DownloadPoll: time.Minute, + DevInit: false, + DevInitAddress: "", + DevInitBlockNum: 0, + ImportFile: "", + AccountsPerSync: 100000, + ThenQuit: false, + Prune: "", + PruneBloomSize: 2048, + ResetToMessage: -1, +} + +func InitConfigAddOptions(prefix string, f *pflag.FlagSet) { + f.Bool(prefix+".force", InitConfigDefault.Force, "if true: in case database exists init code will be reexecuted and genesis block compared to database") + f.String(prefix+".url", InitConfigDefault.Url, "url to download initializtion data - will poll if download fails") + f.String(prefix+".download-path", InitConfigDefault.DownloadPath, "path to save temp downloaded file") + f.Duration(prefix+".download-poll", InitConfigDefault.DownloadPoll, "how long to wait between polling attempts") + f.Bool(prefix+".dev-init", InitConfigDefault.DevInit, "init with dev data (1 account with balance) instead of file import") + f.String(prefix+".dev-init-address", InitConfigDefault.DevInitAddress, "Address of dev-account. Leave empty to use the dev-wallet.") + f.Uint64(prefix+".dev-init-blocknum", InitConfigDefault.DevInitBlockNum, "Number of preinit blocks. Must exist in ancient database.") + f.Bool(prefix+".empty", InitConfigDefault.Empty, "init with empty state") + f.Bool(prefix+".then-quit", InitConfigDefault.ThenQuit, "quit after init is done") + f.String(prefix+".import-file", InitConfigDefault.ImportFile, "path for json data to import") + f.Uint(prefix+".accounts-per-sync", InitConfigDefault.AccountsPerSync, "during init - sync database every X accounts. Lower value for low-memory systems. 0 disables.") + f.String(prefix+".prune", InitConfigDefault.Prune, "pruning for a given use: \"full\" for full nodes serving RPC requests, or \"validator\" for validators") + f.Uint64(prefix+".prune-bloom-size", InitConfigDefault.PruneBloomSize, "the amount of memory in megabytes to use for the pruning bloom filter (higher values prune better)") + f.Int64(prefix+".reset-to-message", InitConfigDefault.ResetToMessage, "forces a reset to an old message height. Also set max-reorg-resequence-depth=0 to force re-reading messages") +} diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index 1427ef161e..745e168375 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -10,93 +10,37 @@ import ( "fmt" "math/big" "os" - "reflect" - "regexp" "runtime" "strings" "sync" "time" + "github.com/offchainlabs/nitro/cmd/conf" + "github.com/offchainlabs/nitro/cmd/prunning" "github.com/offchainlabs/nitro/cmd/util" "github.com/cavaliergopher/grab/v3" extract "github.com/codeclysm/extract/v3" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state/pruner" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rpc" "github.com/offchainlabs/nitro/arbnode" - "github.com/offchainlabs/nitro/arbnode/dataposter/storage" "github.com/offchainlabs/nitro/arbos/arbosState" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/cmd/ipfshelper" "github.com/offchainlabs/nitro/execution/gethexec" - "github.com/offchainlabs/nitro/staker" "github.com/offchainlabs/nitro/statetransfer" - "github.com/spf13/pflag" ) -type InitConfig struct { - Force bool `koanf:"force"` - Url string `koanf:"url"` - DownloadPath string `koanf:"download-path"` - DownloadPoll time.Duration `koanf:"download-poll"` - DevInit bool `koanf:"dev-init"` - DevInitAddress string `koanf:"dev-init-address"` - DevInitBlockNum uint64 `koanf:"dev-init-blocknum"` - Empty bool `koanf:"empty"` - AccountsPerSync uint `koanf:"accounts-per-sync"` - ImportFile string `koanf:"import-file"` - ThenQuit bool `koanf:"then-quit"` - Prune string `koanf:"prune"` - PruneBloomSize uint64 `koanf:"prune-bloom-size"` - ResetToMessage int64 `koanf:"reset-to-message"` -} - -var InitConfigDefault = InitConfig{ - Force: false, - Url: "", - DownloadPath: "/tmp/", - DownloadPoll: time.Minute, - DevInit: false, - DevInitAddress: "", - DevInitBlockNum: 0, - ImportFile: "", - AccountsPerSync: 100000, - ThenQuit: false, - Prune: "", - PruneBloomSize: 2048, - ResetToMessage: -1, -} - -func InitConfigAddOptions(prefix string, f *pflag.FlagSet) { - f.Bool(prefix+".force", InitConfigDefault.Force, "if true: in case database exists init code will be reexecuted and genesis block compared to database") - f.String(prefix+".url", InitConfigDefault.Url, "url to download initializtion data - will poll if download fails") - f.String(prefix+".download-path", InitConfigDefault.DownloadPath, "path to save temp downloaded file") - f.Duration(prefix+".download-poll", InitConfigDefault.DownloadPoll, "how long to wait between polling attempts") - f.Bool(prefix+".dev-init", InitConfigDefault.DevInit, "init with dev data (1 account with balance) instead of file import") - f.String(prefix+".dev-init-address", InitConfigDefault.DevInitAddress, "Address of dev-account. Leave empty to use the dev-wallet.") - f.Uint64(prefix+".dev-init-blocknum", InitConfigDefault.DevInitBlockNum, "Number of preinit blocks. Must exist in ancient database.") - f.Bool(prefix+".empty", InitConfigDefault.Empty, "init with empty state") - f.Bool(prefix+".then-quit", InitConfigDefault.ThenQuit, "quit after init is done") - f.String(prefix+".import-file", InitConfigDefault.ImportFile, "path for json data to import") - f.Uint(prefix+".accounts-per-sync", InitConfigDefault.AccountsPerSync, "during init - sync database every X accounts. Lower value for low-memory systems. 0 disables.") - f.String(prefix+".prune", InitConfigDefault.Prune, "pruning for a given use: \"full\" for full nodes serving RPC requests, or \"validator\" for validators") - f.Uint64(prefix+".prune-bloom-size", InitConfigDefault.PruneBloomSize, "the amount of memory in megabytes to use for the pruning bloom filter (higher values prune better)") - f.Int64(prefix+".reset-to-message", InitConfigDefault.ResetToMessage, "forces a reset to an old message height. Also set max-reorg-resequence-depth=0 to force re-reading messages") -} - -func downloadInit(ctx context.Context, initConfig *InitConfig) (string, error) { +func downloadInit(ctx context.Context, initConfig *conf.InitConfig) (string, error) { if initConfig.Url == "" { return "", nil } @@ -215,228 +159,6 @@ func validateBlockChain(blockChain *core.BlockChain, chainConfig *params.ChainCo return nil } -type importantRoots struct { - chainDb ethdb.Database - roots []common.Hash - heights []uint64 -} - -// The minimum block distance between two important roots -const minRootDistance = 2000 - -// Marks a header as important, and records its root and height. -// If overwrite is true, it'll remove any future roots and replace them with this header. -// If overwrite is false, it'll ignore this header if it has future roots. -func (r *importantRoots) addHeader(header *types.Header, overwrite bool) error { - targetBlockNum := header.Number.Uint64() - for { - if header == nil || header.Root == (common.Hash{}) { - log.Error("missing state of pruning target", "blockNum", targetBlockNum) - return nil - } - exists, err := r.chainDb.Has(header.Root.Bytes()) - if err != nil { - return err - } - if exists { - break - } - num := header.Number.Uint64() - if num%3000 == 0 { - log.Info("looking for old block with state to keep", "current", num, "target", targetBlockNum) - } - // An underflow is fine here because it'll just return nil due to not found - header = rawdb.ReadHeader(r.chainDb, header.ParentHash, num-1) - } - height := header.Number.Uint64() - for len(r.heights) > 0 && r.heights[len(r.heights)-1] > height { - if !overwrite { - return nil - } - r.roots = r.roots[:len(r.roots)-1] - r.heights = r.heights[:len(r.heights)-1] - } - if len(r.heights) > 0 && r.heights[len(r.heights)-1]+minRootDistance > height { - return nil - } - r.roots = append(r.roots, header.Root) - r.heights = append(r.heights, height) - return nil -} - -var hashListRegex = regexp.MustCompile("^(0x)?[0-9a-fA-F]{64}(,(0x)?[0-9a-fA-F]{64})*$") - -// Finds important roots to retain while proving -func findImportantRoots(ctx context.Context, chainDb ethdb.Database, stack *node.Node, nodeConfig *NodeConfig, cacheConfig *core.CacheConfig, l1Client arbutil.L1Interface, rollupAddrs chaininfo.RollupAddresses) ([]common.Hash, error) { - initConfig := &nodeConfig.Init - chainConfig := gethexec.TryReadStoredChainConfig(chainDb) - if chainConfig == nil { - return nil, errors.New("database doesn't have a chain config (was this node initialized?)") - } - arbDb, err := stack.OpenDatabase("arbitrumdata", 0, 0, "", true) - if err != nil { - return nil, err - } - defer func() { - err := arbDb.Close() - if err != nil { - log.Warn("failed to close arbitrum database after finding pruning targets", "err", err) - } - }() - roots := importantRoots{ - chainDb: chainDb, - } - genesisNum := chainConfig.ArbitrumChainParams.GenesisBlockNum - genesisHash := rawdb.ReadCanonicalHash(chainDb, genesisNum) - genesisHeader := rawdb.ReadHeader(chainDb, genesisHash, genesisNum) - if genesisHeader == nil { - return nil, errors.New("missing L2 genesis block header") - } - err = roots.addHeader(genesisHeader, false) - if err != nil { - return nil, err - } - if initConfig.Prune == "validator" { - if l1Client == nil || reflect.ValueOf(l1Client).IsNil() { - return nil, errors.New("an L1 connection is required for validator pruning") - } - callOpts := bind.CallOpts{ - Context: ctx, - BlockNumber: big.NewInt(int64(rpc.FinalizedBlockNumber)), - } - rollup, err := staker.NewRollupWatcher(rollupAddrs.Rollup, l1Client, callOpts) - if err != nil { - return nil, err - } - latestConfirmedNum, err := rollup.LatestConfirmed(&callOpts) - if err != nil { - return nil, err - } - latestConfirmedNode, err := rollup.LookupNode(ctx, latestConfirmedNum) - if err != nil { - return nil, err - } - confirmedHash := latestConfirmedNode.Assertion.AfterState.GlobalState.BlockHash - confirmedNumber := rawdb.ReadHeaderNumber(chainDb, confirmedHash) - var confirmedHeader *types.Header - if confirmedNumber != nil { - confirmedHeader = rawdb.ReadHeader(chainDb, confirmedHash, *confirmedNumber) - } - if confirmedHeader != nil { - err = roots.addHeader(confirmedHeader, false) - if err != nil { - return nil, err - } - } else { - log.Warn("missing latest confirmed block", "hash", confirmedHash) - } - - validatorDb := rawdb.NewTable(arbDb, storage.BlockValidatorPrefix) - lastValidated, err := staker.ReadLastValidatedInfo(validatorDb) - if err != nil { - return nil, err - } - if lastValidated != nil { - var lastValidatedHeader *types.Header - headerNum := rawdb.ReadHeaderNumber(chainDb, lastValidated.GlobalState.BlockHash) - if headerNum != nil { - lastValidatedHeader = rawdb.ReadHeader(chainDb, lastValidated.GlobalState.BlockHash, *headerNum) - } - if lastValidatedHeader != nil { - err = roots.addHeader(lastValidatedHeader, false) - if err != nil { - return nil, err - } - } else { - log.Warn("missing latest validated block", "hash", lastValidated.GlobalState.BlockHash) - } - } - } else if initConfig.Prune == "full" { - if nodeConfig.Node.ValidatorRequired() { - return nil, errors.New("refusing to prune to full-node level when validator is enabled (you should prune in validator mode)") - } - } else if hashListRegex.MatchString(initConfig.Prune) { - parts := strings.Split(initConfig.Prune, ",") - roots := []common.Hash{genesisHeader.Root} - for _, part := range parts { - root := common.HexToHash(part) - if root == genesisHeader.Root { - // This was already included in the builtin list - continue - } - roots = append(roots, root) - } - return roots, nil - } else { - return nil, fmt.Errorf("unknown pruning mode: \"%v\"", initConfig.Prune) - } - if l1Client != nil { - // Find the latest finalized block and add it as a pruning target - l1Block, err := l1Client.BlockByNumber(ctx, big.NewInt(int64(rpc.FinalizedBlockNumber))) - if err != nil { - return nil, fmt.Errorf("failed to get finalized block: %w", err) - } - l1BlockNum := l1Block.NumberU64() - tracker, err := arbnode.NewInboxTracker(arbDb, nil, nil) - if err != nil { - return nil, err - } - batch, err := tracker.GetBatchCount() - if err != nil { - return nil, err - } - for { - if ctx.Err() != nil { - return nil, ctx.Err() - } - if batch == 0 { - // No batch has been finalized - break - } - batch -= 1 - meta, err := tracker.GetBatchMetadata(batch) - if err != nil { - return nil, err - } - if meta.ParentChainBlock <= l1BlockNum { - signedBlockNum := arbutil.MessageCountToBlockNumber(meta.MessageCount, genesisNum) - blockNum := uint64(signedBlockNum) - l2Hash := rawdb.ReadCanonicalHash(chainDb, blockNum) - l2Header := rawdb.ReadHeader(chainDb, l2Hash, blockNum) - if l2Header == nil { - log.Warn("latest finalized L2 block is unknown", "blockNum", signedBlockNum) - break - } - err = roots.addHeader(l2Header, false) - if err != nil { - return nil, err - } - break - } - } - } - roots.roots = append(roots.roots, common.Hash{}) // the latest snapshot - log.Info("found pruning target blocks", "heights", roots.heights, "roots", roots.roots) - return roots.roots, nil -} - -func pruneChainDb(ctx context.Context, chainDb ethdb.Database, stack *node.Node, nodeConfig *NodeConfig, cacheConfig *core.CacheConfig, l1Client arbutil.L1Interface, rollupAddrs chaininfo.RollupAddresses) error { - config := &nodeConfig.Init - if config.Prune == "" { - return pruner.RecoverPruning(stack.InstanceDir(), chainDb) - } - root, err := findImportantRoots(ctx, chainDb, stack, nodeConfig, cacheConfig, l1Client, rollupAddrs) - if err != nil { - return fmt.Errorf("failed to find root to retain for pruning: %w", err) - } - - pruner, err := pruner.NewPruner(chainDb, pruner.Config{Datadir: stack.InstanceDir(), BloomSize: config.PruneBloomSize}) - if err != nil { - return err - } - return pruner.Prune(root) -} - func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeConfig, chainId *big.Int, cacheConfig *core.CacheConfig, l1Client arbutil.L1Interface, rollupAddrs chaininfo.RollupAddresses) (ethdb.Database, *core.BlockChain, error) { if !config.Init.Force { if readOnlyDb, err := stack.OpenDatabaseWithFreezer("l2chaindata", 0, 0, "", "", true); err == nil { @@ -446,7 +168,7 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo if err != nil { return chainDb, nil, err } - err = pruneChainDb(ctx, chainDb, stack, config, cacheConfig, l1Client, rollupAddrs) + err = prunning.PruneChainDb(ctx, chainDb, stack, &config.Init, cacheConfig, l1Client, rollupAddrs, config.Node.ValidatorRequired()) if err != nil { return chainDb, nil, fmt.Errorf("error pruning: %w", err) } @@ -642,7 +364,7 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo return chainDb, l2BlockChain, err } - err = pruneChainDb(ctx, chainDb, stack, config, cacheConfig, l1Client, rollupAddrs) + err = prunning.PruneChainDb(ctx, chainDb, stack, &config.Init, cacheConfig, l1Client, rollupAddrs, config.Node.ValidatorRequired()) if err != nil { return chainDb, nil, fmt.Errorf("error pruning: %w", err) } diff --git a/cmd/nitro/nitro.go b/cmd/nitro/nitro.go index 86093a85d9..ca0d3496f0 100644 --- a/cmd/nitro/nitro.go +++ b/cmd/nitro/nitro.go @@ -677,7 +677,7 @@ type NodeConfig struct { MetricsServer genericconf.MetricsServerConfig `koanf:"metrics-server"` PProf bool `koanf:"pprof"` PprofCfg genericconf.PProf `koanf:"pprof-cfg"` - Init InitConfig `koanf:"init"` + Init conf.InitConfig `koanf:"init"` Rpc genericconf.RpcConfig `koanf:"rpc"` } @@ -699,7 +699,7 @@ var NodeConfigDefault = NodeConfig{ GraphQL: genericconf.GraphQLConfigDefault, Metrics: false, MetricsServer: genericconf.MetricsServerConfigDefault, - Init: InitConfigDefault, + Init: conf.InitConfigDefault, Rpc: genericconf.DefaultRpcConfig, PProf: false, PprofCfg: genericconf.PProfDefault, @@ -726,7 +726,7 @@ func NodeConfigAddOptions(f *flag.FlagSet) { f.Bool("pprof", NodeConfigDefault.PProf, "enable pprof") genericconf.PProfAddOptions("pprof-cfg", f) - InitConfigAddOptions("init", f) + conf.InitConfigAddOptions("init", f) genericconf.RpcConfigAddOptions("rpc", f) } diff --git a/cmd/prunning/prunning.go b/cmd/prunning/prunning.go new file mode 100644 index 0000000000..7538c68a10 --- /dev/null +++ b/cmd/prunning/prunning.go @@ -0,0 +1,250 @@ +package prunning + +import ( + "context" + "errors" + "fmt" + "math/big" + "reflect" + "regexp" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state/pruner" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/rpc" + "github.com/offchainlabs/nitro/arbnode" + "github.com/offchainlabs/nitro/arbnode/dataposter/storage" + "github.com/offchainlabs/nitro/arbutil" + "github.com/offchainlabs/nitro/cmd/chaininfo" + "github.com/offchainlabs/nitro/cmd/conf" + "github.com/offchainlabs/nitro/execution/gethexec" + "github.com/offchainlabs/nitro/staker" +) + +type importantRoots struct { + chainDb ethdb.Database + roots []common.Hash + heights []uint64 +} + +// The minimum block distance between two important roots +const minRootDistance = 2000 + +// Marks a header as important, and records its root and height. +// If overwrite is true, it'll remove any future roots and replace them with this header. +// If overwrite is false, it'll ignore this header if it has future roots. +func (r *importantRoots) addHeader(header *types.Header, overwrite bool) error { + targetBlockNum := header.Number.Uint64() + for { + if header == nil || header.Root == (common.Hash{}) { + log.Error("missing state of pruning target", "blockNum", targetBlockNum) + return nil + } + exists, err := r.chainDb.Has(header.Root.Bytes()) + if err != nil { + return err + } + if exists { + break + } + num := header.Number.Uint64() + if num%3000 == 0 { + log.Info("looking for old block with state to keep", "current", num, "target", targetBlockNum) + } + // An underflow is fine here because it'll just return nil due to not found + header = rawdb.ReadHeader(r.chainDb, header.ParentHash, num-1) + } + height := header.Number.Uint64() + for len(r.heights) > 0 && r.heights[len(r.heights)-1] > height { + if !overwrite { + return nil + } + r.roots = r.roots[:len(r.roots)-1] + r.heights = r.heights[:len(r.heights)-1] + } + if len(r.heights) > 0 && r.heights[len(r.heights)-1]+minRootDistance > height { + return nil + } + r.roots = append(r.roots, header.Root) + r.heights = append(r.heights, height) + return nil +} + +var hashListRegex = regexp.MustCompile("^(0x)?[0-9a-fA-F]{64}(,(0x)?[0-9a-fA-F]{64})*$") + +// Finds important roots to retain while proving +func findImportantRoots(ctx context.Context, chainDb ethdb.Database, stack *node.Node, initConfig *conf.InitConfig, cacheConfig *core.CacheConfig, l1Client arbutil.L1Interface, rollupAddrs chaininfo.RollupAddresses, validatorRequired bool) ([]common.Hash, error) { + chainConfig := gethexec.TryReadStoredChainConfig(chainDb) + if chainConfig == nil { + return nil, errors.New("database doesn't have a chain config (was this node initialized?)") + } + arbDb, err := stack.OpenDatabase("arbitrumdata", 0, 0, "", true) + if err != nil { + return nil, err + } + defer func() { + err := arbDb.Close() + if err != nil { + log.Warn("failed to close arbitrum database after finding pruning targets", "err", err) + } + }() + roots := importantRoots{ + chainDb: chainDb, + } + genesisNum := chainConfig.ArbitrumChainParams.GenesisBlockNum + genesisHash := rawdb.ReadCanonicalHash(chainDb, genesisNum) + genesisHeader := rawdb.ReadHeader(chainDb, genesisHash, genesisNum) + log.Warn("XXX", "genesisNum", genesisNum, "genesisHash", genesisHash) + if genesisHeader == nil { + return nil, errors.New("missing L2 genesis block header") + } + err = roots.addHeader(genesisHeader, false) + if err != nil { + return nil, err + } + if initConfig.Prune == "validator" { + if l1Client == nil || reflect.ValueOf(l1Client).IsNil() { + return nil, errors.New("an L1 connection is required for validator pruning") + } + callOpts := bind.CallOpts{ + Context: ctx, + BlockNumber: big.NewInt(int64(rpc.FinalizedBlockNumber)), + } + rollup, err := staker.NewRollupWatcher(rollupAddrs.Rollup, l1Client, callOpts) + if err != nil { + return nil, err + } + latestConfirmedNum, err := rollup.LatestConfirmed(&callOpts) + if err != nil { + return nil, err + } + latestConfirmedNode, err := rollup.LookupNode(ctx, latestConfirmedNum) + if err != nil { + return nil, err + } + confirmedHash := latestConfirmedNode.Assertion.AfterState.GlobalState.BlockHash + confirmedNumber := rawdb.ReadHeaderNumber(chainDb, confirmedHash) + var confirmedHeader *types.Header + if confirmedNumber != nil { + confirmedHeader = rawdb.ReadHeader(chainDb, confirmedHash, *confirmedNumber) + } + if confirmedHeader != nil { + err = roots.addHeader(confirmedHeader, false) + if err != nil { + return nil, err + } + } else { + log.Warn("missing latest confirmed block", "hash", confirmedHash) + } + + validatorDb := rawdb.NewTable(arbDb, storage.BlockValidatorPrefix) + lastValidated, err := staker.ReadLastValidatedInfo(validatorDb) + if err != nil { + return nil, err + } + if lastValidated != nil { + var lastValidatedHeader *types.Header + headerNum := rawdb.ReadHeaderNumber(chainDb, lastValidated.GlobalState.BlockHash) + if headerNum != nil { + lastValidatedHeader = rawdb.ReadHeader(chainDb, lastValidated.GlobalState.BlockHash, *headerNum) + } + if lastValidatedHeader != nil { + err = roots.addHeader(lastValidatedHeader, false) + if err != nil { + return nil, err + } + } else { + log.Warn("missing latest validated block", "hash", lastValidated.GlobalState.BlockHash) + } + } + } else if initConfig.Prune == "full" { + if validatorRequired { + return nil, errors.New("refusing to prune to full-node level when validator is enabled (you should prune in validator mode)") + } + } else if hashListRegex.MatchString(initConfig.Prune) { + parts := strings.Split(initConfig.Prune, ",") + roots := []common.Hash{genesisHeader.Root} + for _, part := range parts { + root := common.HexToHash(part) + if root == genesisHeader.Root { + // This was already included in the builtin list + continue + } + roots = append(roots, root) + } + return roots, nil + } else { + return nil, fmt.Errorf("unknown pruning mode: \"%v\"", initConfig.Prune) + } + if l1Client != nil { + // Find the latest finalized block and add it as a pruning target + l1Block, err := l1Client.BlockByNumber(ctx, big.NewInt(int64(rpc.FinalizedBlockNumber))) + if err != nil { + return nil, fmt.Errorf("failed to get finalized block: %w", err) + } + l1BlockNum := l1Block.NumberU64() + tracker, err := arbnode.NewInboxTracker(arbDb, nil, nil) + if err != nil { + return nil, err + } + batch, err := tracker.GetBatchCount() + if err != nil { + return nil, err + } + for { + if ctx.Err() != nil { + return nil, ctx.Err() + } + if batch == 0 { + // No batch has been finalized + break + } + batch -= 1 + meta, err := tracker.GetBatchMetadata(batch) + if err != nil { + return nil, err + } + if meta.ParentChainBlock <= l1BlockNum { + signedBlockNum := arbutil.MessageCountToBlockNumber(meta.MessageCount, genesisNum) + blockNum := uint64(signedBlockNum) + l2Hash := rawdb.ReadCanonicalHash(chainDb, blockNum) + l2Header := rawdb.ReadHeader(chainDb, l2Hash, blockNum) + if l2Header == nil { + log.Warn("latest finalized L2 block is unknown", "blockNum", signedBlockNum) + break + } + err = roots.addHeader(l2Header, false) + if err != nil { + return nil, err + } + break + } + } + } + roots.roots = append(roots.roots, common.Hash{}) // the latest snapshot + log.Info("found pruning target blocks", "heights", roots.heights, "roots", roots.roots) + return roots.roots, nil +} + +func PruneChainDb(ctx context.Context, chainDb ethdb.Database, stack *node.Node, initConfig *conf.InitConfig, cacheConfig *core.CacheConfig, l1Client arbutil.L1Interface, rollupAddrs chaininfo.RollupAddresses, validatorRequired bool) error { + if initConfig.Prune == "" { + return pruner.RecoverPruning(stack.InstanceDir(), chainDb) + } + root, err := findImportantRoots(ctx, chainDb, stack, initConfig, cacheConfig, l1Client, rollupAddrs, validatorRequired) + if err != nil { + return fmt.Errorf("failed to find root to retain for pruning: %w", err) + } + + pruner, err := pruner.NewPruner(chainDb, pruner.Config{Datadir: stack.InstanceDir(), BloomSize: initConfig.PruneBloomSize}) + if err != nil { + return err + } + return pruner.Prune(root) +} diff --git a/go-ethereum b/go-ethereum index b1622e6ac4..1e2855b24d 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit b1622e6ac4bf3762aebde92a585de2889d90823f +Subproject commit 1e2855b24d6555c8cfaf471bd9e2c3d19ab5c32c diff --git a/system_tests/common_test.go b/system_tests/common_test.go index 7752fbd34e..937b8980fc 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -698,7 +698,7 @@ func createL2BlockChainWithStackConfig( chainDb, err := stack.OpenDatabase("chaindb", 0, 0, "", false) Require(t, err) - arbDb, err := stack.OpenDatabase("arbdb", 0, 0, "", false) + arbDb, err := stack.OpenDatabase("arbitrumdata", 0, 0, "", false) Require(t, err) initReader := statetransfer.NewMemoryInitDataReader(&l2info.ArbInitData) @@ -903,7 +903,7 @@ func Create2ndNodeWithConfig( l2chainDb, err := l2stack.OpenDatabase("chaindb", 0, 0, "", false) Require(t, err) - l2arbDb, err := l2stack.OpenDatabase("arbdb", 0, 0, "", false) + l2arbDb, err := l2stack.OpenDatabase("arbitrumdata", 0, 0, "", false) Require(t, err) initReader := statetransfer.NewMemoryInitDataReader(l2InitData) diff --git a/system_tests/prunning_test.go b/system_tests/prunning_test.go new file mode 100644 index 0000000000..aadf3c5e9e --- /dev/null +++ b/system_tests/prunning_test.go @@ -0,0 +1,121 @@ +package arbtest + +import ( + "context" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/node" + "github.com/offchainlabs/nitro/cmd/conf" + "github.com/offchainlabs/nitro/cmd/prunning" + "github.com/offchainlabs/nitro/execution/gethexec" + "github.com/offchainlabs/nitro/util/testhelpers" +) + +func countDbEntries(db ethdb.Iteratee) int { + entries := 0 + it := db.NewIterator(nil, nil) + for it.Next() { + entries++ + } + it.Release() + return entries +} + +func TestPrunning(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + var dataDir string + + func() { + builder := NewNodeBuilder(ctx).DefaultConfig(t, true) + _ = builder.Build(t) + dataDir = builder.dataDir + l2cleanupDone := false + defer func() { + if !l2cleanupDone { + builder.L2.cleanup() + } + builder.L1.cleanup() + }() + builder.L2Info.GenerateAccount("User2") + var txs []*types.Transaction + for i := uint64(0); i < 1000; i++ { + tx := builder.L2Info.PrepareTx("Owner", "User2", builder.L2Info.TransferGas, common.Big1, nil) + txs = append(txs, tx) + err := builder.L2.Client.SendTransaction(ctx, tx) + Require(t, err) + } + for _, tx := range txs { + _, err := builder.L2.EnsureTxSucceeded(tx) + Require(t, err) + } + l2cleanupDone = true + builder.L2.cleanup() + t.Log("stopped l2 node") + + stack, err := node.New(builder.l2StackConfig) + Require(t, err) + defer stack.Close() + chainDb, err := stack.OpenDatabase("chaindb", 0, 0, "", false) + Require(t, err) + defer chainDb.Close() + entriesBeforePrunning := countDbEntries(chainDb) + + prand := testhelpers.NewPseudoRandomDataSource(t, 1) + var testKeys [][]byte + for i := 0; i < 100; i++ { + // generate test keys with length of hash to emulate legacy state trie nodes + testKeys = append(testKeys, prand.GetHash().Bytes()) + } + for _, key := range testKeys { + err = chainDb.Put(key, common.FromHex("0xdeadbeef")) + Require(t, err) + } + for _, key := range testKeys { + if has, _ := chainDb.Has(key); !has { + Fatal(t, "internal test error - failed to check existence of test key") + } + } + + initConfig := conf.InitConfigDefault + initConfig.Prune = "full" + coreCacheConfig := gethexec.DefaultCacheConfigFor(stack, &builder.execConfig.Caching) + err = prunning.PruneChainDb(ctx, chainDb, stack, &initConfig, coreCacheConfig, builder.L1.Client, *builder.L2.ConsensusNode.DeployInfo, false) + Require(t, err) + + for _, key := range testKeys { + if has, _ := chainDb.Has(key); has { + Fatal(t, "test key hasn't been prunned as expected") + } + } + + entriesAfterPrunning := countDbEntries(chainDb) + t.Log("db entries pre-prunning:", entriesBeforePrunning) + t.Log("db entries post-prunning:", entriesAfterPrunning) + + if entriesAfterPrunning >= entriesBeforePrunning { + Fatal(t, "The db doesn't have less entires after prunning then before. Before:", entriesBeforePrunning, "After:", entriesAfterPrunning) + } + }() + builder := NewNodeBuilder(ctx).DefaultConfig(t, true) + builder.dataDir = dataDir + cancel = builder.Build(t) + defer cancel() + + builder.L2Info.GenerateAccount("User2") + var txs []*types.Transaction + for i := uint64(0); i < 10; i++ { + tx := builder.L2Info.PrepareTx("Owner", "User2", builder.L2Info.TransferGas, common.Big1, nil) + txs = append(txs, tx) + err := builder.L2.Client.SendTransaction(ctx, tx) + Require(t, err) + } + for _, tx := range txs { + _, err := builder.L2.EnsureTxSucceeded(tx) + Require(t, err) + } +} From 875c558d9b0350d0d1923a1a7028a95c6107940d Mon Sep 17 00:00:00 2001 From: Maciej Kulawik Date: Fri, 15 Dec 2023 15:29:08 +0000 Subject: [PATCH 04/68] add missing default value of InitConfig.Empty --- cmd/conf/init.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/conf/init.go b/cmd/conf/init.go index f228891b90..bebf1955b7 100644 --- a/cmd/conf/init.go +++ b/cmd/conf/init.go @@ -31,6 +31,7 @@ var InitConfigDefault = InitConfig{ DevInit: false, DevInitAddress: "", DevInitBlockNum: 0, + Empty: false, ImportFile: "", AccountsPerSync: 100000, ThenQuit: false, From efe6f059557baa9b9377cd52bdb736a80d5db34d Mon Sep 17 00:00:00 2001 From: ganeshvanahalli Date: Fri, 15 Dec 2023 21:09:40 +0530 Subject: [PATCH 05/68] Add loglevel for ephemeral errors, make data poster nonce ahead of on-chain error an ephemeral error --- arbnode/batch_poster.go | 48 ++++++++++++++++++++--------------------- staker/staker.go | 9 ++++++-- util/log.go | 42 ++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 27 deletions(-) create mode 100644 util/log.go diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 7a4cfc21c2..513f573644 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -37,6 +37,7 @@ import ( "github.com/offchainlabs/nitro/cmd/genericconf" "github.com/offchainlabs/nitro/das" "github.com/offchainlabs/nitro/solgen/go/bridgegen" + "github.com/offchainlabs/nitro/util" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/headerreader" "github.com/offchainlabs/nitro/util/redisutil" @@ -57,22 +58,22 @@ type batchPosterPosition struct { type BatchPoster struct { stopwaiter.StopWaiter - l1Reader *headerreader.HeaderReader - inbox *InboxTracker - streamer *TransactionStreamer - config BatchPosterConfigFetcher - seqInbox *bridgegen.SequencerInbox - bridge *bridgegen.Bridge - syncMonitor *SyncMonitor - seqInboxABI *abi.ABI - seqInboxAddr common.Address - bridgeAddr common.Address - gasRefunderAddr common.Address - building *buildingBatch - daWriter das.DataAvailabilityServiceWriter - dataPoster *dataposter.DataPoster - redisLock *redislock.Simple - firstEphemeralError time.Time // first time a continuous error suspected to be ephemeral occurred + l1Reader *headerreader.HeaderReader + inbox *InboxTracker + streamer *TransactionStreamer + config BatchPosterConfigFetcher + seqInbox *bridgegen.SequencerInbox + bridge *bridgegen.Bridge + syncMonitor *SyncMonitor + seqInboxABI *abi.ABI + seqInboxAddr common.Address + bridgeAddr common.Address + gasRefunderAddr common.Address + building *buildingBatch + daWriter das.DataAvailabilityServiceWriter + dataPoster *dataposter.DataPoster + redisLock *redislock.Simple + // An estimate of the number of batches we want to post but haven't yet. // This doesn't include batches which we don't want to post yet due to the L1 bounds. backlog uint64 @@ -1103,6 +1104,8 @@ func (b *BatchPoster) Start(ctxIn context.Context) { b.redisLock.Start(ctxIn) b.StopWaiter.Start(ctxIn, b) b.LaunchThread(b.pollForReverts) + commonEphemeralError := time.Time{} + exceedMaxMempoolSizeEphemeralError := time.Time{} b.CallIteratively(func(ctx context.Context) time.Duration { var err error if common.HexToAddress(b.config().GasRefunderAddress) != (common.Address{}) { @@ -1127,21 +1130,16 @@ func (b *BatchPoster) Start(ctxIn context.Context) { } posted, err := b.maybePostSequencerBatch(ctx) if err == nil { - b.firstEphemeralError = time.Time{} + commonEphemeralError = time.Time{} + exceedMaxMempoolSizeEphemeralError = time.Time{} } if err != nil { b.building = nil logLevel := log.Error // Likely the inbox tracker just isn't caught up. // Let's see if this error disappears naturally. - if b.firstEphemeralError == (time.Time{}) { - b.firstEphemeralError = time.Now() - logLevel = log.Warn - } else if time.Since(b.firstEphemeralError) < time.Minute { - logLevel = log.Warn - } else if time.Since(b.firstEphemeralError) < time.Minute*5 && strings.Contains(err.Error(), "will exceed max mempool size") { - logLevel = log.Warn - } + logLevel = util.LogLevelEphemeralError(err, "", time.Minute, &commonEphemeralError, logLevel) + logLevel = util.LogLevelEphemeralError(err, "will exceed max mempool size", 5*time.Minute, &exceedMaxMempoolSizeEphemeralError, logLevel) logLevel("error posting batch", "err", err) return b.config().ErrorDelay } else if posted { diff --git a/staker/staker.go b/staker/staker.go index 4f35c1bc9a..522f5e0c59 100644 --- a/staker/staker.go +++ b/staker/staker.go @@ -25,6 +25,7 @@ import ( "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/cmd/genericconf" "github.com/offchainlabs/nitro/staker/txbuilder" + "github.com/offchainlabs/nitro/util" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/headerreader" "github.com/offchainlabs/nitro/util/stopwaiter" @@ -406,6 +407,7 @@ func (s *Staker) Start(ctxIn context.Context) { } s.StopWaiter.Start(ctxIn, s) backoff := time.Second + ephemeralError := time.Time{} s.CallIteratively(func(ctx context.Context) (returningWait time.Duration) { defer func() { panicErr := recover() @@ -438,6 +440,7 @@ func (s *Staker) Start(ctxIn context.Context) { } } if err == nil { + ephemeralError = time.Time{} backoff = time.Second stakerLastSuccessfulActionGauge.Update(time.Now().Unix()) stakerActionSuccessCounter.Inc(1) @@ -449,12 +452,14 @@ func (s *Staker) Start(ctxIn context.Context) { } stakerActionFailureCounter.Inc(1) backoff *= 2 + logLevel := log.Error if backoff > time.Minute { backoff = time.Minute - log.Error("error acting as staker", "err", err) } else { - log.Warn("error acting as staker", "err", err) + logLevel = log.Warn } + logLevel = util.LogLevelEphemeralError(err, "is ahead of on-chain nonce", 10*time.Minute, &ephemeralError, logLevel) + logLevel("error acting as staker", "err", err) return backoff }) s.CallIteratively(func(ctx context.Context) time.Duration { diff --git a/util/log.go b/util/log.go new file mode 100644 index 0000000000..4e0453638e --- /dev/null +++ b/util/log.go @@ -0,0 +1,42 @@ +package util + +import ( + "strings" + "time" + + "github.com/ethereum/go-ethereum/log" +) + +// LogLevelEphemeralError is a convenient intermediary level between log levels Warn and Error +// +// For a given error, errorSubstring, duration, firstOccuranceTime and logLevel +// the function defaults to returning the given logLevel if the error doesnt contain the errorSubstring, +// but if it does, then returns one of the corresponding loglevels as follows +// - Warn: For firstOccuranceTime of error being less than the duration amount of time from Now +// - Error: Otherwise +// +// # Usage Examples +// +// log.LogLevelEphemeralError(err, "not supported yet", 5*time.Minute, &firstEphemeralError, log.Error)("msg") +// log.LogLevelEphemeralError(err, "not supported yet", 5*time.Minute, &firstEphemeralError, log.Error)("msg", "key1", val1) +// log.LogLevelEphemeralError(err, "not supported yet", 5*time.Minute, &firstEphemeralError, log.Error)("msg", "key1", val1, "key2", val2) +func LogLevelEphemeralError( + err error, + errorSubstring string, + ephemeralDuration time.Duration, + firstOccuranceTime *time.Time, + currentLogLevel func(msg string, ctx ...interface{})) func(string, ...interface{}) { + if strings.Contains(err.Error(), errorSubstring) || errorSubstring == "" { + logLevel := log.Error + if *firstOccuranceTime == (time.Time{}) { + *firstOccuranceTime = time.Now() + logLevel = log.Warn + } else if time.Since(*firstOccuranceTime) < ephemeralDuration { + logLevel = log.Warn + } + return logLevel + } else { + *firstOccuranceTime = time.Time{} + return currentLogLevel + } +} From ae5c2f28227b7e9ea9353a8ae9c581232bca5aa7 Mon Sep 17 00:00:00 2001 From: Maciej Kulawik Date: Fri, 15 Dec 2023 15:43:18 +0000 Subject: [PATCH 06/68] cleanup imports --- cmd/nitro/init.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index 745e168375..00012ac62d 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -15,10 +15,6 @@ import ( "sync" "time" - "github.com/offchainlabs/nitro/cmd/conf" - "github.com/offchainlabs/nitro/cmd/prunning" - "github.com/offchainlabs/nitro/cmd/util" - "github.com/cavaliergopher/grab/v3" extract "github.com/codeclysm/extract/v3" "github.com/ethereum/go-ethereum/common" @@ -35,7 +31,10 @@ import ( "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/cmd/chaininfo" + "github.com/offchainlabs/nitro/cmd/conf" "github.com/offchainlabs/nitro/cmd/ipfshelper" + "github.com/offchainlabs/nitro/cmd/prunning" + "github.com/offchainlabs/nitro/cmd/util" "github.com/offchainlabs/nitro/execution/gethexec" "github.com/offchainlabs/nitro/statetransfer" ) From e25a59ecb379bbc2b1ec6fd91d90c88165a00936 Mon Sep 17 00:00:00 2001 From: ganeshvanahalli Date: Mon, 18 Dec 2023 17:46:01 +0530 Subject: [PATCH 07/68] fix typo --- util/log.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/util/log.go b/util/log.go index 4e0453638e..9bf508e532 100644 --- a/util/log.go +++ b/util/log.go @@ -9,10 +9,10 @@ import ( // LogLevelEphemeralError is a convenient intermediary level between log levels Warn and Error // -// For a given error, errorSubstring, duration, firstOccuranceTime and logLevel +// For a given error, errorSubstring, duration, firstOccurrenceTime and logLevel // the function defaults to returning the given logLevel if the error doesnt contain the errorSubstring, // but if it does, then returns one of the corresponding loglevels as follows -// - Warn: For firstOccuranceTime of error being less than the duration amount of time from Now +// - Warn: For firstOccurrenceTime of error being less than the duration amount of time from Now // - Error: Otherwise // // # Usage Examples @@ -24,19 +24,19 @@ func LogLevelEphemeralError( err error, errorSubstring string, ephemeralDuration time.Duration, - firstOccuranceTime *time.Time, + firstOccurrenceTime *time.Time, currentLogLevel func(msg string, ctx ...interface{})) func(string, ...interface{}) { if strings.Contains(err.Error(), errorSubstring) || errorSubstring == "" { logLevel := log.Error - if *firstOccuranceTime == (time.Time{}) { - *firstOccuranceTime = time.Now() + if *firstOccurrenceTime == (time.Time{}) { + *firstOccurrenceTime = time.Now() logLevel = log.Warn - } else if time.Since(*firstOccuranceTime) < ephemeralDuration { + } else if time.Since(*firstOccurrenceTime) < ephemeralDuration { logLevel = log.Warn } return logLevel } else { - *firstOccuranceTime = time.Time{} + *firstOccurrenceTime = time.Time{} return currentLogLevel } } From 6ca851673ed9b86bdca4c60097712f041509f6c5 Mon Sep 17 00:00:00 2001 From: Maciej Kulawik Date: Tue, 19 Dec 2023 14:33:30 +0000 Subject: [PATCH 08/68] fix arbitrum data db name in das test --- system_tests/das_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system_tests/das_test.go b/system_tests/das_test.go index 8c9621d57a..6db339521c 100644 --- a/system_tests/das_test.go +++ b/system_tests/das_test.go @@ -179,7 +179,7 @@ func TestDASRekey(t *testing.T) { l2chainDb, err := l2stackA.OpenDatabase("chaindb", 0, 0, "", false) Require(t, err) - l2arbDb, err := l2stackA.OpenDatabase("arbdb", 0, 0, "", false) + l2arbDb, err := l2stackA.OpenDatabase("arbitrumdata", 0, 0, "", false) Require(t, err) l2blockchain, err := gethexec.GetBlockChain(l2chainDb, nil, chainConfig, gethexec.ConfigDefaultTest().TxLookupLimit) From a0046599c388a8cb1f7837d42ed1d323810a975e Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Tue, 19 Dec 2023 16:46:46 +0100 Subject: [PATCH 09/68] Pass From field from dataposter to external signer --- arbnode/dataposter/data_poster.go | 42 ++++++++++++++++++++++----- arbnode/dataposter/dataposter_test.go | 6 +++- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index 059e080eea..e405c32dda 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/go-redis/redis/v8" "github.com/offchainlabs/nitro/arbnode/dataposter/dbstorage" "github.com/offchainlabs/nitro/arbnode/dataposter/noop" @@ -236,6 +237,31 @@ func rpcClient(ctx context.Context, opts *ExternalSignerCfg) (*rpc.Client, error ) } +// txToSendTxArgs converts transaction to SendTxArgs. This is needed for +// external signer to specify From field. +func txToSendTxArgs(addr common.Address, tx *types.Transaction) (*apitypes.SendTxArgs, error) { + if tx.To() == nil { + return nil, fmt.Errorf("transaction %v has no destination", tx.Hash()) + } + to := common.NewMixedcaseAddress(*tx.To()) + data := (hexutil.Bytes)(tx.Data()) + val := (*hexutil.Big)(tx.Value()) + if val == nil { + val = (*hexutil.Big)(big.NewInt(0)) + } + return &apitypes.SendTxArgs{ + From: common.NewMixedcaseAddress(addr), + To: &to, + Gas: hexutil.Uint64(tx.Gas()), + GasPrice: (*hexutil.Big)(tx.GasPrice()), + MaxFeePerGas: (*hexutil.Big)(tx.GasFeeCap()), + MaxPriorityFeePerGas: (*hexutil.Big)(tx.GasTipCap()), + Value: *val, + Data: &data, + Nonce: hexutil.Uint64(tx.Nonce()), + }, nil +} + // externalSigner returns signer function and ethereum address of the signer. // Returns an error if address isn't specified or if it can't connect to the // signer RPC server. @@ -249,25 +275,25 @@ func externalSigner(ctx context.Context, opts *ExternalSignerCfg) (signerFn, com return nil, common.Address{}, fmt.Errorf("error connecting external signer: %w", err) } sender := common.HexToAddress(opts.Address) - - var hasher types.Signer return func(ctx context.Context, addr common.Address, tx *types.Transaction) (*types.Transaction, error) { // According to the "eth_signTransaction" API definition, this should be // RLP encoded transaction object. // https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_signtransaction var data hexutil.Bytes - if err := client.CallContext(ctx, &data, opts.Method, tx); err != nil { + args, err := txToSendTxArgs(addr, tx) + if err != nil { + return nil, fmt.Errorf("error converting transaction to sendTxArgs: %w", err) + } + if err := client.CallContext(ctx, &data, opts.Method, args); err != nil { return nil, fmt.Errorf("signing transaction: %w", err) } var signedTx types.Transaction if err := rlp.DecodeBytes(data, &signedTx); err != nil { return nil, fmt.Errorf("error decoding signed transaction: %w", err) } - if hasher == nil { - hasher = types.LatestSignerForChainID(tx.ChainId()) - } - if hasher.Hash(tx) != hasher.Hash(&signedTx) { - return nil, fmt.Errorf("transaction: %x from external signer differs from request: %x", hasher.Hash(&signedTx), hasher.Hash(tx)) + hasher := types.LatestSignerForChainID(tx.ChainId()) + if h := hasher.Hash(args.ToTransaction()); h != hasher.Hash(&signedTx) { + return nil, fmt.Errorf("transaction: %x from external signer differs from request: %x", hasher.Hash(&signedTx), h) } return &signedTx, nil }, sender, nil diff --git a/arbnode/dataposter/dataposter_test.go b/arbnode/dataposter/dataposter_test.go index 74b4aff18e..e8028963f7 100644 --- a/arbnode/dataposter/dataposter_test.go +++ b/arbnode/dataposter/dataposter_test.go @@ -93,7 +93,11 @@ func TestExternalSigner(t *testing.T) { if err != nil { t.Fatalf("Error signing transaction with external signer: %v", err) } - want, err := srv.signerFn(addr, tx) + args, err := txToSendTxArgs(addr, tx) + if err != nil { + t.Fatalf("Error converting transaction to sendTxArgs: %v", err) + } + want, err := srv.signerFn(addr, args.ToTransaction()) if err != nil { t.Fatalf("Error signing transaction: %v", err) } From e9f0104add0d754a9c653ed384345273d717c133 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Tue, 19 Dec 2023 16:54:06 +0100 Subject: [PATCH 10/68] Include accessList and chainID in signing requrest --- arbnode/dataposter/data_poster.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index e405c32dda..d5b67505f5 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -249,6 +249,7 @@ func txToSendTxArgs(addr common.Address, tx *types.Transaction) (*apitypes.SendT if val == nil { val = (*hexutil.Big)(big.NewInt(0)) } + al := tx.AccessList() return &apitypes.SendTxArgs{ From: common.NewMixedcaseAddress(addr), To: &to, @@ -257,8 +258,10 @@ func txToSendTxArgs(addr common.Address, tx *types.Transaction) (*apitypes.SendT MaxFeePerGas: (*hexutil.Big)(tx.GasFeeCap()), MaxPriorityFeePerGas: (*hexutil.Big)(tx.GasTipCap()), Value: *val, - Data: &data, Nonce: hexutil.Uint64(tx.Nonce()), + Data: &data, + AccessList: &al, + ChainID: (*hexutil.Big)(tx.ChainId()), }, nil } From a4b4bddd3a8ba743c7665932071f380f5e93fe55 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Wed, 20 Dec 2023 20:28:47 +0530 Subject: [PATCH 11/68] Add a metric for memory used by the replay binary WASM in JIT --- arbitrator/jit/src/machine.rs | 3 ++- arbitrator/jit/src/main.rs | 3 ++- validator/server_jit/jit_machine.go | 33 +++++++++++++++++++------- validator/server_jit/machine_loader.go | 12 ++++++---- validator/server_jit/spawner.go | 10 ++++++-- 5 files changed, 43 insertions(+), 18 deletions(-) diff --git a/arbitrator/jit/src/machine.rs b/arbitrator/jit/src/machine.rs index ed22e12ef9..c9119dd16e 100644 --- a/arbitrator/jit/src/machine.rs +++ b/arbitrator/jit/src/machine.rs @@ -280,7 +280,7 @@ impl WasmEnv { Ok(env) } - pub fn send_results(&mut self, error: Option) { + pub fn send_results(&mut self, error: Option, memory_used: u64) { let writer = match &mut self.process.socket { Some((writer, _)) => writer, None => return, @@ -307,6 +307,7 @@ impl WasmEnv { check!(socket::write_u64(writer, self.small_globals[1])); check!(socket::write_bytes32(writer, &self.large_globals[0])); check!(socket::write_bytes32(writer, &self.large_globals[1])); + check!(socket::write_u64(writer, memory_used)); check!(writer.flush()); } } diff --git a/arbitrator/jit/src/main.rs b/arbitrator/jit/src/main.rs index 513cd067c4..968da2a978 100644 --- a/arbitrator/jit/src/main.rs +++ b/arbitrator/jit/src/main.rs @@ -114,8 +114,9 @@ fn main() { true => None, false => Some(message), }; + let memory_used = memory.size().0 as u64 * 65_536; - env.send_results(error); + env.send_results(error, memory_used); } // require a usize be at least 32 bits wide diff --git a/validator/server_jit/jit_machine.go b/validator/server_jit/jit_machine.go index f763ce3ea0..a41e249cdb 100644 --- a/validator/server_jit/jit_machine.go +++ b/validator/server_jit/jit_machine.go @@ -16,17 +16,21 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/validator" ) +var jitWasmMemoryUsage = metrics.NewRegisteredHistogram("jit/wasm/memoryusage", nil, metrics.NewBoundedHistogramSample()) + type JitMachine struct { - binary string - process *exec.Cmd - stdin io.WriteCloser + binary string + process *exec.Cmd + stdin io.WriteCloser + wasmMemoryUsageLimit int } -func createJitMachine(jitBinary string, binaryPath string, cranelift bool, moduleRoot common.Hash, fatalErrChan chan error) (*JitMachine, error) { +func createJitMachine(jitBinary string, binaryPath string, cranelift bool, wasmMemoryUsageLimit int, moduleRoot common.Hash, fatalErrChan chan error) (*JitMachine, error) { invocation := []string{"--binary", binaryPath, "--forks"} if cranelift { invocation = append(invocation, "--cranelift") @@ -45,9 +49,10 @@ func createJitMachine(jitBinary string, binaryPath string, cranelift bool, modul }() machine := &JitMachine{ - binary: binaryPath, - process: process, - stdin: stdin, + binary: binaryPath, + process: process, + stdin: stdin, + wasmMemoryUsageLimit: wasmMemoryUsageLimit, } return machine, nil } @@ -258,8 +263,18 @@ func (machine *JitMachine) prove( if state.BlockHash, err = readHash(); err != nil { return state, err } - state.SendRoot, err = readHash() - return state, err + if state.SendRoot, err = readHash(); err != nil { + return state, err + } + memoryUsed, err := readUint64() + if err != nil { + return state, fmt.Errorf("failed to read memory usage from Jit machine: %w", err) + } + if memoryUsed > uint64(machine.wasmMemoryUsageLimit) { + log.Warn("memory used by jit wasm exceeds the wasm memory usage limit", "limit", machine.wasmMemoryUsageLimit, "memoryUsed", memoryUsed) + } + jitWasmMemoryUsage.Update(int64(memoryUsed)) + return state, nil default: message := "inter-process communication failure" log.Error("Jit Machine Failure", "message", message) diff --git a/validator/server_jit/machine_loader.go b/validator/server_jit/machine_loader.go index 5705a9a387..3a831928b7 100644 --- a/validator/server_jit/machine_loader.go +++ b/validator/server_jit/machine_loader.go @@ -13,13 +13,15 @@ import ( ) type JitMachineConfig struct { - ProverBinPath string - JitCranelift bool + ProverBinPath string + JitCranelift bool + WasmMemoryUsageLimit int } var DefaultJitMachineConfig = JitMachineConfig{ - JitCranelift: true, - ProverBinPath: "replay.wasm", + JitCranelift: true, + ProverBinPath: "replay.wasm", + WasmMemoryUsageLimit: 4294967296, } func getJitPath() (string, error) { @@ -57,7 +59,7 @@ func NewJitMachineLoader(config *JitMachineConfig, locator *server_common.Machin } createMachineThreadFunc := func(ctx context.Context, moduleRoot common.Hash) (*JitMachine, error) { binPath := filepath.Join(locator.GetMachinePath(moduleRoot), config.ProverBinPath) - return createJitMachine(jitPath, binPath, config.JitCranelift, moduleRoot, fatalErrChan) + return createJitMachine(jitPath, binPath, config.JitCranelift, config.WasmMemoryUsageLimit, moduleRoot, fatalErrChan) } return &JitMachineLoader{ MachineLoader: *server_common.NewMachineLoader[JitMachine](locator, createMachineThreadFunc), diff --git a/validator/server_jit/spawner.go b/validator/server_jit/spawner.go index ff1749506a..6489821b5b 100644 --- a/validator/server_jit/spawner.go +++ b/validator/server_jit/spawner.go @@ -18,18 +18,23 @@ import ( type JitSpawnerConfig struct { Workers int `koanf:"workers" reload:"hot"` Cranelift bool `koanf:"cranelift"` + + // TODO: change WasmMemoryUsageLimit to a string and use resourcemanager.ParseMemLimit + WasmMemoryUsageLimit int `koanf:"wasm-memory-usage-limit"` } type JitSpawnerConfigFecher func() *JitSpawnerConfig var DefaultJitSpawnerConfig = JitSpawnerConfig{ - Workers: 0, - Cranelift: true, + Workers: 0, + Cranelift: true, + WasmMemoryUsageLimit: 4294967296, // 2^32 WASM memeory limit } func JitSpawnerConfigAddOptions(prefix string, f *flag.FlagSet) { f.Int(prefix+".workers", DefaultJitSpawnerConfig.Workers, "number of concurrent validation threads") f.Bool(prefix+".cranelift", DefaultJitSpawnerConfig.Cranelift, "use Cranelift instead of LLVM when validating blocks using the jit-accelerated block validator") + f.Int(prefix+".wasm-memory-usage-limit", DefaultJitSpawnerConfig.WasmMemoryUsageLimit, "if memory used by a jit wasm exceeds this limit, a warning is logged") } type JitSpawner struct { @@ -44,6 +49,7 @@ func NewJitSpawner(locator *server_common.MachineLocator, config JitSpawnerConfi // TODO - preload machines machineConfig := DefaultJitMachineConfig machineConfig.JitCranelift = config().Cranelift + machineConfig.WasmMemoryUsageLimit = config().WasmMemoryUsageLimit loader, err := NewJitMachineLoader(&machineConfig, locator, fatalErrChan) if err != nil { return nil, err From 7a26e97d04ef7a26a9adcd6b53db0afc0bf384d6 Mon Sep 17 00:00:00 2001 From: Maciej Kulawik Date: Wed, 20 Dec 2023 15:03:39 +0000 Subject: [PATCH 12/68] clean up debug log --- cmd/prunning/prunning.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/prunning/prunning.go b/cmd/prunning/prunning.go index 7538c68a10..5efdf8e643 100644 --- a/cmd/prunning/prunning.go +++ b/cmd/prunning/prunning.go @@ -101,7 +101,6 @@ func findImportantRoots(ctx context.Context, chainDb ethdb.Database, stack *node genesisNum := chainConfig.ArbitrumChainParams.GenesisBlockNum genesisHash := rawdb.ReadCanonicalHash(chainDb, genesisNum) genesisHeader := rawdb.ReadHeader(chainDb, genesisHash, genesisNum) - log.Warn("XXX", "genesisNum", genesisNum, "genesisHash", genesisHash) if genesisHeader == nil { return nil, errors.New("missing L2 genesis block header") } From 712bc0ce2658f98b7476375148c72891dd659bb3 Mon Sep 17 00:00:00 2001 From: Maciej Kulawik Date: Wed, 20 Dec 2023 15:10:26 +0000 Subject: [PATCH 13/68] fix typo --- system_tests/prunning_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system_tests/prunning_test.go b/system_tests/prunning_test.go index aadf3c5e9e..cfb1a4bcf7 100644 --- a/system_tests/prunning_test.go +++ b/system_tests/prunning_test.go @@ -98,7 +98,7 @@ func TestPrunning(t *testing.T) { t.Log("db entries post-prunning:", entriesAfterPrunning) if entriesAfterPrunning >= entriesBeforePrunning { - Fatal(t, "The db doesn't have less entires after prunning then before. Before:", entriesBeforePrunning, "After:", entriesAfterPrunning) + Fatal(t, "The db doesn't have less entries after prunning then before. Before:", entriesBeforePrunning, "After:", entriesAfterPrunning) } }() builder := NewNodeBuilder(ctx).DefaultConfig(t, true) From b005bd0c1cce855ac100e3bddb66739dc0e912c0 Mon Sep 17 00:00:00 2001 From: Maciej Kulawik Date: Wed, 20 Dec 2023 18:24:38 +0000 Subject: [PATCH 14/68] count only state entries in diskdb in prunning test --- system_tests/prunning_test.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/system_tests/prunning_test.go b/system_tests/prunning_test.go index cfb1a4bcf7..d522458209 100644 --- a/system_tests/prunning_test.go +++ b/system_tests/prunning_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/node" @@ -14,11 +15,14 @@ import ( "github.com/offchainlabs/nitro/util/testhelpers" ) -func countDbEntries(db ethdb.Iteratee) int { +func countStateEntries(db ethdb.Iteratee) int { entries := 0 it := db.NewIterator(nil, nil) for it.Next() { - entries++ + isCode, _ := rawdb.IsCodeKey(it.Key()) + if len(it.Key()) == common.HashLength || isCode { + entries++ + } } it.Release() return entries @@ -43,7 +47,7 @@ func TestPrunning(t *testing.T) { }() builder.L2Info.GenerateAccount("User2") var txs []*types.Transaction - for i := uint64(0); i < 1000; i++ { + for i := uint64(0); i < 200; i++ { tx := builder.L2Info.PrepareTx("Owner", "User2", builder.L2Info.TransferGas, common.Big1, nil) txs = append(txs, tx) err := builder.L2.Client.SendTransaction(ctx, tx) @@ -63,7 +67,7 @@ func TestPrunning(t *testing.T) { chainDb, err := stack.OpenDatabase("chaindb", 0, 0, "", false) Require(t, err) defer chainDb.Close() - entriesBeforePrunning := countDbEntries(chainDb) + chainDbEntriesBeforePrunning := countStateEntries(chainDb) prand := testhelpers.NewPseudoRandomDataSource(t, 1) var testKeys [][]byte @@ -93,12 +97,12 @@ func TestPrunning(t *testing.T) { } } - entriesAfterPrunning := countDbEntries(chainDb) - t.Log("db entries pre-prunning:", entriesBeforePrunning) - t.Log("db entries post-prunning:", entriesAfterPrunning) + chainDbEntriesAfterPrunning := countStateEntries(chainDb) + t.Log("db entries pre-prunning:", chainDbEntriesBeforePrunning) + t.Log("db entries post-prunning:", chainDbEntriesAfterPrunning) - if entriesAfterPrunning >= entriesBeforePrunning { - Fatal(t, "The db doesn't have less entries after prunning then before. Before:", entriesBeforePrunning, "After:", entriesAfterPrunning) + if chainDbEntriesAfterPrunning >= chainDbEntriesBeforePrunning { + Fatal(t, "The db doesn't have less entries after prunning then before. Before:", chainDbEntriesBeforePrunning, "After:", chainDbEntriesAfterPrunning) } }() builder := NewNodeBuilder(ctx).DefaultConfig(t, true) From a733e5a1b23d9e2f1aaa678d2a072b6be6d5388f Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 20 Dec 2023 16:25:47 -0700 Subject: [PATCH 15/68] Only lock redis in the batch poster when posting the batch --- arbnode/batch_poster.go | 30 ++++++++++++++++++++++++++++-- arbnode/redislock/redis.go | 21 +++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index a54336fd5a..4c8bd36f72 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -16,6 +16,7 @@ import ( "time" "github.com/andybalholm/brotli" + "github.com/go-redis/redis/v8" "github.com/spf13/pflag" "github.com/ethereum/go-ethereum" @@ -117,6 +118,7 @@ type BatchPosterConfig struct { DataPoster dataposter.DataPosterConfig `koanf:"data-poster" reload:"hot"` RedisUrl string `koanf:"redis-url"` RedisLock redislock.SimpleCfg `koanf:"redis-lock" reload:"hot"` + EnableRedisLock bool `koanf:"enable-redis-lock"` ExtraBatchGas uint64 `koanf:"extra-batch-gas" reload:"hot"` ParentChainWallet genericconf.WalletConfig `koanf:"parent-chain-wallet"` L1BlockBound string `koanf:"l1-block-bound" reload:"hot"` @@ -165,6 +167,7 @@ func BatchPosterConfigAddOptions(prefix string, f *pflag.FlagSet) { f.String(prefix+".gas-refunder-address", DefaultBatchPosterConfig.GasRefunderAddress, "The gas refunder contract address (optional)") f.Uint64(prefix+".extra-batch-gas", DefaultBatchPosterConfig.ExtraBatchGas, "use this much more gas than estimation says is necessary to post batches") f.String(prefix+".redis-url", DefaultBatchPosterConfig.RedisUrl, "if non-empty, the Redis URL to store queued transactions in") + f.Bool(prefix+".enable-redis-lock", DefaultBatchPosterConfig.EnableRedisLock, "if a Redis URL is specified, ensure only one batch poster attempts to post batches at a time") f.String(prefix+".l1-block-bound", DefaultBatchPosterConfig.L1BlockBound, "only post messages to batches when they're within the max future block/timestamp as of this L1 block tag (\"safe\", \"finalized\", \"latest\", or \"ignore\" to ignore this check)") f.Duration(prefix+".l1-block-bound-bypass", DefaultBatchPosterConfig.L1BlockBoundBypass, "post batches even if not within the layer 1 future bounds if we're within this margin of the max delay") redislock.AddConfigOptions(prefix+".redis-lock", f) @@ -190,6 +193,7 @@ var DefaultBatchPosterConfig = BatchPosterConfig{ L1BlockBound: "", L1BlockBoundBypass: time.Hour, RedisLock: redislock.DefaultCfg, + EnableRedisLock: true, } var DefaultBatchPosterL1WalletConfig = genericconf.WalletConfig{ @@ -215,6 +219,7 @@ var TestBatchPosterConfig = BatchPosterConfig{ ParentChainWallet: DefaultBatchPosterL1WalletConfig, L1BlockBound: "", L1BlockBoundBypass: time.Hour, + EnableRedisLock: true, } type BatchPosterOpts struct { @@ -254,7 +259,11 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e simpleRedisLockConfig.Key = batchPosterSimpleRedisLockKey return &simpleRedisLockConfig } - redisLock, err := redislock.NewSimple(redisClient, redisLockConfigFetcher, func() bool { return opts.SyncMonitor.Synced() }) + var redisClientForLock redis.UniversalClient + if opts.Config().EnableRedisLock { + redisClientForLock = redisClient + } + redisLock, err := redislock.NewSimple(redisClientForLock, redisLockConfigFetcher, func() bool { return opts.SyncMonitor.Synced() }) if err != nil { return nil, err } @@ -830,6 +839,8 @@ func (b *BatchPoster) estimateGas(ctx context.Context, sequencerMessage []byte, const ethPosBlockTime = 12 * time.Second +var errAttemptLockFailed = errors.New("failed to acquire lock; either another batch poster posted a batch or this node fell behind") + func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) { if b.batchReverted.Load() { return false, fmt.Errorf("batch was reverted, not posting any more batches") @@ -1006,6 +1017,10 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) } if b.daWriter != nil { + if !b.redisLock.AttemptLock(ctx) { + return false, errAttemptLockFailed + } + cert, err := b.daWriter.Store(ctx, sequencerMsg, uint64(time.Now().Add(config.DASRetentionPeriod).Unix()), []byte{}) // b.daWriter will append signature if enabled if errors.Is(err, das.BatchToDasFailed) { if config.DisableDasFallbackStoreDataOnChain { @@ -1043,6 +1058,9 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) if err != nil { return false, err } + if !b.redisLock.AttemptLock(ctx) { + return false, errAttemptLockFailed + } tx, err := b.dataPoster.PostTransaction(ctx, firstMsgTime, nonce, @@ -1147,8 +1165,16 @@ func (b *BatchPoster) Start(ctxIn context.Context) { batchPosterWalletBalance.Update(arbmath.BalancePerEther(walletBalance)) } } - if !b.redisLock.AttemptLock(ctx) { + couldLock, err := b.redisLock.CouldAcquireLock(ctx) + if err != nil { + log.Warn("Error checking if we could acquire redis lock", "err", err) + // Might as well try, worst case we fail to lock + couldLock = true + } + if !couldLock { + log.Debug("Not posting batches right now because another batch poster has the lock or this node is behind") b.building = nil + b.firstEphemeralError = time.Time{} return b.config().PollInterval } posted, err := b.maybePostSequencerBatch(ctx) diff --git a/arbnode/redislock/redis.go b/arbnode/redislock/redis.go index c8252e059f..fe46612527 100644 --- a/arbnode/redislock/redis.go +++ b/arbnode/redislock/redis.go @@ -143,6 +143,27 @@ func (l *Simple) Locked() bool { return time.Now().Before(atomicTimeRead(&l.lockedUntil)) } +// Returns true if a call to AttemptLock will likely succeed +func (l *Simple) CouldAcquireLock(ctx context.Context) (bool, error) { + if l.Locked() { + return true, nil + } + if l.stopping || !l.readyToLock() { + return false, nil + } + // l.client shouldn't be nil here because Locked would've returned true + current, err := l.client.Get(ctx, l.config().Key).Result() + if errors.Is(err, redis.Nil) { + // Lock is free for the taking + return true, nil + } + if err != nil { + return false, err + } + // return true if the lock is free for the taking or is already ours + return current == "" || current == l.myId, nil +} + func (l *Simple) Release(ctx context.Context) { l.mutex.Lock() defer l.mutex.Unlock() From 4e73be72871aa342b918c568c3439a11256278c6 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 21 Dec 2023 00:37:17 -0700 Subject: [PATCH 16/68] Always broadcast feed, even when the node isn't the sequencer --- arbnode/transaction_streamer.go | 12 ++++++------ broadcaster/broadcaster.go | 25 ++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index db0658f923..24ef2a7cc4 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -862,12 +862,6 @@ func (s *TransactionStreamer) WriteMessageFromSequencer(pos arbutil.MessageIndex return err } - if s.broadcastServer != nil { - if err := s.broadcastServer.BroadcastSingle(msgWithMeta, pos); err != nil { - log.Error("failed broadcasting message", "pos", pos, "err", err) - } - } - return nil } @@ -927,6 +921,12 @@ func (s *TransactionStreamer) writeMessages(pos arbutil.MessageIndex, messages [ default: } + if s.broadcastServer != nil { + if err := s.broadcastServer.BroadcastMessages(messages, pos); err != nil { + log.Error("failed broadcasting message", "pos", pos, "err", err) + } + } + return nil } diff --git a/broadcaster/broadcaster.go b/broadcaster/broadcaster.go index 8a70e39810..38ffb0696c 100644 --- a/broadcaster/broadcaster.go +++ b/broadcaster/broadcaster.go @@ -5,6 +5,7 @@ package broadcaster import ( "context" + "errors" "net" "github.com/gobwas/ws" @@ -56,10 +57,11 @@ func (b *Broadcaster) NewBroadcastFeedMessage(message arbostypes.MessageWithMeta }, nil } -func (b *Broadcaster) BroadcastSingle(msg arbostypes.MessageWithMetadata, seq arbutil.MessageIndex) error { +func (b *Broadcaster) BroadcastSingle(msg arbostypes.MessageWithMetadata, seq arbutil.MessageIndex) (err error) { defer func() { if r := recover(); r != nil { log.Error("recovered error in BroadcastSingle", "recover", r) + err = errors.New("panic in BroadcastSingle") } }() bfm, err := b.NewBroadcastFeedMessage(msg, seq) @@ -79,6 +81,27 @@ func (b *Broadcaster) BroadcastSingleFeedMessage(bfm *m.BroadcastFeedMessage) { b.BroadcastFeedMessages(broadcastFeedMessages) } +func (b *Broadcaster) BroadcastMessages(messages []arbostypes.MessageWithMetadata, seq arbutil.MessageIndex) (err error) { + defer func() { + if r := recover(); r != nil { + log.Error("recovered error in BroadcastMessages", "recover", r) + err = errors.New("panic in BroadcastMessages") + } + }() + var feedMessages []*m.BroadcastFeedMessage + for _, msg := range messages { + bfm, err := b.NewBroadcastFeedMessage(msg, seq) + if err != nil { + return err + } + feedMessages = append(feedMessages, bfm) + } + + b.BroadcastFeedMessages(feedMessages) + + return nil +} + func (b *Broadcaster) BroadcastFeedMessages(messages []*m.BroadcastFeedMessage) { bm := &m.BroadcastMessage{ From ae54e0852e53e78f6fb7fafd531bdb066dec7369 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 21 Dec 2023 00:42:56 -0700 Subject: [PATCH 17/68] Add comment about PopulateFeedBacklog in seq coordinator being redundant --- arbnode/seq_coordinator.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arbnode/seq_coordinator.go b/arbnode/seq_coordinator.go index cb6f4fe502..ecf38ddf42 100644 --- a/arbnode/seq_coordinator.go +++ b/arbnode/seq_coordinator.go @@ -650,6 +650,8 @@ func (c *SeqCoordinator) update(ctx context.Context) time.Duration { log.Warn("failed sequencing delayed messages after catching lock", "err", err) } } + // This should be redundant now that even non-primary sequencers broadcast over the feed, + // but the backlog efficiently deduplicates messages, so better safe than sorry. err = c.streamer.PopulateFeedBacklog() if err != nil { log.Warn("failed to populate the feed backlog on lockout acquisition", "err", err) From 29a8c854d4d7bdc13dfcb67299dcd5f200d7faa9 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 21 Dec 2023 00:47:13 -0700 Subject: [PATCH 18/68] Don't acquire redis lock in data poster --- arbnode/dataposter/data_poster.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index 059e080eea..191fd40708 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -86,6 +86,7 @@ type signerFn func(context.Context, common.Address, *types.Transaction) (*types. type AttemptLocker interface { AttemptLock(context.Context) bool + CouldAcquireLock(context.Context) (bool, error) } func parseReplacementTimes(val string) ([]time.Duration, error) { @@ -745,9 +746,6 @@ func (p *DataPoster) Start(ctxIn context.Context) { p.CallIteratively(func(ctx context.Context) time.Duration { p.mutex.Lock() defer p.mutex.Unlock() - if !p.redisLock.AttemptLock(ctx) { - return minWait - } err := p.updateBalance(ctx) if err != nil { log.Warn("failed to update tx poster balance", "err", err) @@ -777,10 +775,16 @@ func (p *DataPoster) Start(ctxIn context.Context) { log.Error("Failed to fetch tx queue contents", "err", err) return minWait } + tryToReplace, err := p.redisLock.CouldAcquireLock(ctx) + if err != nil { + log.Warn("Error checking if we could acquire redis lock", "err", err) + // Might as well try, worst case we hit contention on redis and fail + tryToReplace = true + } for index, tx := range queueContents { backlogOfBatches := len(queueContents) - index - 1 replacing := false - if now.After(tx.NextReplacement) { + if tryToReplace && now.After(tx.NextReplacement) { replacing = true err := p.replaceTx(ctx, tx, uint64(backlogOfBatches)) p.maybeLogError(err, tx, "failed to replace-by-fee transaction") From 90ddc9286a333d74833aab8666ca96cf0abb9b83 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Thu, 21 Dec 2023 14:31:15 +0100 Subject: [PATCH 19/68] Implement test package for external signer, refactor existing tests, set chain ID in dataposter transaction --- arbnode/batch_poster.go | 20 +- arbnode/dataposter/data_poster.go | 28 ++- arbnode/dataposter/dataposter_test.go | 199 +++--------------- .../externalsignertest/externalsignertest.go | 153 ++++++++++++++ arbnode/node.go | 27 ++- arbutil/wait_for_l1.go | 1 + system_tests/batch_poster_test.go | 58 +++-- system_tests/external_signer.go | 188 ----------------- system_tests/staker_test.go | 50 +++-- 9 files changed, 316 insertions(+), 408 deletions(-) create mode 100644 arbnode/dataposter/externalsignertest/externalsignertest.go delete mode 100644 system_tests/external_signer.go diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index a54336fd5a..edd9799ccc 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -218,15 +218,16 @@ var TestBatchPosterConfig = BatchPosterConfig{ } type BatchPosterOpts struct { - DataPosterDB ethdb.Database - L1Reader *headerreader.HeaderReader - Inbox *InboxTracker - Streamer *TransactionStreamer - SyncMonitor *SyncMonitor - Config BatchPosterConfigFetcher - DeployInfo *chaininfo.RollupAddresses - TransactOpts *bind.TransactOpts - DAWriter das.DataAvailabilityServiceWriter + DataPosterDB ethdb.Database + L1Reader *headerreader.HeaderReader + Inbox *InboxTracker + Streamer *TransactionStreamer + SyncMonitor *SyncMonitor + Config BatchPosterConfigFetcher + DeployInfo *chaininfo.RollupAddresses + TransactOpts *bind.TransactOpts + DAWriter das.DataAvailabilityServiceWriter + ParentChainID *big.Int } func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, error) { @@ -291,6 +292,7 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e MetadataRetriever: b.getBatchPosterPosition, ExtraBacklog: b.GetBacklogEstimate, RedisKey: "data-poster.queue", + ParentChainID: opts.ParentChainID, }) if err != nil { return nil, err diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index d5b67505f5..c8a0718053 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -64,6 +64,7 @@ type DataPoster struct { replacementTimes []time.Duration metadataRetriever func(ctx context.Context, blockNum *big.Int) ([]byte, error) extraBacklog func() uint64 + parentChainID *big.Int // These fields are protected by the mutex. // TODO: factor out these fields into separate structure, since now one @@ -120,6 +121,7 @@ type DataPosterOpts struct { MetadataRetriever func(ctx context.Context, blockNum *big.Int) ([]byte, error) ExtraBacklog func() uint64 RedisKey string // Redis storage key + ParentChainID *big.Int } func NewDataPoster(ctx context.Context, opts *DataPosterOpts) (*DataPoster, error) { @@ -180,6 +182,7 @@ func NewDataPoster(ctx context.Context, opts *DataPosterOpts) (*DataPoster, erro errorCount: make(map[uint64]int), maxFeeCapExpression: expression, extraBacklog: opts.ExtraBacklog, + parentChainID: opts.ParentChainID, } if dp.extraBacklog == nil { dp.extraBacklog = func() uint64 { return 0 } @@ -197,6 +200,7 @@ func NewDataPoster(ctx context.Context, opts *DataPosterOpts) (*DataPoster, erro }, } } + return dp, nil } @@ -240,10 +244,11 @@ func rpcClient(ctx context.Context, opts *ExternalSignerCfg) (*rpc.Client, error // txToSendTxArgs converts transaction to SendTxArgs. This is needed for // external signer to specify From field. func txToSendTxArgs(addr common.Address, tx *types.Transaction) (*apitypes.SendTxArgs, error) { - if tx.To() == nil { - return nil, fmt.Errorf("transaction %v has no destination", tx.Hash()) + var to *common.MixedcaseAddress + if tx.To() != nil { + to = new(common.MixedcaseAddress) + *to = common.NewMixedcaseAddress(*tx.To()) } - to := common.NewMixedcaseAddress(*tx.To()) data := (hexutil.Bytes)(tx.Data()) val := (*hexutil.Big)(tx.Value()) if val == nil { @@ -252,7 +257,7 @@ func txToSendTxArgs(addr common.Address, tx *types.Transaction) (*apitypes.SendT al := tx.AccessList() return &apitypes.SendTxArgs{ From: common.NewMixedcaseAddress(addr), - To: &to, + To: to, Gas: hexutil.Uint64(tx.Gas()), GasPrice: (*hexutil.Big)(tx.GasPrice()), MaxFeePerGas: (*hexutil.Big)(tx.GasFeeCap()), @@ -288,17 +293,17 @@ func externalSigner(ctx context.Context, opts *ExternalSignerCfg) (signerFn, com return nil, fmt.Errorf("error converting transaction to sendTxArgs: %w", err) } if err := client.CallContext(ctx, &data, opts.Method, args); err != nil { - return nil, fmt.Errorf("signing transaction: %w", err) + return nil, fmt.Errorf("making signing request to external signer: %w", err) } - var signedTx types.Transaction - if err := rlp.DecodeBytes(data, &signedTx); err != nil { - return nil, fmt.Errorf("error decoding signed transaction: %w", err) + signedTx := &types.Transaction{} + if err := signedTx.UnmarshalBinary(data); err != nil { + return nil, fmt.Errorf("unmarshaling signed transaction: %w", err) } hasher := types.LatestSignerForChainID(tx.ChainId()) - if h := hasher.Hash(args.ToTransaction()); h != hasher.Hash(&signedTx) { - return nil, fmt.Errorf("transaction: %x from external signer differs from request: %x", hasher.Hash(&signedTx), h) + if h := hasher.Hash(args.ToTransaction()); h != hasher.Hash(signedTx) { + return nil, fmt.Errorf("transaction: %x from external signer differs from request: %x", hasher.Hash(signedTx), h) } - return &signedTx, nil + return signedTx, nil }, sender, nil } @@ -583,6 +588,7 @@ func (p *DataPoster) PostTransaction(ctx context.Context, dataCreatedAt time.Tim Value: value, Data: calldata, AccessList: accessList, + ChainID: p.parentChainID, } fullTx, err := p.signer(ctx, p.Sender(), types.NewTx(&inner)) if err != nil { diff --git a/arbnode/dataposter/dataposter_test.go b/arbnode/dataposter/dataposter_test.go index e8028963f7..3d7fa60dc7 100644 --- a/arbnode/dataposter/dataposter_test.go +++ b/arbnode/dataposter/dataposter_test.go @@ -2,27 +2,18 @@ package dataposter import ( "context" - "crypto/tls" - "crypto/x509" - "encoding/json" "fmt" - "io" "math/big" "net/http" - "os" "testing" "time" "github.com/Knetic/govaluate" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/google/go-cmp/cmp" + "github.com/offchainlabs/nitro/arbnode/dataposter/externalsignertest" ) func TestParseReplacementTimes(t *testing.T) { @@ -60,9 +51,24 @@ func TestParseReplacementTimes(t *testing.T) { } } +func signerTestCfg(addr common.Address) (*ExternalSignerCfg, error) { + cp, err := externalsignertest.CertPaths() + if err != nil { + return nil, fmt.Errorf("getting certificates path: %w", err) + } + return &ExternalSignerCfg{ + Address: common.Bytes2Hex(addr.Bytes()), + URL: externalsignertest.SignerURL, + Method: externalsignertest.SignerMethod, + RootCA: cp.ServerCert, + ClientCert: cp.ClientCert, + ClientPrivateKey: cp.ClientKey, + }, nil +} + func TestExternalSigner(t *testing.T) { ctx := context.Background() - httpSrv, srv := newServer(ctx, t) + httpSrv, srv := externalsignertest.NewServer(ctx, t) t.Cleanup(func() { if err := httpSrv.Shutdown(ctx); err != nil { t.Fatalf("Error shutting down http server: %v", err) @@ -76,19 +82,25 @@ func TestExternalSigner(t *testing.T) { return } }() - signer, addr, err := externalSigner(ctx, - &ExternalSignerCfg{ - Address: srv.address.Hex(), - URL: "https://localhost:1234", - Method: "test_signTransaction", - RootCA: cert, - ClientCert: "./testdata/client.crt", - ClientPrivateKey: "./testdata/client.key", - }) + signerCfg, err := signerTestCfg(srv.Address) + if err != nil { + t.Fatalf("Error getting signer test config: %v", err) + } + signer, addr, err := externalSigner(ctx, signerCfg) if err != nil { t.Fatalf("Error getting external signer: %v", err) } - tx := types.NewTransaction(13, common.HexToAddress("0x01"), big.NewInt(1), 2, big.NewInt(3), []byte{0x01, 0x02, 0x03}) + tx := types.NewTx( + &types.DynamicFeeTx{ + Nonce: 13, + GasTipCap: big.NewInt(1), + GasFeeCap: big.NewInt(1), + Gas: 3, + To: nil, + Value: big.NewInt(1), + Data: []byte{0x01, 0x02, 0x03}, + }, + ) got, err := signer(ctx, addr, tx) if err != nil { t.Fatalf("Error signing transaction with external signer: %v", err) @@ -97,7 +109,7 @@ func TestExternalSigner(t *testing.T) { if err != nil { t.Fatalf("Error converting transaction to sendTxArgs: %v", err) } - want, err := srv.signerFn(addr, args.ToTransaction()) + want, err := srv.SignerFn(addr, args.ToTransaction()) if err != nil { t.Fatalf("Error signing transaction: %v", err) } @@ -106,149 +118,6 @@ func TestExternalSigner(t *testing.T) { } } -type server struct { - handlers map[string]func(*json.RawMessage) (string, error) - signerFn bind.SignerFn - address common.Address -} - -type request struct { - ID *json.RawMessage `json:"id"` - Method string `json:"method"` - Params *json.RawMessage `json:"params"` -} - -type response struct { - ID *json.RawMessage `json:"id"` - Result string `json:"result,omitempty"` -} - -// newServer returns http server and server struct that implements RPC methods. -// It sets up an account in temporary directory and cleans up after test is -// done. -func newServer(ctx context.Context, t *testing.T) (*http.Server, *server) { - t.Helper() - signer, address, err := setupAccount("/tmp/keystore") - if err != nil { - t.Fatalf("Error setting up account: %v", err) - } - t.Cleanup(func() { os.RemoveAll("/tmp/keystore") }) - - s := &server{signerFn: signer, address: address} - s.handlers = map[string]func(*json.RawMessage) (string, error){ - "test_signTransaction": s.signTransaction, - } - m := http.NewServeMux() - - clientCert, err := os.ReadFile("./testdata/client.crt") - if err != nil { - t.Fatalf("Error reading client certificate: %v", err) - } - pool := x509.NewCertPool() - pool.AppendCertsFromPEM(clientCert) - - httpSrv := &http.Server{ - Addr: ":1234", - Handler: m, - ReadTimeout: 5 * time.Second, - TLSConfig: &tls.Config{ - MinVersion: tls.VersionTLS12, - ClientAuth: tls.RequireAndVerifyClientCert, - ClientCAs: pool, - }, - } - m.HandleFunc("/", s.mux) - return httpSrv, s -} - -// setupAccount creates a new account in a given directory, unlocks it, creates -// signer with that account and returns it along with account address. -func setupAccount(dir string) (bind.SignerFn, common.Address, error) { - ks := keystore.NewKeyStore( - dir, - keystore.StandardScryptN, - keystore.StandardScryptP, - ) - a, err := ks.NewAccount("password") - if err != nil { - return nil, common.Address{}, fmt.Errorf("creating account account: %w", err) - } - if err := ks.Unlock(a, "password"); err != nil { - return nil, common.Address{}, fmt.Errorf("unlocking account: %w", err) - } - txOpts, err := bind.NewKeyStoreTransactorWithChainID(ks, a, big.NewInt(1)) - if err != nil { - return nil, common.Address{}, fmt.Errorf("creating transactor: %w", err) - } - return txOpts.Signer, a.Address, nil -} - -// UnmarshallFirst unmarshalls slice of params and returns the first one. -// Parameters in Go ethereum RPC calls are marashalled as slices. E.g. -// eth_sendRawTransaction or eth_signTransaction, marshall transaction as a -// slice of transactions in a message: -// https://github.com/ethereum/go-ethereum/blob/0004c6b229b787281760b14fb9460ffd9c2496f1/rpc/client.go#L548 -func unmarshallFirst(params []byte) (*types.Transaction, error) { - var arr []apitypes.SendTxArgs - if err := json.Unmarshal(params, &arr); err != nil { - return nil, fmt.Errorf("unmarshaling first param: %w", err) - } - if len(arr) != 1 { - return nil, fmt.Errorf("argument should be a single transaction, but got: %d", len(arr)) - } - return arr[0].ToTransaction(), nil -} - -func (s *server) signTransaction(params *json.RawMessage) (string, error) { - tx, err := unmarshallFirst(*params) - if err != nil { - return "", err - } - signedTx, err := s.signerFn(s.address, tx) - if err != nil { - return "", fmt.Errorf("signing transaction: %w", err) - } - data, err := rlp.EncodeToBytes(signedTx) - if err != nil { - return "", fmt.Errorf("rlp encoding transaction: %w", err) - } - return hexutil.Encode(data), nil -} - -func (s *server) mux(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - http.Error(w, "can't read body", http.StatusBadRequest) - return - } - var req request - if err := json.Unmarshal(body, &req); err != nil { - http.Error(w, "can't unmarshal JSON request", http.StatusBadRequest) - return - } - method, ok := s.handlers[req.Method] - if !ok { - http.Error(w, "method not found", http.StatusNotFound) - return - } - result, err := method(req.Params) - if err != nil { - fmt.Printf("error calling method: %v\n", err) - http.Error(w, "error calling method", http.StatusInternalServerError) - return - } - resp := response{ID: req.ID, Result: result} - respBytes, err := json.Marshal(resp) - if err != nil { - http.Error(w, fmt.Sprintf("error encoding response: %v", err), http.StatusInternalServerError) - return - } - w.Header().Set("Content-Type", "application/json") - if _, err := w.Write(respBytes); err != nil { - fmt.Printf("error writing response: %v\n", err) - } -} - func TestMaxFeeCapFormulaCalculation(t *testing.T) { // This test alerts, by failing, if the max fee cap formula were to be changed in the DefaultDataPosterConfig to // use new variables other than the ones that are keys of 'parameters' map below diff --git a/arbnode/dataposter/externalsignertest/externalsignertest.go b/arbnode/dataposter/externalsignertest/externalsignertest.go new file mode 100644 index 0000000000..7d15515feb --- /dev/null +++ b/arbnode/dataposter/externalsignertest/externalsignertest.go @@ -0,0 +1,153 @@ +package externalsignertest + +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "math/big" + "net/http" + "os" + "path/filepath" + "runtime" + "strings" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/signer/core/apitypes" +) + +var ( + dataPosterPath = "arbnode/dataposter" + selfPath = filepath.Join(dataPosterPath, "externalsignertest") + + SignerPort = 1234 + SignerURL = fmt.Sprintf("https://localhost:%v", SignerPort) + SignerMethod = "test_signTransaction" +) + +type CertAbsPaths struct { + ServerCert string + ServerKey string + ClientCert string + ClientKey string +} + +func basePath() (string, error) { + _, file, _, ok := runtime.Caller(1) + if !ok { + return "", fmt.Errorf("error getting caller") + } + idx := strings.Index(file, selfPath) + if idx == -1 { + return "", fmt.Errorf("error determining base path, selfPath: %q is not substring of current file path: %q", selfPath, file) + } + return file[:idx], nil +} + +func testDataPath() (string, error) { + base, err := basePath() + if err != nil { + return "", fmt.Errorf("getting base path: %w", err) + } + return filepath.Join(base, dataPosterPath, "testdata"), nil +} + +func CertPaths() (*CertAbsPaths, error) { + td, err := testDataPath() + if err != nil { + return nil, fmt.Errorf("getting test data path: %w", err) + } + return &CertAbsPaths{ + ServerCert: filepath.Join(td, "localhost.crt"), + ServerKey: filepath.Join(td, "localhost.key"), + ClientCert: filepath.Join(td, "client.crt"), + ClientKey: filepath.Join(td, "client.key"), + }, nil +} + +func NewServer(ctx context.Context, t *testing.T) (*http.Server, *SignerAPI) { + rpcServer := rpc.NewServer() + signer, address, err := setupAccount("/tmp/keystore") + if err != nil { + t.Fatalf("Error setting up account: %v", err) + } + t.Cleanup(func() { os.RemoveAll("/tmp/keystore") }) + s := &SignerAPI{SignerFn: signer, Address: address} + if err := rpcServer.RegisterName("test", s); err != nil { + t.Fatalf("Failed to register EthSigningAPI, error: %v", err) + } + cp, err := CertPaths() + if err != nil { + t.Fatalf("Error getting certificate paths: %v", err) + } + clientCert, err := os.ReadFile(cp.ClientCert) + if err != nil { + t.Fatalf("Error reading client certificate: %v", err) + } + pool := x509.NewCertPool() + pool.AppendCertsFromPEM(clientCert) + + httpServer := &http.Server{ + Addr: fmt.Sprintf(":%d", SignerPort), + Handler: rpcServer, + ReadTimeout: 30 * time.Second, + ReadHeaderTimeout: 30 * time.Second, + WriteTimeout: 30 * time.Second, + IdleTimeout: 120 * time.Second, + TLSConfig: &tls.Config{ + MinVersion: tls.VersionTLS12, + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: pool, + }, + } + + return httpServer, s +} + +// setupAccount creates a new account in a given directory, unlocks it, creates +// signer with that account and returns it along with account address. +func setupAccount(dir string) (bind.SignerFn, common.Address, error) { + ks := keystore.NewKeyStore( + dir, + keystore.StandardScryptN, + keystore.StandardScryptP, + ) + a, err := ks.NewAccount("password") + if err != nil { + return nil, common.Address{}, fmt.Errorf("creating account account: %w", err) + } + if err := ks.Unlock(a, "password"); err != nil { + return nil, common.Address{}, fmt.Errorf("unlocking account: %w", err) + } + txOpts, err := bind.NewKeyStoreTransactorWithChainID(ks, a, big.NewInt(1337)) + if err != nil { + return nil, common.Address{}, fmt.Errorf("creating transactor: %w", err) + } + return txOpts.Signer, a.Address, nil +} + +type SignerAPI struct { + SignerFn bind.SignerFn + Address common.Address +} + +func (a *SignerAPI) SignTransaction(ctx context.Context, req *apitypes.SendTxArgs) (hexutil.Bytes, error) { + if req == nil { + return nil, fmt.Errorf("nil request") + } + signedTx, err := a.SignerFn(a.Address, req.ToTransaction()) + if err != nil { + return nil, fmt.Errorf("signing transaction: %w", err) + } + signedTxBytes, err := signedTx.MarshalBinary() + if err != nil { + return nil, fmt.Errorf("marshaling signed transaction: %w", err) + } + return signedTxBytes, nil +} diff --git a/arbnode/node.go b/arbnode/node.go index 8be2a982cf..4c2da8a6d1 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -301,6 +301,7 @@ func checkArbDbSchemaVersion(arbDb ethdb.Database) error { func StakerDataposter( ctx context.Context, db ethdb.Database, l1Reader *headerreader.HeaderReader, transactOpts *bind.TransactOpts, cfgFetcher ConfigFetcher, syncMonitor *SyncMonitor, + parentChainID *big.Int, ) (*dataposter.DataPoster, error) { cfg := cfgFetcher.Get() if transactOpts == nil && cfg.Staker.DataPoster.ExternalSigner.URL == "" { @@ -339,6 +340,7 @@ func StakerDataposter( Config: dpCfg, MetadataRetriever: mdRetriever, RedisKey: sender + ".staker-data-poster.queue", + ParentChainID: parentChainID, }) } @@ -568,6 +570,11 @@ func createNodeImpl( var stakerObj *staker.Staker var messagePruner *MessagePruner + parentChainID, err := l1client.ChainID(ctx) + if err != nil { + return nil, fmt.Errorf("getting parent chain id: %w", err) + } + if config.Staker.Enable { dp, err := StakerDataposter( ctx, @@ -576,6 +583,7 @@ func createNodeImpl( txOptsValidator, configFetcher, syncMonitor, + parentChainID, ) if err != nil { return nil, err @@ -643,15 +651,16 @@ func createNodeImpl( return nil, errors.New("batchposter, but no TxOpts") } batchPoster, err = NewBatchPoster(ctx, &BatchPosterOpts{ - DataPosterDB: rawdb.NewTable(arbDb, storage.BatchPosterPrefix), - L1Reader: l1Reader, - Inbox: inboxTracker, - Streamer: txStreamer, - SyncMonitor: syncMonitor, - Config: func() *BatchPosterConfig { return &configFetcher.Get().BatchPoster }, - DeployInfo: deployInfo, - TransactOpts: txOptsBatchPoster, - DAWriter: daWriter, + DataPosterDB: rawdb.NewTable(arbDb, storage.BatchPosterPrefix), + L1Reader: l1Reader, + Inbox: inboxTracker, + Streamer: txStreamer, + SyncMonitor: syncMonitor, + Config: func() *BatchPosterConfig { return &configFetcher.Get().BatchPoster }, + DeployInfo: deployInfo, + TransactOpts: txOptsBatchPoster, + DAWriter: daWriter, + ParentChainID: parentChainID, }) if err != nil { return nil, err diff --git a/arbutil/wait_for_l1.go b/arbutil/wait_for_l1.go index 12d494a230..b66710dbf0 100644 --- a/arbutil/wait_for_l1.go +++ b/arbutil/wait_for_l1.go @@ -24,6 +24,7 @@ type L1Interface interface { TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error) BlockNumber(ctx context.Context) (uint64, error) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) + ChainID(ctx context.Context) (*big.Int, error) } func SendTxAsCall(ctx context.Context, client L1Interface, tx *types.Transaction, from common.Address, blockNum *big.Int, unlimitedGas bool) ([]byte, error) { diff --git a/system_tests/batch_poster_test.go b/system_tests/batch_poster_test.go index 873a87c6f9..f7bf74f699 100644 --- a/system_tests/batch_poster_test.go +++ b/system_tests/batch_poster_test.go @@ -20,6 +20,8 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/arbnode" + "github.com/offchainlabs/nitro/arbnode/dataposter" + "github.com/offchainlabs/nitro/arbnode/dataposter/externalsignertest" "github.com/offchainlabs/nitro/solgen/go/bridgegen" "github.com/offchainlabs/nitro/solgen/go/upgrade_executorgen" "github.com/offchainlabs/nitro/util/redisutil" @@ -60,10 +62,29 @@ func addNewBatchPoster(ctx context.Context, t *testing.T, builder *NodeBuilder, } } +func externalSignerTestCfg(addr common.Address) (*dataposter.ExternalSignerCfg, error) { + cp, err := externalsignertest.CertPaths() + if err != nil { + return nil, fmt.Errorf("getting certificates path: %w", err) + } + return &dataposter.ExternalSignerCfg{ + Address: common.Bytes2Hex(addr.Bytes()), + URL: externalsignertest.SignerURL, + Method: externalsignertest.SignerMethod, + RootCA: cp.ServerCert, + ClientCert: cp.ClientCert, + ClientPrivateKey: cp.ClientKey, + }, nil +} + func testBatchPosterParallel(t *testing.T, useRedis bool) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - httpSrv, srv := newServer(ctx, t) + httpSrv, srv := externalsignertest.NewServer(ctx, t) + cp, err := externalsignertest.CertPaths() + if err != nil { + t.Fatalf("Error getting cert paths: %v", err) + } t.Cleanup(func() { if err := httpSrv.Shutdown(ctx); err != nil { t.Fatalf("Error shutting down http server: %v", err) @@ -71,7 +92,7 @@ func testBatchPosterParallel(t *testing.T, useRedis bool) { }) go func() { log.Debug("Server is listening on port 1234...") - if err := httpSrv.ListenAndServeTLS(signerServerCert, signerServerKey); err != nil && err != http.ErrServerClosed { + if err := httpSrv.ListenAndServeTLS(cp.ServerCert, cp.ServerKey); err != nil && err != http.ErrServerClosed { log.Debug("ListenAndServeTLS() failed", "error", err) return } @@ -93,7 +114,11 @@ func testBatchPosterParallel(t *testing.T, useRedis bool) { builder := NewNodeBuilder(ctx).DefaultConfig(t, true) builder.nodeConfig.BatchPoster.Enable = false builder.nodeConfig.BatchPoster.RedisUrl = redisUrl - builder.nodeConfig.BatchPoster.DataPoster.ExternalSigner = *externalSignerTestCfg(srv.address) + signerCfg, err := externalSignerTestCfg(srv.Address) + if err != nil { + t.Fatalf("Error getting external signer config: %v", err) + } + builder.nodeConfig.BatchPoster.DataPoster.ExternalSigner = *signerCfg cleanup := builder.Build(t) defer cleanup() @@ -101,10 +126,10 @@ func testBatchPosterParallel(t *testing.T, useRedis bool) { defer cleanupB() builder.L2Info.GenerateAccount("User2") - addNewBatchPoster(ctx, t, builder, srv.address) + addNewBatchPoster(ctx, t, builder, srv.Address) builder.L1.SendWaitTestTransactions(t, []*types.Transaction{ - builder.L1Info.PrepareTxTo("Faucet", &srv.address, 30000, big.NewInt(1e18), nil)}) + builder.L1Info.PrepareTxTo("Faucet", &srv.Address, 30000, big.NewInt(1e18), nil)}) var txs []*types.Transaction @@ -128,20 +153,25 @@ func testBatchPosterParallel(t *testing.T, useRedis bool) { builder.nodeConfig.BatchPoster.MaxSize = len(firstTxData) * 2 startL1Block, err := builder.L1.Client.BlockNumber(ctx) Require(t, err) + parentChainID, err := builder.L1.Client.ChainID(ctx) + if err != nil { + t.Fatalf("Failed to get parent chain id: %v", err) + } for i := 0; i < parallelBatchPosters; i++ { // Make a copy of the batch poster config so NewBatchPoster calling Validate() on it doesn't race batchPosterConfig := builder.nodeConfig.BatchPoster batchPoster, err := arbnode.NewBatchPoster(ctx, &arbnode.BatchPosterOpts{ - DataPosterDB: nil, - L1Reader: builder.L2.ConsensusNode.L1Reader, - Inbox: builder.L2.ConsensusNode.InboxTracker, - Streamer: builder.L2.ConsensusNode.TxStreamer, - SyncMonitor: builder.L2.ConsensusNode.SyncMonitor, - Config: func() *arbnode.BatchPosterConfig { return &batchPosterConfig }, - DeployInfo: builder.L2.ConsensusNode.DeployInfo, - TransactOpts: &seqTxOpts, - DAWriter: nil, + DataPosterDB: nil, + L1Reader: builder.L2.ConsensusNode.L1Reader, + Inbox: builder.L2.ConsensusNode.InboxTracker, + Streamer: builder.L2.ConsensusNode.TxStreamer, + SyncMonitor: builder.L2.ConsensusNode.SyncMonitor, + Config: func() *arbnode.BatchPosterConfig { return &batchPosterConfig }, + DeployInfo: builder.L2.ConsensusNode.DeployInfo, + TransactOpts: &seqTxOpts, + DAWriter: nil, + ParentChainID: parentChainID, }, ) Require(t, err) diff --git a/system_tests/external_signer.go b/system_tests/external_signer.go deleted file mode 100644 index 1ee9c85581..0000000000 --- a/system_tests/external_signer.go +++ /dev/null @@ -1,188 +0,0 @@ -package arbtest - -import ( - "context" - "crypto/tls" - "crypto/x509" - "encoding/json" - "fmt" - "io" - "math/big" - "net/http" - "os" - "testing" - "time" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/signer/core/apitypes" - "github.com/offchainlabs/nitro/arbnode/dataposter" -) - -var ( - signerPort = 1234 - signerURL = fmt.Sprintf("https://localhost:%v", signerPort) - signerMethod = "test_signTransaction" - signerServerCert = "../arbnode/dataposter/testdata/localhost.crt" - signerServerKey = "../arbnode/dataposter/testdata/localhost.key" - signerClientCert = "../arbnode/dataposter/testdata/client.crt" - signerClientPrivateKey = "../arbnode/dataposter/testdata/client.key" -) - -func externalSignerTestCfg(addr common.Address) *dataposter.ExternalSignerCfg { - return &dataposter.ExternalSignerCfg{ - Address: common.Bytes2Hex(addr.Bytes()), - URL: signerURL, - Method: signerMethod, - RootCA: signerServerCert, - ClientCert: signerClientCert, - ClientPrivateKey: signerClientPrivateKey, - } -} - -type server struct { - handlers map[string]func(*json.RawMessage) (string, error) - signerFn bind.SignerFn - address common.Address -} - -type request struct { - ID *json.RawMessage `json:"id"` - Method string `json:"method"` - Params *json.RawMessage `json:"params"` -} - -type response struct { - ID *json.RawMessage `json:"id"` - Result string `json:"result,omitempty"` -} - -// newServer returns http server and server struct that implements RPC methods. -// It sets up an account in temporary directory and cleans up after test is -// done. -func newServer(ctx context.Context, t *testing.T) (*http.Server, *server) { - t.Helper() - signer, address, err := setupAccount("/tmp/keystore") - if err != nil { - t.Fatalf("Error setting up account: %v", err) - } - t.Cleanup(func() { os.RemoveAll("/tmp/keystore") }) - - s := &server{signerFn: signer, address: address} - s.handlers = map[string]func(*json.RawMessage) (string, error){ - signerMethod: s.signTransaction, - } - m := http.NewServeMux() - - clientCert, err := os.ReadFile(signerClientCert) - if err != nil { - t.Fatalf("Error reading client certificate: %v", err) - } - pool := x509.NewCertPool() - pool.AppendCertsFromPEM(clientCert) - - httpSrv := &http.Server{ - Addr: fmt.Sprintf(":%v", signerPort), - Handler: m, - ReadTimeout: 5 * time.Second, - TLSConfig: &tls.Config{ - MinVersion: tls.VersionTLS12, - ClientAuth: tls.RequireAndVerifyClientCert, - ClientCAs: pool, - }, - } - m.HandleFunc("/", s.mux) - return httpSrv, s -} - -// setupAccount creates a new account in a given directory, unlocks it, creates -// signer with that account and returns it along with account address. -func setupAccount(dir string) (bind.SignerFn, common.Address, error) { - ks := keystore.NewKeyStore( - dir, - keystore.StandardScryptN, - keystore.StandardScryptP, - ) - a, err := ks.NewAccount("password") - if err != nil { - return nil, common.Address{}, fmt.Errorf("creating account account: %w", err) - } - if err := ks.Unlock(a, "password"); err != nil { - return nil, common.Address{}, fmt.Errorf("unlocking account: %w", err) - } - txOpts, err := bind.NewKeyStoreTransactorWithChainID(ks, a, big.NewInt(1337)) - if err != nil { - return nil, common.Address{}, fmt.Errorf("creating transactor: %w", err) - } - return txOpts.Signer, a.Address, nil -} - -// UnmarshallFirst unmarshalls slice of params and returns the first one. -// Parameters in Go ethereum RPC calls are marashalled as slices. E.g. -// eth_sendRawTransaction or eth_signTransaction, marshall transaction as a -// slice of transactions in a message: -// https://github.com/ethereum/go-ethereum/blob/0004c6b229b787281760b14fb9460ffd9c2496f1/rpc/client.go#L548 -func unmarshallFirst(params []byte) (*types.Transaction, error) { - var arr []apitypes.SendTxArgs - if err := json.Unmarshal(params, &arr); err != nil { - return nil, fmt.Errorf("unmarshaling first param: %w", err) - } - if len(arr) != 1 { - return nil, fmt.Errorf("argument should be a single transaction, but got: %d", len(arr)) - } - return arr[0].ToTransaction(), nil -} - -func (s *server) signTransaction(params *json.RawMessage) (string, error) { - tx, err := unmarshallFirst(*params) - if err != nil { - return "", err - } - signedTx, err := s.signerFn(s.address, tx) - if err != nil { - return "", fmt.Errorf("signing transaction: %w", err) - } - data, err := rlp.EncodeToBytes(signedTx) - if err != nil { - return "", fmt.Errorf("rlp encoding transaction: %w", err) - } - return hexutil.Encode(data), nil -} - -func (s *server) mux(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - http.Error(w, "can't read body", http.StatusBadRequest) - return - } - var req request - if err := json.Unmarshal(body, &req); err != nil { - http.Error(w, "can't unmarshal JSON request", http.StatusBadRequest) - return - } - method, ok := s.handlers[req.Method] - if !ok { - http.Error(w, "method not found", http.StatusNotFound) - return - } - result, err := method(req.Params) - if err != nil { - fmt.Printf("error calling method: %v\n", err) - http.Error(w, "error calling method", http.StatusInternalServerError) - return - } - resp := response{ID: req.ID, Result: result} - respBytes, err := json.Marshal(resp) - if err != nil { - http.Error(w, fmt.Sprintf("error encoding response: %v", err), http.StatusInternalServerError) - return - } - w.Header().Set("Content-Type", "application/json") - if _, err := w.Write(respBytes); err != nil { - fmt.Printf("error writing response: %v\n", err) - } -} diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index ce726dcf83..6e3ffd6125 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbnode" + "github.com/offchainlabs/nitro/arbnode/dataposter/externalsignertest" "github.com/offchainlabs/nitro/arbnode/dataposter/storage" "github.com/offchainlabs/nitro/arbos/l2pricing" "github.com/offchainlabs/nitro/solgen/go/mocksgen" @@ -60,7 +61,11 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) t.Parallel() ctx, cancelCtx := context.WithCancel(context.Background()) defer cancelCtx() - httpSrv, srv := newServer(ctx, t) + httpSrv, srv := externalsignertest.NewServer(ctx, t) + cp, err := externalsignertest.CertPaths() + if err != nil { + t.Fatalf("Error getting cert paths: %v", err) + } t.Cleanup(func() { if err := httpSrv.Shutdown(ctx); err != nil { t.Fatalf("Error shutting down http server: %v", err) @@ -68,7 +73,7 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) }) go func() { log.Debug("Server is listening on port 1234...") - if err := httpSrv.ListenAndServeTLS(signerServerCert, signerServerKey); err != nil && err != http.ErrServerClosed { + if err := httpSrv.ListenAndServeTLS(cp.ServerCert, cp.ServerKey); err != nil && err != http.ErrServerClosed { log.Debug("ListenAndServeTLS() failed", "error", err) return } @@ -86,10 +91,10 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) cleanupA := builder.Build(t) defer cleanupA() - addNewBatchPoster(ctx, t, builder, srv.address) + addNewBatchPoster(ctx, t, builder, srv.Address) builder.L1.SendWaitTestTransactions(t, []*types.Transaction{ - builder.L1Info.PrepareTxTo("Faucet", &srv.address, 30000, big.NewInt(1).Mul(big.NewInt(1e18), big.NewInt(1e18)), nil)}) + builder.L1Info.PrepareTxTo("Faucet", &srv.Address, 30000, big.NewInt(1).Mul(big.NewInt(1e18), big.NewInt(1e18)), nil)}) l2nodeA := builder.L2.ConsensusNode execNodeA := builder.L2.ExecNode @@ -152,7 +157,7 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) rollupABI, err := abi.JSON(strings.NewReader(rollupgen.RollupAdminLogicABI)) Require(t, err, "unable to parse rollup ABI") - setValidatorCalldata, err := rollupABI.Pack("setValidator", []common.Address{valWalletAddrA, l1authB.From, srv.address}, []bool{true, true, true}) + setValidatorCalldata, err := rollupABI.Pack("setValidator", []common.Address{valWalletAddrA, l1authB.From, srv.Address}, []bool{true, true, true}) Require(t, err, "unable to generate setValidator calldata") tx, err := upgradeExecutor.ExecuteCall(&deployAuth, l2nodeA.DeployInfo.Rollup, setValidatorCalldata) Require(t, err, "unable to set validators") @@ -170,8 +175,18 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) Require(t, err) valConfig := staker.TestL1ValidatorConfig - - dpA, err := arbnode.StakerDataposter(ctx, rawdb.NewTable(l2nodeB.ArbDB, storage.StakerPrefix), l2nodeA.L1Reader, &l1authA, NewFetcherFromConfig(arbnode.ConfigDefaultL1NonSequencerTest()), nil) + parentChainID, err := builder.L1.Client.ChainID(ctx) + if err != nil { + t.Fatalf("Failed to get parent chain id: %v", err) + } + dpA, err := arbnode.StakerDataposter( + ctx, + rawdb.NewTable(l2nodeB.ArbDB, storage.StakerPrefix), + l2nodeA.L1Reader, + &l1authA, NewFetcherFromConfig(arbnode.ConfigDefaultL1NonSequencerTest()), + nil, + parentChainID, + ) if err != nil { t.Fatalf("Error creating validator dataposter: %v", err) } @@ -219,8 +234,19 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) } Require(t, err) cfg := arbnode.ConfigDefaultL1NonSequencerTest() - cfg.Staker.DataPoster.ExternalSigner = *externalSignerTestCfg(srv.address) - dpB, err := arbnode.StakerDataposter(ctx, rawdb.NewTable(l2nodeB.ArbDB, storage.StakerPrefix), l2nodeB.L1Reader, &l1authB, NewFetcherFromConfig(cfg), nil) + signerCfg, err := externalSignerTestCfg(srv.Address) + if err != nil { + t.Fatalf("Error getting external signer config: %v", err) + } + cfg.Staker.DataPoster.ExternalSigner = *signerCfg + dpB, err := arbnode.StakerDataposter( + ctx, + rawdb.NewTable(l2nodeB.ArbDB, storage.StakerPrefix), + l2nodeB.L1Reader, + &l1authB, NewFetcherFromConfig(cfg), + nil, + parentChainID, + ) if err != nil { t.Fatalf("Error creating validator dataposter: %v", err) } @@ -385,14 +411,14 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) Require(t, err, "EnsureTxSucceeded failed for staker", stakerName, "tx") } if faultyStaker { - conflictInfo, err := validatorUtils.FindStakerConflict(&bind.CallOpts{}, l2nodeA.DeployInfo.Rollup, l1authA.From, srv.address, big.NewInt(1024)) + conflictInfo, err := validatorUtils.FindStakerConflict(&bind.CallOpts{}, l2nodeA.DeployInfo.Rollup, l1authA.From, srv.Address, big.NewInt(1024)) Require(t, err) if staker.ConflictType(conflictInfo.Ty) == staker.CONFLICT_TYPE_FOUND { cancelBackgroundTxs() } } if faultyStaker && !sawStakerZombie { - sawStakerZombie, err = rollup.IsZombie(&bind.CallOpts{}, srv.address) + sawStakerZombie, err = rollup.IsZombie(&bind.CallOpts{}, srv.Address) Require(t, err) } isHonestZombie, err := rollup.IsZombie(&bind.CallOpts{}, valWalletAddrA) @@ -413,7 +439,7 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) Require(t, err) } if !stakerBWasStaked { - stakerBWasStaked, err = rollup.IsStaked(&bind.CallOpts{}, srv.address) + stakerBWasStaked, err = rollup.IsStaked(&bind.CallOpts{}, srv.Address) Require(t, err) } for j := 0; j < 5; j++ { From 11d76f62213f50540c280253fc0c5dc8efe31b59 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 21 Dec 2023 10:21:31 -0700 Subject: [PATCH 20/68] Fix broadcastSegment Contains and its usage --- broadcaster/backlog/backlog.go | 2 +- wsbroadcastserver/clientconnection.go | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/broadcaster/backlog/backlog.go b/broadcaster/backlog/backlog.go index 851561f482..0ef32167fe 100644 --- a/broadcaster/backlog/backlog.go +++ b/broadcaster/backlog/backlog.go @@ -379,7 +379,7 @@ func (s *backlogSegment) Contains(i uint64) bool { s.messagesLock.RLock() defer s.messagesLock.RUnlock() start := s.start() - if i < start || i > s.end() { + if i < start || i > s.end() || len(s.messages) == 0 { return false } diff --git a/wsbroadcastserver/clientconnection.go b/wsbroadcastserver/clientconnection.go index 49cd2af7e6..5e8763bd95 100644 --- a/wsbroadcastserver/clientconnection.go +++ b/wsbroadcastserver/clientconnection.go @@ -134,7 +134,10 @@ func (cc *ClientConnection) writeBacklog(ctx context.Context, segment backlog.Ba msgs := prevSegment.Messages() if prevSegment.Contains(uint64(cc.requestedSeqNum)) { requestedIdx := int(cc.requestedSeqNum) - int(prevSegment.Start()) - msgs = msgs[requestedIdx:] + // This might be false if messages were added after we fetched the segment's messages + if len(msgs) > requestedIdx { + msgs = msgs[requestedIdx:] + } } bm := &m.BroadcastMessage{ Version: m.V1, From 4b816bcb16ce04f20bef2a94c1e98dbd1a3cba58 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 21 Dec 2023 10:23:25 -0700 Subject: [PATCH 21/68] Further improve requestedSeqNum check --- wsbroadcastserver/clientconnection.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wsbroadcastserver/clientconnection.go b/wsbroadcastserver/clientconnection.go index 5e8763bd95..82b9603a0e 100644 --- a/wsbroadcastserver/clientconnection.go +++ b/wsbroadcastserver/clientconnection.go @@ -119,6 +119,7 @@ func (cc *ClientConnection) Remove() { func (cc *ClientConnection) writeBacklog(ctx context.Context, segment backlog.BacklogSegment) error { var prevSegment backlog.BacklogSegment + isFirstSegment := true for !backlog.IsBacklogSegmentNil(segment) { // must get the next segment before the messages to be sent are // retrieved ensures another segment is not added in between calls. @@ -132,13 +133,14 @@ func (cc *ClientConnection) writeBacklog(ctx context.Context, segment backlog.Ba } msgs := prevSegment.Messages() - if prevSegment.Contains(uint64(cc.requestedSeqNum)) { + if isFirstSegment && prevSegment.Contains(uint64(cc.requestedSeqNum)) { requestedIdx := int(cc.requestedSeqNum) - int(prevSegment.Start()) // This might be false if messages were added after we fetched the segment's messages - if len(msgs) > requestedIdx { + if len(msgs) >= requestedIdx { msgs = msgs[requestedIdx:] } } + isFirstSegment = false bm := &m.BroadcastMessage{ Version: m.V1, Messages: msgs, From 4eeb994a8773c374968a1e6f44d9e627875a12b2 Mon Sep 17 00:00:00 2001 From: Tristan Wilson Date: Thu, 21 Dec 2023 09:56:33 -0800 Subject: [PATCH 22/68] Fix help text for batch limits We switched to using geth's batch limiting rather than our own, and the help text was wrong. --- cmd/genericconf/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/genericconf/config.go b/cmd/genericconf/config.go index 6b44ace974..50aafbe223 100644 --- a/cmd/genericconf/config.go +++ b/cmd/genericconf/config.go @@ -121,6 +121,6 @@ func (c *RpcConfig) Apply(stackConf *node.Config) { } func RpcConfigAddOptions(prefix string, f *flag.FlagSet) { - f.Int(prefix+".max-batch-response-size", DefaultRpcConfig.MaxBatchResponseSize, "the maximum response size for a JSON-RPC request measured in bytes (-1 means no limit)") - f.Int(prefix+".batch-request-limit", DefaultRpcConfig.BatchRequestLimit, "the maximum number of requests in a batch") + f.Int(prefix+".max-batch-response-size", DefaultRpcConfig.MaxBatchResponseSize, "the maximum response size for a JSON-RPC request measured in bytes (0 means no limit)") + f.Int(prefix+".batch-request-limit", DefaultRpcConfig.BatchRequestLimit, "the maximum number of requests in a batch (0 means no limit)") } From 8eccd0f7a99c0e2e2bc5aed9659faa5a83e52af3 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Fri, 22 Dec 2023 22:13:08 +0530 Subject: [PATCH 23/68] Add metrics for relay backlog size and backlog size in bytes --- broadcaster/backlog/backlog.go | 36 ++++++++++++++++++++++++++++++++++ broadcaster/message/message.go | 6 ++++++ 2 files changed, 42 insertions(+) diff --git a/broadcaster/backlog/backlog.go b/broadcaster/backlog/backlog.go index 0ef32167fe..ae38bdad13 100644 --- a/broadcaster/backlog/backlog.go +++ b/broadcaster/backlog/backlog.go @@ -19,6 +19,8 @@ var ( errOutOfBounds = errors.New("message not found in backlog") confirmedSequenceNumberGauge = metrics.NewRegisteredGauge("arb/sequencenumber/confirmed", nil) + backlogSizeInBytesGauge = metrics.NewRegisteredGauge("arb/feed/backlog/sizeinbytes", nil) + backlogSizeGauge = metrics.NewRegisteredGauge("arb/feed/backlog/size", nil) ) // Backlog defines the interface for backlog. @@ -54,6 +56,26 @@ func (b *backlog) Head() BacklogSegment { return b.head.Load() } +func (b *backlog) backlogSizeInBytes() uint64 { + if b.head.Load() == nil || b.tail.Load() == nil { + return 0 + } + headSeg := b.head.Load() + headSeg.messagesLock.RLock() + headMsg := headSeg.messages[0] + headSeg.messagesLock.RUnlock() + + tailSeg := b.tail.Load() + tailSeg.messagesLock.RLock() + tailMsg := tailSeg.messages[len(tailSeg.messages)-1] + size := tailMsg.CumulativeSumMsgSize + tailSeg.messagesLock.RUnlock() + + size -= headMsg.CumulativeSumMsgSize + size += uint64(len(headMsg.Signature) + len(headMsg.Message.Message.L2msg) + 160) + return size +} + // Append will add the given messages to the backlogSegment at head until // that segment reaches its limit. If messages remain to be added a new segment // will be created. @@ -74,6 +96,10 @@ func (b *backlog) Append(bm *m.BroadcastMessage) error { prevMsgIdx := segment.End() if segment.count() >= b.config().SegmentLimit { + segment.messagesLock.RLock() + msg.CumulativeSumMsgSize = segment.messages[len(segment.messages)-1].CumulativeSumMsgSize + segment.messagesLock.RUnlock() + nextSegment := newBacklogSegment() segment.nextSegment.Store(nextSegment) prevMsgIdx = segment.End() @@ -100,6 +126,8 @@ func (b *backlog) Append(bm *m.BroadcastMessage) error { b.messageCount.Add(1) } + backlogSizeInBytesGauge.Update(int64(b.backlogSizeInBytes())) + backlogSizeGauge.Update(int64(b.Count())) return nil } @@ -238,6 +266,8 @@ func (b *backlog) reset() { b.tail = atomic.Pointer[backlogSegment]{} b.lookupByIndex = &containers.SyncMap[uint64, *backlogSegment]{} b.messageCount.Store(0) + backlogSizeInBytesGauge.Update(0) + backlogSizeGauge.Update(0) } // BacklogSegment defines the interface for backlogSegment. @@ -361,9 +391,15 @@ func (s *backlogSegment) append(prevMsgIdx uint64, msg *m.BroadcastFeedMessage) s.messagesLock.Lock() defer s.messagesLock.Unlock() + prevCumulativeSum := uint64(0) + if len(s.messages) > 0 { + prevCumulativeSum = s.messages[len(s.messages)-1].CumulativeSumMsgSize + } if expSeqNum := prevMsgIdx + 1; prevMsgIdx == 0 || uint64(msg.SequenceNumber) == expSeqNum { + msg.UpdateCumulativeSumMsgSize(prevCumulativeSum) s.messages = append(s.messages, msg) } else if uint64(msg.SequenceNumber) > expSeqNum { + msg.UpdateCumulativeSumMsgSize(prevCumulativeSum) s.messages = nil s.messages = append(s.messages, msg) return fmt.Errorf("new message sequence number (%d) is greater than the expected sequence number (%d): %w", msg.SequenceNumber, expSeqNum, errDropSegments) diff --git a/broadcaster/message/message.go b/broadcaster/message/message.go index f436e765cb..076eb96420 100644 --- a/broadcaster/message/message.go +++ b/broadcaster/message/message.go @@ -35,6 +35,12 @@ type BroadcastFeedMessage struct { SequenceNumber arbutil.MessageIndex `json:"sequenceNumber"` Message arbostypes.MessageWithMetadata `json:"message"` Signature []byte `json:"signature"` + + CumulativeSumMsgSize uint64 `json:"-"` +} + +func (m *BroadcastFeedMessage) UpdateCumulativeSumMsgSize(val uint64) { + m.CumulativeSumMsgSize += val + uint64(len(m.Signature)+len(m.Message.Message.L2msg)+160) } func (m *BroadcastFeedMessage) Hash(chainId uint64) (common.Hash, error) { From 603634df0402b76c1c2a19cda2be7585487342a0 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 22 Dec 2023 11:28:45 -0700 Subject: [PATCH 24/68] Only use redis lock for DAS, not for normal batch posting --- arbnode/batch_poster.go | 53 +++++++++++++++++++------------ arbnode/dataposter/data_poster.go | 17 +++------- arbnode/node.go | 9 ------ arbnode/redislock/redis.go | 5 ++- staker/staker.go | 3 -- 5 files changed, 41 insertions(+), 46 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 4c8bd36f72..870df2e0d5 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -16,7 +16,6 @@ import ( "time" "github.com/andybalholm/brotli" - "github.com/go-redis/redis/v8" "github.com/spf13/pflag" "github.com/ethereum/go-ethereum" @@ -30,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/offchainlabs/nitro/arbnode/dataposter" + "github.com/offchainlabs/nitro/arbnode/dataposter/storage" "github.com/offchainlabs/nitro/arbnode/redislock" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbstate" @@ -118,7 +118,6 @@ type BatchPosterConfig struct { DataPoster dataposter.DataPosterConfig `koanf:"data-poster" reload:"hot"` RedisUrl string `koanf:"redis-url"` RedisLock redislock.SimpleCfg `koanf:"redis-lock" reload:"hot"` - EnableRedisLock bool `koanf:"enable-redis-lock"` ExtraBatchGas uint64 `koanf:"extra-batch-gas" reload:"hot"` ParentChainWallet genericconf.WalletConfig `koanf:"parent-chain-wallet"` L1BlockBound string `koanf:"l1-block-bound" reload:"hot"` @@ -167,7 +166,6 @@ func BatchPosterConfigAddOptions(prefix string, f *pflag.FlagSet) { f.String(prefix+".gas-refunder-address", DefaultBatchPosterConfig.GasRefunderAddress, "The gas refunder contract address (optional)") f.Uint64(prefix+".extra-batch-gas", DefaultBatchPosterConfig.ExtraBatchGas, "use this much more gas than estimation says is necessary to post batches") f.String(prefix+".redis-url", DefaultBatchPosterConfig.RedisUrl, "if non-empty, the Redis URL to store queued transactions in") - f.Bool(prefix+".enable-redis-lock", DefaultBatchPosterConfig.EnableRedisLock, "if a Redis URL is specified, ensure only one batch poster attempts to post batches at a time") f.String(prefix+".l1-block-bound", DefaultBatchPosterConfig.L1BlockBound, "only post messages to batches when they're within the max future block/timestamp as of this L1 block tag (\"safe\", \"finalized\", \"latest\", or \"ignore\" to ignore this check)") f.Duration(prefix+".l1-block-bound-bypass", DefaultBatchPosterConfig.L1BlockBoundBypass, "post batches even if not within the layer 1 future bounds if we're within this margin of the max delay") redislock.AddConfigOptions(prefix+".redis-lock", f) @@ -193,7 +191,6 @@ var DefaultBatchPosterConfig = BatchPosterConfig{ L1BlockBound: "", L1BlockBoundBypass: time.Hour, RedisLock: redislock.DefaultCfg, - EnableRedisLock: true, } var DefaultBatchPosterL1WalletConfig = genericconf.WalletConfig{ @@ -219,7 +216,6 @@ var TestBatchPosterConfig = BatchPosterConfig{ ParentChainWallet: DefaultBatchPosterL1WalletConfig, L1BlockBound: "", L1BlockBoundBypass: time.Hour, - EnableRedisLock: true, } type BatchPosterOpts struct { @@ -259,11 +255,7 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e simpleRedisLockConfig.Key = batchPosterSimpleRedisLockKey return &simpleRedisLockConfig } - var redisClientForLock redis.UniversalClient - if opts.Config().EnableRedisLock { - redisClientForLock = redisClient - } - redisLock, err := redislock.NewSimple(redisClientForLock, redisLockConfigFetcher, func() bool { return opts.SyncMonitor.Synced() }) + redisLock, err := redislock.NewSimple(redisClient, redisLockConfigFetcher, func() bool { return opts.SyncMonitor.Synced() }) if err != nil { return nil, err } @@ -295,7 +287,6 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e HeaderReader: opts.L1Reader, Auth: opts.TransactOpts, RedisClient: redisClient, - RedisLock: redisLock, Config: dataPosterConfigFetcher, MetadataRetriever: b.getBatchPosterPosition, ExtraBacklog: b.GetBacklogEstimate, @@ -779,6 +770,8 @@ func (b *BatchPoster) encodeAddBatch(seqNum *big.Int, prevMsgNum arbutil.Message return fullData, nil } +var ErrNormalGasEstimationFailed = errors.New("normal gas estimation failed") + func (b *BatchPoster) estimateGas(ctx context.Context, sequencerMessage []byte, delayedMessages uint64, realData []byte, realNonce uint64, realAccessList types.AccessList) (uint64, error) { config := b.config() useNormalEstimation := b.dataPoster.MaxMempoolTransactions() == 1 @@ -799,7 +792,7 @@ func (b *BatchPoster) estimateGas(ctx context.Context, sequencerMessage []byte, AccessList: realAccessList, }) if err != nil { - return 0, err + return 0, fmt.Errorf("%w: %w", ErrNormalGasEstimationFailed, err) } return gas + config.ExtraBatchGas, nil } @@ -1021,6 +1014,14 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) return false, errAttemptLockFailed } + gotNonce, gotMeta, err := b.dataPoster.GetNextNonceAndMeta(ctx) + if err != nil { + return false, err + } + if nonce != gotNonce || !bytes.Equal(batchPositionBytes, gotMeta) { + return false, fmt.Errorf("%w: nonce changed from %d to %d while creating batch", storage.ErrStorageRace, nonce, gotNonce) + } + cert, err := b.daWriter.Store(ctx, sequencerMsg, uint64(time.Now().Add(config.DASRetentionPeriod).Unix()), []byte{}) // b.daWriter will append signature if enabled if errors.Is(err, das.BatchToDasFailed) { if config.DisableDasFallbackStoreDataOnChain { @@ -1058,9 +1059,6 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) if err != nil { return false, err } - if !b.redisLock.AttemptLock(ctx) { - return false, errAttemptLockFailed - } tx, err := b.dataPoster.PostTransaction(ctx, firstMsgTime, nonce, @@ -1184,14 +1182,27 @@ func (b *BatchPoster) Start(ctxIn context.Context) { if err != nil { b.building = nil logLevel := log.Error - // Likely the inbox tracker just isn't caught up. - // Let's see if this error disappears naturally. if b.firstEphemeralError == (time.Time{}) { b.firstEphemeralError = time.Now() - logLevel = log.Warn - } else if time.Since(b.firstEphemeralError) < time.Minute { - logLevel = log.Warn - } else if time.Since(b.firstEphemeralError) < time.Minute*5 && strings.Contains(err.Error(), "will exceed max mempool size") { + } + // Likely the inbox tracker just isn't caught up, or there's some other ephemeral error. + // Let's see if this error disappears naturally. + sinceFirstEphemeralError := time.Since(b.firstEphemeralError) + // If the error matches one of these, it's only logged at debug for the first minute, + // then at warn for the next 4 minutes, then at error. If the error isn't one of these, + // it'll be logged at warn for the first minute, then at error. + ignoreAtFirst := errors.Is(err, dataposter.ErrExceedsMaxMempoolSize) || + errors.Is(err, storage.ErrStorageRace) || + errors.Is(err, ErrNormalGasEstimationFailed) || + errors.Is(err, AccumulatorNotFoundErr) || + errors.Is(err, context.Canceled) + if sinceFirstEphemeralError < time.Minute { + if ignoreAtFirst { + logLevel = log.Debug + } else { + logLevel = log.Warn + } + } else if sinceFirstEphemeralError < time.Minute*5 && ignoreAtFirst { logLevel = log.Warn } logLevel("error posting batch", "err", err) diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index 191fd40708..673fd317ee 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -57,7 +57,6 @@ type DataPoster struct { client arbutil.L1Interface auth *bind.TransactOpts signer signerFn - redisLock AttemptLocker config ConfigFetcher usingNoOpStorage bool replacementTimes []time.Duration @@ -115,7 +114,6 @@ type DataPosterOpts struct { HeaderReader *headerreader.HeaderReader Auth *bind.TransactOpts RedisClient redis.UniversalClient - RedisLock AttemptLocker Config ConfigFetcher MetadataRetriever func(ctx context.Context, blockNum *big.Int) ([]byte, error) ExtraBacklog func() uint64 @@ -176,7 +174,6 @@ func NewDataPoster(ctx context.Context, opts *DataPosterOpts) (*DataPoster, erro replacementTimes: replacementTimes, metadataRetriever: opts.MetadataRetriever, queue: queue, - redisLock: opts.RedisLock, errorCount: make(map[uint64]int), maxFeeCapExpression: expression, extraBacklog: opts.ExtraBacklog, @@ -289,6 +286,8 @@ func (p *DataPoster) MaxMempoolTransactions() uint64 { return p.config().MaxMempoolTransactions } +var ErrExceedsMaxMempoolSize = errors.New("posting this transaction will exceed max mempool size") + // Does basic check whether posting transaction with specified nonce would // result in exceeding maximum queue length or maximum transactions in mempool. func (p *DataPoster) canPostWithNonce(ctx context.Context, nextNonce uint64) error { @@ -311,7 +310,7 @@ func (p *DataPoster) canPostWithNonce(ctx context.Context, nextNonce uint64) err return fmt.Errorf("getting nonce of a dataposter sender: %w", err) } if nextNonce >= cfg.MaxMempoolTransactions+unconfirmedNonce { - return fmt.Errorf("posting a transaction with nonce: %d will exceed max mempool size: %d, unconfirmed nonce: %d", nextNonce, cfg.MaxMempoolTransactions, unconfirmedNonce) + return fmt.Errorf("%w: transaction nonce: %d, unconfirmed nonce: %d, max mempool size: %d", ErrExceedsMaxMempoolSize, nextNonce, unconfirmedNonce, cfg.MaxMempoolTransactions) } } return nil @@ -534,7 +533,7 @@ func (p *DataPoster) PostTransaction(ctx context.Context, dataCreatedAt time.Tim return nil, err } if nonce != expectedNonce { - return nil, fmt.Errorf("data poster expected next transaction to have nonce %v but was requested to post transaction with nonce %v", expectedNonce, nonce) + return nil, fmt.Errorf("%w: data poster expected next transaction to have nonce %v but was requested to post transaction with nonce %v", storage.ErrStorageRace, expectedNonce, nonce) } err = p.updateBalance(ctx) @@ -775,16 +774,10 @@ func (p *DataPoster) Start(ctxIn context.Context) { log.Error("Failed to fetch tx queue contents", "err", err) return minWait } - tryToReplace, err := p.redisLock.CouldAcquireLock(ctx) - if err != nil { - log.Warn("Error checking if we could acquire redis lock", "err", err) - // Might as well try, worst case we hit contention on redis and fail - tryToReplace = true - } for index, tx := range queueContents { backlogOfBatches := len(queueContents) - index - 1 replacing := false - if tryToReplace && now.After(tx.NextReplacement) { + if now.After(tx.NextReplacement) { replacing = true err := p.replaceTx(ctx, tx, uint64(backlogOfBatches)) p.maybeLogError(err, tx, "failed to replace-by-fee transaction") diff --git a/arbnode/node.go b/arbnode/node.go index 8be2a982cf..accca10e47 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -25,7 +25,6 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/offchainlabs/nitro/arbnode/dataposter" "github.com/offchainlabs/nitro/arbnode/dataposter/storage" - "github.com/offchainlabs/nitro/arbnode/redislock" "github.com/offchainlabs/nitro/arbnode/resourcemanager" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/broadcastclient" @@ -313,13 +312,6 @@ func StakerDataposter( if err != nil { return nil, fmt.Errorf("creating redis client from url: %w", err) } - lockCfgFetcher := func() *redislock.SimpleCfg { - return &cfg.Staker.RedisLock - } - redisLock, err := redislock.NewSimple(redisC, lockCfgFetcher, func() bool { return syncMonitor.Synced() }) - if err != nil { - return nil, err - } dpCfg := func() *dataposter.DataPosterConfig { return &cfg.Staker.DataPoster } @@ -335,7 +327,6 @@ func StakerDataposter( HeaderReader: l1Reader, Auth: transactOpts, RedisClient: redisC, - RedisLock: redisLock, Config: dpCfg, MetadataRetriever: mdRetriever, RedisKey: sender + ".staker-data-poster.queue", diff --git a/arbnode/redislock/redis.go b/arbnode/redislock/redis.go index fe46612527..09296e3c79 100644 --- a/arbnode/redislock/redis.go +++ b/arbnode/redislock/redis.go @@ -29,6 +29,7 @@ type Simple struct { } type SimpleCfg struct { + Enable bool `koanf:"enable"` MyId string `koanf:"my-id"` LockoutDuration time.Duration `koanf:"lockout-duration" reload:"hot"` RefreshDuration time.Duration `koanf:"refresh-duration" reload:"hot"` @@ -39,6 +40,7 @@ type SimpleCfg struct { type SimpleCfgFetcher func() *SimpleCfg func AddConfigOptions(prefix string, f *flag.FlagSet) { + f.Bool(prefix+".enable", DefaultCfg.Enable, "if false, always treat this as locked and don't write the lock to redis") f.String(prefix+".my-id", "", "this node's id prefix when acquiring the lock (optional)") f.Duration(prefix+".lockout-duration", DefaultCfg.LockoutDuration, "how long lock is held") f.Duration(prefix+".refresh-duration", DefaultCfg.RefreshDuration, "how long between consecutive calls to redis") @@ -60,6 +62,7 @@ func NewSimple(client redis.UniversalClient, config SimpleCfgFetcher, readyToLoc } var DefaultCfg = SimpleCfg{ + Enable: true, LockoutDuration: time.Minute, RefreshDuration: time.Second * 10, Key: "", @@ -137,7 +140,7 @@ func (l *Simple) AttemptLock(ctx context.Context) bool { } func (l *Simple) Locked() bool { - if l.client == nil { + if l.client == nil || !l.config().Enable { return true } return time.Now().Before(atomicTimeRead(&l.lockedUntil)) diff --git a/staker/staker.go b/staker/staker.go index 4f35c1bc9a..5b930dde58 100644 --- a/staker/staker.go +++ b/staker/staker.go @@ -87,7 +87,6 @@ type L1ValidatorConfig struct { GasRefunderAddress string `koanf:"gas-refunder-address"` DataPoster dataposter.DataPosterConfig `koanf:"data-poster" reload:"hot"` RedisUrl string `koanf:"redis-url"` - RedisLock redislock.SimpleCfg `koanf:"redis-lock" reload:"hot"` ExtraGas uint64 `koanf:"extra-gas" reload:"hot"` Dangerous DangerousConfig `koanf:"dangerous"` ParentChainWallet genericconf.WalletConfig `koanf:"parent-chain-wallet"` @@ -154,7 +153,6 @@ var DefaultL1ValidatorConfig = L1ValidatorConfig{ GasRefunderAddress: "", DataPoster: dataposter.DefaultDataPosterConfigForValidator, RedisUrl: "", - RedisLock: redislock.DefaultCfg, ExtraGas: 50000, Dangerous: DefaultDangerousConfig, ParentChainWallet: DefaultValidatorL1WalletConfig, @@ -175,7 +173,6 @@ var TestL1ValidatorConfig = L1ValidatorConfig{ GasRefunderAddress: "", DataPoster: dataposter.TestDataPosterConfigForValidator, RedisUrl: "", - RedisLock: redislock.DefaultCfg, ExtraGas: 50000, Dangerous: DefaultDangerousConfig, ParentChainWallet: DefaultValidatorL1WalletConfig, From 063164e6be44f29f7f562129c415d9e47f8515de Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 22 Dec 2023 11:47:09 -0700 Subject: [PATCH 25/68] Fix redis lock removal from staker config --- staker/staker.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/staker/staker.go b/staker/staker.go index 5b930dde58..5fa6c400c3 100644 --- a/staker/staker.go +++ b/staker/staker.go @@ -21,7 +21,6 @@ import ( flag "github.com/spf13/pflag" "github.com/offchainlabs/nitro/arbnode/dataposter" - "github.com/offchainlabs/nitro/arbnode/redislock" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/cmd/genericconf" "github.com/offchainlabs/nitro/staker/txbuilder" @@ -202,7 +201,6 @@ func L1ValidatorConfigAddOptions(prefix string, f *flag.FlagSet) { f.String(prefix+".redis-url", DefaultL1ValidatorConfig.RedisUrl, "redis url for L1 validator") f.Uint64(prefix+".extra-gas", DefaultL1ValidatorConfig.ExtraGas, "use this much more gas than estimation says is necessary to post transactions") dataposter.DataPosterConfigAddOptions(prefix+".data-poster", f, dataposter.DefaultDataPosterConfigForValidator) - redislock.AddConfigOptions(prefix+".redis-lock", f) DangerousConfigAddOptions(prefix+".dangerous", f) genericconf.WalletConfigAddOptions(prefix+".parent-chain-wallet", f, DefaultL1ValidatorConfig.ParentChainWallet.Pathname) } From 8f9bd0e3f93fa0bbf94173177e8a0d9f44164d96 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 22 Dec 2023 12:06:10 -0700 Subject: [PATCH 26/68] Fix simple redis lock test --- arbnode/simple_redis_lock_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/arbnode/simple_redis_lock_test.go b/arbnode/simple_redis_lock_test.go index b7506145c3..c9dd576749 100644 --- a/arbnode/simple_redis_lock_test.go +++ b/arbnode/simple_redis_lock_test.go @@ -48,6 +48,7 @@ func simpleRedisLockTest(t *testing.T, redisKeySuffix string, chosen int, backgo Require(t, redisClient.Del(ctx, redisKey).Err()) conf := &redislock.SimpleCfg{ + Enable: true, LockoutDuration: test_delay * test_attempts * 10, RefreshDuration: test_delay * 2, Key: redisKey, From 32883f183ceb4073bffec6d6394ddd87aa36281a Mon Sep 17 00:00:00 2001 From: Maciej Kulawik Date: Fri, 22 Dec 2023 20:27:51 +0000 Subject: [PATCH 27/68] fix typo --- cmd/nitro/init.go | 6 +++--- .../prunning.go => pruning/pruning.go} | 2 +- .../{prunning_test.go => pruning_test.go} | 20 +++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) rename cmd/{prunning/prunning.go => pruning/pruning.go} (99%) rename system_tests/{prunning_test.go => pruning_test.go} (79%) diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index 00012ac62d..ada195b5c4 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -33,7 +33,7 @@ import ( "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/cmd/conf" "github.com/offchainlabs/nitro/cmd/ipfshelper" - "github.com/offchainlabs/nitro/cmd/prunning" + "github.com/offchainlabs/nitro/cmd/pruning" "github.com/offchainlabs/nitro/cmd/util" "github.com/offchainlabs/nitro/execution/gethexec" "github.com/offchainlabs/nitro/statetransfer" @@ -167,7 +167,7 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo if err != nil { return chainDb, nil, err } - err = prunning.PruneChainDb(ctx, chainDb, stack, &config.Init, cacheConfig, l1Client, rollupAddrs, config.Node.ValidatorRequired()) + err = pruning.PruneChainDb(ctx, chainDb, stack, &config.Init, cacheConfig, l1Client, rollupAddrs, config.Node.ValidatorRequired()) if err != nil { return chainDb, nil, fmt.Errorf("error pruning: %w", err) } @@ -363,7 +363,7 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo return chainDb, l2BlockChain, err } - err = prunning.PruneChainDb(ctx, chainDb, stack, &config.Init, cacheConfig, l1Client, rollupAddrs, config.Node.ValidatorRequired()) + err = pruning.PruneChainDb(ctx, chainDb, stack, &config.Init, cacheConfig, l1Client, rollupAddrs, config.Node.ValidatorRequired()) if err != nil { return chainDb, nil, fmt.Errorf("error pruning: %w", err) } diff --git a/cmd/prunning/prunning.go b/cmd/pruning/pruning.go similarity index 99% rename from cmd/prunning/prunning.go rename to cmd/pruning/pruning.go index 5efdf8e643..68d89302f0 100644 --- a/cmd/prunning/prunning.go +++ b/cmd/pruning/pruning.go @@ -1,4 +1,4 @@ -package prunning +package pruning import ( "context" diff --git a/system_tests/prunning_test.go b/system_tests/pruning_test.go similarity index 79% rename from system_tests/prunning_test.go rename to system_tests/pruning_test.go index d522458209..e6eefdabd5 100644 --- a/system_tests/prunning_test.go +++ b/system_tests/pruning_test.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/node" "github.com/offchainlabs/nitro/cmd/conf" - "github.com/offchainlabs/nitro/cmd/prunning" + "github.com/offchainlabs/nitro/cmd/pruning" "github.com/offchainlabs/nitro/execution/gethexec" "github.com/offchainlabs/nitro/util/testhelpers" ) @@ -28,7 +28,7 @@ func countStateEntries(db ethdb.Iteratee) int { return entries } -func TestPrunning(t *testing.T) { +func TestPruning(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -67,7 +67,7 @@ func TestPrunning(t *testing.T) { chainDb, err := stack.OpenDatabase("chaindb", 0, 0, "", false) Require(t, err) defer chainDb.Close() - chainDbEntriesBeforePrunning := countStateEntries(chainDb) + chainDbEntriesBeforePruning := countStateEntries(chainDb) prand := testhelpers.NewPseudoRandomDataSource(t, 1) var testKeys [][]byte @@ -88,21 +88,21 @@ func TestPrunning(t *testing.T) { initConfig := conf.InitConfigDefault initConfig.Prune = "full" coreCacheConfig := gethexec.DefaultCacheConfigFor(stack, &builder.execConfig.Caching) - err = prunning.PruneChainDb(ctx, chainDb, stack, &initConfig, coreCacheConfig, builder.L1.Client, *builder.L2.ConsensusNode.DeployInfo, false) + err = pruning.PruneChainDb(ctx, chainDb, stack, &initConfig, coreCacheConfig, builder.L1.Client, *builder.L2.ConsensusNode.DeployInfo, false) Require(t, err) for _, key := range testKeys { if has, _ := chainDb.Has(key); has { - Fatal(t, "test key hasn't been prunned as expected") + Fatal(t, "test key hasn't been pruned as expected") } } - chainDbEntriesAfterPrunning := countStateEntries(chainDb) - t.Log("db entries pre-prunning:", chainDbEntriesBeforePrunning) - t.Log("db entries post-prunning:", chainDbEntriesAfterPrunning) + chainDbEntriesAfterPruning := countStateEntries(chainDb) + t.Log("db entries pre-pruning:", chainDbEntriesBeforePruning) + t.Log("db entries post-pruning:", chainDbEntriesAfterPruning) - if chainDbEntriesAfterPrunning >= chainDbEntriesBeforePrunning { - Fatal(t, "The db doesn't have less entries after prunning then before. Before:", chainDbEntriesBeforePrunning, "After:", chainDbEntriesAfterPrunning) + if chainDbEntriesAfterPruning >= chainDbEntriesBeforePruning { + Fatal(t, "The db doesn't have less entries after pruning then before. Before:", chainDbEntriesBeforePruning, "After:", chainDbEntriesAfterPruning) } }() builder := NewNodeBuilder(ctx).DefaultConfig(t, true) From cb4d3c5374be0d9ed155fe6ed2514b27747f7366 Mon Sep 17 00:00:00 2001 From: Maciej Kulawik Date: Fri, 22 Dec 2023 20:30:11 +0000 Subject: [PATCH 28/68] make only one test tx after pruning --- system_tests/pruning_test.go | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/system_tests/pruning_test.go b/system_tests/pruning_test.go index e6eefdabd5..ef82c0466e 100644 --- a/system_tests/pruning_test.go +++ b/system_tests/pruning_test.go @@ -111,15 +111,9 @@ func TestPruning(t *testing.T) { defer cancel() builder.L2Info.GenerateAccount("User2") - var txs []*types.Transaction - for i := uint64(0); i < 10; i++ { - tx := builder.L2Info.PrepareTx("Owner", "User2", builder.L2Info.TransferGas, common.Big1, nil) - txs = append(txs, tx) - err := builder.L2.Client.SendTransaction(ctx, tx) - Require(t, err) - } - for _, tx := range txs { - _, err := builder.L2.EnsureTxSucceeded(tx) - Require(t, err) - } + tx := builder.L2Info.PrepareTx("Owner", "User2", builder.L2Info.TransferGas, common.Big1, nil) + err := builder.L2.Client.SendTransaction(ctx, tx) + Require(t, err) + _, err = builder.L2.EnsureTxSucceeded(tx) + Require(t, err) } From 51e9b557371048362623878df2939c3c5ffbcacd Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 22 Dec 2023 16:23:15 -0700 Subject: [PATCH 29/68] Clean up code a bit to address two comments --- arbnode/batch_poster.go | 7 +++++-- arbnode/dataposter/data_poster.go | 5 ----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 870df2e0d5..8c0fdd332a 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -1180,6 +1180,10 @@ func (b *BatchPoster) Start(ctxIn context.Context) { b.firstEphemeralError = time.Time{} } if err != nil { + if ctx.Err() != nil { + // Shutting down. No need to print the context canceled error. + return 0 + } b.building = nil logLevel := log.Error if b.firstEphemeralError == (time.Time{}) { @@ -1194,8 +1198,7 @@ func (b *BatchPoster) Start(ctxIn context.Context) { ignoreAtFirst := errors.Is(err, dataposter.ErrExceedsMaxMempoolSize) || errors.Is(err, storage.ErrStorageRace) || errors.Is(err, ErrNormalGasEstimationFailed) || - errors.Is(err, AccumulatorNotFoundErr) || - errors.Is(err, context.Canceled) + errors.Is(err, AccumulatorNotFoundErr) if sinceFirstEphemeralError < time.Minute { if ignoreAtFirst { logLevel = log.Debug diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index 673fd317ee..ff5a945a17 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -83,11 +83,6 @@ type DataPoster struct { // This can be local or external, hence the context parameter. type signerFn func(context.Context, common.Address, *types.Transaction) (*types.Transaction, error) -type AttemptLocker interface { - AttemptLock(context.Context) bool - CouldAcquireLock(context.Context) (bool, error) -} - func parseReplacementTimes(val string) ([]time.Duration, error) { var res []time.Duration var lastReplacementTime time.Duration From ce3a42560fa3a64560475c2cc927254a8ddec231 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Sat, 23 Dec 2023 07:12:36 +0530 Subject: [PATCH 30/68] fix typo --- arbnode/batch_poster.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 4c708c302c..827609ee6f 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -1164,7 +1164,7 @@ func (b *BatchPoster) Start(ctxIn context.Context) { // Likely the inbox tracker just isn't caught up. // Let's see if this error disappears naturally. logLevel = commonEphemeralErrorHandler.LogLevel(err, logLevel) - logLevel = commonEphemeralErrorHandler.LogLevel(err, logLevel) + logLevel = exceedMaxMempoolSizeEphemeralErrorHandler.LogLevel(err, logLevel) logLevel("error posting batch", "err", err) return b.config().ErrorDelay } else if posted { From c95a374c37742d7cfe9e247e7396765e86c1d305 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 22 Dec 2023 22:11:54 -0700 Subject: [PATCH 31/68] Stop execution node after everything else --- arbnode/node.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arbnode/node.go b/arbnode/node.go index accca10e47..c26d78b89e 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -864,9 +864,6 @@ func (n *Node) Start(ctx context.Context) error { } func (n *Node) StopAndWait() { - if n.Execution != nil { - n.Execution.StopAndWait() - } if n.MaintenanceRunner != nil && n.MaintenanceRunner.Started() { n.MaintenanceRunner.StopAndWait() } @@ -919,7 +916,10 @@ func (n *Node) StopAndWait() { if n.DASLifecycleManager != nil { n.DASLifecycleManager.StopAndWaitUntil(2 * time.Second) } + if n.Execution != nil { + n.Execution.StopAndWait() + } if err := n.Stack.Close(); err != nil { - log.Error("error on stak close", "err", err) + log.Error("error on stack close", "err", err) } } From ac8dbc95784209c3419855bc55db0dbfaf30f150 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 22 Dec 2023 22:15:07 -0700 Subject: [PATCH 32/68] Log invalid txs in the STF when the chain is in debug mode --- arbos/block_processor.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/arbos/block_processor.go b/arbos/block_processor.go index 4864a31255..f1838132a8 100644 --- a/arbos/block_processor.go +++ b/arbos/block_processor.go @@ -42,6 +42,21 @@ var EmitReedeemScheduledEvent func(*vm.EVM, uint64, uint64, [32]byte, [32]byte, var EmitTicketCreatedEvent func(*vm.EVM, [32]byte) error var gasUsedSinceStartupCounter = metrics.NewRegisteredCounter("arb/gas_used", nil) +// A helper struct that implements String() by marshalling to JSON. +// This is useful for logging because it's lazy, so if the log level is too high to print the transaction, +// it doesn't waste compute marshalling the transaction when the result wouldn't be used. +type printTxAsJson struct { + tx *types.Transaction +} + +func (p printTxAsJson) String() string { + json, err := p.tx.MarshalJSON() + if err != nil { + return fmt.Sprintf("[error marshalling tx: %v]", err) + } + return string(json) +} + type L1Info struct { poster common.Address l1BlockNumber uint64 @@ -357,7 +372,11 @@ func ProduceBlockAdvanced( hooks.TxErrors = append(hooks.TxErrors, err) if err != nil { - log.Debug("error applying transaction", "tx", tx, "err", err) + logLevel := log.Debug + if chainConfig.DebugMode() { + logLevel = log.Warn + } + logLevel("error applying transaction", "tx", printTxAsJson{tx}, "err", err) if !hooks.DiscardInvalidTxsEarly { // we'll still deduct a TxGas's worth from the block-local rate limiter even if the tx was invalid blockGasLeft = arbmath.SaturatingUSub(blockGasLeft, params.TxGas) From 279dea45f9f0edd123f2e5e7155f7275d903307a Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Sat, 23 Dec 2023 11:37:06 -0700 Subject: [PATCH 33/68] Fix enabling pprof on the relay --- cmd/relay/relay.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cmd/relay/relay.go b/cmd/relay/relay.go index b25aadf57b..40f4f26eec 100644 --- a/cmd/relay/relay.go +++ b/cmd/relay/relay.go @@ -6,7 +6,6 @@ package main import ( "context" "fmt" - "net/http" "os" "os/signal" "syscall" @@ -22,10 +21,6 @@ import ( "github.com/offchainlabs/nitro/relay" ) -func init() { - http.DefaultServeMux = http.NewServeMux() -} - func main() { if err := startup(); err != nil { log.Error("Error running relay", "err", err) From e0f35dd40eaefa6fff4e6077ebef58cf5d0e2e74 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Sat, 23 Dec 2023 13:02:03 -0700 Subject: [PATCH 34/68] Fix memory leak in relay --- broadcaster/backlog/backlog.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/broadcaster/backlog/backlog.go b/broadcaster/backlog/backlog.go index 0ef32167fe..461ec3a6f3 100644 --- a/broadcaster/backlog/backlog.go +++ b/broadcaster/backlog/backlog.go @@ -77,7 +77,6 @@ func (b *backlog) Append(bm *m.BroadcastMessage) error { nextSegment := newBacklogSegment() segment.nextSegment.Store(nextSegment) prevMsgIdx = segment.End() - nextSegment.previousSegment.Store(segment) segment = nextSegment b.tail.Store(segment) } @@ -253,10 +252,9 @@ type BacklogSegment interface { // backlogSegment stores messages up to a limit defined by the backlog. It also // points to the next backlogSegment in the list. type backlogSegment struct { - messagesLock sync.RWMutex - messages []*m.BroadcastFeedMessage - nextSegment atomic.Pointer[backlogSegment] - previousSegment atomic.Pointer[backlogSegment] + messagesLock sync.RWMutex + messages []*m.BroadcastFeedMessage + nextSegment atomic.Pointer[backlogSegment] } // newBacklogSegment creates a backlogSegment object with an empty slice of From fd5edcb829a9c440edffcfc7961e4668c4ab9c5c Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Sat, 23 Dec 2023 13:21:31 -0700 Subject: [PATCH 35/68] Fix race conditions in backlog.reset() --- broadcaster/backlog/backlog.go | 26 ++++++++++++++------------ broadcaster/backlog/backlog_test.go | 8 ++++---- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/broadcaster/backlog/backlog.go b/broadcaster/backlog/backlog.go index 461ec3a6f3..549a38ff86 100644 --- a/broadcaster/backlog/backlog.go +++ b/broadcaster/backlog/backlog.go @@ -35,18 +35,18 @@ type Backlog interface { type backlog struct { head atomic.Pointer[backlogSegment] tail atomic.Pointer[backlogSegment] - lookupByIndex *containers.SyncMap[uint64, *backlogSegment] + lookupByIndex atomic.Pointer[containers.SyncMap[uint64, *backlogSegment]] config ConfigFetcher messageCount atomic.Uint64 } // NewBacklog creates a backlog. func NewBacklog(c ConfigFetcher) Backlog { - lookup := &containers.SyncMap[uint64, *backlogSegment]{} - return &backlog{ - lookupByIndex: lookup, - config: c, + b := &backlog{ + config: c, } + b.lookupByIndex.Store(&containers.SyncMap[uint64, *backlogSegment]{}) + return b } // Head return the head backlogSegment within the backlog. @@ -63,6 +63,7 @@ func (b *backlog) Append(bm *m.BroadcastMessage) error { b.delete(uint64(bm.ConfirmedSequenceNumberMessage.SequenceNumber)) } + lookupByIndex := b.lookupByIndex.Load() for _, msg := range bm.Messages { segment := b.tail.Load() if segment == nil { @@ -95,7 +96,7 @@ func (b *backlog) Append(bm *m.BroadcastMessage) error { } else if err != nil { return err } - b.lookupByIndex.Store(uint64(msg.SequenceNumber), segment) + lookupByIndex.Store(uint64(msg.SequenceNumber), segment) b.messageCount.Add(1) } @@ -160,7 +161,7 @@ func (b *backlog) delete(confirmed uint64) { } if confirmed > tail.End() { - log.Error("confirmed sequence number is past the end of stored messages", "confirmed sequence number", confirmed, "last stored sequence number", tail.End()) + log.Warn("confirmed sequence number is past the end of stored messages", "confirmed sequence number", confirmed, "last stored sequence number", tail.End()) b.reset() return } @@ -211,14 +212,15 @@ func (b *backlog) delete(confirmed uint64) { // removeFromLookup removes all entries from the head segment's start index to // the given confirmed index. func (b *backlog) removeFromLookup(start, end uint64) { + lookupByIndex := b.lookupByIndex.Load() for i := start; i <= end; i++ { - b.lookupByIndex.Delete(i) + lookupByIndex.Delete(i) } } // Lookup attempts to find the backlogSegment storing the given message index. func (b *backlog) Lookup(i uint64) (BacklogSegment, error) { - segment, ok := b.lookupByIndex.Load(i) + segment, ok := b.lookupByIndex.Load().Load(i) if !ok { return nil, fmt.Errorf("error finding backlog segment containing message with SequenceNumber %d", i) } @@ -233,9 +235,9 @@ func (s *backlog) Count() uint64 { // reset removes all segments from the backlog. func (b *backlog) reset() { - b.head = atomic.Pointer[backlogSegment]{} - b.tail = atomic.Pointer[backlogSegment]{} - b.lookupByIndex = &containers.SyncMap[uint64, *backlogSegment]{} + b.head.Store(nil) + b.tail.Store(nil) + b.lookupByIndex.Store(&containers.SyncMap[uint64, *backlogSegment]{}) b.messageCount.Store(0) } diff --git a/broadcaster/backlog/backlog_test.go b/broadcaster/backlog/backlog_test.go index ab25a523f7..ee712de9ed 100644 --- a/broadcaster/backlog/backlog_test.go +++ b/broadcaster/backlog/backlog_test.go @@ -57,9 +57,9 @@ func validateBroadcastMessage(t *testing.T, bm *m.BroadcastMessage, expectedCoun func createDummyBacklog(indexes []arbutil.MessageIndex) (*backlog, error) { b := &backlog{ - lookupByIndex: &containers.SyncMap[uint64, *backlogSegment]{}, - config: func() *Config { return &DefaultTestConfig }, + config: func() *Config { return &DefaultTestConfig }, } + b.lookupByIndex.Store(&containers.SyncMap[uint64, *backlogSegment]{}) bm := &m.BroadcastMessage{Messages: m.CreateDummyBroadcastMessages(indexes)} err := b.Append(bm) return b, err @@ -161,9 +161,9 @@ func TestDeleteInvalidBacklog(t *testing.T) { lookup := &containers.SyncMap[uint64, *backlogSegment]{} lookup.Store(40, s) b := &backlog{ - lookupByIndex: lookup, - config: func() *Config { return &DefaultTestConfig }, + config: func() *Config { return &DefaultTestConfig }, } + b.lookupByIndex.Store(lookup) b.messageCount.Store(2) b.head.Store(s) b.tail.Store(s) From d7b8d02dac1914643c17fff4ba127c425df3b718 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Sun, 24 Dec 2023 10:28:46 -0700 Subject: [PATCH 36/68] Fix Broadcaster BroadcastMessages sequence numbers --- arbnode/node.go | 2 +- broadcaster/broadcaster.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arbnode/node.go b/arbnode/node.go index accca10e47..951403244f 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -764,7 +764,7 @@ func (n *Node) Start(ctx context.Context) error { return fmt.Errorf("error initializing feed broadcast server: %w", err) } } - if n.InboxTracker != nil && n.BroadcastServer != nil && config.Sequencer { + if n.InboxTracker != nil && n.BroadcastServer != nil { // Even if the sequencer coordinator will populate this backlog, // we want to make sure it's populated before any clients connect. err = n.InboxTracker.PopulateFeedBacklog(n.BroadcastServer) diff --git a/broadcaster/broadcaster.go b/broadcaster/broadcaster.go index 38ffb0696c..ed3088ca2e 100644 --- a/broadcaster/broadcaster.go +++ b/broadcaster/broadcaster.go @@ -89,8 +89,8 @@ func (b *Broadcaster) BroadcastMessages(messages []arbostypes.MessageWithMetadat } }() var feedMessages []*m.BroadcastFeedMessage - for _, msg := range messages { - bfm, err := b.NewBroadcastFeedMessage(msg, seq) + for i, msg := range messages { + bfm, err := b.NewBroadcastFeedMessage(msg, seq+arbutil.MessageIndex(i)) if err != nil { return err } From 11cd4efb596c76d5f15091cf1be167bbf9880969 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Sun, 24 Dec 2023 10:42:17 -0700 Subject: [PATCH 37/68] Fix unused variable --- arbnode/node.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/arbnode/node.go b/arbnode/node.go index 951403244f..45ef7206e1 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -731,8 +731,6 @@ func CreateNode( } func (n *Node) Start(ctx context.Context) error { - // config is the static config at start, not a dynamic config - config := n.configFetcher.Get() execClient, ok := n.Execution.(*gethexec.ExecutionNode) if !ok { execClient = nil From 96fe66249283f57acfa8cf55ebfd5c485b311d81 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Sun, 24 Dec 2023 11:49:03 -0700 Subject: [PATCH 38/68] Revert "Merge pull request #2018 from OffchainLabs/linters" This reverts commit 1786f770eacb3d1ceb90fc019b37f69ba198e45c, reversing changes made to fd8d0b4295e638157b102e3cff91cf41a389c35c. --- Makefile | 31 --------------------- arbos/l1pricing/l1pricing.go | 6 ++-- go.mod | 2 -- go.sum | 16 ----------- linter/comparesame/comparesame.go | 12 -------- linter/cryptorand/cryptorand.go | 13 --------- linter/errcheck/errcheck.go | 12 -------- linter/featureconfig/featureconfig.go | 12 -------- linter/gocognit/gocognit.go | 13 --------- linter/ineffassign/ineffassign.go | 12 -------- linter/interfacechecker/interfacechecker.go | 12 -------- linter/logruswitherror/logruswitherror.go | 13 --------- linter/recursivelock/recursivelock.go | 1 - linter/shadowpredecl/shadowpredecl.go | 13 --------- linter/slicedirect/slicedirect.go | 13 --------- linter/uintcast/uintcast.go | 13 --------- 16 files changed, 3 insertions(+), 191 deletions(-) delete mode 100644 linter/comparesame/comparesame.go delete mode 100644 linter/cryptorand/cryptorand.go delete mode 100644 linter/errcheck/errcheck.go delete mode 100644 linter/featureconfig/featureconfig.go delete mode 100644 linter/gocognit/gocognit.go delete mode 100644 linter/ineffassign/ineffassign.go delete mode 100644 linter/interfacechecker/interfacechecker.go delete mode 100644 linter/logruswitherror/logruswitherror.go delete mode 100644 linter/shadowpredecl/shadowpredecl.go delete mode 100644 linter/slicedirect/slicedirect.go delete mode 100644 linter/uintcast/uintcast.go diff --git a/Makefile b/Makefile index d7f4599db3..42fd1e3595 100644 --- a/Makefile +++ b/Makefile @@ -308,37 +308,6 @@ contracts/test/prover/proofs/%.json: $(arbitrator_cases)/%.wasm $(arbitrator_pro .make/lint: $(DEP_PREDICATE) build-node-deps $(ORDER_ONLY_PREDICATE) .make go run ./linter/recursivelock ./... - go run ./linter/comparesame ./... - - # Disabled since we have a lot of use of math/rand package. - # We should probably move to crypto/rand at some point even though most of - # our uses doesn't seem to be security sensitive. - # TODO fix this and enable. - # go run ./linter/cryptorand ./... - - # This yields lot of legitimate warnings, most of which in practice would - # probably never happen. - # # TODO fix this and enable. - # go run ./linter/errcheck ./... - - go run ./linter/featureconfig ./... - - # Disabled since we have high cognitive complexity several places. - # TODO fix this and enable. - # go run ./linter/gocognit ./... - - go run ./linter/ineffassign ./... - go run ./linter/interfacechecker ./... - go run ./linter/logruswitherror ./... - - go run ./linter/shadowpredecl ./... - go run ./linter/slicedirect ./... - - # Disabled since it fails many places, although ones I looked into seem - # to be false positives logically. - # TODO fix this and enable and mark false positives with lint ignore. - # go run ./linter/uintcast ./... - go run ./linter/koanf ./... go run ./linter/pointercheck ./... golangci-lint run --fix diff --git a/arbos/l1pricing/l1pricing.go b/arbos/l1pricing/l1pricing.go index be5540b601..27ecae8b85 100644 --- a/arbos/l1pricing/l1pricing.go +++ b/arbos/l1pricing/l1pricing.go @@ -259,11 +259,11 @@ func (ps *L1PricingState) AddToL1FeesAvailable(delta *big.Int) (*big.Int, error) if err != nil { return nil, err } - newFee := new(big.Int).Add(old, delta) - if err := ps.SetL1FeesAvailable(newFee); err != nil { + new := new(big.Int).Add(old, delta) + if err := ps.SetL1FeesAvailable(new); err != nil { return nil, err } - return newFee, nil + return new, nil } func (ps *L1PricingState) TransferFromL1FeesAvailable( diff --git a/go.mod b/go.mod index f87887e18c..e504542a2f 100644 --- a/go.mod +++ b/go.mod @@ -124,7 +124,6 @@ require ( github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b // indirect github.com/gorilla/mux v1.8.0 // indirect - github.com/gostaticanalysis/comment v1.4.2 // indirect github.com/graph-gophers/graphql-go v1.3.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/h2non/filetype v1.0.6 // indirect @@ -254,7 +253,6 @@ require ( github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/supranational/blst v0.3.11 // indirect github.com/urfave/cli/v2 v2.25.7 // indirect - github.com/uudashr/gocognit v1.0.5 // indirect github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // indirect diff --git a/go.sum b/go.sum index 4e3f0f62b4..0a9649c434 100644 --- a/go.sum +++ b/go.sum @@ -581,9 +581,6 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gostaticanalysis/comment v1.4.2 h1:hlnx5+S2fY9Zo9ePo4AhgYsYHbM2+eAv8m/s1JiCd6Q= -github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= -github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -628,7 +625,6 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -1424,11 +1420,6 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.4.0 h1:CtfRrOVZtbDj8rt1WXjklw0kqqJQwICrCKmlfUuBUUw= github.com/openzipkin/zipkin-go v0.4.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ= -github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= -github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= -github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= -github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= -github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -1637,8 +1628,6 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45 github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= -github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= @@ -1669,8 +1658,6 @@ github.com/urfave/cli/v2 v2.24.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6f github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= -github.com/uudashr/gocognit v1.0.5 h1:rrSex7oHr3/pPLQ0xoWq108XMU8s678FJcQ+aSfOHa4= -github.com/uudashr/gocognit v1.0.5/go.mod h1:wgYz0mitoKOTysqxTDMOUXg+Jb5SvtihkfmugIZYpEA= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= @@ -1876,7 +1863,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 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.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= @@ -2153,10 +2139,8 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= 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.4/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.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= diff --git a/linter/comparesame/comparesame.go b/linter/comparesame/comparesame.go deleted file mode 100644 index 1d91592e99..0000000000 --- a/linter/comparesame/comparesame.go +++ /dev/null @@ -1,12 +0,0 @@ -// Static Analyzer to ensure code does not contain comparisons of identical expressions. -package main - -import ( - "github.com/prysmaticlabs/prysm/v4/tools/analyzers/comparesame" - - "golang.org/x/tools/go/analysis/singlechecker" -) - -func main() { - singlechecker.Main(comparesame.Analyzer) -} diff --git a/linter/cryptorand/cryptorand.go b/linter/cryptorand/cryptorand.go deleted file mode 100644 index a7cd1ca65a..0000000000 --- a/linter/cryptorand/cryptorand.go +++ /dev/null @@ -1,13 +0,0 @@ -// Static Analyzer to ensure the crypto/rand package is used for randomness -// throughout the codebase. -package main - -import ( - "github.com/prysmaticlabs/prysm/v4/tools/analyzers/cryptorand" - - "golang.org/x/tools/go/analysis/singlechecker" -) - -func main() { - singlechecker.Main(cryptorand.Analyzer) -} diff --git a/linter/errcheck/errcheck.go b/linter/errcheck/errcheck.go deleted file mode 100644 index 8275b2bab1..0000000000 --- a/linter/errcheck/errcheck.go +++ /dev/null @@ -1,12 +0,0 @@ -// Static analyzer to ensure that errors are handled in go code. -package main - -import ( - "github.com/prysmaticlabs/prysm/v4/tools/analyzers/errcheck" - - "golang.org/x/tools/go/analysis/singlechecker" -) - -func main() { - singlechecker.Main(errcheck.Analyzer) -} diff --git a/linter/featureconfig/featureconfig.go b/linter/featureconfig/featureconfig.go deleted file mode 100644 index bb9c2dd3a1..0000000000 --- a/linter/featureconfig/featureconfig.go +++ /dev/null @@ -1,12 +0,0 @@ -// Static analyzer to prevent leaking globals in tests. -package main - -import ( - "github.com/prysmaticlabs/prysm/v4/tools/analyzers/featureconfig" - - "golang.org/x/tools/go/analysis/singlechecker" -) - -func main() { - singlechecker.Main(featureconfig.Analyzer) -} diff --git a/linter/gocognit/gocognit.go b/linter/gocognit/gocognit.go deleted file mode 100644 index a50d0ac08d..0000000000 --- a/linter/gocognit/gocognit.go +++ /dev/null @@ -1,13 +0,0 @@ -// Static analyzer that checks for high cognitive complexity and complains when -// it's too high. -package main - -import ( - "github.com/prysmaticlabs/prysm/v4/tools/analyzers/gocognit" - - "golang.org/x/tools/go/analysis/singlechecker" -) - -func main() { - singlechecker.Main(gocognit.Analyzer) -} diff --git a/linter/ineffassign/ineffassign.go b/linter/ineffassign/ineffassign.go deleted file mode 100644 index 697e104822..0000000000 --- a/linter/ineffassign/ineffassign.go +++ /dev/null @@ -1,12 +0,0 @@ -// Static analyzer for detecting ineffectual assignments. -package main - -import ( - "github.com/prysmaticlabs/prysm/v4/tools/analyzers/ineffassign" - - "golang.org/x/tools/go/analysis/singlechecker" -) - -func main() { - singlechecker.Main(ineffassign.Analyzer) -} diff --git a/linter/interfacechecker/interfacechecker.go b/linter/interfacechecker/interfacechecker.go deleted file mode 100644 index 50bb56f749..0000000000 --- a/linter/interfacechecker/interfacechecker.go +++ /dev/null @@ -1,12 +0,0 @@ -// Static analyzer to prevent conditional checks on select interfaces. -package main - -import ( - "github.com/prysmaticlabs/prysm/v4/tools/analyzers/interfacechecker" - - "golang.org/x/tools/go/analysis/singlechecker" -) - -func main() { - singlechecker.Main(interfacechecker.Analyzer) -} diff --git a/linter/logruswitherror/logruswitherror.go b/linter/logruswitherror/logruswitherror.go deleted file mode 100644 index d0da1fcb12..0000000000 --- a/linter/logruswitherror/logruswitherror.go +++ /dev/null @@ -1,13 +0,0 @@ -// Static analyzer to ensure that log statements do not use errors in -// templated log statements. Authors should use logrus.WithError(). -package main - -import ( - "github.com/prysmaticlabs/prysm/v4/tools/analyzers/logruswitherror" - - "golang.org/x/tools/go/analysis/singlechecker" -) - -func main() { - singlechecker.Main(logruswitherror.Analyzer) -} diff --git a/linter/recursivelock/recursivelock.go b/linter/recursivelock/recursivelock.go index 8c8caff382..9349759cf3 100644 --- a/linter/recursivelock/recursivelock.go +++ b/linter/recursivelock/recursivelock.go @@ -1,4 +1,3 @@ -// Static Analyzer for detecting nested or recursive mutex read lock statements. package main import ( diff --git a/linter/shadowpredecl/shadowpredecl.go b/linter/shadowpredecl/shadowpredecl.go deleted file mode 100644 index e51ff0da83..0000000000 --- a/linter/shadowpredecl/shadowpredecl.go +++ /dev/null @@ -1,13 +0,0 @@ -// Static analyzer which disallows declaring constructs that shadow predeclared -// Go identifiers by having the same name. -package main - -import ( - "github.com/prysmaticlabs/prysm/v4/tools/analyzers/shadowpredecl" - - "golang.org/x/tools/go/analysis/singlechecker" -) - -func main() { - singlechecker.Main(shadowpredecl.Analyzer) -} diff --git a/linter/slicedirect/slicedirect.go b/linter/slicedirect/slicedirect.go deleted file mode 100644 index b31404013d..0000000000 --- a/linter/slicedirect/slicedirect.go +++ /dev/null @@ -1,13 +0,0 @@ -// Static analyzer to ensure that code does not contain applications of [:] -// on expressions which are already slices. -package main - -import ( - "github.com/prysmaticlabs/prysm/v4/tools/analyzers/slicedirect" - - "golang.org/x/tools/go/analysis/singlechecker" -) - -func main() { - singlechecker.Main(slicedirect.Analyzer) -} diff --git a/linter/uintcast/uintcast.go b/linter/uintcast/uintcast.go deleted file mode 100644 index 1f0eb78575..0000000000 --- a/linter/uintcast/uintcast.go +++ /dev/null @@ -1,13 +0,0 @@ -// Static analyzer for detecting unsafe uint to int casts. -// Use `lint:ignore uintcast` with proper justification to ignore this check. -package main - -import ( - "github.com/prysmaticlabs/prysm/v4/tools/analyzers/uintcast" - - "golang.org/x/tools/go/analysis/singlechecker" -) - -func main() { - singlechecker.Main(uintcast.Analyzer) -} From 68917ae115f3a777420d7874e73dd8e7c97feddd Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Sun, 24 Dec 2023 11:49:17 -0700 Subject: [PATCH 39/68] Revert "Merge pull request #2016 from OffchainLabs/recursivelock-lint" This reverts commit 459a63be17fc52566c26c50af75f6f5617ae2c1d, reversing changes made to a0e63172ca4c6ed08612622cb1605da02b599c6a. --- Makefile | 1 - go.mod | 32 +++++++++--------- go.sum | 47 --------------------------- linter/recursivelock/recursivelock.go | 11 ------- 4 files changed, 15 insertions(+), 76 deletions(-) delete mode 100644 linter/recursivelock/recursivelock.go diff --git a/Makefile b/Makefile index 42fd1e3595..4221100961 100644 --- a/Makefile +++ b/Makefile @@ -307,7 +307,6 @@ contracts/test/prover/proofs/%.json: $(arbitrator_cases)/%.wasm $(arbitrator_pro # strategic rules to minimize dependency building .make/lint: $(DEP_PREDICATE) build-node-deps $(ORDER_ONLY_PREDICATE) .make - go run ./linter/recursivelock ./... go run ./linter/koanf ./... go run ./linter/pointercheck ./... golangci-lint run --fix diff --git a/go.mod b/go.mod index e504542a2f..bdda6a61a1 100644 --- a/go.mod +++ b/go.mod @@ -18,11 +18,11 @@ require ( github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.10 github.com/aws/aws-sdk-go-v2/service/s3 v1.26.9 github.com/cavaliergopher/grab/v3 v3.0.1 - github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 + github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 github.com/codeclysm/extract/v3 v3.0.2 github.com/dgraph-io/badger/v3 v3.2103.2 github.com/enescakir/emoji v1.0.0 - github.com/ethereum/go-ethereum v1.13.1 + github.com/ethereum/go-ethereum v1.10.26 github.com/fatih/structtag v1.2.0 github.com/gdamore/tcell/v2 v2.6.0 github.com/google/go-cmp v0.5.9 @@ -73,7 +73,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/blang/semver/v4 v4.0.0 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect @@ -97,7 +97,7 @@ require ( github.com/dgraph-io/ristretto v0.1.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 // indirect + github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 // indirect github.com/dustin/go-humanize v1.0.0 // indirect github.com/elastic/gosigar v0.14.2 // indirect github.com/emirpasic/gods v1.18.1 // indirect @@ -131,7 +131,7 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 // indirect - github.com/huin/goupnp v1.3.0 // indirect + github.com/huin/goupnp v1.1.0 // indirect github.com/ipfs/bbloom v0.0.4 // indirect github.com/ipfs/go-bitfield v1.1.0 // indirect github.com/ipfs/go-block-format v0.1.1 // indirect @@ -195,7 +195,7 @@ require ( github.com/libp2p/go-libp2p-asn-util v0.3.0 // indirect github.com/libp2p/go-libp2p-kad-dht v0.21.1 // indirect github.com/libp2p/go-libp2p-kbucket v0.5.0 // indirect - github.com/libp2p/go-libp2p-pubsub v0.9.3 // indirect + github.com/libp2p/go-libp2p-pubsub v0.9.0 // indirect github.com/libp2p/go-libp2p-pubsub-router v0.6.0 // indirect github.com/libp2p/go-libp2p-record v0.2.0 // indirect github.com/libp2p/go-libp2p-routing-helpers v0.6.2 // indirect @@ -238,7 +238,6 @@ require ( github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect - github.com/prysmaticlabs/prysm/v4 v4.1.1 // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-19 v0.3.3 // indirect github.com/quic-go/qtls-go1-20 v0.2.3 // indirect @@ -246,13 +245,13 @@ require ( github.com/quic-go/webtransport-go v0.5.2 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect github.com/rhnvrm/simples3 v0.6.1 // indirect - github.com/rivo/uniseg v0.4.4 // indirect + github.com/rivo/uniseg v0.4.3 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/samber/lo v1.36.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/supranational/blst v0.3.11 // indirect - github.com/urfave/cli/v2 v2.25.7 // indirect + github.com/urfave/cli/v2 v2.24.1 // indirect github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // indirect @@ -262,7 +261,6 @@ require ( github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 // indirect - github.com/yusufpapurcu/wmi v1.2.2 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel v1.7.0 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.7.0 // indirect @@ -310,12 +308,12 @@ require ( require ( github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect - github.com/VictoriaMetrics/fastcache v1.12.0 // indirect + github.com/VictoriaMetrics/fastcache v1.6.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect 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-20191108122812-4678299bea08 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect + github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect + github.com/go-ole/go-ole v1.2.1 // 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 @@ -332,11 +330,11 @@ require ( github.com/mitchellh/pointerstructure v1.2.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/rs/cors v1.7.0 // indirect - github.com/shirou/gopsutil v3.21.11+incompatible // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/status-im/keycard-go v0.2.0 // indirect - github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d - github.com/tklauser/go-sysconf v0.3.12 // indirect - github.com/tklauser/numcpus v0.6.1 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 + github.com/tklauser/go-sysconf v0.3.5 // indirect + github.com/tklauser/numcpus v0.2.2 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect ) diff --git a/go.sum b/go.sum index 0a9649c434..bf8b4b826d 100644 --- a/go.sum +++ b/go.sum @@ -173,8 +173,6 @@ github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13P github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= -github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= -github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= @@ -200,7 +198,6 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/ceramicnetwork/go-dag-jose v0.1.0 h1:yJ/HVlfKpnD3LdYP03AHyTvbm3BpPiz2oZiOeReJRdU= github.com/ceramicnetwork/go-dag-jose v0.1.0/go.mod h1:qYA1nYt0X8u4XoMAVoOV3upUVKtrxy/I670Dg5F0wjI= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= -github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -228,7 +225,6 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= -github.com/cockroachdb/datadriven v1.0.3-0.20230801171734-e384cf455877 h1:1MLK4YpFtIEo3ZtMA5C795Wtv5VuUnrXX7mQG+aHg6o= github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= @@ -236,8 +232,6 @@ github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZe github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk= github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM= -github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 h1:T+Np/xtzIjYM/P5NAw0e2Rf1FGvzDau1h54MKvx8G7w= -github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06/go.mod h1:bynZ3gvVyhlvjLI7PT6dmZ7g76xzJ7HpxfjgkzCGz6s= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= @@ -313,8 +307,6 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 h1:+3HCtB74++ClLy8GgjUQYeC8R4ILzVcIe8+5edAJJnE= github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= -github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 h1:qwcF+vdFrvPSEUDSX5RVoRccG8a5DhOdWdQ4zN62zzo= -github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -368,7 +360,6 @@ github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09 github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0= @@ -376,8 +367,6 @@ 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-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= -github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/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= @@ -414,8 +403,6 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-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= @@ -549,7 +536,6 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b h1:Qcx5LM0fSiks9uCyFZwDBUasd3lxd1RM0GYpL+Li5o4= github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= @@ -654,12 +640,9 @@ github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmK github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= github.com/huin/goupnp v1.1.0 h1:gEe0Dp/lZmPZiDFzJJaOfUpOvv2MKUkoBX8lDrn9vKU= github.com/huin/goupnp v1.1.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= -github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= -github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= @@ -1075,8 +1058,6 @@ github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuD github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= github.com/libp2p/go-libp2p-pubsub v0.9.0 h1:mcLb4WzwhUG4OKb0rp1/bYMd/DYhvMyzJheQH3LMd1s= github.com/libp2p/go-libp2p-pubsub v0.9.0/go.mod h1:OEsj0Cc/BpkqikXRTrVspWU/Hx7bMZwHP+6vNMd+c7I= -github.com/libp2p/go-libp2p-pubsub v0.9.3 h1:ihcz9oIBMaCK9kcx+yHWm3mLAFBMAUsM4ux42aikDxo= -github.com/libp2p/go-libp2p-pubsub v0.9.3/go.mod h1:RYA7aM9jIic5VV47WXu4GkcRxRhrdElWf8xtyli+Dzc= github.com/libp2p/go-libp2p-pubsub-router v0.6.0 h1:D30iKdlqDt5ZmLEYhHELCMRj8b4sFAqrUcshIUvVP/s= github.com/libp2p/go-libp2p-pubsub-router v0.6.0/go.mod h1:FY/q0/RBTKsLA7l4vqC2cbRbOvyDotg8PJQ7j8FDudE= github.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= @@ -1391,7 +1372,6 @@ github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= @@ -1401,8 +1381,6 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= @@ -1486,8 +1464,6 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/prysmaticlabs/prysm/v4 v4.1.1 h1:sbBkgfPzo/SGTJ5IimtsZSGECoRlhbowR1rEhTOdvHo= -github.com/prysmaticlabs/prysm/v4 v4.1.1/go.mod h1:+o907dc4mwEE0wJkQ8RrzCroC+q2WCzdCLtikwonw8c= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/qtls-go1-19 v0.3.3 h1:wznEHvJwd+2X3PqftRha0SUKmGsnb6dfArMhy9PeJVE= @@ -1512,8 +1488,6 @@ github.com/rivo/tview v0.0.0-20230814110005-ccc2c8119703/go.mod h1:nVwGv4MP47T0j github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -1539,8 +1513,6 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= -github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= @@ -1616,7 +1588,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= @@ -1625,18 +1596,12 @@ github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3 github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= -github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= -github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= -github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= @@ -1655,8 +1620,6 @@ github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60Nt github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/urfave/cli/v2 v2.24.1 h1:/QYYr7g0EhwXEML8jO+8OYt5trPnLHS0p3mrgExJ5NU= github.com/urfave/cli/v2 v2.24.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= -github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= -github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= @@ -1721,8 +1684,6 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA= -github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= -github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= @@ -1925,8 +1886,6 @@ golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= @@ -1989,7 +1948,6 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2042,10 +2000,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2053,8 +2009,6 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -2149,7 +2103,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= diff --git a/linter/recursivelock/recursivelock.go b/linter/recursivelock/recursivelock.go deleted file mode 100644 index 9349759cf3..0000000000 --- a/linter/recursivelock/recursivelock.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import ( - "github.com/prysmaticlabs/prysm/v4/tools/analyzers/recursivelock" - - "golang.org/x/tools/go/analysis/singlechecker" -) - -func main() { - singlechecker.Main(recursivelock.Analyzer) -} From 1fac15d8bd2b0c3152c937cd444ba89d4abdedd2 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 27 Dec 2023 11:38:27 -0700 Subject: [PATCH 40/68] Fix sequencer forwarder panic on lack of targets --- execution/gethexec/forwarder.go | 2 ++ execution/gethexec/sequencer.go | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/execution/gethexec/forwarder.go b/execution/gethexec/forwarder.go index dc3420f8de..7fec46c635 100644 --- a/execution/gethexec/forwarder.go +++ b/execution/gethexec/forwarder.go @@ -205,6 +205,8 @@ func (f *TxForwarder) Initialize(inctx context.Context) error { f.targets = targets if len(f.rpcClients) > 0 { f.enabled.Store(true) + } else if lastError == nil { + return errors.New("no non-empty forwarding targets specified") } else { return lastError } diff --git a/execution/gethexec/sequencer.go b/execution/gethexec/sequencer.go index 1c60702c85..b21a274527 100644 --- a/execution/gethexec/sequencer.go +++ b/execution/gethexec/sequencer.go @@ -487,7 +487,7 @@ func (s *Sequencer) CheckHealth(ctx context.Context) error { func (s *Sequencer) ForwardTarget() string { s.activeMutex.Lock() defer s.activeMutex.Unlock() - if s.forwarder == nil { + if s.forwarder == nil || len(s.forwarder.targets) == 0 { return "" } return s.forwarder.targets[0] @@ -497,7 +497,7 @@ func (s *Sequencer) ForwardTo(url string) error { s.activeMutex.Lock() defer s.activeMutex.Unlock() if s.forwarder != nil { - if s.forwarder.targets[0] == url { + if len(s.forwarder.targets) > 0 && s.forwarder.targets[0] == url { log.Warn("attempted to update sequencer forward target with existing target", "url", url) return nil } From 966a1724ac5bcf0eb94e9c245c44c6a33a6ba692 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 27 Dec 2023 12:04:48 -0700 Subject: [PATCH 41/68] Fix broken NodeInterface methods --- execution/gethexec/node.go | 2 +- system_tests/nodeinterface_test.go | 37 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/execution/gethexec/node.go b/execution/gethexec/node.go index 7dd6e301fe..00337cc355 100644 --- a/execution/gethexec/node.go +++ b/execution/gethexec/node.go @@ -248,7 +248,7 @@ func CreateExecutionNode( } func (n *ExecutionNode) Initialize(ctx context.Context, arbnode interface{}, sync arbitrum.SyncProgressBackend) error { - n.ArbInterface.Initialize(n) + n.ArbInterface.Initialize(arbnode) err := n.Backend.Start() if err != nil { return fmt.Errorf("error starting geth backend: %w", err) diff --git a/system_tests/nodeinterface_test.go b/system_tests/nodeinterface_test.go index 40953a449d..3424a58e9e 100644 --- a/system_tests/nodeinterface_test.go +++ b/system_tests/nodeinterface_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/offchainlabs/nitro/arbos/util" "github.com/offchainlabs/nitro/solgen/go/node_interfacegen" @@ -73,3 +74,39 @@ func TestL2BlockRangeForL1(t *testing.T) { t.Fatalf("GetL2BlockRangeForL1 didn't fail for an invalid input") } } + +func TestGetL1Confirmations(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + builder := NewNodeBuilder(ctx).DefaultConfig(t, true) + cleanup := builder.Build(t) + defer cleanup() + + nodeInterface, err := node_interfacegen.NewNodeInterface(types.NodeInterfaceAddress, builder.L2.Client) + Require(t, err) + + genesisBlock, err := builder.L2.Client.BlockByNumber(ctx, big.NewInt(0)) + Require(t, err) + l1Confs, err := nodeInterface.GetL1Confirmations(&bind.CallOpts{}, genesisBlock.Hash()) + Require(t, err) + + numTransactions := 200 + + if l1Confs >= uint64(numTransactions) { + t.Fatalf("L1Confirmations for latest block %v is already %v (over %v)", genesisBlock.Number(), l1Confs, numTransactions) + } + + for i := 0; i < numTransactions; i++ { + builder.L1.TransferBalance(t, "User", "User", common.Big0, builder.L1Info) + } + + l1Confs, err = nodeInterface.GetL1Confirmations(&bind.CallOpts{}, genesisBlock.Hash()) + Require(t, err) + + // Allow a gap of 10 for asynchronicity, just in case + if l1Confs+10 < uint64(numTransactions) { + t.Fatalf("L1Confirmations for latest block %v is only %v (did not hit expected %v)", genesisBlock.Number(), l1Confs, numTransactions) + } +} From c75e6049a787889d5bbcb20787b49d47573254f9 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 27 Dec 2023 12:22:20 -0700 Subject: [PATCH 42/68] Allow the TxForwarder to be empty --- execution/gethexec/forwarder.go | 16 ++++++++++++---- execution/gethexec/sequencer.go | 6 +++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/execution/gethexec/forwarder.go b/execution/gethexec/forwarder.go index 7fec46c635..9783c604a1 100644 --- a/execution/gethexec/forwarder.go +++ b/execution/gethexec/forwarder.go @@ -151,7 +151,7 @@ func (f *TxForwarder) PublishTransaction(inctx context.Context, tx *types.Transa if err == nil || !f.tryNewForwarderErrors.MatchString(err.Error()) { return err } - log.Info("error forwarding transaction to a backup target", "target", f.targets[pos], "err", err) + log.Warn("error forwarding transaction to a backup target", "target", f.targets[pos], "err", err) } return errors.New("failed to publish transaction to any of the forwarding targets") } @@ -161,7 +161,9 @@ const maxHealthTimeout = 10 * time.Second // CheckHealth returns health of the highest priority forwarding target func (f *TxForwarder) CheckHealth(inctx context.Context) error { - if !f.enabled.Load() { + // If f.enabled is false, len(f.rpcClients) should always be greater than zero, + // but better safe than sorry. + if !f.enabled.Load() || len(f.rpcClients) == 0 { return ErrNoSequencer } f.healthMutex.Lock() @@ -205,8 +207,6 @@ func (f *TxForwarder) Initialize(inctx context.Context) error { f.targets = targets if len(f.rpcClients) > 0 { f.enabled.Store(true) - } else if lastError == nil { - return errors.New("no non-empty forwarding targets specified") } else { return lastError } @@ -232,6 +232,14 @@ func (f *TxForwarder) Started() bool { return true } +// Returns the URL of the first forwarding target, or an empty string if none are set. +func (f *TxForwarder) PrimaryTarget() string { + if len(f.targets) == 0 { + return "" + } + return f.targets[0] +} + type TxDropper struct{} func NewTxDropper() *TxDropper { diff --git a/execution/gethexec/sequencer.go b/execution/gethexec/sequencer.go index b21a274527..5db38cbb4d 100644 --- a/execution/gethexec/sequencer.go +++ b/execution/gethexec/sequencer.go @@ -487,17 +487,17 @@ func (s *Sequencer) CheckHealth(ctx context.Context) error { func (s *Sequencer) ForwardTarget() string { s.activeMutex.Lock() defer s.activeMutex.Unlock() - if s.forwarder == nil || len(s.forwarder.targets) == 0 { + if s.forwarder == nil { return "" } - return s.forwarder.targets[0] + return s.forwarder.PrimaryTarget() } func (s *Sequencer) ForwardTo(url string) error { s.activeMutex.Lock() defer s.activeMutex.Unlock() if s.forwarder != nil { - if len(s.forwarder.targets) > 0 && s.forwarder.targets[0] == url { + if s.forwarder.PrimaryTarget() == url { log.Warn("attempted to update sequencer forward target with existing target", "url", url) return nil } From 02bfe08a2da0ce08522c52f0507d5fc49927b841 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 27 Dec 2023 12:26:01 -0700 Subject: [PATCH 43/68] Fix comment --- execution/gethexec/forwarder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/execution/gethexec/forwarder.go b/execution/gethexec/forwarder.go index 9783c604a1..984c7224e8 100644 --- a/execution/gethexec/forwarder.go +++ b/execution/gethexec/forwarder.go @@ -161,7 +161,7 @@ const maxHealthTimeout = 10 * time.Second // CheckHealth returns health of the highest priority forwarding target func (f *TxForwarder) CheckHealth(inctx context.Context) error { - // If f.enabled is false, len(f.rpcClients) should always be greater than zero, + // If f.enabled is true, len(f.rpcClients) should always be greater than zero, // but better safe than sorry. if !f.enabled.Load() || len(f.rpcClients) == 0 { return ErrNoSequencer From 3d4111ca9b6bd9759c66c1182b0e693ae827da9b Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Sat, 30 Dec 2023 17:21:57 -0600 Subject: [PATCH 44/68] update change date --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 0739c2d8bf..e16976d32d 100644 --- a/LICENSE +++ b/LICENSE @@ -30,7 +30,7 @@ Additional Use Grant: You may use the Licensed Work in a production environment -Change Date: Dec 31, 2027 +Change Date: Dec 31, 2028 Change License: Apache License Version 2.0 From 172765d50b35c7fbd77031893bf37c35a3a50f12 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Sat, 30 Dec 2023 17:45:29 -0600 Subject: [PATCH 45/68] update license year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index e16976d32d..c96c4d6cb2 100644 --- a/LICENSE +++ b/LICENSE @@ -10,7 +10,7 @@ Parameters Licensor: Offchain Labs Licensed Work: Arbitrum Nitro - The Licensed Work is (c) 2021-2023 Offchain Labs + The Licensed Work is (c) 2021-2024 Offchain Labs Additional Use Grant: You may use the Licensed Work in a production environment solely to provide a point of interface to permit end users or applications From e2086163542bf28ee70b1dcfb2d302e4ed5b8cbf Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Mon, 1 Jan 2024 21:58:48 -0600 Subject: [PATCH 46/68] Hide error when getting latest confirmed block --- util/headerreader/header_reader.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/util/headerreader/header_reader.go b/util/headerreader/header_reader.go index a5100f74fa..c5b8fa23f1 100644 --- a/util/headerreader/header_reader.go +++ b/util/headerreader/header_reader.go @@ -53,6 +53,7 @@ type HeaderReader struct { type cachedHeader struct { mutex sync.Mutex + blockTag string // "safe" or "finalized" rpcBlockNum *big.Int headWhenCached *types.Header header *types.Header @@ -135,8 +136,8 @@ func New(ctx context.Context, client arbutil.L1Interface, config ConfigFetcher, arbSys: arbSys, outChannels: make(map[chan<- *types.Header]struct{}), outChannelsBehind: make(map[chan<- *types.Header]struct{}), - safe: cachedHeader{rpcBlockNum: big.NewInt(rpc.SafeBlockNumber.Int64())}, - finalized: cachedHeader{rpcBlockNum: big.NewInt(rpc.FinalizedBlockNumber.Int64())}, + safe: cachedHeader{blockTag: "safe", rpcBlockNum: big.NewInt(rpc.SafeBlockNumber.Int64())}, + finalized: cachedHeader{blockTag: "finalized", rpcBlockNum: big.NewInt(rpc.FinalizedBlockNumber.Int64())}, }, nil } @@ -470,6 +471,11 @@ func (s *HeaderReader) getCached(ctx context.Context, c *cachedHeader) (*types.H } header, err := s.client.HeaderByNumber(ctx, c.rpcBlockNum) if err != nil { + if !errors.Is(err, context.Canceled) { + log.Warn("Failed to get latest confirmed block", "blockTag", c.blockTag, "err", err) + // Hide error to caller to avoid exposing potentially sensitive L1 information. + err = fmt.Errorf("failed to get latest %v block", c.blockTag) + } return nil, err } c.header = header From ef961aef3af2e5b1651024428af51a85f0d2655b Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Tue, 2 Jan 2024 11:26:11 +0530 Subject: [PATCH 47/68] enable EphemeralErrorHandler to ignore error for a certain duration --- arbnode/batch_poster.go | 26 ++++++++++++++++++++------ staker/staker.go | 2 +- util/log.go | 20 ++++++++++++++++---- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 005012c5a1..8885dab13d 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -1145,8 +1145,18 @@ func (b *BatchPoster) Start(ctxIn context.Context) { b.redisLock.Start(ctxIn) b.StopWaiter.Start(ctxIn, b) b.LaunchThread(b.pollForReverts) - commonEphemeralErrorHandler := util.NewEphemeralErrorHandler(time.Minute, "") - exceedMaxMempoolSizeEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, "will exceed max mempool size") + commonEphemeralErrorHandler := util.NewEphemeralErrorHandler(time.Minute, "", 0, nil) + exceedMaxMempoolSizeEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, dataposter.ErrExceedsMaxMempoolSize.Error(), time.Minute, log.Debug) + storageRaceEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, storage.ErrStorageRace.Error(), time.Minute, log.Debug) + normalGasEstimationFailedEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, ErrNormalGasEstimationFailed.Error(), time.Minute, log.Debug) + accumulatorNotFoundEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, AccumulatorNotFoundErr.Error(), time.Minute, log.Debug) + resetAllEphemeralErrs := func() { + commonEphemeralErrorHandler.Reset() + exceedMaxMempoolSizeEphemeralErrorHandler.Reset() + storageRaceEphemeralErrorHandler.Reset() + normalGasEstimationFailedEphemeralErrorHandler.Reset() + accumulatorNotFoundEphemeralErrorHandler.Reset() + } b.CallIteratively(func(ctx context.Context) time.Duration { var err error if common.HexToAddress(b.config().GasRefunderAddress) != (common.Address{}) { @@ -1174,14 +1184,12 @@ func (b *BatchPoster) Start(ctxIn context.Context) { if !couldLock { log.Debug("Not posting batches right now because another batch poster has the lock or this node is behind") b.building = nil - commonEphemeralErrorHandler.Reset() - exceedMaxMempoolSizeEphemeralErrorHandler.Reset() + resetAllEphemeralErrs() return b.config().PollInterval } posted, err := b.maybePostSequencerBatch(ctx) if err == nil { - commonEphemeralErrorHandler.Reset() - exceedMaxMempoolSizeEphemeralErrorHandler.Reset() + resetAllEphemeralErrs() } if err != nil { if ctx.Err() != nil { @@ -1193,7 +1201,13 @@ func (b *BatchPoster) Start(ctxIn context.Context) { // Likely the inbox tracker just isn't caught up. // Let's see if this error disappears naturally. logLevel = commonEphemeralErrorHandler.LogLevel(err, logLevel) + // If the error matches one of these, it's only logged at debug for the first minute, + // then at warn for the next 4 minutes, then at error. If the error isn't one of these, + // it'll be logged at warn for the first minute, then at error. logLevel = exceedMaxMempoolSizeEphemeralErrorHandler.LogLevel(err, logLevel) + logLevel = storageRaceEphemeralErrorHandler.LogLevel(err, logLevel) + logLevel = normalGasEstimationFailedEphemeralErrorHandler.LogLevel(err, logLevel) + logLevel = accumulatorNotFoundEphemeralErrorHandler.LogLevel(err, logLevel) logLevel("error posting batch", "err", err) return b.config().ErrorDelay } else if posted { diff --git a/staker/staker.go b/staker/staker.go index 7a2342c260..002dd1f754 100644 --- a/staker/staker.go +++ b/staker/staker.go @@ -402,7 +402,7 @@ func (s *Staker) Start(ctxIn context.Context) { } s.StopWaiter.Start(ctxIn, s) backoff := time.Second - ephemeralErrorHandler := util.NewEphemeralErrorHandler(10*time.Minute, "is ahead of on-chain nonce") + ephemeralErrorHandler := util.NewEphemeralErrorHandler(10*time.Minute, "is ahead of on-chain nonce", 0, nil) s.CallIteratively(func(ctx context.Context) (returningWait time.Duration) { defer func() { panicErr := recover() diff --git a/util/log.go b/util/log.go index b68f503f54..0d87b55977 100644 --- a/util/log.go +++ b/util/log.go @@ -13,13 +13,18 @@ type EphemeralErrorHandler struct { Duration time.Duration ErrorString string FirstOccurrence *time.Time + + IgnoreDuration time.Duration + IgnoredErrLogLevel func(string, ...interface{}) // Default IgnoredErrLogLevel is log.Debug } -func NewEphemeralErrorHandler(duration time.Duration, errorString string) *EphemeralErrorHandler { +func NewEphemeralErrorHandler(duration time.Duration, errorString string, ignoreDuration time.Duration, ignoredErrLogLevel func(msg string, ctx ...interface{})) *EphemeralErrorHandler { return &EphemeralErrorHandler{ - Duration: duration, - ErrorString: errorString, - FirstOccurrence: &time.Time{}, + Duration: duration, + ErrorString: errorString, + FirstOccurrence: &time.Time{}, + IgnoreDuration: ignoreDuration, + IgnoredErrLogLevel: ignoredErrLogLevel, } } @@ -39,6 +44,13 @@ func (h *EphemeralErrorHandler) LogLevel(err error, currentLogLevel func(msg str return currentLogLevel } + if h.IgnoreDuration != 0 && time.Since(*h.FirstOccurrence) < h.IgnoreDuration { + if h.IgnoredErrLogLevel != nil { + return h.IgnoredErrLogLevel + } + return log.Debug + } + logLevel := log.Error if *h.FirstOccurrence == (time.Time{}) { *h.FirstOccurrence = time.Now() From b19326290ce9518ebfd704b8a87eb2021b9a2769 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Tue, 2 Jan 2024 20:01:07 +0530 Subject: [PATCH 48/68] code refactor --- arbnode/batch_poster.go | 10 +++--- staker/staker.go | 2 +- util/log.go | 21 +++++++------ util/log_test.go | 70 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 16 deletions(-) create mode 100644 util/log_test.go diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 8885dab13d..4bd0e2490a 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -1145,11 +1145,11 @@ func (b *BatchPoster) Start(ctxIn context.Context) { b.redisLock.Start(ctxIn) b.StopWaiter.Start(ctxIn, b) b.LaunchThread(b.pollForReverts) - commonEphemeralErrorHandler := util.NewEphemeralErrorHandler(time.Minute, "", 0, nil) - exceedMaxMempoolSizeEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, dataposter.ErrExceedsMaxMempoolSize.Error(), time.Minute, log.Debug) - storageRaceEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, storage.ErrStorageRace.Error(), time.Minute, log.Debug) - normalGasEstimationFailedEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, ErrNormalGasEstimationFailed.Error(), time.Minute, log.Debug) - accumulatorNotFoundEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, AccumulatorNotFoundErr.Error(), time.Minute, log.Debug) + commonEphemeralErrorHandler := util.NewEphemeralErrorHandler(time.Minute, "", 0) + exceedMaxMempoolSizeEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, dataposter.ErrExceedsMaxMempoolSize.Error(), time.Minute) + storageRaceEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, storage.ErrStorageRace.Error(), time.Minute) + normalGasEstimationFailedEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, ErrNormalGasEstimationFailed.Error(), time.Minute) + accumulatorNotFoundEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, AccumulatorNotFoundErr.Error(), time.Minute) resetAllEphemeralErrs := func() { commonEphemeralErrorHandler.Reset() exceedMaxMempoolSizeEphemeralErrorHandler.Reset() diff --git a/staker/staker.go b/staker/staker.go index 002dd1f754..2a95e9c9f7 100644 --- a/staker/staker.go +++ b/staker/staker.go @@ -402,7 +402,7 @@ func (s *Staker) Start(ctxIn context.Context) { } s.StopWaiter.Start(ctxIn, s) backoff := time.Second - ephemeralErrorHandler := util.NewEphemeralErrorHandler(10*time.Minute, "is ahead of on-chain nonce", 0, nil) + ephemeralErrorHandler := util.NewEphemeralErrorHandler(10*time.Minute, "is ahead of on-chain nonce", 0) s.CallIteratively(func(ctx context.Context) (returningWait time.Duration) { defer func() { panicErr := recover() diff --git a/util/log.go b/util/log.go index 0d87b55977..dbeed8051d 100644 --- a/util/log.go +++ b/util/log.go @@ -18,18 +18,19 @@ type EphemeralErrorHandler struct { IgnoredErrLogLevel func(string, ...interface{}) // Default IgnoredErrLogLevel is log.Debug } -func NewEphemeralErrorHandler(duration time.Duration, errorString string, ignoreDuration time.Duration, ignoredErrLogLevel func(msg string, ctx ...interface{})) *EphemeralErrorHandler { +func NewEphemeralErrorHandler(duration time.Duration, errorString string, ignoreDuration time.Duration) *EphemeralErrorHandler { return &EphemeralErrorHandler{ Duration: duration, ErrorString: errorString, FirstOccurrence: &time.Time{}, IgnoreDuration: ignoreDuration, - IgnoredErrLogLevel: ignoredErrLogLevel, + IgnoredErrLogLevel: log.Debug, } } -// LogLevel method defaults to returning the input currentLogLevel if the givenerror doesnt contain the errorSubstring, +// LogLevel method defaults to returning the input currentLogLevel if the given error doesnt contain the errorSubstring, // but if it does, then returns one of the corresponding loglevels as follows +// - IgnoredErrLogLevel - if the error has been repeating for less than the IgnoreDuration of time. Defaults to log.Debug // - log.Warn - if the error has been repeating for less than the given duration of time // - log.Error - Otherwise // @@ -44,6 +45,10 @@ func (h *EphemeralErrorHandler) LogLevel(err error, currentLogLevel func(msg str return currentLogLevel } + if *h.FirstOccurrence == (time.Time{}) { + *h.FirstOccurrence = time.Now() + } + if h.IgnoreDuration != 0 && time.Since(*h.FirstOccurrence) < h.IgnoreDuration { if h.IgnoredErrLogLevel != nil { return h.IgnoredErrLogLevel @@ -51,14 +56,10 @@ func (h *EphemeralErrorHandler) LogLevel(err error, currentLogLevel func(msg str return log.Debug } - logLevel := log.Error - if *h.FirstOccurrence == (time.Time{}) { - *h.FirstOccurrence = time.Now() - logLevel = log.Warn - } else if time.Since(*h.FirstOccurrence) < h.Duration { - logLevel = log.Warn + if time.Since(*h.FirstOccurrence) < h.Duration { + return log.Warn } - return logLevel + return log.Error } func (h *EphemeralErrorHandler) Reset() { diff --git a/util/log_test.go b/util/log_test.go new file mode 100644 index 0000000000..f8007373f2 --- /dev/null +++ b/util/log_test.go @@ -0,0 +1,70 @@ +package util + +import ( + "errors" + "reflect" + "testing" + "time" + + "github.com/ethereum/go-ethereum/log" +) + +func compareFunctions(f1, f2 func(msg string, ctx ...interface{})) bool { + return reflect.ValueOf(f1).Pointer() == reflect.ValueOf(f2).Pointer() +} +func TestSimple(t *testing.T) { + allErrHandler := NewEphemeralErrorHandler(2500*time.Millisecond, "", time.Second) + err := errors.New("sample error") + logLevel := allErrHandler.LogLevel(err, log.Error) + if !compareFunctions(log.Debug, logLevel) { + t.Fatalf("incorrect loglevel output. Want: Debug") + } + + time.Sleep(1 * time.Second) + logLevel = allErrHandler.LogLevel(err, log.Error) + if !compareFunctions(log.Warn, logLevel) { + t.Fatalf("incorrect loglevel output. Want: Warn") + } + + time.Sleep(2 * time.Second) + logLevel = allErrHandler.LogLevel(err, log.Error) + if !compareFunctions(log.Error, logLevel) { + t.Fatalf("incorrect loglevel output. Want: Error") + } +} + +func TestComplex(t *testing.T) { + // Simulation: errorA happens continuously for 2 seconds and then errorB happens + errorAHandler := NewEphemeralErrorHandler(time.Second, "errorA", 0) + errorBHandler := NewEphemeralErrorHandler(1500*time.Millisecond, "errorB", 0) + + // Computes result of chaining two ephemeral error handlers for a given recurring error + chainingErrHandlers := func(err error) func(string, ...interface{}) { + logLevel := log.Error + logLevel = errorAHandler.LogLevel(err, logLevel) + logLevel = errorBHandler.LogLevel(err, logLevel) + return logLevel + } + + errA := errors.New("this is a sample errorA") + if !compareFunctions(log.Warn, chainingErrHandlers(errA)) { + t.Fatalf("incorrect loglevel output. Want: Warn") + } + time.Sleep(2 * time.Second) + if !compareFunctions(log.Error, chainingErrHandlers(errA)) { + t.Fatalf("incorrect loglevel output. Want: Error") + } + + errB := errors.New("this is a sample errorB") + if !compareFunctions(log.Warn, chainingErrHandlers(errB)) { + t.Fatalf("incorrect loglevel output. Want: Warn") + } + if !compareFunctions(log.Warn, chainingErrHandlers(errA)) { + t.Fatalf("incorrect loglevel output. Want: Warn") + } + + errC := errors.New("random error") + if !compareFunctions(log.Error, chainingErrHandlers(errC)) { + t.Fatalf("incorrect loglevel output. Want: Error") + } +} From e8ec476514ba6b9286380eb2d8190296c628767a Mon Sep 17 00:00:00 2001 From: Joshua Colvin Date: Thu, 4 Jan 2024 17:55:21 -0700 Subject: [PATCH 49/68] Make clippy happy --- arbitrator/prover/src/machine.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 0849312f3d..6ca552d83c 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -362,7 +362,7 @@ impl Module { bin.memories.len() <= 1, "Multiple memories are not supported" ); - if let Some(limits) = bin.memories.get(0) { + if let Some(limits) = bin.memories.first() { let page_size = Memory::PAGE_SIZE; let initial = limits.initial; // validate() checks this is less than max::u32 let allowed = u32::MAX as u64 / Memory::PAGE_SIZE - 1; // we require the size remain *below* 2^32 From b9e9bcfecc6f857e20bb9b46d18847927af57a35 Mon Sep 17 00:00:00 2001 From: Joshua Colvin Date: Thu, 4 Jan 2024 18:22:06 -0700 Subject: [PATCH 50/68] Make clippy happy --- arbitrator/jit/src/syscall.rs | 6 +++--- arbitrator/wasm-libraries/go-stub/src/lib.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arbitrator/jit/src/syscall.rs b/arbitrator/jit/src/syscall.rs index c81641a7f8..4f657eeefa 100644 --- a/arbitrator/jit/src/syscall.rs +++ b/arbitrator/jit/src/syscall.rs @@ -337,7 +337,7 @@ pub fn js_value_call(mut env: WasmEnvMut, sp: u32) -> MaybeEscape { let value = match (object, method_name.as_slice()) { (Ref(GO_ID), b"_makeFuncWrapper") => { - let arg = match args.get(0) { + let arg = match args.first() { Some(arg) => arg, None => fail!( "Go trying to call Go._makeFuncWrapper with bad args {:?}", @@ -415,7 +415,7 @@ pub fn js_value_call(mut env: WasmEnvMut, sp: u32) -> MaybeEscape { (Ref(CRYPTO_ID), b"getRandomValues") => { let name = "crypto.getRandomValues"; - let id = match args.get(0) { + let id = match args.first() { Some(Ref(x)) => x, _ => fail!("Go trying to call {name} with bad args {:?}", args), }; @@ -456,7 +456,7 @@ pub fn js_value_new(mut env: WasmEnvMut, sp: u32) { let args_len = sp.read_u64(2); let args = sp.read_value_slice(args_ptr, args_len); match class { - UINT8_ARRAY_ID => match args.get(0) { + UINT8_ARRAY_ID => match args.first() { Some(JsValue::Number(size)) => { let id = pool.insert(DynamicObject::Uint8Array(vec![0; *size as usize])); sp.write_u64(4, GoValue::Object(id).encode()); diff --git a/arbitrator/wasm-libraries/go-stub/src/lib.rs b/arbitrator/wasm-libraries/go-stub/src/lib.rs index df77893fcb..1a5d1963c7 100644 --- a/arbitrator/wasm-libraries/go-stub/src/lib.rs +++ b/arbitrator/wasm-libraries/go-stub/src/lib.rs @@ -218,7 +218,7 @@ pub unsafe extern "C" fn go__syscall_js_valueNew(sp: GoStack) { let args_len = sp.read_u64(2); let args = read_value_slice(args_ptr, args_len); if class == UINT8_ARRAY_ID { - if let Some(InterpValue::Number(size)) = args.get(0) { + if let Some(InterpValue::Number(size)) = args.first() { let id = DynamicObjectPool::singleton() .insert(DynamicObject::Uint8Array(vec![0; *size as usize])); sp.write_u64(4, GoValue::Object(id).encode()); @@ -321,7 +321,7 @@ unsafe fn value_call_impl(sp: &mut GoStack) -> Result { let args_len = sp.read_u64(4); let args = read_value_slice(args_ptr, args_len); if object == InterpValue::Ref(GO_ID) && &method_name == b"_makeFuncWrapper" { - let id = args.get(0).ok_or_else(|| { + let id = args.first().ok_or_else(|| { format!( "Go attempting to call Go._makeFuncWrapper with bad args {:?}", args, @@ -405,7 +405,7 @@ unsafe fn value_call_impl(sp: &mut GoStack) -> Result { )) } } else if object == InterpValue::Ref(CRYPTO_ID) && &method_name == b"getRandomValues" { - let id = match args.get(0) { + let id = match args.first() { Some(InterpValue::Ref(x)) => *x, _ => { return Err(format!( From c02e8401caec041e2d8b856ecc8189f910a21475 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Fri, 5 Jan 2024 10:23:33 +0530 Subject: [PATCH 51/68] address PR comments --- staker/block_validator.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/staker/block_validator.go b/staker/block_validator.go index e649b112b5..352335a5db 100644 --- a/staker/block_validator.go +++ b/staker/block_validator.go @@ -98,11 +98,6 @@ type BlockValidatorConfig struct { func (c *BlockValidatorConfig) Validate() error { if c.MemoryFreeLimit == "default" { c.memoryFreeLimit = 1073741824 // 1GB - _, err := resourcemanager.NewCgroupsMemoryLimitCheckerIfSupported(c.memoryFreeLimit) - if err != nil { - log.Warn("Cgroups V1 or V2 is unsupported, memory-free-limit feature inside block-validator is disabled") - c.MemoryFreeLimit = "" - } } else if c.MemoryFreeLimit != "" { limit, err := resourcemanager.ParseMemLimit(c.MemoryFreeLimit) if err != nil { @@ -241,9 +236,14 @@ func NewBlockValidator( if config().MemoryFreeLimit != "" { limtchecker, err := resourcemanager.NewCgroupsMemoryLimitCheckerIfSupported(config().memoryFreeLimit) if err != nil { - return nil, fmt.Errorf("failed to create MemoryFreeLimitChecker, Cgroups V1 or V2 is unsupported") + if config().MemoryFreeLimit == "default" { + log.Warn("Cgroups V1 or V2 is unsupported, memory-free-limit feature inside block-validator is disabled") + } else { + return nil, fmt.Errorf("failed to create MemoryFreeLimitChecker, Cgroups V1 or V2 is unsupported") + } + } else { + ret.MemoryFreeLimitChecker = limtchecker } - ret.MemoryFreeLimitChecker = limtchecker } return ret, nil } From cc89883f629d5b43efc06c27b71cfac774b7259f Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Fri, 5 Jan 2024 10:28:25 +0530 Subject: [PATCH 52/68] address PR comments --- broadcaster/backlog/backlog.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/broadcaster/backlog/backlog.go b/broadcaster/backlog/backlog.go index 265f6b5af1..d2f7c66be7 100644 --- a/broadcaster/backlog/backlog.go +++ b/broadcaster/backlog/backlog.go @@ -19,8 +19,8 @@ var ( errOutOfBounds = errors.New("message not found in backlog") confirmedSequenceNumberGauge = metrics.NewRegisteredGauge("arb/sequencenumber/confirmed", nil) - backlogSizeInBytesGauge = metrics.NewRegisteredGauge("arb/feed/backlog/sizeinbytes", nil) - backlogSizeGauge = metrics.NewRegisteredGauge("arb/feed/backlog/size", nil) + backlogSizeInBytesGauge = metrics.NewRegisteredGauge("arb/feed/backlog/bytes", nil) + backlogSizeGauge = metrics.NewRegisteredGauge("arb/feed/backlog/messages", nil) ) // Backlog defines the interface for backlog. From 8dc996ccc0a83c7a4be78113baf6bcfac0caa1b4 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Mon, 8 Jan 2024 19:00:22 +0530 Subject: [PATCH 53/68] Handle long sequencer URLs and fix fetching the list of live sequencers --- .../seq-coordinator-manager.go | 21 +++++++++++++++---- util/redisutil/redis_coordinator.go | 16 ++++++++++---- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/cmd/seq-coordinator-manager/seq-coordinator-manager.go b/cmd/seq-coordinator-manager/seq-coordinator-manager.go index 07bc26af2c..daafe1e5e1 100644 --- a/cmd/seq-coordinator-manager/seq-coordinator-manager.go +++ b/cmd/seq-coordinator-manager/seq-coordinator-manager.go @@ -34,6 +34,7 @@ type manager struct { livelinessSet map[string]bool priorityList []string nonPriorityList []string + maxURLSize int } func main() { @@ -57,6 +58,9 @@ func main() { }, prioritiesSet: make(map[string]bool), livelinessSet: make(map[string]bool), + // maxURLSize dictates the allowed max length for sequencer urls + // urls exceeding this size will be truncated with an ellipsis + maxURLSize: 100, } seqManager.refreshAllLists(ctx) @@ -160,11 +164,11 @@ func main() { flex.SetDirection(tview.FlexRow). AddItem(priorityHeading, 0, 1, false). AddItem(tview.NewFlex(). - AddItem(prioritySeqList, 0, 2, true). + AddItem(prioritySeqList, seqManager.maxURLSize+20, 0, true). AddItem(priorityForm, 0, 3, true), 0, 12, true). AddItem(nonPriorityHeading, 0, 1, false). AddItem(tview.NewFlex(). - AddItem(nonPrioritySeqList, 0, 2, true). + AddItem(nonPrioritySeqList, seqManager.maxURLSize+20, 0, true). AddItem(nonPriorityForm, 0, 3, true), 0, 12, true). AddItem(instructions, 0, 3, false).SetBorder(true) @@ -243,13 +247,22 @@ func (sm *manager) populateLists(ctx context.Context) { if _, ok := sm.livelinessSet[seqURL]; ok { status = fmt.Sprintf("(%d) %v ", index, emoji.GreenCircle) } - prioritySeqList.AddItem(status+seqURL+sec, "", rune(0), nil).SetSecondaryTextColor(tcell.ColorPurple) + url := seqURL + if len(seqURL) > sm.maxURLSize { + url = seqURL[:sm.maxURLSize] + "..." + } + prioritySeqList.AddItem(status+url+sec, "", rune(0), nil).SetSecondaryTextColor(tcell.ColorPurple) + } nonPrioritySeqList.Clear() status := fmt.Sprintf("(-) %v ", emoji.GreenCircle) for _, seqURL := range sm.nonPriorityList { - nonPrioritySeqList.AddItem(status+seqURL, "", rune(0), nil) + url := seqURL + if len(seqURL) > sm.maxURLSize { + url = seqURL[:sm.maxURLSize] + "..." + } + nonPrioritySeqList.AddItem(status+url, "", rune(0), nil) } } diff --git a/util/redisutil/redis_coordinator.go b/util/redisutil/redis_coordinator.go index 357dfb2e93..6af141c668 100644 --- a/util/redisutil/redis_coordinator.go +++ b/util/redisutil/redis_coordinator.go @@ -89,11 +89,19 @@ func (rc *RedisCoordinator) GetPriorities(ctx context.Context) ([]string, error) return prioritiesList, nil } -// GetLiveliness returns a map whose keys are sequencers that have their liveliness set to OK +// GetLiveliness returns a list of sequencers that have their liveliness set to OK func (rc *RedisCoordinator) GetLiveliness(ctx context.Context) ([]string, error) { - livelinessList, _, err := rc.Client.Scan(ctx, 0, WANTS_LOCKOUT_KEY_PREFIX+"*", 0).Result() - if err != nil { - return []string{}, err + var livelinessList []string + cursor := uint64(0) + for { + keySlice, cursor, err := rc.Client.Scan(ctx, cursor, WANTS_LOCKOUT_KEY_PREFIX+"*", 0).Result() + if err != nil { + return []string{}, err + } + livelinessList = append(livelinessList, keySlice...) + if cursor == 0 { + break + } } for i, elem := range livelinessList { url := strings.TrimPrefix(elem, WANTS_LOCKOUT_KEY_PREFIX) From 552939ff3bf92579f9a93d1d961e366b69b9dd00 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Mon, 8 Jan 2024 11:33:49 -0600 Subject: [PATCH 54/68] Fix getL1Confirmations on L3s --- nodeInterface/NodeInterface.go | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/nodeInterface/NodeInterface.go b/nodeInterface/NodeInterface.go index 3b377bc5a0..bdcfb569f4 100644 --- a/nodeInterface/NodeInterface.go +++ b/nodeInterface/NodeInterface.go @@ -10,6 +10,7 @@ import ( "math/big" "sort" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/arbitrum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -17,6 +18,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" "github.com/offchainlabs/nitro/arbnode" "github.com/offchainlabs/nitro/arbos" @@ -24,6 +26,7 @@ import ( "github.com/offchainlabs/nitro/arbos/retryables" "github.com/offchainlabs/nitro/arbos/util" "github.com/offchainlabs/nitro/arbutil" + "github.com/offchainlabs/nitro/solgen/go/node_interfacegen" "github.com/offchainlabs/nitro/staker" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/merkletree" @@ -94,14 +97,37 @@ func (n NodeInterface) GetL1Confirmations(c ctx, evm mech, blockHash bytes32) (u return 0, err } } - latestL1Block, latestBatchCount := node.InboxReader.GetLastReadBlockAndBatchCount() - if latestBatchCount <= batch { - return 0, nil // batch was reorg'd out? - } meta, err := node.InboxTracker.GetBatchMetadata(batch) if err != nil { return 0, err } + if node.L1Reader.IsParentChainArbitrum() { + parentChainClient := node.L1Reader.Client() + parentNodeInterface, err := node_interfacegen.NewNodeInterface(types.NodeInterfaceAddress, parentChainClient) + if err != nil { + return 0, err + } + parentChainBlock, err := parentChainClient.BlockByNumber(n.context, new(big.Int).SetUint64(meta.ParentChainBlock)) + if err != nil { + // Hide the parent chain RPC error from the client in case it contains sensitive information. + // Likely though, this error is just "not found" because the block got reorg'd. + return 0, fmt.Errorf("failed to get parent chain block %v containing batch", meta.ParentChainBlock) + } + confs, err := parentNodeInterface.GetL1Confirmations(&bind.CallOpts{Context: n.context}, parentChainBlock.Hash()) + if err != nil { + log.Warn( + "Failed to get L1 confirmations from parent chain", + "blockNumber", meta.ParentChainBlock, + "blockHash", parentChainBlock.Hash(), "err", err, + ) + return 0, fmt.Errorf("failed to get L1 confirmations from parent chain for block %v", parentChainBlock.Hash()) + } + return confs, nil + } + latestL1Block, latestBatchCount := node.InboxReader.GetLastReadBlockAndBatchCount() + if latestBatchCount <= batch { + return 0, nil // batch was reorg'd out? + } if latestL1Block < meta.ParentChainBlock || arbutil.BlockNumberToMessageCount(blockNum, genesis) > meta.MessageCount { return 0, nil } From a211e2b7c11ed356c663f0aae4e7593f0153c161 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Mon, 8 Jan 2024 12:47:31 -0600 Subject: [PATCH 55/68] Fix test hitting panic if len(msgs)==0 --- wsbroadcastserver/clientconnection.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wsbroadcastserver/clientconnection.go b/wsbroadcastserver/clientconnection.go index 82b9603a0e..6f5bf54e4d 100644 --- a/wsbroadcastserver/clientconnection.go +++ b/wsbroadcastserver/clientconnection.go @@ -140,6 +140,9 @@ func (cc *ClientConnection) writeBacklog(ctx context.Context, segment backlog.Ba msgs = msgs[requestedIdx:] } } + if len(msgs) == 0 { + break + } isFirstSegment = false bm := &m.BroadcastMessage{ Version: m.V1, From 5169b84ecff1d8254b5e8372bb2f1cb5a30a9740 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Wed, 10 Jan 2024 17:14:12 +0100 Subject: [PATCH 56/68] Pass parent chain id to createNode instead of making chainID call using l1 client --- arbnode/node.go | 9 +++------ cmd/nitro/nitro.go | 1 + go-ethereum | 2 +- system_tests/common_test.go | 7 +++---- system_tests/das_test.go | 9 ++++----- system_tests/full_challenge_impl_test.go | 5 +++-- system_tests/recreatestate_rpc_test.go | 5 +++-- 7 files changed, 18 insertions(+), 20 deletions(-) diff --git a/arbnode/node.go b/arbnode/node.go index ae350de021..f92dcefe7c 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -348,6 +348,7 @@ func createNodeImpl( txOptsBatchPoster *bind.TransactOpts, dataSigner signature.DataSignerFunc, fatalErrChan chan error, + parentChainID *big.Int, ) (*Node, error) { config := configFetcher.Get() @@ -561,11 +562,6 @@ func createNodeImpl( var stakerObj *staker.Staker var messagePruner *MessagePruner - parentChainID, err := l1client.ChainID(ctx) - if err != nil { - return nil, fmt.Errorf("getting parent chain id: %w", err) - } - if config.Staker.Enable { dp, err := StakerDataposter( ctx, @@ -709,8 +705,9 @@ func CreateNode( txOptsBatchPoster *bind.TransactOpts, dataSigner signature.DataSignerFunc, fatalErrChan chan error, + parentChainID *big.Int, ) (*Node, error) { - currentNode, err := createNodeImpl(ctx, stack, exec, arbDb, configFetcher, l2Config, l1client, deployInfo, txOptsValidator, txOptsBatchPoster, dataSigner, fatalErrChan) + currentNode, err := createNodeImpl(ctx, stack, exec, arbDb, configFetcher, l2Config, l1client, deployInfo, txOptsValidator, txOptsBatchPoster, dataSigner, fatalErrChan, parentChainID) if err != nil { return nil, err } diff --git a/cmd/nitro/nitro.go b/cmd/nitro/nitro.go index 55c8d7704a..45f539488d 100644 --- a/cmd/nitro/nitro.go +++ b/cmd/nitro/nitro.go @@ -536,6 +536,7 @@ func mainImpl() int { l1TransactionOptsBatchPoster, dataSigner, fatalErrChan, + big.NewInt(int64(nodeConfig.ParentChain.ID)), ) if err != nil { log.Error("failed to create node", "err", err) diff --git a/go-ethereum b/go-ethereum index 1e2855b24d..b1622e6ac4 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 1e2855b24d6555c8cfaf471bd9e2c3d19ab5c32c +Subproject commit b1622e6ac4bf3762aebde92a585de2889d90823f diff --git a/system_tests/common_test.go b/system_tests/common_test.go index 937b8980fc..2e17a50ede 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -782,10 +782,9 @@ func createTestNodeWithL1( execConfigFetcher := func() *gethexec.Config { return execConfig } execNode, err := gethexec.CreateExecutionNode(ctx, l2stack, l2chainDb, l2blockchain, l1client, execConfigFetcher) Require(t, err) - currentNode, err = arbnode.CreateNode( ctx, l2stack, execNode, l2arbDb, NewFetcherFromConfig(nodeConfig), l2blockchain.Config(), l1client, - addresses, sequencerTxOptsPtr, sequencerTxOptsPtr, dataSigner, fatalErrChan, + addresses, sequencerTxOptsPtr, sequencerTxOptsPtr, dataSigner, fatalErrChan, big.NewInt(1337), ) Require(t, err) @@ -821,7 +820,7 @@ func createTestNode( execNode, err := gethexec.CreateExecutionNode(ctx, stack, chainDb, blockchain, nil, execConfigFetcher) Require(t, err) - currentNode, err := arbnode.CreateNode(ctx, stack, execNode, arbDb, NewFetcherFromConfig(nodeConfig), blockchain.Config(), nil, nil, nil, nil, nil, feedErrChan) + currentNode, err := arbnode.CreateNode(ctx, stack, execNode, arbDb, NewFetcherFromConfig(nodeConfig), blockchain.Config(), nil, nil, nil, nil, nil, feedErrChan, big.NewInt(1337)) Require(t, err) // Give the node an init message @@ -925,7 +924,7 @@ func Create2ndNodeWithConfig( currentExec, err := gethexec.CreateExecutionNode(ctx, l2stack, l2chainDb, l2blockchain, l1client, configFetcher) Require(t, err) - currentNode, err := arbnode.CreateNode(ctx, l2stack, currentExec, l2arbDb, NewFetcherFromConfig(nodeConfig), l2blockchain.Config(), l1client, first.DeployInfo, &txOpts, &txOpts, dataSigner, feedErrChan) + currentNode, err := arbnode.CreateNode(ctx, l2stack, currentExec, l2arbDb, NewFetcherFromConfig(nodeConfig), l2blockchain.Config(), l1client, first.DeployInfo, &txOpts, &txOpts, dataSigner, feedErrChan, big.NewInt(13)) Require(t, err) err = currentNode.Start(ctx) diff --git a/system_tests/das_test.go b/system_tests/das_test.go index 6db339521c..96de52e197 100644 --- a/system_tests/das_test.go +++ b/system_tests/das_test.go @@ -123,7 +123,7 @@ func TestDASRekey(t *testing.T) { l1NodeConfigB := arbnode.ConfigDefaultL1NonSequencerTest() sequencerTxOpts := l1info.GetDefaultTransactOpts("Sequencer", ctx) sequencerTxOptsPtr := &sequencerTxOpts - + parentChainID := big.NewInt(1337) { authorizeDASKeyset(t, ctx, pubkeyA, l1info, l1client) @@ -141,8 +141,7 @@ func TestDASRekey(t *testing.T) { l1NodeConfigA.DataAvailability.ParentChainNodeURL = "none" execA, err := gethexec.CreateExecutionNode(ctx, l2stackA, l2chainDb, l2blockchain, l1client, gethexec.ConfigDefaultTest) Require(t, err) - - nodeA, err := arbnode.CreateNode(ctx, l2stackA, execA, l2arbDb, NewFetcherFromConfig(l1NodeConfigA), l2blockchain.Config(), l1client, addresses, sequencerTxOptsPtr, sequencerTxOptsPtr, nil, feedErrChan) + nodeA, err := arbnode.CreateNode(ctx, l2stackA, execA, l2arbDb, NewFetcherFromConfig(l1NodeConfigA), l2blockchain.Config(), l1client, addresses, sequencerTxOptsPtr, sequencerTxOptsPtr, nil, feedErrChan, parentChainID) Require(t, err) Require(t, nodeA.Start(ctx)) l2clientA := ClientForStack(t, l2stackA) @@ -189,7 +188,7 @@ func TestDASRekey(t *testing.T) { Require(t, err) l1NodeConfigA.DataAvailability.RPCAggregator = aggConfigForBackend(t, backendConfigB) - nodeA, err := arbnode.CreateNode(ctx, l2stackA, execA, l2arbDb, NewFetcherFromConfig(l1NodeConfigA), l2blockchain.Config(), l1client, addresses, sequencerTxOptsPtr, sequencerTxOptsPtr, nil, feedErrChan) + nodeA, err := arbnode.CreateNode(ctx, l2stackA, execA, l2arbDb, NewFetcherFromConfig(l1NodeConfigA), l2blockchain.Config(), l1client, addresses, sequencerTxOptsPtr, sequencerTxOptsPtr, nil, feedErrChan, parentChainID) Require(t, err) Require(t, nodeA.Start(ctx)) l2clientA := ClientForStack(t, l2stackA) @@ -322,7 +321,7 @@ func TestDASComplexConfigAndRestMirror(t *testing.T) { sequencerTxOpts := l1info.GetDefaultTransactOpts("Sequencer", ctx) sequencerTxOptsPtr := &sequencerTxOpts - nodeA, err := arbnode.CreateNode(ctx, l2stackA, execA, l2arbDb, NewFetcherFromConfig(l1NodeConfigA), l2blockchain.Config(), l1client, addresses, sequencerTxOptsPtr, sequencerTxOptsPtr, dataSigner, feedErrChan) + nodeA, err := arbnode.CreateNode(ctx, l2stackA, execA, l2arbDb, NewFetcherFromConfig(l1NodeConfigA), l2blockchain.Config(), l1client, addresses, sequencerTxOptsPtr, sequencerTxOptsPtr, dataSigner, feedErrChan, big.NewInt(1337)) Require(t, err) Require(t, nodeA.Start(ctx)) l2clientA := ClientForStack(t, l2stackA) diff --git a/system_tests/full_challenge_impl_test.go b/system_tests/full_challenge_impl_test.go index 43ba6a056c..118d17ec81 100644 --- a/system_tests/full_challenge_impl_test.go +++ b/system_tests/full_challenge_impl_test.go @@ -278,7 +278,8 @@ func RunChallengeTest(t *testing.T, asserterIsCorrect bool, useStubs bool, chall asserterRollupAddresses.SequencerInbox = asserterSeqInboxAddr asserterExec, err := gethexec.CreateExecutionNode(ctx, asserterL2Stack, asserterL2ChainDb, asserterL2Blockchain, l1Backend, gethexec.ConfigDefaultTest) Require(t, err) - asserterL2, err := arbnode.CreateNode(ctx, asserterL2Stack, asserterExec, asserterL2ArbDb, NewFetcherFromConfig(conf), chainConfig, l1Backend, asserterRollupAddresses, nil, nil, nil, fatalErrChan) + parentChainID := big.NewInt(1337) + asserterL2, err := arbnode.CreateNode(ctx, asserterL2Stack, asserterExec, asserterL2ArbDb, NewFetcherFromConfig(conf), chainConfig, l1Backend, asserterRollupAddresses, nil, nil, nil, fatalErrChan, parentChainID) Require(t, err) err = asserterL2.Start(ctx) Require(t, err) @@ -289,7 +290,7 @@ func RunChallengeTest(t *testing.T, asserterIsCorrect bool, useStubs bool, chall challengerRollupAddresses.SequencerInbox = challengerSeqInboxAddr challengerExec, err := gethexec.CreateExecutionNode(ctx, challengerL2Stack, challengerL2ChainDb, challengerL2Blockchain, l1Backend, gethexec.ConfigDefaultTest) Require(t, err) - challengerL2, err := arbnode.CreateNode(ctx, challengerL2Stack, challengerExec, challengerL2ArbDb, NewFetcherFromConfig(conf), chainConfig, l1Backend, &challengerRollupAddresses, nil, nil, nil, fatalErrChan) + challengerL2, err := arbnode.CreateNode(ctx, challengerL2Stack, challengerExec, challengerL2ArbDb, NewFetcherFromConfig(conf), chainConfig, l1Backend, &challengerRollupAddresses, nil, nil, nil, fatalErrChan, parentChainID) Require(t, err) err = challengerL2.Start(ctx) Require(t, err) diff --git a/system_tests/recreatestate_rpc_test.go b/system_tests/recreatestate_rpc_test.go index 9429155d7c..f5bdca0970 100644 --- a/system_tests/recreatestate_rpc_test.go +++ b/system_tests/recreatestate_rpc_test.go @@ -334,7 +334,8 @@ func testSkippingSavingStateAndRecreatingAfterRestart(t *testing.T, cacheConfig execNode, err := gethexec.CreateExecutionNode(ctx1, stack, chainDb, blockchain, nil, execConfigFetcher) Require(t, err) - node, err := arbnode.CreateNode(ctx1, stack, execNode, arbDb, NewFetcherFromConfig(arbnode.ConfigDefaultL2Test()), blockchain.Config(), nil, nil, nil, nil, nil, feedErrChan) + parentChainID := big.NewInt(1337) + node, err := arbnode.CreateNode(ctx1, stack, execNode, arbDb, NewFetcherFromConfig(arbnode.ConfigDefaultL2Test()), blockchain.Config(), nil, nil, nil, nil, nil, feedErrChan, parentChainID) Require(t, err) err = node.TxStreamer.AddFakeInitMessage() Require(t, err) @@ -375,7 +376,7 @@ func testSkippingSavingStateAndRecreatingAfterRestart(t *testing.T, cacheConfig execNode, err = gethexec.CreateExecutionNode(ctx1, stack, chainDb, blockchain, nil, execConfigFetcher) Require(t, err) - node, err = arbnode.CreateNode(ctx, stack, execNode, arbDb, NewFetcherFromConfig(arbnode.ConfigDefaultL2Test()), blockchain.Config(), nil, node.DeployInfo, nil, nil, nil, feedErrChan) + node, err = arbnode.CreateNode(ctx, stack, execNode, arbDb, NewFetcherFromConfig(arbnode.ConfigDefaultL2Test()), blockchain.Config(), nil, node.DeployInfo, nil, nil, nil, feedErrChan, parentChainID) Require(t, err) Require(t, node.Start(ctx)) client = ClientForStack(t, stack) From bc3186f9e6a820ec906d74941248ecb1869cff19 Mon Sep 17 00:00:00 2001 From: Nodar Ambroladze Date: Thu, 11 Jan 2024 09:51:49 +0100 Subject: [PATCH 57/68] Update go-ethereum pin --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index b1622e6ac4..1e2855b24d 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit b1622e6ac4bf3762aebde92a585de2889d90823f +Subproject commit 1e2855b24d6555c8cfaf471bd9e2c3d19ab5c32c From bbb8cf7c88151c8e9d0eca03e1179629370d8b7e Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Thu, 11 Jan 2024 14:39:15 +0530 Subject: [PATCH 58/68] optimize impl and address PR comments --- broadcaster/backlog/backlog.go | 27 +++++++++++++++++++++------ broadcaster/message/message.go | 6 +++++- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/broadcaster/backlog/backlog.go b/broadcaster/backlog/backlog.go index d2f7c66be7..366092999a 100644 --- a/broadcaster/backlog/backlog.go +++ b/broadcaster/backlog/backlog.go @@ -56,24 +56,30 @@ func (b *backlog) Head() BacklogSegment { return b.head.Load() } -func (b *backlog) backlogSizeInBytes() uint64 { +func (b *backlog) backlogSizeInBytes() (uint64, error) { if b.head.Load() == nil || b.tail.Load() == nil { - return 0 + return 0, errors.New("the head or tail segment of feed backlog is nil") } headSeg := b.head.Load() headSeg.messagesLock.RLock() + if len(headSeg.messages) == 0 { + return 0, errors.New("head segment of the feed backlog is empty") + } headMsg := headSeg.messages[0] headSeg.messagesLock.RUnlock() tailSeg := b.tail.Load() tailSeg.messagesLock.RLock() + if len(tailSeg.messages) == 0 { + return 0, errors.New("tail segment of the feed backlog is empty") + } tailMsg := tailSeg.messages[len(tailSeg.messages)-1] size := tailMsg.CumulativeSumMsgSize tailSeg.messagesLock.RUnlock() size -= headMsg.CumulativeSumMsgSize - size += uint64(len(headMsg.Signature) + len(headMsg.Message.Message.L2msg) + 160) - return size + size += headMsg.Size() + return size, nil } // Append will add the given messages to the backlogSegment at head until @@ -83,6 +89,12 @@ func (b *backlog) Append(bm *m.BroadcastMessage) error { if bm.ConfirmedSequenceNumberMessage != nil { b.delete(uint64(bm.ConfirmedSequenceNumberMessage.SequenceNumber)) + size, err := b.backlogSizeInBytes() + if err != nil { + log.Warn("error calculating backlogSizeInBytes", "err", err) + } else { + backlogSizeInBytesGauge.Update(int64(size)) + } } lookupByIndex := b.lookupByIndex.Load() @@ -98,7 +110,9 @@ func (b *backlog) Append(bm *m.BroadcastMessage) error { prevMsgIdx := segment.End() if segment.count() >= b.config().SegmentLimit { segment.messagesLock.RLock() - msg.CumulativeSumMsgSize = segment.messages[len(segment.messages)-1].CumulativeSumMsgSize + if len(segment.messages) > 0 { + msg.CumulativeSumMsgSize = segment.messages[len(segment.messages)-1].CumulativeSumMsgSize + } segment.messagesLock.RUnlock() nextSegment := newBacklogSegment() @@ -115,6 +129,7 @@ func (b *backlog) Append(bm *m.BroadcastMessage) error { b.head.Store(segment) b.tail.Store(segment) b.messageCount.Store(0) + backlogSizeInBytesGauge.Update(0) log.Warn(err.Error()) } else if errors.Is(err, errSequenceNumberSeen) { log.Info("ignoring message sequence number, already in backlog", "message sequence number", msg.SequenceNumber) @@ -124,9 +139,9 @@ func (b *backlog) Append(bm *m.BroadcastMessage) error { } lookupByIndex.Store(uint64(msg.SequenceNumber), segment) b.messageCount.Add(1) + backlogSizeInBytesGauge.Inc(int64(msg.Size())) } - backlogSizeInBytesGauge.Update(int64(b.backlogSizeInBytes())) backlogSizeGauge.Update(int64(b.Count())) return nil } diff --git a/broadcaster/message/message.go b/broadcaster/message/message.go index 076eb96420..a575ae5cd0 100644 --- a/broadcaster/message/message.go +++ b/broadcaster/message/message.go @@ -39,8 +39,12 @@ type BroadcastFeedMessage struct { CumulativeSumMsgSize uint64 `json:"-"` } +func (m *BroadcastFeedMessage) Size() uint64 { + return uint64(len(m.Signature) + len(m.Message.Message.L2msg) + 160) +} + func (m *BroadcastFeedMessage) UpdateCumulativeSumMsgSize(val uint64) { - m.CumulativeSumMsgSize += val + uint64(len(m.Signature)+len(m.Message.Message.L2msg)+160) + m.CumulativeSumMsgSize += val + m.Size() } func (m *BroadcastFeedMessage) Hash(chainId uint64) (common.Hash, error) { From 9dd855619bbf393baa06cdef322248cb35a1a3ec Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Thu, 11 Jan 2024 16:01:51 +0530 Subject: [PATCH 59/68] address PR comments --- cmd/seq-coordinator-manager/seq-coordinator-manager.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/seq-coordinator-manager/seq-coordinator-manager.go b/cmd/seq-coordinator-manager/seq-coordinator-manager.go index daafe1e5e1..43d90441ef 100644 --- a/cmd/seq-coordinator-manager/seq-coordinator-manager.go +++ b/cmd/seq-coordinator-manager/seq-coordinator-manager.go @@ -164,6 +164,7 @@ func main() { flex.SetDirection(tview.FlexRow). AddItem(priorityHeading, 0, 1, false). AddItem(tview.NewFlex(). + // fixedSize is maxURLSize plus 20 characters to accomodate ellipsis, statuses and emojis AddItem(prioritySeqList, seqManager.maxURLSize+20, 0, true). AddItem(priorityForm, 0, 3, true), 0, 12, true). AddItem(nonPriorityHeading, 0, 1, false). From d6e4f734982a59b76bd2c58a6214e6387432a5d4 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Thu, 11 Jan 2024 23:00:57 +0530 Subject: [PATCH 60/68] address PR comments --- broadcaster/backlog/backlog.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/broadcaster/backlog/backlog.go b/broadcaster/backlog/backlog.go index 366092999a..67df9e6bbd 100644 --- a/broadcaster/backlog/backlog.go +++ b/broadcaster/backlog/backlog.go @@ -57,10 +57,15 @@ func (b *backlog) Head() BacklogSegment { } func (b *backlog) backlogSizeInBytes() (uint64, error) { - if b.head.Load() == nil || b.tail.Load() == nil { + headSeg := b.head.Load() + tailSeg := b.tail.Load() + if headSeg == nil || tailSeg == nil { + if headSeg == tailSeg { + return 0, nil + } return 0, errors.New("the head or tail segment of feed backlog is nil") } - headSeg := b.head.Load() + headSeg.messagesLock.RLock() if len(headSeg.messages) == 0 { return 0, errors.New("head segment of the feed backlog is empty") @@ -68,7 +73,6 @@ func (b *backlog) backlogSizeInBytes() (uint64, error) { headMsg := headSeg.messages[0] headSeg.messagesLock.RUnlock() - tailSeg := b.tail.Load() tailSeg.messagesLock.RLock() if len(tailSeg.messages) == 0 { return 0, errors.New("tail segment of the feed backlog is empty") From 1e743ca226a46917b7c5fbe12238939f45b080d6 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Thu, 11 Jan 2024 23:29:14 +0530 Subject: [PATCH 61/68] fix failing custom lint --- broadcaster/backlog/backlog.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/broadcaster/backlog/backlog.go b/broadcaster/backlog/backlog.go index 67df9e6bbd..f6501105c2 100644 --- a/broadcaster/backlog/backlog.go +++ b/broadcaster/backlog/backlog.go @@ -60,7 +60,7 @@ func (b *backlog) backlogSizeInBytes() (uint64, error) { headSeg := b.head.Load() tailSeg := b.tail.Load() if headSeg == nil || tailSeg == nil { - if headSeg == tailSeg { + if headSeg == nil && tailSeg == nil { return 0, nil } return 0, errors.New("the head or tail segment of feed backlog is nil") From 199fa1ff17ea0b2e19e4e0d98ae5c6ea7f8ab7cb Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 12 Jan 2024 17:47:36 -0600 Subject: [PATCH 62/68] Add option to disable batch poster access lists --- arbnode/batch_poster.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 4bd0e2490a..a75e7b524c 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -122,6 +122,7 @@ type BatchPosterConfig struct { ParentChainWallet genericconf.WalletConfig `koanf:"parent-chain-wallet"` L1BlockBound string `koanf:"l1-block-bound" reload:"hot"` L1BlockBoundBypass time.Duration `koanf:"l1-block-bound-bypass" reload:"hot"` + UseAccessLists bool `koanf:"use-access-lists" reload:"hot"` gasRefunder common.Address l1BlockBound l1BlockBound @@ -168,6 +169,7 @@ func BatchPosterConfigAddOptions(prefix string, f *pflag.FlagSet) { f.String(prefix+".redis-url", DefaultBatchPosterConfig.RedisUrl, "if non-empty, the Redis URL to store queued transactions in") f.String(prefix+".l1-block-bound", DefaultBatchPosterConfig.L1BlockBound, "only post messages to batches when they're within the max future block/timestamp as of this L1 block tag (\"safe\", \"finalized\", \"latest\", or \"ignore\" to ignore this check)") f.Duration(prefix+".l1-block-bound-bypass", DefaultBatchPosterConfig.L1BlockBoundBypass, "post batches even if not within the layer 1 future bounds if we're within this margin of the max delay") + f.Bool(prefix+".use-access-lists", DefaultBatchPosterConfig.UseAccessLists, "post batches with access lists to reduce gas usage (disabled for L3s)") redislock.AddConfigOptions(prefix+".redis-lock", f) dataposter.DataPosterConfigAddOptions(prefix+".data-poster", f, dataposter.DefaultDataPosterConfig) genericconf.WalletConfigAddOptions(prefix+".parent-chain-wallet", f, DefaultBatchPosterConfig.ParentChainWallet.Pathname) @@ -190,6 +192,7 @@ var DefaultBatchPosterConfig = BatchPosterConfig{ ParentChainWallet: DefaultBatchPosterL1WalletConfig, L1BlockBound: "", L1BlockBoundBypass: time.Hour, + UseAccessLists: true, RedisLock: redislock.DefaultCfg, } @@ -216,6 +219,7 @@ var TestBatchPosterConfig = BatchPosterConfig{ ParentChainWallet: DefaultBatchPosterL1WalletConfig, L1BlockBound: "", L1BlockBoundBypass: time.Hour, + UseAccessLists: true, } type BatchPosterOpts struct { @@ -298,7 +302,7 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e // Dataposter sender may be external signer address, so we should initialize // access list after initializing dataposter. b.accessList = func(SequencerInboxAccs, AfterDelayedMessagesRead int) types.AccessList { - if opts.L1Reader.IsParentChainArbitrum() { + if !b.config().UseAccessLists || opts.L1Reader.IsParentChainArbitrum() { // Access lists cost gas instead of saving gas when posting to L2s, // because data is expensive in comparison to computation. return nil From 44319b73ee718288d08a4408f018a486a56db3cf Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 16 Jan 2024 12:02:23 -0700 Subject: [PATCH 63/68] Add a check that the database chain id matches the config --- cmd/nitro/init.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index ada195b5c4..4cf5dcda06 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -37,6 +37,7 @@ import ( "github.com/offchainlabs/nitro/cmd/util" "github.com/offchainlabs/nitro/execution/gethexec" "github.com/offchainlabs/nitro/statetransfer" + "github.com/offchainlabs/nitro/util/arbmath" ) func downloadInit(ctx context.Context, initConfig *conf.InitConfig) (string, error) { @@ -163,6 +164,9 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo if readOnlyDb, err := stack.OpenDatabaseWithFreezer("l2chaindata", 0, 0, "", "", true); err == nil { if chainConfig := gethexec.TryReadStoredChainConfig(readOnlyDb); chainConfig != nil { readOnlyDb.Close() + if !arbmath.BigEquals(chainConfig.ChainID, chainId) { + return nil, nil, fmt.Errorf("database has chain ID %v but config has chain ID %v (are you sure this database is for the right chain?)", chainConfig.ChainID, chainId) + } chainDb, err := stack.OpenDatabaseWithFreezer("l2chaindata", config.Execution.Caching.DatabaseCache, config.Persistent.Handles, config.Persistent.Ancient, "", false) if err != nil { return chainDb, nil, err From 47c6024b32bd1a51b44307f4fbd47a638343ff33 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Thu, 18 Jan 2024 14:27:59 -0500 Subject: [PATCH 64/68] Update readme and license for AEP --- LICENSE => LICENSE.md | 25 +++++++++++++------------ README.md | 6 +++++- 2 files changed, 18 insertions(+), 13 deletions(-) rename LICENSE => LICENSE.md (80%) diff --git a/LICENSE b/LICENSE.md similarity index 80% rename from LICENSE rename to LICENSE.md index c96c4d6cb2..0f679ff243 100644 --- a/LICENSE +++ b/LICENSE.md @@ -16,18 +16,19 @@ Additional Use Grant: You may use the Licensed Work in a production environment to provide a point of interface to permit end users or applications utilizing the Covered Arbitrum Chains to interact and query the state of a Covered Arbitrum Chain, including without limitation - validating the correctness of the posted chain state. For purposes - of this Additional Use Grant, the "Covered Arbitrum Chains" are - means (a) Arbitrum One (chainid:42161), Arbitrum Nova (chainid:42170), - Arbitrum Rinkeby testnet/Rinkarby (chainid:421611), and - Arbitrum Nitro Goerli testnet (chainid:421613) (b) any future - blockchains authorized to be designated as Covered Arbitrum Chains - by the decentralized autonomous organization governing the Arbitrum - network; and (c) any “Layer 3” Arbitrum-based blockchain that is built - on and settles to another Covered Arbitrum Chain. - - - + validating the correctness of the posted chain state, or to deploy + and operate (x) a blockchain that settles to a Covered Arbitrum Chain + or (y) a blockchain in accordance with, and subject to, the [Arbitrum + Expansion Program Term of Use](https://docs.arbitrum.foundation/assets/files/Arbitrum%20Expansion%20Program%20Jan182024.pdf). For purposes of this + Additional Use Grant, the "Covered Arbitrum Chains" are means + (a) Arbitrum One (chainid:42161), Arbitrum Nova (chainid:42170), + rbitrum Rinkeby testnet/Rinkarby (chainid:421611),Arbitrum Nitro + Goerli testnet (chainid:421613), and Arbitrum Sepolia Testnet + (chainid:421614); (b) any future blockchains authorized to be + designated as Covered Arbitrum Chains by the decentralized autonomous + organization governing the Arbitrum network; and (c) any “Layer 3” + Arbitrum-based blockchain that is built on and settles to another + Covered Arbitrum Chain. Change Date: Dec 31, 2028 diff --git a/README.md b/README.md index 67a182ec30..a02d55456d 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,11 @@ Arbitrum One successfully migrated from the Classic Arbitrum stack onto Nitro on ## License -We currently have Nitro [licensed](./LICENSE) under a Business Source License, similar to our friends at Uniswap and Aave, with an "Additional Use Grant" to ensure that everyone can have full comfort using and running nodes on all public Arbitrum chains. +Nitro is currently licensed under a [Business Source License](./LICENSE), similar to our friends at Uniswap and Aave, with an "Additional Use Grant" to ensure that everyone can have full comfort using and running nodes on all public Arbitrum chains. + +The Additional Use Grant also permits the deployment of the Nitro software, in a permissionless fashion and without cost, as a new blockchain provided that the chain settles to either Arbitrum One or Arbitrum Nova. + +For those that prefer to deploy the Nitro software either directly on Ethereum (i.e. an L2) or have it settle to another Layer-2 on top of Ethereum, the [Arbitrum Expansion Program (the "AEP")](https://docs.arbitrum.foundation/assets/files/Arbitrum%20Expansion%20Program%20Jan182024.pdf) was recently established. The AEP allows for the permissionless deployment in the aforementioned fashion provided that 10% of net revenue (as more fully described in the AEP) is contributed back to the Arbitrum community in accordance with the requirements of the AEP. ## Contact From f1e34a7d6963d229fd939dfee67f41f3550ba02d Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Thu, 18 Jan 2024 14:45:11 -0500 Subject: [PATCH 65/68] Fix typo in BSL --- LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.md b/LICENSE.md index 0f679ff243..c03439a3b9 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -20,7 +20,7 @@ Additional Use Grant: You may use the Licensed Work in a production environment and operate (x) a blockchain that settles to a Covered Arbitrum Chain or (y) a blockchain in accordance with, and subject to, the [Arbitrum Expansion Program Term of Use](https://docs.arbitrum.foundation/assets/files/Arbitrum%20Expansion%20Program%20Jan182024.pdf). For purposes of this - Additional Use Grant, the "Covered Arbitrum Chains" are means + Additional Use Grant, the "Covered Arbitrum Chains" are (a) Arbitrum One (chainid:42161), Arbitrum Nova (chainid:42170), rbitrum Rinkeby testnet/Rinkarby (chainid:421611),Arbitrum Nitro Goerli testnet (chainid:421613), and Arbitrum Sepolia Testnet From 4ad269370febf749294c3957966e4257f7dea294 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Thu, 18 Jan 2024 14:59:23 -0500 Subject: [PATCH 66/68] Fix AEP link --- LICENSE.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index c03439a3b9..ea9a53da75 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -19,7 +19,7 @@ Additional Use Grant: You may use the Licensed Work in a production environment validating the correctness of the posted chain state, or to deploy and operate (x) a blockchain that settles to a Covered Arbitrum Chain or (y) a blockchain in accordance with, and subject to, the [Arbitrum - Expansion Program Term of Use](https://docs.arbitrum.foundation/assets/files/Arbitrum%20Expansion%20Program%20Jan182024.pdf). For purposes of this + Expansion Program Term of Use](https://docs.arbitrum.foundation/assets/files/Arbitrum%20Expansion%20Program%20Jan182024-4f08b0c2cb476a55dc153380fa3e64b0.pdf). For purposes of this Additional Use Grant, the "Covered Arbitrum Chains" are (a) Arbitrum One (chainid:42161), Arbitrum Nova (chainid:42170), rbitrum Rinkeby testnet/Rinkarby (chainid:421611),Arbitrum Nitro diff --git a/README.md b/README.md index a02d55456d..4a522be82f 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Nitro is currently licensed under a [Business Source License](./LICENSE), simila The Additional Use Grant also permits the deployment of the Nitro software, in a permissionless fashion and without cost, as a new blockchain provided that the chain settles to either Arbitrum One or Arbitrum Nova. -For those that prefer to deploy the Nitro software either directly on Ethereum (i.e. an L2) or have it settle to another Layer-2 on top of Ethereum, the [Arbitrum Expansion Program (the "AEP")](https://docs.arbitrum.foundation/assets/files/Arbitrum%20Expansion%20Program%20Jan182024.pdf) was recently established. The AEP allows for the permissionless deployment in the aforementioned fashion provided that 10% of net revenue (as more fully described in the AEP) is contributed back to the Arbitrum community in accordance with the requirements of the AEP. +For those that prefer to deploy the Nitro software either directly on Ethereum (i.e. an L2) or have it settle to another Layer-2 on top of Ethereum, the [Arbitrum Expansion Program (the "AEP")](https://docs.arbitrum.foundation/assets/files/Arbitrum%20Expansion%20Program%20Jan182024-4f08b0c2cb476a55dc153380fa3e64b0.pdf) was recently established. The AEP allows for the permissionless deployment in the aforementioned fashion provided that 10% of net revenue (as more fully described in the AEP) is contributed back to the Arbitrum community in accordance with the requirements of the AEP. ## Contact From 9c2bd63444b80071a3fc77b053670149d0f9642f Mon Sep 17 00:00:00 2001 From: ImJeremyHe Date: Mon, 22 Jan 2024 12:48:44 +0800 Subject: [PATCH 67/68] Bump espresso-sequencer-go --- arbos/parse_l2_test.go | 16 +++++++++------- go.mod | 3 ++- go.sum | 6 ++++-- system_tests/espresso_test.go | 14 +++++++++----- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/arbos/parse_l2_test.go b/arbos/parse_l2_test.go index 59b2018078..5687e2d75e 100644 --- a/arbos/parse_l2_test.go +++ b/arbos/parse_l2_test.go @@ -19,12 +19,11 @@ func TestEspressoParsing(t *testing.T) { } expectJst := &arbostypes.EspressoBlockJustification{ Header: espressoTypes.Header{ - TransactionsRoot: espressoTypes.NmtRoot{Root: []byte{7, 8, 9}}, - L1Head: 1, - Timestamp: 2, - Height: 3, - L1Finalized: &espressoTypes.L1BlockInfo{}, - PayloadCommitment: espressoTypes.Bytes{1, 2, 3}, + TransactionsRoot: espressoTypes.NmtRoot{Root: []byte{7, 8, 9}}, + L1Head: 1, + Timestamp: 2, + Height: 3, + L1Finalized: &espressoTypes.L1BlockInfo{}, }, Proof: []byte{9}, } @@ -38,7 +37,10 @@ func TestEspressoParsing(t *testing.T) { Fail(t) } - if !reflect.DeepEqual(actualJst, expectJst) { + if !reflect.DeepEqual(actualJst.Proof, expectJst.Proof) { + Fail(t) + } + if !reflect.DeepEqual(actualJst.Header.TransactionsRoot, expectJst.Header.TransactionsRoot) { Fail(t) } diff --git a/go.mod b/go.mod index 9eaa2121e9..158fc86fc6 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ replace github.com/VictoriaMetrics/fastcache => ./fastcache replace github.com/ethereum/go-ethereum => ./go-ethereum require ( - github.com/EspressoSystems/espresso-sequencer-go v0.0.3 + github.com/EspressoSystems/espresso-sequencer-go v0.0.4 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible github.com/Shopify/toxiproxy v2.1.4+incompatible github.com/alicebob/miniredis/v2 v2.21.0 @@ -255,6 +255,7 @@ require ( github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/samber/lo v1.36.0 // indirect + github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/supranational/blst v0.3.11 // indirect github.com/urfave/cli/v2 v2.25.7 // indirect diff --git a/go.sum b/go.sum index e7cfdb4224..fc1a613b38 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,8 @@ github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3 github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/EspressoSystems/espresso-sequencer-go v0.0.3 h1:A227KQpb46Lfccz+b/OGEfA2+pjrHN8f6+XzxBVUVmw= -github.com/EspressoSystems/espresso-sequencer-go v0.0.3/go.mod h1:T3MyQRnfbCSTBhAAG2WASmVPCwWkl0/sPKKY5Z1ewtg= +github.com/EspressoSystems/espresso-sequencer-go v0.0.4 h1:/um6EBWciClEM2rBLpA9I8vIyok9D9rEGE+7zBVXR6g= +github.com/EspressoSystems/espresso-sequencer-go v0.0.4/go.mod h1:9dSL1bj0l+jpgaMRmi55YeRBd3AhOZz8/HXQcQ42mRQ= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= @@ -1552,6 +1552,8 @@ github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go. github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f h1:1R9KdKjCNSd7F8iGTxIpoID9prlYH8nuNYKt0XvweHA= +github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f/go.mod h1:vQhwQ4meQEDfahT5kd61wLAF5AAeh5ZPLVI4JJ/tYo8= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= diff --git a/system_tests/espresso_test.go b/system_tests/espresso_test.go index 44bd8f1b0a..174a791341 100644 --- a/system_tests/espresso_test.go +++ b/system_tests/espresso_test.go @@ -11,6 +11,7 @@ import ( "time" espressoClient "github.com/EspressoSystems/espresso-sequencer-go/client" + tagged_base64 "github.com/EspressoSystems/espresso-sequencer-go/tagged-base64" espressoTypes "github.com/EspressoSystems/espresso-sequencer-go/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" @@ -80,7 +81,7 @@ func createMockHotShot(ctx context.Context, t *testing.T, l2Info *BlockchainTest httpmock.RegisterResponder( "GET", - `=~http://127.0.0.1:50000/status/latest_block_height`, + `=~http://127.0.0.1:50000/status/block-height`, func(r *http.Request) (*http.Response, error) { return httpmock.NewStringResponse(200, strconv.Itoa(startHotShotBlock)), nil }, @@ -96,13 +97,16 @@ func createMockHotShot(ctx context.Context, t *testing.T, l2Info *BlockchainTest if block < uint64(staleBlocks) { timestamp = 0 } + pc, _ := tagged_base64.New("header", []byte{byte(block)}) header := espressoTypes.Header{ // Since we don't realize the validation of espresso yet, // mock a simple nmt root here - Height: block, - TransactionsRoot: espressoTypes.NmtRoot{Root: []byte{}}, - L1Head: 0, // Currently not used - Timestamp: timestamp, + Height: block, + TransactionsRoot: espressoTypes.NmtRoot{Root: []byte{}}, + L1Head: 0, // Currently not used + Timestamp: timestamp, + PayloadCommitment: pc, + BlockMerkleTreeRoot: pc, } return httpmock.NewJsonResponse(200, header) }) From 3b5c503a2bd3839f2cd0d6a0d75a335020086f05 Mon Sep 17 00:00:00 2001 From: ImJeremyHe Date: Tue, 23 Jan 2024 11:08:04 +0800 Subject: [PATCH 68/68] Fix the unparsing jst --- arbos/parse_l2.go | 13 +++++++++++-- arbos/parse_l2_test.go | 20 +++++++++++++------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/arbos/parse_l2.go b/arbos/parse_l2.go index 533d025857..aeafd1cc67 100644 --- a/arbos/parse_l2.go +++ b/arbos/parse_l2.go @@ -3,6 +3,7 @@ package arbos import ( "bytes" "encoding/binary" + "encoding/json" "errors" "fmt" "io" @@ -237,7 +238,11 @@ func parseEspressoMsg(rd io.Reader) ([]espressoTypes.Bytes, *arbostypes.Espresso } if jst == nil { j := new(arbostypes.EspressoBlockJustification) - if err := rlp.DecodeBytes(nextMsg, &j); err != nil { + s := []byte{} + if err := rlp.DecodeBytes(nextMsg, &s); err != nil { + return nil, nil, err + } + if err := json.Unmarshal(s, j); err != nil { return nil, nil, err } jst = j @@ -469,7 +474,11 @@ func MessageFromEspresso(header *arbostypes.L1IncomingMessageHeader, txes []espr var l2Message []byte l2Message = append(l2Message, L2MessageKind_EspressoTx) - jstBin, err := rlp.EncodeToBytes(jst) + jstJson, err := json.Marshal(jst) + if err != nil { + return arbostypes.L1IncomingMessage{}, err + } + jstBin, err := rlp.EncodeToBytes(jstJson) if err != nil { return arbostypes.L1IncomingMessage{}, err } diff --git a/arbos/parse_l2_test.go b/arbos/parse_l2_test.go index 5687e2d75e..9800119b2e 100644 --- a/arbos/parse_l2_test.go +++ b/arbos/parse_l2_test.go @@ -4,6 +4,7 @@ import ( "reflect" "testing" + tagged_base64 "github.com/EspressoSystems/espresso-sequencer-go/tagged-base64" espressoTypes "github.com/EspressoSystems/espresso-sequencer-go/types" "github.com/offchainlabs/nitro/arbos/arbostypes" ) @@ -17,13 +18,19 @@ func TestEspressoParsing(t *testing.T) { Kind: arbostypes.L1MessageType_L2Message, BlockNumber: 1, } + payloadCommitment, err := tagged_base64.New("payloadCommitment", []byte{1, 2, 3}) + Require(t, err) + root, err := tagged_base64.New("root", []byte{4, 5, 6}) + Require(t, err) expectJst := &arbostypes.EspressoBlockJustification{ Header: espressoTypes.Header{ - TransactionsRoot: espressoTypes.NmtRoot{Root: []byte{7, 8, 9}}, - L1Head: 1, - Timestamp: 2, - Height: 3, - L1Finalized: &espressoTypes.L1BlockInfo{}, + TransactionsRoot: espressoTypes.NmtRoot{Root: []byte{7, 8, 9}}, + L1Head: 1, + Timestamp: 2, + Height: 3, + L1Finalized: &espressoTypes.L1BlockInfo{}, + PayloadCommitment: payloadCommitment, + BlockMerkleTreeRoot: root, }, Proof: []byte{9}, } @@ -40,8 +47,7 @@ func TestEspressoParsing(t *testing.T) { if !reflect.DeepEqual(actualJst.Proof, expectJst.Proof) { Fail(t) } - if !reflect.DeepEqual(actualJst.Header.TransactionsRoot, expectJst.Header.TransactionsRoot) { + if !reflect.DeepEqual(actualJst.Header, expectJst.Header) { Fail(t) } - }