diff --git a/beacon_chain/attestation_aggregation.nim b/beacon_chain/attestation_aggregation.nim index a877cb5c6..c91d067c8 100644 --- a/beacon_chain/attestation_aggregation.nim +++ b/beacon_chain/attestation_aggregation.nim @@ -74,6 +74,31 @@ proc aggregate_attestations*( none(AggregateAndProof) +proc isValidAttestationSlot( + pool: AttestationPool, attestationSlot: Slot, attestationBlck: BlockRef): bool = + # If we allow voting for very old blocks, the state transaction below will go + # nuts and keep processing empty slots + logScope: + attestationSlot + attestationBlck = shortLog(attestationBlck) + + if not (attestationBlck.slot > pool.blockPool.finalizedHead.slot): + debug "voting for already-finalized block" + return false + + # we'll also cap it at 4 epochs which is somewhat arbitrary, but puts an + # upper bound on the processing done to validate the attestation + # TODO revisit with less arbitrary approach + if not (attestationSlot >= attestationBlck.slot): + debug "voting for block that didn't exist at the time" + return false + + if not ((attestationSlot - attestationBlck.slot) <= uint64(4 * SLOTS_PER_EPOCH)): + debug "voting for very old block" + return false + + true + # https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#attestation-subnets proc isValidAttestation*( pool: var AttestationPool, attestation: Attestation, current_slot: Slot, @@ -132,6 +157,10 @@ proc isValidAttestation*( pool.blockPool.addMissing(attestation.data.beacon_block_root) return false + if not isValidAttestationSlot(pool, attestation.data.slot, attestationBlck): + # Not in spec - check that rewinding to the state is sane + return false + pool.blockPool.withState( pool.blockPool.tmpState, BlockSlot(blck: attestationBlck, slot: attestation.data.slot)): @@ -222,6 +251,10 @@ proc isValidAggregatedAttestation*( debug "isValidAggregatedAttestation: attestation has no or invalid aggregation bits" return false + if not isValidAttestationSlot(pool, aggregate.data.slot, attestationBlck): + # Not in spec - check that rewinding to the state is sane + return false + # [REJECT] aggregate_and_proof.selection_proof selects the validator as an # aggregator for the slot -- i.e. is_aggregator(state, aggregate.data.slot, # aggregate.data.index, aggregate_and_proof.selection_proof) returns True. diff --git a/beacon_chain/beacon_node.nim b/beacon_chain/beacon_node.nim index ef6fbb22a..f9f94dbcc 100644 --- a/beacon_chain/beacon_node.nim +++ b/beacon_chain/beacon_node.nim @@ -694,6 +694,7 @@ proc installBeaconApiHandlers(rpcServer: RpcServer, node: BeaconNode) = root: Option[Eth2Digest]) -> StringOfJson: requireOneOf(slot, root) if slot.isSome: + # TODO sanity check slot so that it doesn't cause excessive rewinding let blk = node.blockPool.head.blck.atSlot(slot.get) node.blockPool.withState(node.blockPool.tmpState, blk): return jsonResult(state)