fix(protocol): reject a duplicate account ID prefix before inserting - #3867
Open
erkancamli wants to merge 2 commits into
Open
erkancamli wants to merge 2 commits into
erkancamli wants to merge 2 commits into
Conversation
This was referenced Sep 14, 2026
AccountTree::insert wrote the leaf and only then rejected a duplicate prefix, leaving the rejected entry behind and breaking the one entry per leaf invariant that the accessors rely on. Rebased onto next; the CHANGELOG entry now appends to the existing Unreleased Fixes section instead of opening a second one.
erkancamli
force-pushed
the
fix/account-tree-duplicate-prefix
branch
from
September 15, 2026 18:45
b2e0feb to
950ec2e
Compare
Author
|
Rebased onto The cause was mine: all three of my open PRs opened a new Separately, no workflow runs have been approved on any of the three, so there are no checks for a reviewer to go on. Would someone mind enabling them? |
The if-let only covered SmtLeaf::Single, so a Multiple leaf fell through and was inserted into silently. The base code's num_entries() >= 2 check did catch that case, so the first revision was a small regression there. Match all three variants the way compute_mutations already does a few lines below. It reports Multiple as unreachable!; this returns DuplicateIdPrefix instead, so a tree that somehow already holds one is not grown further and the caller gets an error rather than a panic. Also documents that the tree is left unmodified on error, which is the whole point of the change.
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.
What
AccountTree::insertinserts into the SMT and only then asks whether the leaf now holds two entries:The error is returned, but the insert is never undone. The caller is told the operation failed while the tree has already changed: the root advanced, and the leaf now holds two entries, which the type documents as impossible ("Each account ID prefix must be unique").
Three places rely on that invariant with
unreachable!(), so the next call on the same tree panics rather than erroring:account_commitments():let SmtLeaf::Single((key, commitment)) = leaf else { unreachable!("empty and multiple variant should never be encountered") }compute_mutations():SmtLeaf::Multiple(_) => unreachable!(...)AccountTree::newFor a block producer that applies account updates one at a time, a single rejected duplicate-prefix update silently moves the account tree root away from what consensus expects and turns every later
account_commitmentsorcompute_mutationscall on that tree into a process-killing panic. Recovery means rebuilding the tree.Fix
Check the leaf before mutating, which is exactly what
PartialAccountTree::insertin the sibling module already does:Updating an existing account keeps working, since that path hits the same key.
insert_fails_on_duplicate_prefixandinsert_succeeds_on_multiple_updatesare unchanged and still pass.Test
insert_with_duplicate_prefix_does_not_mutate_treereuses the existingsetup_duplicate_prefix_ids()helper, inserts the first ID, takes the root, expects the second insert to fail, and then asserts the tree is untouched. The last assertion is the important one:account_commitments()panics on a multi-entry leaf, so before the fix the test does not merely fail an assert, it aborts.Notes
AssetVault::add_non_composable_assethas the same insert-then-report shape and is worth a look, but it is a different crate path and a different argument, so I left it out of this PR. Happy to send it separately.--ignore-rust-version.cargo fmt --checkon stable disagrees with this repo's nightly rustfmt config across the whole crate (build.rsincluded), so I matched the surrounding style by hand rather than trusting it. Please lean on CI for both.