Skip to content

Commit

Permalink
feat: store rotation period (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
mpetrun5 authored Jan 8, 2024
1 parent efb6f65 commit 19a54c0
Show file tree
Hide file tree
Showing 14 changed files with 447 additions and 55 deletions.
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ test:
./scripts/tests.sh

genmocks:
mockgen -source=./chains/evm/listener/events/handlers/deposit.go -destination=./mock/deposit.go -package mock
mockgen -source=./chains/evm/listener/events/handlers/rotate.go -destination=./mock/rotate.go -package mock
mockgen -source=./chains/evm/listener/handlers/step.go -destination=./mock/step.go -package mock
mockgen -source=./chains/evm/listener/handlers/rotate.go -destination=./mock/rotate.go -package mock
mockgen -source=./chains/evm/listener/listener.go -destination=./mock/listener.go -package mock
mockgen -source=./chains/evm/executor/executor.go -destination=./mock/executor.go -package mock
mockgen -source=./chains/evm/prover/prover.go -destination=./mock/prover.go -package mock

mockgen -destination=./mock/store.go -package mock github.com/sygmaprotocol/sygma-core/store KeyValueReaderWriter

PLATFORMS := linux/amd64 darwin/amd64 darwin/arm64 linux/arm
temp = $(subst /, ,$@)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package handlers

import (
"context"
"math/big"

"github.com/attestantio/go-eth2-client/api"
apiv1 "github.com/attestantio/go-eth2-client/api/v1"
Expand All @@ -19,32 +20,47 @@ type SyncCommitteeFetcher interface {
SyncCommittee(ctx context.Context, opts *api.SyncCommitteeOpts) (*api.Response[*apiv1.SyncCommittee], error)
}

type PeriodStorer interface {
Period(domainID uint8) (*big.Int, error)
StorePeriod(domainID uint8, period *big.Int) error
}

type RotateHandler struct {
domainID uint8
domains []uint8
msgChan chan []*message.Message

prover Prover
prover Prover
periodStorer PeriodStorer

syncCommitteeFetcher SyncCommitteeFetcher
currentSyncCommittee *apiv1.SyncCommittee
committeePeriodLength uint64
}

func NewRotateHandler(msgChan chan []*message.Message, syncCommitteeFetcher SyncCommitteeFetcher, prover Prover, domainID uint8, domains []uint8) *RotateHandler {
func NewRotateHandler(msgChan chan []*message.Message, periodStorer PeriodStorer, prover Prover, domainID uint8, domains []uint8, committeePeriodLenght uint64) *RotateHandler {
return &RotateHandler{
syncCommitteeFetcher: syncCommitteeFetcher,
prover: prover,
domainID: domainID,
domains: domains,
msgChan: msgChan,
currentSyncCommittee: &apiv1.SyncCommittee{},
prover: prover,
periodStorer: periodStorer,
domainID: domainID,
domains: domains,
msgChan: msgChan,
committeePeriodLength: committeePeriodLenght,
}
}

// HandleEvents checks if there is a new sync committee and sends a rotate message
// if there is
// HandleEvents checks if the current period is newer than the last stored
// period and rotates the committee if it is
func (h *RotateHandler) HandleEvents(checkpoint *apiv1.Finality) error {
args, err := h.prover.RotateArgs(uint64(checkpoint.Finalized.Epoch))
latestPeriod, err := h.periodStorer.Period(h.domainID)
if err != nil {
return err
}
currentPeriod := uint64(checkpoint.Finalized.Epoch) / h.committeePeriodLength
if currentPeriod <= latestPeriod.Uint64() {
return nil
}

targetPeriod := latestPeriod.Add(latestPeriod, big.NewInt(1))
args, err := h.prover.RotateArgs(targetPeriod.Uint64())
if err != nil {
return err
}
Expand All @@ -61,17 +77,7 @@ func (h *RotateHandler) HandleEvents(checkpoint *apiv1.Finality) error {
Spec: args.Spec,
}

syncCommittee, err := h.syncCommitteeFetcher.SyncCommittee(context.Background(), &api.SyncCommitteeOpts{
State: "finalized",
})
if err != nil {
return err
}
if syncCommittee.Data.String() == h.currentSyncCommittee.String() {
return nil
}

log.Info().Uint8("domainID", h.domainID).Msgf("Rotating committee")
log.Info().Uint8("domainID", h.domainID).Uint64("period", targetPeriod.Uint64()).Msgf("Rotating committee")

rotateProof, err := h.prover.RotateProof(args)
if err != nil {
Expand All @@ -98,5 +104,5 @@ func (h *RotateHandler) HandleEvents(checkpoint *apiv1.Finality) error {
}
}

return nil
return h.periodStorer.StorePeriod(h.domainID, targetPeriod)
}
116 changes: 116 additions & 0 deletions chains/evm/listener/handlers/rotate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// The Licensed Work is (c) 2023 Sygma
// SPDX-License-Identifier: LGPL-3.0-only

package handlers_test

import (
"fmt"
"math/big"
"testing"

apiv1 "github.com/attestantio/go-eth2-client/api/v1"
"github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/stretchr/testify/suite"
"github.com/sygmaprotocol/spectre-node/chains/evm/listener/handlers"
evmMessage "github.com/sygmaprotocol/spectre-node/chains/evm/message"
"github.com/sygmaprotocol/spectre-node/chains/evm/prover"
"github.com/sygmaprotocol/spectre-node/mock"
"github.com/sygmaprotocol/sygma-core/relayer/message"
consensus "github.com/umbracle/go-eth-consensus"
"go.uber.org/mock/gomock"
)

func readFromChannel(msgChan chan []*message.Message) ([]*message.Message, error) {
select {
case msgs := <-msgChan:
return msgs, nil
default:
return make([]*message.Message, 0), fmt.Errorf("no message sent")
}
}

type RotateHandlerTestSuite struct {
suite.Suite

handler *handlers.RotateHandler

msgChan chan []*message.Message
mockProver *mock.MockProver
mockPeriodStorer *mock.MockPeriodStorer
}

func TestRunRotateTestSuite(t *testing.T) {
suite.Run(t, new(RotateHandlerTestSuite))
}

func (s *RotateHandlerTestSuite) SetupTest() {
ctrl := gomock.NewController(s.T())
s.mockProver = mock.NewMockProver(ctrl)
s.mockPeriodStorer = mock.NewMockPeriodStorer(ctrl)
s.msgChan = make(chan []*message.Message, 2)
s.handler = handlers.NewRotateHandler(
s.msgChan,
s.mockPeriodStorer,
s.mockProver,
1,
[]uint8{2, 3},
256,
)
}

func (s *RotateHandlerTestSuite) Test_HandleEvents_PeriodFetchingFails() {
s.mockPeriodStorer.EXPECT().Period(uint8(1)).Return(nil, fmt.Errorf("error"))

err := s.handler.HandleEvents(&apiv1.Finality{})
s.NotNil(err)

_, err = readFromChannel(s.msgChan)
s.NotNil(err)
}

func (s *RotateHandlerTestSuite) Test_HandleEvents_CurrentPeriodOlderThanLatest() {
s.mockPeriodStorer.EXPECT().Period(uint8(1)).Return(big.NewInt(2), nil)

err := s.handler.HandleEvents(&apiv1.Finality{
Finalized: &phase0.Checkpoint{
Epoch: phase0.Epoch(300),
},
})
s.Nil(err)

_, err = readFromChannel(s.msgChan)
s.NotNil(err)
}

func (s *RotateHandlerTestSuite) Test_HandleEvents_ValidPeriod() {
s.mockPeriodStorer.EXPECT().Period(uint8(1)).Return(big.NewInt(2), nil)
s.mockPeriodStorer.EXPECT().StorePeriod(uint8(1), big.NewInt(3)).Return(nil)
s.mockProver.EXPECT().RotateArgs(uint64(3)).Return(&prover.RotateArgs{
Update: &consensus.LightClientUpdateCapella{},
Domain: phase0.Domain{},
Spec: "mainnet",
Pubkeys: [512][48]byte{},
}, nil)
s.mockProver.EXPECT().RotateProof(gomock.Any()).Return(&prover.EvmProof[evmMessage.RotateInput]{
Proof: []byte{},
Input: evmMessage.RotateInput{},
}, nil)
s.mockProver.EXPECT().StepProof(gomock.Any()).Return(&prover.EvmProof[evmMessage.SyncStepInput]{
Proof: []byte{},
Input: evmMessage.SyncStepInput{},
}, nil)

err := s.handler.HandleEvents(&apiv1.Finality{
Finalized: &phase0.Checkpoint{
Epoch: phase0.Epoch(1500),
},
})
s.Nil(err)

msg1, err := readFromChannel(s.msgChan)
s.Nil(err)
s.Equal(len(msg1), 1)
msg2, err := readFromChannel(s.msgChan)
s.Nil(err)
s.Equal(len(msg2), 1)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type BlockFetcher interface {
SignedBeaconBlock(ctx context.Context, opts *api.SignedBeaconBlockOpts) (*api.Response[*spec.VersionedSignedBeaconBlock], error)
}

type DepositEventHandler struct {
type StepEventHandler struct {
msgChan chan []*message.Message

eventFetcher EventFetcher
Expand All @@ -50,7 +50,7 @@ type DepositEventHandler struct {
routerAddress common.Address
}

func NewDepositEventHandler(
func NewStepEventHandler(
msgChan chan []*message.Message,
eventFetcher EventFetcher,
blockFetcher BlockFetcher,
Expand All @@ -59,9 +59,9 @@ func NewDepositEventHandler(
domainID uint8,
domains []uint8,
blockInterval uint64,
) *DepositEventHandler {
) *StepEventHandler {
routerABI, _ := ethereumABI.JSON(strings.NewReader(abi.RouterABI))
return &DepositEventHandler{
return &StepEventHandler{
eventFetcher: eventFetcher,
blockFetcher: blockFetcher,
prover: prover,
Expand All @@ -74,9 +74,8 @@ func NewDepositEventHandler(
}
}

// HandleEvents fetches deposit events and if deposits exists, submits a step message
// to be executed on the destination network
func (h *DepositEventHandler) HandleEvents(checkpoint *apiv1.Finality) error {
// HandleEvents executes the step for the latest finality checkpoint
func (h *StepEventHandler) HandleEvents(checkpoint *apiv1.Finality) error {
args, err := h.prover.StepArgs()
if err != nil {
return err
Expand Down
16 changes: 6 additions & 10 deletions chains/evm/prover/prover.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,23 +61,20 @@ type Prover struct {
beaconClient BeaconClient
proverClient ProverClient

spec Spec
committeePeriodLength uint64
spec Spec
}

func NewProver(
proverClient ProverClient,
beaconClient BeaconClient,
lightClient LightClient,
spec Spec,
committeePeriodLength uint64,
) *Prover {
return &Prover{
proverClient: proverClient,
spec: spec,
committeePeriodLength: committeePeriodLength,
beaconClient: beaconClient,
lightClient: lightClient,
proverClient: proverClient,
spec: spec,
beaconClient: beaconClient,
lightClient: lightClient,
}
}

Expand Down Expand Up @@ -209,8 +206,7 @@ func (p *Prover) StepArgs() (*StepArgs, error) {
}, nil
}

func (p *Prover) RotateArgs(epoch uint64) (*RotateArgs, error) {
period := epoch / p.committeePeriodLength
func (p *Prover) RotateArgs(period uint64) (*RotateArgs, error) {
updates, err := p.lightClient.Updates(period)
if err != nil {
return nil, err
Expand Down
5 changes: 5 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const PREFIX = "SPECTRE"
type Config struct {
Observability *Observability `env_config:"observability"`
Prover *Prover `env_config:"prover"`
Store *Store `env_config:"store"`
Domains map[uint8]string `required:"true"`
}

Expand All @@ -23,6 +24,10 @@ type Prover struct {
URL string `required:"true"`
}

type Store struct {
Path string `default:"./lvldbdata"`
}

// LoadConfig loads config from the environment and validates the fields
func LoadConfig() (*Config, error) {
var c Config
Expand Down
7 changes: 7 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ func (s *ConfigTestSuite) Test_LoadConfig_DefaultValues() {
Prover: &config.Prover{
URL: "http://prover.com",
},
Store: &config.Store{
Path: "./lvldbdata",
},
Domains: domains,
})
}
Expand All @@ -56,6 +59,7 @@ func (s *ConfigTestSuite) Test_LoadEVMConfig_SuccessfulLoad() {
os.Setenv("SPECTRE_OBSERVABILITY_LOG_LEVEL", "info")
os.Setenv("SPECTRE_OBSERVABILITY_LOG_FILE", "out2.log")
os.Setenv("SPECTRE_OBSERVABILITY_HEALTH_PORT", "9003")
os.Setenv("SPECTRE_STORE_PATH", "./custom_path")
os.Setenv("SPECTRE_PROVER_URL", "http://prover.com")
os.Setenv("SPECTRE_DOMAINS", "1:evm,2:evm")

Expand All @@ -74,6 +78,9 @@ func (s *ConfigTestSuite) Test_LoadEVMConfig_SuccessfulLoad() {
Prover: &config.Prover{
URL: "http://prover.com",
},
Store: &config.Store{
Path: "./custom_path",
},
Domains: domains,
})
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/rs/zerolog v1.31.0
github.com/stretchr/testify v1.8.4
github.com/sygmaprotocol/sygma-core v0.0.0-20231023115554-62219e098d0d
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a
github.com/umbracle/go-eth-consensus v0.1.3-0.20230605085523-929b6624372a
github.com/ybbus/jsonrpc/v3 v3.1.5
go.uber.org/mock v0.3.0
Expand Down Expand Up @@ -59,7 +60,6 @@ require (
github.com/r3labs/sse/v2 v2.10.0 // indirect
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
github.com/supranational/blst v0.3.11 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
go.opentelemetry.io/otel v1.16.0 // indirect
Expand Down
Loading

0 comments on commit 19a54c0

Please sign in to comment.