Conversation
| pub(super) type FinalizedBeaconHeaderStates<T: Config> = | ||
| StorageValue<_, BoundedVec<FinalizedHeaderState, T::MaxFinalizedHeadersToKeep>, ValueQuery>; |
There was a problem hiding this comment.
FinalizedHeaderState used here is for pruning obsolete data in FinalizedBeaconState. I would prefer we keep it.
snowbridge/parachain/pallets/ethereum-beacon-client/src/lib.rs
Lines 786 to 790 in 8f6ed31
There was a problem hiding this comment.
Well, after thinking about the pruning algorithm some more, I don't think we should be using FinalizedBeaconHeaderStates for pruning. That was a mistake, I believe.
FinalizedBeaconHeaderStates was originally introduced to help the offchain relayer generate ancestry proofs for execution headers updates. The storage item only needed contain the last few dozen recently imported finalized header states.
Then for pruning, we decided to reuse FinalizedBeaconHeaderStates for another purpose - to determine an older finalized header state we could prune. This was a mistake.
However this introduces a very bad constraint. If we want to keep the last months of beacon headers, then we need to store a month of state in FinalizedBeaconHeaderStates. But this storage item is a vector. We can't keep so much data in a vector in storage. This will eventually cause weight problems.
So I want to us to eliminate FinalizedBeaconHeaderStates. I don't think its necessary. I'm certain we can change the algorithm in the relayer to not rely on it.
And for pruning finalized beacon state, that's not a priority right now. We can introduce the Ring Buffer approach in another PR. But not right now - I want us to focus on these cleanups first. We can revisit pruning at a later time.
There was a problem hiding this comment.
Yes, that make sense. Just remove pruing stuff.
There was a problem hiding this comment.
If we want to keep the last months of beacon headers
I think no need to cache that much data, based on the calculateClosestCheckpointSlot algorithm here checkpoint only valid in range of 1 sync period(i.e. 27.3 hour). Consider finalized_update changes very infrequently(i.e. almost 12mins) in mainnet, that means 27*60/12=135 checkpoints at most.
Another reason is since we've removed SyncCommittees Map storage with only the current/next SyncCommittee. That means we can only verify updates in recent sync period, cache a lot of history headers does not make sense any more.
Any way, I agree we can make this PR focus on ALC spec compliant and revisit pruning at a later time.
There was a problem hiding this comment.
I think no need to cache that much data, based on the calculateClosestCheckpointSlot algorithm here checkpoint only valid in range of 1 sync period(i.e. 27.3 hour). Consider finalized_update changes very infrequently(i.e. almost 12mins) in mainnet, that means 27*60/12=135 checkpoints at most.
If a finalized checkpoint is always the first slot in a finalized epoch, then I think it is not necessary for the relayer to need the FinalizedBeaconHeaderSlotsCache storage . I've added more details in SNO-449.
claravanstaden
left a comment
There was a problem hiding this comment.
This is awesome! 🙌
| @@ -0,0 +1,43 @@ | |||
| { | |||
| "header": { | |||
There was a problem hiding this comment.
I wonder if this shouldn't be beacon_header, for clarity?
| serde(deny_unknown_fields, bound(serialize = ""), bound(deserialize = "")) | ||
| )] | ||
| pub struct ExecutionHeaderUpdate { | ||
| /// Header for the beacon block containing the execution payload |
There was a problem hiding this comment.
Nice! I neglected comments like these, apologies. 😄
| SyncCommitteeUpdate beaconjson.Update | ||
| FinalizedHeaderUpdate beaconjson.Update |
There was a problem hiding this comment.
Can we use only Update in the relayer as well and remove one of these?
There was a problem hiding this comment.
we need both. The relayer syncing algorithm hasn't changed at all. Only the data structures we send to the light client
| const ( | ||
| pathToBeaconBenchmarkData = "parachain/pallets/ethereum-beacon-client/src/benchmarking" | ||
| pathToBenchmarkDataTemplate = "parachain/templates/beacon_benchmarking_data.rs.mustache" | ||
| pathToBenchmarkDataTemplate = "parachain/templates/benchmark-fixtures.mustache" |
There was a problem hiding this comment.
I wonder if we shouldn't indicate that this is beacon data related?
| checkPointBytes, _ := types.EncodeToBytes(checkPointScale) | ||
| // Call index for EthereumBeaconClient.force_checkpoint | ||
| checkPointCallIndex := "0x3201" | ||
| checkPointCallIndex := "0x3200" |
There was a problem hiding this comment.
Call index for extrinsic force_checkpoint, we use it for beacon client provisioning, replace the genesis config before.
| // lastFinalizedHeader, err := h.writer.GetLastFinalizedHeaderState() | ||
| // if err != nil { | ||
| // return fmt.Errorf("fetch last finalized header from parachain: %w", err) | ||
| // } | ||
| // lastFinalizedHeaderPeriod := h.syncer.ComputeSyncPeriodAtSlot(lastFinalizedHeader.BeaconSlot) | ||
|
|
||
| // Period + 1 because the sync committee update contains the next period's sync committee | ||
| if lastSyncedSyncCommitteePeriod != period+1 { | ||
| return fmt.Errorf("synced committee period %d not imported successfully", lastSyncedSyncCommitteePeriod) | ||
| } | ||
| // if lastFinalizedHeaderPeriod != period+1 { | ||
| // return fmt.Errorf("synced committee period %d not imported successfully", lastFinalizedHeaderPeriod) | ||
| // } |
There was a problem hiding this comment.
Can we remove this? Is this because we have a combined header and sync committee period submit extrinsic now?
| SignatureSlot uint64 `json:"signature_slot"` | ||
| BlockRootsRoot string `json:"block_roots_root"` | ||
| BlockRootBranch []string `json:"block_roots_branch"` | ||
| type Update struct { |
There was a problem hiding this comment.
What would the frequency of this update to the beacon client be? Trying to wrap my head around what the change means practically. 😄
There was a problem hiding this comment.
Since SyncCommitteeUpdate is just a FinalizedUpdate with one extra field NextSyncCommittee, we merge these two data types together In this PR and frequency is just same as the previous FinalizedUpdate.
There was a problem hiding this comment.
I haven't actually changed the syncing algorithm much at all. Only the data structures and the light client APIs the relayer talks to. The syncing algorithm used by the relayer remains compatible with the new design. Which is great.
When the relayer needs to submit a new sync committee, it sends this Update data structure. And the same for when it only needs to submit a new finalized header.
* Strict follows ALC * Polish * Merge recent change * Fix tests * Polish * Generate all updates in same command * Sync version of lodestar * Clean code * Add more checks when generate test data
I've refactored the light client to conform more closely to the ALC consensus specs.
The light client now has a single
submitextrinsic for processing both finality and sync committee updates.Everything is green and running fine: Unit tests, benchmarks, relaying, E2E env
Storage
The storage in the light client is also simpler. These new storage items have replaced other ones:
It is no longer necessary to store historical sync committees, so I have removed that storage map.
Removed Features
I've decided to remove weak subjectivity checks from the scope of our initial release. I think the problem of dealing with long-range attacks has turned out to be quite a bit more complex than we anticipated. Then, given what we know now about the security flaws in the ALC protocol, long-range attacks are no longer the most pressing security vulnerability.
Since the current code in pallet was incomplete, and still needed to support governance-backed re-activation of the bridge, I felt it best to just remove it all for now.
Development Experience
Finally, the development experience is more straightforward. I've removed the
minimalfeature flag, and replaced it with abeacon-spec-mainnetflag. So I've inverted the behavior, and this ends up actually working a lot better.When running unit tests, we usually want to runt the
minimalspec tests, as those are extensive and easier to maintain and troubleshoot. So I've enabled the miminal spec tests by default, and you don't need to pass any feature flags to run them.However, when working with benchmarks, we always need to use the
mainnetspec. So when theruntime-benchmarksfeature is enabled, thebeacon-spec-mainnetis automatically enabled too.So can just run benchmark tests using:
Fixes: SNO-446, SNO-439