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

harden REST API atSlot against non-finalized blocks #3538

Merged
merged 6 commits into from
Mar 23, 2022

Conversation

arnetheduck
Copy link
Member

@arnetheduck arnetheduck commented Mar 22, 2022

  • harden validator API against pre-finalized slot requests
  • check syncHorizon when responding to validator api requests too far
    from head
  • limit state-id based requests to one epoch ahead of head
  • put historic data bounds on block/attestation/etc validator production API, preventing them from being used with already-finalized slots
  • add validator block smoke tests
  • make rest test create a new genesis with the tests running roughly in
    the first epoch to allow testing a few more boundary conditions

* harden validator API against pre-finalized slot requests
* check `syncHorizon` when responding to validator api requests too far
from `head`
* limit state-id based requests to one epoch ahead of `head`
if res.slot + uint64(2 * SLOTS_PER_EPOCH) < slot:
let head = node.dag.head

if slot > head.slot and not node.isSynced(head):
Copy link
Contributor

Choose a reason for hiding this comment

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

node.isSynced returns false when head is more than syncHorizon behind the wall clock, it does not take into account the requested slot.

The old logic allowed slot to be syncHorizon ahead of head even while not yet fully synced.
The new logic no longer allows even requesting a single slot ahead of head while sync has not completed.

Intended?

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, while syncing we don't want to allow spurious requests for slots that will cause expensive empty-slots replays of the state

# head to avoid long empty slot replays (in particular a second epoch
# transition)
if stateIdent.slot.epoch + 1 > node.dag.head.slot.epoch:
return err("Requesting state too far ahead of head current head")
Copy link
Contributor

Choose a reason for hiding this comment

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

  • There is an extra head in the error message.
  • Logic looks alright, slot.epoch + 1 does not seem to be overflowable.

Copy link
Member Author

Choose a reason for hiding this comment

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

actually it was overflowing because of the magic far_future_epoch handling (that at some point should be applied to + as well...)

max(
getStateField(node.dag.headState, current_justified_checkpoint).epoch,
node.dag.finalizedHead.slot.epoch)
ok(node.dag.head.atEpochStart(justifiedEpoch).toBlockSlotId().expect("not nil"))
Copy link
Contributor

Choose a reason for hiding this comment

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

Good catch!

let res = slot.get()
if res.isErr():
return RestApiResponse.jsonError(Http400, InvalidSlotValueError,
$res.error())
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: Indentation of the second line w.r.t. (, on all of these.

Copy link
Member Author

Choose a reason for hiding this comment

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

this is true, but also a bit of a chore / best-effort thing as long as we don't have an autoformatter.. the best way to highlight these is through the little suggestion button which gives the right perfomance/effort ratio for fixing them (and avoids typing NIT in ALL-CAPS :))

Screenshot from 2022-03-22 20-17-04

@@ -400,6 +406,9 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http400, InvalidSlotValueError,
$res.error())
res.get()
if qslot <= node.dag.finalizedHead.slot:
Copy link
Contributor

Choose a reason for hiding this comment

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

The altair check of other endpoints is inside the block fetching qslot -- should this additional check be located similarly, as in, first loading into rslot, then validating against the allowed range, then assigning to qslot? It doesn't matter semantically, it's more of a style / consistency thing.

Copy link
Member Author

Choose a reason for hiding this comment

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

indeed, there's a myriad of these little improvements that one could make with infinite time at hand..

@github-actions
Copy link

github-actions bot commented Mar 22, 2022

Unit Test Results

     12 files  ±0     834 suites  ±0   56m 2s ⏱️ + 1m 55s
1 686 tests ±0  1 638 ✔️ ±0    48 💤 ±0  0 ±0 
9 825 runs  ±0  9 713 ✔️ ±0  112 💤 ±0  0 ±0 

Results for commit 73c0d4c. ± Comparison against base commit e7d017b.

♻️ This comment has been updated with latest results.

make rest test create a new genesis with the tests running roughly in
the first epoch to allow testing a few more boundary conditions
@arnetheduck arnetheduck changed the title harden atSlot against non-finalized blocks harden REST API atSlot against non-finalized blocks Mar 23, 2022
@arnetheduck arnetheduck merged commit bc80ac3 into unstable Mar 23, 2022
@arnetheduck arnetheduck deleted the harden-atslot branch March 23, 2022 11:42
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.

None yet

2 participants