mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-02-14 06:55:07 -05:00
Compare commits
2 Commits
gloas/fork
...
gossip-att
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d524bf6ef | ||
|
|
22e77add54 |
@@ -112,6 +112,34 @@ func ProcessExecutionPayload(
|
||||
return errors.Wrap(err, "signature verification failed")
|
||||
}
|
||||
|
||||
envelope, err := signedEnvelope.Envelope()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get envelope from signed envelope")
|
||||
}
|
||||
|
||||
if err := ApplyExecutionPayload(ctx, st, envelope); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r, err := st.HashTreeRoot(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get hash tree root")
|
||||
}
|
||||
if r != envelope.StateRoot() {
|
||||
return fmt.Errorf("state root mismatch: expected %#x, got %#x", envelope.StateRoot(), r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApplyExecutionPayload applies the execution payload envelope to the state and performs the same
|
||||
// consistency checks as the full processing path. This keeps the post-payload state root computation
|
||||
// on a shared code path, even though some bid/payload checks are not strictly required for the root itself.
|
||||
func ApplyExecutionPayload(
|
||||
ctx context.Context,
|
||||
st state.BeaconState,
|
||||
envelope interfaces.ROExecutionPayloadEnvelope,
|
||||
) error {
|
||||
latestHeader := st.LatestBlockHeader()
|
||||
if len(latestHeader.StateRoot) == 0 || bytes.Equal(latestHeader.StateRoot, make([]byte, 32)) {
|
||||
previousStateRoot, err := st.HashTreeRoot(ctx)
|
||||
@@ -128,10 +156,6 @@ func ProcessExecutionPayload(
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not compute block header root")
|
||||
}
|
||||
envelope, err := signedEnvelope.Envelope()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get envelope from signed envelope")
|
||||
}
|
||||
|
||||
beaconBlockRoot := envelope.BeaconBlockRoot()
|
||||
if !bytes.Equal(beaconBlockRoot[:], blockHeaderRoot[:]) {
|
||||
@@ -217,14 +241,6 @@ func ProcessExecutionPayload(
|
||||
return errors.Wrap(err, "could not set latest block hash")
|
||||
}
|
||||
|
||||
r, err := st.HashTreeRoot(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get hash tree root")
|
||||
}
|
||||
if r != envelope.StateRoot() {
|
||||
return fmt.Errorf("state root mismatch: expected %#x, got %#x", envelope.StateRoot(), r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -268,10 +268,23 @@ func (s *Service) validateCommitteeIndexAndCount(
|
||||
a eth.Att,
|
||||
bs state.ReadOnlyBeaconState,
|
||||
) (primitives.CommitteeIndex, uint64, pubsub.ValidationResult, error) {
|
||||
// - [REJECT] attestation.data.index == 0
|
||||
if a.Version() >= version.Electra && a.GetData().CommitteeIndex != 0 {
|
||||
return 0, 0, pubsub.ValidationReject, errors.New("attestation data's committee index must be 0")
|
||||
// Validate committee index based on fork.
|
||||
if a.Version() >= version.Electra {
|
||||
data := a.GetData()
|
||||
attEpoch := slots.ToEpoch(data.Slot)
|
||||
isGloas := attEpoch >= params.BeaconConfig().GloasForkEpoch
|
||||
if isGloas {
|
||||
if result, err := s.validateGloasCommitteeIndex(data); result != pubsub.ValidationAccept {
|
||||
return 0, 0, result, err
|
||||
}
|
||||
} else {
|
||||
// [REJECT] attestation.data.index == 0 (New in Electra, removed in Gloas)
|
||||
if data.CommitteeIndex != 0 {
|
||||
return 0, 0, pubsub.ValidationReject, errors.New("attestation data's committee index must be 0")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
valCount, err := helpers.ActiveValidatorCount(ctx, bs, slots.ToEpoch(a.GetData().Slot))
|
||||
if err != nil {
|
||||
return 0, 0, pubsub.ValidationIgnore, err
|
||||
@@ -356,6 +369,29 @@ func validateAttestingIndex(
|
||||
return pubsub.ValidationAccept, nil
|
||||
}
|
||||
|
||||
// validateGloasCommitteeIndex validates committee index rules for Gloas fork.
|
||||
// [REJECT] attestation.data.index < 2. (New in Gloas)
|
||||
// [REJECT] attestation.data.index == 0 if block.slot == attestation.data.slot. (New in Gloas)
|
||||
func (s *Service) validateGloasCommitteeIndex(data *eth.AttestationData) (pubsub.ValidationResult, error) {
|
||||
if data.CommitteeIndex >= 2 {
|
||||
return pubsub.ValidationReject, errors.New("attestation data's committee index must be < 2")
|
||||
}
|
||||
|
||||
// Same-slot attestations must use committee index 0
|
||||
if data.CommitteeIndex != 0 {
|
||||
blockRoot := bytesutil.ToBytes32(data.BeaconBlockRoot)
|
||||
slot, err := s.cfg.chain.RecentBlockSlot(blockRoot)
|
||||
if err != nil {
|
||||
return pubsub.ValidationIgnore, err
|
||||
}
|
||||
if slot == data.Slot {
|
||||
return pubsub.ValidationReject, errors.New("same slot attestations must use committee index 0")
|
||||
}
|
||||
}
|
||||
|
||||
return pubsub.ValidationAccept, nil
|
||||
}
|
||||
|
||||
// generateUnaggregatedAttCacheKey generates the cache key for unaggregated attestation tracking.
|
||||
func generateUnaggregatedAttCacheKey(att eth.Att) (string, error) {
|
||||
var attester uint64
|
||||
|
||||
@@ -684,3 +684,75 @@ func Test_validateCommitteeIndexAndCount_Boundary(t *testing.T) {
|
||||
require.ErrorContains(t, "committee index", err)
|
||||
require.Equal(t, pubsub.ValidationReject, res)
|
||||
}
|
||||
|
||||
func Test_validateGloasCommitteeIndex(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
committeeIndex primitives.CommitteeIndex
|
||||
attestationSlot primitives.Slot
|
||||
blockSlot primitives.Slot
|
||||
wantResult pubsub.ValidationResult
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "committee index >= 2 should reject",
|
||||
committeeIndex: 2,
|
||||
attestationSlot: 10,
|
||||
blockSlot: 10,
|
||||
wantResult: pubsub.ValidationReject,
|
||||
wantErr: "committee index must be < 2",
|
||||
},
|
||||
{
|
||||
name: "committee index 0 should accept",
|
||||
committeeIndex: 0,
|
||||
attestationSlot: 10,
|
||||
blockSlot: 10,
|
||||
wantResult: pubsub.ValidationAccept,
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "committee index 1 different-slot should accept",
|
||||
committeeIndex: 1,
|
||||
attestationSlot: 10,
|
||||
blockSlot: 9,
|
||||
wantResult: pubsub.ValidationAccept,
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "committee index 1 same-slot should reject",
|
||||
committeeIndex: 1,
|
||||
attestationSlot: 10,
|
||||
blockSlot: 10,
|
||||
wantResult: pubsub.ValidationReject,
|
||||
wantErr: "same slot attestations must use committee index 0",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockChain := &mockChain.ChainService{
|
||||
BlockSlot: tt.blockSlot,
|
||||
}
|
||||
s := &Service{
|
||||
cfg: &config{
|
||||
chain: mockChain,
|
||||
},
|
||||
}
|
||||
|
||||
data := ðpb.AttestationData{
|
||||
Slot: tt.attestationSlot,
|
||||
CommitteeIndex: tt.committeeIndex,
|
||||
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot"), 32),
|
||||
}
|
||||
|
||||
result, err := s.validateGloasCommitteeIndex(data)
|
||||
|
||||
require.Equal(t, tt.wantResult, result)
|
||||
if tt.wantErr != "" {
|
||||
require.ErrorContains(t, tt.wantErr, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
3
changelog/terence_gloas-attestation-validation.md
Normal file
3
changelog/terence_gloas-attestation-validation.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Add gossip beacon attestation validation conditions for Gloas fork
|
||||
2
changelog/tt_apply_execution_payload.md
Normal file
2
changelog/tt_apply_execution_payload.md
Normal file
@@ -0,0 +1,2 @@
|
||||
### Ignored
|
||||
- Refactor ProcessExecutionPayload to ApplyExecutionPayload
|
||||
Reference in New Issue
Block a user