Skip to content

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

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

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.

This adds checkEnvelope(proof, expectedPath): 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. verifyGroveDBProof takes expectedPath; verifyItemProof passes its path through; verifyQueryProof takes options.path.

The TS verifier was already refusing the renamed-layer case by accident — the orphaned tree node falls into the leaf branch and its value bytes fail the committed-valueHash binding — so for this SDK the change is a stated property with a precise error, plus the prove_options pin. The fixtures are real grovedb 3.1.0 proofs over the indexed-data layout [subgroves, aave-v3-lending, indexed, Supply].

Testing

  • real proofs verify to the root at their path — four real proofs (single key, range, absent key, limited range) verify at their path with the right result counts.
  • without the path check … leaf binding — pins that the bare verifier still rejects the renamed layer, and why.
  • renamed lower layer is rejected at the query path / dropped lower layer … — both forgeries fail with does not descend.
  • a shorter or longer path is rejected — the path must match the envelope exactly.
  • non-default prove_options is rejected even without a path.
  • verifyItemProof descends when given the path / verifyQueryProof descends when options.path is given — the wrappers wire it.
  • Existing suites: the hardening test's synthetic wrapper now emits the default prove_options byte like every real proof; the fixture test accepts the new wrong-path message.

Full suite: 485 passed (481 before), 11 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.

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. checkEnvelope walks the envelope down every segment of the query path and pins prove_options to the chain default; verifyItemProof and verifyQueryProof pass the path through when they have it.
@pauldelucia
pauldelucia merged commit 6235113 into master Aug 27, 2026
3 checks passed
@pauldelucia
pauldelucia deleted the fix/grovedb-envelope-descent branch August 27, 2026 06:08
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.

1 participant