prism_crypto/
commitment.rs1#![allow(missing_docs)]
5
6use core::marker::PhantomData;
7
8use uor_foundation::enforcement::{GroundedShape, ShapeViolation};
9use uor_foundation::pipeline::{ConstrainedTypeShape, ConstraintRef, IntoBindingValue, TermValue};
10use uor_foundation_sdk::axis;
11
12use crate::hash::{HashAxis, Sha256Hasher};
13
14axis! {
15 pub trait CommitmentAxis: AxisExtension {
17 const AXIS_ADDRESS: &'static str = "https://uor.foundation/axis/CommitmentAxis";
18 const MAX_OUTPUT_BYTES: usize = 96;
19 fn commit(input: &[u8], out: &mut [u8]) -> Result<usize, ShapeViolation>;
25 }
26}
27
28const SHA256_BYTES: usize = 32;
29
30fn shape_violation(constraint: &'static str) -> ShapeViolation {
31 ShapeViolation {
32 shape_iri: "https://uor.foundation/axis/CommitmentAxis/MerkleRoot",
33 constraint_iri: constraint,
34 property_iri: "https://uor.foundation/axis/inputBytes",
35 expected_range: "https://uor.foundation/axis/MerkleLeafSequence",
36 min_count: 0,
37 max_count: 0,
38 kind: uor_foundation::ViolationKind::ValueCheck,
39 }
40}
41
42const MERKLE_STACK_DEPTH: usize = usize::BITS as usize;
50
51#[derive(Debug, Clone, Copy)]
64pub struct MerkleRoot<H: HashAxis, const LEAF_BYTES: usize = SHA256_BYTES>(PhantomData<H>);
65
66impl<H: HashAxis, const LEAF_BYTES: usize> Default for MerkleRoot<H, LEAF_BYTES> {
67 fn default() -> Self {
68 Self(PhantomData)
69 }
70}
71
72impl<H: HashAxis, const LEAF_BYTES: usize> CommitmentAxis for MerkleRoot<H, LEAF_BYTES> {
73 const AXIS_ADDRESS: &'static str =
74 "https://uor.foundation/axis/CommitmentAxis/MerkleRootParametric";
75 const MAX_OUTPUT_BYTES: usize = LEAF_BYTES;
76
77 fn commit(input: &[u8], out: &mut [u8]) -> Result<usize, ShapeViolation> {
78 if LEAF_BYTES == 0 {
79 return Err(shape_violation(
80 "https://uor.foundation/axis/CommitmentAxis/MerkleRoot/leafBytesNonZero",
81 ));
82 }
83 if input.is_empty() || input.len() % LEAF_BYTES != 0 {
84 return Err(shape_violation(
85 "https://uor.foundation/axis/CommitmentAxis/MerkleRoot/leafAlignment",
86 ));
87 }
88 let leaf_count = input.len() / LEAF_BYTES;
89 if !leaf_count.is_power_of_two() {
90 return Err(shape_violation(
91 "https://uor.foundation/axis/CommitmentAxis/MerkleRoot/powerOfTwoLeaves",
92 ));
93 }
94 if out.len() < LEAF_BYTES {
95 return Err(shape_violation(
96 "https://uor.foundation/axis/CommitmentAxis/MerkleRoot/outputBuffer",
97 ));
98 }
99 let mut roots = [[0u8; LEAF_BYTES]; MERKLE_STACK_DEPTH];
108 let mut levels = [0u32; MERKLE_STACK_DEPTH];
109 let mut top = 0usize;
110 for i in 0..leaf_count {
111 let mut cur = [0u8; LEAF_BYTES];
112 cur.copy_from_slice(&input[i * LEAF_BYTES..(i + 1) * LEAF_BYTES]);
113 let mut cur_level = 0u32;
114 while top > 0 && levels[top - 1] == cur_level {
117 top -= 1;
118 let mut pair = [[0u8; LEAF_BYTES]; 2];
119 pair[0] = roots[top];
120 pair[1] = cur;
121 let mut digest = [0u8; LEAF_BYTES];
122 H::hash(pair.as_flattened(), &mut digest[..])?;
123 cur = digest;
124 cur_level += 1;
125 }
126 roots[top] = cur;
127 levels[top] = cur_level;
128 top += 1;
129 }
130 out[..LEAF_BYTES].copy_from_slice(&roots[0][..LEAF_BYTES]);
132 Ok(LEAF_BYTES)
133 }
134}
135
136axis_extension_impl_for_commitment_axis!(
138 @generic MerkleRoot<H, LEAF_BYTES>,
139 [H: HashAxis, const LEAF_BYTES: usize]
140);
141
142pub type MerkleRootCommitment = MerkleRoot<Sha256Hasher, SHA256_BYTES>;
144
145#[derive(Debug, Clone, Copy)]
154pub struct MerkleProofShape<const MAX_DEPTH: usize, const LEAF_BYTES: usize = SHA256_BYTES>;
155
156impl<const MAX_DEPTH: usize, const LEAF_BYTES: usize> Default
157 for MerkleProofShape<MAX_DEPTH, LEAF_BYTES>
158{
159 fn default() -> Self {
160 Self
161 }
162}
163
164impl<const MAX_DEPTH: usize, const LEAF_BYTES: usize> ConstrainedTypeShape
165 for MerkleProofShape<MAX_DEPTH, LEAF_BYTES>
166{
167 const IRI: &'static str = "https://uor.foundation/type/ConstrainedType";
168 const SITE_COUNT: usize = MAX_DEPTH * LEAF_BYTES + 8;
169 const CONSTRAINTS: &'static [ConstraintRef] = &[];
170 #[allow(clippy::cast_possible_truncation)]
171 const CYCLE_SIZE: u64 = 256u64.saturating_pow((MAX_DEPTH * LEAF_BYTES + 8) as u32);
172}
173
174impl<const MAX_DEPTH: usize, const LEAF_BYTES: usize> uor_foundation::pipeline::__sdk_seal::Sealed
175 for MerkleProofShape<MAX_DEPTH, LEAF_BYTES>
176{
177}
178impl<const MAX_DEPTH: usize, const LEAF_BYTES: usize> GroundedShape
179 for MerkleProofShape<MAX_DEPTH, LEAF_BYTES>
180{
181}
182impl<'a, const MAX_DEPTH: usize, const LEAF_BYTES: usize> IntoBindingValue<'a>
183 for MerkleProofShape<MAX_DEPTH, LEAF_BYTES>
184{
185 fn as_binding_value<const INLINE_BYTES: usize>(&self) -> TermValue<'a, INLINE_BYTES> {
186 TermValue::empty()
187 }
188}