Repository navigation
fix(node): check each block's author against the set valid at its height - #27
Merged
Merged
Conversation
After the stage rotation of 2026-10-29 node1, which was behind, could not
catch up: every old block failed "Block author verification failed:
expected author <new key>, but found <old key>", about 2,800 times in 30
minutes. verify_block_author compared every imported block, sync included,
with the current authority list, and a rotation changes every key that
signed the history. Any new node would be stuck the same way.
- aura.rs: PastAuthoritySet { until_block, authorities } and
Aura.past_sets. A block's set is the first past set whose until_block
reaches its height, else the current one; each set has its own slot
length (60 / len). With no past sets nothing changes.
- Config: optional [[previous_authorities]] tables, at the end of the
TOML. Blockchain::with_previous_authorities validates them at start
(each like authorities, plus oldest first) and main.rs wires them.
- Only the check of an OLD block reads them. The head is still checked
against authorities alone, so a rotated-out key stays dead.
Tests: aura.rs unit tests, validate_past_authority_sets, config parsing,
and tests/authority_rotation.rs (a node on the new set refuses block 1
from the old key, imports it once the old set is declared, and still
refuses the old key at block 2).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
ubuntu-latest became 26.04 in October 2026. Its newer compiler cannot build the pinned librocksdb-sys 0.16 (RocksDB 8.10 headers lack #include <cstdint>: "'uint64_t' has not been declared"), so a cold build fails before any test runs. Every earlier green run restored a cached build of it, which hid this until the new image changed the cache key. 24.04 is the image those runs used. The Docker build is not affected: it compiles in its own base image. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Stage was rotated onto new validator keys on 2026-10-29 (clutch-deploy #161). node1 cannot sync across it. Every old block fails with
Block author verification failed: expected author <new key>, but found <old key>, about 2,800 times in 30 minutes.The cause is in
verify_block_author. It compared every imported block, sync included, with today's authority list. Blocks signed before a rotation were signed by the old keys, so every one fails. The same happens to any new node. The rotation guide and the rehearsal say history still validates. That is wrong when the authorities change.This does not heal node1 by itself. node1 was already stuck at about block 144,507 (the real chain passed block 144,508 on 2026-10-23 at 03:30 UTC). When the deploy of 2026-10-29 restarted it at 14:44:56, it made blocks of its own on that old head (144,508 and up, one per minute) before it had synced, because a node does not check if it is behind before it authors. Those blocks are not on the real chain, and a node cannot switch chains. So node1's data must be wiped once and resynced from the other two nodes. With this change and the table below that resync works, and the old keys cannot be used to make a fork while a node is below block 163,015.
What changes
aura.rs:PastAuthoritySet { until_block, authorities }andAura.past_sets. A block's set is the first past set whoseuntil_blockreaches its height, else the current set. Each set keeps its own slot length (60 / len). With no past sets nothing changes.[[previous_authorities]]tables, each withuntil_block(the last block that set authored) andauthorities. They go at the end of the TOML file.Blockchain::with_previous_authorities, wired inmain.rs. It checks the lists at start likeauthorities(not empty, no duplicates, at most 60), and that they come oldest first.CLAUDE.md, in the Config section.f4a2e13):test.ymlruns onubuntu-24.04instead ofubuntu-latest.ubuntu-latestbecame 26.04 in October 2026, and its newer compiler cannot build the pinnedlibrocksdb-sys(RocksDB 8.10 headers lack#include <cstdint>). Earlier green runs restored a cached build, which hid this until the new image changed the cache key. Any other PR to this repo fails the same way until the pin is onmain.What does not change
authoritiesalone, so a rotated-out key stays dead pastuntil_block.Tests
CI is green on
f4a2e13(run 40901110494): 118 unit tests and every integration suite pass. The new tests, by name:aura::tests::an_old_block_is_checked_against_the_set_that_authored_itaura::tests::without_the_old_set_a_rotated_out_authors_block_is_refusedaura::tests::the_last_block_of_an_old_set_is_inclusive_and_the_next_one_is_notaura::tests::each_of_several_past_sets_covers_its_own_rangeaura::tests::a_past_set_of_another_size_keeps_its_own_slot_lengthaura::tests::a_block_of_an_old_set_is_still_bound_to_real_timeblockchain::tests::past_authority_sets_must_be_valid_and_oldest_firstconfiguration::tests::previous_authorities_default_to_noneandprevious_authorities_read_as_tables_oldest_firsta_node_that_missed_the_rotation_syncs_the_blocks_of_the_old_authority(tests/authority_rotation.rs): a node on the new set refuses block 1 from the old key, imports it once the old set is declared, and still refuses the old key at block 2.Not tried on a real network yet. That is the stage rollout below.
Rollout for stage (after the merge)
A merge builds an image, and stage moves its node pin by itself. Then a clutch-deploy change adds this to the end of
config/node-stage/node{1,2,3}.toml:Block 163,015 is the last block the old keys authored (explorer: 14:44:40 UTC). Block 163,016 is the first block of the new keys. The order of the two changes does not matter: an image without this change ignores the tables. node1 then needs its data wiped and a resync (see above); about 163,000 blocks at 100 blocks every 10 seconds take several hours.
Follow-up in clutch-deploy: correct
docs/AUTHORITY-ROTATION.mdand the rehearsal, which claim that history still validates after a rotation.🤖 Generated with Claude Code