lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

package_boundary.rs (9566B)


      1 use std::collections::BTreeSet;
      2 
      3 #[allow(unused_imports)]
      4 use radroots_event_codec::{authoring as _, canonical as _, decode as _, encode as _, verify as _};
      5 
      6 const MANIFEST: &str = include_str!("../Cargo.toml");
      7 const README: &str = include_str!("../README.md");
      8 const ROOT: &str = include_str!("../src/lib.rs");
      9 const RHI: &str = include_str!("../src/rhi.rs");
     10 const VERIFICATION: &str = include_str!("../src/verification/v1.rs");
     11 const EXAMPLE: &str = include_str!("../examples/verify_profile.rs");
     12 const FUZZ_LOCK: &str = include_str!("../../../fuzz/event_codec/Cargo.lock");
     13 const FUZZ_MANIFEST: &str = include_str!("../../../fuzz/event_codec/Cargo.toml");
     14 const PUBLIC_API: &str = include_str!("../../../contracts/api_baselines/radroots_event_codec.txt");
     15 
     16 #[test]
     17 fn manifest_has_final_identity_and_required_radroots_dependencies() {
     18     assert!(MANIFEST.contains("name = \"radroots_event_codec\""));
     19     assert!(MANIFEST.contains("version = \"0.1.0-alpha\""));
     20     assert!(MANIFEST.contains("publish = [\"crates-io\"]"));
     21     assert!(MANIFEST.contains("[lib]\nname = \"radroots_event_codec\""));
     22 
     23     let dependencies = table_keys(MANIFEST, "[dependencies]");
     24     for dependency in ["radroots_blossom", "radroots_event", "radroots_protocol"] {
     25         assert!(
     26             dependencies.contains(dependency),
     27             "missing required Radroots dependency {dependency}"
     28         );
     29     }
     30     assert!(
     31         MANIFEST.contains("radroots_protocol = { workspace = true, default-features = false }")
     32     );
     33 }
     34 
     35 #[test]
     36 fn consolidated_parser_fuzz_package_uses_the_repository_version_contract() {
     37     let package = "name = \"radroots_parser_fuzz\"\npublish = false\nversion = \"0.1.0-alpha\"";
     38     let locked_package = "name = \"radroots_parser_fuzz\"\nversion = \"0.1.0-alpha\"";
     39 
     40     assert!(FUZZ_MANIFEST.contains(package));
     41     assert!(FUZZ_LOCK.contains(locked_package));
     42     assert!(!FUZZ_MANIFEST.contains("version = \"0.0.0\""));
     43 }
     44 
     45 #[test]
     46 fn crate_root_declares_every_approved_module() {
     47     let declared = root_declarations("pub mod ");
     48     for module in [
     49         "admission",
     50         "authoring",
     51         "canonical",
     52         "decode",
     53         "encode",
     54         "manifest",
     55         "verify",
     56     ] {
     57         assert!(
     58             declared.contains(module),
     59             "missing approved module {module}"
     60         );
     61     }
     62 }
     63 
     64 #[test]
     65 fn canonical_root_exports_are_explicit_and_host_types_do_not_leak() {
     66     for export in [
     67         "pub use codec::Codec;",
     68         "pub use decode::DecodeError;",
     69         "pub use encode::EncodeError;",
     70         "pub use verify::VerificationError;",
     71     ] {
     72         assert!(
     73             ROOT.contains(export),
     74             "missing canonical root export {export}"
     75         );
     76     }
     77 
     78     assert!(ROOT.contains("#![cfg_attr(not(feature = \"std\"), no_std)]"));
     79     assert!(!ROOT.contains("pub trait "));
     80     assert!(!ROOT.contains("TEMPORARY COMPATIBILITY QUARANTINE"));
     81     for forbidden in [
     82         "nostr::",
     83         "nostr_sdk::",
     84         "reqwest::",
     85         "sqlx::",
     86         "tokio::",
     87         "std::os::",
     88     ] {
     89         assert!(
     90             !ROOT.contains(forbidden),
     91             "crate root must not expose host path {forbidden}"
     92         );
     93     }
     94 }
     95 
     96 #[test]
     97 fn compatibility_surface_is_removed() {
     98     assert!(MANIFEST.contains("publish = [\"crates-io\"]"));
     99     for module in [
    100         "comment",
    101         "deletion",
    102         "error",
    103         "job",
    104         "knowledge",
    105         "profile",
    106         "verification",
    107         "wire",
    108     ] {
    109         assert!(
    110             !ROOT.contains(&format!("pub mod {module};")),
    111             "compatibility module {module} remains public"
    112         );
    113     }
    114 
    115     for retired_root_export in [
    116         "pub use encode::tag_builders::RadrootsEventTagBuilder;",
    117         "pub use verify::{",
    118         "pub use manifest::registry_v7::{",
    119         "pub use manifest::{",
    120     ] {
    121         assert!(
    122             !ROOT.contains(retired_root_export),
    123             "retired prefixed root export remains: {retired_root_export}"
    124         );
    125     }
    126 }
    127 
    128 #[test]
    129 fn rhi_contract_is_private_curated_and_host_free() {
    130     assert!(ROOT.contains("#[cfg(feature = \"json\")]\nmod rhi;"));
    131     assert!(!ROOT.contains("pub mod rhi;"));
    132 
    133     for forbidden in [
    134         "nostr_sdk::",
    135         "reqwest::",
    136         "sqlx::",
    137         "tokio::",
    138         "std::fs",
    139         "std::net",
    140         "std::process",
    141     ] {
    142         assert!(
    143             !RHI.contains(forbidden),
    144             "RHI codec must not acquire host authority through {forbidden}"
    145         );
    146     }
    147 }
    148 
    149 #[test]
    150 fn codec_runtime_is_protocol_neutral_and_host_free() {
    151     let features = table_keys(MANIFEST, "[features]");
    152     let dependencies = table_keys(MANIFEST, "[dependencies]");
    153 
    154     assert!(!features.contains("nostr"));
    155     assert!(dependencies.contains("secp256k1"));
    156     for forbidden in [
    157         "keyring",
    158         "nostr",
    159         "nostr-sdk",
    160         "reqwest",
    161         "sqlx",
    162         "tokio",
    163         "wasm-bindgen",
    164         "web-sys",
    165     ] {
    166         assert!(
    167             !dependencies.contains(forbidden),
    168             "codec runtime must not depend on {forbidden}"
    169         );
    170     }
    171     assert!(!VERIFICATION.contains("nostr::"));
    172     assert!(!VERIFICATION.contains("feature = \"nostr\""));
    173 }
    174 
    175 #[test]
    176 fn serialization_features_are_explicit_additive_and_final() {
    177     let features = table_keys(MANIFEST, "[features]");
    178     assert_eq!(
    179         features,
    180         BTreeSet::from(["default", "json", "knowledge", "manifests", "serde", "std"])
    181     );
    182     assert!(MANIFEST.contains("default = [\"std\", \"json\"]"));
    183     assert!(MANIFEST.contains("json = [\"serde\", \"dep:serde_json\"]"));
    184     assert!(MANIFEST.contains("knowledge = [\"json\", \"radroots_event/knowledge\"]"));
    185     assert!(MANIFEST.contains("manifests = [\"knowledge\"]"));
    186 
    187     for forbidden in [
    188         "serde_json",
    189         "contract-manifest",
    190         "knowledge-nip54",
    191         "dto-bindgen",
    192         "codegen",
    193     ] {
    194         assert!(
    195             !features.contains(forbidden),
    196             "retired public feature {forbidden} must remain absent"
    197         );
    198     }
    199 }
    200 
    201 #[test]
    202 fn package_documentation_and_reviewed_api_baseline_are_complete() {
    203     for section in [
    204         "## Canonical surface",
    205         "## Verification pipeline",
    206         "## Features",
    207         "## Serialization and canonicalization",
    208         "## Security and trust boundaries",
    209         "## Side effects, cancellation, and commit points",
    210         "## Intended consumers",
    211         "## Package charter",
    212     ] {
    213         assert!(README.contains(section), "README is missing {section}");
    214     }
    215     assert!(ROOT.contains("#![doc = include_str!(\"../README.md\")]"));
    216     assert!(EXAMPLE.contains("use radroots_event_codec::{admission, decode, verify};"));
    217 
    218     assert!(PUBLIC_API.starts_with("pub mod radroots_event_codec\n"));
    219     for item in [
    220         "pub mod radroots_event_codec::admission",
    221         "pub mod radroots_event_codec::authoring",
    222         "pub mod radroots_event_codec::canonical",
    223         "pub mod radroots_event_codec::decode",
    224         "pub mod radroots_event_codec::encode",
    225         "pub mod radroots_event_codec::manifest",
    226         "pub mod radroots_event_codec::verify",
    227         "pub use radroots_event_codec::VerificationError",
    228         "pub struct radroots_event_codec::authoring::BlossomAuthorizationPlan",
    229         "pub const radroots_event_codec::authoring::REGISTRY_V7_TYPED_AUTHORING_CONTRACT_IDS: [&str; 16]",
    230         "pub fn radroots_event_codec::authoring::AuthoredEventPlan::from_trade_mutation",
    231         "pub fn radroots_event_codec::authoring::AuthoredEventPlan::from_rhi_evidence_attestation",
    232         "pub fn radroots_event_codec::decode::rhi::rhi_evidence_attestation_from_verified_event",
    233         "pub fn radroots_event_codec::decode::rhi::validate_rhi_evidence_attestation_supersession",
    234         "pub fn radroots_event_codec::encode::rhi::rhi_evidence_attestation_event_build_with_extra_tags",
    235         "pub fn radroots_event_codec::decode::trade::trade_mutation_from_verified_event",
    236         "pub fn radroots_event_codec::encode::trade::trade_mutation_event_build_with_extra_tags",
    237         "pub enum radroots_event_codec::decode::trade::RadrootsTradeMutationError",
    238     ] {
    239         assert!(PUBLIC_API.contains(item), "API baseline is missing {item}");
    240     }
    241     for forbidden in ["nostr_sdk::", "reqwest::", "sqlx::", "tokio::", "keyring::"] {
    242         assert!(
    243             !PUBLIC_API.contains(forbidden),
    244             "API baseline exposes forbidden host path {forbidden}"
    245         );
    246     }
    247     let retired = "RadrootsTradeMutationParseError";
    248     assert!(
    249         !PUBLIC_API.contains(retired),
    250         "API baseline retains unsafe trade authoring surface {retired}"
    251     );
    252 }
    253 
    254 fn table_keys<'a>(manifest: &'a str, heading: &str) -> BTreeSet<&'a str> {
    255     let Some((_, table)) = manifest.split_once(heading) else {
    256         return BTreeSet::new();
    257     };
    258     table
    259         .lines()
    260         .skip(1)
    261         .take_while(|line| !line.trim_start().starts_with('['))
    262         .filter_map(|line| {
    263             let line = line.trim();
    264             (line
    265                 .bytes()
    266                 .next()
    267                 .is_some_and(|byte| byte.is_ascii_lowercase() || byte == b'_')
    268                 && !line.starts_with('#'))
    269             .then(|| line.split_once('=').map(|(key, _)| key.trim()))
    270             .flatten()
    271         })
    272         .collect()
    273 }
    274 
    275 fn root_declarations(prefix: &str) -> BTreeSet<&str> {
    276     ROOT.lines()
    277         .map(str::trim)
    278         .filter_map(|line| line.strip_prefix(prefix))
    279         .filter_map(|name| name.strip_suffix(';'))
    280         .collect()
    281 }