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

Implement EIP7805: Fork-choice enforced Inclusion Lists #14754

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

terencechain
Copy link
Member

Initial implementation of FOCIL based on:

Looking for feedback on the spec, design decisions, and end to end testing strategies

uint64 slot = 1 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Slot"];
uint64 validator_index = 2 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.ValidatorIndex"];
bytes inclusion_list_committee_root = 3 [(ethereum.eth.ext.ssz_size) = "32"];
repeated bytes transactions = 4 [(ethereum.eth.ext.ssz_size) = "?,?", (ethereum.eth.ext.ssz_max) = "1048576,1073741824"];

Choose a reason for hiding this comment

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

Will allowing max size of transactions to 1 GiB instead of 8 KiB leave the room for DoS attack?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, this is a ssz list size, it's just a theoretical limit. The 8kb check is enforced on the gossip level

totalSize += len(transaction)
}
if totalSize > 8*1024 {
return pubsub.ValidationReject, errors.New("total size of transactions exceeds 8KB")

Choose a reason for hiding this comment

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

Suggested change
return pubsub.ValidationReject, errors.New("total size of transactions exceeds 8KB")
return pubsub.ValidationReject, errors.New("total size of transactions exceeds 8KiB")

// GetInclusionListCommittee retrieves the validator indices assigned to the inclusion list committee
// for a given slot. Returns an error if the state or slot does not meet the required constraints.
func GetInclusionListCommittee(ctx context.Context, state state.ReadOnlyBeaconState, slot primitives.Slot) ([]primitives.ValidatorIndex, error) {
if state.Version() < version.Electra {

Choose a reason for hiding this comment

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

Suggested change
if state.Version() < version.Electra {
if state.Version() < version.FOCIL {

@@ -743,6 +743,10 @@ func (v *validator) RolesAt(ctx context.Context, slot primitives.Slot) (map[[fie
syncCommitteeValidators[duty.ValidatorIndex] = bytesutil.ToBytes48(duty.PublicKey)
}

if duty.InclusionListCommitteeSlot == slot {

Choose a reason for hiding this comment

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

I guess this is a known issue but beacon REST API for UpdateDuties() is not updated. It could save us some time in the future to explicitly add a TODO in dutiesForEpoch() in validator/client/beacon-api/duties.go.


// GetInclusionListCommittee retrieves the validator indices assigned to the inclusion list committee
// for a given slot. Returns an error if the state or slot does not meet the required constraints.
func GetInclusionListCommittee(ctx context.Context, state state.ReadOnlyBeaconState, slot primitives.Slot) ([]primitives.ValidatorIndex, error) {

Choose a reason for hiding this comment

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

This one gets called multiple times. What do you think about caching it?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it makes sense to do it later. I don't want to introduce premature optimization unless it's necessary at this point. It's not a blocker to interop

defer cancel()

result := &primitives.PayloadID{}
err := s.rpcClient.CallContext(ctx, result, UpdatePayloadWithInclusionListV1, &InclusionListV1{

Choose a reason for hiding this comment

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

Suggested change
err := s.rpcClient.CallContext(ctx, result, UpdatePayloadWithInclusionListV1, &InclusionListV1{
err := s.rpcClient.CallContext(ctx, result, UpdatePayloadWithInclusionListV1, payloadID, &InclusionListV1{

// Validate slot constraints.
currentSlot := s.cfg.clock.CurrentSlot()
if il.Message.Slot == currentSlot || il.Message.Slot+1 == currentSlot {
return pubsub.ValidationReject, errors.New("slot is equal to the previous or current slot")

Choose a reason for hiding this comment

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

Suggested change
return pubsub.ValidationReject, errors.New("slot is equal to the previous or current slot")
return pubsub.ValidationReject, errors.New("slot is not equal to the previous or current slot")

return pubsub.ValidationIgnore, err
}
deadline := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot
if il.Message.Slot+1 == currentSlot && secondsSinceSlotStart > deadline {

Choose a reason for hiding this comment

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

Should we need to change consensus spec's p2p part to [IGNORE] The slot message.slot is equal to the previous slot and the current time is less than attestation_deadline seconds into the slot.?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes we should

}
deadline := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot
if il.Message.Slot+1 == currentSlot && secondsSinceSlotStart > deadline {
return pubsub.ValidationIgnore, errors.New("slot is equal to the current slot, or it is equal to the previous slot and the current time is less than attestation_deadline seconds into the slot")

Choose a reason for hiding this comment

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

It seems the error messages here show why it went wrong instead of what it should be.

@@ -215,6 +217,7 @@ func (s *Service) Start() {
}
s.spawnProcessAttestationsRoutine()
go s.runLateBlockTasks()
go s.updateBlockWithInclusionListRoutine()

Choose a reason for hiding this comment

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

What makes it better to have a routine here instead of updating a payload when a validator has a proposer role? Easier implementation?

Copy link
Member Author

Choose a reason for hiding this comment

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

The routine is a no op unless the validator has a proposer role regardless

@terencechain terencechain force-pushed the focil branch 4 times, most recently from 23b7573 to 7536720 Compare December 31, 2024 03:51
if entry.txs == nil {
entry.txs = txs
} else {
entry.seenTwice = true

Choose a reason for hiding this comment

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

Is there any chance the same IL received from different peer?

Copy link
Member Author

@terencechain terencechain Dec 31, 2024

Choose a reason for hiding this comment

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

Libp2p covers such case, it filters by content so we should not be importing same IL from different peer

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.

2 participants