Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion commitlint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = {
'type-enum': [
2,
'always',
['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore', 'revert', 'perf']
['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore', 'revert', 'perf', 'qol']
],
'footer-max-line-length': [2, 'always', Infinity],
'footer-leading-blank': [0]
Expand Down
28 changes: 28 additions & 0 deletions pkg/postgres/migrations/202503182109_sidecarVersions/up.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package _202503182109_sidecarVersions

import (
"database/sql"
"github.com/Layr-Labs/sidecar/internal/config"
"gorm.io/gorm"
)

type Migration struct {
}

func (m *Migration) Up(db *sql.DB, grm *gorm.DB, cfg *config.Config) error {

query := `
create table if not exists sidecar_versions (
id serial primary key,
version text not null,
state_root_block_launched_at bigint not null,
created_at timestamp with time zone default current_timestamp
)
`
res := grm.Exec(query)
return res.Error
}

func (m *Migration) GetName() string {
return "202503182109_sidecarVersions"
}
2 changes: 2 additions & 0 deletions pkg/postgres/migrations/migrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package migrations
import (
"database/sql"
"fmt"
_202503182109_sidecarVersions "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202503182109_sidecarVersions"
"time"

_202501241111_addIndexesForRpcFunctions "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202501241111_addIndexesForRpcFunctions"
Expand Down Expand Up @@ -179,6 +180,7 @@ func (m *Migrator) MigrateAll() error {
&_202503042014_stakerOperatorIndex.Migration{},
&_202502180836_snapshotUniqueConstraints.Migration{},
&_202503051449_addContractTypeColumn.Migration{},
&_202503182109_sidecarVersions.Migration{},
}

for _, migration := range migrations {
Expand Down
90 changes: 90 additions & 0 deletions pkg/runtime/runtime.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package runtime

import (
"errors"
"github.com/Layr-Labs/sidecar/internal/config"
"go.uber.org/zap"
"golang.org/x/mod/semver"
"gorm.io/gorm"
)

type SidecarRuntime struct {
grm *gorm.DB
globalConfig *config.Config
logger *zap.Logger
}

func NewSidecarRuntime(grm *gorm.DB, globalConfig *config.Config, l *zap.Logger) *SidecarRuntime {
return &SidecarRuntime{
grm: grm,
globalConfig: globalConfig,
logger: l,
}
}

func (s *SidecarRuntime) GetRecentlyLaunchedSidecarVersion() (*SidecarVersions, error) {
var sv SidecarVersions
res := s.grm.Model(&SidecarVersions{}).Order("id desc").First(&sv)
if res.Error != nil {
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, res.Error
}
return &sv, nil
}

func (s *SidecarRuntime) InsertRecentlyLaunchedSidecarVersion(version string, stateRootBlockLaunchedAt uint64) (*SidecarVersions, error) {
sv := &SidecarVersions{
Version: version,
StateRootBlockLaunchedAt: stateRootBlockLaunchedAt,
}
res := s.grm.Model(&SidecarVersions{}).Create(&sv)
if res.Error != nil {
return nil, res.Error
}
return sv, nil
}

func (s *SidecarRuntime) ValidateAndUpdateSidecarVersion(version string) error {
if version == "" {
return errors.New("empty version")
}

if version == "unknown" {
s.logger.Sugar().Warnw("runtime version is unknown, not inserting into sidecar_versions", zap.String("version", version))
return nil
}

lastSeenVersion, err := s.GetRecentlyLaunchedSidecarVersion()
if err != nil {
return err
}

if lastSeenVersion != nil {
// new version should be >= last seen version
cmp := semver.Compare(version, lastSeenVersion.Version)
if cmp < 0 {
return errors.New("runtime version is older than last seen version")
}
if cmp == 0 {
s.logger.Sugar().Infow("runtime version is the same as the last seen version", zap.String("version", version))
return nil
}
}

var blockNumber uint64
res := s.grm.Raw("SELECT MAX(eth_block_number) FROM state_roots").Scan(&blockNumber)
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
return res.Error
}
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
blockNumber = s.globalConfig.GetGenesisBlockNumber()
}

res = s.grm.Model(&SidecarVersions{}).Create(&SidecarVersions{
Version: version,
StateRootBlockLaunchedAt: blockNumber,
})
return res.Error
}
154 changes: 154 additions & 0 deletions pkg/runtime/runtime_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package runtime

import (
"fmt"
"github.com/Layr-Labs/sidecar/internal/config"
"github.com/Layr-Labs/sidecar/internal/logger"
"github.com/Layr-Labs/sidecar/internal/tests"
"github.com/Layr-Labs/sidecar/pkg/eigenState/stateManager"
"github.com/Layr-Labs/sidecar/pkg/postgres"
"github.com/Layr-Labs/sidecar/pkg/storage"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"gorm.io/gorm"
"os"
"testing"
)

func setup() (
string,
*gorm.DB,
*zap.Logger,
*config.Config,
error,
) {
cfg := config.NewConfig()
cfg.Debug = os.Getenv(config.Debug) == "true"
cfg.DatabaseConfig = *tests.GetDbConfigFromEnv()

l, _ := logger.NewLogger(&logger.LoggerConfig{Debug: cfg.Debug})

dbname, _, grm, err := postgres.GetTestPostgresDatabase(cfg.DatabaseConfig, cfg, l)
if err != nil {
return dbname, nil, nil, nil, err
}

return dbname, grm, l, cfg, nil
}

func insertStateRoot(grm *gorm.DB, blockNumber int) error {
root, _ := uuid.NewRandom()
stateRoot := &stateManager.StateRoot{
EthBlockNumber: uint64(blockNumber),
EthBlockHash: fmt.Sprintf("0xSomething-%d", blockNumber),
StateRoot: root.String(),
}

block := &storage.Block{
Number: uint64(blockNumber),
Hash: "something",
}

res := grm.Model(&storage.Block{}).Create(&block)
if res.Error != nil {
return res.Error
}

res = grm.Model(&stateManager.StateRoot{}).Create(&stateRoot)
return res.Error
}

func Test_SidecarRuntime(t *testing.T) {
dbName, grm, l, cfg, err := setup()
if err != nil {
t.Fatal(err)
}

rtime := NewSidecarRuntime(grm, cfg, l)

t.Run("Should insert a new version when there isnt one", func(t *testing.T) {
version := "v1.0.0"
blockNumber := 1

err = insertStateRoot(grm, blockNumber)
assert.Nil(t, err)

err := rtime.ValidateAndUpdateSidecarVersion(version)
assert.Nil(t, err)
})
t.Run("Should fail due to the version being older", func(t *testing.T) {
version := "v0.1.0"
blockNumber := 2

err = insertStateRoot(grm, blockNumber)
assert.Nil(t, err)

err := rtime.ValidateAndUpdateSidecarVersion(version)
assert.NotNil(t, err)
})
t.Run("Should upgrade the version to a minor release", func(t *testing.T) {
version := "v1.1.0"
blockNumber := 3

err = insertStateRoot(grm, blockNumber)
assert.Nil(t, err)

err := rtime.ValidateAndUpdateSidecarVersion(version)
assert.Nil(t, err)
})
t.Run("Should upgrade the version to a patch release", func(t *testing.T) {
version := "v1.1.1"
blockNumber := 4

err = insertStateRoot(grm, blockNumber)
assert.Nil(t, err)

err := rtime.ValidateAndUpdateSidecarVersion(version)
assert.Nil(t, err)
})
t.Run("Should upgrade the version to a major release", func(t *testing.T) {
version := "v2.0.0"
blockNumber := 5

err = insertStateRoot(grm, blockNumber)
assert.Nil(t, err)

err := rtime.ValidateAndUpdateSidecarVersion(version)
assert.Nil(t, err)
})
t.Run("Should upgrade the version with a commit suffix", func(t *testing.T) {
version := "v2.0.0+abc123"
blockNumber := 6

err = insertStateRoot(grm, blockNumber)
assert.Nil(t, err)

err := rtime.ValidateAndUpdateSidecarVersion(version)
assert.Nil(t, err)
})
t.Run("Should upgrade the version with an RC suffix", func(t *testing.T) {
version := "v2.0.1-rc.1"
blockNumber := 7

err = insertStateRoot(grm, blockNumber)
assert.Nil(t, err)

err := rtime.ValidateAndUpdateSidecarVersion(version)
assert.Nil(t, err)
})
t.Run("Should upgrade the version with an RC suffix and commit suffix", func(t *testing.T) {
version := "v2.0.1-rc.1+abc123"
blockNumber := 8

err = insertStateRoot(grm, blockNumber)
assert.Nil(t, err)

err := rtime.ValidateAndUpdateSidecarVersion(version)
assert.Nil(t, err)
})

t.Cleanup(func() {
postgres.TeardownTestDatabase(dbName, cfg, grm, l)
})
}
10 changes: 10 additions & 0 deletions pkg/runtime/tables.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package runtime

import "time"

type SidecarVersions struct {
Id uint64 `gorm:"type:serial"`
Version string
StateRootBlockLaunchedAt uint64
CreatedAt *time.Time
}
11 changes: 11 additions & 0 deletions pkg/sidecar/sidecar.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package sidecar
import (
"context"
"github.com/Layr-Labs/sidecar/internal/config"
"github.com/Layr-Labs/sidecar/internal/version"
"github.com/Layr-Labs/sidecar/pkg/clients/ethereum"
"github.com/Layr-Labs/sidecar/pkg/eigenState/stateManager"
"github.com/Layr-Labs/sidecar/pkg/metaState/metaStateManager"
"github.com/Layr-Labs/sidecar/pkg/pipeline"
"github.com/Layr-Labs/sidecar/pkg/proofs"
"github.com/Layr-Labs/sidecar/pkg/rewards"
"github.com/Layr-Labs/sidecar/pkg/rewardsCalculatorQueue"
"github.com/Layr-Labs/sidecar/pkg/runtime"
"github.com/Layr-Labs/sidecar/pkg/storage"
"go.uber.org/zap"
"sync/atomic"
Expand All @@ -33,6 +35,7 @@ type Sidecar struct {
RewardProofs *proofs.RewardsProofsStore
ShutdownChan chan bool
shouldShutdown *atomic.Bool
sidecarRuntime *runtime.SidecarRuntime
}

func NewSidecar(
Expand All @@ -50,6 +53,9 @@ func NewSidecar(
) *Sidecar {
shouldShutdown := &atomic.Bool{}
shouldShutdown.Store(false)

rt := runtime.NewSidecarRuntime(em.DB, gCfg, l)

return &Sidecar{
Logger: l,
Config: cfg,
Expand All @@ -64,12 +70,17 @@ func NewSidecar(
StateManager: em,
ShutdownChan: make(chan bool),
shouldShutdown: shouldShutdown,
sidecarRuntime: rt,
}
}

func (s *Sidecar) Start(ctx context.Context) {
s.Logger.Info("Starting sidecar")

if err := s.sidecarRuntime.ValidateAndUpdateSidecarVersion(version.GetVersion()); err != nil {
s.Logger.Sugar().Fatalw("Failed to validate and update sidecar version", zap.Error(err))
}

// Spin up a goroutine that listens on a channel for a shutdown signal.
// When the signal is received, set shouldShutdown to true and return.
go func() {
Expand Down