State Proofs - Algorand Specifications

Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Algorand Specifications

State Proofs (a.k.a. Compact Certificates) allow external parties to efficiently validate Algorand blocks.

The technical report provides the overall approach of State Proofs; this section describes the specific details of how State Proofs are realized in Algorand.

As a brief summary of the technical report, State Proofs operate in three steps:

  1. The first step is to commit to a set of participants eligible to produce signatures, along with a weight for each participant. In Algorand’s case, these end up being the online accounts, and the weights are the account μALGO balances.

  2. The second step is for each participant to sign the same message, and broadcast this signature to others. In Algorand’s case, the message would contain a commitment on blocks in a specific period.

  3. The third step is for Relay Nodes to collect these signatures from a significant fraction of participants (by weight) and generate a State Proof. Given enough signatures, a Relay Node can form a State Proof, which effectively consists of a small number of signatures, pseudo-randomly chosen out of all the signatures.

The resulting State Proof proves that at least some ProvenWeightProvenWeight of participants have signed the message. The actual weight of all participants who have signed the message must be greater than ProvenWeightProvenWeight.

The State Proof scheme requires a commitment to a dense array of participants, in some well-defined order. Algorand uses Vector Commitment to guarantee this property.

Leaf hashing is done in the following manner:

Leaf=Hash(spp||Weight||KeyLifeTime||StateProofpk),Leaf=Hash(spp||Weight||KeyLifeTime||StateProofpk),

for each online participant.

Where:

Similarly to the participant commitment, the State Proof scheme requires a commitment to a signature array.

Leaf hashing is done in the following manner:

Leaf=Hash(sps||L||SerializedMerkleSignature),Leaf=Hash(sps||L||SerializedMerkleSignature),

for each online participant.

Where:

When a signature is missing in the signature array, i.e., the prover didn’t receive a signature for this slot, the slot would be decoded as an empty string. As a result, the vector commitment leaf of this slot would be the hash value of the constant domain separatorMB (the bottom leaf).

As described in the technical report section IV.A, a State Proof contains a pseudorandomly chosen set of signatures. The choice is made using a coin.

In Algorand’s implementation, the coin derivation is made in the following manner:

Hin=(spc||Version||ParticipantCommitment||ln(ProvenWeight)||SignatureCommitment||SignedWeight||StateProofMessageHash)Hin=(spc||Version||ParticipantCommitment||ln⁡(ProvenWeight)||SignatureCommitment||SignedWeight||StateProofMessageHash)

Where:

For short, we refer below to the revealed signatures simply as “reveals”.

We compute:

R=SHAKE256(Hin)R=SHAKE256(Hin)

Then, for every reveal, we:

This would guarantee a uniform random coin in [0,SignedWeight)[0,SignedWeight).

A State Proof consists of seven fields:

PositionsToReveal=[IntToInd(coin0),…,IntToInd(coinNumReveals−1)]PositionsToReveal=[IntToInd(coin0),…,IntToInd(coinNumReveals−1)]

Where IntToIndIntToInd and NumRevealsNumReveals are defined in the technical report, section IV.

Note that, although the State Proof contains a commitment to the signatures, it does not contain a commitment to the participants.

The set of participants must already be known to verify a State Proof. In practice, a commitment to the participants is stored in the Block Header of an earlier block, and in the State Proof message proven by the previous State Proof.

A State Proof is valid for the message hash, with respect to a commitment to the array of participants, if:

TT is defined in the technical report, section IV.

In order for the SNARK prover for State Proofs to be efficient enough, we must impose an upper-bound MaxRevealsCMaxRevealsC on the number of “reveals” the State Proof can contain, while still reaching its target security strength targetC=192targetC=192. Concretely, we currently wish to set MaxRevealsC=480MaxRevealsC=480.

Similarly, the quantum-secure verifier aims for a larger security strength of targetPQ=256targetPQ=256, and we can also impose an upper-bound MaxRevealsPQMaxRevealsPQ on the number of reveals it can handle. (Recall that a smaller number of reveals means that SignedWeightProvenWeightSignedWeightProvenWeight must be larger to reach particular security strength, so we cannot set MaxRevealsCMaxRevealsC or MaxRevealsPQMaxRevealsPQ too low.)

To generate a SNARK proof, we need to be able to “downgrade” a valid State Proof with targetPQtargetPQ strength into one with merely targetCtargetC strength, by truncating some of the reveals to stay within the bounds.

First, let us prove that a valid State Proof with (( NRev_{PQ} ) number of reveals that satisfies Equation (5) in SNARK-Friendly Weight Threshold Verification for a given targetCtargetC can be “downgrade” to have:

NumRevealsC=ceil(NumRevealsPQ×targetCtargetPQ)NumRevealsC=ceil(NumRevealsPQ×targetCtargetPQ)

We remark that values d,b,T,Y,Dd,b,T,Y,D (in SNARK-Friendly Weight Threshold Verification) only depend on SignedWeightSignedWeight, but not the number of reveals nor the target.

Hence, we just need to prove that:

NumRevealsC>=targetC×T×YDNumRevealsC>=targetC×T×YD

Which implies it is sufficient to prove:

NumRevealsPQ×targetCtargetPQ>=targetC×T×YDNumRevealsPQ×targetCtargetPQ>=targetC×T×YD

Since targetC>0targetC>0 and targetPQ>0targetPQ>0, we just need to prove that:

NumRevealsPQ>=targetPQ×T×YD.NumRevealsPQ>=targetPQ×T×YD.

This last inequality holds since the State Proof satisfies Equation (5).

For a given MaxRevealsCMaxRevealsC and the desired security strengths, we need to calculate a suitable targetPQtargetPQ bound so that the following property holds:

Since the “downgraded“ State Proof has:

NumRevealsC=ceil(NumRevealsPQ×targetCtargetPQ),NumRevealsC=ceil(NumRevealsPQ×targetCtargetPQ),

And NumRevealsPQ<=MaxRevealsPQNumRevealsPQ<=MaxRevealsPQ, and NumRevealsC<=MaxRevealsCNumRevealsC<=MaxRevealsC we get:

MaxRevealsC<=ceil(MaxRevealsPQ×targetCtargetPQ)MaxRevealsC<=ceil(MaxRevealsPQ×targetCtargetPQ)

And we can set

MaxRevealsC<=ceil(MaxRevealsPQ×targetCtargetPQ)MaxRevealsC<=ceil(MaxRevealsPQ×targetCtargetPQ)

Since reveals do not bottleneck the quantum-secure verifier, we can take:

MaxRevealsPQ<=floor(MaxRevealsC×targetPQtargetC)MaxRevealsPQ<=floor(MaxRevealsC×targetPQtargetC)

To be an equality, i.e., MaxRevealsPQ=floor(…)MaxRevealsPQ=floor(…).

Therefore, we must set MaxRevealsPQ=640MaxRevealsPQ=640.