Skip to content

fix(grovedb): require proofs to descend to the query path - #7

Merged
pauldelucia merged 1 commit into
masterfrom
fix/grovedb-envelope-descent
Aug 27, 2026
Merged

fix(grovedb): require proofs to descend to the query path#7
pauldelucia merged 1 commit into
masterfrom
fix/grovedb-envelope-descent

Conversation

@pauldelucia

Copy link
Copy Markdown
Contributor

What

GroveDB's layered verifier looks the next layer up by the proof envelope's lower_layers map key, and that key is not hash-bound. A prover who renames or drops the entry for a subtree on the query path gets the same root hash with that subtree's results silently gone — a proof of K = V verifies as a proof that K is absent.

check_envelope(proof, expected_path) closes it: the envelope must carry a lower layer for every segment of the query path (once a layer is present its root is hash-bound to the parent), nothing may hang below the path, and prove_options — prover-chosen bytes that steer limit accounting — is pinned to the chain default. VerifyOptions.expected_path runs it, and GroveDBProofVerifier passes the path from a PathQueryData through.

Testing

  • test_honest_envelope_descends_to_the_path — a well-formed envelope for [subgroves, aave-v3-lending, indexed, Supply] passes.
  • test_renamed_lower_layer_is_rejected — renaming the entry for a path subtree fails with does not descend.
  • test_dropped_lower_layer_is_rejected — dropping the deepest layer fails the same way.
  • test_extra_lower_layers_below_the_path_are_rejected — anything hanging below the queried path is refused.
  • test_non_default_prove_options_is_rejected.
  • test_verify_options_expected_path_invokes_the_guard — wiring: verify_grovedb_proof calls the guard when a path is set.

The guard is tested by constructing the decoded envelope directly, so it does not depend on the byte decoder. Full suite: 305 passed (299 before), 13 skipped.

Why

A client that treats "not in the results" as "absent" would accept a lie of omission from any API server, with a valid root. Requiring the proof to reach the queried path is what makes an empty result at that path a proven absence.

Note (separate, larger issue — not fixed here)

This SDK's proof decoder reads fixed-width (u32 variant, u64 lengths, big-endian) integers, but grovedb 3.1.0 serializes GroveDBProof with bincode varint encoding. So the decoder does not parse a real grovedb proof (a real single-key proof reports version 4195337), which is why the tests above build the decoded structure directly rather than from real proof bytes. The TypeScript SDK's decoder is varint and does parse real proofs; aligning this one (and the Go/Swift decoders) is a larger follow-up worth its own PR.

GroveDB's layered verifier finds the next layer by the proof envelope's lower_layers map key, which is not hash-bound. A prover who renames or drops the entry for a subtree on the query path gets the same root hash with that subtree's results silently gone, so a proof of one value verifies as a proof of absence. check_envelope walks the envelope down every segment of the query path and pins prove_options to the chain default; the ProofVerifier passes the path through.
@pauldelucia
pauldelucia merged commit a16a5c7 into masterAug 27, 2026
1 check passed
@pauldelucia
pauldelucia deleted the fix/grovedb-envelope-descent branch August 27, 2026 06:08
Sign up for freeto 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.

1 participant

@pauldelucia