Skip to content

Commit

Permalink
relayer progress
Browse files Browse the repository at this point in the history
  • Loading branch information
claravanstaden authored and claravanstaden committed Mar 18, 2024
1 parent b66be24 commit a237b1b
Show file tree
Hide file tree
Showing 19 changed files with 322 additions and 43 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/relayer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: contracts

on:
push:
paths:
- "relayer/**"
branches:
- main
pull_request:
paths:
- "relayer/**"

jobs:
build:
runs-on: snowbridge-runner
timeout-minutes: 15
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 2

- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.20

- name: Check out code into the Go module directory
uses: actions/checkout@v3

- name: Get dependencies
run: go get -v -t -d ./...

- name: Run tests
run: go test -v ./...
257 changes: 250 additions & 7 deletions relayer/relays/beacon/header/header_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import (
"github.com/snowfork/snowbridge/relayer/relays/beacon/header/syncer"
"github.com/snowfork/snowbridge/relayer/relays/beacon/header/syncer/api"
"github.com/snowfork/snowbridge/relayer/relays/beacon/state"
"github.com/snowfork/snowbridge/relayer/relays/beacon/store"
"github.com/snowfork/snowbridge/relayer/relays/testutil"
"github.com/stretchr/testify/require"
"testing"
)

// Verifies that the closest checkpoint is populated successfully if it is not populated in the first place.
func TestSyncInterimFinalizedUpdate(t *testing.T) {
func TestSyncInterimFinalizedUpdate_WithDataFromAPI(t *testing.T) {
settings := config.SpecSettings{
SlotsInEpoch: 32,
EpochsPerSyncCommitteePeriod: 256,
Expand All @@ -31,22 +32,103 @@ func TestSyncInterimFinalizedUpdate(t *testing.T) {
require.NoError(t, err)
headerAtSlot4571136, err := testutil.GetHeaderAtSlot(4571136)
require.NoError(t, err)
headerAtSlot4571137, err := testutil.GetHeaderAtSlot(4571137)
require.NoError(t, err)

client.HeadersBySlot = map[uint64]api.BeaconHeader{
4571072: headerAtSlot4571072,
4571136: headerAtSlot4571136,
4571137: headerAtSlot4571137,
}

client.Header = map[common.Hash]api.BeaconHeader{
common.HexToHash("0x5119c1f71943a3eea34ddc48c7fe399d4b66f939350036431847ed0913448749"): headerAtSlot4571072,
}

blockAtSlot4571137, err := testutil.GetBlockAtSlot(4571137)
require.NoError(t, err)

client.BlocksAtSlot = map[uint64]api.BeaconBlockResponse{
4571137: blockAtSlot4571137,
}

beaconStates := map[uint64]bool{
4571072: true,
4571136: true,
}

client.BeaconStates = beaconStates

h := Header{
cache: cache.New(settings.SlotsInEpoch, settings.EpochsPerSyncCommitteePeriod),
writer: &testutil.MockWriter{
LastFinalizedState: state.FinalizedHeader{
BeaconBlockRoot: common.Hash{},
BeaconSlot: 4562496,
InitialCheckpointRoot: common.Hash{},
InitialCheckpointSlot: 0,
},
},
syncer: syncer,
slotsInEpoch: settings.SlotsInEpoch,
epochsPerSyncCommitteePeriod: settings.EpochsPerSyncCommitteePeriod,
}

// Find a checkpoint for a slot that is just out of the on-chain synced finalized header block roots range
err = h.syncInterimFinalizedUpdate(context.Background(), 4570722)
require.NoError(t, err)
}

func TestSyncInterimFinalizedUpdate_WithDataFromStore(t *testing.T) {
settings := config.SpecSettings{
SlotsInEpoch: 32,
EpochsPerSyncCommitteePeriod: 256,
DenebForkEpoch: 0,
}

client := testutil.MockAPI{}

beaconStore := testutil.MockStore{}

syncer := syncer.New(&client, settings, &beaconStore)

//blockAtSlot4563009, err := testutil.GetBlockAtSlot(4563009)
//require.NoError(t, err)
headerAtSlot4571072, err := testutil.GetHeaderAtSlot(4571072)
require.NoError(t, err)
headerAtSlot4571136, err := testutil.GetHeaderAtSlot(4571136)
require.NoError(t, err)
headerAtSlot4571137, err := testutil.GetHeaderAtSlot(4571137)
require.NoError(t, err)

client.HeadersBySlot = map[uint64]api.BeaconHeader{
4571072: headerAtSlot4571072,
4571136: headerAtSlot4571136,
4571137: headerAtSlot4571137,
}

client.Header = map[common.Hash]api.BeaconHeader{
common.HexToHash("0x5119c1f71943a3eea34ddc48c7fe399d4b66f939350036431847ed0913448749"): headerAtSlot4571072,
}

/*client.BlocksAtSlot = map[uint64]api.BeaconBlockResponse{
4563009: blockAtSlot4563009,
}*/
blockAtSlot4571137, err := testutil.GetBlockAtSlot(4571137)
require.NoError(t, err)

client.BlocksAtSlot = map[uint64]api.BeaconBlockResponse{
4571137: blockAtSlot4571137,
}

attestedState, err := testutil.LoadFile("4571136.ssz")
require.NoError(t, err)

finalizedState, err := testutil.LoadFile("4571072.ssz")
require.NoError(t, err)

// Return the beacon state from the stpore
beaconStore.BeaconStateData = store.StoredBeaconData{
AttestedSlot: 4571136,
FinalizedSlot: 4571072,
AttestedBeaconState: attestedState,
FinalizedBeaconState: finalizedState,
}

h := Header{
cache: cache.New(settings.SlotsInEpoch, settings.EpochsPerSyncCommitteePeriod),
Expand All @@ -63,8 +145,169 @@ func TestSyncInterimFinalizedUpdate(t *testing.T) {
epochsPerSyncCommitteePeriod: settings.EpochsPerSyncCommitteePeriod,
}

//4571072
// Find a checkpoint for a slot that is just out of the on-chain synced finalized header block roots range
err = h.syncInterimFinalizedUpdate(context.Background(), 4570722)
require.NoError(t, err)
}

// Test a scenario where there is a usable beacon update in beacon data store, but it is a different attested and
// finalized state that we calculated to use.
func TestSyncInterimFinalizedUpdate_WithDataFromStoreWithDifferentBlocks(t *testing.T) {
settings := config.SpecSettings{
SlotsInEpoch: 32,
EpochsPerSyncCommitteePeriod: 256,
DenebForkEpoch: 0,
}

client := testutil.MockAPI{}

beaconStore := testutil.MockStore{}

syncer := syncer.New(&client, settings, &beaconStore)

headerAtSlot4570752, err := testutil.GetHeaderAtSlot(4570752)
require.NoError(t, err)
headerAtSlot4570816, err := testutil.GetHeaderAtSlot(4570816)
require.NoError(t, err)
headerAtSlot4570817, err := testutil.GetHeaderAtSlot(4570817)
require.NoError(t, err)

client.HeadersBySlot = map[uint64]api.BeaconHeader{
4570752: headerAtSlot4570752,
4570816: headerAtSlot4570816,
4570817: headerAtSlot4570817,
}

client.Header = map[common.Hash]api.BeaconHeader{
common.HexToHash("0x968a372336b4e08a6bbd25e9f31b336d322ede1e5c70763f61d2241ad3d66d36"): headerAtSlot4570752,
}

blockAtSlot4570819, err := testutil.GetBlockAtSlot(4570819)
require.NoError(t, err)

client.BlocksAtSlot = map[uint64]api.BeaconBlockResponse{
4571137: blockAtSlot4570819,
}

attestedState, err := testutil.LoadFile("4570816.ssz")
require.NoError(t, err)

finalizedState, err := testutil.LoadFile("4570752.ssz")
require.NoError(t, err)

// Return the beacon state from the store
beaconStore.BeaconStateData = store.StoredBeaconData{
AttestedSlot: 4570816,
FinalizedSlot: 4570752,
AttestedBeaconState: attestedState,
FinalizedBeaconState: finalizedState,
}

h := Header{
cache: cache.New(settings.SlotsInEpoch, settings.EpochsPerSyncCommitteePeriod),
writer: &testutil.MockWriter{
LastFinalizedState: state.FinalizedHeader{
BeaconBlockRoot: common.Hash{},
BeaconSlot: 4562496,
InitialCheckpointRoot: common.Hash{},
InitialCheckpointSlot: 0,
},
},
syncer: syncer,
slotsInEpoch: settings.SlotsInEpoch,
epochsPerSyncCommitteePeriod: settings.EpochsPerSyncCommitteePeriod,
}

// Find a checkpoint for a slot that is just out of the on-chain synced finalized header block roots range
err = h.syncInterimFinalizedUpdate(context.Background(), 4570722)
require.NoError(t, err)
}

// Test a scenario where we can get beacon data from the API, but cannot download the beacon state from the API
// or store.
func TestSyncInterimFinalizedUpdate_BeaconStateNotAvailableInAPIAndStore(t *testing.T) {
settings := config.SpecSettings{
SlotsInEpoch: 32,
EpochsPerSyncCommitteePeriod: 256,
DenebForkEpoch: 0,
}

client := testutil.MockAPI{}

beaconStore := testutil.MockStore{}

syncer := syncer.New(&client, settings, &beaconStore)

headerAtSlot4571072, err := testutil.GetHeaderAtSlot(4571072)
require.NoError(t, err)
headerAtSlot4571136, err := testutil.GetHeaderAtSlot(4571136)
require.NoError(t, err)
headerAtSlot4571137, err := testutil.GetHeaderAtSlot(4571137)
require.NoError(t, err)

client.HeadersBySlot = map[uint64]api.BeaconHeader{
4571072: headerAtSlot4571072,
4571136: headerAtSlot4571136,
4571137: headerAtSlot4571137,
}

h := Header{
cache: cache.New(settings.SlotsInEpoch, settings.EpochsPerSyncCommitteePeriod),
writer: &testutil.MockWriter{
LastFinalizedState: state.FinalizedHeader{
BeaconBlockRoot: common.Hash{},
BeaconSlot: 4562496,
InitialCheckpointRoot: common.Hash{},
InitialCheckpointSlot: 0,
},
},
syncer: syncer,
slotsInEpoch: settings.SlotsInEpoch,
epochsPerSyncCommitteePeriod: settings.EpochsPerSyncCommitteePeriod,
}

// Find a checkpoint for a slot that is just out of the on-chain synced finalized header block roots range
err = h.syncInterimFinalizedUpdate(context.Background(), 4570722)
require.Error(t, err)
}

func TestSyncInterimFinalizedUpdate_NoValidBlocksFound(t *testing.T) {
settings := config.SpecSettings{
SlotsInEpoch: 32,
EpochsPerSyncCommitteePeriod: 256,
DenebForkEpoch: 0,
}

client := testutil.MockAPI{}

beaconStore := testutil.MockStore{}

syncer := syncer.New(&client, settings, &beaconStore)

headerAtSlot4571072, err := testutil.GetHeaderAtSlot(4571072)
require.NoError(t, err)

// Only 1 valid header found
client.HeadersBySlot = map[uint64]api.BeaconHeader{
4571072: headerAtSlot4571072,
}

h := Header{
cache: cache.New(settings.SlotsInEpoch, settings.EpochsPerSyncCommitteePeriod),
writer: &testutil.MockWriter{
LastFinalizedState: state.FinalizedHeader{
BeaconBlockRoot: common.Hash{},
BeaconSlot: 4562496,
InitialCheckpointRoot: common.Hash{},
InitialCheckpointSlot: 0,
},
},
syncer: syncer,
slotsInEpoch: settings.SlotsInEpoch,
epochsPerSyncCommitteePeriod: settings.EpochsPerSyncCommitteePeriod,
}

// Find a checkpoint for a slot that is just out of the on-chain synced finalized header block roots range
err = h.syncInterimFinalizedUpdate(context.Background(), 4570722)
require.Errorf(t, err, "cannot find blocks at boundaries")
}
12 changes: 8 additions & 4 deletions relayer/relays/beacon/header/syncer/syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func New(client api.BeaconAPI, setting config.SpecSettings, store store.BeaconSt
}

type finalizedUpdateContainer struct {
AttestedSlot uint64
AttestedState state.BeaconState
FinalizedState state.BeaconState
FinalizedHeader api.BeaconHeader
Expand Down Expand Up @@ -544,6 +545,9 @@ func (s *Syncer) GetFinalizedUpdateAtAttestedSlot(attestedSlot uint64, lastSynce
if err != nil {
return update, fmt.Errorf("fetch beacon data from api and data store failure: %w", err)
}

// The datastore may not have found the attested slot we wanted, but provided another valid one
attestedSlot = data.AttestedSlot
}

// Finalized header proof
Expand Down Expand Up @@ -660,6 +664,7 @@ func (s *Syncer) getBeaconDataFromClient(attestedSlot uint64) (finalizedUpdateCo
var response finalizedUpdateContainer
var err error

response.AttestedSlot = attestedSlot
// Get the beacon data first since it is mostly likely to fail
response.AttestedState, err = s.getBeaconStateAtSlot(attestedSlot)
if err != nil {
Expand All @@ -668,15 +673,12 @@ func (s *Syncer) getBeaconDataFromClient(attestedSlot uint64) (finalizedUpdateCo

response.FinalizedCheckPoint = *response.AttestedState.GetFinalizedCheckpoint()

log.WithField("ROOT", common.BytesToHash(response.FinalizedCheckPoint.Root)).Info("root is")
// Get the finalized header at the given slot state
response.FinalizedHeader, err = s.Client.GetHeader(common.BytesToHash(response.FinalizedCheckPoint.Root))
if err != nil {
return response, fmt.Errorf("fetch header: %w", err)
}

log.WithField("SLOT", response.FinalizedHeader.Slot).Info("slot is is")

response.FinalizedState, err = s.getBeaconStateAtSlot(response.FinalizedHeader.Slot)
if err != nil {
return response, fmt.Errorf("fetch attested header beacon state at slot %d: %w", attestedSlot, err)
Expand All @@ -691,11 +693,13 @@ func (s *Syncer) getBeaconDataFromStore(originalSlot uint64) (finalizedUpdateCon
var response finalizedUpdateContainer
var err error

data, err := s.store.FindBeaconStateWithinSyncPeriodRange(originalSlot, s.setting.SlotsInEpoch*s.setting.EpochsPerSyncCommitteePeriod)
checkpointSlot := s.CalculateNextCheckpointSlot(originalSlot)
data, err := s.store.FindBeaconStateWithinSyncPeriodRange(originalSlot, checkpointSlot)
if err != nil {
return finalizedUpdateContainer{}, err
}

response.AttestedSlot = data.AttestedSlot
response.AttestedState, err = s.unmarshalBeaconState(data.AttestedSlot, data.AttestedBeaconState)
if err != nil {
return finalizedUpdateContainer{}, err
Expand Down
Loading

0 comments on commit a237b1b

Please sign in to comment.