diff --git a/cmd/serve.go b/cmd/serve.go index 78cbbb4..6a379ed 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -22,6 +22,8 @@ const ( func CommandServe(cfg *config.Config) *cli.Command { l1RpcFallback := &cli.StringSlice{} l1WalletAddresses := &cli.StringSlice{} + l2FlashblocksPrivateStreams := &cli.StringSlice{} + l2FlashblocksPublicStreams := &cli.StringSlice{} l2RpcFallback := &cli.StringSlice{} l2WalletAddresses := &cli.StringSlice{} @@ -195,6 +197,39 @@ func CommandServe(cfg *config.Config) *cli.Command { Value: "incrementFlashblockNumber()", }, + &cli.Int64Flag{ + Category: strings.ToUpper(categoryL2), + Destination: &cfg.L2.MonitorFlashblocksMaxWsMessageSizeKb, + EnvVars: []string{envPrefix + strings.ToUpper(categoryL2) + "_MONITOR_FLASHBLOCKS_MAX_WS_MESSAGE_SIZE_KB"}, + Name: categoryL2 + "-monitor-flashblocks-max-ws-message-size-kb", + Usage: "max size (in kb) of l2 builder flashblocks ws messages", + Value: 256, + }, + + &cli.StringFlag{ // --l2-monitor-flashblocks-main-public-stream-name + Category: strings.ToUpper(categoryL2), + Destination: &cfg.L2.MonitorFlashblocksMainPublicStreamName, + EnvVars: []string{envPrefix + strings.ToUpper(categoryL2) + "_MONITOR_FLASHBLOCKS_MAIN_PUBLIC_STREAM"}, + Name: categoryL2 + "-monitor-flashblocks-main-public-stream", + Usage: "the name of the main public l2 flashblocks stream", + }, + + &cli.StringSliceFlag{ // --l2-monitor-flashblocks-private-stream + Category: strings.ToUpper(categoryL2), + Destination: l2FlashblocksPrivateStreams, + EnvVars: []string{envPrefix + strings.ToUpper(categoryL2) + "_MONITOR_FLASHBLOCKS_PRIVATE_STREAMS"}, + Name: categoryL2 + "-monitor-flashblocks-private-stream", + Usage: "private websocket stream(s) of l2 flashblocks", + }, + + &cli.StringSliceFlag{ // --l2-monitor-flashblocks-public-stream + Category: strings.ToUpper(categoryL2), + Destination: l2FlashblocksPublicStreams, + EnvVars: []string{envPrefix + strings.ToUpper(categoryL2) + "_MONITOR_FLASHBLOCKS_PUBLIC_STREAMS"}, + Name: categoryL2 + "-monitor-flashblocks-public-stream", + Usage: "public websocket stream(s) of l2 flashblocks", + }, + &cli.BoolFlag{ // --l2-monitor-tx-receipts Category: strings.ToUpper(categoryL2), Destination: &cfg.L2.MonitorTxReceipts, @@ -354,17 +389,47 @@ func CommandServe(cfg *config.Config) *cli.Command { { cfg.L2.RpcFallback = l2RpcFallback.Value() - _walletAddresses := make(map[string]string, len(l2WalletAddresses.Value())) - for _, wa := range l2WalletAddresses.Value() { - parts := strings.Split(wa, "=") - if len(parts) != 2 { - return fmt.Errorf("invalid wallet address (mush be like `name=0xNNNN`): %s", wa) + { + _flashblocksPrivateStreams := make(map[string]string, len(l2FlashblocksPrivateStreams.Value())) + for _, wa := range l2FlashblocksPrivateStreams.Value() { + parts := strings.Split(wa, "=") + if len(parts) != 2 { + return fmt.Errorf("invalid private flashblocks stream (must be like `name=ws://f.q.d.n:1111`): %s", wa) + } + name := strings.TrimSpace(parts[0]) + url := strings.TrimSpace(parts[1]) + _flashblocksPrivateStreams[name] = url } - name := strings.TrimSpace(parts[0]) - addr := strings.TrimSpace(parts[1]) - _walletAddresses[name] = addr + cfg.L2.MonitorFlashblocksPrivateStreams = _flashblocksPrivateStreams + } + + { + _flashblocksPublicStreams := make(map[string]string, len(l2FlashblocksPublicStreams.Value())) + for _, wa := range l2FlashblocksPublicStreams.Value() { + parts := strings.Split(wa, "=") + if len(parts) != 2 { + return fmt.Errorf("invalid public flashblocks stream (must be like `name=ws://f.q.d.n:1111`): %s", wa) + } + name := strings.TrimSpace(parts[0]) + url := strings.TrimSpace(parts[1]) + _flashblocksPublicStreams[name] = url + } + cfg.L2.MonitorFlashblocksPublicStreams = _flashblocksPublicStreams + } + + { + _walletAddresses := make(map[string]string, len(l2WalletAddresses.Value())) + for _, wa := range l2WalletAddresses.Value() { + parts := strings.Split(wa, "=") + if len(parts) != 2 { + return fmt.Errorf("invalid wallet address (mush be like `name=0xNNNN`): %s", wa) + } + name := strings.TrimSpace(parts[0]) + addr := strings.TrimSpace(parts[1]) + _walletAddresses[name] = addr + } + cfg.L2.MonitorWalletAddresses = _walletAddresses } - cfg.L2.MonitorWalletAddresses = _walletAddresses } return cfg.Validate() diff --git a/config/l2.go b/config/l2.go index 178a31a..0bb5451 100644 --- a/config/l2.go +++ b/config/l2.go @@ -29,6 +29,10 @@ type L2 struct { MonitorBuilderPolicyContractFunctionSignature string `yaml:"monitor_builder_policy_contract_function_signature"` MonitorFlashblockNumberContract string `yaml:"monitor_builder_flashblock_number_contract"` MonitorFlashblockNumberContractFunctionSignature string `yaml:"monitor_builder_flashblock_number_contract_function_signature"` + MonitorFlashblocksMainPublicStreamName string `yaml:"monitor_flashblocks_main_public_stream_name"` + MonitorFlashblocksMaxWsMessageSizeKb int64 `yaml:"monitor_flashblocks_max_ws_message_size_kb"` + MonitorFlashblocksPrivateStreams map[string]string `yaml:"monitor_flashblocks_private_streams"` + MonitorFlashblocksPublicStreams map[string]string `yaml:"monitor_flashblocks_public_streams"` MonitorFlashtestationRegistryContract string `yaml:"monitor_flashtestation_registry_contract"` MonitorFlashtestationRegistryEventSignature string `yaml:"monitor_flashtestation_registry_event_signature"` MonitorFlashtestationRegistryFunctionSignature string `yaml:"monitor_flashtestation_registry_function_signature"` @@ -46,6 +50,10 @@ var ( errL2InvalidBuilderAddress = errors.New("invalid l2 builder address") errL2InvalidBuilderPolicyContact = errors.New("invalid l2 builder policy contract address") errL2InvalidFlashblockNumberContact = errors.New("invalid l2 flashblocks number contract address") + errL2InvalidFlashblocksMaxWsMessageSizeKb = errors.New("invalid l2 flashblocks max ws message size") + errL2InvalidFlashblocksPrivateStream = errors.New("invalid l2 private flashblocks stream url") + errL2InvalidFlashblocksPublicStream = errors.New("invalid l2 public flashblocks stream url") + errL2InvalidFlashblocksPublicStreamName = errors.New("invalid l2 public flashblocks stream name") errL2InvalidFlashtestationsRegistryContact = errors.New("invalid l2 flashtestations registry contract address") errL2InvalidRpc = errors.New("invalid l2 rpc url") errL2InvalidRpcFallback = errors.New("invalid l2 fallback rpc url") @@ -168,6 +176,50 @@ func (cfg *L2) Validate() error { } } + { // monitor_flashblocks_max_ws_message_size_kb + if cfg.MonitorFlashblocksMaxWsMessageSizeKb < 16 || cfg.MonitorFlashblocksMaxWsMessageSizeKb > 1024 { + errs = append(errs, fmt.Errorf("%w: must be within [16..1024] range: %d", + errL2InvalidFlashblocksMaxWsMessageSizeKb, + cfg.MonitorFlashblocksMaxWsMessageSizeKb, + )) + } + } + + { // monitor_flashblocks_main_public_stream_name + if cfg.MonitorFlashblocksMainPublicStreamName != "" { + if _, exists := cfg.MonitorFlashblocksPublicStreams[cfg.MonitorFlashblocksMainPublicStreamName]; !exists { + errs = append(errs, fmt.Errorf("%w: public stream name is not configured: %s", + errL2InvalidFlashblocksPublicStreamName, + cfg.MonitorFlashblocksMainPublicStreamName, + )) + } + } + } + + { // monitor_flashblocks_private_streams + for _, flashblocks := range cfg.MonitorFlashblocksPrivateStreams { + if _, err := url.Parse(flashblocks); err != nil { + errs = append(errs, fmt.Errorf("%w: %s: %w", + errL2InvalidFlashblocksPrivateStream, + flashblocks, + err, + )) + } + } + } + + { // monitor_flashblocks_public_streams + for _, flashblocks := range cfg.MonitorFlashblocksPublicStreams { + if _, err := url.Parse(flashblocks); err != nil { + errs = append(errs, fmt.Errorf("%w: %s: %w", + errL2InvalidFlashblocksPublicStream, + flashblocks, + err, + )) + } + } + } + { // monitor_wallet_address for _, wa := range cfg.MonitorWalletAddresses { _addr, err := ethcommon.ParseHexOrString(wa) diff --git a/go.mod b/go.mod index eee68b2..50fd2e0 100644 --- a/go.mod +++ b/go.mod @@ -3,79 +3,81 @@ module github.com/flashbots/chain-monitor go 1.24.0 require ( - github.com/ethereum/go-ethereum v1.16.1 + github.com/andybalholm/brotli v1.2.0 + github.com/coder/websocket v1.8.14 + github.com/ethereum/go-ethereum v1.16.7 github.com/google/uuid v1.6.0 - github.com/prometheus/client_golang v1.22.0 - github.com/stretchr/testify v1.10.0 + github.com/prometheus/client_golang v1.23.2 + github.com/stretchr/testify v1.11.1 github.com/urfave/cli/v2 v2.27.7 - go.opentelemetry.io/otel v1.37.0 - go.opentelemetry.io/otel/exporters/prometheus v0.59.0 - go.opentelemetry.io/otel/metric v1.37.0 - go.opentelemetry.io/otel/sdk v1.37.0 - go.opentelemetry.io/otel/sdk/metric v1.37.0 - go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.40.0 + go.opentelemetry.io/otel v1.38.0 + go.opentelemetry.io/otel/exporters/prometheus v0.60.0 + go.opentelemetry.io/otel/metric v1.38.0 + go.opentelemetry.io/otel/sdk v1.38.0 + go.opentelemetry.io/otel/sdk/metric v1.38.0 + go.uber.org/zap v1.27.1 + golang.org/x/crypto v0.45.0 ) -replace github.com/ethereum/go-ethereum => github.com/ethereum-optimism/op-geth v1.101511.1 +replace github.com/ethereum/go-ethereum => github.com/ethereum-optimism/op-geth v1.101603.5 -replace github.com/ethereum/go-ethereum/common => github.com/ethereum-optimism/op-geth/common v1.101511.1 +replace github.com/ethereum/go-ethereum/common => github.com/ethereum-optimism/op-geth/common v1.101603.5 -replace github.com/ethereum/go-ethereum/core/types => github.com/ethereum-optimism/op-geth/core/types v1.101511.1 +replace github.com/ethereum/go-ethereum/core/types => github.com/ethereum-optimism/op-geth/core/types v1.101603.5 require ( github.com/BurntSushi/toml v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.22.0 // indirect + github.com/bits-and-blooms/bitset v1.24.4 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cockroachdb/pebble v1.1.5 // indirect - github.com/consensys/gnark-crypto v0.18.0 // indirect + github.com/consensys/gnark-crypto v0.19.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect - github.com/crate-crypto/go-eth-kzg v1.3.0 // indirect + github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deckarep/golang-set/v2 v2.8.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect - github.com/ethereum/c-kzg-4844/v2 v2.1.1 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect github.com/ethereum/go-verkle v0.2.2 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect - github.com/hashicorp/go-bexpr v0.1.14 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect + github.com/hashicorp/go-bexpr v0.1.15 // indirect github.com/holiman/uint256 v1.3.2 // indirect - github.com/klauspost/compress v1.18.0 // indirect - github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/klauspost/compress v1.18.1 // indirect + github.com/mattn/go-runewidth v0.0.19 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/naoina/go-stringutil v0.1.0 // indirect github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 // indirect github.com/pion/dtls/v2 v2.2.12 // indirect github.com/pion/transport/v2 v2.2.10 // indirect - github.com/pion/transport/v3 v3.0.7 // indirect + github.com/pion/transport/v3 v3.1.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect - github.com/prometheus/procfs v0.17.0 // indirect - github.com/rivo/uniseg v0.4.7 // indirect + github.com/prometheus/common v0.67.4 // indirect + github.com/prometheus/otlptranslator v0.0.2 // indirect + github.com/prometheus/procfs v0.19.2 // indirect github.com/rs/cors v1.11.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect - github.com/supranational/blst v0.3.15 // indirect + github.com/supranational/blst v0.3.16 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect - github.com/tklauser/go-sysconf v0.3.15 // indirect - github.com/tklauser/numcpus v0.10.0 // indirect - github.com/wlynxg/anet v0.0.5 // indirect + github.com/tklauser/go-sysconf v0.3.16 // indirect + github.com/tklauser/numcpus v0.11.0 // indirect github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel/trace v1.37.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/time v0.12.0 // indirect - google.golang.org/protobuf v1.36.6 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/time v0.14.0 // indirect + google.golang.org/protobuf v1.36.10 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index abe7dfd..2ed9551 100644 --- a/go.sum +++ b/go.sum @@ -6,15 +6,19 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= +github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= +github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4= -github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE= +github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= +github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= @@ -27,32 +31,38 @@ github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwP github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= -github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0= -github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c= +github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g= +github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= +github.com/consensys/gnark-crypto v0.19.2 h1:qrEAIXq3T4egxqiliFFoNrepkIWVEeIYwt3UL0fvS80= +github.com/consensys/gnark-crypto v0.19.2/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/crate-crypto/go-eth-kzg v1.3.0 h1:05GrhASN9kDAidaFJOda6A4BEvgvuXbazXg/0E3OOdI= -github.com/crate-crypto/go-eth-kzg v1.3.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= +github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg= +github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg= github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= -github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4= -github.com/crate-crypto/go-kzg-4844 v1.1.0/go.mod h1:JolLjpSff1tCCJKaJx4psrlEdlXuJEC996PL3tTAFks= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= +github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ= github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/ethereum-optimism/op-geth v1.101511.1 h1:uhSV/JBnrcJyldt7NE96QBlEL+Ozo5CBHr6YhaJvsuo= -github.com/ethereum-optimism/op-geth v1.101511.1/go.mod h1:SkytozVEPtnUeBlquwl0Qv5JKvrN/Y5aqh+VkQo/EOI= -github.com/ethereum/c-kzg-4844/v2 v2.1.1 h1:KhzBVjmURsfr1+S3k/VE35T02+AW2qU9t9gr4R6YpSo= -github.com/ethereum/c-kzg-4844/v2 v2.1.1/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E= +github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/ethereum-optimism/op-geth v1.101603.5 h1:TK0SUGDYAJpCcftCD91AV4A1c7AjfPMVoZVBslAQx8E= +github.com/ethereum-optimism/op-geth v1.101603.5/go.mod h1:cnGR2M8zX91+rRQxXyNTEOEpw/IwdR8P11FQX7Xaqwk= +github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= +github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= +github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= +github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= 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= @@ -67,12 +77,12 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= -github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= -github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -98,10 +108,12 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/go-bexpr v0.1.14 h1:uKDeyuOhWhT1r5CiMTjdVY4Aoxdxs6EtwgTGnlosyp4= -github.com/hashicorp/go-bexpr v0.1.14/go.mod h1:gN7hRKB3s7yT+YvTdnhZVLTENejvhlkZ8UE4YVBS+Q8= -github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= -github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= +github.com/hashicorp/go-bexpr v0.1.15 h1:flTYJAqZAlK+t8ezezb6WQGlRO1D4+GEF/HmH+xZo5k= +github.com/hashicorp/go-bexpr v0.1.15/go.mod h1:HGKbAByHn2aJWUV47gL7+IjLK79iU3EZIbOwCXJZLoE= +github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db h1:IZUYC/xb3giYwBLMnr8d0TGTzPKFGNTCGgGLoyeX330= +github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db/go.mod h1:xTEYN9KCHxuYHs+NmrmzFcnvHMzLLNiGFafCb1n3Mfg= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= @@ -112,8 +124,10 @@ github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFck github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= +github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -126,8 +140,10 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= -github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= +github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQhoHZAz4x7TIw= @@ -153,33 +169,33 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= -github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8= +github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so= github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q= github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E= -github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= -github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= +github.com/pion/transport/v3 v3.1.1 h1:Tr684+fnnKlhPceU+ICdrw6KKkTms+5qHMgw6bIkYOM= +github.com/pion/transport/v3 v3.1.1/go.mod h1:+c2eewC5WJQHiAA46fkMMzoYZSuGzA/7E2FPrOYHctQ= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= -github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= -github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc= +github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= +github.com/prometheus/otlptranslator v0.0.2 h1:+1CdeLVrRQ6Psmhnobldo0kTp96Rj80DRXRd5OSnMEQ= +github.com/prometheus/otlptranslator v0.0.2/go.mod h1:P8AwMgdD7XEr6QRUJ2QWLpiAZTgTE2UYgjlu3svompI= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -194,16 +210,16 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/supranational/blst v0.3.15 h1:rd9viN6tfARE5wv3KZJ9H8e1cg0jXW8syFCcsbHa76o= -github.com/supranational/blst v0.3.15/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= +github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= -github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= -github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= -github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= -github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= +github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= +github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= +github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= +github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= @@ -211,40 +227,44 @@ github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= -go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= -go.opentelemetry.io/otel/exporters/prometheus v0.59.0 h1:HHf+wKS6o5++XZhS98wvILrLVgHxjA/AMjqHKes+uzo= -go.opentelemetry.io/otel/exporters/prometheus v0.59.0/go.mod h1:R8GpRXTZrqvXHDEGVH5bF6+JqAZcK8PjJcZ5nGhEWiE= -go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= -go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= -go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= -go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= -go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= -go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= -go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= -go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/exporters/prometheus v0.60.0 h1:cGtQxGvZbnrWdC2GyjZi0PDKVSLWP/Jocix3QWfXtbo= +go.opentelemetry.io/otel/exporters/prometheus v0.60.0/go.mod h1:hkd1EekxNo69PTV4OWFGZcKQiIqg0RfuWExcPKFvepk= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc h1:TS73t7x3KarrNd5qAipmspBDS1rkMcgVG/fS1aRb4Rc= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY= +golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= golang.org/x/mod v0.3.0/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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -262,15 +282,15 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -294,8 +314,8 @@ golang.org/x/sys v0.5.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.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -310,10 +330,10 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= -golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= -golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -332,8 +352,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/metrics/exports.go b/metrics/exports.go index 6770dc9..50d9b17 100644 --- a/metrics/exports.go +++ b/metrics/exports.go @@ -18,6 +18,12 @@ var ( FlashblocksLandedCount otelapi.Int64Gauge FlashblocksMissedCount otelapi.Int64Gauge + FlashblocksDropped otelapi.Int64Counter + FlashblocksMismatched otelapi.Int64Counter + FlashblocksReceiveFailureCount otelapi.Int64Counter + FlashblocksReceiveSuccessCount otelapi.Int64Counter + FlashblocksSkipped otelapi.Int64Counter + FlashtestationsLandedCount otelapi.Int64Gauge FlashtestationsMissedCount otelapi.Int64Gauge RegisteredFlashtestationsCount otelapi.Int64Gauge @@ -59,6 +65,12 @@ var ( setupFlashblocksLandedCount, setupFlashblocksMissedCount, + setupFlashblocksDropped, + setupFlashblocksMismatched, + setupFlashblocksReceiveFailureCount, + setupFlashblocksReceiveSuccessCount, + setupFlashblocksSkipped, + setupFlashtestationsLandedCount, setupFlashtestationsMissedCount, setupRegisteredFlashtestationsCount, diff --git a/metrics/metrics.go b/metrics/metrics.go index 0eaf516..e4c90d4 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -133,6 +133,65 @@ func setupFlashblocksMissedCount(ctx context.Context, _ *config.ProbeTx) error { return nil } +func setupFlashblocksDropped(ctx context.Context, _ *config.ProbeTx) error { + m, err := meter.Int64Counter("flashblocks_dropped_count", + otelapi.WithDescription( + "count of flashblocks that were produced by builder but were not included into the block", + ), + ) + if err != nil { + return err + } + FlashblocksDropped = m + return nil +} + +func setupFlashblocksMismatched(ctx context.Context, _ *config.ProbeTx) error { + m, err := meter.Int64Counter("flashblocks_mismatched_count", + otelapi.WithDescription( + "count of mismatching flashblocks (all streams must produce identical flashblocks)", + ), + ) + if err != nil { + return err + } + FlashblocksMismatched = m + return nil +} + +func setupFlashblocksReceiveFailureCount(ctx context.Context, _ *config.ProbeTx) error { + m, err := meter.Int64Counter("flashblocks_receive_failure_count", + otelapi.WithDescription("count of failures to receive a flashblock"), + ) + if err != nil { + return err + } + FlashblocksReceiveFailureCount = m + return nil +} + +func setupFlashblocksReceiveSuccessCount(ctx context.Context, _ *config.ProbeTx) error { + m, err := meter.Int64Counter("flashblocks_receive_success_count", + otelapi.WithDescription("count of successfully received flashblocks"), + ) + if err != nil { + return err + } + FlashblocksReceiveSuccessCount = m + return nil +} + +func setupFlashblocksSkipped(ctx context.Context, _ *config.ProbeTx) error { + m, err := meter.Int64Counter("flashblocks_skipped_count", + otelapi.WithDescription("count of flashblocks skipped by a stream (e.g. receiving index 4 right after 0 means 3 skipped flashblocks)"), + ) + if err != nil { + return err + } + FlashblocksSkipped = m + return nil +} + func setupFlashtestationsLandedCount(ctx context.Context, _ *config.ProbeTx) error { m, err := meter.Int64Gauge("flashtestations_landed_count", otelapi.WithDescription("flashtestations landed by our builder"), diff --git a/readme.md b/readme.md index 9f8fbd6..9a73ae2 100644 --- a/readme.md +++ b/readme.md @@ -51,32 +51,36 @@ OPTIONS: L2 - --l2-block-time duration average duration between consecutive blocks on l2 (default: 2s) [$CHAIN_MONITOR_L2_BLOCK_TIME] - --l2-flashblocks-per-block value expected count of non-deposit flashblocks per block on l2 (default: 4) [$CHAIN_MONITOR_L2_FLASHBLOCKS_PER_BLOCK] - --l2-flashtestations-per-block value expected count of flashtestations per block on l2 (default: 1) [$CHAIN_MONITOR_L2_FLASHTESTATIONS_PER_BLOCK] - --l2-genesis-time value genesis time of the chain (used to determine current height) (default: 0) [$CHAIN_MONITOR_L2_GENESIS_TIME] - --l2-monitor-builder-address address l2 builder address to monitor [$CHAIN_MONITOR_L2_MONITOR_BUILDER_ADDRESS] - --l2-monitor-builder-policy-add-workload-id-event-signature signature l2 builder policy event signature to add workload id (default: "WorkloadAddedToPolicy(bytes32)") [$CHAIN_MONITOR_L2_MONITOR_BUILDER_POLICY_ADD_WORKLOAD_ID_EVENT_SIGNATURE] - --l2-monitor-builder-policy-add-workload-id-signature signature l2 builder policy function signature to add workload id (default: "addWorkloadToPolicy(bytes32,string,string[])") [$CHAIN_MONITOR_L2_MONITOR_BUILDER_POLICY_ADD_WORKLOAD_ID_SIGNATURE] - --l2-monitor-builder-policy-contract address l2 builder flashtestations policy contract address to monitor [$CHAIN_MONITOR_L2_MONITOR_BUILDER_POLICY_CONTRACT] - --l2-monitor-builder-policy-contract-function-signature signature l2 builder flashtestations policy contract function signature to monitor (default: "permitVerifyBlockBuilderProof(uint8,bytes32,uint256,bytes)") [$CHAIN_MONITOR_L2_MONITOR_BUILDER_POLICY_CONTRACT_FUNCTION_SIGNATURE] - --l2-monitor-flashblock-number-contract address l2 builder flashblock number contract address to monitor [$CHAIN_MONITOR_L2_MONITOR_FLASHBLOCK_NUMBER_CONTRACT] - --l2-monitor-flashblock-number-contract-function-signature signature l2 builder flashblock number contract function signature to monitor (default: "incrementFlashblockNumber()") [$CHAIN_MONITOR_L2_MONITOR_FLASHBLOCK_NUMBER_CONTRACT_FUNCTION_SIGNATURE] - --l2-monitor-flashtestations-registry-contract address l2 builder flashtestations registry contract address to monitor [$CHAIN_MONITOR_L2_MONITOR_FLASHTESTATIONS_REGISTRY_CONTRACT] - --l2-monitor-flashtestations-registry-contract-function-signature signature l2 builder flashtestations registry contract function signature to monitor (default: "permitRegisterTEEService(bytes,bytes,uint256,uint256,bytes)") [$CHAIN_MONITOR_L2_MONITOR_FLASHTESTATIONS_REGISTRY_CONTRACT_FUNCTION_SIGNATURE] - --l2-monitor-flashtestations-registry-event-signature signature l2 builder flashtestations registry contract event signature to monitor (default: "TEEServiceRegistered(address,bytes,bool)") [$CHAIN_MONITOR_L2_MONITOR_FLASHTESTATIONS_REGISTRY_EVENT_SIGNATURE] - --l2-monitor-tx-receipts l2 monitor transactions receipts (can be slow on busy chains) (default: false) [$CHAIN_MONITOR_L2_MONITOR_TX_RECEIPTS] - --l2-monitor-wallet label=address [ --l2-monitor-wallet label=address ] list of l2 wallet label=address to monitor the balances of [$CHAIN_MONITOR_L2_MONITOR_WALLET] - --l2-network-id number on every rpc call, verify that network id matches this number (default: do not check) [$CHAIN_MONITOR_L2_NETWORK_ID] - --l2-probe-tx-gas-limit limit l2 probe transaction gas limit (default: 1000000) [$CHAIN_MONITOR_L2_PROBE_TX_GAS_LIMIT] - --l2-probe-tx-gas-price-adjustment percent l2 probe transaction gas price adjustment in percent (default: 10) [$CHAIN_MONITOR_L2_PROBE_TX_GAS_PRICE_ADJUSTMENT] - --l2-probe-tx-gas-price-cap wei l2 probe transaction gas price cap in wei (default: 10) [$CHAIN_MONITOR_L2_PROBE_TX_GAS_PRICE_CAP] - --l2-probe-tx-nonce-reset-interval interval interval at which to conditionally reset l2 probe tx nonce (default: 10m0s) [$CHAIN_MONITOR_L2_PROBE_TX_NONCE_RESET_INTERVAL] - --l2-probe-tx-nonce-reset-threshold difference difference probeTxSent-probeTxLanded that should trigger probe tx nonce reset (default: 60) [$CHAIN_MONITOR_L2PROBE_TX_RESET_THRESHOLD] - --l2-probe-tx-private-key key l2 private key to send tx inclusion latency probes with [$CHAIN_MONITOR_L2_PROBE_TX_PRIVATE_KEY] - --l2-reorg-window duration max duration of block history to keep in memory for the l2 reorg adjustments (default: 24h0m0s) [$CHAIN_MONITOR_L2_REORG_WINDOW] - --l2-rpc url url of l2 rpc endpoint (default: "http://127.0.0.1:8645") [$CHAIN_MONITOR_L2_RPC] - --l2-rpc-fallback url [ --l2-rpc-fallback url ] urls of fallback l2 rpc endpoints [$CHAIN_MONITOR_L2_RPC_FALLBACK] + --l2-block-time duration average duration between consecutive blocks on l2 (default: 2s) [$CHAIN_MONITOR_L2_BLOCK_TIME] + --l2-flashblocks-per-block value expected count of non-deposit flashblocks per block on l2 (default: 4) [$CHAIN_MONITOR_L2_FLASHBLOCKS_PER_BLOCK] + --l2-flashtestations-per-block value expected count of flashtestations per block on l2 (default: 1) [$CHAIN_MONITOR_L2_FLASHTESTATIONS_PER_BLOCK] + --l2-genesis-time value genesis time of the chain (used to determine current height) (default: 0) [$CHAIN_MONITOR_L2_GENESIS_TIME] + --l2-monitor-builder-address address l2 builder address to monitor [$CHAIN_MONITOR_L2_MONITOR_BUILDER_ADDRESS] + --l2-monitor-builder-policy-add-workload-id-event-signature signature l2 builder policy event signature to add workload id (default: "WorkloadAddedToPolicy(bytes32)") [$CHAIN_MONITOR_L2_MONITOR_BUILDER_POLICY_ADD_WORKLOAD_ID_EVENT_SIGNATURE] + --l2-monitor-builder-policy-add-workload-id-signature signature l2 builder policy function signature to add workload id (default: "addWorkloadToPolicy(bytes32,string,string[])") [$CHAIN_MONITOR_L2_MONITOR_BUILDER_POLICY_ADD_WORKLOAD_ID_SIGNATURE] + --l2-monitor-builder-policy-contract address l2 builder flashtestations policy contract address to monitor [$CHAIN_MONITOR_L2_MONITOR_BUILDER_POLICY_CONTRACT] + --l2-monitor-builder-policy-contract-function-signature signature l2 builder flashtestations policy contract function signature to monitor (default: "permitVerifyBlockBuilderProof(uint8,bytes32,uint256,bytes)") [$CHAIN_MONITOR_L2_MONITOR_BUILDER_POLICY_CONTRACT_FUNCTION_SIGNATURE] + --l2-monitor-flashblock-number-contract address l2 builder flashblock number contract address to monitor [$CHAIN_MONITOR_L2_MONITOR_FLASHBLOCK_NUMBER_CONTRACT] + --l2-monitor-flashblock-number-contract-function-signature signature l2 builder flashblock number contract function signature to monitor (default: "incrementFlashblockNumber()") [$CHAIN_MONITOR_L2_MONITOR_FLASHBLOCK_NUMBER_CONTRACT_FUNCTION_SIGNATURE] + --l2-monitor-flashblocks-main-public-stream value the name of the main public l2 flashblocks stream [$CHAIN_MONITOR_L2_MONITOR_FLASHBLOCKS_MAIN_PUBLIC_STREAM] + --l2-monitor-flashblocks-max-ws-message-size-kb value max size (in kb) of l2 builder flashblocks ws messages (default: 256) [$CHAIN_MONITOR_L2_MONITOR_FLASHBLOCKS_MAX_WS_MESSAGE_SIZE_KB] + --l2-monitor-flashblocks-private-stream value [ --l2-monitor-flashblocks-private-stream value ] private websocket stream(s) of l2 flashblocks [$CHAIN_MONITOR_L2_MONITOR_FLASHBLOCKS_PRIVATE_STREAMS] + --l2-monitor-flashblocks-public-stream value [ --l2-monitor-flashblocks-public-stream value ] public websocket stream(s) of l2 flashblocks [$CHAIN_MONITOR_L2_MONITOR_FLASHBLOCKS_PUBLIC_STREAMS] + --l2-monitor-flashtestations-registry-contract address l2 builder flashtestations registry contract address to monitor [$CHAIN_MONITOR_L2_MONITOR_FLASHTESTATIONS_REGISTRY_CONTRACT] + --l2-monitor-flashtestations-registry-contract-function-signature signature l2 builder flashtestations registry contract function signature to monitor (default: "permitRegisterTEEService(bytes,bytes,uint256,uint256,bytes)") [$CHAIN_MONITOR_L2_MONITOR_FLASHTESTATIONS_REGISTRY_CONTRACT_FUNCTION_SIGNATURE] + --l2-monitor-flashtestations-registry-event-signature signature l2 builder flashtestations registry contract event signature to monitor (default: "TEEServiceRegistered(address,bytes,bool)") [$CHAIN_MONITOR_L2_MONITOR_FLASHTESTATIONS_REGISTRY_EVENT_SIGNATURE] + --l2-monitor-tx-receipts l2 monitor transactions receipts (can be slow on busy chains) (default: false) [$CHAIN_MONITOR_L2_MONITOR_TX_RECEIPTS] + --l2-monitor-wallet label=address [ --l2-monitor-wallet label=address ] list of l2 wallet label=address to monitor the balances of [$CHAIN_MONITOR_L2_MONITOR_WALLET] + --l2-network-id number on every rpc call, verify that network id matches this number (default: do not check) [$CHAIN_MONITOR_L2_NETWORK_ID] + --l2-probe-tx-gas-limit limit l2 probe transaction gas limit (default: 1000000) [$CHAIN_MONITOR_L2_PROBE_TX_GAS_LIMIT] + --l2-probe-tx-gas-price-adjustment percent l2 probe transaction gas price adjustment in percent (default: 10) [$CHAIN_MONITOR_L2_PROBE_TX_GAS_PRICE_ADJUSTMENT] + --l2-probe-tx-gas-price-cap wei l2 probe transaction gas price cap in wei (default: 10) [$CHAIN_MONITOR_L2_PROBE_TX_GAS_PRICE_CAP] + --l2-probe-tx-nonce-reset-interval interval interval at which to conditionally reset l2 probe tx nonce (default: 10m0s) [$CHAIN_MONITOR_L2_PROBE_TX_NONCE_RESET_INTERVAL] + --l2-probe-tx-nonce-reset-threshold difference difference probeTxSent-probeTxLanded that should trigger probe tx nonce reset (default: 60) [$CHAIN_MONITOR_L2PROBE_TX_RESET_THRESHOLD] + --l2-probe-tx-private-key key l2 private key to send tx inclusion latency probes with [$CHAIN_MONITOR_L2_PROBE_TX_PRIVATE_KEY] + --l2-reorg-window duration max duration of block history to keep in memory for the l2 reorg adjustments (default: 24h0m0s) [$CHAIN_MONITOR_L2_REORG_WINDOW] + --l2-rpc url url of l2 rpc endpoint (default: "http://127.0.0.1:8645") [$CHAIN_MONITOR_L2_RPC] + --l2-rpc-fallback url [ --l2-rpc-fallback url ] urls of fallback l2 rpc endpoints [$CHAIN_MONITOR_L2_RPC_FALLBACK] SERVER diff --git a/server/l2.go b/server/l2.go index 7c21e84..e4d8f06 100644 --- a/server/l2.go +++ b/server/l2.go @@ -14,6 +14,7 @@ import ( type L2 struct { blockInspector *l2.BlockInspector + flashblocksMonitor *l2.FlashblocksMonitor txInclusionLatencyMonitor *l2.TxInclusionLatencyMonitor walletObserver *wallet.Observer @@ -29,6 +30,14 @@ func newL2(cfg *config.L2) (*L2, error) { ) } + flashblocksMonitor, err := l2.NewFlashblocksMonitor(cfg) + if err != nil { + return nil, fmt.Errorf( + "failed to initialise flashblocks monitor: %w", + err, + ) + } + txInclusionLatencyMonitor, err := l2.NewTxInclusionLatencyMonitor(cfg) if err != nil { return nil, fmt.Errorf( @@ -47,6 +56,7 @@ func newL2(cfg *config.L2) (*L2, error) { return &L2{ blockInspector: blockInspector, + flashblocksMonitor: flashblocksMonitor, txInclusionLatencyMonitor: txInclusionLatencyMonitor, walletObserver: walletObserver, }, nil @@ -60,7 +70,8 @@ func (l2 *L2) run(ctx context.Context) { ctx, cancel := context.WithCancel(ctx) l2.canceller = cancel - l2.blockInspector.Run(ctx) + flashblocks := l2.flashblocksMonitor.Run(ctx) + l2.blockInspector.Run(ctx, flashblocks) l2.txInclusionLatencyMonitor.Run(ctx) l2.walletObserver.Run(ctx) } @@ -72,6 +83,7 @@ func (l2 *L2) stop() { l2.walletObserver.Stop() l2.txInclusionLatencyMonitor.Stop() + l2.flashblocksMonitor.Stop() l2.blockInspector.Stop() } @@ -82,6 +94,10 @@ func (l2 *L2) observe(ctx context.Context, o otelapi.Observer) error { errs = append(errs, err) } + if err := l2.flashblocksMonitor.Observe(ctx, o); err != nil { + errs = append(errs, err) + } + if err := l2.txInclusionLatencyMonitor.Observe(ctx, o); err != nil { errs = append(errs, err) } diff --git a/server/l2/block_inspector.go b/server/l2/block_inspector.go index ec3ae52..fdf5478 100644 --- a/server/l2/block_inspector.go +++ b/server/l2/block_inspector.go @@ -11,6 +11,7 @@ import ( "os" "path/filepath" "slices" + "strings" "sync" "time" @@ -45,6 +46,9 @@ type BlockInspector struct { blockHeight uint64 blocks *types.RingBuffer[blockRecord] + flashblocks map[uint64]map[string][]*flashblockEvent + mxFlashblocks sync.Mutex + metrics *blockInspectorMetrics unwinding bool @@ -323,7 +327,10 @@ func NewBlockInspector(cfg *config.L2) (*BlockInspector, error) { return bi, nil } -func (bi *BlockInspector) Run(ctx context.Context) { +func (bi *BlockInspector) Run( + ctx context.Context, + flashblocks *<-chan *flashblockEvent, +) { if bi == nil { return } @@ -344,6 +351,21 @@ func (bi *BlockInspector) Run(ctx context.Context) { } } }() + + if flashblocks != nil { + bi.flashblocks = make(map[uint64]map[string][]*flashblockEvent) + + go func() { + for { + select { + case <-bi.done: + return + case evt := <-*flashblocks: + bi.processFlashblock(ctx, evt) + } + } + }() + } } func (bi *BlockInspector) Stop() { @@ -695,6 +717,70 @@ func (bi *BlockInspector) processBlock(ctx context.Context, blockNumber uint64) } } + if bi.flashblocks != nil { + bi.mxFlashblocks.Lock() + + blockHash := strings.TrimPrefix(strings.ToLower(block.Hash().String()), "0x") + + if payloads, seen := bi.flashblocks[blockNumber]; seen { + matched := false + scanningPayloads: + for _, flashblocks := range payloads { + for idx := len(flashblocks) - 1; idx >= 0; idx-- { + if fb := flashblocks[idx]; fb != nil && + strings.TrimPrefix(strings.ToLower(fb.flashblock.Diff.BlockHash), "0x") == blockHash { + // --- + matched = true + + if fb.flashblock.Index < len(flashblocks)-1 { + dropped := len(flashblocks) - fb.flashblock.Index - 1 + metrics.FlashblocksDropped.Add(ctx, int64(dropped), otelapi.WithAttributes( + attribute.KeyValue{Key: "kind", Value: attribute.StringValue("l2")}, + attribute.KeyValue{Key: "network_id", Value: attribute.Int64Value(bi.cfg.chainID.Int64())}, + )) + for jdx := fb.flashblock.Index + 1; jdx < len(flashblocks); jdx++ { + if dfb := flashblocks[jdx]; dfb != nil { + l.Warn("Flashblock was dropped", + zap.Any("flashblock", fb.flashblock), + ) + } + } + } + + break scanningPayloads + } + } + } + + if !matched { + dropped := 0 + for _, flashblocks := range payloads { + dropped += len(flashblocks) + for idx := len(flashblocks); idx >= 0; idx-- { + fb := flashblocks[idx] + if fb != nil { + l.Warn("Flashblock was dropped", + zap.Any("flashblock", fb.flashblock), + ) + } + } + } + metrics.FlashblocksDropped.Add(ctx, int64(dropped), otelapi.WithAttributes( + attribute.KeyValue{Key: "kind", Value: attribute.StringValue("l2")}, + attribute.KeyValue{Key: "network_id", Value: attribute.Int64Value(bi.cfg.chainID.Int64())}, + )) + } + } + + for fbBlockNumber := range bi.flashblocks { + if fbBlockNumber <= blockNumber { + delete(bi.flashblocks, fbBlockNumber) + } + } + + bi.mxFlashblocks.Unlock() + } + metrics.FailedTxPerBlock.Record(ctx, failedTxCount) if bi.blocks.Length() > bi.cfg.reorgWindow { @@ -704,6 +790,51 @@ func (bi *BlockInspector) processBlock(ctx context.Context, blockNumber uint64) return nil } +func (bi *BlockInspector) processFlashblock(ctx context.Context, evt *flashblockEvent) { + l := logutils.LoggerFromContext(ctx).With( + zap.Uint64("block_number", evt.flashblock.Metadata.BlockNumber), + zap.String("kind", "l2"), + ) + + fb := evt.flashblock + + bi.mxFlashblocks.Lock() + defer bi.mxFlashblocks.Unlock() + + if _, exists := bi.flashblocks[fb.Metadata.BlockNumber]; !exists { + bi.flashblocks[fb.Metadata.BlockNumber] = make(map[string][]*flashblockEvent) + } + if _, exists := bi.flashblocks[fb.Metadata.BlockNumber][fb.PayloadId]; !exists { + bi.flashblocks[fb.Metadata.BlockNumber][fb.PayloadId] = make([]*flashblockEvent, 0, bi.cfg.flashblocksPerBlock) + } + + if len(bi.flashblocks[fb.Metadata.BlockNumber][fb.PayloadId]) > fb.Index { + existing := bi.flashblocks[fb.Metadata.BlockNumber][fb.PayloadId][fb.Index] + if existing == nil { + bi.flashblocks[fb.Metadata.BlockNumber][fb.PayloadId][fb.Index] = evt + return // out-of-order delivery + } + if fb.Equal(existing.flashblock) { + return // double-delivery of the same flashblock + } + l.Warn("Received different flashblocks for the same payload id and index", + zap.Any("this", fb), + zap.Any("that", existing), + ) + bi.flashblocks[fb.Metadata.BlockNumber][fb.PayloadId][fb.Index] = evt + } else { + // fill up the gaps (if any) + for idx := len(bi.flashblocks[fb.Metadata.BlockNumber][fb.PayloadId]); idx <= fb.Index; idx++ { + bi.flashblocks[fb.Metadata.BlockNumber][fb.PayloadId] = append( + bi.flashblocks[fb.Metadata.BlockNumber][fb.PayloadId], + nil, + ) + } + // store + bi.flashblocks[fb.Metadata.BlockNumber][fb.PayloadId][fb.Index] = evt + } +} + func (bi *BlockInspector) processReorgUnwind(ctx context.Context) error { l := logutils.LoggerFromContext(ctx) diff --git a/server/l2/flashblocks_monitor.go b/server/l2/flashblocks_monitor.go new file mode 100644 index 0000000..7b35e8e --- /dev/null +++ b/server/l2/flashblocks_monitor.go @@ -0,0 +1,440 @@ +package l2 + +import ( + "bytes" + "context" + "encoding/json" + + "io" + "time" + + "github.com/andybalholm/brotli" + "github.com/coder/websocket" + "github.com/flashbots/chain-monitor/config" + "github.com/flashbots/chain-monitor/logutils" + "github.com/flashbots/chain-monitor/metrics" + "github.com/flashbots/chain-monitor/types" + "go.opentelemetry.io/otel/attribute" + otelapi "go.opentelemetry.io/otel/metric" + + "go.uber.org/zap" +) + +type FlashblocksMonitor struct { + // parameters + + cfg *flashblocksMonitorConfig + + // actors + + stop context.CancelFunc + + // streams + + flashblocksPrivate chan *flashblockEvent + flashblocksPublic chan *flashblockEvent + + lastFlashblockPrivate map[string]*flashblockEvent + lastFlashblockPublic map[string]*flashblockEvent +} + +type flashblocksMonitorConfig struct { + flashblocksPerBlock int + networkID int64 + maxMessageSize int64 + mainPublicStreamName string + publicStreams map[string]string + privateStreams map[string]string +} + +type flashblockEvent struct { + stream string + timestamp time.Time + flashblock types.Flashblock +} + +const ( + wsBackoffMin = 200 * time.Millisecond + wsBackoffMax = time.Minute + wsBackoffFactor = time.Duration(2) + + wsTimeout = 30 * time.Second +) + +func NewFlashblocksMonitor(cfg *config.L2) (*FlashblocksMonitor, error) { + if len(cfg.MonitorFlashblocksPrivateStreams) == 0 && + len(cfg.MonitorFlashblocksPublicStreams) == 0 { + // --- + return nil, nil + } + + fm := &FlashblocksMonitor{ + flashblocksPrivate: make(chan *flashblockEvent, len(cfg.MonitorFlashblocksPrivateStreams)), + flashblocksPublic: make(chan *flashblockEvent, len(cfg.MonitorFlashblocksPublicStreams)), + + lastFlashblockPrivate: make(map[string]*flashblockEvent, len(cfg.MonitorFlashblocksPrivateStreams)), + lastFlashblockPublic: make(map[string]*flashblockEvent, len(cfg.MonitorFlashblocksPublicStreams)), + + cfg: &flashblocksMonitorConfig{ + mainPublicStreamName: cfg.MonitorFlashblocksMainPublicStreamName, + maxMessageSize: cfg.MonitorFlashblocksMaxWsMessageSizeKb * 1024, + networkID: int64(cfg.NetworkID), + privateStreams: cfg.MonitorFlashblocksPrivateStreams, + publicStreams: cfg.MonitorFlashblocksPublicStreams, + }, + } + + return fm, nil +} + +func (fm *FlashblocksMonitor) Run(ctx context.Context) *<-chan *flashblockEvent { + if fm == nil { + return nil + } + + processingContext := logutils.ContextWithLogger( + context.Background(), + logutils.LoggerFromContext(ctx), + ) + + processingContext, cancel := context.WithCancel(processingContext) + fm.stop = cancel + + for stream, url := range fm.cfg.privateStreams { + fm.readStream(ctx, stream, url, fm.flashblocksPrivate) + } + for stream, url := range fm.cfg.publicStreams { + fm.readStream(ctx, stream, url, fm.flashblocksPublic) + } + + flashblocks := make(chan *flashblockEvent, 2*fm.cfg.flashblocksPerBlock) + fm.processFlashblocks(processingContext, flashblocks) + + var output <-chan *flashblockEvent = flashblocks + + return &output +} + +func (fm *FlashblocksMonitor) Stop() { + if fm == nil { + return + } + + if fm.stop != nil { + fm.stop() + } +} + +func (fm *FlashblocksMonitor) Observe(_ context.Context, o otelapi.Observer) error { + if fm == nil { + return nil + } + + return nil +} + +func (fm *FlashblocksMonitor) readStream( + ctx context.Context, + streamID, streamUrl string, + flashblocks chan<- *flashblockEvent, +) { + go func() { + l := logutils.LoggerFromContext(ctx) + + backoff := wsBackoffMin + + redial: + for ctx.Err() == nil { + var ( + conn *websocket.Conn + doneReceiving context.CancelFunc + ) + + { // dial + l.Info("Connecting to flashblocks stream...", + zap.String("stream", streamID), + zap.String("url", streamUrl), + ) + + dialCtx, doneDialling := context.WithTimeout(ctx, wsTimeout) + + _conn, _, err := websocket.Dial(dialCtx, streamUrl, &websocket.DialOptions{ + CompressionMode: websocket.CompressionContextTakeover, + }) + if err != nil { + doneDialling() + + metrics.FlashblocksReceiveFailureCount.Add(ctx, 1, otelapi.WithAttributes( + attribute.KeyValue{Key: "kind", Value: attribute.StringValue("l2")}, + attribute.KeyValue{Key: "stream", Value: attribute.StringValue(streamID)}, + attribute.KeyValue{Key: "network_id", Value: attribute.Int64Value(fm.cfg.networkID)}, + )) + l.Warn("Failed to connect to flashblocks stream", + zap.Error(err), + zap.String("stream", streamID), + zap.String("url", streamUrl), + zap.Duration("backoff", backoff), + ) + time.Sleep(backoff) + backoff = min(wsBackoffFactor*backoff, wsBackoffMax) + continue redial + } + _conn.SetReadLimit(fm.cfg.maxMessageSize) + + backoff = wsBackoffMin + conn = _conn + doneReceiving = doneDialling + } + + { // receive + for ctx.Err() == nil { + readCtx, doneReading := context.WithTimeout(ctx, wsTimeout) + mtype, mbytes, err := conn.Read(readCtx) + if err != nil { + metrics.FlashblocksReceiveFailureCount.Add(ctx, 1, otelapi.WithAttributes( + attribute.KeyValue{Key: "kind", Value: attribute.StringValue("l2")}, + attribute.KeyValue{Key: "stream", Value: attribute.StringValue(streamID)}, + attribute.KeyValue{Key: "network_id", Value: attribute.Int64Value(fm.cfg.networkID)}, + )) + l.Warn("Failed to read message from flashblocks stream", + zap.Error(err), + zap.String("stream", streamID), + zap.String("url", streamUrl), + ) + doneReading() + continue redial + } + doneReading() + + timestamp := time.Now() + + if mtype == websocket.MessageBinary { // binary means compressed text + brotliReader := brotli.NewReader(bytes.NewReader(mbytes)) + dbytes, err := io.ReadAll(brotliReader) + if err != nil { + metrics.FlashblocksReceiveFailureCount.Add(ctx, 1, otelapi.WithAttributes( + attribute.KeyValue{Key: "kind", Value: attribute.StringValue("l2")}, + attribute.KeyValue{Key: "stream", Value: attribute.StringValue(streamID)}, + attribute.KeyValue{Key: "network_id", Value: attribute.Int64Value(fm.cfg.networkID)}, + )) + l.Warn("Failed to decompress binary message from flashblocks stream, ignoring...", + zap.Error(err), + zap.String("stream", streamID), + zap.String("url", streamUrl), + ) + continue + } + mbytes = dbytes + } + + event := flashblockEvent{ + stream: streamID, + timestamp: timestamp, + } + if err := json.Unmarshal(mbytes, &event.flashblock); err != nil { + metrics.FlashblocksReceiveFailureCount.Add(ctx, 1, otelapi.WithAttributes( + attribute.KeyValue{Key: "kind", Value: attribute.StringValue("l2")}, + attribute.KeyValue{Key: "stream", Value: attribute.StringValue(streamID)}, + attribute.KeyValue{Key: "network_id", Value: attribute.Int64Value(fm.cfg.networkID)}, + )) + l.Error("Failed to parse flashblock", + zap.Error(err), + zap.String("stream", streamID), + zap.String("url", streamUrl), + zap.String("message", string(mbytes)), + ) + continue + } + + flashblocks <- &event + } + + doneReceiving() + } + } + }() +} + +func (fm *FlashblocksMonitor) processFlashblocks( + ctx context.Context, + output chan<- *flashblockEvent, +) { + go func() { + l := logutils.LoggerFromContext(ctx) + + for ctx.Err() == nil { + select { + case fb := <-fm.flashblocksPublic: + metrics.FlashblocksReceiveSuccessCount.Add(ctx, 1, otelapi.WithAttributes( + attribute.KeyValue{Key: "kind", Value: attribute.StringValue("l2")}, + attribute.KeyValue{Key: "stream", Value: attribute.StringValue(fb.stream)}, + attribute.KeyValue{Key: "stream_type", Value: attribute.StringValue("public")}, + attribute.KeyValue{Key: "network_id", Value: attribute.Int64Value(fm.cfg.networkID)}, + )) + l.Debug("Received flashblock on public stream", + zap.Time("timestamp", fb.timestamp), + zap.String("stream", fb.stream), + zap.Any("flashblock", fb.flashblock), + ) + last, exists := fm.lastFlashblockPublic[fb.stream] + if !exists { + fm.lastFlashblockPublic[fb.stream] = fb + continue // it's a first flashblock we've got + } + + fm.processFlashblock(ctx, fb, last) + fm.lastFlashblockPublic[fb.stream] = fb + fm.detectInconsistentFlashblocks(ctx, fb) + + if fb.stream == fm.cfg.mainPublicStreamName { + select { + case output <- fb: + // no-op + default: + // we shouldn't block if there's no reader + } + } + + case fb := <-fm.flashblocksPrivate: + metrics.FlashblocksReceiveSuccessCount.Add(ctx, 1, otelapi.WithAttributes( + attribute.KeyValue{Key: "kind", Value: attribute.StringValue("l2")}, + attribute.KeyValue{Key: "stream", Value: attribute.StringValue(fb.stream)}, + attribute.KeyValue{Key: "stream_type", Value: attribute.StringValue("private")}, + attribute.KeyValue{Key: "network_id", Value: attribute.Int64Value(fm.cfg.networkID)}, + )) + l.Debug("Received flashblock on private stream", + zap.Time("timestamp", fb.timestamp), + zap.String("stream", fb.stream), + zap.Any("flashblock", fb.flashblock), + ) + last, exists := fm.lastFlashblockPrivate[fb.stream] + if !exists { + fm.lastFlashblockPrivate[fb.stream] = fb + continue // it's a first flashblock we've got + } + + fm.processFlashblock(ctx, fb, last) + fm.lastFlashblockPrivate[fb.stream] = fb + fm.detectInconsistentFlashblocks(ctx, fb) + } + } + }() +} + +func (fm *FlashblocksMonitor) processFlashblock(ctx context.Context, this, last *flashblockEvent) { + l := logutils.LoggerFromContext(ctx) + + if this.flashblock.Metadata.BlockNumber < last.flashblock.Metadata.BlockNumber { + l.Warn("Received a flashblock with lower block number than the previous one (reorg?)", + zap.String("stream", this.stream), + zap.String("prev_payload_id", last.flashblock.PayloadId), + zap.Uint64("prev_block", last.flashblock.Metadata.BlockNumber), + zap.Int("prev_index", last.flashblock.Index), + zap.String("this_payload_id", this.flashblock.PayloadId), + zap.Uint64("this_block", this.flashblock.Metadata.BlockNumber), + zap.Int("this_index", this.flashblock.Index), + ) + return + } + + if this.flashblock.Metadata.BlockNumber == last.flashblock.Metadata.BlockNumber && + this.flashblock.Index <= last.flashblock.Index { + // --- + l.Warn("Received a flashblock with lower index than the previous one (reorg?)", + zap.String("stream", this.stream), + zap.String("prev_payload_id", last.flashblock.PayloadId), + zap.Uint64("prev_block", last.flashblock.Metadata.BlockNumber), + zap.Int("prev_index", last.flashblock.Index), + zap.String("this_payload_id", this.flashblock.PayloadId), + zap.Uint64("this_block", this.flashblock.Metadata.BlockNumber), + zap.Int("this_index", this.flashblock.Index), + ) + return + } + + if this.flashblock.Metadata.BlockNumber == last.flashblock.Metadata.BlockNumber { + skippedFlashblocks := this.flashblock.Index - last.flashblock.Index - 1 + + if skippedFlashblocks > 0 { + metrics.FlashblocksSkipped.Add(ctx, int64(skippedFlashblocks), otelapi.WithAttributes( + attribute.KeyValue{Key: "kind", Value: attribute.StringValue("l2")}, + attribute.KeyValue{Key: "stream", Value: attribute.StringValue(this.stream)}, + attribute.KeyValue{Key: "network_id", Value: attribute.Int64Value(fm.cfg.networkID)}, + )) + l.Warn("Flashblock(s) were skipped", + zap.String("stream", this.stream), + zap.Int("count", skippedFlashblocks), + zap.Uint64("prev_block", last.flashblock.Metadata.BlockNumber), + zap.Int("prev_index", last.flashblock.Index), + zap.Uint64("this_block", this.flashblock.Metadata.BlockNumber), + zap.Int("this_index", this.flashblock.Index), + ) + } + + return + } + + skippedBlocks := this.flashblock.Metadata.BlockNumber - last.flashblock.Metadata.BlockNumber - 1 + if skippedBlocks > 0 { + skippedFlashblocks := int(skippedBlocks)*fm.cfg.flashblocksPerBlock + last.flashblock.Index + + metrics.FlashblocksSkipped.Add(ctx, int64(skippedFlashblocks), otelapi.WithAttributes( + attribute.KeyValue{Key: "kind", Value: attribute.StringValue("l2")}, + attribute.KeyValue{Key: "stream", Value: attribute.StringValue(this.stream)}, + attribute.KeyValue{Key: "network_id", Value: attribute.Int64Value(fm.cfg.networkID)}, + )) + l.Warn("Flashblock(s) were skipped", + zap.String("stream", this.stream), + zap.Int("count", skippedFlashblocks), + zap.Uint64("prev_block", last.flashblock.Metadata.BlockNumber), + zap.Int("prev_index", last.flashblock.Index), + zap.Uint64("this_block", this.flashblock.Metadata.BlockNumber), + zap.Int("this_index", this.flashblock.Index), + ) + } +} + +func (fm *FlashblocksMonitor) detectInconsistentFlashblocks(ctx context.Context, this *flashblockEvent) { + l := logutils.LoggerFromContext(ctx) + + compare := func(this, that *flashblockEvent) bool { + if this == nil || that == nil { + return true + } + + if this.flashblock.PayloadId != that.flashblock.PayloadId { + return true + } + if this.flashblock.Index != that.flashblock.Index { + return true + } + + if this.flashblock.Metadata.Equal(that.flashblock.Metadata) { + return true + } + + l.Warn("Mismatching flashblocks", + zap.String("payload_id", this.flashblock.PayloadId), + zap.Int("index", this.flashblock.Index), + zap.Any("this", this), + zap.Any("that", that), + ) + return false + } + + matches := true + for _, that := range fm.lastFlashblockPrivate { + matches = matches && compare(this, that) + } + for _, that := range fm.lastFlashblockPublic { + matches = matches && compare(this, that) + } + + if !matches { + metrics.FlashblocksMismatched.Add(ctx, 1, otelapi.WithAttributes( + attribute.KeyValue{Key: "kind", Value: attribute.StringValue("l2")}, + attribute.KeyValue{Key: "stream", Value: attribute.StringValue(this.stream)}, + attribute.KeyValue{Key: "network_id", Value: attribute.Int64Value(fm.cfg.networkID)}, + )) + } +} diff --git a/types/flashblock.go b/types/flashblock.go new file mode 100644 index 0000000..4fce3a2 --- /dev/null +++ b/types/flashblock.go @@ -0,0 +1,40 @@ +package types + +type Flashblock struct { + PayloadId string `json:"payload_id"` + Index int `json:"index"` + + Metadata FlashblockMetadata `json:"metadata"` + + Diff FlashblockDiff `json:"diff"` +} + +type FlashblockMetadata struct { + BlockNumber uint64 `json:"block_number"` +} + +type FlashblockDiff struct { + BlockHash string `json:"block_hash"` + + StateRoot string `json:"state_root"` + ReceiptsRoot string `json:"receipts_root"` + WithdrawalsRoot string `json:"withdrawals_root"` +} + +func (fb Flashblock) Equal(another Flashblock) bool { + return fb.PayloadId == another.PayloadId && + fb.Index == another.Index && + fb.Metadata.Equal(another.Metadata) && + fb.Diff.Equal(another.Diff) +} + +func (fbm FlashblockMetadata) Equal(another FlashblockMetadata) bool { + return fbm.BlockNumber == another.BlockNumber +} + +func (fbd FlashblockDiff) Equal(another FlashblockDiff) bool { + return fbd.BlockHash == another.BlockHash && + fbd.StateRoot == another.StateRoot && + fbd.ReceiptsRoot == another.ReceiptsRoot && + fbd.WithdrawalsRoot == another.WithdrawalsRoot +}