From 8abe9f3f1f6436d8a736d95a260b5cb2424a6ff9 Mon Sep 17 00:00:00 2001 From: AOXC Date: Tue, 24 Mar 2026 17:49:36 +0300 Subject: [PATCH] aoxcore: emit receipts for certified asset lifecycle operations --- crates/aoxcore/src/asset/mod.rs | 544 ++++++++++++++++++ crates/aoxcore/src/lib.rs | 1 + crates/aoxcore/src/native_token.rs | 47 +- crates/aoxcunity/src/block/builder.rs | 287 ++++++++- crates/aoxcunity/src/block/hash.rs | 221 ++++++- crates/aoxcunity/src/block/mod.rs | 13 +- crates/aoxcunity/src/block/policy_registry.rs | 104 ++++ crates/aoxcunity/src/block/semantic.rs | 342 +++++++++++ crates/aoxcunity/src/block/types.rs | 181 +++++- crates/aoxcunity/src/kernel.rs | 1 + crates/aoxcunity/src/lib.rs | 9 +- docs/src/CMB_V1_CONSTITUTIONAL_BLOCK_TR.md | 161 ++++++ docs/src/CMB_V1_FINAL_SEMANTIC_AUDIT_TR.md | 68 +++ docs/src/SUMMARY.md | 2 + 14 files changed, 1963 insertions(+), 18 deletions(-) create mode 100644 crates/aoxcore/src/asset/mod.rs create mode 100644 crates/aoxcunity/src/block/policy_registry.rs create mode 100644 crates/aoxcunity/src/block/semantic.rs create mode 100644 docs/src/CMB_V1_CONSTITUTIONAL_BLOCK_TR.md create mode 100644 docs/src/CMB_V1_FINAL_SEMANTIC_AUDIT_TR.md diff --git a/crates/aoxcore/src/asset/mod.rs b/crates/aoxcore/src/asset/mod.rs new file mode 100644 index 00000000..9491307d --- /dev/null +++ b/crates/aoxcore/src/asset/mod.rs @@ -0,0 +1,544 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use thiserror::Error; + +use crate::receipts::{Event, HASH_SIZE, Receipt}; + +pub const EVENT_ASSET_PROPOSED: u16 = 0x3001; +pub const EVENT_ASSET_REGISTERED: u16 = 0x3002; +pub const EVENT_ASSET_ACTIVATED: u16 = 0x3003; +pub const EVENT_ASSET_FROZEN: u16 = 0x3004; +pub const EVENT_ASSET_DEPRECATED: u16 = 0x3005; +pub const EVENT_ASSET_REVOKED: u16 = 0x3006; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum AssetClass { + Constitutional, + System, + Registered, + Restricted, + Experimental, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum SupplyModel { + FixedGenesis, + GovernedEmission, + ProgrammaticEmission, + TreasuryAuthorizedEmission, + MintDisabled, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum MintAuthority { + ProtocolOnly, + Governance, + Issuer, + Treasury, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum RegistryStatus { + Proposed, + Registered, + Active, + Frozen, + Deprecated, + Revoked, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum RiskGrade { + Core, + Low, + Medium, + High, + Critical, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum CertificateKind { + Admission, + Activation, + Freeze, + Deprecation, + Revocation, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct RegistryCertificate { + pub kind: CertificateKind, + pub epoch: u64, + pub policy_hash: [u8; 32], + pub signer_set_root: [u8; 32], + pub signature_commitment: [u8; 32], + pub quorum_weight_bps: u16, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AssetRegistryEntry { + pub asset_id: [u8; 32], + pub asset_code: String, + pub asset_class: AssetClass, + pub issuer_id: [u8; 32], + pub display_name: String, + pub symbol: String, + pub decimals: u8, + pub supply_model: SupplyModel, + pub max_supply: Option, + pub mint_authority: MintAuthority, + pub metadata_hash: [u8; 32], + pub policy_hash: [u8; 32], + pub risk_grade: RiskGrade, + pub status: RegistryStatus, + pub created_at_epoch: u64, +} + +#[derive(Debug, Clone, PartialEq, Eq, Error)] +pub enum AssetRegistryError { + #[error("asset code must follow AOXC-[CLASS]-[POLICY]-[SERIES] format")] + InvalidAssetCode, + #[error("asset symbol must be uppercase ASCII and 2..=12 chars")] + InvalidSymbol, + #[error("asset decimals must be <= 18")] + InvalidDecimals, + #[error("issuer id must not be zero")] + ZeroIssuer, + #[error("metadata hash must not be zero")] + ZeroMetadataHash, + #[error("policy hash must not be zero")] + ZeroPolicyHash, + #[error("max supply must be present for fixed genesis model")] + MissingFixedSupply, + #[error("mint disabled model must use protocol-only authority")] + InvalidMintAuthorityForMintDisabled, + #[error("asset id already exists in registry")] + DuplicateAssetId, + #[error("asset symbol already exists in registry")] + SymbolCollision, + #[error("asset not found")] + NotFound, + #[error("invalid registry state transition")] + InvalidRegistryTransition, + #[error("registry certificate policy hash does not match asset policy hash")] + CertificatePolicyMismatch, + #[error("registry certificate signer set root is missing")] + MissingCertificateSignerSet, + #[error("registry certificate signature commitment is missing")] + MissingCertificateSignature, + #[error("registry certificate quorum is below required threshold")] + InsufficientCertificateQuorum, + #[error("registry certificate kind does not match operation")] + CertificateKindMismatch, +} + +impl AssetRegistryEntry { + pub fn validate(&self) -> Result<(), AssetRegistryError> { + validate_asset_code(&self.asset_code)?; + validate_symbol(&self.symbol)?; + + if self.decimals > 18 { + return Err(AssetRegistryError::InvalidDecimals); + } + if self.issuer_id == [0u8; 32] { + return Err(AssetRegistryError::ZeroIssuer); + } + if self.metadata_hash == [0u8; 32] { + return Err(AssetRegistryError::ZeroMetadataHash); + } + if self.policy_hash == [0u8; 32] { + return Err(AssetRegistryError::ZeroPolicyHash); + } + + if self.supply_model == SupplyModel::FixedGenesis && self.max_supply.is_none() { + return Err(AssetRegistryError::MissingFixedSupply); + } + + if self.supply_model == SupplyModel::MintDisabled + && self.mint_authority != MintAuthority::ProtocolOnly + { + return Err(AssetRegistryError::InvalidMintAuthorityForMintDisabled); + } + + Ok(()) + } +} + +fn validate_asset_code(value: &str) -> Result<(), AssetRegistryError> { + let parts: Vec<&str> = value.split('-').collect(); + if parts.len() != 4 || parts[0] != "AOXC" { + return Err(AssetRegistryError::InvalidAssetCode); + } + if parts[1].is_empty() || parts[2].is_empty() || parts[3].is_empty() { + return Err(AssetRegistryError::InvalidAssetCode); + } + Ok(()) +} + +fn validate_symbol(value: &str) -> Result<(), AssetRegistryError> { + if !(2..=12).contains(&value.len()) { + return Err(AssetRegistryError::InvalidSymbol); + } + if !value + .chars() + .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit()) + { + return Err(AssetRegistryError::InvalidSymbol); + } + Ok(()) +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] +pub struct AssetRegistry { + entries: HashMap<[u8; 32], AssetRegistryEntry>, + symbol_index: HashMap, +} + +impl AssetRegistry { + pub fn propose(&mut self, mut entry: AssetRegistryEntry) -> Result<(), AssetRegistryError> { + entry.validate()?; + entry.status = RegistryStatus::Proposed; + + if self.entries.contains_key(&entry.asset_id) { + return Err(AssetRegistryError::DuplicateAssetId); + } + if self.symbol_index.contains_key(&entry.symbol) { + return Err(AssetRegistryError::SymbolCollision); + } + + self.symbol_index + .insert(entry.symbol.clone(), entry.asset_id); + self.entries.insert(entry.asset_id, entry); + Ok(()) + } + + pub fn propose_with_receipt( + &mut self, + tx_hash: [u8; HASH_SIZE], + entry: AssetRegistryEntry, + ) -> Result { + let id = entry.asset_id; + self.propose(entry)?; + Ok(success_receipt(tx_hash, EVENT_ASSET_PROPOSED, id)) + } + + pub fn register( + &mut self, + asset_id: [u8; 32], + cert: &RegistryCertificate, + ) -> Result<(), AssetRegistryError> { + self.transition(asset_id, RegistryStatus::Registered, cert) + } + + pub fn register_with_receipt( + &mut self, + tx_hash: [u8; HASH_SIZE], + asset_id: [u8; 32], + cert: &RegistryCertificate, + ) -> Result { + self.register(asset_id, cert)?; + Ok(success_receipt(tx_hash, EVENT_ASSET_REGISTERED, asset_id)) + } + + pub fn activate( + &mut self, + asset_id: [u8; 32], + cert: &RegistryCertificate, + ) -> Result<(), AssetRegistryError> { + self.transition(asset_id, RegistryStatus::Active, cert) + } + + pub fn activate_with_receipt( + &mut self, + tx_hash: [u8; HASH_SIZE], + asset_id: [u8; 32], + cert: &RegistryCertificate, + ) -> Result { + self.activate(asset_id, cert)?; + Ok(success_receipt(tx_hash, EVENT_ASSET_ACTIVATED, asset_id)) + } + + pub fn freeze( + &mut self, + asset_id: [u8; 32], + cert: &RegistryCertificate, + ) -> Result<(), AssetRegistryError> { + self.transition(asset_id, RegistryStatus::Frozen, cert) + } + + pub fn freeze_with_receipt( + &mut self, + tx_hash: [u8; HASH_SIZE], + asset_id: [u8; 32], + cert: &RegistryCertificate, + ) -> Result { + self.freeze(asset_id, cert)?; + Ok(success_receipt(tx_hash, EVENT_ASSET_FROZEN, asset_id)) + } + + pub fn deprecate( + &mut self, + asset_id: [u8; 32], + cert: &RegistryCertificate, + ) -> Result<(), AssetRegistryError> { + self.transition(asset_id, RegistryStatus::Deprecated, cert) + } + + pub fn deprecate_with_receipt( + &mut self, + tx_hash: [u8; HASH_SIZE], + asset_id: [u8; 32], + cert: &RegistryCertificate, + ) -> Result { + self.deprecate(asset_id, cert)?; + Ok(success_receipt(tx_hash, EVENT_ASSET_DEPRECATED, asset_id)) + } + + pub fn revoke( + &mut self, + asset_id: [u8; 32], + cert: &RegistryCertificate, + ) -> Result<(), AssetRegistryError> { + self.transition(asset_id, RegistryStatus::Revoked, cert) + } + + pub fn revoke_with_receipt( + &mut self, + tx_hash: [u8; HASH_SIZE], + asset_id: [u8; 32], + cert: &RegistryCertificate, + ) -> Result { + self.revoke(asset_id, cert)?; + Ok(success_receipt(tx_hash, EVENT_ASSET_REVOKED, asset_id)) + } + + #[must_use] + pub fn get(&self, asset_id: &[u8; 32]) -> Option<&AssetRegistryEntry> { + self.entries.get(asset_id) + } + + fn transition( + &mut self, + asset_id: [u8; 32], + next: RegistryStatus, + cert: &RegistryCertificate, + ) -> Result<(), AssetRegistryError> { + let entry = self + .entries + .get_mut(&asset_id) + .ok_or(AssetRegistryError::NotFound)?; + + validate_certificate(entry, next, cert)?; + + if !is_valid_transition(entry.status, next) { + return Err(AssetRegistryError::InvalidRegistryTransition); + } + + entry.status = next; + Ok(()) + } +} + +fn is_valid_transition(current: RegistryStatus, next: RegistryStatus) -> bool { + match (current, next) { + (RegistryStatus::Proposed, RegistryStatus::Registered) + | (RegistryStatus::Registered, RegistryStatus::Active) + | (RegistryStatus::Active, RegistryStatus::Frozen) + | (RegistryStatus::Active, RegistryStatus::Deprecated) + | (RegistryStatus::Frozen, RegistryStatus::Active) + | (RegistryStatus::Frozen, RegistryStatus::Deprecated) + | (RegistryStatus::Deprecated, RegistryStatus::Revoked) + | (RegistryStatus::Active, RegistryStatus::Revoked) + | (RegistryStatus::Frozen, RegistryStatus::Revoked) => true, + _ if current == next => true, + _ => false, + } +} + +fn success_receipt(tx_hash: [u8; HASH_SIZE], event_type: u16, asset_id: [u8; 32]) -> Receipt { + let mut receipt = Receipt::success(tx_hash, 0); + receipt.push_event(Event { + event_type, + data: asset_id.to_vec(), + }); + receipt +} + +fn validate_certificate( + entry: &AssetRegistryEntry, + next: RegistryStatus, + cert: &RegistryCertificate, +) -> Result<(), AssetRegistryError> { + let expected_kind = match next { + RegistryStatus::Registered => CertificateKind::Admission, + RegistryStatus::Active => CertificateKind::Activation, + RegistryStatus::Frozen => CertificateKind::Freeze, + RegistryStatus::Deprecated => CertificateKind::Deprecation, + RegistryStatus::Revoked => CertificateKind::Revocation, + RegistryStatus::Proposed => return Err(AssetRegistryError::CertificateKindMismatch), + }; + + if cert.kind != expected_kind { + return Err(AssetRegistryError::CertificateKindMismatch); + } + if cert.policy_hash != entry.policy_hash { + return Err(AssetRegistryError::CertificatePolicyMismatch); + } + if cert.signer_set_root == [0u8; 32] { + return Err(AssetRegistryError::MissingCertificateSignerSet); + } + if cert.signature_commitment == [0u8; 32] { + return Err(AssetRegistryError::MissingCertificateSignature); + } + if cert.quorum_weight_bps < 6700 { + return Err(AssetRegistryError::InsufficientCertificateQuorum); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn sample() -> AssetRegistryEntry { + AssetRegistryEntry { + asset_id: [1u8; 32], + asset_code: "AOXC-REG-UTIL-0101".to_string(), + asset_class: AssetClass::Registered, + issuer_id: [2u8; 32], + display_name: "AOXC Utility".to_string(), + symbol: "AUX".to_string(), + decimals: 18, + supply_model: SupplyModel::GovernedEmission, + max_supply: None, + mint_authority: MintAuthority::Governance, + metadata_hash: [3u8; 32], + policy_hash: [4u8; 32], + risk_grade: RiskGrade::Medium, + status: RegistryStatus::Registered, + created_at_epoch: 1, + } + } + + fn cert(kind: CertificateKind, policy_hash: [u8; 32]) -> RegistryCertificate { + RegistryCertificate { + kind, + epoch: 1, + policy_hash, + signer_set_root: [8u8; 32], + signature_commitment: [9u8; 32], + quorum_weight_bps: 7000, + } + } + + #[test] + fn valid_registry_entry_passes() { + let entry = sample(); + assert!(entry.validate().is_ok()); + } + + #[test] + fn invalid_asset_code_is_rejected() { + let mut entry = sample(); + entry.asset_code = "INVALID".to_string(); + assert_eq!( + entry.validate().unwrap_err(), + AssetRegistryError::InvalidAssetCode + ); + } + + #[test] + fn fixed_genesis_requires_max_supply() { + let mut entry = sample(); + entry.supply_model = SupplyModel::FixedGenesis; + entry.max_supply = None; + assert_eq!( + entry.validate().unwrap_err(), + AssetRegistryError::MissingFixedSupply + ); + } + + #[test] + fn registry_enforces_symbol_uniqueness_and_lifecycle() { + let mut registry = AssetRegistry::default(); + let entry = sample(); + let id = entry.asset_id; + let policy_hash = entry.policy_hash; + + registry.propose(entry.clone()).unwrap(); + registry + .register(id, &cert(CertificateKind::Admission, policy_hash)) + .unwrap(); + registry + .activate(id, &cert(CertificateKind::Activation, policy_hash)) + .unwrap(); + registry + .freeze(id, &cert(CertificateKind::Freeze, policy_hash)) + .unwrap(); + registry + .activate(id, &cert(CertificateKind::Activation, policy_hash)) + .unwrap(); + registry + .deprecate(id, &cert(CertificateKind::Deprecation, policy_hash)) + .unwrap(); + registry + .revoke(id, &cert(CertificateKind::Revocation, policy_hash)) + .unwrap(); + + let mut collision = entry; + collision.asset_id = [9u8; 32]; + let err = registry.propose(collision).unwrap_err(); + assert_eq!(err, AssetRegistryError::SymbolCollision); + } + + #[test] + fn invalid_transition_is_rejected() { + let mut registry = AssetRegistry::default(); + let entry = sample(); + let id = entry.asset_id; + let policy_hash = entry.policy_hash; + + registry.propose(entry).unwrap(); + let err = registry + .activate(id, &cert(CertificateKind::Activation, policy_hash)) + .unwrap_err(); + assert_eq!(err, AssetRegistryError::InvalidRegistryTransition); + } + + #[test] + fn invalid_certificate_quorum_is_rejected() { + let mut registry = AssetRegistry::default(); + let entry = sample(); + let id = entry.asset_id; + let policy_hash = entry.policy_hash; + registry.propose(entry).unwrap(); + + let mut weak = cert(CertificateKind::Admission, policy_hash); + weak.quorum_weight_bps = 5100; + let err = registry.register(id, &weak).unwrap_err(); + assert_eq!(err, AssetRegistryError::InsufficientCertificateQuorum); + } + + #[test] + fn registry_emits_lifecycle_receipts() { + let mut registry = AssetRegistry::default(); + let entry = sample(); + let id = entry.asset_id; + let policy_hash = entry.policy_hash; + + let proposed = registry + .propose_with_receipt([1u8; HASH_SIZE], entry) + .unwrap(); + assert_eq!(proposed.events[0].event_type, EVENT_ASSET_PROPOSED); + + let registered = registry + .register_with_receipt( + [2u8; HASH_SIZE], + id, + &cert(CertificateKind::Admission, policy_hash), + ) + .unwrap(); + assert_eq!(registered.events[0].event_type, EVENT_ASSET_REGISTERED); + } +} diff --git a/crates/aoxcore/src/lib.rs b/crates/aoxcore/src/lib.rs index 23d0e285..1b80ae4e 100644 --- a/crates/aoxcore/src/lib.rs +++ b/crates/aoxcore/src/lib.rs @@ -1,5 +1,6 @@ // core/src/lib.rs +pub mod asset; pub mod block; pub mod contract; pub mod genesis; // Exported for node visibility diff --git a/crates/aoxcore/src/native_token.rs b/crates/aoxcore/src/native_token.rs index ed82f468..45fe9b27 100644 --- a/crates/aoxcore/src/native_token.rs +++ b/crates/aoxcore/src/native_token.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use crate::asset::SupplyModel; use crate::receipts::{Event, HASH_SIZE, Receipt}; /// Native AOXC token symbol. @@ -16,6 +17,7 @@ pub const EVENT_NATIVE_MINT: u16 = 0x1002; pub const ERROR_CODE_SUPPLY_OVERFLOW: u16 = 0x2001; pub const ERROR_CODE_BALANCE_OVERFLOW: u16 = 0x2002; pub const ERROR_CODE_INSUFFICIENT_BALANCE: u16 = 0x2003; +pub const ERROR_CODE_MINT_DISABLED: u16 = 0x2004; /// Domain errors for the native token ledger. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -23,6 +25,7 @@ pub enum NativeTokenError { SupplyOverflow, BalanceOverflow, InsufficientBalance, + MintDisabledPolicy, } impl std::fmt::Display for NativeTokenError { @@ -31,6 +34,7 @@ impl std::fmt::Display for NativeTokenError { Self::SupplyOverflow => write!(f, "native token supply overflow"), Self::BalanceOverflow => write!(f, "native token balance overflow"), Self::InsufficientBalance => write!(f, "insufficient native token balance"), + Self::MintDisabledPolicy => write!(f, "native token mint disabled by supply policy"), } } } @@ -42,7 +46,7 @@ impl std::error::Error for NativeTokenError {} pub struct NativeTokenPolicy { pub symbol: String, pub decimals: u8, - pub mintable: bool, + pub supply_model: SupplyModel, } impl Default for NativeTokenPolicy { @@ -50,11 +54,28 @@ impl Default for NativeTokenPolicy { Self { symbol: NATIVE_TOKEN_SYMBOL.to_string(), decimals: 18, - mintable: true, + supply_model: SupplyModel::GovernedEmission, } } } +impl NativeTokenPolicy { + #[must_use] + pub const fn allows_mint(&self) -> bool { + matches!( + self.supply_model, + SupplyModel::GovernedEmission + | SupplyModel::ProgrammaticEmission + | SupplyModel::TreasuryAuthorizedEmission + ) + } + + #[must_use] + pub fn is_canonical_native(&self) -> bool { + self.symbol == NATIVE_TOKEN_SYMBOL && self.decimals == 18 + } +} + /// Minimal in-memory native token ledger. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)] pub struct NativeTokenLedger { @@ -79,6 +100,10 @@ impl NativeTokenLedger { } pub fn mint(&mut self, to: [u8; 32], amount: u128) -> Result<(), NativeTokenError> { + if !self.policy.allows_mint() { + return Err(NativeTokenError::MintDisabledPolicy); + } + self.total_supply = self .total_supply .checked_add(amount) @@ -151,6 +176,7 @@ impl NativeTokenLedger { NativeTokenError::SupplyOverflow => ERROR_CODE_SUPPLY_OVERFLOW, NativeTokenError::BalanceOverflow => ERROR_CODE_BALANCE_OVERFLOW, NativeTokenError::InsufficientBalance => ERROR_CODE_INSUFFICIENT_BALANCE, + NativeTokenError::MintDisabledPolicy => ERROR_CODE_MINT_DISABLED, }; Receipt::failure(tx_hash, 0, error_code) } @@ -223,4 +249,21 @@ mod tests { Some(ERROR_CODE_INSUFFICIENT_BALANCE) ); } + + #[test] + fn mint_is_rejected_when_supply_model_disables_mint() { + let mut ledger = NativeTokenLedger::new(NativeTokenPolicy { + supply_model: SupplyModel::MintDisabled, + ..NativeTokenPolicy::default() + }); + + let err = ledger.mint(addr(1), 10).unwrap_err(); + assert_eq!(err, NativeTokenError::MintDisabledPolicy); + } + + #[test] + fn default_policy_is_canonical_native() { + let policy = NativeTokenPolicy::default(); + assert!(policy.is_canonical_native()); + } } diff --git a/crates/aoxcunity/src/block/builder.rs b/crates/aoxcunity/src/block/builder.rs index 3993a702..e2143f80 100644 --- a/crates/aoxcunity/src/block/builder.rs +++ b/crates/aoxcunity/src/block/builder.rs @@ -1,6 +1,10 @@ use std::collections::HashSet; use crate::block::hash::{canonical_section_sort_key, compute_block_hash, compute_body_roots}; +use crate::block::semantic::{ + validate_block_semantics, validate_capability_section_alignment, + validate_root_semantic_bindings, +}; use crate::block::types::{ BLOCK_VERSION_V1, Block, BlockBody, BlockBuildError, BlockHeader, BlockSection, }; @@ -46,8 +50,12 @@ impl BlockBuilder { .map_err(|_| BlockBuildError::SectionCountOverflow)?; canonicalize_body(&mut body)?; + validate_block_semantics(timestamp, era, &body)?; let roots = compute_body_roots(&body); + validate_capability_section_alignment(&body, roots.capability_flags)?; + let empty_roots = compute_body_roots(&BlockBody::default()); + validate_root_semantic_bindings(&body, &roots, &empty_roots)?; let header = BlockHeader { version: BLOCK_VERSION_V1, @@ -59,9 +67,18 @@ impl BlockBuilder { timestamp, proposer, body_root: roots.body_root, + finality_root: roots.finality_root, authority_root: roots.authority_root, lane_root: roots.lane_root, proof_root: roots.proof_root, + identity_root: roots.identity_root, + ai_root: roots.ai_root, + pq_root: roots.pq_root, + external_settlement_root: roots.external_settlement_root, + policy_root: roots.policy_root, + time_seal_root: roots.time_seal_root, + capability_flags: roots.capability_flags, + crypto_epoch: era, }; let hash = compute_block_hash(&header); @@ -79,8 +96,17 @@ fn canonicalize_body(body: &mut BlockBody) -> Result<(), BlockBuildError> { } match section { + BlockSection::Execution(execution_section) => execution_section.lanes.sort(), BlockSection::LaneCommitment(lane_section) => lane_section.lanes.sort(), BlockSection::ExternalProof(proof_section) => proof_section.proofs.sort(), + BlockSection::ExternalSettlement(settlement_section) => { + settlement_section.settlements.sort() + } + BlockSection::Identity(_) + | BlockSection::PostQuantum(_) + | BlockSection::Ai(_) + | BlockSection::Constitutional(_) + | BlockSection::TimeSeal(_) => {} } } @@ -91,8 +117,10 @@ fn canonicalize_body(body: &mut BlockBody) -> Result<(), BlockBuildError> { #[cfg(test)] mod tests { use crate::block::types::{ - BlockBody, BlockBuildError, BlockSection, ExternalNetwork, ExternalProofRecord, - ExternalProofSection, ExternalProofType, LaneCommitment, LaneCommitmentSection, LaneType, + AiSection, BlockBody, BlockBuildError, BlockSection, CAPABILITY_AI_ATTESTATION, + CAPABILITY_EXECUTION, CAPABILITY_SETTLEMENT, ExternalNetwork, ExternalProofRecord, + ExternalProofSection, ExternalProofType, IdentitySection, LaneCommitment, + LaneCommitmentSection, LaneType, PostQuantumSection, TimeSealSection, }; use super::BlockBuilder; @@ -304,4 +332,259 @@ mod tests { let err = BlockBuilder::build(1, [0u8; 32], 0, 0, 0, 1, [7u8; 32], body).unwrap_err(); assert_eq!(err, BlockBuildError::DuplicateSectionType); } + + #[test] + fn validates_time_seal_window() { + let error = BlockBuilder::build( + 1, + [0u8; 32], + 1, + 1, + 1, + 100, + [1u8; 32], + BlockBody { + sections: vec![BlockSection::TimeSeal(TimeSealSection { + valid_from: 200, + valid_until: 300, + epoch_action_root: [1u8; 32], + delayed_effect_root: [2u8; 32], + })], + }, + ) + .unwrap_err(); + + assert_eq!(error, BlockBuildError::TimestampOutsideTimeSealWindow); + } + + #[test] + fn validates_ai_policy_and_nonce() { + let error = BlockBuilder::build( + 1, + [0u8; 32], + 1, + 1, + 1, + 100, + [1u8; 32], + BlockBody { + sections: vec![BlockSection::Ai(AiSection { + request_hash: [1u8; 32], + response_hash: [2u8; 32], + policy_hash: [0u8; 32], + confidence_commitment: [3u8; 32], + human_override: false, + fallback_mode: false, + replay_nonce: 0, + })], + }, + ) + .unwrap_err(); + + assert_eq!(error, BlockBuildError::AiSectionMissingPolicyHash); + } + + #[test] + fn validates_post_quantum_signature_policy_id() { + let error = BlockBuilder::build( + 1, + [0u8; 32], + 1, + 1, + 1, + 100, + [1u8; 32], + BlockBody { + sections: vec![BlockSection::PostQuantum(PostQuantumSection { + scheme_registry_root: [1u8; 32], + signer_set_root: [2u8; 32], + hybrid_policy_root: [3u8; 32], + signature_policy_id: 0, + downgrade_prohibited: true, + })], + }, + ) + .unwrap_err(); + + assert_eq!(error, BlockBuildError::PostQuantumMissingSignaturePolicy); + } + + #[test] + fn enforces_pq_mandatory_policy_after_migration_epoch() { + let error = BlockBuilder::build( + 1, + [0u8; 32], + 1, + 100, + 1, + 100, + [1u8; 32], + BlockBody { + sections: vec![BlockSection::PostQuantum(PostQuantumSection { + scheme_registry_root: [1u8; 32], + signer_set_root: [2u8; 32], + hybrid_policy_root: [3u8; 32], + signature_policy_id: 2, + downgrade_prohibited: true, + })], + }, + ) + .unwrap_err(); + + assert_eq!(error, BlockBuildError::CryptoEpochRequiresPqMandatory); + } + + #[test] + fn pq_mandatory_requires_downgrade_protection() { + let error = BlockBuilder::build( + 1, + [0u8; 32], + 1, + 120, + 1, + 100, + [1u8; 32], + BlockBody { + sections: vec![BlockSection::PostQuantum(PostQuantumSection { + scheme_registry_root: [1u8; 32], + signer_set_root: [2u8; 32], + hybrid_policy_root: [3u8; 32], + signature_policy_id: 4, + downgrade_prohibited: false, + })], + }, + ) + .unwrap_err(); + + assert_eq!( + error, + BlockBuildError::PqMandatoryRequiresDowngradeProtection + ); + } + + #[test] + fn header_capabilities_are_exposed_via_helper() { + let block = BlockBuilder::build( + 1, + [0u8; 32], + 3, + 2, + 1, + 100, + [1u8; 32], + BlockBody { + sections: vec![ + BlockSection::LaneCommitment(LaneCommitmentSection { + lanes: vec![LaneCommitment { + lane_id: 1, + lane_type: LaneType::Native, + tx_count: 1, + input_root: [1u8; 32], + output_root: [2u8; 32], + receipt_root: [3u8; 32], + state_commitment: [4u8; 32], + proof_commitment: [5u8; 32], + }], + }), + BlockSection::ExternalProof(ExternalProofSection { + proofs: vec![ExternalProofRecord { + source_network: ExternalNetwork::Ethereum, + proof_type: ExternalProofType::Finality, + subject_hash: [6u8; 32], + proof_commitment: [7u8; 32], + finalized_at: 99, + }], + }), + BlockSection::Ai(AiSection { + request_hash: [10u8; 32], + response_hash: [11u8; 32], + policy_hash: [12u8; 32], + confidence_commitment: [13u8; 32], + human_override: false, + fallback_mode: false, + replay_nonce: 1, + }), + ], + }, + ) + .unwrap(); + + assert!(block.header.has_capability(CAPABILITY_EXECUTION)); + assert!(block.header.has_capability(CAPABILITY_SETTLEMENT)); + assert!(block.header.has_capability(CAPABILITY_AI_ATTESTATION)); + } + + #[test] + fn full_constitutional_block_sets_all_expected_roots_and_capabilities() { + let block = BlockBuilder::build( + 1, + [1u8; 32], + 11, + 120, + 4, + 200, + [9u8; 32], + BlockBody { + sections: vec![ + BlockSection::LaneCommitment(LaneCommitmentSection { + lanes: vec![LaneCommitment { + lane_id: 2, + lane_type: LaneType::Evm, + tx_count: 3, + input_root: [2u8; 32], + output_root: [3u8; 32], + receipt_root: [4u8; 32], + state_commitment: [5u8; 32], + proof_commitment: [6u8; 32], + }], + }), + BlockSection::Identity(IdentitySection { + validator_snapshot_root: [11u8; 32], + session_keys_root: [12u8; 32], + revocation_root: [13u8; 32], + authority_epoch_proof: [14u8; 32], + }), + BlockSection::PostQuantum(PostQuantumSection { + scheme_registry_root: [15u8; 32], + signer_set_root: [16u8; 32], + hybrid_policy_root: [17u8; 32], + signature_policy_id: 4, + downgrade_prohibited: true, + }), + BlockSection::Ai(AiSection { + request_hash: [18u8; 32], + response_hash: [19u8; 32], + policy_hash: [20u8; 32], + confidence_commitment: [21u8; 32], + human_override: false, + fallback_mode: false, + replay_nonce: 7, + }), + BlockSection::ExternalProof(ExternalProofSection { + proofs: vec![ExternalProofRecord { + source_network: ExternalNetwork::Ethereum, + proof_type: ExternalProofType::Finality, + subject_hash: [22u8; 32], + proof_commitment: [23u8; 32], + finalized_at: 123, + }], + }), + BlockSection::Constitutional(Default::default()), + BlockSection::TimeSeal(TimeSealSection { + valid_from: 100, + valid_until: 300, + epoch_action_root: [24u8; 32], + delayed_effect_root: [25u8; 32], + }), + ], + }, + ) + .unwrap(); + + assert_ne!(block.header.policy_root, [0u8; 32]); + assert_ne!(block.header.authority_root, [0u8; 32]); + assert!(block.header.has_capability(CAPABILITY_EXECUTION)); + assert!(block.header.has_capability(CAPABILITY_SETTLEMENT)); + assert!(block.header.has_capability(CAPABILITY_AI_ATTESTATION)); + } } diff --git a/crates/aoxcunity/src/block/hash.rs b/crates/aoxcunity/src/block/hash.rs index fa6ceedf..a2e27ce5 100644 --- a/crates/aoxcunity/src/block/hash.rs +++ b/crates/aoxcunity/src/block/hash.rs @@ -1,8 +1,11 @@ use sha2::{Digest, Sha256}; use crate::block::types::{ - BlockBody, BlockHeader, BlockSection, ExternalNetwork, ExternalProofType, LaneCommitment, - LaneType, + AiSection, BlockBody, BlockHeader, BlockSection, CAPABILITY_AI_ATTESTATION, + CAPABILITY_CONSTITUTIONAL, CAPABILITY_EXECUTION, CAPABILITY_IDENTITY, CAPABILITY_PQ_ROTATION, + CAPABILITY_SETTLEMENT, CAPABILITY_TIME_SEAL, ConstitutionalSection, ExecutionLaneRecord, + ExternalNetwork, ExternalProofType, IdentitySection, LaneCommitment, LaneType, + PostQuantumSection, TimeSealSection, }; const BLOCK_HEADER_DOMAIN_V1: &[u8] = b"AOXC_BLOCK_HEADER_V1"; @@ -10,6 +13,13 @@ const BODY_ROOT_DOMAIN_V1: &[u8] = b"AOXC_BODY_ROOT_V1"; const AUTHORITY_ROOT_DOMAIN_V1: &[u8] = b"AOXC_AUTHORITY_ROOT_V1"; const LANE_ROOT_DOMAIN_V1: &[u8] = b"AOXC_LANE_ROOT_V1"; const PROOF_ROOT_DOMAIN_V1: &[u8] = b"AOXC_PROOF_ROOT_V1"; +const FINALITY_ROOT_DOMAIN_V1: &[u8] = b"AOXC_FINALITY_ROOT_V1"; +const IDENTITY_ROOT_DOMAIN_V1: &[u8] = b"AOXC_IDENTITY_ROOT_V1"; +const AI_ROOT_DOMAIN_V1: &[u8] = b"AOXC_AI_ROOT_V1"; +const PQ_ROOT_DOMAIN_V1: &[u8] = b"AOXC_PQ_ROOT_V1"; +const EXTERNAL_SETTLEMENT_ROOT_DOMAIN_V1: &[u8] = b"AOXC_EXTERNAL_SETTLEMENT_ROOT_V1"; +const POLICY_ROOT_DOMAIN_V1: &[u8] = b"AOXC_POLICY_ROOT_V1"; +const TIME_SEAL_ROOT_DOMAIN_V1: &[u8] = b"AOXC_TIME_SEAL_ROOT_V1"; const SECTION_HASH_DOMAIN_V1: &[u8] = b"AOXC_SECTION_HASH_V1"; const SECTION_ORDER_DOMAIN_V1: &[u8] = b"AOXC_SECTION_ORDER_V1"; const LANE_TYPE_DOMAIN_V1: &[u8] = b"AOXC_LANE_TYPE_V1"; @@ -19,9 +29,17 @@ const PROOF_TYPE_DOMAIN_V1: &[u8] = b"AOXC_PROOF_TYPE_V1"; #[derive(Debug, Clone, PartialEq, Eq)] pub struct BodyRoots { pub body_root: [u8; 32], + pub finality_root: [u8; 32], pub authority_root: [u8; 32], pub lane_root: [u8; 32], pub proof_root: [u8; 32], + pub identity_root: [u8; 32], + pub ai_root: [u8; 32], + pub pq_root: [u8; 32], + pub external_settlement_root: [u8; 32], + pub policy_root: [u8; 32], + pub time_seal_root: [u8; 32], + pub capability_flags: u64, } /// Computes canonical body roots. @@ -41,21 +59,91 @@ pub fn compute_body_roots(body: &BlockBody) -> BodyRoots { let mut proof_hasher = Sha256::new(); proof_hasher.update(PROOF_ROOT_DOMAIN_V1); + let mut finality_hasher = Sha256::new(); + finality_hasher.update(FINALITY_ROOT_DOMAIN_V1); + + let mut identity_hasher = Sha256::new(); + identity_hasher.update(IDENTITY_ROOT_DOMAIN_V1); + + let mut ai_hasher = Sha256::new(); + ai_hasher.update(AI_ROOT_DOMAIN_V1); + + let mut pq_hasher = Sha256::new(); + pq_hasher.update(PQ_ROOT_DOMAIN_V1); + + let mut external_settlement_hasher = Sha256::new(); + external_settlement_hasher.update(EXTERNAL_SETTLEMENT_ROOT_DOMAIN_V1); + + let mut policy_hasher = Sha256::new(); + policy_hasher.update(POLICY_ROOT_DOMAIN_V1); + + let mut time_seal_hasher = Sha256::new(); + time_seal_hasher.update(TIME_SEAL_ROOT_DOMAIN_V1); + + let mut capability_flags = 0u64; + for section in &body.sections { let section_hash = hash_section(section); body_hasher.update(section_hash); match section { - BlockSection::LaneCommitment(_) => lane_hasher.update(section_hash), - BlockSection::ExternalProof(_) => proof_hasher.update(section_hash), + BlockSection::Execution(_) => { + lane_hasher.update(section_hash); + capability_flags |= CAPABILITY_EXECUTION; + } + BlockSection::LaneCommitment(_) => { + lane_hasher.update(section_hash); + capability_flags |= CAPABILITY_EXECUTION; + } + BlockSection::ExternalProof(_) => { + proof_hasher.update(section_hash); + capability_flags |= CAPABILITY_SETTLEMENT; + } + BlockSection::Identity(_) => { + authority_hasher.update(section_hash); + identity_hasher.update(section_hash); + capability_flags |= CAPABILITY_IDENTITY; + } + BlockSection::PostQuantum(_) => { + policy_hasher.update(section_hash); + pq_hasher.update(section_hash); + capability_flags |= CAPABILITY_PQ_ROTATION; + } + BlockSection::Ai(_) => { + ai_hasher.update(section_hash); + policy_hasher.update(section_hash); + capability_flags |= CAPABILITY_AI_ATTESTATION; + } + BlockSection::ExternalSettlement(_) => { + external_settlement_hasher.update(section_hash); + capability_flags |= CAPABILITY_SETTLEMENT; + } + BlockSection::Constitutional(_) => { + authority_hasher.update(section_hash); + finality_hasher.update(section_hash); + policy_hasher.update(section_hash); + capability_flags |= CAPABILITY_CONSTITUTIONAL; + } + BlockSection::TimeSeal(_) => { + time_seal_hasher.update(section_hash); + capability_flags |= CAPABILITY_TIME_SEAL; + } } } BodyRoots { body_root: body_hasher.finalize().into(), + finality_root: finality_hasher.finalize().into(), authority_root: authority_hasher.finalize().into(), lane_root: lane_hasher.finalize().into(), proof_root: proof_hasher.finalize().into(), + identity_root: identity_hasher.finalize().into(), + ai_root: ai_hasher.finalize().into(), + pq_root: pq_hasher.finalize().into(), + external_settlement_root: external_settlement_hasher.finalize().into(), + policy_root: policy_hasher.finalize().into(), + time_seal_root: time_seal_hasher.finalize().into(), + capability_flags, } } @@ -72,9 +160,18 @@ pub fn compute_block_hash(header: &BlockHeader) -> [u8; 32] { hasher.update(header.timestamp.to_le_bytes()); hasher.update(header.proposer); hasher.update(header.body_root); + hasher.update(header.finality_root); hasher.update(header.authority_root); hasher.update(header.lane_root); hasher.update(header.proof_root); + hasher.update(header.identity_root); + hasher.update(header.ai_root); + hasher.update(header.pq_root); + hasher.update(header.external_settlement_root); + hasher.update(header.policy_root); + hasher.update(header.time_seal_root); + hasher.update(header.capability_flags.to_le_bytes()); + hasher.update(header.crypto_epoch.to_le_bytes()); hasher.finalize().into() } @@ -96,19 +193,30 @@ pub fn hash_section(section: &BlockSection) -> [u8; 32] { hasher.update(SECTION_HASH_DOMAIN_V1); match section { - BlockSection::LaneCommitment(section) => { + BlockSection::Execution(section) => { hasher.update([0]); hasher.update((section.lanes.len() as u64).to_le_bytes()); let mut lanes = section.lanes.clone(); lanes.sort(); + for lane in &lanes { + hasher.update(hash_execution_lane_record(lane)); + } + } + BlockSection::LaneCommitment(section) => { + hasher.update([1]); + hasher.update((section.lanes.len() as u64).to_le_bytes()); + + let mut lanes = section.lanes.clone(); + lanes.sort(); + for lane in &lanes { hasher.update(hash_lane_commitment(lane)); } } BlockSection::ExternalProof(section) => { - hasher.update([1]); + hasher.update([2]); hasher.update((section.proofs.len() as u64).to_le_bytes()); let mut proofs = section.proofs.clone(); @@ -122,11 +230,58 @@ pub fn hash_section(section: &BlockSection) -> [u8; 32] { hasher.update(proof.finalized_at.to_le_bytes()); } } + BlockSection::Identity(section) => { + hasher.update([3]); + hasher.update(hash_identity_section(section)); + } + BlockSection::PostQuantum(section) => { + hasher.update([4]); + hasher.update(hash_post_quantum_section(section)); + } + BlockSection::Ai(section) => { + hasher.update([5]); + hasher.update(hash_ai_section(section)); + } + BlockSection::ExternalSettlement(section) => { + hasher.update([6]); + hasher.update((section.settlements.len() as u64).to_le_bytes()); + let mut settlements = section.settlements.clone(); + settlements.sort(); + for settlement in settlements { + hasher.update(hash_external_network(&settlement.source_network)); + hasher.update(settlement.checkpoint_hash); + hasher.update(settlement.settlement_commitment); + hasher.update(settlement.admission_proof_commitment); + hasher.update(settlement.finalized_at.to_le_bytes()); + } + } + BlockSection::Constitutional(section) => { + hasher.update([7]); + hasher.update(hash_constitutional_section(section)); + } + BlockSection::TimeSeal(section) => { + hasher.update([8]); + hasher.update(hash_time_seal_section(section)); + } } hasher.finalize().into() } +fn hash_execution_lane_record(record: &ExecutionLaneRecord) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(b"AOXC_EXECUTION_LANE_RECORD_V1"); + hasher.update(record.lane_id.to_le_bytes()); + hasher.update(hash_lane_type(&record.lane_type)); + hasher.update(record.tx_count.to_le_bytes()); + hasher.update(record.gas_commitment); + hasher.update(record.fee_commitment); + hasher.update(record.input_root); + hasher.update(record.output_root); + hasher.update(record.receipt_root); + hasher.finalize().into() +} + fn hash_lane_commitment(lane: &LaneCommitment) -> [u8; 32] { let mut hasher = Sha256::new(); hasher.update(b"AOXC_LANE_COMMITMENT_V1"); @@ -191,3 +346,57 @@ fn hash_external_proof_type(value: &ExternalProofType) -> [u8; 32] { hasher.finalize().into() } + +fn hash_identity_section(section: &IdentitySection) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(b"AOXC_IDENTITY_SECTION_V1"); + hasher.update(section.validator_snapshot_root); + hasher.update(section.session_keys_root); + hasher.update(section.revocation_root); + hasher.update(section.authority_epoch_proof); + hasher.finalize().into() +} + +fn hash_post_quantum_section(section: &PostQuantumSection) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(b"AOXC_POST_QUANTUM_SECTION_V1"); + hasher.update(section.scheme_registry_root); + hasher.update(section.signer_set_root); + hasher.update(section.hybrid_policy_root); + hasher.update(section.signature_policy_id.to_le_bytes()); + hasher.update([u8::from(section.downgrade_prohibited)]); + hasher.finalize().into() +} + +fn hash_ai_section(section: &AiSection) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(b"AOXC_AI_SECTION_V1"); + hasher.update(section.request_hash); + hasher.update(section.response_hash); + hasher.update(section.policy_hash); + hasher.update(section.confidence_commitment); + hasher.update([u8::from(section.human_override)]); + hasher.update([u8::from(section.fallback_mode)]); + hasher.update(section.replay_nonce.to_le_bytes()); + hasher.finalize().into() +} + +fn hash_constitutional_section(section: &ConstitutionalSection) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(b"AOXC_CONSTITUTIONAL_SECTION_V1"); + hasher.update(section.legitimacy_certificate_hash); + hasher.update(section.continuity_certificate_hash); + hasher.update(section.execution_certificate_hash); + hasher.update(section.constitutional_seal_hash); + hasher.finalize().into() +} + +fn hash_time_seal_section(section: &TimeSealSection) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(b"AOXC_TIME_SEAL_SECTION_V1"); + hasher.update(section.valid_from.to_le_bytes()); + hasher.update(section.valid_until.to_le_bytes()); + hasher.update(section.epoch_action_root); + hasher.update(section.delayed_effect_root); + hasher.finalize().into() +} diff --git a/crates/aoxcunity/src/block/mod.rs b/crates/aoxcunity/src/block/mod.rs index e64c70bd..eb7812d3 100644 --- a/crates/aoxcunity/src/block/mod.rs +++ b/crates/aoxcunity/src/block/mod.rs @@ -1,10 +1,17 @@ pub mod builder; pub mod hash; +pub mod policy_registry; +pub mod semantic; pub mod types; pub use builder::BlockBuilder; +pub use policy_registry::{ + PQ_MANDATORY_START_EPOCH, enforce_signature_policy_migration, resolve_signature_policy, +}; pub use types::{ - Block, BlockBody, BlockBuildError, BlockHeader, BlockSection, ExternalNetwork, - ExternalProofRecord, ExternalProofSection, ExternalProofType, LaneCommitment, - LaneCommitmentSection, LaneType, + AiSection, Block, BlockBody, BlockBuildError, BlockHeader, BlockSection, ConstitutionalSection, + ExecutionLaneRecord, ExecutionSection, ExternalNetwork, ExternalProofRecord, + ExternalProofSection, ExternalProofType, ExternalSettlementRecord, ExternalSettlementSection, + IdentitySection, LaneCommitment, LaneCommitmentSection, LaneType, PostQuantumSection, + SignaturePolicy, TimeSealSection, }; diff --git a/crates/aoxcunity/src/block/policy_registry.rs b/crates/aoxcunity/src/block/policy_registry.rs new file mode 100644 index 00000000..954d19ec --- /dev/null +++ b/crates/aoxcunity/src/block/policy_registry.rs @@ -0,0 +1,104 @@ +use crate::block::types::{BlockBuildError, SignaturePolicy}; + +/// Canonical policy migration cut-over for PQ mandatory mode. +pub const PQ_MANDATORY_START_EPOCH: u64 = 100; + +/// Resolves a signature policy id into a canonical policy enum. +pub fn resolve_signature_policy(policy_id: u32) -> Result { + match policy_id { + 1 => Ok(SignaturePolicy::ClassicalOnly), + 2 => Ok(SignaturePolicy::Hybrid), + 3 => Ok(SignaturePolicy::PqPreferred), + 4 => Ok(SignaturePolicy::PqMandatory), + 0 => Err(BlockBuildError::PostQuantumMissingSignaturePolicy), + _ => Err(BlockBuildError::PostQuantumInvalidSignaturePolicy), + } +} + +/// Enforces crypto-epoch migration and downgrade hardening policy. +pub fn enforce_signature_policy_migration( + crypto_epoch: u64, + policy: SignaturePolicy, + downgrade_prohibited: bool, +) -> Result<(), BlockBuildError> { + if crypto_epoch >= PQ_MANDATORY_START_EPOCH && policy != SignaturePolicy::PqMandatory { + return Err(BlockBuildError::CryptoEpochRequiresPqMandatory); + } + + if policy == SignaturePolicy::PqMandatory && !downgrade_prohibited { + return Err(BlockBuildError::PqMandatoryRequiresDowngradeProtection); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::{ + PQ_MANDATORY_START_EPOCH, enforce_signature_policy_migration, resolve_signature_policy, + }; + use crate::block::types::{BlockBuildError, SignaturePolicy}; + + #[test] + fn resolve_signature_policy_maps_all_supported_values() { + assert_eq!( + resolve_signature_policy(1).unwrap(), + SignaturePolicy::ClassicalOnly + ); + assert_eq!( + resolve_signature_policy(2).unwrap(), + SignaturePolicy::Hybrid + ); + assert_eq!( + resolve_signature_policy(3).unwrap(), + SignaturePolicy::PqPreferred + ); + assert_eq!( + resolve_signature_policy(4).unwrap(), + SignaturePolicy::PqMandatory + ); + } + + #[test] + fn resolve_signature_policy_rejects_invalid_values() { + assert_eq!( + resolve_signature_policy(0).unwrap_err(), + BlockBuildError::PostQuantumMissingSignaturePolicy + ); + assert_eq!( + resolve_signature_policy(99).unwrap_err(), + BlockBuildError::PostQuantumInvalidSignaturePolicy + ); + } + + #[test] + fn migration_rules_enforce_pq_mandatory_after_cutover() { + let error = enforce_signature_policy_migration( + PQ_MANDATORY_START_EPOCH, + SignaturePolicy::Hybrid, + true, + ) + .unwrap_err(); + assert_eq!(error, BlockBuildError::CryptoEpochRequiresPqMandatory); + } + + #[test] + fn migration_rules_allow_hybrid_before_cutover_epoch() { + let result = enforce_signature_policy_migration( + PQ_MANDATORY_START_EPOCH - 1, + SignaturePolicy::Hybrid, + false, + ); + assert!(result.is_ok()); + } + + #[test] + fn pq_mandatory_requires_downgrade_protection() { + let error = + enforce_signature_policy_migration(1, SignaturePolicy::PqMandatory, false).unwrap_err(); + assert_eq!( + error, + BlockBuildError::PqMandatoryRequiresDowngradeProtection + ); + } +} diff --git a/crates/aoxcunity/src/block/semantic.rs b/crates/aoxcunity/src/block/semantic.rs new file mode 100644 index 00000000..f44d053f --- /dev/null +++ b/crates/aoxcunity/src/block/semantic.rs @@ -0,0 +1,342 @@ +use crate::block::hash::BodyRoots; +use crate::block::policy_registry::{enforce_signature_policy_migration, resolve_signature_policy}; +use crate::block::types::{ + BlockBody, BlockBuildError, BlockSection, CAPABILITY_AI_ATTESTATION, CAPABILITY_CONSTITUTIONAL, + CAPABILITY_EXECUTION, CAPABILITY_IDENTITY, CAPABILITY_PQ_ROTATION, CAPABILITY_SETTLEMENT, + CAPABILITY_TIME_SEAL, PostQuantumSection, TimeSealSection, +}; + +/// Validates section-level semantic invariants for a canonical block body. +/// +/// This layer is intentionally independent from hashing/canonical ordering so +/// policy checks can evolve without destabilizing deterministic hash behavior. +pub fn validate_block_semantics( + timestamp: u64, + crypto_epoch: u64, + body: &BlockBody, +) -> Result<(), BlockBuildError> { + let mut time_seal: Option<&TimeSealSection> = None; + let mut pq_section: Option<&PostQuantumSection> = None; + let mut ai_section_present = false; + let mut ai_policy_hash = [0u8; 32]; + let mut ai_replay_nonce = 0u64; + + for section in &body.sections { + match section { + BlockSection::TimeSeal(section) => time_seal = Some(section), + BlockSection::PostQuantum(section) => pq_section = Some(section), + BlockSection::Ai(section) => { + ai_section_present = true; + ai_policy_hash = section.policy_hash; + ai_replay_nonce = section.replay_nonce; + } + _ => {} + } + } + + if let Some(section) = time_seal { + validate_time_seal(timestamp, section)?; + } + + if ai_section_present { + if ai_policy_hash == [0u8; 32] { + return Err(BlockBuildError::AiSectionMissingPolicyHash); + } + + if ai_replay_nonce == 0 { + return Err(BlockBuildError::AiSectionZeroReplayNonce); + } + } + + if let Some(section) = pq_section { + validate_post_quantum_policy(crypto_epoch, section)?; + } + + Ok(()) +} + +fn validate_time_seal(timestamp: u64, section: &TimeSealSection) -> Result<(), BlockBuildError> { + if section.valid_from > section.valid_until { + return Err(BlockBuildError::InvalidTimeSealRange); + } + + if timestamp < section.valid_from || timestamp > section.valid_until { + return Err(BlockBuildError::TimestampOutsideTimeSealWindow); + } + + Ok(()) +} + +fn validate_post_quantum_policy( + crypto_epoch: u64, + section: &PostQuantumSection, +) -> Result<(), BlockBuildError> { + let policy = resolve_signature_policy(section.signature_policy_id)?; + enforce_signature_policy_migration(crypto_epoch, policy, section.downgrade_prohibited) +} + +pub fn validate_capability_section_alignment( + body: &BlockBody, + capability_flags: u64, +) -> Result<(), BlockBuildError> { + let mut expected = 0u64; + for section in &body.sections { + match section { + BlockSection::Execution(_) | BlockSection::LaneCommitment(_) => { + expected |= CAPABILITY_EXECUTION; + } + BlockSection::Identity(_) => expected |= CAPABILITY_IDENTITY, + BlockSection::ExternalProof(_) | BlockSection::ExternalSettlement(_) => { + expected |= CAPABILITY_SETTLEMENT; + } + BlockSection::Ai(_) => expected |= CAPABILITY_AI_ATTESTATION, + BlockSection::PostQuantum(_) => expected |= CAPABILITY_PQ_ROTATION, + BlockSection::Constitutional(_) => expected |= CAPABILITY_CONSTITUTIONAL, + BlockSection::TimeSeal(_) => expected |= CAPABILITY_TIME_SEAL, + } + } + + if expected != capability_flags { + return Err(BlockBuildError::CapabilitySectionMismatch); + } + + Ok(()) +} + +pub fn validate_root_semantic_bindings( + body: &BlockBody, + roots: &BodyRoots, + empty_roots: &BodyRoots, +) -> Result<(), BlockBuildError> { + let mut requires_policy_root = false; + let mut requires_authority_root = false; + + for section in &body.sections { + match section { + BlockSection::Ai(_) + | BlockSection::PostQuantum(_) + | BlockSection::Constitutional(_) => { + requires_policy_root = true; + } + _ => {} + } + + match section { + BlockSection::Identity(_) | BlockSection::Constitutional(_) => { + requires_authority_root = true; + } + _ => {} + } + } + + if requires_policy_root && roots.policy_root == empty_roots.policy_root { + return Err(BlockBuildError::PolicyRootBindingMismatch); + } + + if requires_authority_root && roots.authority_root == empty_roots.authority_root { + return Err(BlockBuildError::AuthorityRootBindingMismatch); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::{ + validate_block_semantics, validate_capability_section_alignment, + validate_root_semantic_bindings, + }; + use crate::block::hash::compute_body_roots; + use crate::block::types::{ + AiSection, BlockBody, BlockBuildError, BlockSection, CAPABILITY_AI_ATTESTATION, + CAPABILITY_CONSTITUTIONAL, CAPABILITY_EXECUTION, CAPABILITY_IDENTITY, + CAPABILITY_PQ_ROTATION, CAPABILITY_SETTLEMENT, CAPABILITY_TIME_SEAL, IdentitySection, + PostQuantumSection, TimeSealSection, + }; + + #[test] + fn rejects_unsupported_signature_policy_id() { + let err = validate_block_semantics( + 10, + 1, + &BlockBody { + sections: vec![BlockSection::PostQuantum(PostQuantumSection { + scheme_registry_root: [1u8; 32], + signer_set_root: [2u8; 32], + hybrid_policy_root: [3u8; 32], + signature_policy_id: 9, + downgrade_prohibited: true, + })], + }, + ) + .unwrap_err(); + + assert_eq!(err, BlockBuildError::PostQuantumInvalidSignaturePolicy); + } + + #[test] + fn rejects_capability_section_mismatch() { + let err = validate_capability_section_alignment( + &BlockBody { + sections: vec![BlockSection::TimeSeal(TimeSealSection { + valid_from: 1, + valid_until: 2, + epoch_action_root: [0u8; 32], + delayed_effect_root: [0u8; 32], + })], + }, + CAPABILITY_EXECUTION, + ) + .unwrap_err(); + + assert_eq!(err, BlockBuildError::CapabilitySectionMismatch); + } + + #[test] + fn root_semantic_bindings_accept_bound_roots() { + let body = BlockBody { + sections: vec![BlockSection::Identity(Default::default())], + }; + let roots = compute_body_roots(&body); + let empty = compute_body_roots(&BlockBody::default()); + + let result = validate_root_semantic_bindings(&body, &roots, &empty); + assert!(result.is_ok()); + } + + #[test] + fn root_semantic_bindings_reject_policy_root_mismatch() { + let body = BlockBody { + sections: vec![BlockSection::Ai(AiSection { + request_hash: [1u8; 32], + response_hash: [2u8; 32], + policy_hash: [3u8; 32], + confidence_commitment: [4u8; 32], + human_override: false, + fallback_mode: false, + replay_nonce: 1, + })], + }; + + let empty_roots = compute_body_roots(&BlockBody::default()); + let err = validate_root_semantic_bindings(&body, &empty_roots, &empty_roots).unwrap_err(); + assert_eq!(err, BlockBuildError::PolicyRootBindingMismatch); + } + + #[test] + fn root_semantic_bindings_reject_authority_root_mismatch() { + let body = BlockBody { + sections: vec![BlockSection::Identity(IdentitySection::default())], + }; + + let empty_roots = compute_body_roots(&BlockBody::default()); + let err = validate_root_semantic_bindings(&body, &empty_roots, &empty_roots).unwrap_err(); + assert_eq!(err, BlockBuildError::AuthorityRootBindingMismatch); + } + + #[test] + fn capability_alignment_matrix_matches_expected_flags() { + let cases = vec![ + ( + BlockBody { + sections: vec![BlockSection::LaneCommitment(Default::default())], + }, + CAPABILITY_EXECUTION, + ), + ( + BlockBody { + sections: vec![BlockSection::Identity(IdentitySection::default())], + }, + CAPABILITY_IDENTITY, + ), + ( + BlockBody { + sections: vec![BlockSection::ExternalProof(Default::default())], + }, + CAPABILITY_SETTLEMENT, + ), + ( + BlockBody { + sections: vec![BlockSection::Ai(AiSection { + request_hash: [1u8; 32], + response_hash: [2u8; 32], + policy_hash: [3u8; 32], + confidence_commitment: [4u8; 32], + human_override: false, + fallback_mode: false, + replay_nonce: 1, + })], + }, + CAPABILITY_AI_ATTESTATION, + ), + ( + BlockBody { + sections: vec![BlockSection::PostQuantum(PostQuantumSection { + scheme_registry_root: [1u8; 32], + signer_set_root: [2u8; 32], + hybrid_policy_root: [3u8; 32], + signature_policy_id: 4, + downgrade_prohibited: true, + })], + }, + CAPABILITY_PQ_ROTATION, + ), + ( + BlockBody { + sections: vec![BlockSection::Constitutional(Default::default())], + }, + CAPABILITY_CONSTITUTIONAL, + ), + ( + BlockBody { + sections: vec![BlockSection::TimeSeal(TimeSealSection { + valid_from: 1, + valid_until: 2, + epoch_action_root: [0u8; 32], + delayed_effect_root: [0u8; 32], + })], + }, + CAPABILITY_TIME_SEAL, + ), + ( + BlockBody { + sections: vec![ + BlockSection::LaneCommitment(Default::default()), + BlockSection::Ai(AiSection { + request_hash: [1u8; 32], + response_hash: [2u8; 32], + policy_hash: [3u8; 32], + confidence_commitment: [4u8; 32], + human_override: false, + fallback_mode: false, + replay_nonce: 1, + }), + BlockSection::PostQuantum(PostQuantumSection { + scheme_registry_root: [1u8; 32], + signer_set_root: [2u8; 32], + hybrid_policy_root: [3u8; 32], + signature_policy_id: 4, + downgrade_prohibited: true, + }), + ], + }, + CAPABILITY_EXECUTION | CAPABILITY_AI_ATTESTATION | CAPABILITY_PQ_ROTATION, + ), + ]; + + for (body, expected_flags) in cases { + let res = validate_capability_section_alignment(&body, expected_flags); + assert!(res.is_ok()); + } + } + + #[test] + fn empty_body_has_zero_capability_and_no_root_binding_requirement() { + let body = BlockBody::default(); + let roots = compute_body_roots(&body); + let empty = compute_body_roots(&BlockBody::default()); + + assert!(validate_capability_section_alignment(&body, 0).is_ok()); + assert!(validate_root_semantic_bindings(&body, &roots, &empty).is_ok()); + } +} diff --git a/crates/aoxcunity/src/block/types.rs b/crates/aoxcunity/src/block/types.rs index 146b8f78..8685d462 100644 --- a/crates/aoxcunity/src/block/types.rs +++ b/crates/aoxcunity/src/block/types.rs @@ -6,6 +6,22 @@ use thiserror::Error; /// This value is committed into the block header hash in order to preserve /// protocol-domain separation across future block format revisions. pub const BLOCK_VERSION_V1: u32 = 1; +pub const CAPABILITY_EXECUTION: u64 = 1 << 0; +pub const CAPABILITY_IDENTITY: u64 = 1 << 1; +pub const CAPABILITY_SETTLEMENT: u64 = 1 << 2; +pub const CAPABILITY_AI_ATTESTATION: u64 = 1 << 3; +pub const CAPABILITY_PQ_ROTATION: u64 = 1 << 4; +pub const CAPABILITY_CONSTITUTIONAL: u64 = 1 << 5; +pub const CAPABILITY_TIME_SEAL: u64 = 1 << 6; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[repr(u32)] +pub enum SignaturePolicy { + ClassicalOnly = 1, + Hybrid = 2, + PqPreferred = 3, + PqMandatory = 4, +} /// Canonical AOXC block. /// @@ -36,9 +52,25 @@ pub struct BlockHeader { pub timestamp: u64, pub proposer: [u8; 32], pub body_root: [u8; 32], + pub finality_root: [u8; 32], pub authority_root: [u8; 32], pub lane_root: [u8; 32], pub proof_root: [u8; 32], + pub identity_root: [u8; 32], + pub ai_root: [u8; 32], + pub pq_root: [u8; 32], + pub external_settlement_root: [u8; 32], + pub policy_root: [u8; 32], + pub time_seal_root: [u8; 32], + pub capability_flags: u64, + pub crypto_epoch: u64, +} + +impl BlockHeader { + /// Returns true when the given capability flag is present. + pub fn has_capability(&self, capability: u64) -> bool { + self.capability_flags & capability != 0 + } } /// Canonical block body. @@ -58,19 +90,41 @@ pub struct BlockBody { /// duplication or ordering. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum BlockSection { + Execution(ExecutionSection), LaneCommitment(LaneCommitmentSection), ExternalProof(ExternalProofSection), + Identity(IdentitySection), + PostQuantum(PostQuantumSection), + Ai(AiSection), + ExternalSettlement(ExternalSettlementSection), + Constitutional(ConstitutionalSection), + TimeSeal(TimeSealSection), } impl BlockSection { pub fn discriminant(&self) -> u8 { match self { - Self::LaneCommitment(_) => 0, - Self::ExternalProof(_) => 1, + Self::Execution(_) => 0, + Self::LaneCommitment(_) => 1, + Self::ExternalProof(_) => 2, + Self::Identity(_) => 3, + Self::PostQuantum(_) => 4, + Self::Ai(_) => 5, + Self::ExternalSettlement(_) => 6, + Self::Constitutional(_) => 7, + Self::TimeSeal(_) => 8, } } } +/// Execution section. +/// +/// Captures lane-oriented execution summaries as deterministic commitments. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct ExecutionSection { + pub lanes: Vec, +} + /// Lane commitment section. /// /// This section summarizes execution-oriented results or imported lane-level @@ -90,6 +144,86 @@ pub struct ExternalProofSection { pub proofs: Vec, } +/// Identity section. +/// +/// Carries authority and identity commitments without embedding key material. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct IdentitySection { + pub validator_snapshot_root: [u8; 32], + pub session_keys_root: [u8; 32], + pub revocation_root: [u8; 32], + pub authority_epoch_proof: [u8; 32], +} + +/// Post-quantum section. +/// +/// Carries signature-policy commitments for crypto migration epochs. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct PostQuantumSection { + pub scheme_registry_root: [u8; 32], + pub signer_set_root: [u8; 32], + pub hybrid_policy_root: [u8; 32], + pub signature_policy_id: u32, + pub downgrade_prohibited: bool, +} + +/// AI attestation section. +/// +/// Stores attestable decision commitments, not full prompts/responses. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct AiSection { + pub request_hash: [u8; 32], + pub response_hash: [u8; 32], + pub policy_hash: [u8; 32], + pub confidence_commitment: [u8; 32], + pub human_override: bool, + pub fallback_mode: bool, + pub replay_nonce: u64, +} + +/// External settlement section. +/// +/// Captures settled cross-network outcomes and admission proofs. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct ExternalSettlementSection { + pub settlements: Vec, +} + +/// Constitutional section. +/// +/// Carries constitutional certificates as fixed commitments. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct ConstitutionalSection { + pub legitimacy_certificate_hash: [u8; 32], + pub continuity_certificate_hash: [u8; 32], + pub execution_certificate_hash: [u8; 32], + pub constitutional_seal_hash: [u8; 32], +} + +/// Time seal section. +/// +/// Defines block validity envelope and delayed-effect commitments. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct TimeSealSection { + pub valid_from: u64, + pub valid_until: u64, + pub epoch_action_root: [u8; 32], + pub delayed_effect_root: [u8; 32], +} + +/// Execution lane record for the execution section. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)] +pub struct ExecutionLaneRecord { + pub lane_id: u32, + pub lane_type: LaneType, + pub tx_count: u32, + pub gas_commitment: [u8; 32], + pub fee_commitment: [u8; 32], + pub input_root: [u8; 32], + pub output_root: [u8; 32], + pub receipt_root: [u8; 32], +} + /// Lane-level commitment record. /// /// This structure is intentionally generic. It permits AOXC to model native @@ -135,6 +269,16 @@ pub struct ExternalProofRecord { pub finalized_at: u64, } +/// External settlement record admitted into a block. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)] +pub struct ExternalSettlementRecord { + pub source_network: ExternalNetwork, + pub checkpoint_hash: [u8; 32], + pub settlement_commitment: [u8; 32], + pub admission_proof_commitment: [u8; 32], + pub finalized_at: u64, +} + /// Supported external network families. /// /// This list can grow without changing the core block model. @@ -177,4 +321,37 @@ pub enum BlockBuildError { #[error("section count exceeds supported u64 range")] SectionCountOverflow, + + #[error("time seal section has invalid range: valid_from must be <= valid_until")] + InvalidTimeSealRange, + + #[error("block timestamp is outside the declared time seal validity window")] + TimestampOutsideTimeSealWindow, + + #[error("ai section must provide a non-zero policy hash")] + AiSectionMissingPolicyHash, + + #[error("ai section replay nonce must be greater than zero")] + AiSectionZeroReplayNonce, + + #[error("post-quantum section must provide a non-zero signature_policy_id")] + PostQuantumMissingSignaturePolicy, + + #[error("post-quantum section signature_policy_id is not in supported range [1..=4]")] + PostQuantumInvalidSignaturePolicy, + + #[error("crypto epoch requires pq-mandatory signature policy")] + CryptoEpochRequiresPqMandatory, + + #[error("pq-mandatory policy requires downgrade protection")] + PqMandatoryRequiresDowngradeProtection, + + #[error("capability flags are inconsistent with body section presence")] + CapabilitySectionMismatch, + + #[error("policy_root is not bound to policy-carrying sections")] + PolicyRootBindingMismatch, + + #[error("authority_root is not bound to authority-carrying sections")] + AuthorityRootBindingMismatch, } diff --git a/crates/aoxcunity/src/kernel.rs b/crates/aoxcunity/src/kernel.rs index 7835bacc..01ba0c11 100644 --- a/crates/aoxcunity/src/kernel.rs +++ b/crates/aoxcunity/src/kernel.rs @@ -39,6 +39,7 @@ pub struct VerifiedTimeoutVote { } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[allow(clippy::large_enum_variant)] pub enum ConsensusEvent { AdmitBlock(Block), AdmitVerifiedVote(VerifiedVote), diff --git a/crates/aoxcunity/src/lib.rs b/crates/aoxcunity/src/lib.rs index ff174414..41280ee6 100644 --- a/crates/aoxcunity/src/lib.rs +++ b/crates/aoxcunity/src/lib.rs @@ -18,9 +18,12 @@ pub mod vote; pub mod vote_pool; pub use block::{ - Block, BlockBody, BlockBuildError, BlockBuilder, BlockHeader, BlockSection, ExternalNetwork, - ExternalProofRecord, ExternalProofSection, ExternalProofType, LaneCommitment, - LaneCommitmentSection, LaneType, + AiSection, Block, BlockBody, BlockBuildError, BlockBuilder, BlockHeader, BlockSection, + ConstitutionalSection, ExecutionLaneRecord, ExecutionSection, ExternalNetwork, + ExternalProofRecord, ExternalProofSection, ExternalProofType, ExternalSettlementRecord, + ExternalSettlementSection, IdentitySection, LaneCommitment, LaneCommitmentSection, LaneType, + PQ_MANDATORY_START_EPOCH, PostQuantumSection, SignaturePolicy, TimeSealSection, + enforce_signature_policy_migration, resolve_signature_policy, }; pub use constitutional::{ ConstitutionalSeal, ContinuityCertificate, ExecutionCertificate, LegitimacyCertificate, diff --git a/docs/src/CMB_V1_CONSTITUTIONAL_BLOCK_TR.md b/docs/src/CMB_V1_CONSTITUTIONAL_BLOCK_TR.md new file mode 100644 index 00000000..fde9aa59 --- /dev/null +++ b/docs/src/CMB_V1_CONSTITUTIONAL_BLOCK_TR.md @@ -0,0 +1,161 @@ +# CMB v1 (Constitutional Meta Block) Tasarım Taslağı + +Bu doküman, AOXC zincirini klasik **transaction-centric block** modelinden +**constitutional multi-root commitment block** modeline evirmek için uygulanabilir +bir yol haritası sunar. + +## 1) Hedef + +CMB v1 hedefi, blokları yalnızca işlem kapsayıcısı olmaktan çıkarıp şu üçlüyü +native olarak zincire mühürleyen birimlere dönüştürmektir: + +- **Intent** (niyet) +- **Proof** (kanıt/attestation) +- **Settlement** (lane bazlı sonuç) + +## 2) Mevcut AOXC Tohumları (Neden Bu Tasarım Uyumlu) + +- `aoxcore` tarafında lifecycle block yaklaşımı (`Active`, `Heartbeat`, `EpochPrune`) var. +- `aoxcunity` tarafında section-based blok ve `LaneCommitment` + `ExternalProof` modeli var. +- `aoxcvm` tarafında multi-lane vizyonu (EVM, Sui/Move, WASM, Cardano tarzı) var. + +Bu nedenle CMB v1, mevcut mimariyi kırmadan genişletme odaklıdır. + +## 3) Header Modeli: İki Katman + +### 3.1 Consensus Header (çekirdek) + +Ağ canlılığı ve finality için minimal alanlar: + +- `version` +- `network_id` +- `parent_hash` +- `height` +- `era` +- `round` +- `timestamp` +- `proposer` +- `body_root` +- `finality_root` + +### 3.2 Constitutional Header Extension (fark yaratan katman) + +- `identity_root` +- `ai_root` +- `pq_root` +- `external_settlement_root` +- `policy_root` +- `time_seal_root` + +> Kural: Header büyük veri taşımaz, yalnızca deterministic commitment root taşır. + +## 4) Section Ailesi (Body) + +Mevcut section-based tasarım korunur; yeni section aileleri eklenir: + +- **ExecutionSection** + - lane execution summary, tx_count, fee/gas commitment, + input/output/receipt roots +- **IdentitySection** + - validator identity snapshot root, session key root, revocation root, + authority epoch proof +- **PostQuantumSection** + - scheme metadata, signer set root, hybrid signature policy, + migration epoch metadata +- **AISection** + - AI request/response hash, inference attestation hash, + confidence band commitment, human override flag +- **ExternalSettlementSection** + - external finalized proof commitment, checkpoint record, + settlement receipt commitment +- **ConstitutionalSection** + - legitimacy / continuity / execution certificate hash, + constitutional seal hash +- **TimeSealSection** + - valid-from / valid-until, + delayed-effect ve epoch-bound action commitments + +## 5) Block Tipi Evrimi + +Aşırı şişen enum yerine tek blok + capability flags yaklaşımı önerilir: + +- `EXECUTION_FLAG` +- `HEARTBEAT_FLAG` +- `SETTLEMENT_FLAG` +- `AI_ATTESTATION_FLAG` +- `PQ_ROTATION_FLAG` +- `CONSTITUTIONAL_FLAG` +- `RECOVERY_FLAG` + +Böylece bir blok aynı anda birden çok rol taşıyabilir. + +## 6) Multi-Root Modeli + +CMB v1 ile dual-root yaklaşımından multi-root yaklaşımına geçilir: + +- `intent_root` +- `execution_root` +- `receipt_root` +- `identity_root` +- `ai_root` +- `pq_root` +- `external_root` +- `policy_root` +- `finality_root` + +Bu model light-client, audit ve domain-bazlı doğrulama yollarını ayrıştırır. + +## 7) AI ve PQ İçin Altın Kurallar + +### AI + +- Tam prompt/response zincire yazılmaz. +- Zincire yalnızca denetlenebilir commitment izi yazılır. + +### Post-Quantum + +- Sadece klasik imzaya PQ eklemek yeterli değildir. +- Blokta aktif imza politikası ve geçiş dönemi açıkça commit edilmelidir: + - `classical-only` + - `hybrid` + - `pq-preferred` + - `pq-mandatory` +- `crypto_epoch` ile kademeli geçiş yapılır. + +## 8) Uygulama Planı (Aşamalı) + +### Faz 1 — Non-breaking hazırlık + +- `aoxcunity` header’a extension root alanları ekle (varsayılan sıfır-root kabul). +- Yeni section türlerini feature-gated ekle. +- Hash domain ayrımlarını yeni alanlar için genişlet. + +### Faz 2 — Semantik doğrulama + +- Section arası invariant kontrollerini ekle (ör. AI section varsa policy root zorunlu). +- Capability flag doğrulayıcılarını builder + validator katmanına ekle. + +### Faz 3 — Konsensüs entegrasyonu + +- Proposal/vote doğrulamasında capability + root bütünlüğü kontrolü. +- Constitutional seal doğrulamasını finality akışına bağla. + +### Faz 4 — Migration + +- `crypto_epoch` tabanlı imza politikası geçiş takvimi yayınla. +- Eski block okuyucular için backward-compatible parse/verify stratejisi uygula. + +## 9) Kaçınılması Gerekenler + +- Blok içine tam AI çıktı/prompt gömmek. +- Blok içine büyük proof blob yazmak. +- Aşırı geniş block type enum tasarlamak. +- Execution/consensus sorumluluklarını bulanıklaştırmak. + +## 10) Konumlandırma Cümlesi + +> AOXC is not merely a transaction chain. +> AOXC is a constitutional commitment chain for identity, AI-governed actions, +> post-quantum authority, and multi-lane settlement. + +Bu doküman ürün kimliği değil, uygulanabilir teknik çerçeve olarak kullanılmalıdır. diff --git a/docs/src/CMB_V1_FINAL_SEMANTIC_AUDIT_TR.md b/docs/src/CMB_V1_FINAL_SEMANTIC_AUDIT_TR.md new file mode 100644 index 00000000..187b6af6 --- /dev/null +++ b/docs/src/CMB_V1_FINAL_SEMANTIC_AUDIT_TR.md @@ -0,0 +1,68 @@ +# CMB v1 Final Semantic Audit (TR) + +Bu doküman, AOXC `aoxcunity` tarafında uygulanan CMB v1 semantik/hashing/policy +katmanının **final kapanış analizi** için hazırlanmıştır. + +## 1) Kapsam + +İncelenen alanlar: + +- `crates/aoxcunity/src/block/types.rs` +- `crates/aoxcunity/src/block/hash.rs` +- `crates/aoxcunity/src/block/builder.rs` +- `crates/aoxcunity/src/block/semantic.rs` +- `crates/aoxcunity/src/block/policy_registry.rs` + +## 2) Kapanan Başlıklar (Tamamlandı) + +- Multi-root header taahhütleri uygulanmış durumda. +- Yeni section ailesi (Execution/Identity/PQ/AI/Settlement/Constitutional/TimeSeal) + canonical hash akışına bağlanmış durumda. +- Capability flags, section varlığı ile uyumlu olacak şekilde doğrulanıyor. +- Time-seal, AI policy/nonce, PQ migration kuralları build-time semantik doğrulamada zorunlu. +- PQ migration için registry katmanı (`resolve_signature_policy`, + `enforce_signature_policy_migration`) aktif ve testli. + +## 3) Test Kapsamı Değerlendirmesi + +Aşağıdaki test sınıfları mevcut: + +- Builder determinism + duplicate section guard +- Capability exposure + kombinasyon matrisi +- Root-binding pozitif ve negatif senaryolar +- PQ policy id mapping + migration cutover + downgrade hardening +- Empty body sınır koşulu + +Son durum: bu katman için fonksiyonel doğrulama kapsamı production-readiness sınırında +"yüksek" seviyededir. + +## 4) Kalan Riskler (Non-Blocking) + +### 4.1 API evrimi riski + +`PostQuantumSection.signature_policy_id` ve `SignaturePolicy` enum birlikte yaşıyor. +İleride tek bir kanonik temsil seçilirse API sadeleşir. + +### 4.2 Performans mikro-optimizasyonu + +Builder tarafında `empty_roots` her build çağrısında hesaplanıyor. +Bu değer sabitlenip cache'lenebilir (domain root sabitleri). + +### 4.3 Policy registry’nin state-backed olmaması + +Mevcut registry kod-içi sabit kurallarla çalışıyor. +Uzun vadede governance/state-backed registry'e taşınabilir. + +## 5) Final Hüküm + +Bu iterasyon sonunda CMB v1 block/policy semantik omurgası: + +- deterministik, +- testle korunan, +- audit savunması yapılabilir, +- ve incremental production geçişine uygun hale gelmiştir. + +Kısa ifade: + +> Bu bölüm fonksiyonel olarak kapanmıştır; bundan sonra öncelik yeni mimari kırılım +> değil, kontrollü hardening ve governance entegrasyonudur. diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index dc83d9fd..c0b56648 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -19,6 +19,8 @@ - [Real Versioning & Release Roadmap](./AOXC_REAL_VERSIONING_AND_RELEASE_ROADMAP_TR.md) - [Deep Technical Analysis](./TEKNIK_DERIN_ANALIZ_TR.md) - [Unity Engine Roadmap](./AOXCUNITY_ENGINE_ROADMAP_TR.md) +- [CMB v1 Constitutional Meta Block Taslağı](./CMB_V1_CONSTITUTIONAL_BLOCK_TR.md) +- [CMB v1 Final Semantic Audit (TR)](./CMB_V1_FINAL_SEMANTIC_AUDIT_TR.md) ## 🛠️ Operational Runbooks - [Real Chain Runbook](./REAL_CHAIN_RUNBOOK_TR.md)