File size: 5,054 Bytes
103262d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | // snapkitty-sovereign-addr — Non-recursive artifact addressing
//
// Algorithm:
// 1. validate JSON admissibility
// 2. normalize all strings to Unicode NFC
// 3. serialize canonical JSON with sorted keys, no whitespace
// 4. hash canonical bytes with SHA-256
// 5. emit snapaddr:<digest>
// 6. generate WORM receipt
// 7. never recurse in validation rules; use explicit staged traversal
pub mod canonical;
pub mod admissibility;
pub mod datalog;
pub mod receipt;
pub mod worm;
pub mod cli;
use serde::{Deserialize, Serialize};
use sha2::{Sha256, Digest};
use thiserror::Error;
/// Errors from sovereign addressing.
#[derive(Error, Debug, Clone)]
pub enum SnapAddrError {
#[error("not JSON admissible: {0}")]
NotAdmissible(String),
#[error("Unicode NFC normalization failed: {0}")]
NormalizationFailed(String),
#[error("canonical serialization failed: {0}")]
CanonicalFailed(String),
#[error("empty canonical bytes")]
EmptyCanonical,
#[error("address must be exactly 64 hex chars")]
InvalidAddressLength,
}
/// WORM receipt for a sovereign address.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct WormReceipt {
pub standard: String,
pub algorithm: String,
pub canonicalization: String,
pub address: String,
pub status: String,
pub seal: String,
pub governance: String,
}
/// Compute the canonical bytes of a JSON value.
///
/// Algorithm:
/// 1. Normalize all strings to Unicode NFC
/// 2. Sort object keys recursively
/// 3. Serialize with no whitespace
pub fn canonical_bytes(value: &serde_json::Value) -> Result<Vec<u8>, SnapAddrError> {
canonical::to_canonical(value)
}
/// Compute the sovereign address of a JSON value.
///
/// Returns: snapaddr:<64 lowercase hex chars>
pub fn sovereign_address(value: &serde_json::Value) -> Result<String, SnapAddrError> {
let bytes = canonical_bytes(value)?;
Ok(format!("snapaddr:{}", hex::encode(bytes)))
}
/// Compute the sovereign address as raw bytes.
pub fn sovereign_address_bytes(value: &serde_json::Value) -> Result<[u8; 32], SnapAddrError> {
let bytes = canonical_bytes(value)?;
let mut hasher = Sha256::new();
hasher.update(&bytes);
let result = hasher.finalize();
let mut out = [0u8; 32];
out.copy_from_slice(&result);
Ok(out)
}
/// Generate a WORM receipt for a JSON value.
pub fn worm_receipt(value: &serde_json::Value) -> Result<WormReceipt, SnapAddrError> {
worm::generate_receipt(value)
}
/// Verify that an address matches a JSON value.
pub fn verify_address(
value: &serde_json::Value,
expected_addr: &str,
) -> Result<bool, SnapAddrError> {
let addr = sovereign_address(value)?;
Ok(addr == expected_addr)
}
/// Verify a WORM receipt.
pub fn verify_receipt(receipt: &WormReceipt) -> Result<bool, SnapAddrError> {
worm::verify_receipt(receipt)
}
/// Normalize a JSON value to Unicode NFC (used by datalog).
pub fn normalize_value(value: &serde_json::Value) -> Result<serde_json::Value, SnapAddrError> {
canonical::normalize_value(value)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sovereign_address_simple() {
let value = serde_json::json!({"key": "value"});
let addr = sovereign_address(&value).unwrap();
assert!(addr.starts_with("snapaddr:"));
assert_eq!(addr.len(), 64 + "snapaddr:".len());
}
#[test]
fn test_sovereign_address_deterministic() {
let v1 = serde_json::json!({"a": 1, "b": 2});
let v2 = serde_json::json!({"b": 2, "a": 1});
assert_eq!(sovereign_address(&v1).unwrap(), sovereign_address(&v2).unwrap());
}
#[test]
fn test_sovereign_address_bytes() {
let value = serde_json::json!({"test": true});
let bytes = sovereign_address_bytes(&value).unwrap();
assert_eq!(bytes.len(), 32);
}
#[test]
fn test_worm_receipt() {
let value = serde_json::json!({"prime": 42});
let receipt = worm_receipt(&value).unwrap();
assert_eq!(receipt.standard, "SNAPKITTY-SOVEREIGN-ADDR-1");
assert_eq!(receipt.status, "accepted");
assert!(receipt.seal.starts_with("snapaddr:"));
}
#[test]
fn test_verify_address() {
let value = serde_json::json!({"check": "this"});
let addr = sovereign_address(&value).unwrap();
assert!(verify_address(&value, &addr).unwrap());
assert!(!verify_address(&value, "snapaddr:0000000000000000000000000000000000000000000000000000000000000000").unwrap());
}
#[test]
fn test_unicode_nfc_equivalence() {
// "é" can be U+00E9 or U+0065 U+0301 — both should produce same address
let v1 = serde_json::json!({"name": "\u{00e9}"});
let v2 = serde_json::json!({"name": "\u{0065}\u{0301}"});
assert_eq!(sovereign_address(&v1).unwrap(), sovereign_address(&v2).unwrap());
}
}
|