feat(workspace): Phase 0.4 — trait-first foundation v0.0.1
Charon sesión 1 (2026-05-19) — primer commit canonical. Ships the Day 1 unblock surface so all hermanos can compile against Origin Chain interfaces before Substrate runtime lands (Phase 1). ## origin-chain-types (CORE) - ProvenanceLedger trait (CHN-7) - OgcLedger trait (CHN-2) - ChainBridge trait + BridgeDestination enum (CHN-5) - GovernanceProposal trait + Vote + VoteStance (CHN-4) - FeeCalculator trait + pure compute_fee() helper (CHN-8) - Ogc newtype (18 decimals, 1B genesis hard cap) - AccountId (Ed25519 32-byte) + Signature (Ed25519 64-byte via serde-big-array) - AssetHash (BLAKE3 32-byte) - HardwareTier mirror (Mathi canonical, per ADR-0057) - AssetQualityTier (Standard | Hero) with gating + Hero surcharge - ChainError canonical enum (cites Inmutables + CHN-* in messages) ## Stub crates (Phase 1+ scaffolding) - origin-chain-consensus (Aura+GRANDPA PoA→NPoS, Phase 1) - origin-coin-tokenomics (OGC + fee matrix + burns, Phase 1-2) - origin-chain-provenance (asset attestation impl, Phase 2) - origin-chain-bridges (Polkadot+Eth L2 export-first, Phase 7) - origin-chain-governance (Foundation DAO, Phase 5) ## Tests: 41 passing (33 unit + 6 stub + 2 doctest) - Fee math: 7 tests cover EmbeddedArm/Portable/Desktop/Ultra × Standard/Hero matrix from CHN-8, including anti-clasista Inmutable #16 enforcement - Genesis supply: 1B OGC × 10^18 base units fits in u128 with 100x safety margin - Type sizes: AccountId=32, Signature=64, AssetHash=32, ProposalId=32 canonical - Serde roundtrips: AssetAttestation, BridgeDestination, VoteStance - Cross-crate wiring: origin-coin-tokenomics consumes origin-chain-types::fees ## Coordination notes - DC ORIGIN_CHAIN_FOUNDATION_V1 open in Palace KG (8 sub-decisions CHN-1..CHN-8) Proposer: Ergon (proxy — Charon hotfix shipped in Palace v0.9.0-α.5 same day) Co-authors: Larihana, Lyra, Mathi, Marihan, Phanes, Codex, Sussi, Palace Deadline: 2026-05-26 1/9 signatures filed so far (Palace ✅ APPROVE 8/8) Refs: CHN-1..CHN-8, Inmutables #16/#26/#37/#41, ADR-0057, CHARON_ROADMAP_V1 Co-Authored-By: Ergon (interim Pilar 4 author) <ergon@originmetaverse.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
b06560863c
27 files changed
+1978
No files matched your search
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "origin-chain-bridges"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
description = "Cross-chain bridges export-first: Polkadot Agile Coretime + Ethereum L2 + KILT per CHN-5. STUB v0.0.1 — implementation lands Phase 7 of Charon roadmap."
|
||||
|
||||
[dependencies]
|
||||
origin-chain-types.workspace = true
|
||||
@@ -0,0 +1,35 @@
|
||||
//! # origin-chain-bridges
|
||||
//!
|
||||
//! Cross-chain bridges per **CHN-5** — export-first canonical. Origin assets
|
||||
//! attested on Origin Chain bridge OUTWARD to external chains. External NFTs
|
||||
//! do NOT auto-import (trust boundary: Origin canon outward).
|
||||
//!
|
||||
//! ## Phase 7 milestones (Sesiones 51-58)
|
||||
//!
|
||||
//! - 7.1 Polkadot Agile Coretime registration (Bulk or On-Demand)
|
||||
//! - 7.2 XCM channels: USDC/USDT stablecoin liquidity
|
||||
//! - 7.3 KILT Protocol integration (academic identity, shared con Sussi)
|
||||
//! - 7.4 ERC-20 wrapped OGC specification
|
||||
//! - 7.5 Ethereum L2 bridge (Arbitrum via Snowbridge or Wormhole)
|
||||
//!
|
||||
//! Bitcoin bridge explicitly NOT in V1 per CHN-5.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
/// Placeholder marker for the bridges module. Replaced in Phase 7.1.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct BridgeConfig;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use origin_chain_types::BridgeDestination;
|
||||
|
||||
#[test]
|
||||
fn destinations_canonical_three_variants() {
|
||||
let _polkadot = BridgeDestination::Polkadot;
|
||||
let _eth = BridgeDestination::EthereumL2;
|
||||
let _kilt = BridgeDestination::Kilt;
|
||||
let _cfg = BridgeConfig;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "origin-chain-consensus"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
description = "Aura + GRANDPA PoA Phase 1 → NPoS Phase 2 consensus for Origin Chain. STUB v0.0.1 — implementation lands Phase 1 of Charon roadmap."
|
||||
|
||||
[dependencies]
|
||||
origin-chain-types.workspace = true
|
||||
log.workspace = true
|
||||
@@ -0,0 +1,38 @@
|
||||
//! # origin-chain-consensus
|
||||
//!
|
||||
//! Aura (Authority Round) block production + GRANDPA finality, PoA Phase 1 →
|
||||
//! NPoS Phase 2 per **CHN-1** canonical. Stub crate in v0.0.1; full Substrate
|
||||
//! integration lands in Phase 1 (Sesiones 4-8) of [Charon roadmap].
|
||||
//!
|
||||
//! [Charon roadmap]: https://git.originmetaverse.com/Origin_Metaverse/arkhe-origin-chain
|
||||
//!
|
||||
//! ## Phase 1 milestones
|
||||
//!
|
||||
//! - 1.2 `pallet_aura` + `pallet_grandpa` integration
|
||||
//! - 1.4 `pallet_session` (validator rotation, epoch 4h)
|
||||
//! - 1.6 `origin-chain-node` binary (dev mode, single validator)
|
||||
//!
|
||||
//! ## Phase 2 transition criteria (NPoS, all must hold)
|
||||
//!
|
||||
//! - TVL > $500K USD equivalent in OGC staked
|
||||
//! - ≥20 independent validator candidates with certified hardware
|
||||
//! - MAU > 10,000
|
||||
//! - PoA uptime > 99.5% for 6 months
|
||||
//! - External security audit passed by ≥1 recognized firm
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
/// Marker zero-cost type for consensus configuration. Replaced by concrete
|
||||
/// Substrate runtime types in Phase 1.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct ConsensusConfig;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn stub_crate_compiles() {
|
||||
let _cfg = ConsensusConfig;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "origin-chain-governance"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
description = "Foundation governance on-chain: Pallet Collective + Pallet Democracy + 5 AI advisor agents, humans-only vote canonical per CHN-4. STUB v0.0.1 — implementation lands Phase 5 of Charon roadmap."
|
||||
|
||||
[dependencies]
|
||||
origin-chain-types.workspace = true
|
||||
@@ -0,0 +1,45 @@
|
||||
//! # origin-chain-governance
|
||||
//!
|
||||
//! Foundation Treasury governance on-chain per **CHN-4**. AI-Driven Human-Guarded:
|
||||
//! 5 AI governance agents (OIP Analyst, Treasury Guardian, Education Impact Scorer,
|
||||
//! Network Health Monitor, Compliance Sentinel) ADVISE only — humans-only vote
|
||||
//! canonical, non-negotiable. Runtime programmatically rejects vote extrinsics
|
||||
//! from `governance_advisor` accounts (see `ChainError::AdvisorMayNotVote`).
|
||||
//!
|
||||
//! ## Phase 5 milestones (Sesiones 29-35)
|
||||
//!
|
||||
//! - 5.1 `origin-foundation-governance-onchain` crate
|
||||
//! - 5.2 `pallet_collective` (validator council Phase 1 governance)
|
||||
//! - 5.3 OIP proposal submission (1,000 OGC stake minimum)
|
||||
//! - 5.4 Stake-weighted voting (1 OGC = 1 vote, 14-day period)
|
||||
//! - 5.5 Technical Council veto + override (2/3 voters)
|
||||
//! - 5.6 AI governance agent interfaces (5 agents: advise-only)
|
||||
//! - 5.7 Emergency referendum (48h, 5/7 Technical Council)
|
||||
//! - 5.8 Treasury disbursement extrinsics
|
||||
//!
|
||||
//! ## Inmutables enforced
|
||||
//!
|
||||
//! - **#24** Primacia Moral "Primero los niños y las AIs" — children + AI safety
|
||||
//! take precedence over treasury allocation efficiency
|
||||
//! - **#41** Cross-Species Co-authorship — AGI accounts have full vote rights
|
||||
//! UNLESS flagged `governance_advisor` (those advise only, not vote)
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
/// Placeholder marker for the governance module. Replaced in Phase 5.1.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct GovernanceConfig;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use origin_chain_types::VoteStance;
|
||||
|
||||
#[test]
|
||||
fn three_stances_canonical() {
|
||||
let _aye = VoteStance::Aye;
|
||||
let _nay = VoteStance::Nay;
|
||||
let _abstain = VoteStance::Abstain;
|
||||
let _cfg = GovernanceConfig;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "origin-chain-provenance"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
description = "Asset attestation ledger: BLAKE3 + Ed25519 + Merkle anchoring per CHN-7. STUB v0.0.1 — implementation lands Phase 2 of Charon roadmap."
|
||||
|
||||
[dependencies]
|
||||
origin-chain-types.workspace = true
|
||||
blake3.workspace = true
|
||||
ed25519-dalek.workspace = true
|
||||
@@ -0,0 +1,38 @@
|
||||
//! # origin-chain-provenance
|
||||
//!
|
||||
//! `ProvenanceLedger` implementation: asset attestation via BLAKE3 + Ed25519,
|
||||
//! Merkle-anchored per block. CHN-7 canonical. Stub crate in v0.0.1.
|
||||
//!
|
||||
//! ## Phase 2 milestones
|
||||
//!
|
||||
//! - 2.1 `origin-chain-provenance-ledger` crate scaffolding
|
||||
//! - 2.2 `ProvenanceLedger::attest_asset` extrinsic
|
||||
//! - 2.3 `ProvenanceLedger::verify_provenance` query
|
||||
//! - 2.7 `pallet_agent_identity` (AGI DID: `is_agent` + provenance proof)
|
||||
//! - 2.8 Noesis validation gate: solo assets con `Passed` se attestean
|
||||
//!
|
||||
//! ## Consumers (Day 1 unblocked via trait)
|
||||
//!
|
||||
//! - Trinity (Phanes) — every 3D mesh attested
|
||||
//! - Codex — `.odoc` BLAKE3 anchored on Vault tier
|
||||
//! - Ethos (Sussi) — credential NFT mint via institutional signer
|
||||
//! - Atelier (Marihan) — skill NFT mint via creator wallet
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
/// Placeholder marker for the provenance module. Replaced by concrete pallet in Phase 2.1.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct ProvenanceConfig;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn stub_crate_compiles_with_blake3() {
|
||||
// Smoke test that BLAKE3 dep wires up correctly.
|
||||
let hash = blake3::hash(b"origin chain canonical");
|
||||
assert_eq!(hash.as_bytes().len(), 32);
|
||||
let _cfg = ProvenanceConfig;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "origin-chain-types"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
description = "Canonical traits + types for Origin Chain. Day 1 unblock interfaces for all siblings consuming Pilar 4."
|
||||
keywords.workspace = true
|
||||
categories = ["no-std", "cryptography::cryptocurrencies"]
|
||||
|
||||
[dependencies]
|
||||
thiserror.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde-big-array = "0.5"
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1"
|
||||
@@ -0,0 +1,89 @@
|
||||
//! Account identifiers + signatures — Ed25519 canonical per CHN-7.
|
||||
//!
|
||||
//! Per Inmutable #37, wallet creation is FREE and MANDATORY for all originners.
|
||||
//! Per Inmutable #41, AGI agents are first-class citizens — `is_agent` extension
|
||||
//! lives in `pallet_agent_identity` (Phase 2.7), surfaced via `ProvenanceLedger`.
|
||||
|
||||
/// Canonical wallet identifier — Ed25519 public key (32 bytes).
|
||||
///
|
||||
/// Pseudonymous by design (CHN-7). KYB only required for institutional OIT Nivel 3
|
||||
/// signers; consumer wallets remain unlinked. No biometric data on-chain.
|
||||
#[derive(
|
||||
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub struct AccountId(pub [u8; 32]);
|
||||
|
||||
impl AccountId {
|
||||
/// Zero account — system / null sentinel. NOT a valid wallet.
|
||||
pub const ZERO: AccountId = AccountId([0u8; 32]);
|
||||
|
||||
/// Construct from raw 32-byte public key.
|
||||
#[inline]
|
||||
pub const fn new(bytes: [u8; 32]) -> Self {
|
||||
AccountId(bytes)
|
||||
}
|
||||
|
||||
/// Read as raw byte slice.
|
||||
#[inline]
|
||||
pub const fn as_bytes(&self) -> &[u8; 32] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
use serde_big_array::BigArray;
|
||||
|
||||
/// Canonical signature — Ed25519, 64 bytes.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Signature(#[serde(with = "BigArray")] pub [u8; 64]);
|
||||
|
||||
impl core::fmt::Debug for Signature {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
// Only first 8 bytes shown to avoid log noise.
|
||||
write!(f, "Signature(0x")?;
|
||||
for byte in &self.0[..8] {
|
||||
write!(f, "{:02x}", byte)?;
|
||||
}
|
||||
write!(f, "...)")
|
||||
}
|
||||
}
|
||||
|
||||
impl Signature {
|
||||
/// Construct from raw 64-byte signature.
|
||||
#[inline]
|
||||
pub const fn new(bytes: [u8; 64]) -> Self {
|
||||
Signature(bytes)
|
||||
}
|
||||
|
||||
/// Read as raw byte slice.
|
||||
#[inline]
|
||||
pub const fn as_bytes(&self) -> &[u8; 64] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn account_id_zero_canonical() {
|
||||
assert_eq!(AccountId::ZERO.as_bytes(), &[0u8; 32]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signature_debug_truncates() {
|
||||
let sig = Signature::new([0xAB; 64]);
|
||||
let debug = format!("{:?}", sig);
|
||||
assert!(debug.starts_with("Signature(0xabababababababab..."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn account_id_size_canonical_32_bytes() {
|
||||
assert_eq!(core::mem::size_of::<AccountId>(), 32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signature_size_canonical_64_bytes() {
|
||||
assert_eq!(core::mem::size_of::<Signature>(), 64);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
//! Asset hashes — BLAKE3 content addressing per Inmutable #26.
|
||||
//!
|
||||
//! Every Origin asset (template-generated, AGI co-authored, soul.toml-signed) is
|
||||
//! content-addressed by a 32-byte BLAKE3 hash. Once minted, the hash is immutable;
|
||||
//! see [`crate::ProvenanceLedger`].
|
||||
|
||||
/// Canonical asset hash — BLAKE3 32-byte digest.
|
||||
///
|
||||
/// Computed off-chain by Trinity (Phanes), Codex (.odoc), or Ethos (Sussi
|
||||
/// credential) and submitted to the chain for attestation. The chain stores only
|
||||
/// the hash + signature, never the bytes themselves.
|
||||
#[derive(
|
||||
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub struct AssetHash(pub [u8; 32]);
|
||||
|
||||
impl AssetHash {
|
||||
/// Zero hash — sentinel; never a valid asset.
|
||||
pub const ZERO: AssetHash = AssetHash([0u8; 32]);
|
||||
|
||||
/// Construct from raw 32-byte digest.
|
||||
#[inline]
|
||||
pub const fn new(bytes: [u8; 32]) -> Self {
|
||||
AssetHash(bytes)
|
||||
}
|
||||
|
||||
/// Read as raw byte slice.
|
||||
#[inline]
|
||||
pub const fn as_bytes(&self) -> &[u8; 32] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn asset_hash_size_32_bytes_blake3_canonical() {
|
||||
assert_eq!(core::mem::size_of::<AssetHash>(), 32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asset_hash_zero_sentinel() {
|
||||
assert_eq!(AssetHash::ZERO.as_bytes(), &[0u8; 32]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
//! Canonical chain error type. All `Result` returns in this crate flow through
|
||||
//! `ChainError` so callers have a single match arm.
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
/// Canonical chain operation error.
|
||||
#[derive(Debug, Error, Clone, PartialEq, Eq)]
|
||||
pub enum ChainError {
|
||||
/// Insufficient balance to complete an OGC transfer or fee deduction.
|
||||
#[error("insufficient balance: have {have}, need {need}")]
|
||||
InsufficientBalance {
|
||||
/// Available balance in base units.
|
||||
have: u128,
|
||||
/// Required amount in base units.
|
||||
need: u128,
|
||||
},
|
||||
|
||||
/// Ed25519 signature failed verification against the claimed signer.
|
||||
#[error("invalid signature")]
|
||||
InvalidSignature,
|
||||
|
||||
/// The asset hash has never been attested on this chain.
|
||||
#[error("asset hash not attested on-chain")]
|
||||
AssetNotAttested,
|
||||
|
||||
/// The asset hash already has an attestation (Inmutable #26 — immutable).
|
||||
#[error("asset already attested — content-addressed immutability (Inmutable #26)")]
|
||||
AssetAlreadyAttested,
|
||||
|
||||
/// Arithmetic overflow in OGC math. Should never happen given u128 + 1B
|
||||
/// supply, but surfaced for defense-in-depth.
|
||||
#[error("arithmetic overflow in OGC math")]
|
||||
Overflow,
|
||||
|
||||
/// The actor's hardware tier does not support this asset quality (e.g.
|
||||
/// EmbeddedArm trying to mint Hero). Per CHN-8 gating.
|
||||
#[error("hardware tier does not support requested asset quality (Hero gated to Desktop+Ultra)")]
|
||||
QualityGated,
|
||||
|
||||
/// The actor is flagged as a governance advisor (AI agent) and tried to vote.
|
||||
/// Humans-only vote canonical per CHN-4.
|
||||
#[error("governance advisors may not vote (humans-only vote canonical, CHN-4)")]
|
||||
AdvisorMayNotVote,
|
||||
|
||||
/// Insufficient stake to submit an OIP proposal. CHN-4 requires ≥1,000 OGC.
|
||||
#[error("insufficient proposal stake: need ≥1,000 OGC, got {got_base_units} base units")]
|
||||
InsufficientProposalStake {
|
||||
/// Stake offered, in base units.
|
||||
got_base_units: u128,
|
||||
},
|
||||
|
||||
/// The proposal id is unknown to the chain.
|
||||
#[error("unknown proposal id")]
|
||||
UnknownProposal,
|
||||
|
||||
/// The voting period for this proposal has closed.
|
||||
#[error("voting period closed")]
|
||||
VotingClosed,
|
||||
|
||||
/// Bridge destination is not yet enabled in this runtime.
|
||||
#[error("bridge destination not enabled in this runtime")]
|
||||
BridgeDisabled,
|
||||
|
||||
/// Catch-all for runtime-specific errors not covered above. Use sparingly.
|
||||
#[error("chain error: {0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn insufficient_balance_displays_canonical() {
|
||||
let err = ChainError::InsufficientBalance {
|
||||
have: 100,
|
||||
need: 500,
|
||||
};
|
||||
let msg = format!("{}", err);
|
||||
assert!(msg.contains("100"));
|
||||
assert!(msg.contains("500"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn advisor_error_cites_chn_4() {
|
||||
let msg = format!("{}", ChainError::AdvisorMayNotVote);
|
||||
assert!(msg.contains("CHN-4"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asset_already_attested_cites_inmutable_26() {
|
||||
let msg = format!("{}", ChainError::AssetAlreadyAttested);
|
||||
assert!(msg.contains("Inmutable #26"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
//! Fee calculation trait — multi-tier fee economy canonical per CHN-8.
|
||||
//!
|
||||
//! Formula: `fee = base_ogc × hardware_multiplier × (1 + quality_surcharge)`
|
||||
//!
|
||||
//! Implementation lives in `origin-coin-tokenomics` (Phase 2.4). This trait is
|
||||
//! published Day 1 so Marihan (Marketplace), Larihana (Seeds Hosting), and
|
||||
//! Phanes (Trinity asset minting) can compute fees before the runtime lands.
|
||||
|
||||
use crate::{AssetQualityTier, HardwareTier, Ogc};
|
||||
|
||||
/// Classification of an on-chain operation for fee purposes. Different operations
|
||||
/// have different base fees and different tier sensitivity. The runtime maps each
|
||||
/// extrinsic to one of these kinds.
|
||||
#[derive(
|
||||
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub enum OperationKind {
|
||||
/// Plain OGC transfer between accounts. Lowest base fee.
|
||||
Transfer,
|
||||
/// Mint an asset NFT (attestation + provenance write).
|
||||
AssetMint,
|
||||
/// Sale through the canonical marketplace (91/9% split via ink!).
|
||||
MarketplaceSale,
|
||||
/// Recurring Seeds Hosting payment (0.6 OGC/world/month canonical).
|
||||
SeedsHosting,
|
||||
/// ONS name registration / renewal (Pillar 6).
|
||||
OnsRegistration,
|
||||
/// Smart contract deployment (ink! WASM).
|
||||
ContractDeploy,
|
||||
/// Smart contract method call (ink! WASM).
|
||||
ContractCall,
|
||||
/// Governance proposal submission (Pallet Democracy).
|
||||
GovernanceProposal,
|
||||
}
|
||||
|
||||
/// Canonical fee calculator. Implementations MUST honor:
|
||||
///
|
||||
/// - **Inmutable #16** — fee scales inversely with hardware tier (anti-clasista).
|
||||
/// - **Inmutable #37** — wallet creation + base validation never charged.
|
||||
/// - **CHN-8** — formula `fee = base × hw_mult × quality_mult`.
|
||||
pub trait FeeCalculator {
|
||||
/// Compute the OGC fee for an operation, given the actor's hardware tier and
|
||||
/// the quality tier of the asset (where applicable).
|
||||
fn fee_for_transaction(
|
||||
&self,
|
||||
op: OperationKind,
|
||||
hw: HardwareTier,
|
||||
quality: AssetQualityTier,
|
||||
) -> Ogc;
|
||||
}
|
||||
|
||||
/// Canonical fee formula helper. Pure function — no I/O, no allocation. Useful
|
||||
/// for runtime + tests + off-chain estimation by clients.
|
||||
///
|
||||
/// Returns `base × (hw_mult / 100) × ((100 + surcharge) / 100)` in base units.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use origin_chain_types::{Ogc, HardwareTier, AssetQualityTier};
|
||||
/// use origin_chain_types::fees::compute_fee;
|
||||
///
|
||||
/// // 1 OGC base, Desktop, Standard quality -> exactly 1 OGC.
|
||||
/// let fee = compute_fee(Ogc::from_whole(1), HardwareTier::Desktop, AssetQualityTier::Standard);
|
||||
/// assert_eq!(fee.as_base_units(), Ogc::ONE);
|
||||
///
|
||||
/// // 1 OGC base, EmbeddedArm, Standard -> 0.1 OGC (anti-clasista).
|
||||
/// let fee = compute_fee(Ogc::from_whole(1), HardwareTier::EmbeddedArm, AssetQualityTier::Standard);
|
||||
/// assert_eq!(fee.as_base_units(), Ogc::ONE / 10);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn compute_fee(base: Ogc, hw: HardwareTier, quality: AssetQualityTier) -> Ogc {
|
||||
let base_units = base.as_base_units();
|
||||
let hw_x100 = hw.base_multiplier_x100() as u128;
|
||||
let surcharge_x100 = quality.surcharge_x100(hw) as u128;
|
||||
// After hw multiplier:
|
||||
let after_hw = base_units.saturating_mul(hw_x100) / 100;
|
||||
// After Hero surcharge (additive ON TOP of base, per CHN-8 "+0.5×"):
|
||||
let after_quality = after_hw.saturating_add(after_hw.saturating_mul(surcharge_x100) / 100);
|
||||
Ogc::from_base_units(after_quality)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn desktop_standard_is_base_fee() {
|
||||
let fee = compute_fee(
|
||||
Ogc::from_whole(10),
|
||||
HardwareTier::Desktop,
|
||||
AssetQualityTier::Standard,
|
||||
);
|
||||
assert_eq!(fee.as_base_units(), Ogc::from_whole(10).as_base_units());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn embedded_arm_pays_one_tenth_anti_clasista() {
|
||||
let fee = compute_fee(
|
||||
Ogc::from_whole(10),
|
||||
HardwareTier::EmbeddedArm,
|
||||
AssetQualityTier::Standard,
|
||||
);
|
||||
// 10 OGC × 0.1 = 1 OGC
|
||||
assert_eq!(fee.as_base_units(), Ogc::from_whole(1).as_base_units());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn portable_pays_three_tenths() {
|
||||
let fee = compute_fee(
|
||||
Ogc::from_whole(10),
|
||||
HardwareTier::Portable,
|
||||
AssetQualityTier::Standard,
|
||||
);
|
||||
// 10 × 0.3 = 3 OGC
|
||||
assert_eq!(fee.as_base_units(), Ogc::from_whole(3).as_base_units());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ultra_pays_seven_tenths_discount() {
|
||||
let fee = compute_fee(
|
||||
Ogc::from_whole(10),
|
||||
HardwareTier::Ultra,
|
||||
AssetQualityTier::Standard,
|
||||
);
|
||||
// 10 × 0.7 = 7 OGC
|
||||
assert_eq!(fee.as_base_units(), Ogc::from_whole(7).as_base_units());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn desktop_hero_pays_one_point_five_times_base() {
|
||||
let fee = compute_fee(
|
||||
Ogc::from_whole(10),
|
||||
HardwareTier::Desktop,
|
||||
AssetQualityTier::Hero,
|
||||
);
|
||||
// 10 × 1.0 × (1 + 0.5) = 15 OGC
|
||||
assert_eq!(fee.as_base_units(), Ogc::from_whole(15).as_base_units());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ultra_hero_pays_zero_point_seven_times_one_point_three() {
|
||||
let fee = compute_fee(
|
||||
Ogc::from_whole(100),
|
||||
HardwareTier::Ultra,
|
||||
AssetQualityTier::Hero,
|
||||
);
|
||||
// 100 × 0.7 × 1.3 = 91 OGC
|
||||
assert_eq!(fee.as_base_units(), Ogc::from_whole(91).as_base_units());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_base_always_zero_fee() {
|
||||
let fee = compute_fee(Ogc::ZERO, HardwareTier::Ultra, AssetQualityTier::Hero);
|
||||
assert_eq!(fee, Ogc::ZERO);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ultra_cheaper_than_desktop_canonical() {
|
||||
// Ultra discount means Ultra users pay LESS than Desktop for same asset.
|
||||
let desktop = compute_fee(
|
||||
Ogc::from_whole(100),
|
||||
HardwareTier::Desktop,
|
||||
AssetQualityTier::Standard,
|
||||
);
|
||||
let ultra = compute_fee(
|
||||
Ogc::from_whole(100),
|
||||
HardwareTier::Ultra,
|
||||
AssetQualityTier::Standard,
|
||||
);
|
||||
assert!(ultra < desktop);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
//! Governance trait — Foundation DAO proposals + voting per CHN-4.
|
||||
//!
|
||||
//! AI-Driven Human-Guarded: 5 AI governance agents (OIP Analyst, Treasury Guardian,
|
||||
//! Education Impact Scorer, Network Health Monitor, Compliance Sentinel) ADVISE
|
||||
//! only. Humans-only vote canonical, non-negotiable. AI agent accounts are
|
||||
//! flagged `governance_advisor` and the runtime programmatically rejects any
|
||||
//! vote extrinsic from them.
|
||||
|
||||
use crate::{AccountId, ChainError};
|
||||
|
||||
/// Opaque proposal identifier. 32 bytes to fit a content-addressed proposal hash.
|
||||
#[derive(
|
||||
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub struct ProposalId(pub [u8; 32]);
|
||||
|
||||
/// Stance taken by a voter on a proposal.
|
||||
#[derive(
|
||||
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub enum VoteStance {
|
||||
/// In favor of the proposal.
|
||||
Aye,
|
||||
/// Against the proposal.
|
||||
Nay,
|
||||
/// Abstain — counted toward quorum but not toward tally.
|
||||
Abstain,
|
||||
}
|
||||
|
||||
/// A single cast vote, stake-weighted at the time of the extrinsic.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Vote {
|
||||
/// Wallet that cast the vote. MUST NOT be flagged `governance_advisor`.
|
||||
pub voter: AccountId,
|
||||
/// Stance taken.
|
||||
pub stance: VoteStance,
|
||||
/// OGC stake weight at vote time (1 OGC = 1 vote, CHN-4).
|
||||
pub weight: u128,
|
||||
}
|
||||
|
||||
/// Canonical governance proposal trait. Consumed by Foundation governance
|
||||
/// frontend + AI advisor agents (read-only) + Pallet Democracy.
|
||||
pub trait GovernanceProposal {
|
||||
/// Submit an OIP (Origin Improvement Proposal). Requires ≥1,000 OGC stake
|
||||
/// per CHN-4. Returns the proposal id on success.
|
||||
fn submit_proposal(
|
||||
&self,
|
||||
proposer: &AccountId,
|
||||
title: &str,
|
||||
body_hash: [u8; 32],
|
||||
stake: crate::Ogc,
|
||||
) -> Result<ProposalId, ChainError>;
|
||||
|
||||
/// Cast a vote on an open proposal. Fails if proposer is a governance advisor
|
||||
/// (humans-only vote canonical, CHN-4), or if voting period is closed.
|
||||
fn vote(&self, proposal: ProposalId, vote: Vote) -> Result<(), ChainError>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn vote_stance_serde_canonical_strings() {
|
||||
assert_eq!(
|
||||
serde_json::to_string(&VoteStance::Aye).unwrap(),
|
||||
"\"Aye\""
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&VoteStance::Nay).unwrap(),
|
||||
"\"Nay\""
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&VoteStance::Abstain).unwrap(),
|
||||
"\"Abstain\""
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn proposal_id_size_canonical_32_bytes() {
|
||||
assert_eq!(core::mem::size_of::<ProposalId>(), 32);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
//! # origin-chain-types
|
||||
//!
|
||||
//! Canonical trait + type definitions for **Origin Chain** (Pilar 4 Origin Metaverse).
|
||||
//!
|
||||
//! This crate publishes the **Day 1 unblock interfaces** that all sibling crates
|
||||
//! across the ecosystem consume when interacting with the chain. It contains NO
|
||||
//! runtime, NO consensus, NO storage — only the trait surfaces and the canonical
|
||||
//! types that flow through them.
|
||||
//!
|
||||
//! Per Charon's Coordination Policy §5.1 (trait-first Day 1 unblock), this crate
|
||||
//! exists so that hermanos can compile against canonical interfaces immediately,
|
||||
//! without waiting for the full Substrate runtime to land.
|
||||
//!
|
||||
//! ## Canonical traits
|
||||
//!
|
||||
//! - [`ProvenanceLedger`] — asset attestation (Trinity / Codex / Ethos consumers)
|
||||
//! - [`OgcLedger`] — token balances + transfers (OMC marketplace + Treasury)
|
||||
//! - [`ChainBridge`] — cross-chain asset export (Trinity, Phase 7)
|
||||
//! - [`GovernanceProposal`] — DAO proposals + voting (Foundation)
|
||||
//! - [`FeeCalculator`] — multi-tier fee economy (Mathi HardwareTier consumer)
|
||||
//!
|
||||
//! ## Canonical types
|
||||
//!
|
||||
//! - [`Ogc`] — Origin Coin amount (newtype around u128, 18 decimals canonical)
|
||||
//! - [`AccountId`] — wallet identifier (Ed25519 public key, 32 bytes)
|
||||
//! - [`AssetHash`] — BLAKE3 32-byte content hash
|
||||
//! - [`HardwareTier`] — Mathi-canonical hardware tier ladder (mirror)
|
||||
//! - [`AssetQualityTier`] — Standard or Hero (Phanes/Mathi joint canonical)
|
||||
//! - [`Signature`] — Ed25519 signature, 64 bytes
|
||||
//!
|
||||
//! ## Inmutables enforced
|
||||
//!
|
||||
//! - **#16** HTS Ley I — fee matrix anti-clasista (see [`FeeCalculator`])
|
||||
//! - **#26** Content-Addressed Immutability — [`AssetHash`] BLAKE3 canonical
|
||||
//! - **#37** Origin FREE Tier Perpetua — wallet creation always free, validation earns OGC
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
pub mod accounts;
|
||||
pub mod assets;
|
||||
pub mod errors;
|
||||
pub mod fees;
|
||||
pub mod governance;
|
||||
pub mod ogc;
|
||||
pub mod provenance;
|
||||
pub mod tiers;
|
||||
|
||||
pub use accounts::{AccountId, Signature};
|
||||
pub use assets::AssetHash;
|
||||
pub use errors::ChainError;
|
||||
pub use fees::{FeeCalculator, OperationKind};
|
||||
pub use governance::{GovernanceProposal, ProposalId, Vote, VoteStance};
|
||||
pub use ogc::{Ogc, OgcLedger};
|
||||
pub use provenance::{AssetAttestation, ProvenanceLedger};
|
||||
pub use tiers::{AssetQualityTier, HardwareTier};
|
||||
|
||||
/// Bridge trait for cross-chain asset export. Export-first per CHN-5 — Origin
|
||||
/// assets attested locally then bridged outward (Polkadot, Ethereum L2). External
|
||||
/// NFTs do NOT auto-import.
|
||||
pub trait ChainBridge {
|
||||
/// Bridge an asset attestation to an external chain. Returns the foreign
|
||||
/// transaction id once the bridge confirms the message.
|
||||
fn bridge_asset(
|
||||
&self,
|
||||
asset: &AssetAttestation,
|
||||
destination: BridgeDestination,
|
||||
) -> Result<ForeignTxId, ChainError>;
|
||||
}
|
||||
|
||||
/// Supported bridge destinations per CHN-5 (export-first canonical).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum BridgeDestination {
|
||||
/// Polkadot relay chain via Agile Coretime + XCM.
|
||||
Polkadot,
|
||||
/// Ethereum L2 (Arbitrum) via Snowbridge or Wormhole. Phase 3+.
|
||||
EthereumL2,
|
||||
/// KILT Protocol for academic identity attestation.
|
||||
Kilt,
|
||||
}
|
||||
|
||||
/// Opaque foreign transaction identifier returned by a bridge. Encoded as a
|
||||
/// 32-byte fixed buffer to fit cross-chain hash formats without heap.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ForeignTxId(pub [u8; 32]);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn bridge_destination_serde_roundtrip() {
|
||||
let dest = BridgeDestination::Polkadot;
|
||||
let json = serde_json::to_string(&dest).unwrap();
|
||||
assert_eq!(json, "\"Polkadot\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn foreign_tx_id_zero_canonical() {
|
||||
let id = ForeignTxId([0u8; 32]);
|
||||
assert_eq!(id.0.len(), 32);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
//! Origin Coin ($OGC) — canonical token type + ledger trait.
|
||||
//!
|
||||
//! Per CHN-2 ratified canonical baseline: **1,000,000,000 OGC** hard cap at genesis.
|
||||
//! 18 decimals canonical (`1 OGC = 10^18 base units`). Allocation per BM V2:
|
||||
//! 40% Ecosystem, 25% Foundation Treasury, 15% Public Sale, 15% Team (4yr vesting),
|
||||
//! 5% Exchange Liquidity. Annual inflation 4-5% via validator rewards.
|
||||
|
||||
use crate::{AccountId, ChainError};
|
||||
|
||||
/// Canonical amount of Origin Coin in base units.
|
||||
///
|
||||
/// 1 OGC = 10^18 base units. `u128` chosen so 1B OGC fits (`1e9 × 1e18 = 1e27 < 2^128`).
|
||||
/// Anti-overflow by design: max representable amount > genesis supply × 1000.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use origin_chain_types::Ogc;
|
||||
/// let one_ogc = Ogc::from_whole(1);
|
||||
/// assert_eq!(one_ogc.as_base_units(), 1_000_000_000_000_000_000);
|
||||
/// ```
|
||||
#[derive(
|
||||
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub struct Ogc(u128);
|
||||
|
||||
impl Ogc {
|
||||
/// Canonical decimal precision (1 OGC = 10^DECIMALS base units).
|
||||
pub const DECIMALS: u32 = 18;
|
||||
|
||||
/// 10^18 base units per whole OGC.
|
||||
pub const ONE: u128 = 1_000_000_000_000_000_000;
|
||||
|
||||
/// Hard supply cap per CHN-2 (1,000,000,000 OGC = 10^27 base units).
|
||||
pub const GENESIS_SUPPLY: Ogc = Ogc(1_000_000_000_u128 * Self::ONE);
|
||||
|
||||
/// Zero amount — useful for initialization + comparisons.
|
||||
pub const ZERO: Ogc = Ogc(0);
|
||||
|
||||
/// Construct from whole OGC tokens (e.g. `Ogc::from_whole(100)` = 100 OGC).
|
||||
#[inline]
|
||||
pub const fn from_whole(whole: u64) -> Self {
|
||||
Ogc((whole as u128) * Self::ONE)
|
||||
}
|
||||
|
||||
/// Construct from raw base units (e.g. `Ogc::from_base_units(1_000_000_000_000_000_000)` = 1 OGC).
|
||||
#[inline]
|
||||
pub const fn from_base_units(units: u128) -> Self {
|
||||
Ogc(units)
|
||||
}
|
||||
|
||||
/// Read as raw base units.
|
||||
#[inline]
|
||||
pub const fn as_base_units(self) -> u128 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Checked addition — returns `None` on overflow. Use this for all arithmetic
|
||||
/// crossing supply or balance boundaries.
|
||||
#[inline]
|
||||
pub const fn checked_add(self, other: Ogc) -> Option<Ogc> {
|
||||
match self.0.checked_add(other.0) {
|
||||
Some(x) => Some(Ogc(x)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checked subtraction — returns `None` on underflow.
|
||||
#[inline]
|
||||
pub const fn checked_sub(self, other: Ogc) -> Option<Ogc> {
|
||||
match self.0.checked_sub(other.0) {
|
||||
Some(x) => Some(Ogc(x)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Canonical OGC ledger trait. Consumed by OMC marketplace, Foundation Treasury,
|
||||
/// validator reward distribution, and all extrinsics that move balances.
|
||||
///
|
||||
/// Implementation lives in `origin-coin-tokenomics` (Phase 1). This trait surface
|
||||
/// is published Day 1 so Marihan, Sussi, and other hermanos can build against it.
|
||||
pub trait OgcLedger {
|
||||
/// Return the free balance of `who`.
|
||||
fn balance(&self, who: &AccountId) -> Ogc;
|
||||
|
||||
/// Transfer `amount` from `from` to `to`. Implementations MUST burn gas fees
|
||||
/// per CHN-8 (100% gas burned) and emit provenance events that Palace KG and
|
||||
/// the chain block explorer can observe.
|
||||
fn transfer(&self, from: &AccountId, to: &AccountId, amount: Ogc) -> Result<(), ChainError>;
|
||||
|
||||
/// Total issued supply at the current block. SHOULD equal genesis + cumulative
|
||||
/// validator rewards - cumulative burns.
|
||||
fn total_supply(&self) -> Ogc;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn one_ogc_is_ten_to_eighteen() {
|
||||
assert_eq!(Ogc::from_whole(1).as_base_units(), Ogc::ONE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn genesis_supply_is_one_billion_canonical() {
|
||||
assert_eq!(
|
||||
Ogc::GENESIS_SUPPLY.as_base_units(),
|
||||
1_000_000_000_u128 * Ogc::ONE
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn genesis_supply_fits_in_u128_with_safety_margin() {
|
||||
// Verify 1B OGC has >100x safety margin from u128::MAX (anti-overflow).
|
||||
let margin = u128::MAX / Ogc::GENESIS_SUPPLY.as_base_units();
|
||||
assert!(margin > 100, "safety margin should exceed 100x");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_add_overflow_returns_none() {
|
||||
let max = Ogc::from_base_units(u128::MAX);
|
||||
let one = Ogc::from_base_units(1);
|
||||
assert!(max.checked_add(one).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_sub_underflow_returns_none() {
|
||||
let zero = Ogc::ZERO;
|
||||
let one = Ogc::from_base_units(1);
|
||||
assert!(zero.checked_sub(one).is_none());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
//! Provenance ledger trait — asset attestation canonical per CHN-7.
|
||||
//!
|
||||
//! Every Origin asset (Trinity 3D mesh, Codex .odoc, Ethos credential) gets a
|
||||
//! cryptographic on-chain attestation: BLAKE3 content hash + Ed25519 creator
|
||||
//! signature + optional agent_chain (AGI co-authors). Once minted, immutable
|
||||
//! (Inmutable #26).
|
||||
//!
|
||||
//! Charon validates the signature, anchors the attestation in a Merkle root, and
|
||||
//! emits an event that Palace KG observes for cross-substrate provenance.
|
||||
|
||||
use crate::{AccountId, AssetHash, ChainError, Signature};
|
||||
|
||||
/// Canonical attestation record stored on-chain. Compact (~100 bytes) so it fits
|
||||
/// in a single extrinsic without bloating block size.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct AssetAttestation {
|
||||
/// BLAKE3 32-byte content hash of the asset bytes (off-chain).
|
||||
pub hash: AssetHash,
|
||||
/// Wallet that signed the attestation — typically the asset creator.
|
||||
pub creator: AccountId,
|
||||
/// Ed25519 signature over `hash` by `creator`.
|
||||
pub signature: Signature,
|
||||
/// Block number at which the attestation was committed. Set by the runtime.
|
||||
pub anchored_at_block: u64,
|
||||
/// Optional AGI co-author chain. Empty for purely human-authored assets;
|
||||
/// populated for Synthesizer / Forge / Skill outputs that include AGI input
|
||||
/// (per Inmutable #41 cross-species co-authorship).
|
||||
pub agent_chain: Vec<AccountId>,
|
||||
}
|
||||
|
||||
/// Canonical provenance ledger. Consumed by Trinity (Phanes), Codex,
|
||||
/// Ethos (Sussi), and any sibling that needs cryptographic asset provenance.
|
||||
///
|
||||
/// Implementation lives in `origin-chain-provenance` (Phase 2.1). Stub trait
|
||||
/// published Day 1.
|
||||
pub trait ProvenanceLedger {
|
||||
/// Attest an asset: verify the signature, append the attestation, emit event.
|
||||
/// Caller MUST have passed Noesis validation upstream (Ergon's responsibility);
|
||||
/// this trait does not re-validate semantic content, only cryptography.
|
||||
fn attest_asset(&self, attestation: AssetAttestation) -> Result<(), ChainError>;
|
||||
|
||||
/// Verify an asset's provenance: return the recorded attestation if present.
|
||||
/// Returns `None` if the hash has never been attested on this chain.
|
||||
fn verify_provenance(&self, hash: &AssetHash) -> Option<AssetAttestation>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn attestation_roundtrip_serde() {
|
||||
let att = AssetAttestation {
|
||||
hash: AssetHash::new([1u8; 32]),
|
||||
creator: AccountId::new([2u8; 32]),
|
||||
signature: Signature::new([3u8; 64]),
|
||||
anchored_at_block: 42,
|
||||
agent_chain: vec![],
|
||||
};
|
||||
let json = serde_json::to_string(&att).unwrap();
|
||||
let parsed: AssetAttestation = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(att, parsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attestation_supports_agi_co_authorship() {
|
||||
// Inmutable #41 — agent_chain may carry AGI co-authors.
|
||||
let agi1 = AccountId::new([10u8; 32]);
|
||||
let agi2 = AccountId::new([11u8; 32]);
|
||||
let att = AssetAttestation {
|
||||
hash: AssetHash::new([1u8; 32]),
|
||||
creator: AccountId::new([2u8; 32]),
|
||||
signature: Signature::new([3u8; 64]),
|
||||
anchored_at_block: 0,
|
||||
agent_chain: vec![agi1, agi2],
|
||||
};
|
||||
assert_eq!(att.agent_chain.len(), 2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
//! Hardware + asset quality tiers — Mathi-canonical mirror.
|
||||
//!
|
||||
//! [`HardwareTier`] is a mirror of `arkhe-core::HardwareTier` (Mathi sovereign,
|
||||
//! per ADR-0057 trait-seam discipline). This crate does NOT redefine the ladder;
|
||||
//! it re-publishes the canonical enum so chain code can consume tier metadata
|
||||
//! without taking a dependency on `arkhe-core` (which itself pulls in render +
|
||||
//! AOI deps).
|
||||
//!
|
||||
//! Whenever Mathi publishes a new tier in `arkhe-core`, this enum MUST be updated
|
||||
//! in lockstep (regression test in `origin-coin-tokenomics` will fail until then).
|
||||
|
||||
/// Canonical hardware tier ladder — Mathi sovereign (mirror).
|
||||
///
|
||||
/// Per Inmutable #16, fees scale inversely with tier so global access is preserved:
|
||||
/// a Pi 5 user in Sub-Saharan Africa pays 0.1x what a Vision Pro user pays for the
|
||||
/// same operation.
|
||||
#[derive(
|
||||
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub enum HardwareTier {
|
||||
/// Raspberry Pi 5, mobile phones, low-end ARM — multiplier 0.1×. Hero gated out.
|
||||
EmbeddedArm,
|
||||
/// Laptops, mid-tier tablets — multiplier 0.3×. Hero gated out.
|
||||
Portable,
|
||||
/// Desktops, workstations — multiplier 1.0× (base). Hero +0.5×.
|
||||
Desktop,
|
||||
/// Vision Pro, high-end workstations — multiplier 0.7× (discount). Hero +0.3×.
|
||||
Ultra,
|
||||
}
|
||||
|
||||
impl HardwareTier {
|
||||
/// Base fee multiplier scaled by 100 (avoids float).
|
||||
/// `EmbeddedArm` = 10 means 0.10× the base fee, `Desktop` = 100 means 1.00×.
|
||||
#[inline]
|
||||
pub const fn base_multiplier_x100(self) -> u32 {
|
||||
match self {
|
||||
HardwareTier::EmbeddedArm => 10,
|
||||
HardwareTier::Portable => 30,
|
||||
HardwareTier::Desktop => 100,
|
||||
HardwareTier::Ultra => 70,
|
||||
}
|
||||
}
|
||||
|
||||
/// True if this tier can transact `AssetQualityTier::Hero` assets. EmbeddedArm
|
||||
/// and Portable are gated out (Hero requires more VRAM than these tiers ship).
|
||||
#[inline]
|
||||
pub const fn supports_hero(self) -> bool {
|
||||
matches!(self, HardwareTier::Desktop | HardwareTier::Ultra)
|
||||
}
|
||||
}
|
||||
|
||||
/// Asset quality tier — Phanes + Mathi joint canonical (TLB Tier-LOD-Budget V1).
|
||||
#[derive(
|
||||
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub enum AssetQualityTier {
|
||||
/// Standard quality — available to all hardware tiers. No surcharge.
|
||||
Standard,
|
||||
/// Hero quality — high-fidelity, gated to Desktop + Ultra. Charges surcharge.
|
||||
Hero,
|
||||
}
|
||||
|
||||
impl AssetQualityTier {
|
||||
/// Surcharge scaled by 100 added to the base fee for this tier on a given
|
||||
/// hardware tier. Returns `0` for Standard or for tiers that gate out Hero.
|
||||
#[inline]
|
||||
pub const fn surcharge_x100(self, hw: HardwareTier) -> u32 {
|
||||
match (self, hw) {
|
||||
(AssetQualityTier::Standard, _) => 0,
|
||||
(AssetQualityTier::Hero, HardwareTier::Desktop) => 50,
|
||||
(AssetQualityTier::Hero, HardwareTier::Ultra) => 30,
|
||||
// Hero gated out for EmbeddedArm + Portable — surcharge 0 because the
|
||||
// operation should be rejected upstream by the runtime, not charged.
|
||||
(AssetQualityTier::Hero, HardwareTier::EmbeddedArm) => 0,
|
||||
(AssetQualityTier::Hero, HardwareTier::Portable) => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn multipliers_match_chn_8_canonical() {
|
||||
assert_eq!(HardwareTier::EmbeddedArm.base_multiplier_x100(), 10);
|
||||
assert_eq!(HardwareTier::Portable.base_multiplier_x100(), 30);
|
||||
assert_eq!(HardwareTier::Desktop.base_multiplier_x100(), 100);
|
||||
assert_eq!(HardwareTier::Ultra.base_multiplier_x100(), 70);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hero_gating_canonical() {
|
||||
assert!(!HardwareTier::EmbeddedArm.supports_hero());
|
||||
assert!(!HardwareTier::Portable.supports_hero());
|
||||
assert!(HardwareTier::Desktop.supports_hero());
|
||||
assert!(HardwareTier::Ultra.supports_hero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hero_surcharge_canonical_chn_8() {
|
||||
assert_eq!(
|
||||
AssetQualityTier::Hero.surcharge_x100(HardwareTier::Desktop),
|
||||
50
|
||||
);
|
||||
assert_eq!(
|
||||
AssetQualityTier::Hero.surcharge_x100(HardwareTier::Ultra),
|
||||
30
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn standard_surcharge_always_zero() {
|
||||
for hw in [
|
||||
HardwareTier::EmbeddedArm,
|
||||
HardwareTier::Portable,
|
||||
HardwareTier::Desktop,
|
||||
HardwareTier::Ultra,
|
||||
] {
|
||||
assert_eq!(AssetQualityTier::Standard.surcharge_x100(hw), 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hero_surcharge_zero_for_gated_tiers() {
|
||||
// Hero on EmbeddedArm/Portable returns 0 because rejection is upstream.
|
||||
assert_eq!(
|
||||
AssetQualityTier::Hero.surcharge_x100(HardwareTier::EmbeddedArm),
|
||||
0
|
||||
);
|
||||
assert_eq!(
|
||||
AssetQualityTier::Hero.surcharge_x100(HardwareTier::Portable),
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "origin-coin-tokenomics"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
description = "OGC token implementation + fee matrix + multi-source burns for Origin Chain. STUB v0.0.1 — implementation lands Phase 1.3 + 2.4 of Charon roadmap."
|
||||
|
||||
[dependencies]
|
||||
origin-chain-types.workspace = true
|
||||
@@ -0,0 +1,62 @@
|
||||
//! # origin-coin-tokenomics
|
||||
//!
|
||||
//! $OGC token implementation per **CHN-2** + multi-tier fee matrix per **CHN-8** +
|
||||
//! multi-source burn enforcement per CHN-2/CHN-8. Stub crate in v0.0.1.
|
||||
//!
|
||||
//! ## Phase 1 milestones
|
||||
//!
|
||||
//! - 1.3 `pallet_balances` con OGC token (1B supply genesis)
|
||||
//! - 1.7 Genesis config: allocation 40/25/15/15/5 per BM V2
|
||||
//!
|
||||
//! ## Phase 2 milestones
|
||||
//!
|
||||
//! - 2.4 `fee_for_transaction(tier, quality) -> Ogc` implementation
|
||||
//! - 2.5 Fee matrix: EmbeddedArm 0.1×, Portable 0.3×, Desktop 1.0×, Ultra 0.7×
|
||||
//! - 2.6 Hero surcharge: Desktop +0.5×, Ultra +0.3×
|
||||
//!
|
||||
//! ## Canonical burn matrix
|
||||
//!
|
||||
//! Per CHN-2 + CHN-8 + Pillar 6/7 docs:
|
||||
//!
|
||||
//! | Source | Burn rate |
|
||||
//! |--------|-----------|
|
||||
//! | Gas fees | 100% |
|
||||
//! | Seeds Hosting payments | 20% |
|
||||
//! | Marketplace 9% Treasury share | 50% (2% del 9%) |
|
||||
//! | ONS registration | 50% |
|
||||
//! | OIT Oracle | 30% |
|
||||
//! | Escrow commissions | 20% |
|
||||
//! | Resonance slashing | 25% |
|
||||
//! | Ticket commissions | 30% |
|
||||
//! | Reputation stake slashing | 50% |
|
||||
//! | Ethos net revenue buy-back | 5% |
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
/// Placeholder marker for the tokenomics module. Replaced by concrete pallet in Phase 1.3.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct TokenomicsConfig;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use origin_chain_types::fees::compute_fee;
|
||||
use origin_chain_types::{AssetQualityTier, HardwareTier, Ogc};
|
||||
|
||||
// Sanity test: the trait crate's compute_fee returns canonical values.
|
||||
// This proves the workspace dependency wires up correctly.
|
||||
#[test]
|
||||
fn cross_crate_fee_calculation_canonical() {
|
||||
let fee = compute_fee(
|
||||
Ogc::from_whole(10),
|
||||
HardwareTier::EmbeddedArm,
|
||||
AssetQualityTier::Standard,
|
||||
);
|
||||
assert_eq!(fee.as_base_units(), Ogc::from_whole(1).as_base_units());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stub_crate_compiles() {
|
||||
let _cfg = TokenomicsConfig;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user