Commit d4a22a7c authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-supervisor: Introduce ChainID type. (#11088)

parent 38f898fb
package metrics package metrics
import ( import (
"math/big" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
...@@ -16,11 +15,11 @@ type Metricer interface { ...@@ -16,11 +15,11 @@ type Metricer interface {
opmetrics.RPCMetricer opmetrics.RPCMetricer
CacheAdd(chainID *big.Int, label string, cacheSize int, evicted bool) CacheAdd(chainID types.ChainID, label string, cacheSize int, evicted bool)
CacheGet(chainID *big.Int, label string, hit bool) CacheGet(chainID types.ChainID, label string, hit bool)
RecordDBEntryCount(chainID *big.Int, count int64) RecordDBEntryCount(chainID types.ChainID, count int64)
RecordDBSearchEntriesRead(chainID *big.Int, count int64) RecordDBSearchEntriesRead(chainID types.ChainID, count int64)
Document() []opmetrics.DocumentedMetric Document() []opmetrics.DocumentedMetric
} }
...@@ -141,7 +140,7 @@ func (m *Metrics) RecordUp() { ...@@ -141,7 +140,7 @@ func (m *Metrics) RecordUp() {
m.up.Set(1) m.up.Set(1)
} }
func (m *Metrics) CacheAdd(chainID *big.Int, label string, cacheSize int, evicted bool) { func (m *Metrics) CacheAdd(chainID types.ChainID, label string, cacheSize int, evicted bool) {
chain := chainIDLabel(chainID) chain := chainIDLabel(chainID)
m.CacheSizeVec.WithLabelValues(chain, label).Set(float64(cacheSize)) m.CacheSizeVec.WithLabelValues(chain, label).Set(float64(cacheSize))
if evicted { if evicted {
...@@ -151,7 +150,7 @@ func (m *Metrics) CacheAdd(chainID *big.Int, label string, cacheSize int, evicte ...@@ -151,7 +150,7 @@ func (m *Metrics) CacheAdd(chainID *big.Int, label string, cacheSize int, evicte
} }
} }
func (m *Metrics) CacheGet(chainID *big.Int, label string, hit bool) { func (m *Metrics) CacheGet(chainID types.ChainID, label string, hit bool) {
chain := chainIDLabel(chainID) chain := chainIDLabel(chainID)
if hit { if hit {
m.CacheGetVec.WithLabelValues(chain, label, "true").Inc() m.CacheGetVec.WithLabelValues(chain, label, "true").Inc()
...@@ -160,14 +159,14 @@ func (m *Metrics) CacheGet(chainID *big.Int, label string, hit bool) { ...@@ -160,14 +159,14 @@ func (m *Metrics) CacheGet(chainID *big.Int, label string, hit bool) {
} }
} }
func (m *Metrics) RecordDBEntryCount(chainID *big.Int, count int64) { func (m *Metrics) RecordDBEntryCount(chainID types.ChainID, count int64) {
m.DBEntryCountVec.WithLabelValues(chainIDLabel(chainID)).Set(float64(count)) m.DBEntryCountVec.WithLabelValues(chainIDLabel(chainID)).Set(float64(count))
} }
func (m *Metrics) RecordDBSearchEntriesRead(chainID *big.Int, count int64) { func (m *Metrics) RecordDBSearchEntriesRead(chainID types.ChainID, count int64) {
m.DBSearchEntriesReadVec.WithLabelValues(chainIDLabel(chainID)).Observe(float64(count)) m.DBSearchEntriesReadVec.WithLabelValues(chainIDLabel(chainID)).Observe(float64(count))
} }
func chainIDLabel(chainID *big.Int) string { func chainIDLabel(chainID types.ChainID) string {
return chainID.Text(10) return chainID.String()
} }
package metrics package metrics
import ( import (
"math/big"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
) )
type noopMetrics struct { type noopMetrics struct {
...@@ -17,8 +16,8 @@ func (*noopMetrics) Document() []opmetrics.DocumentedMetric { return nil } ...@@ -17,8 +16,8 @@ func (*noopMetrics) Document() []opmetrics.DocumentedMetric { return nil }
func (*noopMetrics) RecordInfo(version string) {} func (*noopMetrics) RecordInfo(version string) {}
func (*noopMetrics) RecordUp() {} func (*noopMetrics) RecordUp() {}
func (m *noopMetrics) CacheAdd(_ *big.Int, _ string, _ int, _ bool) {} func (m *noopMetrics) CacheAdd(_ types.ChainID, _ string, _ int, _ bool) {}
func (m *noopMetrics) CacheGet(_ *big.Int, _ string, _ bool) {} func (m *noopMetrics) CacheGet(_ types.ChainID, _ string, _ bool) {}
func (m *noopMetrics) RecordDBEntryCount(_ *big.Int, _ int64) {} func (m *noopMetrics) RecordDBEntryCount(_ types.ChainID, _ int64) {}
func (m *noopMetrics) RecordDBSearchEntriesRead(_ *big.Int, _ int64) {} func (m *noopMetrics) RecordDBSearchEntriesRead(_ types.ChainID, _ int64) {}
...@@ -5,7 +5,6 @@ import ( ...@@ -5,7 +5,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"math/big"
"sync/atomic" "sync/atomic"
"time" "time"
...@@ -64,16 +63,16 @@ func NewSupervisorBackend(ctx context.Context, logger log.Logger, m Metrics, cfg ...@@ -64,16 +63,16 @@ func NewSupervisorBackend(ctx context.Context, logger log.Logger, m Metrics, cfg
}, nil }, nil
} }
func createRpcClient(ctx context.Context, logger log.Logger, rpc string) (client.RPC, *big.Int, error) { func createRpcClient(ctx context.Context, logger log.Logger, rpc string) (client.RPC, types.ChainID, error) {
ethClient, err := dial.DialEthClientWithTimeout(ctx, 10*time.Second, logger, rpc) ethClient, err := dial.DialEthClientWithTimeout(ctx, 10*time.Second, logger, rpc)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to connect to rpc %v: %w", rpc, err) return nil, types.ChainID{}, fmt.Errorf("failed to connect to rpc %v: %w", rpc, err)
} }
chainID, err := ethClient.ChainID(ctx) chainID, err := ethClient.ChainID(ctx)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to load chain id for rpc %v: %w", rpc, err) return nil, types.ChainID{}, fmt.Errorf("failed to load chain id for rpc %v: %w", rpc, err)
} }
return client.NewBaseRPCClient(ethClient.Client()), chainID, nil return client.NewBaseRPCClient(ethClient.Client()), types.ChainIDFromBig(chainID), nil
} }
func (su *SupervisorBackend) Start(ctx context.Context) error { func (su *SupervisorBackend) Start(ctx context.Context) error {
......
package backend package backend
import ( import (
"math/big"
"github.com/ethereum-optimism/optimism/op-service/sources/caching" "github.com/ethereum-optimism/optimism/op-service/sources/caching"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
) )
type Metrics interface { type Metrics interface {
CacheAdd(chainID *big.Int, label string, cacheSize int, evicted bool) CacheAdd(chainID types.ChainID, label string, cacheSize int, evicted bool)
CacheGet(chainID *big.Int, label string, hit bool) CacheGet(chainID types.ChainID, label string, hit bool)
RecordDBEntryCount(chainID *big.Int, count int64) RecordDBEntryCount(chainID types.ChainID, count int64)
RecordDBSearchEntriesRead(chainID *big.Int, count int64) RecordDBSearchEntriesRead(chainID types.ChainID, count int64)
} }
// chainMetrics is an adapter between the metrics API expected by clients that assume there's only a single chain // chainMetrics is an adapter between the metrics API expected by clients that assume there's only a single chain
// and the actual metrics implementation which requires a chain ID to identify the source chain. // and the actual metrics implementation which requires a chain ID to identify the source chain.
type chainMetrics struct { type chainMetrics struct {
chainID *big.Int chainID types.ChainID
delegate Metrics delegate Metrics
} }
func newChainMetrics(chainID *big.Int, delegate Metrics) *chainMetrics { func newChainMetrics(chainID types.ChainID, delegate Metrics) *chainMetrics {
return &chainMetrics{ return &chainMetrics{
chainID: chainID, chainID: chainID,
delegate: delegate, delegate: delegate,
......
...@@ -2,12 +2,13 @@ package backend ...@@ -2,12 +2,13 @@ package backend
import ( import (
"fmt" "fmt"
"math/big"
"os" "os"
"path/filepath" "path/filepath"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
) )
func prepLogDBPath(chainID *big.Int, datadir string) (string, error) { func prepLogDBPath(chainID types.ChainID, datadir string) (string, error) {
dir, err := prepChainDir(chainID, datadir) dir, err := prepChainDir(chainID, datadir)
if err != nil { if err != nil {
return "", err return "", err
...@@ -15,8 +16,8 @@ func prepLogDBPath(chainID *big.Int, datadir string) (string, error) { ...@@ -15,8 +16,8 @@ func prepLogDBPath(chainID *big.Int, datadir string) (string, error) {
return filepath.Join(dir, "log.db"), nil return filepath.Join(dir, "log.db"), nil
} }
func prepChainDir(chainID *big.Int, datadir string) (string, error) { func prepChainDir(chainID types.ChainID, datadir string) (string, error) {
dir := filepath.Join(datadir, chainID.Text(10)) dir := filepath.Join(datadir, chainID.String())
if err := os.MkdirAll(dir, 0755); err != nil { if err := os.MkdirAll(dir, 0755); err != nil {
return "", fmt.Errorf("failed to create chain directory %v: %w", dir, err) return "", fmt.Errorf("failed to create chain directory %v: %w", dir, err)
} }
......
...@@ -6,14 +6,16 @@ import ( ...@@ -6,14 +6,16 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestLogDBPath(t *testing.T) { func TestLogDBPath(t *testing.T) {
base := t.TempDir() base := t.TempDir()
chainIDStr := "42984928492928428424243444" chainIDStr := "42984928492928428424243444"
chainID, ok := new(big.Int).SetString(chainIDStr, 10) chainIDBig, ok := new(big.Int).SetString(chainIDStr, 10)
require.True(t, ok) require.True(t, ok)
chainID := types.ChainIDFromBig(chainIDBig)
expected := filepath.Join(base, "subdir", chainIDStr, "log.db") expected := filepath.Join(base, "subdir", chainIDStr, "log.db")
path, err := prepLogDBPath(chainID, filepath.Join(base, "subdir")) path, err := prepLogDBPath(chainID, filepath.Join(base, "subdir"))
require.NoError(t, err) require.NoError(t, err)
......
...@@ -3,14 +3,14 @@ package source ...@@ -3,14 +3,14 @@ package source
import ( import (
"context" "context"
"fmt" "fmt"
"math/big"
"time" "time"
"github.com/ethereum-optimism/optimism/op-service/client" "github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum-optimism/optimism/op-service/sources/caching" "github.com/ethereum-optimism/optimism/op-service/sources/caching"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
ethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
...@@ -31,7 +31,7 @@ type ChainMonitor struct { ...@@ -31,7 +31,7 @@ type ChainMonitor struct {
headMonitor *HeadMonitor headMonitor *HeadMonitor
} }
func NewChainMonitor(ctx context.Context, logger log.Logger, m Metrics, chainID *big.Int, rpc string, client client.RPC) (*ChainMonitor, error) { func NewChainMonitor(ctx context.Context, logger log.Logger, m Metrics, chainID types.ChainID, rpc string, client client.RPC) (*ChainMonitor, error) {
logger = logger.New("chainID", chainID) logger = logger.New("chainID", chainID)
cl, err := newClient(ctx, logger, m, rpc, client, pollInterval, trustRpc, rpcKind) cl, err := newClient(ctx, logger, m, rpc, client, pollInterval, trustRpc, rpcKind)
if err != nil { if err != nil {
...@@ -67,7 +67,7 @@ type loggingReceiptProcessor struct { ...@@ -67,7 +67,7 @@ type loggingReceiptProcessor struct {
log log.Logger log log.Logger
} }
func (n *loggingReceiptProcessor) ProcessLogs(_ context.Context, block eth.L1BlockRef, rcpts types.Receipts) error { func (n *loggingReceiptProcessor) ProcessLogs(_ context.Context, block eth.L1BlockRef, rcpts ethTypes.Receipts) error {
n.log.Info("Process unsafe block", "block", block, "rcpts", len(rcpts)) n.log.Info("Process unsafe block", "block", block, "rcpts", len(rcpts))
return nil return nil
} }
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"math/big"
"github.com/holiman/uint256" "github.com/holiman/uint256"
...@@ -87,3 +88,17 @@ const ( ...@@ -87,3 +88,17 @@ const (
CrossUnsafe SafetyLevel = "cross-unsafe" CrossUnsafe SafetyLevel = "cross-unsafe"
Unsafe SafetyLevel = "unsafe" Unsafe SafetyLevel = "unsafe"
) )
type ChainID uint256.Int
func ChainIDFromBig(chainID *big.Int) ChainID {
return ChainID(*uint256.MustFromBig(chainID))
}
func ChainIDFromUInt64(i uint64) ChainID {
return ChainID(*uint256.NewInt(i))
}
func (id ChainID) String() string {
return ((*uint256.Int)(&id)).Dec()
}
...@@ -2,6 +2,7 @@ package types ...@@ -2,6 +2,7 @@ package types
import ( import (
"encoding/json" "encoding/json"
"math"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -38,3 +39,23 @@ func FuzzRoundtripIdentifierJSONMarshal(f *testing.F) { ...@@ -38,3 +39,23 @@ func FuzzRoundtripIdentifierJSONMarshal(f *testing.F) {
require.Equal(t, id.ChainID, dec.ChainID) require.Equal(t, id.ChainID, dec.ChainID)
}) })
} }
func TestChainID_String(t *testing.T) {
tests := []struct {
input ChainID
expected string
}{
{ChainIDFromUInt64(0), "0"},
{ChainIDFromUInt64(1), "1"},
{ChainIDFromUInt64(871975192374), "871975192374"},
{ChainIDFromUInt64(math.MaxInt64), "9223372036854775807"},
{ChainID(*uint256.NewInt(math.MaxUint64)), "18446744073709551615"},
{ChainID(*uint256.MustFromDecimal("1844674407370955161618446744073709551616")), "1844674407370955161618446744073709551616"},
}
for _, test := range tests {
test := test
t.Run(test.expected, func(t *testing.T) {
require.Equal(t, test.expected, test.input.String())
})
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment