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};
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
42pub const MAX_MERKLE_LEAVES: usize = 64;
46
47const MAX_LEAF_WIDTH: usize = 64;
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 if leaf_count > MAX_MERKLE_LEAVES {
100 return Err(shape_violation(
101 "https://uor.foundation/axis/CommitmentAxis/MerkleRoot/maxLeaves",
102 ));
103 }
104 if LEAF_BYTES > MAX_LEAF_WIDTH {
109 return Err(shape_violation(
110 "https://uor.foundation/axis/CommitmentAxis/MerkleRoot/leafBytesInRange",
111 ));
112 }
113 let mut layer = [[0u8; MAX_LEAF_WIDTH]; MAX_MERKLE_LEAVES];
114 for i in 0..leaf_count {
115 layer[i][..LEAF_BYTES].copy_from_slice(&input[i * LEAF_BYTES..(i + 1) * LEAF_BYTES]);
116 }
117 let mut pair_buf = [0u8; 2 * MAX_LEAF_WIDTH];
118 let mut digest_buf = [0u8; MAX_LEAF_WIDTH];
119 let mut len = leaf_count;
120 while len > 1 {
121 let half = len / 2;
122 for i in 0..half {
123 pair_buf[..LEAF_BYTES].copy_from_slice(&layer[2 * i][..LEAF_BYTES]);
124 pair_buf[LEAF_BYTES..2 * LEAF_BYTES]
125 .copy_from_slice(&layer[2 * i + 1][..LEAF_BYTES]);
126 H::hash(&pair_buf[..2 * LEAF_BYTES], &mut digest_buf[..LEAF_BYTES])?;
127 layer[i][..LEAF_BYTES].copy_from_slice(&digest_buf[..LEAF_BYTES]);
128 }
129 len = half;
130 }
131 out[..LEAF_BYTES].copy_from_slice(&layer[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<const MAX_DEPTH: usize, const LEAF_BYTES: usize> IntoBindingValue
183 for MerkleProofShape<MAX_DEPTH, LEAF_BYTES>
184{
185 const MAX_BYTES: usize = MAX_DEPTH * LEAF_BYTES + 8;
186
187 fn into_binding_bytes(&self, _out: &mut [u8]) -> Result<usize, ShapeViolation> {
188 Ok(0)
189 }
190}