add Electra attester slashing pool (#6579)

This commit is contained in:
tersec 2024-09-24 09:01:40 +00:00 committed by GitHub
parent 841904bd53
commit d4e441e694
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 131 additions and 43 deletions

View File

@ -1008,14 +1008,15 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
OK: 14/14 Fail: 0/14 Skip: 0/14
## Validator change pool testing suite
```diff
+ addValidatorChangeMessage/getAttesterSlashingMessage OK
+ addValidatorChangeMessage/getAttesterSlashingMessage (Electra) OK
+ addValidatorChangeMessage/getAttesterSlashingMessage (Phase 0) OK
+ addValidatorChangeMessage/getBlsToExecutionChange (post-capella) OK
+ addValidatorChangeMessage/getBlsToExecutionChange (pre-capella) OK
+ addValidatorChangeMessage/getProposerSlashingMessage OK
+ addValidatorChangeMessage/getVoluntaryExitMessage OK
+ pre-pre-fork voluntary exit OK
```
OK: 6/6 Fail: 0/6 Skip: 0/6
OK: 7/7 Fail: 0/7 Skip: 0/7
## Validator pool
```diff
+ Doppelganger for genesis validator OK
@ -1125,4 +1126,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2
OK: 9/9 Fail: 0/9 Skip: 0/9
---TOTAL---
OK: 762/767 Fail: 0/767 Skip: 5/767
OK: 763/768 Fail: 0/768 Skip: 5/768

View File

@ -34,15 +34,20 @@ type
proc(data: SignedBLSToExecutionChange) {.gcsafe, raises: [].}
OnProposerSlashingCallback =
proc(data: ProposerSlashing) {.gcsafe, raises: [].}
OnAttesterSlashingCallback =
OnPhase0AttesterSlashingCallback =
proc(data: phase0.AttesterSlashing) {.gcsafe, raises: [].}
OnElectraAttesterSlashingCallback =
proc(data: electra.AttesterSlashing) {.gcsafe, raises: [].}
ValidatorChangePool* = object
## The validator change pool tracks attester slashings, proposer slashings,
## voluntary exits, and BLS to execution changes that could be added to a
## proposed block.
attester_slashings*: Deque[phase0.AttesterSlashing] ## \
phase0_attester_slashings*: Deque[phase0.AttesterSlashing] ## \
## Not a function of chain DAG branch; just used as a FIFO queue for blocks
electra_attester_slashings*: Deque[electra.AttesterSlashing] ## \
## Not a function of chain DAG branch; just used as a FIFO queue for blocks
proposer_slashings*: Deque[ProposerSlashing] ## \
@ -58,7 +63,8 @@ type
## Not a function of chain DAG branch; just used as a FIFO queue for blocks
prior_seen_attester_slashed_indices: HashSet[uint64] ## \
## Records attester-slashed indices seen.
## Records attester-slashed indices seen. Share these across attester
## slashing types.
prior_seen_proposer_slashed_indices: HashSet[uint64] ## \
## Records proposer-slashed indices seen.
@ -74,20 +80,26 @@ type
onVoluntaryExitReceived*: OnVoluntaryExitCallback
onBLSToExecutionChangeReceived*: OnBLSToExecutionChangeCallback
onProposerSlashingReceived*: OnProposerSlashingCallback
onAttesterSlashingReceived*: OnAttesterSlashingCallback
onPhase0AttesterSlashingReceived*: OnPhase0AttesterSlashingCallback
onElectraAttesterSlashingReceived*: OnElectraAttesterSlashingCallback
func init*(T: type ValidatorChangePool, dag: ChainDAGRef,
attestationPool: ref AttestationPool = nil,
onVoluntaryExit: OnVoluntaryExitCallback = nil,
onBLSToExecutionChange: OnBLSToExecutionChangeCallback = nil,
onProposerSlashing: OnProposerSlashingCallback = nil,
onAttesterSlashing: OnAttesterSlashingCallback = nil): T =
onPhase0AttesterSlashing: OnPhase0AttesterSlashingCallback = nil,
onElectraAttesterSlashing: OnElectraAttesterSlashingCallback = nil):
T =
## Initialize an ValidatorChangePool from the dag `headState`
T(
# Allow filtering some validator change messages during block production
attester_slashings:
phase0_attester_slashings:
initDeque[phase0.AttesterSlashing](
initialSize = ATTESTER_SLASHINGS_BOUND.int),
electra_attester_slashings:
initDeque[electra.AttesterSlashing](
initialSize = ATTESTER_SLASHINGS_BOUND.int),
proposer_slashings:
initDeque[ProposerSlashing](initialSize = PROPOSER_SLASHINGS_BOUND.int),
voluntary_exits:
@ -107,7 +119,8 @@ func init*(T: type ValidatorChangePool, dag: ChainDAGRef,
onVoluntaryExitReceived: onVoluntaryExit,
onBLSToExecutionChangeReceived: onBLSToExecutionChange,
onProposerSlashingReceived: onProposerSlashing,
onAttesterSlashingReceived: onAttesterSlashing)
onPhase0AttesterSlashingReceived: onPhase0AttesterSlashing,
onElectraAttesterSlashingReceived: onElectraAttesterSlashing)
func addValidatorChangeMessage(
subpool: var auto, seenpool: var auto, validatorChangeMessage: auto,
@ -133,7 +146,9 @@ iterator getValidatorIndices(
bls_to_execution_change: SignedBLSToExecutionChange): uint64 =
yield bls_to_execution_change.message.validator_index
func isSeen*(pool: ValidatorChangePool, msg: phase0.AttesterSlashing): bool =
func isSeen*(
pool: ValidatorChangePool,
msg: phase0.AttesterSlashing | electra.AttesterSlashing): bool =
for idx in getValidatorIndices(msg):
# One index is enough!
if idx notin pool.prior_seen_attester_slashed_indices:
@ -154,12 +169,23 @@ func isSeen*(pool: ValidatorChangePool, msg: SignedBLSToExecutionChange): bool =
func addMessage*(pool: var ValidatorChangePool, msg: phase0.AttesterSlashing) =
for idx in getValidatorIndices(msg):
pool.prior_seen_attester_slashed_indices.incl idx
if pool.attestationPool != nil:
if not pool.attestationPool.isNil:
let i = ValidatorIndex.init(idx).valueOr:
continue
pool.attestationPool.forkChoice.process_equivocation(i)
pool.attester_slashings.addValidatorChangeMessage(
pool.phase0_attester_slashings.addValidatorChangeMessage(
pool.prior_seen_attester_slashed_indices, msg, ATTESTER_SLASHINGS_BOUND)
func addMessage*(pool: var ValidatorChangePool, msg: electra.AttesterSlashing) =
for idx in getValidatorIndices(msg):
pool.prior_seen_attester_slashed_indices.incl idx
if not pool.attestationPool.isNil:
let i = ValidatorIndex.init(idx).valueOr:
continue
pool.attestationPool.forkChoice.process_equivocation(i)
pool.electra_attester_slashings.addValidatorChangeMessage(
pool.prior_seen_attester_slashed_indices, msg, ATTESTER_SLASHINGS_BOUND)
func addMessage*(pool: var ValidatorChangePool, msg: ProposerSlashing) =
@ -193,7 +219,7 @@ proc validateValidatorChangeMessage(
check_proposer_slashing(state, msg, {}).isOk
proc validateValidatorChangeMessage(
cfg: RuntimeConfig, state: ForkyBeaconState, msg:
phase0.AttesterSlashing): bool =
phase0.AttesterSlashing | electra.AttesterSlashing): bool =
check_attester_slashing(state, msg, {}).isOk
proc validateValidatorChangeMessage(
cfg: RuntimeConfig, state: ForkyBeaconState, msg: SignedVoluntaryExit):
@ -252,11 +278,13 @@ proc getBeaconBlockValidatorChanges*(
res: BeaconBlockValidatorChanges
getValidatorChangeMessagesForBlock(
pool.attester_slashings, cfg, state, indices, res.attester_slashings)
pool.phase0_attester_slashings, cfg, state, indices,
res.phase0_attester_slashings)
getValidatorChangeMessagesForBlock(
pool.proposer_slashings, cfg, state, indices, res.proposer_slashings)
getValidatorChangeMessagesForBlock(
pool.voluntary_exits, cfg, state, indices, res.voluntary_exits)
when typeof(state).kind >= ConsensusFork.Capella:
# Prioritize these
getValidatorChangeMessagesForBlock(
@ -267,4 +295,9 @@ proc getBeaconBlockValidatorChanges*(
pool.bls_to_execution_changes_gossip, cfg, state, indices,
res.bls_to_execution_changes)
when typeof(state).kind >= ConsensusFork.Electra:
getValidatorChangeMessagesForBlock(
pool.electra_attester_slashings, cfg, state, indices,
res.electra_attester_slashings)
res

View File

@ -1246,8 +1246,10 @@ proc validateAttesterSlashing*(
return pool.checkedReject(attester_slashing_validity.error)
# Send notification about new attester slashing via callback
if not(isNil(pool.onAttesterSlashingReceived)):
pool.onAttesterSlashingReceived(attester_slashing)
if not(isNil(pool.onPhase0AttesterSlashingReceived)):
pool.onPhase0AttesterSlashingReceived(attester_slashing)
debugComment "apparently there's no gopssip validation in place for electra attslashings"
ok()

View File

@ -291,8 +291,11 @@ proc initFullNode(
node.eventBus.blsToExecQueue.emit(data)
proc onProposerSlashingAdded(data: ProposerSlashing) =
node.eventBus.propSlashQueue.emit(data)
proc onAttesterSlashingAdded(data: phase0.AttesterSlashing) =
proc onPhase0AttesterSlashingAdded(data: phase0.AttesterSlashing) =
node.eventBus.attSlashQueue.emit(data)
proc onElectraAttesterSlashingAdded(data: electra.AttesterSlashing) =
debugComment "electra att slasher queue"
discard
proc onBlobSidecarAdded(data: BlobSidecarInfoObject) =
node.eventBus.blobSidecarQueue.emit(data)
proc onBlockAdded(data: ForkedTrustedSignedBeaconBlock) =
@ -392,7 +395,8 @@ proc initFullNode(
LightClientPool())
validatorChangePool = newClone(ValidatorChangePool.init(
dag, attestationPool, onVoluntaryExitAdded, onBLSToExecutionChangeAdded,
onProposerSlashingAdded, onAttesterSlashingAdded))
onProposerSlashingAdded, onPhase0AttesterSlashingAdded,
onElectraAttesterSlashingAdded))
blobQuarantine = newClone(BlobQuarantine.init(onBlobSidecarAdded))
consensusManager = ConsensusManager.new(
dag, attestationPool, quarantine, node.elManager,

View File

@ -1353,7 +1353,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
router.api2(MethodGet, "/eth/v1/beacon/pool/attester_slashings") do (
) -> RestApiResponse:
RestApiResponse.jsonResponse(
toSeq(node.validatorChangePool.attester_slashings))
toSeq(node.validatorChangePool.phase0_attester_slashings))
# https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolAttesterSlashings
router.api(MethodPost, "/eth/v1/beacon/pool/attester_slashings") do (

View File

@ -498,15 +498,6 @@ type
SigVerifiedBeaconBlockBody |
TrustedBeaconBlockBody
BeaconBlockValidatorChanges* = object
# Collection of exits that are suitable for block production
proposer_slashings*: List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]
attester_slashings*:
List[phase0.AttesterSlashing, Limit MAX_ATTESTER_SLASHINGS]
voluntary_exits*: List[SignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS]
bls_to_execution_changes*:
List[SignedBLSToExecutionChange, Limit MAX_BLS_TO_EXECUTION_CHANGES]
BeaconStateDiffPreSnapshot* = object
eth1_data_votes_recent*: seq[Eth1Data]
eth1_data_votes_len*: int

View File

@ -32,8 +32,8 @@ from ./altair import
TrustedSyncAggregate, num_active_participants
from ./bellatrix import BloomLogs, ExecutionAddress, Transaction
from ./capella import
ExecutionBranch, HistoricalSummary, SignedBLSToExecutionChangeList,
Withdrawal, EXECUTION_PAYLOAD_GINDEX
ExecutionBranch, HistoricalSummary, SignedBLSToExecutionChange,
SignedBLSToExecutionChangeList, Withdrawal, EXECUTION_PAYLOAD_GINDEX
from ./deneb import Blobs, BlobsBundle, KzgCommitments, KzgProofs
export json_serialization, base, kzg4844
@ -630,6 +630,17 @@ type
kzg_proofs*: KzgProofs
blobs*: Blobs
BeaconBlockValidatorChanges* = object
# Collection of exits that are suitable for block production
proposer_slashings*: List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]
phase0_attester_slashings*:
List[phase0.AttesterSlashing, Limit MAX_ATTESTER_SLASHINGS]
electra_attester_slashings*:
List[electra.AttesterSlashing, Limit MAX_ATTESTER_SLASHINGS_ELECTRA]
voluntary_exits*: List[SignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS]
bls_to_execution_changes*:
List[SignedBLSToExecutionChange, Limit MAX_BLS_TO_EXECUTION_CHANGES]
# TODO: There should be only a single generic HashedBeaconState definition
func initHashedBeaconState*(s: BeaconState): HashedBeaconState =
HashedBeaconState(data: s)

View File

@ -375,7 +375,7 @@ func partialBeaconBlock*(
eth1_data: eth1_data,
graffiti: graffiti,
proposer_slashings: validator_changes.proposer_slashings,
attester_slashings: validator_changes.attester_slashings,
attester_slashings: validator_changes.phase0_attester_slashings,
attestations:
List[phase0.Attestation, Limit MAX_ATTESTATIONS](attestations),
deposits: List[Deposit, Limit MAX_DEPOSITS](deposits),
@ -500,7 +500,7 @@ proc makeBeaconBlockWithRewards*(
hash_tree_root(eth1_data),
hash_tree_root(graffiti),
hash_tree_root(validator_changes.proposer_slashings),
hash_tree_root(validator_changes.attester_slashings),
hash_tree_root(validator_changes.phase0_attester_slashings),
hash_tree_root(
List[phase0.Attestation, Limit MAX_ATTESTATIONS](
attestations)),
@ -524,7 +524,7 @@ proc makeBeaconBlockWithRewards*(
hash_tree_root(eth1_data),
hash_tree_root(graffiti),
hash_tree_root(validator_changes.proposer_slashings),
hash_tree_root(validator_changes.attester_slashings),
hash_tree_root(validator_changes.electra_attester_slashings),
hash_tree_root(
List[electra.Attestation, Limit MAX_ATTESTATIONS](
attestations)),

View File

@ -27,7 +27,7 @@ func makeSignedBeaconBlockHeader(
fork, genesis_validators_root, slot, hash_tree_root(tmp),
MockPrivKeys[proposer_index]).toValidatorSig())
func makeIndexedAttestation(
func makePhase0IndexedAttestation(
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
validator_index: uint64, beacon_block_root: Eth2Digest):
phase0.IndexedAttestation =
@ -41,6 +41,21 @@ func makeIndexedAttestation(
fork, genesis_validators_root, tmp,
MockPrivKeys[validator_index]).toValidatorSig)
func makeElectraIndexedAttestation(
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
validator_index: uint64, beacon_block_root: Eth2Digest):
electra.IndexedAttestation =
let tmp = AttestationData(slot: slot, beacon_block_root: beacon_block_root)
electra.IndexedAttestation(
data: tmp,
attesting_indices:
List[uint64, Limit MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT](
@[validator_index]),
signature: get_attestation_signature(
fork, genesis_validators_root, tmp,
MockPrivKeys[validator_index]).toValidatorSig)
func makeSignedVoluntaryExit(
fork: Fork, genesis_validators_root: Eth2Digest, epoch: Epoch,
validator_index: uint64): SignedVoluntaryExit =
@ -62,6 +77,8 @@ suite "Validator change pool testing suite":
tmp.ALTAIR_FORK_EPOCH = Epoch(tmp.SHARD_COMMITTEE_PERIOD)
tmp.BELLATRIX_FORK_EPOCH = Epoch(tmp.SHARD_COMMITTEE_PERIOD) + 1
tmp.CAPELLA_FORK_EPOCH = Epoch(tmp.SHARD_COMMITTEE_PERIOD) + 2
tmp.DENEB_FORK_EPOCH = Epoch(tmp.SHARD_COMMITTEE_PERIOD) + 3
tmp.ELECTRA_FORK_EPOCH = Epoch(tmp.SHARD_COMMITTEE_PERIOD) + 4
tmp
validatorMonitor = newClone(ValidatorMonitor.init())
@ -95,14 +112,14 @@ suite "Validator change pool testing suite":
cfg, forkyState.data).proposer_slashings.lenu64 ==
min(i + 1, MAX_PROPOSER_SLASHINGS)
test "addValidatorChangeMessage/getAttesterSlashingMessage":
test "addValidatorChangeMessage/getAttesterSlashingMessage (Phase 0)":
for i in 0'u64 .. MAX_ATTESTER_SLASHINGS + 5:
for j in 0'u64 .. i:
let
msg = phase0.AttesterSlashing(
attestation_1: makeIndexedAttestation(
attestation_1: makePhase0IndexedAttestation(
fork, genesis_validators_root, Slot(1), j, makeFakeHash(0)),
attestation_2: makeIndexedAttestation(
attestation_2: makePhase0IndexedAttestation(
fork, genesis_validators_root, Slot(1), j, makeFakeHash(1)))
if i == 0:
@ -113,9 +130,39 @@ suite "Validator change pool testing suite":
withState(dag.headState):
check:
pool[].getBeaconBlockValidatorChanges(
cfg, forkyState.data).attester_slashings.lenu64 ==
cfg, forkyState.data).phase0_attester_slashings.lenu64 ==
min(i + 1, MAX_ATTESTER_SLASHINGS)
test "addValidatorChangeMessage/getAttesterSlashingMessage (Electra)":
var
cache: StateCache
info: ForkedEpochInfo
process_slots(
dag.cfg, dag.headState,
Epoch(dag.cfg.SHARD_COMMITTEE_PERIOD).start_slot + 1 + SLOTS_PER_EPOCH * 5,
cache, info, {}).expect("ok")
let fork = dag.forkAtEpoch(dag.headState.get_current_epoch())
for i in 0'u64 .. MAX_ATTESTER_SLASHINGS_ELECTRA + 5:
for j in 0'u64 .. i:
let
msg = electra.AttesterSlashing(
attestation_1: makeElectraIndexedAttestation(
fork, genesis_validators_root, Slot(1), j, makeFakeHash(0)),
attestation_2: makeElectraIndexedAttestation(
fork, genesis_validators_root, Slot(1), j, makeFakeHash(1)))
if i == 0:
check not pool[].isSeen(msg)
pool[].addMessage(msg)
check: pool[].isSeen(msg)
withState(dag.headState):
check:
pool[].getBeaconBlockValidatorChanges(
cfg, forkyState.data).electra_attester_slashings.lenu64 ==
min(i + 1, MAX_ATTESTER_SLASHINGS_ELECTRA)
test "addValidatorChangeMessage/getVoluntaryExitMessage":
# Need to advance state or it will not accept voluntary exits
var
@ -125,8 +172,7 @@ suite "Validator change pool testing suite":
dag.cfg, dag.headState,
Epoch(dag.cfg.SHARD_COMMITTEE_PERIOD).start_slot + 1, cache, info,
{}).expect("ok")
let
fork = dag.forkAtEpoch(dag.headState.get_current_epoch())
let fork = dag.forkAtEpoch(dag.headState.get_current_epoch())
for i in 0'u64 .. MAX_VOLUNTARY_EXITS + 5:
for j in 0'u64 .. i:
@ -145,7 +191,7 @@ suite "Validator change pool testing suite":
min(i + 1, MAX_VOLUNTARY_EXITS)
test "addValidatorChangeMessage/getBlsToExecutionChange (pre-capella)":
# Need to advance state or it will not accept voluntary exits
# Need to advance state or it will not accept execution changes
var
cache: StateCache
info: ForkedEpochInfo
@ -176,7 +222,7 @@ suite "Validator change pool testing suite":
cfg, forkyState.data).bls_to_execution_changes.len == 0
test "addValidatorChangeMessage/getBlsToExecutionChange (post-capella)":
# Need to advance state or it will not accept voluntary exits
# Need to advance state or it will not accept execution changes
var
cache: StateCache
info: ForkedEpochInfo