prism_crypto/
commitment.rs1#![allow(missing_docs)]
5
6use core::marker::PhantomData;
7
8use uor_foundation::enforcement::{GroundedShape, ShapeViolation};
9use uor_foundation::pipeline::{
10 AxisExtension, ConstrainedTypeShape, ConstraintRef, IntoBindingValue,
11};
12use uor_foundation_sdk::axis;
13
14use crate::hash::{HashAxis, Sha256Hasher};
15
16axis! {
17 pub trait CommitmentAxis: AxisExtension {
19 const AXIS_ADDRESS: &'static str = "https://uor.foundation/axis/CommitmentAxis";
20 const MAX_OUTPUT_BYTES: usize = 96;
21 fn commit(input: &[u8], out: &mut [u8]) -> Result<usize, ShapeViolation>;
27 }
28}
29
30const SHA256_BYTES: usize = 32;
31
32fn shape_violation(constraint: &'static str) -> ShapeViolation {
33 ShapeViolation {
34 shape_iri: "https://uor.foundation/axis/CommitmentAxis/MerkleRoot",
35 constraint_iri: constraint,
36 property_iri: "https://uor.foundation/axis/inputBytes",
37 expected_range: "https://uor.foundation/axis/MerkleLeafSequence",
38 min_count: 0,
39 max_count: 0,
40 kind: uor_foundation::ViolationKind::ValueCheck,
41 }
42}
43
44pub const MAX_MERKLE_LEAVES: usize = 64;
48
49const MAX_LEAF_WIDTH: usize = 64;
52
53#[derive(Debug, Clone, Copy)]
66pub struct MerkleRoot<H: HashAxis, const LEAF_BYTES: usize = SHA256_BYTES>(PhantomData<H>);
67
68impl<H: HashAxis, const LEAF_BYTES: usize> Default for MerkleRoot<H, LEAF_BYTES> {
69 fn default() -> Self {
70 Self(PhantomData)
71 }
72}
73
74impl<H: HashAxis, const LEAF_BYTES: usize> CommitmentAxis for MerkleRoot<H, LEAF_BYTES> {
75 const AXIS_ADDRESS: &'static str =
76 "https://uor.foundation/axis/CommitmentAxis/MerkleRootParametric";
77 const MAX_OUTPUT_BYTES: usize = LEAF_BYTES;
78
79 fn commit(input: &[u8], out: &mut [u8]) -> Result<usize, ShapeViolation> {
80 if LEAF_BYTES == 0 {
81 return Err(shape_violation(
82 "https://uor.foundation/axis/CommitmentAxis/MerkleRoot/leafBytesNonZero",
83 ));
84 }
85 if input.is_empty() || input.len() % LEAF_BYTES != 0 {
86 return Err(shape_violation(
87 "https://uor.foundation/axis/CommitmentAxis/MerkleRoot/leafAlignment",
88 ));
89 }
90 let leaf_count = input.len() / LEAF_BYTES;
91 if !leaf_count.is_power_of_two() {
92 return Err(shape_violation(
93 "https://uor.foundation/axis/CommitmentAxis/MerkleRoot/powerOfTwoLeaves",
94 ));
95 }
96 if out.len() < LEAF_BYTES {
97 return Err(shape_violation(
98 "https://uor.foundation/axis/CommitmentAxis/MerkleRoot/outputBuffer",
99 ));
100 }
101 if leaf_count > MAX_MERKLE_LEAVES {
102 return Err(shape_violation(
103 "https://uor.foundation/axis/CommitmentAxis/MerkleRoot/maxLeaves",
104 ));
105 }
106 if LEAF_BYTES > MAX_LEAF_WIDTH {
111 return Err(shape_violation(
112 "https://uor.foundation/axis/CommitmentAxis/MerkleRoot/leafBytesInRange",
113 ));
114 }
115 let mut layer = [[0u8; MAX_LEAF_WIDTH]; MAX_MERKLE_LEAVES];
116 for i in 0..leaf_count {
117 layer[i][..LEAF_BYTES].copy_from_slice(&input[i * LEAF_BYTES..(i + 1) * LEAF_BYTES]);
118 }
119 let mut pair_buf = [0u8; 2 * MAX_LEAF_WIDTH];
120 let mut digest_buf = [0u8; MAX_LEAF_WIDTH];
121 let mut len = leaf_count;
122 while len > 1 {
123 let half = len / 2;
124 for i in 0..half {
125 pair_buf[..LEAF_BYTES].copy_from_slice(&layer[2 * i][..LEAF_BYTES]);
126 pair_buf[LEAF_BYTES..2 * LEAF_BYTES]
127 .copy_from_slice(&layer[2 * i + 1][..LEAF_BYTES]);
128 H::hash(&pair_buf[..2 * LEAF_BYTES], &mut digest_buf[..LEAF_BYTES])?;
129 layer[i][..LEAF_BYTES].copy_from_slice(&digest_buf[..LEAF_BYTES]);
130 }
131 len = half;
132 }
133 out[..LEAF_BYTES].copy_from_slice(&layer[0][..LEAF_BYTES]);
134 Ok(LEAF_BYTES)
135 }
136}
137
138axis_extension_impl_for_commitment_axis!(
140 @generic MerkleRoot<H, LEAF_BYTES>,
141 [H: HashAxis, const LEAF_BYTES: usize]
142);
143
144pub type MerkleRootCommitment = MerkleRoot<Sha256Hasher, SHA256_BYTES>;
146
147#[derive(Debug, Clone, Copy)]
156pub struct MerkleProofShape<const MAX_DEPTH: usize, const LEAF_BYTES: usize = SHA256_BYTES>;
157
158impl<const MAX_DEPTH: usize, const LEAF_BYTES: usize> Default
159 for MerkleProofShape<MAX_DEPTH, LEAF_BYTES>
160{
161 fn default() -> Self {
162 Self
163 }
164}
165
166impl<const MAX_DEPTH: usize, const LEAF_BYTES: usize> ConstrainedTypeShape
167 for MerkleProofShape<MAX_DEPTH, LEAF_BYTES>
168{
169 const IRI: &'static str = "https://uor.foundation/type/ConstrainedType";
170 const SITE_COUNT: usize = MAX_DEPTH * LEAF_BYTES + 8;
171 const CONSTRAINTS: &'static [ConstraintRef] = &[];
172 #[allow(clippy::cast_possible_truncation)]
173 const CYCLE_SIZE: u64 = 256u64.saturating_pow((MAX_DEPTH * LEAF_BYTES + 8) as u32);
174}
175
176impl<const MAX_DEPTH: usize, const LEAF_BYTES: usize> uor_foundation::pipeline::__sdk_seal::Sealed
177 for MerkleProofShape<MAX_DEPTH, LEAF_BYTES>
178{
179}
180impl<const MAX_DEPTH: usize, const LEAF_BYTES: usize> GroundedShape
181 for MerkleProofShape<MAX_DEPTH, LEAF_BYTES>
182{
183}
184impl<const MAX_DEPTH: usize, const LEAF_BYTES: usize> IntoBindingValue
185 for MerkleProofShape<MAX_DEPTH, LEAF_BYTES>
186{
187 const MAX_BYTES: usize = MAX_DEPTH * LEAF_BYTES + 8;
188
189 fn into_binding_bytes(&self, _out: &mut [u8]) -> Result<usize, ShapeViolation> {
190 Ok(0)
191 }
192}