| Safe Haskell | Safe-Inferred |
|---|---|
| Language | GHC2021 |
Hydra.Tx.Accumulator
Synopsis
- data HydraAccumulator
- $sel:unHydraAccumulator:HydraAccumulator :: HydraAccumulator -> Accumulator
- getAccumulatorHash :: HydraAccumulator -> ByteString
- getAccumulatorCommitment :: HydraAccumulator -> BuiltinBLS12_381_G1_Element
- computeG1CommitmentBytes :: Accumulator -> ByteString
- accumulatorSize :: HydraAccumulator -> Int
- maxAccumulatorSize :: Int
- checkAccumulatorSize :: HydraAccumulator -> Either (Int, Int) ()
- deployedFanoutBatchSize :: Int
- build :: [ByteString] -> HydraAccumulator
- buildFromUTxO :: forall tx. IsTx tx => UTxOType tx -> HydraAccumulator
- buildFromSnapshotUTxOs :: forall tx. IsTx tx => UTxOType tx -> Maybe (UTxOType tx) -> Maybe (UTxOType tx) -> (HydraAccumulator, HydraAccumulator)
- applyUTxODelta :: forall tx. IsTx tx => HydraAccumulator -> UTxOType tx -> UTxOType tx -> HydraAccumulator
- removeOutputs :: forall tx. IsTx tx => HydraAccumulator -> UTxOType tx -> HydraAccumulator
- crsG2Points :: Int -> [Point2]
- crsG1Points :: Int -> [Point1]
- requiredCRSPointCount :: HydraAccumulator -> Int
- defaultItems :: Int
- createMembershipProof :: [Element] -> HydraAccumulator -> [Point1] -> Either Text ByteString
- createMembershipProofFromUTxO :: forall tx. IsTx tx => UTxOType tx -> HydraAccumulator -> [Point1] -> Either Text ByteString
- createCRSG2Datum :: Int -> TxOutDatum ctx
Documentation
data HydraAccumulator Source #
Instances
| Show HydraAccumulator Source # | Shows only the element map: the derived instance would force the cached commitment, so showing head state in logs or test output would compute a BLS commitment as a side effect. |
Defined in Hydra.Tx.Accumulator | |
| Eq HydraAccumulator Source # | |
Defined in Hydra.Tx.Accumulator Methods (==) :: HydraAccumulator -> HydraAccumulator -> Bool Source # (/=) :: HydraAccumulator -> HydraAccumulator -> Bool Source # | |
$sel:unHydraAccumulator:HydraAccumulator :: HydraAccumulator -> Accumulator Source #
getAccumulatorHash :: HydraAccumulator -> ByteString Source #
Get a blake2b-256 hash of the accumulator commitment (compressed G1 point).
This is a pure function that returns a 32-byte deterministic hash of the
compressed G1 accumulator commitment. It is what gets signed by all parties
in the multi-signature and stored as accumulatorHash in on-chain datums.
Hashing the compressed G1 point (rather than the serialized map) binds the
signed hash to the exact G1 point stored in accumulatorCommitment,
allowing the on-chain validator to verify their consistency.
The result is cached inside HydraAccumulator as a lazy thunk and computed
at most once per value, regardless of how many times this function is called.
getAccumulatorCommitment :: HydraAccumulator -> BuiltinBLS12_381_G1_Element Source #
computeG1CommitmentBytes :: Accumulator -> ByteString Source #
Compute the compressed G1 commitment for an accumulator through the rust-accumulator FFI (divide-and-conquer FFT polynomial expansion and a Pippenger multi-scalar multiplication), which is orders of magnitude faster than expanding the polynomial in Haskell. Bit-for-bit equal to the PlutusTx reference path; see the equivalence properties and golden values in Hydra.Tx.AccumulatorSpec.
accumulatorSize :: HydraAccumulator -> Int Source #
Number of UTxOs tracked by the accumulator.
maxAccumulatorSize :: Int Source #
Maximum accumulator size, re-exported from KZGTrustedSetup for convenience.
checkAccumulatorSize :: HydraAccumulator -> Either (Int, Int) () Source #
Check an accumulator against the capacity of the embedded G1 CRS, yielding its size and the maximum when it does not fit.
SECURITY: computeG1CommitmentBytes enforces this limit with error, and it
is reached through the lazy commitment thunk of HydraAccumulator -- i.e.
from wherever that thunk is first forced, which includes 'ToJSON (Snapshot
tx)' and getSignableRepresentation. Every path that builds an accumulator
from data this node did not produce itself must therefore check the size
before the value can be forced, traced or echoed. This check is safe to run
on an over-capacity accumulator: like accumulatorSize it only folds the
element map and never touches the cached commitment or hash. See
validateClientInput (the client API boundary) and
HeadLogic (the protocol-logic backstop).
deployedFanoutBatchSize :: Int Source #
Largest subset a single fanout transaction can distribute, re-exported from
KZGTrustedSetup for convenience.
build :: [ByteString] -> HydraAccumulator Source #
Arguments
| :: forall tx. IsTx tx | |
| => UTxOType tx | The UTxO set to build the accumulator from |
| -> HydraAccumulator | The resulting accumulator containing one element per TxOut |
Build an accumulator from a UTxO by serializing each individual TxOut.
This is the CORRECT way to build an accumulator for partial fanout proofs. Each TxOut becomes a separate element in the accumulator, allowing you to later prove that a subset of TxOuts was part of the original set.
The serialization matches how hashTxOuts works on-chain:
Each element = Builtins.serialiseData (toBuiltinData plutusTxOut)
Example usage:
> -- Build accumulator from the full UTxO set
> let fullAcc = buildFromUTxO Tx utxo
>
> -- Later, prove a subset exists
> let crs = crsG1Points (requiredCRSPointCount fullAcc)
> result <- createMembershipProofFromUTxO Tx subsetUTxO fullAcc crs
This approach allows proving that 2 out of 5 UTxOs are part of the original set, which is essential for partial fanout functionality.
buildFromSnapshotUTxOs Source #
Arguments
| :: forall tx. IsTx tx | |
| => UTxOType tx | The main snapshot UTxO set |
| -> Maybe (UTxOType tx) | UTxOs to be committed (if any) |
| -> Maybe (UTxOType tx) | UTxOs to be decommitted (if any) |
| -> (HydraAccumulator, HydraAccumulator) | The snapshot accumulator and the applied accumulator |
Build the two accumulators a snapshot commits to, see Snapshot.
When a snapshot is signed nobody knows yet whether its pending increment or decrement will land on chain before the head is closed, and which outputs the head owes at close time depends on that. So the parties sign a commitment for each of the two futures:
- the snapshot accumulator, over the head's content while it stays at the snapshot's version: the snapshot UTxO plus a pending decommit (still inside until the decrement lands); a pending commit is not yet inside.
- the applied accumulator, over the head's content once the pending action has been applied on chain: the snapshot UTxO plus a pending commit (the increment absorbed it); a pending decommit has been paid out.
Close and Contest store the one matching their redeemer kind (Unused/Any and
Used respectively, checkClose), so the on-chain
commitment always describes exactly the outputs a fanout must distribute.
With nothing pending both are the same value and are computed once.
Merging via UTxO union keeps the same canonical TxIn-sorted element order
used by every other accumulator call site, so the commitments stored in the
snapshot and all downstream proofs are built from the same element sets by
construction (the underlying HydraAccumulator is a Map keyed by element
bytes, so insertion order does not affect the commitment value).
Arguments
| :: forall tx. IsTx tx | |
| => HydraAccumulator | Accumulator built from the previous combined UTxO set |
| -> UTxOType tx | The previous combined UTxO set |
| -> UTxOType tx | The new combined UTxO set |
| -> HydraAccumulator |
Update an accumulator from one snapshot's combined UTxO set to the next
by adding and removing only the changed outputs, avoiding the per-output
serialization and hashing of a full rebuild. Extensionally equal to
buildFromUTxO on the new set (see the property in
Hydra.Tx.AccumulatorSpec): the underlying map tracks element multiplicity
and the TxIn-keyed set difference removes exactly one occurrence per
consumed input. Falls back to a full rebuild if a removed element is
missing or has lower multiplicity than the removals require, which would
indicate the given accumulator was not built from the given previous UTxO
set.
Arguments
| :: forall tx. IsTx tx | |
| => HydraAccumulator | Accumulator to remove from |
| -> UTxOType tx | Outputs to remove, one occurrence each |
| -> HydraAccumulator |
Remove one occurrence of each given output from an accumulator.
Unlike applyUTxODelta the outputs to remove are given directly, so the
result does not depend on which TxIn holds them. That matters wherever the
two sets are related by content rather than by TxIn: partial fanout accepts
a user selection as a sub-multiset of outputs (see
isSubMultisetOf), so the same TxIn can carry a different
TxOut on each side and a TxIn-keyed difference would then remove the
wrong element, or none at all.
Removing exactly the distributed outputs is also what makes the on-chain
split identity A = P_K * A' hold by construction, for the A that was
verified against the head datum.
removeElement no-ops once an element's count is exhausted, so
an output that is not in the accumulator leaves a commitment that does not
bind the intended set; the on-chain identity then fails and the transaction
is rejected, exactly as a fresh build over an inconsistent set would be.
CRS (Common Reference String)
crsG2Points :: Int -> [Point2] Source #
Returns the first n G2 powers of tau from the EIP-4844 trusted setup.
Used as the on-chain CRS for verifying membership proofs:
[G2, τ·G2, ..., τ^(n-1)·G2].
crsG1Points :: Int -> [Point1] Source #
Returns the first n G1 powers of tau from the EIP-4844 trusted setup.
Used as the off-chain CRS for building accumulator commitments and membership proofs:
[G1, τ·G1, ..., τ^(n-1)·G1].
requiredCRSPointCount :: HydraAccumulator -> Int Source #
Returns the number of G1 CRS points required for this accumulator.
An n-element accumulator polynomial has degree n, so needs n+1 G1 points
[G1, τ·G1, ..., τⁿ·G1] to compute the commitment A(τ)·G1 and proofs.
n is the total element count including duplicates (sum of all counts).
defaultItems :: Int Source #
Number of G2 points published in the on-chain CRS UTxO datum.
This is the deployed G2 CRS length. It directly caps the largest
subset that can be verified in a single fanout / partial-fanout pairing
check: a subset of N elements yields a polynomial of degree N (one
(X - sᵢ) factor per element), and the on-chain MSM to evaluate
P_S(τ)·G2 needs N+1 G2 points, so the deployed batch limit is
deployedFanoutBatchSize.
The trusted-setup file embeds 65 G2 points (see
maxFanoutBatchSize); only the first defaultItems
are written into the CRS UTxO at script-registry publication time
(see buildScriptPublishingTxs). The head
validator is compiled against the hash of that CRS datum and rejects
any other one, so raising defaultItems means re-publishing the CRS
UTxO and recompiling the scripts. It is bounded above by
KZGTrustedSetup.maxFanoutBatchSize + 1.
Membership proofs for partial fanout
createMembershipProof Source #
Arguments
| :: [Element] | The subset of elements to prove membership of (e.g., UTxOs being fanned out) |
| -> HydraAccumulator | The full accumulator from the confirmed snapshot |
| -> [Point1] | Common Reference String (CRS) for the cryptographic proof |
| -> Either Text ByteString | Returns the compressed proof point, or an error if elements are missing or CRS is too short |
Create a membership proof for a subset of UTxO elements.
This function uses getPolyCommitOverG1 from haskell-accumulator's Bindings module: https://github.com/cardano-scaling/haskell-accumulator/blob/main/haskell-accumulator/lib/Bindings.hs
Given a subset of elements and the full accumulator, it: 1. Removes the subset elements from the accumulator 2. Computes a polynomial commitment over G1 for the remaining elements 3. Returns the proof as a compressed G1 point
createMembershipProofFromUTxO Source #
Arguments
| :: forall tx. IsTx tx | |
| => UTxOType tx | The subset of UTxO to prove membership of (e.g., UTxOs being fanned out) |
| -> HydraAccumulator | The full accumulator from the confirmed snapshot (built with buildFromUTxO) |
| -> [Point1] | Common Reference String (CRS) for the cryptographic proof |
| -> Either Text ByteString | Returns the compressed proof point, or an error if elements are missing or CRS is too short |
Create a membership proof from a UTxO subset.
This function extracts individual TxOut elements from the subset UTxO and proves
they exist in the full accumulator. The full accumulator must be built using
buildFromUTxO for this to work correctly.
The proof is verified on-chain via e(commitment_G1, G2) = e(proof_G1, P_S(τ)·G2).
createCRSG2Datum :: Int -> TxOutDatum ctx Source #