1use core::marker::PhantomData;
76
77use uor_foundation::enforcement::{Hasher, ShapeViolation};
78use uor_foundation::pipeline::{
79 ChainComplexBytes, ChainComplexResolver, CochainComplexBytes, CochainComplexResolver,
80 CohomologyGroupResolver, ConstrainedTypeShape, ConstraintRef, HomologyGroupResolver,
81 HomotopyGroupResolver, HomotopyGroupsBytes, KInvariantResolver, NerveResolver,
82 PostnikovResolver, PostnikovTowerBytes, SimplicialComplexBytes,
83};
84use uor_foundation::{HostBounds, ViolationKind};
85use uor_foundation_sdk::resolver;
86
87use crate::model::{AddressLabel, ADDRESS_LABEL_BYTES, JSON_INPUT_MAX_BYTES};
88use crate::shapes::bounds::AddrBounds;
89
90const WIRE_FORMAT_ADDRESS_BYTES: usize = ADDRESS_LABEL_BYTES;
94
95const CARRIER_BYTES: usize = <AddrBounds as HostBounds>::TERM_VALUE_MAX_BYTES;
99
100const NERVE_VERTEX_COUNT: u32 = 71;
102const NERVE_HIGHEST_DIM: u32 = 0;
104
105const GEOMETRY_TAIL_BYTES: usize = 8 + 4 + 4 + 4 + NERVE_VERTEX_COUNT as usize;
109
110const LENGTH_PREFIX_BYTES: usize = 4;
112
113#[allow(dead_code)]
116const CARRIER_PAYLOAD_MAX: usize = CARRIER_BYTES - LENGTH_PREFIX_BYTES - GEOMETRY_TAIL_BYTES;
117
118const _: () = {
121 assert!(
122 JSON_INPUT_MAX_BYTES <= CARRIER_PAYLOAD_MAX,
123 "JsonInput::MAX_BYTES must fit within CARRIER_BYTES - LENGTH_PREFIX_BYTES - GEOMETRY_TAIL_BYTES"
124 );
125};
126
127const OFFSET_GEOMETRY_TAIL: usize = CARRIER_BYTES - GEOMETRY_TAIL_BYTES;
129const OFFSET_STAGE_TAG: usize = OFFSET_GEOMETRY_TAIL;
131const OFFSET_VERTEX_COUNT: usize = OFFSET_STAGE_TAG + 8;
133const OFFSET_HIGHEST_DIM: usize = OFFSET_VERTEX_COUNT + 4;
135const OFFSET_RESERVED: usize = OFFSET_HIGHEST_DIM + 4;
137#[allow(dead_code)]
139const OFFSET_VERTEX_POSITIONS: usize = OFFSET_RESERVED + 4;
140
141const PSI_1_NERVE_TAG: u64 = 1;
144const PSI_2_CHAIN_COMPLEX_TAG: u64 = 2;
145const PSI_3_HOMOLOGY_GROUPS_TAG: u64 = 3;
146const PSI_5_COCHAIN_COMPLEX_TAG: u64 = 5;
147const PSI_6_COHOMOLOGY_GROUPS_TAG: u64 = 6;
148const PSI_7_POSTNIKOV_TOWER_TAG: u64 = 7;
149const PSI_8_HOMOTOPY_GROUPS_TAG: u64 = 8;
150
151const INPUT_LEN_VIOLATION: ShapeViolation = ShapeViolation {
154 shape_iri: "https://uor.foundation/addr/resolver/JsonInputBytes",
155 constraint_iri: "https://uor.foundation/addr/resolver/JsonInputBytes/maxBytes",
156 property_iri: "https://uor.foundation/addr/resolver/JsonInputBytes/byteCount",
157 expected_range: "http://www.w3.org/2001/XMLSchema#nonNegativeInteger",
158 min_count: 0,
159 max_count: JSON_INPUT_MAX_BYTES as u32,
160 kind: ViolationKind::ValueCheck,
161};
162
163const CARRIER_INPUT_VIOLATION: ShapeViolation = ShapeViolation {
164 shape_iri: "https://uor.foundation/addr/resolver/CarrierInput",
165 constraint_iri: "https://uor.foundation/addr/resolver/CarrierInput/expectedCarrierBytes",
166 property_iri: "https://uor.foundation/addr/resolver/CarrierInput/byteCount",
167 expected_range: "http://www.w3.org/2001/XMLSchema#unsignedInt",
168 min_count: CARRIER_BYTES as u32,
169 max_count: CARRIER_BYTES as u32,
170 kind: ViolationKind::ValueCheck,
171};
172
173const CARRIER_BUFFER_VIOLATION: ShapeViolation = ShapeViolation {
174 shape_iri: "https://uor.foundation/addr/resolver/CarrierBuffer",
175 constraint_iri: "https://uor.foundation/addr/resolver/CarrierBuffer/minBytes",
176 property_iri: "https://uor.foundation/addr/resolver/CarrierBuffer/byteCount",
177 expected_range: "http://www.w3.org/2001/XMLSchema#nonNegativeInteger",
178 min_count: CARRIER_BYTES as u32,
179 max_count: 0,
180 kind: ViolationKind::ValueCheck,
181};
182
183const ADDR_OUTPUT_VIOLATION: ShapeViolation = ShapeViolation {
184 shape_iri: "https://uor.foundation/addr/resolver/AddressLabelBuffer",
185 constraint_iri: "https://uor.foundation/addr/resolver/AddressLabelBuffer/minBytes",
186 property_iri: "https://uor.foundation/addr/resolver/AddressLabelBuffer/byteCount",
187 expected_range: "http://www.w3.org/2001/XMLSchema#nonNegativeInteger",
188 min_count: WIRE_FORMAT_ADDRESS_BYTES as u32,
189 max_count: 0,
190 kind: ViolationKind::ValueCheck,
191};
192
193const UPSTREAM_TAG_VIOLATION: ShapeViolation = ShapeViolation {
194 shape_iri: "https://uor.foundation/addr/resolver/UpstreamStageTag",
195 constraint_iri: "https://uor.foundation/addr/resolver/UpstreamStageTag/expectedTag",
196 property_iri: "https://uor.foundation/addr/resolver/UpstreamStageTag/stageTag",
197 expected_range: "http://www.w3.org/2001/XMLSchema#unsignedLong",
198 min_count: 1,
199 max_count: 1,
200 kind: ViolationKind::ValueCheck,
201};
202
203const GEOMETRY_VIOLATION: ShapeViolation = ShapeViolation {
204 shape_iri: "https://uor.foundation/addr/resolver/CarrierGeometry",
205 constraint_iri:
206 "https://uor.foundation/addr/resolver/CarrierGeometry/seventyOneVerticesDiscrete",
207 property_iri: "https://uor.foundation/addr/resolver/CarrierGeometry/vertexCount",
208 expected_range: "http://www.w3.org/2001/XMLSchema#unsignedInt",
209 min_count: NERVE_VERTEX_COUNT,
210 max_count: NERVE_VERTEX_COUNT,
211 kind: ViolationKind::ValueCheck,
212};
213
214const _: () = {
220 let cs = <AddressLabel as ConstrainedTypeShape>::CONSTRAINTS;
221 assert!(
222 cs.len() == NERVE_VERTEX_COUNT as usize,
223 "AddressLabel::CONSTRAINTS must declare exactly 71 Site instances (algebraic-closure)"
224 );
225 let mut i = 0;
226 while i < cs.len() {
227 match cs[i] {
228 ConstraintRef::Site { position } => {
229 assert!(
230 position as usize == i,
231 "AddressLabel::CONSTRAINTS: Site_i must pin position i for i ∈ [0, 71)"
232 );
233 }
234 _ => panic!("AddressLabel::CONSTRAINTS may only contain ConstraintRef::Site instances"),
235 }
236 i += 1;
237 }
238};
239
240const GEOMETRY_TAIL_AFTER_STAGE_TAG: [u8; GEOMETRY_TAIL_BYTES - 8] = {
247 let mut t = [0u8; GEOMETRY_TAIL_BYTES - 8];
248 let vc = NERVE_VERTEX_COUNT.to_be_bytes();
249 t[0] = vc[0];
250 t[1] = vc[1];
251 t[2] = vc[2];
252 t[3] = vc[3];
253 let mut i = 0;
255 while i < NERVE_VERTEX_COUNT as usize {
256 t[12 + i] = i as u8;
257 i += 1;
258 }
259 t
260};
261
262struct DecodedCarrier<'a> {
266 canonical: &'a [u8],
270 stage_tag: u64,
272 vertex_count: u32,
274 highest_dim: u32,
276}
277
278#[inline]
279fn decode_carrier(bytes: &[u8]) -> Result<DecodedCarrier<'_>, ShapeViolation> {
280 if bytes.len() != CARRIER_BYTES {
281 return Err(CARRIER_INPUT_VIOLATION);
282 }
283 let canonical_len =
284 u32::from_be_bytes(bytes[..LENGTH_PREFIX_BYTES].try_into().unwrap_or([0; 4])) as usize;
285 if canonical_len > JSON_INPUT_MAX_BYTES {
286 return Err(INPUT_LEN_VIOLATION);
287 }
288 let canonical = &bytes[LENGTH_PREFIX_BYTES..LENGTH_PREFIX_BYTES + canonical_len];
289 let stage_tag = u64::from_be_bytes(
290 bytes[OFFSET_STAGE_TAG..OFFSET_VERTEX_COUNT]
291 .try_into()
292 .unwrap_or([0; 8]),
293 );
294 let vertex_count = u32::from_be_bytes(
295 bytes[OFFSET_VERTEX_COUNT..OFFSET_HIGHEST_DIM]
296 .try_into()
297 .unwrap_or([0; 4]),
298 );
299 let highest_dim = u32::from_be_bytes(
300 bytes[OFFSET_HIGHEST_DIM..OFFSET_RESERVED]
301 .try_into()
302 .unwrap_or([0; 4]),
303 );
304 Ok(DecodedCarrier {
305 canonical,
306 stage_tag,
307 vertex_count,
308 highest_dim,
309 })
310}
311
312#[inline]
317fn validate_upstream(
318 carrier: &DecodedCarrier<'_>,
319 expected_upstream_tag: u64,
320) -> Result<(), ShapeViolation> {
321 if carrier.stage_tag != expected_upstream_tag {
322 return Err(UPSTREAM_TAG_VIOLATION);
323 }
324 if carrier.vertex_count != NERVE_VERTEX_COUNT {
325 return Err(GEOMETRY_VIOLATION);
326 }
327 if carrier.highest_dim != NERVE_HIGHEST_DIM {
328 return Err(GEOMETRY_VIOLATION);
329 }
330 Ok(())
331}
332
333#[inline]
339fn emit_carrier(canonical: &[u8], stage_tag: u64, out: &mut [u8]) -> Result<usize, ShapeViolation> {
340 if out.len() < CARRIER_BYTES {
341 return Err(CARRIER_BUFFER_VIOLATION);
342 }
343 if canonical.len() > JSON_INPUT_MAX_BYTES {
344 return Err(INPUT_LEN_VIOLATION);
345 }
346
347 let len_bytes = (canonical.len() as u32).to_be_bytes();
349 out[..LENGTH_PREFIX_BYTES].copy_from_slice(&len_bytes);
350 out[LENGTH_PREFIX_BYTES..LENGTH_PREFIX_BYTES + canonical.len()].copy_from_slice(canonical);
352 let zero_start = LENGTH_PREFIX_BYTES + canonical.len();
354 out[zero_start..OFFSET_GEOMETRY_TAIL].fill(0);
355 out[OFFSET_STAGE_TAG..OFFSET_VERTEX_COUNT].copy_from_slice(&stage_tag.to_be_bytes());
357 out[OFFSET_VERTEX_COUNT..].copy_from_slice(&GEOMETRY_TAIL_AFTER_STAGE_TAG);
359
360 Ok(CARRIER_BYTES)
361}
362
363#[derive(Debug)]
371pub struct AddressNerveResolver<H>(PhantomData<H>);
372
373impl<H: Hasher> uor_foundation::pipeline::__sdk_seal::Sealed for AddressNerveResolver<H> {}
374
375impl<H: Hasher> NerveResolver<H> for AddressNerveResolver<H> {
376 #[inline]
377 fn resolve(&self, input: &[u8], out: &mut [u8]) -> Result<usize, ShapeViolation> {
378 if input.len() > JSON_INPUT_MAX_BYTES {
379 return Err(INPUT_LEN_VIOLATION);
380 }
381 emit_carrier(input, PSI_1_NERVE_TAG, out)
382 }
383}
384
385#[derive(Debug)]
390pub struct AddressChainComplexResolver<H>(PhantomData<H>);
391
392impl<H: Hasher> uor_foundation::pipeline::__sdk_seal::Sealed for AddressChainComplexResolver<H> {}
393
394impl<H: Hasher> ChainComplexResolver<H> for AddressChainComplexResolver<H> {
395 #[inline]
396 fn resolve(
397 &self,
398 input: SimplicialComplexBytes<'_>,
399 out: &mut [u8],
400 ) -> Result<usize, ShapeViolation> {
401 let carrier = decode_carrier(input.as_bytes())?;
402 validate_upstream(&carrier, PSI_1_NERVE_TAG)?;
403 emit_carrier(carrier.canonical, PSI_2_CHAIN_COMPLEX_TAG, out)
404 }
405}
406
407#[derive(Debug)]
411pub struct AddressHomologyGroupResolver<H>(PhantomData<H>);
412
413impl<H: Hasher> uor_foundation::pipeline::__sdk_seal::Sealed for AddressHomologyGroupResolver<H> {}
414
415impl<H: Hasher> HomologyGroupResolver<H> for AddressHomologyGroupResolver<H> {
416 #[inline]
417 fn resolve(
418 &self,
419 input: ChainComplexBytes<'_>,
420 out: &mut [u8],
421 ) -> Result<usize, ShapeViolation> {
422 let carrier = decode_carrier(input.as_bytes())?;
423 validate_upstream(&carrier, PSI_2_CHAIN_COMPLEX_TAG)?;
424 emit_carrier(carrier.canonical, PSI_3_HOMOLOGY_GROUPS_TAG, out)
425 }
426}
427
428#[derive(Debug)]
432pub struct AddressCochainComplexResolver<H>(PhantomData<H>);
433
434impl<H: Hasher> uor_foundation::pipeline::__sdk_seal::Sealed for AddressCochainComplexResolver<H> {}
435
436impl<H: Hasher> CochainComplexResolver<H> for AddressCochainComplexResolver<H> {
437 #[inline]
438 fn resolve(
439 &self,
440 input: ChainComplexBytes<'_>,
441 out: &mut [u8],
442 ) -> Result<usize, ShapeViolation> {
443 let carrier = decode_carrier(input.as_bytes())?;
444 validate_upstream(&carrier, PSI_2_CHAIN_COMPLEX_TAG)?;
445 emit_carrier(carrier.canonical, PSI_5_COCHAIN_COMPLEX_TAG, out)
446 }
447}
448
449#[derive(Debug)]
453pub struct AddressCohomologyGroupResolver<H>(PhantomData<H>);
454
455impl<H: Hasher> uor_foundation::pipeline::__sdk_seal::Sealed for AddressCohomologyGroupResolver<H> {}
456
457impl<H: Hasher> CohomologyGroupResolver<H> for AddressCohomologyGroupResolver<H> {
458 #[inline]
459 fn resolve(
460 &self,
461 input: CochainComplexBytes<'_>,
462 out: &mut [u8],
463 ) -> Result<usize, ShapeViolation> {
464 let carrier = decode_carrier(input.as_bytes())?;
465 validate_upstream(&carrier, PSI_5_COCHAIN_COMPLEX_TAG)?;
466 emit_carrier(carrier.canonical, PSI_6_COHOMOLOGY_GROUPS_TAG, out)
467 }
468}
469
470#[derive(Debug)]
475pub struct AddressPostnikovResolver<H>(PhantomData<H>);
476
477impl<H: Hasher> uor_foundation::pipeline::__sdk_seal::Sealed for AddressPostnikovResolver<H> {}
478
479impl<H: Hasher> PostnikovResolver<H> for AddressPostnikovResolver<H> {
480 #[inline]
481 fn resolve(
482 &self,
483 input: SimplicialComplexBytes<'_>,
484 out: &mut [u8],
485 ) -> Result<usize, ShapeViolation> {
486 let carrier = decode_carrier(input.as_bytes())?;
487 validate_upstream(&carrier, PSI_1_NERVE_TAG)?;
488 emit_carrier(carrier.canonical, PSI_7_POSTNIKOV_TOWER_TAG, out)
489 }
490}
491
492#[derive(Debug)]
496pub struct AddressHomotopyGroupResolver<H>(PhantomData<H>);
497
498impl<H: Hasher> uor_foundation::pipeline::__sdk_seal::Sealed for AddressHomotopyGroupResolver<H> {}
499
500impl<H: Hasher> HomotopyGroupResolver<H> for AddressHomotopyGroupResolver<H> {
501 #[inline]
502 fn resolve(
503 &self,
504 input: PostnikovTowerBytes<'_>,
505 out: &mut [u8],
506 ) -> Result<usize, ShapeViolation> {
507 let carrier = decode_carrier(input.as_bytes())?;
508 validate_upstream(&carrier, PSI_7_POSTNIKOV_TOWER_TAG)?;
509 emit_carrier(carrier.canonical, PSI_8_HOMOTOPY_GROUPS_TAG, out)
510 }
511}
512
513#[derive(Debug)]
539pub struct AddressKInvariantResolver<H>(PhantomData<H>);
540
541impl<H: Hasher> uor_foundation::pipeline::__sdk_seal::Sealed for AddressKInvariantResolver<H> {}
542
543impl<H: Hasher> KInvariantResolver<H> for AddressKInvariantResolver<H> {
544 #[inline]
545 fn resolve(
546 &self,
547 input: HomotopyGroupsBytes<'_>,
548 out: &mut [u8],
549 ) -> Result<usize, ShapeViolation> {
550 if out.len() < WIRE_FORMAT_ADDRESS_BYTES {
551 return Err(ADDR_OUTPUT_VIOLATION);
552 }
553
554 let carrier = decode_carrier(input.as_bytes())?;
555 validate_upstream(&carrier, PSI_8_HOMOTOPY_GROUPS_TAG)?;
556
557 let digest: [u8; 32] = H::initial().fold_bytes(carrier.canonical).finalize_to_32();
561
562 out[..7].copy_from_slice(b"sha256:");
564 for (i, byte) in digest.iter().enumerate() {
565 out[7 + 2 * i] = HEX_LOWER[(byte >> 4) as usize];
566 out[7 + 2 * i + 1] = HEX_LOWER[(byte & 0x0F) as usize];
567 }
568
569 Ok(WIRE_FORMAT_ADDRESS_BYTES)
570 }
571}
572
573const HEX_LOWER: [u8; 16] = *b"0123456789abcdef";
574
575trait FinalizeTo32 {
581 fn finalize_to_32(self) -> [u8; 32];
582}
583
584impl<H: Hasher> FinalizeTo32 for H {
585 #[inline]
586 fn finalize_to_32(self) -> [u8; 32] {
587 let buf = <H as Hasher>::finalize(self);
588 let mut out = [0u8; 32];
589 out.copy_from_slice(&buf[..32]);
593 out
594 }
595}
596
597macro_rules! impl_default_for_resolver {
600 ($resolver:ident) => {
601 impl<H: Hasher> Default for $resolver<H> {
602 #[inline]
603 fn default() -> Self {
604 Self(PhantomData)
605 }
606 }
607 };
608}
609
610impl_default_for_resolver!(AddressNerveResolver);
611impl_default_for_resolver!(AddressChainComplexResolver);
612impl_default_for_resolver!(AddressHomologyGroupResolver);
613impl_default_for_resolver!(AddressCochainComplexResolver);
614impl_default_for_resolver!(AddressCohomologyGroupResolver);
615impl_default_for_resolver!(AddressPostnikovResolver);
616impl_default_for_resolver!(AddressHomotopyGroupResolver);
617impl_default_for_resolver!(AddressKInvariantResolver);
618
619resolver! {
620 pub struct AddressResolverTuple<H: ::uor_foundation::enforcement::Hasher> {
621 nerve: AddressNerveResolver<H>,
622 chain_complex: AddressChainComplexResolver<H>,
623 homology_groups: AddressHomologyGroupResolver<H>,
624 cochain_complex: AddressCochainComplexResolver<H>,
625 cohomology_groups: AddressCohomologyGroupResolver<H>,
626 postnikov: AddressPostnikovResolver<H>,
627 homotopy_groups: AddressHomotopyGroupResolver<H>,
628 k_invariants: AddressKInvariantResolver<H>,
629 }
630}
631
632const _: () = {
635 assert!(
636 CARRIER_BYTES <= <AddrBounds as HostBounds>::NERVE_OUTPUT_BYTES_MAX,
637 "ψ_1 carrier exceeds NERVE_OUTPUT_BYTES_MAX"
638 );
639 assert!(
640 CARRIER_BYTES <= <AddrBounds as HostBounds>::CHAIN_COMPLEX_OUTPUT_BYTES_MAX,
641 "ψ_2 carrier exceeds CHAIN_COMPLEX_OUTPUT_BYTES_MAX"
642 );
643 assert!(
644 CARRIER_BYTES <= <AddrBounds as HostBounds>::HOMOLOGY_GROUPS_OUTPUT_BYTES_MAX,
645 "ψ_3 carrier exceeds HOMOLOGY_GROUPS_OUTPUT_BYTES_MAX"
646 );
647 assert!(
648 CARRIER_BYTES <= <AddrBounds as HostBounds>::COCHAIN_COMPLEX_OUTPUT_BYTES_MAX,
649 "ψ_5 carrier exceeds COCHAIN_COMPLEX_OUTPUT_BYTES_MAX"
650 );
651 assert!(
652 CARRIER_BYTES <= <AddrBounds as HostBounds>::COHOMOLOGY_GROUPS_OUTPUT_BYTES_MAX,
653 "ψ_6 carrier exceeds COHOMOLOGY_GROUPS_OUTPUT_BYTES_MAX"
654 );
655 assert!(
656 CARRIER_BYTES <= <AddrBounds as HostBounds>::POSTNIKOV_TOWER_OUTPUT_BYTES_MAX,
657 "ψ_7 carrier exceeds POSTNIKOV_TOWER_OUTPUT_BYTES_MAX"
658 );
659 assert!(
660 CARRIER_BYTES <= <AddrBounds as HostBounds>::HOMOTOPY_GROUPS_OUTPUT_BYTES_MAX,
661 "ψ_8 carrier exceeds HOMOTOPY_GROUPS_OUTPUT_BYTES_MAX"
662 );
663 assert!(
664 WIRE_FORMAT_ADDRESS_BYTES <= <AddrBounds as HostBounds>::K_INVARIANTS_OUTPUT_BYTES_MAX,
665 "ψ_9 label exceeds K_INVARIANTS_OUTPUT_BYTES_MAX"
666 );
667};