feat(provenance): Phase 2.1 PROVENANCE pallet scaffold v0.0.14 trait-first foundation
Phase 2.1 governance-first pattern (DC PROVENANCE_PALLET_V1 5/10 firmas, ratify target 2026-05-29). Scaffold skeleton compile-clean canonical mientras Ergon ships arkhe-noesis-attestation-types sub-crate Cap 42+ (target 2026-05-30→06-05). Deliverables canonical: - origin-chain-types/src/attestation.rs (NEW 250 LOC + 9 tests) — vendor copy temporal ValidationAttestation (interim Option B per Ergon decision Cap 41+) + OitIdentity + ValidationVerdict + SeverityTier + helper methods canonical (is_anchorable PROV-6, is_institutional PROV-5, signing_payload PROV-4 stub) - origin-chain-provenance/src/lib.rs (NEW 260 LOC + 2 scaffold tests) — #[frame_support::pallet] structural skeleton: storage + dispatchable + events + errors + 5 RejectionReason variants (incluyendo MalformedValidationAttestation per Marihan refinement Phase 2.2 anticipated) - 3 existing types canonical (AssetHash, AccountId, Signature, AssetAttestation): added Encode + Decode + DecodeWithMemTracking + MaxEncodedLen + TypeInfo derives - workspace parity-scale-codec: max-encoded-len feature canonical added Sub-decisions canonical implementadas (scaffold-level): - PROV-2 Storage: StorageMap<AssetHash, AssetAttestation> Blake2_128Concat - PROV-3 Dispatchable: attest_asset(origin, ValidationAttestation) flow básico - PROV-5 OIT Level >= 3 enforce: via ValidationAttestation.validator_oit.is_institutional() - PROV-6 is_anchorable check: verdict Passed/Warning + severity NOT Block/Critical - PROV-7 Events: AssetAttested + AttestationRejected Lo que NO funciona aún (deferred future slices): - PROV-4 Ed25519 verification real (placeholder, requires Ergon sub-crate) - Pallet wire en runtime canonical (v0.0.15 post DC ratify) - Runtime API verify_provenance PROV-8 (v0.0.17) - BoundedVec<AccountId, ConstU32<10>> agent_chain (v0.0.15) - #[pallet::without_storage_info] scaffold-level (remove post BoundedVec) Verification end-to-end: - cargo check workspace verde 1.77s sin regression - pallet-origin-provenance: 2/2 scaffold tests pass - origin-chain-types: 45 tests pass (36 existing + 9 nuevos attestation) - Slice 3 mock-WASM chain_spec builder operativo (NO regression) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
274ddf3957
commit
4359515f45
11 files changed
+640
-29
No files matched your search
@@ -7,9 +7,38 @@ 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."
|
||||
description = "Asset attestation pallet canonical per CHN-7. Phase 2.1 scaffold v0.0.14 — trait-first foundation pattern. Real impl post Ergon arkhe-noesis-attestation-types Cap 42+ sub-crate."
|
||||
|
||||
[dependencies]
|
||||
origin-chain-types.workspace = true
|
||||
blake3.workspace = true
|
||||
ed25519-dalek.workspace = true
|
||||
# Origin Chain canonical types (vendor ValidationAttestation interim Option B)
|
||||
origin-chain-types = { workspace = true }
|
||||
|
||||
# FRAME canonical pallet macro + storage + dispatch + events + errors
|
||||
frame-support = { workspace = true }
|
||||
frame-system = { workspace = true }
|
||||
|
||||
# Crypto canonical
|
||||
blake3 = { workspace = true }
|
||||
ed25519-dalek = { workspace = true }
|
||||
|
||||
# SCALE codec canonical
|
||||
parity-scale-codec = { workspace = true }
|
||||
scale-info = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"origin-chain-types/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"parity-scale-codec/std",
|
||||
"scale-info/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"frame-support/try-runtime",
|
||||
"frame-system/try-runtime",
|
||||
]
|
||||
@@ -1,38 +1,269 @@
|
||||
//! # origin-chain-provenance
|
||||
//! # pallet-origin-provenance — Phase 2.1 scaffold v0.0.14
|
||||
//!
|
||||
//! `ProvenanceLedger` implementation: asset attestation via BLAKE3 + Ed25519,
|
||||
//! Merkle-anchored per block. CHN-7 canonical. Stub crate in v0.0.1.
|
||||
//! Asset attestation pallet canonical per CHN-7 + DC PROVENANCE_PALLET_V1
|
||||
//! (5/10 firmas hoy 2026-05-24, deadline ratificación 2026-05-29).
|
||||
//!
|
||||
//! ## Phase 2 milestones
|
||||
//! ## Status canonical: SCAFFOLD v0.0.14 (trait-first foundation pattern)
|
||||
//!
|
||||
//! - 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
|
||||
//! Este crate provee la **structural skeleton** del pallet:
|
||||
//! - Storage layout canonical `StorageMap<AssetHash, AssetAttestation>` (PROV-2)
|
||||
//! - Dispatchable canonical `attest_asset(origin, ValidationAttestation)` (PROV-3)
|
||||
//! - Events canonical `AssetAttested` + `AttestationRejected` (PROV-7)
|
||||
//! - Errors canonical (PROV-4/5/6)
|
||||
//! - Pallet Config trait canonical
|
||||
//!
|
||||
//! ## Consumers (Day 1 unblocked via trait)
|
||||
//! ## Lo que NO funciona aún (real impl post-Ergon sub-crate Cap 42+)
|
||||
//!
|
||||
//! - 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
|
||||
//! - Ed25519 verification real canonical (PROV-4) — `todo!()` placeholder
|
||||
//! - OIT Level >= 3 enforcement canonical (PROV-5) — preparado pero no live
|
||||
//! - is_anchorable check canonical (PROV-6) — preparado pero no live
|
||||
//! - Real `ValidationAttestation` consumption — usa vendor copy interim Option B
|
||||
//!
|
||||
//! Per Ergon decision (diary `3f773a03` 2026-05-24): él ships
|
||||
//! `arkhe-noesis-attestation-types` standalone canonical Cap 42+ (target
|
||||
//! 2026-05-30 → 06-05). Mientras tanto, mi vendor copy en
|
||||
//! `origin-chain-types::attestation` permite avanzar canonical compile-only.
|
||||
//!
|
||||
//! ## Roadmap canonical post-ratificación DC PROVENANCE_PALLET_V1
|
||||
//!
|
||||
//! 1. **v0.0.14 (ESTA)**: Scaffold skeleton canonical compile-only
|
||||
//! 2. **v0.0.15** (post 2026-05-29 DC ratify): Storage + events + errors wired
|
||||
//! 3. **v0.0.16** (post 2026-06-05 Ergon sub-crate): Real Ed25519 + OIT verify
|
||||
//! 4. **v0.0.17**: Runtime API canonical verify_provenance + wire en runtime
|
||||
//! 5. **v0.1.0** (post Q3 2026 testnet): full integration end-to-end
|
||||
//!
|
||||
//! ## Cross-pillar consumers canonical desbloqueados (post-impl)
|
||||
//!
|
||||
//! - Trinity (Phanes) asset mesh BLAKE3 → `attest_asset` canonical
|
||||
//! - Codex (.odoc) chain_attestation block populated by Vault tier
|
||||
//! - Ethos (Sussi) credential NFT mint + chain attestation
|
||||
//! - Atelier (Marihan) skill NFT mint via institutional wallet
|
||||
//! - Origindocs (David ratificado 2026-05-24): academic IP defensible
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
/// Placeholder marker for the provenance module. Replaced by concrete pallet in Phase 2.1.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct ProvenanceConfig;
|
||||
pub use pallet::*;
|
||||
|
||||
/// Re-export canonical types desde origin-chain-types para consumers.
|
||||
pub use origin_chain_types::{
|
||||
AssetAttestation, AssetHash, OitIdentity, SeverityTier, ValidationAttestation,
|
||||
ValidationVerdict,
|
||||
};
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
use origin_chain_types::{AssetAttestation, AssetHash, ValidationAttestation};
|
||||
|
||||
/// Pallet configuration trait canonical.
|
||||
///
|
||||
/// Per DC PROVENANCE_PALLET_V1 PROV-3 + ADR-0057 trait-seam discipline.
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
/// Runtime event type canonical wired into `RuntimeEvent` aggregate.
|
||||
type RuntimeEvent: From<Event<Self>>
|
||||
+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
|
||||
// Future canonical (post OCAFB ratify):
|
||||
// type OitFeeSchedule: arkhe_amis_server::OitFeeSchedule;
|
||||
}
|
||||
|
||||
/// Pallet definition canonical.
|
||||
///
|
||||
/// `without_storage_info` canonical (scaffold v0.0.14): bypass MaxEncodedLen
|
||||
/// requirement para `AssetAttestation` (que tiene Vec<AccountId> agent_chain
|
||||
/// sin bound). Post v0.0.15: replace con BoundedVec<AccountId, ConstU32<10>>
|
||||
/// + remove without_storage_info canonical.
|
||||
#[pallet::pallet]
|
||||
#[pallet::without_storage_info]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
/// PROV-2 canonical: `StorageMap<AssetHash, AssetAttestation>` canonical.
|
||||
///
|
||||
/// O(1) lookup canonical via Blake2_128Concat hasher (FRAME-native canonical).
|
||||
/// Memory canonical ~140 bytes per attestation canonical.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn attestation_of)]
|
||||
pub type Attestations<T: Config> =
|
||||
StorageMap<_, Blake2_128Concat, AssetHash, AssetAttestation, OptionQuery>;
|
||||
|
||||
/// PROV-7 canonical: Events emisión canonical.
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
/// Asset attestation canonical anchored on-chain (PROV-7).
|
||||
AssetAttested {
|
||||
/// BLAKE3 content hash canonical del asset attested.
|
||||
asset_hash: AssetHash,
|
||||
/// Block number canonical at which attestation landed.
|
||||
block_number: BlockNumberFor<T>,
|
||||
},
|
||||
/// Attestation rejected canonical con reason canonical (PROV-7).
|
||||
AttestationRejected {
|
||||
/// BLAKE3 content hash canonical que falló validation.
|
||||
asset_hash: AssetHash,
|
||||
/// Reason canonical de rejection per RejectionReason enum.
|
||||
reason: RejectionReason,
|
||||
},
|
||||
}
|
||||
|
||||
/// Rejection reasons canonical PROV-7.
|
||||
///
|
||||
/// **Marihan refinement Phase 2.2 noted** (DC PROVENANCE_PALLET_V1 signature
|
||||
/// `2e6b8469`): añadir variant `MalformedValidationAttestation` para casos
|
||||
/// donde `signing_payload()` falle parse pre-Ed25519-verify. Non-blocking
|
||||
/// Phase 2.1 — incluido aquí canonical anticipated.
|
||||
#[derive(
|
||||
Encode,
|
||||
Decode,
|
||||
DecodeWithMemTracking,
|
||||
MaxEncodedLen,
|
||||
TypeInfo,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
RuntimeDebug,
|
||||
)]
|
||||
pub enum RejectionReason {
|
||||
/// Ed25519 signature verification canonical failed (PROV-4).
|
||||
InvalidSignature,
|
||||
/// Validator OIT level canonical < 3 (institutional required PROV-5).
|
||||
ValidatorTierInsufficient,
|
||||
/// Attestation NOT anchorable canonical per is_anchorable check (PROV-6).
|
||||
NotAnchorable,
|
||||
/// Asset hash canonical already attested previamente (idempotency canonical).
|
||||
AlreadyAttested,
|
||||
/// Validation attestation canonical malformed (Marihan refinement Phase 2.2).
|
||||
MalformedValidationAttestation,
|
||||
}
|
||||
|
||||
/// Pallet errors canonical (PROV-4/5/6).
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
/// PROV-4 canonical: Ed25519 signature verification canonical failed.
|
||||
InvalidSignature,
|
||||
/// PROV-5 canonical: validator OIT level canonical insufficient (< 3).
|
||||
ValidatorTierInsufficient,
|
||||
/// PROV-6 canonical: attestation NOT anchorable canonical (verdict/severity).
|
||||
NotAnchorable,
|
||||
/// PROV-2 canonical: asset hash canonical already attested previamente.
|
||||
AlreadyAttested,
|
||||
/// Marihan refinement Phase 2.2 noted canonical.
|
||||
MalformedValidationAttestation,
|
||||
}
|
||||
|
||||
/// Dispatchables canonical (PROV-3 canonical: attest_asset).
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// PROV-3 canonical: `attest_asset` extrinsic canonical.
|
||||
///
|
||||
/// Submits a `ValidationAttestation` (Ergon Slice 7.0r struct) para
|
||||
/// anchoring canonical on-chain. Flow canonical 6-step per DC PROV-3:
|
||||
///
|
||||
/// 1. PROV-4 canonical: Ed25519 signature verification canonical
|
||||
/// 2. PROV-5 canonical: validate `validator_oit.level >= 3` institutional
|
||||
/// 3. PROV-6 canonical: check `is_anchorable()` (verdict + severity)
|
||||
/// 4. PROV-2 canonical: convert ValidationAttestation → AssetAttestation
|
||||
/// 5. PROV-2 canonical: insert StorageMap canonical
|
||||
/// 6. PROV-7 canonical: emit AssetAttested event canonical
|
||||
///
|
||||
/// **SCAFFOLD v0.0.14 canonical**: real impl deferred post Ergon Cap 42+
|
||||
/// sub-crate ship + Ed25519 verification wire-up. Current canonical solo
|
||||
/// validates basic state (origin signed + not already attested) + emite
|
||||
/// placeholder event canonical.
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
pub fn attest_asset(
|
||||
origin: OriginFor<T>,
|
||||
validation_attestation: ValidationAttestation,
|
||||
) -> DispatchResult {
|
||||
// Verify origin canonical signed.
|
||||
let _signer = ensure_signed(origin)?;
|
||||
|
||||
let asset_hash = validation_attestation.asset_id;
|
||||
|
||||
// PROV-2 idempotency check canonical.
|
||||
ensure!(
|
||||
!Attestations::<T>::contains_key(asset_hash),
|
||||
Error::<T>::AlreadyAttested
|
||||
);
|
||||
|
||||
// PROV-5 OIT level >= 3 enforcement canonical.
|
||||
ensure!(
|
||||
validation_attestation.validator_oit.is_institutional(),
|
||||
Error::<T>::ValidatorTierInsufficient
|
||||
);
|
||||
|
||||
// PROV-6 is_anchorable check canonical.
|
||||
ensure!(
|
||||
validation_attestation.is_anchorable(),
|
||||
Error::<T>::NotAnchorable
|
||||
);
|
||||
|
||||
// PROV-4 Ed25519 verification canonical — SCAFFOLD placeholder.
|
||||
// Real impl post Ergon Cap 42+ sub-crate. Por ahora skip verify canonical
|
||||
// (any caller signed origin can attest — testing canonical only).
|
||||
// TODO Phase 2.1 v0.0.16: ed25519_dalek verify canonical aquí.
|
||||
|
||||
// PROV-2 canonical: convert + insert canonical.
|
||||
// SCAFFOLD: minimal AssetAttestation construction (without creator/sig
|
||||
// mapping canonical — that's Ergon sub-crate concern post Cap 42+).
|
||||
let attestation = AssetAttestation {
|
||||
hash: asset_hash,
|
||||
creator: origin_chain_types::AccountId::new([0u8; 32]), // SCAFFOLD placeholder
|
||||
signature: origin_chain_types::Signature::new(validation_attestation.signature),
|
||||
anchored_at_block: 0, // SCAFFOLD — block_number wire post v0.0.15
|
||||
agent_chain: alloc::vec![],
|
||||
};
|
||||
|
||||
Attestations::<T>::insert(asset_hash, attestation);
|
||||
|
||||
// PROV-7 emit event canonical.
|
||||
let block_number = <frame_system::Pallet<T>>::block_number();
|
||||
Self::deposit_event(Event::AssetAttested {
|
||||
asset_hash,
|
||||
block_number,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// Compile-time API surface verification canonical (scaffold-level).
|
||||
// Real functional tests post v0.0.15 + mock runtime + Ergon sub-crate land.
|
||||
|
||||
#[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;
|
||||
fn pallet_scaffold_compiles_canonical() {
|
||||
// Scaffold compiles — verified by cargo check.
|
||||
// Real tests require mock runtime canonical (Phase 2.1 v0.0.15+).
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejection_reason_variants_canonical() {
|
||||
// Verify enum variants canonical exist + Marihan refinement included.
|
||||
// Use std-only assertion via discriminant.
|
||||
let r1 = RejectionReason::InvalidSignature;
|
||||
let r2 = RejectionReason::ValidatorTierInsufficient;
|
||||
let _r3 = RejectionReason::NotAnchorable;
|
||||
let r4 = RejectionReason::AlreadyAttested;
|
||||
let r5 = RejectionReason::MalformedValidationAttestation;
|
||||
assert_ne!(
|
||||
core::mem::discriminant(&r1),
|
||||
core::mem::discriminant(&r2)
|
||||
);
|
||||
assert_ne!(
|
||||
core::mem::discriminant(&r4),
|
||||
core::mem::discriminant(&r5)
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user