Skip to content

Commit dcceec2

Browse files
authored
Merge pull request #8 from unicitynetwork/token-split-2
Token split 2
2 parents 5169ec3 + 156ed96 commit dcceec2

5 files changed

Lines changed: 404 additions & 200 deletions

File tree

appendix-hashtrees.tex

Lines changed: 90 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -241,139 +241,13 @@ \subsection{Inclusion and Exclusion Proofs}
241241
\end{itemize}
242242

243243

244-
\section{Sparse Merkle Sum Trees}\label{app:smst}
245-
246-
A \emph{Sparse Merkle Sum Tree}\index{Sparse Merkle Sum Tree} (SMST) extends Sparse Merkle Tree with value tracking. Each node carries both a hash and a non-negative integer value. Internal nodes sum their children's values, enabling cryptographic proofs that a leaf's value is part of a known total. This structure is used for value conservation proofs in token splitting (Sec.~\ref{sec:token-splitting}).
247-
248-
\subsection{Node Structure}
249-
250-
Each node in an SMST carries a pair $(h, v)$, where $h \in \hashtype$ is the node hash and $v \in \mathbb{N}$ is the accumulated value.
251-
252-
\begin{itemize}
253-
\item \textbf{Leaf node}: $(h, v)$ where:
254-
\begin{itemize}
255-
\item $p \in \bitstr$ -- path label from this leaf to its parent
256-
\item $d \in \bytes{\ast}$ -- leaf data
257-
\item $v \in \mathbb{N}$ -- leaf value (e.g. asset amount)
258-
\item $h = H(p, d, v)$
259-
\end{itemize}
260-
\item \textbf{Branch node}: $(h, v)$ where:
261-
\begin{itemize}
262-
\item $p \in \bitstr$ -- path label from this node to its parent
263-
\item $(h_L, v_L)$ -- left child hash and value
264-
\item $(h_R, v_R)$ -- right child hash and value
265-
\item $h = H(p, h_L, v_L, h_R, v_R)$
266-
\item $v = v_L + v_R$
267-
\end{itemize}
268-
\item \textbf{Root node}: Uses the fixed path label $p = 1$ in hash computation:
269-
\begin{itemize}
270-
\item $h = H(1, h_L, v_L, h_R, v_R)$
271-
\item $v = v_L + v_R$
272-
\end{itemize}
273-
\end{itemize}
274-
275-
An empty child is represented by $(h, v) = (\bot, 0)$.
276-
277-
\subsection{Function $\textsc{smst\_leaf\_hash}$}\label{sec:smst-leaf-hash}
278-
279-
Computes the hash of an SMST leaf node.
280-
281-
\textbf{Input}:
282-
\begin{enumerate}
283-
\item $p \in \bitstr$ -- path segment from this leaf to its parent
284-
\item $d \in \hashtype$ -- leaf data
285-
\item $v \in \mathbb{N}$ -- leaf value
286-
\end{enumerate}
287-
288-
\textbf{Output}: $h \in \hashtype$ -- hash of the leaf node
289-
290-
\textbf{Computation}:
291-
\begin{algorithmic}
292-
\Function{smst\_leaf\_hash}{$p, d, v$}
293-
\State \Return $H(p, d, v)$
294-
\EndFunction
295-
\end{algorithmic}
296-
297-
\subsection{Function $\textsc{smst\_branch\_hash}$}\label{sec:smst-branch-hash}
298-
299-
Computes the hash and accumulated value of an SMST branch node.
300-
301-
\textbf{Input}:
302-
\begin{enumerate}
303-
\item $p \in \bitstr$ -- path segment from this node to its parent
304-
\item $(h_L, v_L) \in (\hashtype \cup \{\bot\}) \times \mathbb{N}$ -- left child
305-
\item $(h_R, v_R) \in (\hashtype \cup \{\bot\}) \times \mathbb{N}$ -- right child
306-
\end{enumerate}
307-
308-
\textbf{Output}: $(h, v) \in \hashtype \times \mathbb{N}$ -- hash and value of the branch node
309-
310-
\textbf{Computation}:
311-
\begin{algorithmic}
312-
\Function{smst\_branch\_hash}{$p, (h_L, v_L), (h_R, v_R)$}
313-
\State $h \gets H(p, h_L, v_L, h_R, v_R)$
314-
\State $v \gets v_L + v_R$
315-
\State \Return $(h, v)$
316-
\EndFunction
317-
\end{algorithmic}
318-
319-
\subsection{Inclusion Certificate}\label{sec:smst-inclusion-cert}
320-
321-
An \emph{Inclusion Certificate} for an SMST leaf with key $k$ is a sequence of triples:
322-
\[
323-
C^\mathsf{inc} = \langle (p_1, d_1, v_1), (p_2, d_2, v_2), \ldots, (p_n, d_n, v_n) \rangle
324-
\]
325-
where:
326-
\begin{itemize}
327-
\item $(p_1, d_1, v_1)$ -- the leaf's path segment, data, and value
328-
\item $(p_i, d_i, v_i)$ for $i > 1$ -- path segment $p_i$ to parent, sibling hash $d_i$, and sibling value $v_i$
329-
\end{itemize}
330-
331-
\subsection{Function $\textsc{smst\_verify\_inclusion}$}\label{sec:smst-verify-inclusion}
332-
333-
Verifies an SMST inclusion certificate by reconstructing the root hash and total value.
334-
335-
\textbf{Input}:
336-
\begin{enumerate}
337-
\item $k \in \hashtype$ -- key
338-
\item $v_\mathsf{leaf} \in \mathbb{N}$ -- expected leaf value
339-
\item $C^\mathsf{inc} = \langle (p_1, d_1, v_1), \ldots, (p_n, d_n, v_n) \rangle$ -- inclusion certificate
340-
\item $h_{\mathsf{root}} \in \hashtype$ -- expected root hash
341-
\end{enumerate}
342-
343-
\textbf{Output}: \TRUE or \FALSE
344-
345-
\textbf{Computation}:
346-
\begin{algorithmic}
347-
\Function{smst\_verify\_inclusion}{$k, v_\mathsf{leaf}, C^\mathsf{inc}, h_{\mathsf{root}}$}
348-
\If {$v_1 \neq v_\mathsf{leaf}$}
349-
\State \Return \FALSE \Comment{Leaf value mismatch}
350-
\EndIf
351-
\State $h \gets H(p_1, d_1, v_1)$ \Comment{Hash leaf node}
352-
\State $v_\mathsf{sum} \gets v_1$
353-
\State $q \gets p_1$ \Comment{Reconstructed path}
354-
\For {$i \gets 2$ to $n$}
355-
\State $b \gets$ rightmost bit of $p_{i-1}$
356-
\If {$b = 0$}
357-
\State $h \gets H(p_i, h, v_\mathsf{sum}, d_i, v_i)$ \Comment{Current is left child}
358-
\Else
359-
\State $h \gets H(p_i, d_i, v_i, h, v_\mathsf{sum})$ \Comment{Current is right child}
360-
\EndIf
361-
\State $v_\mathsf{sum} \gets v_\mathsf{sum} + v_i$
362-
\State $q \gets q \| p_i$ \Comment{Extend reconstructed path}
363-
\EndFor
364-
\State \Return $(h = h_{\mathsf{root}}) \land (q = k)$
365-
\EndFunction
366-
\end{algorithmic}
367-
368-
In addition to verifying the root hash, the accumulated sum $v_\mathsf{sum}$ at the root equals the total value in the tree, enabling value conservation checks: the verifier confirms both that the leaf value matches the claimed allocation and that the root sum matches the original token's total value for the given asset.
369-
370244
\section{Radix Sparse Merkle Trees}\label{app:rsmt}
371245

372-
A \emph{Radix Sparse Merkle Tree}\index{Radix Sparse Merkle Tree} (RSMT) is a leaf-anchored, path-compressed binary Merkle tree over a 256-bit key space. It authenticates a finite map $k \mapsto v$, where every stored key appears as exactly one leaf and every internal node records the absolute bit position at which its descendant keys bifurcate. The implementation documented here uses LSB-first bit order.\footnote{Reference Python implementation: \href{https://github.com/unicitynetwork/unicity-yellowpaper-tex/blob/main/ndrsmt3o.py}{\texttt{ndrsmt3o.py}}.} Each leaf hash commits to the full 256-bit key, and each internal node hash commits to the explicit bifurcation depth. Consequently, splitting an edge above an unchanged subtree does not alter that subtree's hash: batch insertion rehashes only the changed search frontier and the newly created nodes. The tree supports inclusion certificates, non-inclusion certificates and consistency proofs.
246+
A \emph{Radix Sparse Merkle Tree}\index{Radix Sparse Merkle Tree} (RSMT) is a leaf-anchored, path-compressed binary Merkle tree over a 256-bit key space. It authenticates a finite map $k \mapsto v$, where every stored key appears as exactly one leaf and every internal node records the absolute bit position at which its descendant keys bifurcate. The implementation documented here uses byte-order-preserving LSB-in-byte bit numbering. Each leaf hash commits to the full 256-bit key, and each internal node hash commits to the explicit bifurcation depth. Consequently, splitting an edge above an unchanged subtree does not alter that subtree's hash: batch insertion rehashes only the changed search frontier and the newly created nodes. The tree supports inclusion certificates, non-inclusion certificates and consistency proofs.
373247

374248
\subsection{Tree Structure}
375249

376-
Keys are elements of $\bytes{32}$ (256 bits), addressed in LSB-first order\footnote{See Appendix~\ref{app:ordering} for bit-string conventions.}. Bit position $i$ means the $i$-th least significant bit.
250+
Keys are elements of $\bytes{32}$ (256 bits). Write $k=k_0\|k_1\|\cdots\|k_{31}$ in wire byte order. Bit position $i$ addresses bit $i\bmod 8$ of byte $k_{\lfloor i/8\rfloor}$, where bit $0$ of a byte is its least significant bit. Thus depth $0$ reads the low bit of $k_0$, depth $7$ reads the high bit of $k_0$, depth $8$ reads the low bit of $k_1$, and depth $255$ reads the high bit of $k_{31}$. Tree traversal MUST NOT reinterpret the key as a single big-endian integer for bit addressing.
377251

378252
\subsubsection{Node Format}
379253

@@ -516,7 +390,7 @@ \subsubsection{Function $\textsc{rsmt\_batch\_insert\_np}$}\label{sec:rsmt-batch
516390

517391
\subsection{Shard Split}\label{sec:rsmt-shard-split}
518392

519-
When a shard $\sigma$ is split into two sub-shards $\sigma \| 0$ and $\sigma \| 1$ (Sec.~\ref{sec:sharding}), the RSMT maintained by the parent shard must be partitioned accordingly: each successor keeps only those leaves whose keys route to it under the sharding function $f_{\mathcal{SH}}$. In the LSB-first convention of Section~\ref{sec:rsmt-sort-order}, a key $k$ belongs to $\sigma \| b$ if bit $|\sigma|$ of $k$ equals $b$.
393+
When a shard $\sigma$ is split into two sub-shards $\sigma \| 0$ and $\sigma \| 1$ (Sec.~\ref{sec:sharding}), the RSMT maintained by the parent shard must be partitioned accordingly: each successor keeps only those leaves whose keys route to it under the sharding function $f_{\mathcal{SH}}$. In the bit-numbering convention of Section~\ref{app:rsmt}, a key $k$ belongs to $\sigma \| b$ if bit $|\sigma|$ of $k$ equals $b$.
520394

521395
\subsubsection{Function $\textsc{rsmt\_shard\_split}$}\label{sec:rsmt-shard-split-fn}
522396

@@ -581,8 +455,8 @@ \subsubsection{Format}
581455

582456
An inclusion certificate is a pair $C^\mathsf{inc} = (\mathit{bitmap}, \mathit{siblings})$ where:
583457
\begin{itemize}
584-
\item $\mathit{bitmap} \in \bytes{32}$ -- a 256-bit vector. Bit $d$ is set if and only if a sibling hash is required at depth $d$ on the path from the leaf to the root.
585-
\item $\mathit{siblings} = \langle s_1, \ldots, s_n \rangle \in \hashtype^n$ where $n = \mathsf{bitcount}(\mathit{bitmap})$ -- sibling hashes ordered leaf-to-root (ascending depth).
458+
\item $\mathit{bitmap} \in \bytes{32}$ -- a 256-bit vector. Bit $d$ is stored as bit $d\bmod 8$ of byte $\lfloor d/8\rfloor$ and is set if and only if a sibling hash is required at depth $d$ on the path from the leaf to the root.
459+
\item $\mathit{siblings} = \langle s_1, \ldots, s_n \rangle \in \hashtype^n$ where $n = \mathsf{bitcount}(\mathit{bitmap})$ -- sibling hashes ordered root-to-leaf (ascending depth).
586460
\end{itemize}
587461

588462
\noindent\textbf{Wire format}: $\mathit{bitmap}[32] \,\|\, s_1[32] \,\|\, \ldots \,\|\, s_n[32]$, for a total of $32 + 32n$ bytes.
@@ -638,7 +512,7 @@ \subsubsection{Format}
638512
\item $k_\ell \in \bytes{32}$ -- key of the neighbouring leaf
639513
\item $h_\ell \in \hashtype$ -- hash of the neighbouring leaf ($= \Call{rsmt\_leaf\_hash}{k_\ell, v_\ell}$)
640514
\item $\mathit{bitmap} \in \bytes{32}$ -- branching-depth bitmap (same semantics as inclusion certificate, Section~\ref{sec:rsmt-inclusion-cert})
641-
\item $\mathit{siblings} = \langle s_1, \ldots, s_n \rangle \in \hashtype^n$ where $n = \mathsf{bitcount}(\mathit{bitmap})$ -- sibling hashes, leaf-to-root
515+
\item $\mathit{siblings} = \langle s_1, \ldots, s_n \rangle \in \hashtype^n$ where $n = \mathsf{bitcount}(\mathit{bitmap})$ -- sibling hashes, root-to-leaf
642516
\end{itemize}
643517

644518
\noindent\textbf{Wire format}: $k_\ell[32] \,\|\, h_\ell[32] \,\|\, \mathit{bitmap}[32] \,\|\, s_1[32] \,\|\, \ldots \,\|\, s_n[32]$, for a total of $96 + 32n$ bytes.
@@ -703,11 +577,11 @@ \subsection{Consistency Proof}\label{sec:rsmt-consistency-proof}
703577

704578
\subsubsection{Sort Order}\label{sec:rsmt-sort-order}
705579

706-
The batch and proof use \emph{LSB-first traversal order}. The sort key of a 256-bit key $k$ is obtained by reversing the byte order of $k$ and then independently reversing the bits within each byte. This places the least significant bit of the key first in lexicographic order, matching the tree's branching order.
580+
The batch and proof use the tree traversal order defined in Section~\ref{app:rsmt}. The sort key of a 256-bit key $k=k_0\|k_1\|\cdots\|k_{31}$ is obtained by independently reversing the bits within each byte while preserving byte order. This places bit $0$ of $k_0$ first in lexicographic order, then bit $1$ of $k_0$, and so on through bit $7$ of $k_{31}$, matching the tree's branching order.
707581

708582
\subsubsection{Proof Format}
709583

710-
The proof $\pi$ is a flat stream of opcodes emitted from a post-order, LSB-first traversal of the tree:
584+
The proof $\pi$ is a flat stream of opcodes emitted from a post-order traversal of the tree in the order defined above:
711585

712586
\begin{center}
713587
\begin{tabular}{clcl}
@@ -829,3 +703,85 @@ \subsubsection{Verification}
829703
\item Verify the ZK proof, obtaining authentic public inputs $h'$, $h$
830704
\item Confirm these values match the claimed Input Reco rd
831705
\end{enumerate}
706+
707+
708+
\section{Radix Sparse Merkle Sum Trees}\label{app:rsmst}
709+
710+
A \emph{Radix Sparse Merkle Sum Tree}\index{Radix Sparse Merkle Sum Tree} (RSMST) is the radix sparse Merkle tree of Appendix~\ref{app:rsmt} with a positive amount at every leaf and an accumulated sum at every internal node. It is used for split allocation roots (Sec.~\ref{sec:token-splitting}). The tree deliberately reuses the RSMT key space, byte-order-preserving bit numbering, canonical path-compressed structure and inclusion-walk logic.
711+
712+
\subsection{Tree Structure}
713+
714+
Keys are 32-byte strings. For token splitting the key is the output token identifier $\mathsf{id}_j$, the leaf data is the 32-byte output commitment $d_j$, and the leaf amount is the positive asset allocation $v_j(\mathsf{aid})$.
715+
716+
An RSMST leaf carries $(k,d,v)$ where $k,d\in\bytes{32}$ and $1\leq v<2^{256}$. An internal node carries an absolute bifurcation depth $\delta\in\{0,\ldots,255\}$ and two child pairs $(h_L,v_L)$ and $(h_R,v_R)$; its sum is $v_L+v_R$. Construction MUST use the same canonical path-compressed binary trie as RSMT, MUST reject duplicate keys and zero-valued leaves, and MUST reject any internal sum overflow. A split allocation tree is never empty.
717+
718+
For a tree $\mathcal{S}$, $\Call{RSMSTRoot}{\mathcal{S}}$ returns the root hash and root sum. If $\mathcal{S}$ has one leaf, the root hash is that leaf hash and the root sum is that leaf amount. Otherwise the root is the hash and sum of the top internal node.
719+
720+
\subsection{Hash Computation}
721+
722+
Hash inputs use fixed binary encodings, not general-purpose CBOR tuples. Let $\Call{u256}{x}$ be the 32-byte big-endian encoding of $x$ and let $\Call{u8}{\delta}$ be the one-byte encoding of $\delta$. Define one-byte domain separators:
723+
\[
724+
\mathsf{RSMST\_LEAF}=\mathtt{0x10},\qquad
725+
\mathsf{RSMST\_NODE}=\mathtt{0x11}.
726+
\]
727+
728+
\begin{algorithmic}
729+
\Function{rsmst\_leaf\_hash}{$k,d,v$}
730+
\State \Return $\mathsf{SHA\text{-}256}(\mathsf{RSMST\_LEAF}\|k\|d\|\Call{u256}{v})$
731+
\EndFunction
732+
\end{algorithmic}
733+
734+
\begin{algorithmic}
735+
\Function{rsmst\_node\_hash}{$\delta,(h_L,v_L),(h_R,v_R)$}
736+
\State $v \gets v_L+v_R$ using checked 256-bit addition; on overflow return $(\bot,0)$
737+
\State $h \gets \mathsf{SHA\text{-}256}(\mathsf{RSMST\_NODE}\|\Call{u8}{\delta}\|h_L\|\Call{u256}{v_L}\|h_R\|\Call{u256}{v_R})$
738+
\State \Return $(h,v)$
739+
\EndFunction
740+
\end{algorithmic}
741+
742+
\subsection{Explicit-Depth Inclusion Proof}\label{sec:rsmst-inclusion-proof}
743+
744+
An RSMST inclusion proof is a leaf-to-root sequence of sibling entries:
745+
\[
746+
C^\mathsf{inc}=\langle(\delta_1,s_1,w_1),\ldots,(\delta_n,s_n,w_n)\rangle,
747+
\]
748+
where $0\leq n\leq256$, $\delta_i\in\{0,\ldots,255\}$ is the sibling's parent bifurcation depth, $s_i\in\hashtype$ is the sibling subtree hash, and $1\leq w_i<2^{256}$ is the sibling subtree sum. Depths MUST be strictly decreasing in proof order because the proof is ordered from the leaf toward the root. No bitmap is carried; the depth in each sibling entry fully identifies the branch step.
749+
750+
\subsection{Function $\textsc{rsmst\_verify\_inclusion}$}\label{sec:rsmst-verify-inclusion}
751+
752+
\textbf{Input}:
753+
\begin{enumerate}
754+
\item $k \in \bytes{32}$ -- expected leaf key;
755+
\item $d \in \bytes{32}$ -- expected leaf data;
756+
\item $v \in \mathbb{N}$, $1\leq v<2^{256}$ -- expected leaf amount;
757+
\item $C^\mathsf{inc}$ -- explicit-depth inclusion proof; and
758+
\item $r \in \bytes{32}$ -- expected root hash.
759+
\end{enumerate}
760+
761+
\textbf{Output}: $(b,v_\mathsf{root}) \in \bool\times\mathbb{N}$, where $b$ is the success flag and $v_\mathsf{root}$ is the reconstructed root sum.
762+
763+
\begin{algorithmic}
764+
\Function{rsmst\_verify\_inclusion}{$k,d,v,C^\mathsf{inc},r$}
765+
\If{$v=0$, $v\geq2^{256}$, $\len{C^\mathsf{inc}}>256$, or any field is non-canonical} \State \Return $(\FALSE,0)$ \EndIf
766+
\State $h \gets \Call{rsmst\_leaf\_hash}{k,d,v}$
767+
\State $v_\mathsf{sum} \gets v$
768+
\State $\delta_\mathsf{prev}\gets256$
769+
\ForAll{$(\delta,s,w)$ in $C^\mathsf{inc}$}
770+
\If{$\delta\geq\delta_\mathsf{prev}$, $\delta\notin\{0,\ldots,255\}$, $w=0$, or $w\geq2^{256}$}
771+
\State \Return $(\FALSE,0)$
772+
\EndIf
773+
\If{bit $\delta$ of $k$ is $0$}
774+
\State $(h',v')\gets\Call{rsmst\_node\_hash}{\delta,(h,v_\mathsf{sum}),(s,w)}$
775+
\Else
776+
\State $(h',v')\gets\Call{rsmst\_node\_hash}{\delta,(s,w),(h,v_\mathsf{sum})}$
777+
\EndIf
778+
\If{$h'=\bot$} \State \Return $(\FALSE,0)$ \EndIf
779+
\State $h\gets h'$
780+
\State $v_\mathsf{sum}\gets v'$
781+
\State $\delta_\mathsf{prev}\gets\delta$
782+
\EndFor
783+
\State \Return $((h=r),v_\mathsf{sum})$
784+
\EndFunction
785+
\end{algorithmic}
786+
787+
The verifier checks value conservation by comparing $v_\mathsf{root}$ to the authenticated source amount for the same asset. The root hash alone is not sufficient: sibling sums are verification-critical proof data because they are committed by every internal node hash.

0 commit comments

Comments
 (0)