$$\def\Fq{{\mathbb{F}_q}} \def\Ftwo{{\mathbb{F}_2}} \def\Zp{{\mathbb{Z}_p}} \def\Zbin{{\mathbb{Z}_{2^m}}} \def\thin{{\thinspace}} \def\Byte{{\mathbb{B}}} \def\Bit{{\{0, 1\}}} \def\typecolon{\mathbin{\large :}} \def\neg{{\text{-}}} \def\constb{{\textbf{const }}} \def\as{{\textbf{ as }}} \def\msb{{\textsf{msb}}} \def\if{{\text{if }}} \def\ifb{{\textbf{if } \hspace{1pt}}} \def\bi{{\ \ }} \def\hso{{\hspace{1pt}}} \def\Function{{\textbf{Function: }}} \def\init{{\textsf{init}}} \def\for{{\textbf{for }}} \def\foreach{{\textbf{for each }}} \def\bcolon{{\hspace{1pt} \textbf{:}}} \def\state{{\textsf{state}}} \def\xor{\oplus_\text{xor}} \def\bit{{\textsf{bit}}} \def\bits{{\textsf{bits}}} \def\line#1{{{\small \rm \rlap{#1.}\hphantom{10.}} \ \ }} \def\while{{\textbf{while }}} \def\pluseq{\mathrel{+}=} \def\return{{\textbf{return }}} \def\else{{\textbf{else}}} \def\len{{\textbf{len}}} \def\preimage{{\textsf{preimage}}} \def\DomainTag{{\text{DomainTag}}} \def\Arity{{\text{Arity}}} \def\HashType{{\text{HashType}}} \def\MerkleTree{{\text{MerkleTree}}} \def\ConstInputLen{{\text{ConstInputLen}}} \def\Mds{{\mathcal{M}}} \def\la{{\langle}} \def\ra{{\rangle}} \def\do{{\textbf{do }}} \def\timesb{{\textbf{ times}}} \def\RC{{\text{RoundConstants}}} \def\push{{\textbf{.push}}} \def\dotprod{{{\boldsymbol\cdot} \hso}} \def\row{{\textsf{row}}} \def\acc{{\textsf{acc}}} \def\push{{\textbf{.push}}} \def\extend{{\textbf{.extend}}} \def\reverse{{\textbf{reverse}}} \def\MdsInv{{\mathcal{M}^{\text{-} 1}}} \def\deq{\overset{\diamond}=} \def\alloc{{\textsf{alloc}}} \def\allocpub{{\textsf{alloc_pub}}} \def\bitlen{{\textbf{bitlen}}} \def\typecolon{\mathbin{\large :}} \def\CommDOld{{\text{CommDOld}}} \def\CommDNew{{\text{CommDNew}}} \def\CommROld{{\text{CommROld}}} \def\CommRNew{{\text{CommRNew}}} \def\Hs{{\textbf{Hs}}} \def\pub{{\text{pub}}} \def\priv{{\text{priv}}} \def\lebitsgadget{{\textsf{le_bits_gadget}}} \def\poseidongadget{{\textsf{poseidon_gadget}}}$$ # Empty-Sector-Update Circuit Spec (Nov-2021) ## Notation and Constants ### General $[n]$ The set containing the first $n$ unsigned integers $(0, 1, \ldots, n - 1)$. $i \in [n]$ Indicates that $i$ is an index pointing to an element in an array of $n$ elements; equivalent to $i \in \mathbb{Z}$ where $0 \leq i < n$. $x \typecolon \text{SomeType}^{[n]}$ Indicates that the value $x$ is an array of length $n$ where each element of the array is of type $\text{SomeType}$. $x \typecolon \Fq$ Indicates that $x$ is a prime field element, specifically an element of BLS12-381's scalar field having modulus $q$. $(a_0, \ldots, a_{n - 1})$ An array of $n$ elements. $\boldsymbol{a}[i \hso \textbf{..} \hso j]$ A slice $(a_i, \ldots, a_{j - 1})$ of the array $\boldsymbol{a} = (a_0, \ldots, a_{n - 1})$. ### Configuration Parameters $\text{SectorNodes} \in \{2^{30}, 2^{31}\}$ The number of nodes in the sector; allowed sector sizes are 32GiB and 64GiB. $k \in [P]$ The index of the partition in the sector for which a proof is being generated. $h \in \Hs$ The number of high bits taken from each challenge's [little-endian] binary representation. Filecoin currently defaults to using $h = 10$. ### Constants $\constb A = 128$ The number of apex-leafs per partition. $\constb \text{ApexLeafBits}, \text{ApexTreeHeight} = \log_2(A) = 7$ The number of bits required to specify the index of an apex-leaf; equivalently the height of each partition's apex-tree, i.e. each apex-tree contains $8 = 7 + 1$ rows (including leafs and root). $\constb \text{ApexLeafOffset} = \text{NodeBits} - (\text{PartitionBits} + \text{ApexLeafBits})$ The position in a $\text{TreeDNew}$ Merkle proof which contains an apex-leaf. $\constb C = 86$ The number of challenges per partition. $\constb \text{ChallengesPerPRF} = \left\lfloor{\lfloor \log_2(q) \rfloor \over \text{RandChallengeBits}}\right\rfloor = 9$ The maximum number of challenges which can be derived from a single Poseidon digest when Poseidon is used as a pseudorandom function (PRF). $\lfloor \log_2(q) \rfloor = 254$ is the number of fully utilized bits in a field element $\Fq$. $\constb \Hs = (7, 8, 9, 10, 11, 12)$ An array containing the allowed values for $h$. $\constb \text{h_index} = \Hs\textsf{.index_of}(h)$ The index of the configuration parameter $h$ in $\Hs$. $\constb \text{h_select} = 2^{\text{h_index}}$ An integer which encodes $\text{h_index}$. $\constb \text{h_select_bits} \typecolon \Bit^{[\len(\Hs)]} = \textsf{le_bits}(\text{h_select})$ The binary representation of $\text{h_select}$. Note that exactly one bit in $\text{h_select_bits}$ is set which gives the index ($\text{h_index}$) of the chosen $h$ in $\Hs$. $\constb \text{k_and_h_select} = k \vee (\text{h_select} \ll \text{PartitionBits})$ A packed integer containing $k$ and $\text{h_select}$. $\constb \text{k_and_h_select_bits} \typecolon \Bit^{[\text{PartitionBits} + \len(\Hs)]} = \text{k_bits} \mathbin{\Vert} \text{h_select_bits} = \textsf{le_bits}(\text{k_and_h_select})$ The binary representation of $\text{k_and_h_select}$. The first bit is the least-significant of $\text{k_bits}$ and last bit is the most-significant of $\text{h_select}$. $\constb \text{k_bits} \typecolon \Bit^{[\text{PartitionBits}]} = \textsf{le_bits}(k)$ The binary representation of the chosen partition-index $k$. $\constb \text{NodeBits} = \log_2(\text{SectorNodes}) = \begin{cases}30 & \text{32GiB sector-size} \\ 31 & \text{64GiB sector-size}\end{cases}$ The number of bits required to specify the index of a node in a sector. $\constb P = 16$ The number of partitions per sector. $\constb \text{PartitionBits}, \text{PartitionTreeHeight} = \log_2(P) = 4$ The number of bits required to specify a partition-index $k \in [P]$; equivalently the height of the top of a sector's $\text{TreeDNew}$ (starting at the tree row containing $P$ number of nodes and continuing upwards until the tree's root $\text{CommDNew}$ is reached). $\constb \text{PartitionNodes} = {\text{SectorNodes} \over P} = \begin{cases}2^{26} & \text{32GiB sector-size} \\ 2^{27} & \text{64GiB sector-size}\end{cases}$ The number of nodes in each partition of a sector. $\constb \text{PRFsPerPartition} = \left\lceil {C \over \text{ChallengesPerPRF}} \right\rceil = 10$ The number of calls to the Poseidon-PRF required to generate a partition's $C$ challenges. $\constb \text{RandChallengeBits} = \log_2(\text{PartitionNodes}) = \text{NodeBits} - \text{PartitionBits}$ The number of random bits generated per challenge; equivalently the number of bits required to specify the index of a node in a partition. Note that during challenge generation for a partition $k$, all challenge's will fall within the $k^{th}$ partition of the sector, i.e. the first $\text{RandChallengeBits}$ bits of each challenge are randomly chosen and the last $\text{PartitionBits}$ bits of each challenge are the same. $\constb \text{TreeDHeight} = \log_2(\text{SectorNodes})$ $\constb \text{TreeRHeight} = \log_8(\text{SectorNodes})$ The heights of trees that have been generated over a sector of values. $\text{TreeD}$'s are arity-2 and use $\text{Sha256}$, whereas $\text{TreeR}$'s are arity-8 and use $\text{Poseidon}$. Note that adding 1 to a tree's height (for the leafs) $\text{height} + 1$ gives the number of "rows" in the tree. ### Old and New Sectors $D_\text{old} \typecolon \Fq^{[\text{SectorNodes}]}$ An empty sector of data input to SDR-PoRep. Note that $D_\text{new}$ is not used by the empty-sector-update proof. $R_\text{old} \typecolon \Fq^{[\text{SectorNodes}]}$ The replica of $D_\text{old}$ output by SDR-PoRep. $D_\text{new} \typecolon \Fq^{[\text{SectorNodes}]}$ The data which is replacing the empty sector data $D_\text{old}$. $R_\text{new} \typecolon \Fq^{[\text{SectorNodes}]}$ The encoding of the new data $D_\text{new}$. ### Old and New Merkle Trees and Commitments $\text{TreeROld} = \text{OctTree}\textsf{::from_leaves}(R_\text{old})$ An arity-8 Merkle tree built over the replica $R_\text{old}$ output by running SDR-PoRep on an empty sector $D_\text{old}$. $\text{TreeDNew} = \text{BinTree}\textsf{::from_leaves}(D_\text{new})$ An arity-2 Merkle tree built over the data $D_\text{new}$ which is replacing that of the old empty sector $D_\text{old}$. $\text{TreeRNew} = \text{OctTree}\textsf{::from_leaves}(R_\text{new})$ An arity-8 Merkle tree built over the empty-sector-update encoding $R_\text{new}$ of the new sector data $D_\text{new}$. $\text{CommROld} \typecolon \Fq = \text{Poseidon}((\text{CommC}, \text{TreeROld}\textsf{.root}))$ $\text{CommDNew} \typecolon \Fq = \text{TreeDNew}\textsf{.root}$ $\text{CommRNew} \typecolon \Fq = \text{Poseidon}((\text{CommC}, \text{TreeRNew}\textsf{.root}))$ Commitments to three Merkle trees. Note that $\text{CommC}$ is a commitment which was generated by SDR-PoRep when encoding $D_\text{old}$ into $R_\text{old}$. ### Merkle Proofs $\textbf{struct } \text{BinTreeProof}\ \{$ $\quad \textsf{leaf} \typecolon \Fq,$ $\quad \textsf{path} \typecolon \Fq^{[\text{TreeDHeight}]},$ $\}$ Arity-2 Merkle proof type. $\textbf{struct } \text{OctTreeProof}\ \{$ $\quad \textsf{leaf} \typecolon \Fq,$ $\quad \textsf{path} \typecolon {\Fq^{[7]}}^{[\text{TreeRHeight}]},$ $\}$ Arity-8 Merkle proof type. ### Empty-Sector-Update Proof $\textbf{struct}\ \text{PartitionProof}\ \{$ $\quad \text{ChallengeProofs},$ $\quad \text{ApexLeafs},$ $\}$ Represents an empty-sector-update proof. One $\text{PartitionProof}$ is generated for each partition $k$ of an updated sector, thus $P$ number of $\text{PartitionProof}$'s are generated per updated sector. $C$ number of challenges are generated per partition, thus each $\text{PartitionProof}$ contains $C$ number of $\text{ChallengeProof}$'s. $\textbf{struct } \text{ChallengeProof}\ \{$ $\quad \textsf{proof_r_old} \typecolon \text{OctTreeProof},$ $\quad \textsf{proof_d_new} \typecolon \text{BinTreeProof},$ $\quad \textsf{proof_r_new} \typecolon \text{OctTreeProof},$ $\}$ Three Merkle proofs are generated per challenge, one for each of the trees $\text{TreeROld}$, $\text{TreeDNew}$, and $\text{TreeRNew}$. $\text{ChallengeProofs} \typecolon \text{ChallengeProof}^{[C]}$ Denotes all Merkle proofs generated for a partition. $\text{PartitionPath} \typecolon \Fq^{[\text{PartitionTreeHeight}]} = \text{ChallengeProof}\textsf{.proof_d_new}\textsf{.path}[\text{RandChallengeBits} \textbf{..}]$ Denotes the final portion of all $\text{TreeDNew}$ Merkle proofs (starting from the partition's apex-root and continuing upwards until $\text{TreeDNew}\text{.root}$ is reached) generated for this partition. Note a partition's $\text{PartitionPath}$ is constant for all challenges in a partition, thus $\text{ChallengeProof}$ may correspond to any challenge in a partition. $\text{ApexLeafs} \typecolon \Fq^{[A]} = \text{TreeDNew}\textsf{.row}_{\text{ApexLeafsOffset}}[kA \hso \textbf{..} (k + 1)A]$ The $k^{th}$ partition's apex-leafs, i.e. the $k^{th}$ chunk of $A$ nodes from the $\text{TreeDNew}$ row containing $P*A$ values. ## TreeDNew, Apex-Tree, and Partition-Tree ![](https://i.imgur.com/UffNh5W.png) $\text{ApexTree}$ and $\text{PartitionTree}$ are sub-trees of a sector's $\text{TreeDNew}$. Each $\text{TreeDNew}$ has exactly one $\text{PartitionTree}$ (the top 5 rows of $\text{TreeDNew}$) and $P$ unique $\text{ApexTree}$'s (one for each partition). The root of the $k^{th}$ partition's $\text{ApexTree}$ is the $k^{th}$ leaf in $\text{PartitionTree}$. Each Merkle challenge is the index of a node in a sector, thus challenges are either 30 or 31 bits (32GiB sectors contain $2^{30}$ nodes, 64GiB sectors contain $2^{31}$ nodes). ![](https://i.imgur.com/e1a0NGm.png) ### Partition Trees The last $\text{PartitionBits} = \log_2(P) = 4$ bits of a challenge represent the challenge's partition-index $k$. All challenges in a partition have the same partition bits. Partition bits are used during $\text{TreeDNew}$ Merkle proof validation to hash from a partition's apex-root up to $\text{TreeDNew}\textsf{.root}$. The top $4 + 1 = 5$ rows of $\text{TreeDNew}$ are a sub-tree (of height 4) called $\text{PartitionTree}$ whose leafs are a sector's apex-roots and whose root is $\text{TreeDNew}\textsf{.root}$. ### Apex Trees The $\text{ApexLeafBits} = \log_2(A) = 7$ bits preceding a challenge's partition bits represent the index of the challenge's apex-leaf in the partition's $\text{ApexLeafs}$. Apex-leaf bits are used during $\text{TreeDNew}$ Merkle proof validation to hash from a challenge's apex-leaf up to the partition's apex-root. ### Below Apex Leafs All challenge bits which precede the apex-leaf bits, i.e. the challenge's first $\text{ApexLeafOffset} = \text{NodeBits} - (\text{PartitionBits} + \text{ApexLeafBits})$ bits, are used during $\text{TreeDNew}$ Merkle proof validation to hash from the challenge's leaf in $\text{TreeDNew}$ up to the challenge's apex-leaf. ## Randomness Generation ### Challenge Generation Challenge generation is the process by which we generate $C = 86$ challenges per partition in an updated sector (of $\text{SectorNodes} = 2^{30}$ or $2^{31}$ number nodes) by calling Poseidon-PRF (128-bit security level, prime field $\Fq$, arity-2, width-3, see [Poseidon-PRF gadget](#Poseidon-and-Poseidon-PRF) for domain separation tag) $\text{PRFsPerPartition} = 10$ number of times to generate the binary representation of each of the partition's challenges. The preimage for each $j \in [\text{PRFsPerPartition}]$ call to Poseidon-PRF in a partition is the concatenation of the unique identifier $\text{CommRNew}$ associated with the updated sector and the unique index of the PRF call in the sector $\text{PRFsPerPartition} * k + j$. The number of fully utilized bits in a Poseidon digest is $\lfloor q \rfloor = 254$; if a sector partition contains $\text{PartitionNodes} = 2^{26}$ (32GiB sector-size) or $2^{27}$ (64GiB sector-size) nodes (where $P = 16$ is the number of partitions per sector), then the number of challenge generated from each PRF digest is $\text{ChallengesPerPRF} = \left\lfloor {254 \over 26} \right\rfloor = \left\lfloor {254 \over 27} \right\rfloor = 9$. We denote $\text{RandChallengeBits} = \log_2(\text{PartitionNodes}) = 26$ or $27$ as the number of bits taken from a PRF digest for each challenge. The $j^{th}$ chunk of $\text{ChallengesPerPRF}$ challenges $(c_0, \ldots, c_8)$ for partition $k \in [P]$ is generated as follows: \eqalign{ & \text{digest} \typecolon \Fq = \text{PoseidonPRF}((\text{CommRNew}, \text{PRFsPerPartition} * k + j)) \\ & c_0 = \textsf{le_bits}(\text{digest})[0 * \text{RandChallengeBits} \hso \textbf{..} \hso 1 * \text{RandChallengeBits}] \\ & \quad \vdots \\ & c_8 = \textsf{le_bits}(\text{digest})[8 * \text{RandChallengeBits} \hso \textbf{..} \hso 9 * \text{RandChallengeBits}] \\ } A challenge $c \in [\text{PartitionNodes}]$ is an index of a node in a partition of $\text{PartitionNodes}$ number of nodes; appending the little-endian binary representation of the partition-index $k \in [P]$ onto $c$'s binary representation $\textsf{le_bits}(c) \mathbin{\Vert} \textsf{le_bits}(k)$ yields the challenge's node-index across all nodes in the sector. ### Phi, Rho, and Labeling We assign a value $\text{phi}$ for each updated sector: $$\text{phi} = \text{PoseidonPRF}((\text{CommDNew}, \text{CommROld}))$$ where $\text{CommROld}$ is a commitment to the empty sector's SDR replica and $\text{CommDNew}$ is a commitment to the new data which will overwrite the empty sector. The Empty-Sector-Update algorithm takes a configuration parameter $h \in \Hs$ which specifies the number of high (i.e. most-significant) bits taken from each challenge's $c_i$ binary representation denoted $\text{high}_i$: $$\forall i \in [P * C]: \text{high}_i = c_i \gg (\log_2(\text{SectorNodes}) - h)$$ note that $P * C$ is the total number of challenges per sector. For each challenge $c_i$, a value $\text{rho}_i$ is computed from the challenge's $h$ high bits $\text{high}_i$ and the sector's $\text{phi}$. $$\forall i \in [P * C]: \text{rho}_i = \text{PoseidonPRF}((\text{phi}, \text{high}_i))$$ $\text{rho}_i$ is used to compute the encoding of the $i^{th}$ node's new data, see [Old and New Sectors](#Old-and-New-Sectors) for notation. $$R_\text{new}[i] = \text{R}_\text{old}[i] + D_\text{new}[i] * \text{rho}_i$$ ## Circuit ### Notation $a \typecolon \Fq \deq \textsf{alloc}(x)$ Denotes that $a$ is an allocated value in R1CS having the value $x$, where $x$ is an unallocated value. $a \typecolon \Fq \deq \textsf{alloc_pub}(x)$ Denotes that $a$ is an allocated value in R1CS and is a public-input. The prover assigns the value of $a$ in R1CS to that of $x$, where $x$ is an unallocated value. $\Fq$ A BLS12-381 scalar field element allocated in R1CS. $\Ftwo$ A boolean constrained BLS12-381 scalar field element allocated in R1CS. ### Implementation $\overline{\underline{\textbf{Circuit:}\ \text{SDR Empty Sector Update} \ \ }}$ $\textbf{#}\ \ \text{Public-inputs}$ $\line{1} \text{comm_r_old} \deq \allocpub(\CommROld_\pub)$ $\line{2} \text{comm_d_new} \deq \allocpub(\CommDNew_\pub)$ $\line{3} \text{comm_r_new} \deq \allocpub(\CommRNew_\pub)$ $\textbf{#}\ \ \text{Unpack k and h_select from their packed public-input}$ $\line{4} \text{k_and_h_select} \deq \allocpub(\text{k_and_h_select}_\pub)$ $\line{5} \text{k_and_h_select_bits} \typecolon \Ftwo^{[\text{PartitionBits} + \len(\Hs)]} \ \deq \lebitsgadget(\text{k_and_h_select})$ $\line{6} \text{k_bits} \typecolon \Ftwo^{[\text{PartitionBits}]} = \text{k_and_h_select_bits}[\textbf{..} \text{PartitionBits}]$ $\line{7} \text{h_select_bits} \typecolon \Ftwo^{[\len(\Hs)]} = \text{k_and_h_select_bits}[\text{PartitionBits}\textbf{..}]$ $\line{8} \text{k} \deq \textsf{lc_gadget}(\sum_{i \in [\text{PartitionBits}]} 2^i * \text{k_bits}[i]) \quad\quad\quad \textbf{#}\ 2^i \text{ is an unallocated constant}$ $\textbf{#}\ \ \text{Compute phi for this sector}$ $\line{9} \text{phi} \deq \textsf{poseidon_prf_gadget}((\text{comm_d_new}, \text{comm_r_old}))$ $\textbf{#}\ \ \text{Verify the roots of TreeROld and TreeRNew against public commitments}$ $\line{10} \text{comm_c} \deq \alloc(\text{CommC}_\priv)$ $\line{11} \text{root_r_old} \deq \alloc(\text{RootROld}_\priv)$ $\line{12} \text{root_r_new} \deq \alloc(\text{RootRNew}_\priv)$ $\line{13} \text{comm_r_old_calc} \deq \poseidongadget((\text{comm_c}, \text{root_r_old}))$ $\line{14} \text{comm_r_new_calc} \deq \poseidongadget((\text{comm_c}, \text{root_r_new}))$ $\line{15} \textsf{assert_eq}(\text{comm_r_old_calc}, \text{comm_r_old})$ $\line{16} \textsf{assert_eq}(\text{comm_r_new_calc}, \text{comm_r_new})$ $\textbf{#}\ \ \text{Verify the witnessed apex-leafs against the public commitment CommDNew}$ $\line{17} \text{apex_leafs} \typecolon \Fq^{[\text{ApexLeafs}]} \deq (\alloc(\text{ApexLeafs}_\priv[i]) \mid i \in [\text{ApexLeafs}])$ $\line{18} \text{apex_root} \deq \textsf{apex_root_gadget}(\text{apex_leafs})$ $\line{19} \text{partition_path} \typecolon \Fq^{[\text{PartitionTreeHeight}]} \deq (\textsf{alloc}(\text{PartitionPath}_\text{priv}[i]) \mid i \in [\text{PartitionTreeHeight}])$ $\line{20} \text{comm_d_new_calc} \deq \textsf{bintree_root_gadget}(\text{k_bits}, \text{apex_root}, \text{partition_path})$ $\line{21} \textsf{assert_eq}(\text{comm_d_new_calc}, \text{comm_d_new})$ $\textbf{#}\ \ \text{Generate this partition's challenges}$ $\line{22} \text{rand_challenge_bits} \typecolon {\Ftwo^{[\text{RandChallengeBits}]}}^{[C]} \deq \textsf{gen_challenge_bits_gadget}(\text{comm_r_new}, \text{k})$ $\textbf{#}\ \ \text{Verify each challenge's Merkle proofs}$ $\line{23} \for i \in [C]\bcolon$ $\line{24} \quad (\text{proof_r_old}, \text{proof_d_new}, \text{proof_r_new}) \leftarrow \text{ChallengeProofs}_\priv[i]$ $\quad\quad \ \ \textbf{#}\ \text{Get this challenge's binary representation}$ $\line{25} \quad \text{challenge_sans_partition_bits} \typecolon \Ftwo^{[\text{RandChallengeBits}]} = \text{rand_challenge_bits}[i]$ $\line{26} \quad \text{challenge_bits} \typecolon \Ftwo^{[\text{NodeBits}]} = \text{challenge_sans_partition_bits} \mathbin{\Vert} \text{k_bits}$ $\quad\quad\ \ \textbf{#}\ \text{Calculate this challenge's encoding}$ $\line{27} \quad \text{high} \deq \textsf{get_high_bits_gadget}(\text{challenge_bits}, \text{h_select_bits})$ $\line{28} \quad \text{rho} \deq \textsf{poseidon_prf_gadget}((\text{phi}, \text{high}))$ $\line{29} \quad \text{leaf_r_old} \deq \alloc(\text{proof_r_old}\textsf{.leaf})$ $\line{30} \quad \text{leaf_d_new} \deq \alloc(\text{proof_d_new}\textsf{.leaf})$ $\line{31} \quad \text{leaf_d_new_rho} \deq \textsf{mul_gadget}(\text{rho}, \text{leaf_d_new})$ $\line{32} \quad \text{leaf_r_new} \deq \textsf{add_gadget}(\text{leaf_r_old}, \text{leaf_d_new_rho})$ $\quad\quad\ \ \textbf{#}\ \text{Get this challenge's apex-leaf from the array of validated apex-leafs}$ $\line{33} \quad \text{apex_leaf_bits} \typecolon \Ftwo^{[\text{ApexLeafBits}]} = \text{challenge_sans_partition_bits}[\text{ApexLeafOffset} \hso \textbf{..}]$ $\line{34} \quad \text{apex_leaf} \deq \textsf{select_gadget}(\text{apex_leafs}, \text{apex_leaf_bits})$ $\quad\quad\ \ \textbf{#}\ \text{Verify this challenge's TreeDNew Merkle proof up to the apex-leaf}$ $\line{35} \quad \text{below_apex_leaf_bits} \typecolon \Ftwo^{[\text{ApexLeafOffset}]} = \text{challenge_sans_partition_bits}[\textbf{..} \hso \text{ApexLeafOffset}]$ $\line{36} \quad \text{below_apex_leaf_path} \typecolon \Fq^{[\text{ApexLeafOffset}]} \deq (\textsf{alloc}(\text{proof_d_new}\textsf{.path}[i]) \mid i \in [\text{ApexLeafOffset}])$ $\line{37} \quad \text{apex_leaf_calc} \typecolon \Fq \deq \textsf{bintree_root_gadget}($ $\phantom{\line{71}\quad} \quad \text{below_apex_leaf_bits},$ $\phantom{\line{72}\quad} \quad \text{leaf_d_new},$ $\phantom{\line{73}\quad} \quad \text{below_apex_leaf_path},$ $\phantom{\line{74}\quad})$ $\line{38} \quad \textsf{assert_eq}(\text{apex_leaf_calc}, \text{apex_leaf})$ $\quad\quad\ \ \textbf{#}\ \text{Verify this challenge's TreeROld Merkle proof}$ $\line{39} \quad \text{path_r_old} \typecolon {\Fq^{[7]}}^{[\text{TreeRHeight}]} \deq ((\alloc(\text{proof_r_old}\textsf{.path}[h][s]) \mid s \in [7]) \mid h \in [\text{TreeRHeight}])$ $\line{40} \quad \text{root_r_old_calc} \deq \textsf{octtree_root_gadget}(\text{challenge_bits}, \text{leaf_r_old}, \text{path_r_old})$ $\line{41} \quad \textsf{assert_eq}(\text{root_r_old_calc}, \text{root_r_old})$ $\quad\quad\ \ \textbf{#}\ \text{Verify this challenge's TreeRNew Merkle proof}$ $\line{42} \quad \text{path_r_new} \typecolon {\Fq^{[7]}}^{[\text{TreeRHeight}]} \deq ((\alloc(\text{proof_r_new}\textsf{.path}[h][s]) \mid s \in [7]) \mid h \in [\text{TreeRHeight}])$ $\line{43} \quad \text{root_r_new_calc} \deq \textsf{bintree_root_gadget}(\text{challenge_bits}, \text{leaf_r_new}, \text{path_r_new})$ $\line{44} \quad \textsf{assert_eq}(\text{root_r_new_calc}, \text{root_r_new})$ ## Gadgets $\textsf{add_gadget}(x \typecolon \Fq, y \typecolon \Fq) \rightarrow \Fq$ Allocates and returns the value $z = x + y$ and adds the R1CS constraint $(x + y) * (1) = z$. $\textsf{mul_gadget}(x \typecolon \Fq, y \typecolon \Fq) \rightarrow \Fq$ Allocates and returns the value $z = x * y$ and adds the R1CS constraint $(x) * (y) = z$. $\textsf{lc_gadget}(c_1x_1 + \ldots + c_nx_n) \rightarrow \Fq$ Allocates and returns the evaluation $y$ of the provided linear-combination; each $x_i$ is a value allocated in R1CS and each $c_i$ is an unallocated constant by which $x_i$ is scaled. This gadget adds the R1CS constraint $(c_1x_1 + \ldots + c_nx_n) * (1) = y$. $\textsf{sum_gadget}((x_1, \ldots, x_n)) \rightarrow \Fq$ Allocates and returns the the sum of the array of allocated values $(x_1, \ldots, x_n)$. Equivalent to calling $\textsf{lc_gadget}(\sum_{i \in [n]} 1 * x_i)$. $\textsf{le_bits_gadget}(x \typecolon \Fq) \rightarrow \Ftwo^{[n]}$ Allocates and returns the little-endian binary representation of the allocated value $x$. This gadget allocates $n$ boolean constrained values $(b_0, \ldots, b_{n - 1})$ in R1CS (adding one boolean constraint per bit) and adds the constraint $(2^0b_0 + \ldots + 2^{n - 1}b_{n - 1}) * (1) = x$ where each $2^i$ is an unallocated constant. ### Poseidon and Poseidon-PRF $\textsf{poseidon_gadget}(\text{preimage} \typecolon \Fq^{[2]}) \rightarrow \Fq$ Given an allocated array of two field elements ($\text{preimage}$) computes the Poseidon digest (width-3) of the preimage in R1CS and returns the allocated digest. This gadget uses the Poseidon "Merkle Tree arity-2" domain separation tag, i.e. the first field element of the width-3 initial state is $2^\text{arity} - 1 = 3$ and the remaining two elements of the initial state are $\text{preimage}$. $\textsf{poseidon_prf_gadget}(\text{preimage} \typecolon \Fq^{[2]}) \rightarrow \Fq$ Uses Poseidon as a pseudorandom function (PRF). This gadget is identical to $\textsf{poseidon_gadget}$ except that here we use a "custom" Poseidon domain separation tag of $2^{40}$. ### Apex-Root Creates an arity-2 SHA256 Merkle tree in R1CS from the leafs $\text{apex_leafs}$ and returns the root as an allocated value. $\overline{\underline{\textbf{Gadget:}\ \textsf{apex_root_gadget}(\text{apex_leafs} \typecolon \Fq^{[A]}) \rightarrow \Fq}}$ $\line{1} \text{row} = \text{apex_leafs}$ $\line{2} \textbf{while } \len(\text{row}) > 1 \boldsymbol{:}$ $\line{3} \quad \text{row} \typecolon \Fq^{[\len(\text{row}) / 2]} \deq (\textsf{sha256_gadget}((\text{row}[2i], \text{values}[2i + 1])) \mid i \in [\len(\text{row}) / 2])$ $\line{4} \textbf{return } \text{row}[0]$ ### Challenge Generation Generates $C$ random challenges for partition $k$ of the updated sector corresponding to $\text{CommRNew}$. $\overline{\underline{\textbf{Gadget:}\ \textsf{gen_challenge_bits_gadget}(\text{comm_r_new} \typecolon \Fq, k \typecolon \Fq) \rightarrow {\Ftwo^{[\text{RandChallengeBits}]}}^{[C]}}}$ $\line{1} \text{rand_challenge_bits} \typecolon {\Ftwo^{[\text{RandChallengeBits}]}}^{[C]} = (\ )$ $\line{2} \textbf{for } i \in [\text{PRFsPerPartition}] \boldsymbol{:}$ $\line{3} \quad \text{digest_index} \deq \textsf{lc_gadget}(\text{PRFsPerPartition} * k + i * \text{R1CS}\textsf{.one}) \quad\quad \textbf{#}\ \text{PRFsPerPartition}\ \text{and}\ i\ \text{are unallocated constants}$ $\line{4} \quad \text{digest} \deq \textsf{poseidon_prf_gadget}((\text{comm_r_new}, \text{digest_index}))$ $\line{5} \quad \text{digest_bits} \typecolon \Ftwo^{[255]} \deq \textsf{le_bits_gadget}(\text{digest}) \quad\quad \textbf{#}\ 255 = \lceil\log_2(q)\rceil\ \text{is the bit-length of } \Fq$ $\quad\quad\ \ \textbf{#}\ \text{Chunk digest_bits by challenge (chunks of size RandChallengeBits)}$ $\line{6} \quad \textbf{for } j \in [\text{ChallengesPerPRF}] \boldsymbol{:}$ $\line{7} \quad\quad \text{challenge_bits} \typecolon \Ftwo^{[\text{RandChallengeBits}]} = \text{digest_bits}[j * \text{RandChallengeBits} \textbf{..} \hso (j + 1)\text{RandChallengeBits}]$ $\line{8} \quad\quad \text{rand_challenge_bits}\textsf{.push}(\text{challenge_bits})$ $\textbf{#}\ \ \text{Truncate the return value because the last PRF adds more challenges than necessary}$ $\line{9} \textbf{return } \text{rand_challenge_bits}[\textbf{..} \hso C]$ ### Get Challenge High Bits Returns the $h$ high bits of $\text{challenge_bits}$, allocated as a single field element, where $h$ is chosen from $\Hs$ via $\text{h_select_bits}$. $\overline{\underline{\textbf{Gadget:}\ \textsf{get_high_bits_gadget}(\text{challenge_bits} \typecolon \Ftwo^{[\text{NodeBits}]}, \text{h_select_bits} \typecolon \Ftwo^{[\len(\Hs)]}) \rightarrow \Fq}}$ $\line{1} \text{scaled_highs} \typecolon \Fq^{[\len(\Hs)]} = (\ )$ $\line{2} \textbf{for } (h, \text{h_select_bit}) \in \textbf{zip}(\Hs, \text{h_select_bits}) \boldsymbol{:}$ $\quad\quad\ \ \textbf{#}\ \text{Pack the } h \text{ high bits of challenge_bits into a single } \Fq$ $\line{3} \quad \text{high_bits} = \text{challenge_bits}[\text{NodeBits} - h \hso \textbf{..}]$ $\line{4} \quad \text{high} \deq \textsf{lc_gadget}(\sum_{i \in [h]} 2^i * \text{high_bits}[i]) \quad\quad\quad \textbf{#}\ \text{Note: } 2^i \text{ is an unallocated constant}$ $\quad\quad\ \ \textbf{#}\ \text{Scale by } 0 \text{ or } 1$ $\line{5} \quad \text{high_or_zero} \deq \textsf{mul_gadget}(\text{high}, \text{h_select_bit})$ $\line{6} \quad \text{scaled_highs}\textsf{.push}(\text{high_or_zero})$ $\textbf{#}\ \ \text{The sum of the scaled values is the selected high value}$ $\line{7} \text{selected_high} \deq \textsf{sum_gadget}(\text{scaled_highs})$ $\line{8} \textbf{return } \text{selected_high}$