Documentation

VCVio.CryptoFoundations.MerkleTree.Inductive.Extractability

Inductive Merkle Tree Extractability #

This file develops extractability for the inductive Merkle tree commitment scheme. The extractor reconstructs a partial tree from the committing adversary's query log and the opened root, and the main theorem bounds the probability that an adversary opens a leaf that disagrees with the extracted tree.

Main definitions #

We make several definitions to set up and analyze the extractability game under namespace InductiveMerkleTree:

Main results #

Note that our bound is looser than the SNARGs book's bound in Lemma 18.5.1 of ((qb - 1) * qb) / 2 / |α| + (depth + 1) * 2 * size / |α|. This is because we have simplified the proof at the expense of tightness (tighter, that is, in the qb >> size case) by analyzing collisions for the full game at once. A future improvement might be to re-structure the proof to recover the tighter bound.

TODO #

References #

@[implicit_reducible]

Local IsUniformSpec opt-in for spec α: the single Merkle hash oracle samples uniformly from α whenever α is finite and inhabited. Kept local so that downstream files outside this module do not silently pick up uniform semantics for spec α.

Instances For

    Adversary #

    An adversary in the Merkle tree extractability game, packaged as a single two-phase object: a committing phase that produces a claimed root together with auxiliary state, and an opening phase that consumes the auxiliary state to produce a (leaf index, leaf value, authentication path) triple.

    Instances For

      The combined two-phase execution of 𝒜 has total query bound qb.

      Instances For

        Extractability game #

        def InductiveMerkleTree.extractorChildren {α : Type} [DecidableEq α] (cache : (spec α).QueryLog) (a : α) :
        Option (α × α)

        The child-decomposition function used by the Merkle-tree extractor. Given a cache and a node value a, look up the first query in cache whose response is a and return its input pair; if no such entry exists, return none.

        Instances For

          The extraction algorithm for Merkle trees: from a query log cache, a root, and a skeleton s, build a partial tree of type FullData (Option α) s by walking down from root. A node with value some a looks up the unique log entry whose response is a and uses its input pair as the children's values; in every other case (no matching entry, or the parent is already none) both children are none. Implemented as optionPopulateDown driven by extractorChildren.

          Instances For

            The game for extractability.

            This is represented as a single OracleComp that runs the committing adversary, extractor, opening adversary, and verifier in sequence and returns the transcript of the execution.

            Instances For

              The event that the adversary wins the extractability game: verification passes but the extracted leaf or proof does not match.

              Instances For

                If the adversary 𝒜 has two-phase total query bound qb, then the full extractability game has total query bound qb + s.depth.

                The extra s.depth accounts for the verifyProof step, which traverses the path from the queried leaf to the root, making at most s.depth oracle queries.

                def InductiveMerkleTree.ChainInLog {α : Type} {s : BinaryTree.Skeleton} (log : (spec α).QueryLog) (leaf root : α) (idx : BinaryTree.SkeletonLeafIndex s) :

                Predicate stating that log contains a hash chain from leaf (combined with the sibling values in proof) up to root along the path determined by idx.

                Instances For
                  theorem InductiveMerkleTree.logHasCollision_of_chainInLog_of_ne {α : Type} {s : BinaryTree.Skeleton} (idx : BinaryTree.SkeletonLeafIndex s) (log : (spec α).QueryLog) (root x y : α) (proof₁ proof₂ : List.Vector α idx.depth) (hne : (x, proof₁) (y, proof₂)) (hc₁ : ChainInLog log x root idx proof₁) (hc₂ : ChainInLog log y root idx proof₂) :

                  Log-level binding (Collision Lemma at the log level). Log-formalized analog of getPutativeRootWithHash_binding_collision: two distinct openings (x, proof₁) ≠ (y, proof₂) of the same root at the same index, both witnessed by hash chains ChainInLog in the same log, force log to contain a hash collision (two log entries with equal responses but distinct inputs).

                  theorem InductiveMerkleTree.chainInLog_of_extractor_get_ne_none {α : Type} [DecidableEq α] {s : BinaryTree.Skeleton} (idx : BinaryTree.SkeletonLeafIndex s) (log : (spec α).QueryLog) (root : α) (h_ne_none : (extractor s log root).get idx.toNodeIndex none) :
                  ∃ (extLeaf : α) (extProof : List.Vector α idx.depth), (extractor s log root).get idx.toNodeIndex = some extLeaf (generateProof (extractor s log root) idx).toList = List.map some extProof.toList ChainInLog log extLeaf root idx extProof

                  Extractor recovery to a log chain. When the extractor's path at idx is intact (the value there is ≠ none), the extracted leaf value and proof form a hash chain ChainInLog in the log. The conclusion bundles three facts: the extracted leaf (extLeaf), the recovered authentication path (extProof), and a chain witness in log connecting them to root.

                  theorem InductiveMerkleTree.extractability {α : Type} [DecidableEq α] [Fintype α] [Inhabited α] {s : BinaryTree.Skeleton} (𝒜 : Adversary α s) (qb : ) (h_IsQueryBound_qb : 𝒜.IsTwoPhaseTotalQueryBound qb) :

                  The extractability theorem for Merkle trees.

                  Adapting from the SNARGs book Lemma 18.5.1:

                  For any adversary 𝒜 whose committing and opening phases together obey the two-phase total query bound qb, if the game runs 𝒜.commit and 𝒜.opening, and the extractor algorithm is run on the resulting cache and root, then with probability at most κ does 𝒜 "win the extractability game", i.e. simultaneously

                  • the merkle tree verification passes on the proof from 𝒜.opening
                  • but the extracted (leaf value, proof) pair does not match the adversary's (leaf value, proof) pair

                  Where κ is 1/|α| * ((qb + s.depth)^2 / 2 + 1).