Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Limit finalized header gap #1156

Merged
merged 35 commits into from
Mar 21, 2024
Merged

Limit finalized header gap #1156

merged 35 commits into from
Mar 21, 2024

Conversation

claravanstaden
Copy link
Contributor

@claravanstaden claravanstaden commented Mar 13, 2024

Adds the following:

  • an interim finalized update between sync committee updates if the headers are more than 1982 slots apart
  • a MySQLite datastore to store metadata and ssz blobs on disk. Prunes after X number of states.
  • command to store ssz states on disk - should be configured to run with cron twice a day or so
  • extract interfaces so that we can easier write tests and get coverage up for the relayer. While the relayer is not part of our on-chain code, it is vital to our bridge and can stall the bridge if buggy

claravanstaden added 9 commits March 14, 2024 12:26
# Conflicts:
#	relayer/relays/beacon/header/header.go
#	relayer/relays/beacon/state/beacon_deneb_encoding.go
#	relayer/relays/beacon/state/beacon_encoding.go
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming we have configured a fallback client which can fetch the beacon state for arbitrary slots. Do we still need to store the beacon state in a local db here? Is that for another kind of fallback?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have removed the fallback client since I encountered similar issues with Prysm that we are having with Lodestar. I think fundamentally relying on the beacon node and its config is risky. I think the beacon store adds a layer of reassurance that we'll be able to get the proofs we need if our bridge goes offline for an extended period.

@claravanstaden claravanstaden marked this pull request as ready for review March 18, 2024 14:23
Comment on lines 19 to 29
BatchCall(ctx context.Context, extrinsic string, calls []interface{}) error
WriteToParachainAndRateLimit(ctx context.Context, extrinsicName string, payload ...interface{}) error
WriteToParachainAndWatch(ctx context.Context, extrinsicName string, payload ...interface{}) error
GetLastFinalizedHeaderState() (state.FinalizedHeader, error)
GetFinalizedStateByStorageKey(key string) (scale.BeaconState, error)
GetLastBasicChannelBlockNumber() (uint64, error)
GetLastBasicChannelNonceByAddress(address common.Address) (uint64, error)
GetFinalizedHeaderStateByBlockRoot(blockRoot types.H256) (state.FinalizedHeader, error)
GetCompactExecutionHeaderStateByBlockHash(blockHash types.H256) (state.CompactExecutionHeaderState, error)
GetLastFinalizedStateIndex() (types.U32, error)
GetFinalizedBeaconRootByIndex(index uint32) (types.H256, error)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract interfaces that we can mock out in tests.

Comment on lines +250 to +348
func (wr *ParachainWriter) writeToParachain(ctx context.Context, extrinsicName string, payload ...interface{}) (*author.ExtrinsicStatusSubscription, error) {
extI, err := wr.prepExtrinstic(ctx, extrinsicName, payload...)
if err != nil {
return nil, err
}

sub, err := wr.conn.API().RPC.Author.SubmitAndWatchExtrinsic(*extI)
if err != nil {
return nil, err
}

return sub, nil
}

func (wr *ParachainWriter) queryAccountNonce() (uint32, error) {
key, err := types.CreateStorageKey(wr.conn.Metadata(), "System", "Account", wr.conn.Keypair().PublicKey, nil)
if err != nil {
return 0, err
}

var accountInfo types.AccountInfo
ok, err := wr.conn.API().RPC.State.GetStorageLatest(key, &accountInfo)
if err != nil {
return 0, err
}
if !ok {
return 0, fmt.Errorf("no account info found for %s", wr.conn.Keypair().URI)
}

return uint32(accountInfo.Nonce), nil
}

func (wr *ParachainWriter) prepExtrinstic(ctx context.Context, extrinsicName string, payload ...interface{}) (*types.Extrinsic, error) {
meta, err := wr.conn.API().RPC.State.GetMetadataLatest()
if err != nil {
return nil, err
}

c, err := types.NewCall(meta, extrinsicName, payload...)
if err != nil {
return nil, err
}

latestHash, err := wr.conn.API().RPC.Chain.GetFinalizedHead()
if err != nil {
return nil, err
}

latestBlock, err := wr.conn.API().RPC.Chain.GetBlock(latestHash)
if err != nil {
return nil, err
}

ext := types.NewExtrinsic(c)
era := NewMortalEra(uint64(latestBlock.Block.Header.Number))

genesisHash, err := wr.conn.API().RPC.Chain.GetBlockHash(0)
if err != nil {
return nil, err
}

rv, err := wr.conn.API().RPC.State.GetRuntimeVersionLatest()
if err != nil {
return nil, err
}

o := types.SignatureOptions{
BlockHash: latestHash,
Era: era,
GenesisHash: genesisHash,
Nonce: types.NewUCompactFromUInt(uint64(wr.nonce)),
SpecVersion: rv.SpecVersion,
Tip: types.NewUCompactFromUInt(0),
TransactionVersion: rv.TransactionVersion,
}

extI := ext

err = extI.Sign(*wr.conn.Keypair(), o)
if err != nil {
return nil, err
}

return &extI, nil
}

func (wr *ParachainWriter) prepCall(extrinsicName string, payload ...interface{}) (*types.Call, error) {
meta, err := wr.conn.API().RPC.State.GetMetadataLatest()
if err != nil {
return nil, err
}

c, err := types.NewCall(meta, extrinsicName, payload...)
if err != nil {
return nil, err
}
return &c, nil
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just reordered the private methods.

Comment on lines 332 to 333
if checkpointSlot == 0 {
log.WithFields(log.Fields{"calculatedCheckpointSlot": checkpointSlot}).Info("checkpoint slot not available, try with slot in next sync period instead")
checkpointSlot = h.syncer.CalculateNextCheckpointSlot(slot)
lastFinalizedHeaderState, err := h.writer.GetLastFinalizedHeaderState()
checkpointSlot, err = h.populateCheckPointCacheWithDataFromChain(slot, checkpointSlot)
Copy link
Contributor

@yrong yrong Mar 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit weird this function is called with checkpointSlot as const zero, then could it be simplified as h.populateCheckPointCacheWithDataFromChain(slot)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point! de118ca

func (h *Header) getHeaderUpdateBySlot(slot uint64) (scale.HeaderUpdatePayload, error) {
slot = slot + 1
Copy link
Contributor

@yrong yrong Mar 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is adding this line correctly and the smoke test register_token still work? I don't think we need it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch 😄 0da2b16

@@ -386,3 +457,40 @@ func (h *Header) isInitialSyncPeriod() bool {
lastFinalizedPeriod := h.syncer.ComputeSyncPeriodAtSlot(h.cache.Finalized.LastSyncedSlot)
return initialPeriod == lastFinalizedPeriod
}

func (h *Header) findClosestCheckPoint(slot uint64) (state.FinalizedHeader, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC it's not closest anymore instead the most distant. So the previous name findCheckPointBackward make more sense here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed here b5334dc.

claravanstaden added 5 commits March 20, 2024 14:23
# Conflicts:
#	relayer/cmd/root.go
#	relayer/relays/beacon/header/header.go
#	relayer/relays/beacon/state/beacon_deneb_encoding.go
#	relayer/relays/beacon/state/beacon_encoding.go
Copy link
Contributor

@yrong yrong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

@claravanstaden claravanstaden merged commit 8a51ac9 into main Mar 21, 2024
3 checks passed
@claravanstaden claravanstaden deleted the limit-finalized-headers-gap branch March 21, 2024 08:38
claravanstaden added a commit that referenced this pull request Apr 4, 2024
* construct finalized update

* extract interfaces for tests

* mock data and tests

* adds db store

* completes db store

* test works

* cleanup and fixes

* cleanup and fixes

* tests

* relayer progress

* finish tests

* formatting

* move the interim finalized header check

* fix ci

* fix ci

* adds working dir

* go mod download

* version

* wrap go version

* update

* add gopath to bin

* install sszgen

* missing go install

* remove build

* skip utility tests

* remove unused file

* remove test line

* rename method

* refactor method

* Fix compile error and breaking tests

* clean up imports

* fix duplicate import

---------

Co-authored-by: claravanstaden <Cats 4 life!>
Co-authored-by: ron <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants