diff --git a/Cargo.toml b/Cargo.toml index 297fb88..8f013d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 20 +# version: 21 [workspace] resolver = "3" members = ["crates/ksp-core-lib"] [workspace.package] -version = "0.1.1-pre.2.fix.1" +version = "0.1.1-pre.3" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-core-lib/Cargo.toml b/crates/ksp-core-lib/Cargo.toml index 6dba046..6210381 100644 --- a/crates/ksp-core-lib/Cargo.toml +++ b/crates/ksp-core-lib/Cargo.toml @@ -1,5 +1,5 @@ # file: crates/ksp-core-lib/Cargo.toml -# version: 1 +# version: 2 [package] name = "ksp-core-lib" @@ -7,5 +7,8 @@ version.workspace = true edition.workspace = true repository.workspace = true +[dependencies] +solana-pubkey = { version = "4.3.0", default-features = false } + [lints] workspace = true diff --git a/crates/ksp-core-lib/src/lib.rs b/crates/ksp-core-lib/src/lib.rs index 96acc07..f0ad1f2 100644 --- a/crates/ksp-core-lib/src/lib.rs +++ b/crates/ksp-core-lib/src/lib.rs @@ -1,5 +1,5 @@ // file: crates/ksp-core-lib/src/lib.rs -// version: 4 +// version: 5 #![warn(missing_docs)] #![deny(unreachable_pub)] #![forbid(unsafe_code)] @@ -7,6 +7,7 @@ //! Core contracts shared by the foundational KSP layers. mod error; +mod program_ids; /// Common KSP error type used by higher-level crates. pub use self::error::Error; @@ -16,3 +17,99 @@ pub use self::error::ErrorCode; pub use self::error::ErrorContext; /// Common KSP result alias using [`Error`]. pub use self::error::Result; +/// Canonical Address Lookup Table Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_ADDRESS_LOOKUP_TABLE; +/// Canonical Compute Budget Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_COMPUTE_BUDGET; +/// Canonical Config Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_CONFIG; +/// Canonical Feature Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_FEATURE; +/// Canonical upgradeable BPF Loader Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_LOADER_BPF_UPGRADEABLE; +/// Canonical deprecated BPF Loader Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_LOADER_BPF_V1; +/// Canonical BPF Loader v2 Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_LOADER_BPF_V2; +/// Canonical Native Loader Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_LOADER_NATIVE; +/// Canonical Loader v4 Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_LOADER_V4; +/// Canonical Ed25519 precompile Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_PRECOMPILE_ED25519; +/// Canonical Secp256k1 precompile Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_PRECOMPILE_SECP256K1; +/// Canonical Secp256r1 precompile Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_PRECOMPILE_SECP256R1; +/// Canonical Slashing Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_SLASHING; +/// Canonical Stake Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_STAKE; +/// Canonical System Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_SYSTEM; +/// Canonical Vote Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_VOTE; +/// Canonical ZK ElGamal Proof Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_ZK_ELGAMAL_PROOF; +/// Canonical ZK Token Proof Program ID as Base58 text. +pub use self::program_ids::PRGID_SOLANA_ZK_TOKEN_PROOF; +/// Canonical Address Lookup Table Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_ADDRESS_LOOKUP_TABLE; +/// Canonical Compute Budget Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_COMPUTE_BUDGET; +/// Canonical Config Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_CONFIG; +/// Canonical Feature Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_FEATURE; +/// Canonical upgradeable BPF Loader Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_LOADER_BPF_UPGRADEABLE; +/// Canonical deprecated BPF Loader Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_LOADER_BPF_V1; +/// Canonical BPF Loader v2 Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_LOADER_BPF_V2; +/// Canonical Native Loader Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_LOADER_NATIVE; +/// Canonical Loader v4 Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_LOADER_V4; +/// Canonical Ed25519 precompile Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_PRECOMPILE_ED25519; +/// Canonical Secp256k1 precompile Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_PRECOMPILE_SECP256K1; +/// Canonical Secp256r1 precompile Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_PRECOMPILE_SECP256R1; +/// Canonical Slashing Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_SLASHING; +/// Canonical Stake Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_STAKE; +/// Canonical System Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_SYSTEM; +/// Canonical Vote Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_VOTE; +/// Canonical ZK ElGamal Proof Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_ZK_ELGAMAL_PROOF; +/// Canonical ZK Token Proof Program ID as a typed [`Pubkey`]. +pub use self::program_ids::PRGIDPK_SOLANA_ZK_TOKEN_PROOF; +/// Immutable descriptor for one registered Program ID. +pub use self::program_ids::ProgramIdEntry; +/// Borrowed multi-axis filter for the canonical Program ID registry. +pub use self::program_ids::ProgramIdFilter; +/// Technical classification of one registered Program ID. +pub use self::program_ids::ProgramIdKind; +/// Returns the canonical Program ID registry. +pub use self::program_ids::entries; +/// Finds one registered Program ID by its Base58 representation. +pub use self::program_ids::find_program_id; +/// Finds one registered Program ID by its typed [`Pubkey`] representation. +pub use self::program_ids::find_program_pubkey; +/// Returns the Solana core/native Program ID view. +pub use self::program_ids::native_program_ids; +/// Returns Program IDs matching a multi-axis filter. +pub use self::program_ids::program_ids; +/// Returns Program IDs belonging to one domain. +pub use self::program_ids::program_ids_by_domain; +/// Returns Program IDs belonging to one family. +pub use self::program_ids::program_ids_by_family; +/// Returns Program IDs belonging to one protocol or project. +pub use self::program_ids::program_ids_by_protocol; +/// Solana account address primitive used by KSP Program IDs. +pub use solana_pubkey::Pubkey; diff --git a/crates/ksp-core-lib/src/program_ids.rs b/crates/ksp-core-lib/src/program_ids.rs new file mode 100644 index 0000000..8beec38 --- /dev/null +++ b/crates/ksp-core-lib/src/program_ids.rs @@ -0,0 +1,486 @@ +// file: crates/ksp-core-lib/src/program_ids.rs +// version: 1 + +const DOMAIN_SOLANA: &str = "solana"; +const FAMILY_CONSENSUS: &str = "consensus"; +const FAMILY_LOADER: &str = "loader"; +const FAMILY_PRECOMPILE: &str = "precompile"; +const FAMILY_PROOF: &str = "proof"; +const FAMILY_RUNTIME: &str = "runtime"; +const PROTOCOL_SOLANA: &str = "solana"; + +/// Declares one KSP-owned Solana Program ID as matching Base58 and typed constants. +/// +/// The Base58 literal is written once and decoded at compile time through [`crate::Pubkey`]. +#[macro_export] +macro_rules! declare_program_id { + ($string_name:ident, $pubkey_name:ident, $value:literal) => { + #[doc = concat!("Base58 Program ID declared as `", stringify!($string_name), "`.")] + pub const $string_name: &str = $value; + #[doc = concat!("Typed Program ID corresponding to `", stringify!($string_name), "`.")] + pub const $pubkey_name: $crate::Pubkey = $crate::Pubkey::from_str_const($string_name); + }; +} + +crate::declare_program_id!(PRGID_SOLANA_ADDRESS_LOOKUP_TABLE, PRGIDPK_SOLANA_ADDRESS_LOOKUP_TABLE, "AddressLookupTab1e1111111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_LOADER_BPF_V1, PRGIDPK_SOLANA_LOADER_BPF_V1, "BPFLoader1111111111111111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_LOADER_BPF_V2, PRGIDPK_SOLANA_LOADER_BPF_V2, "BPFLoader2111111111111111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_LOADER_BPF_UPGRADEABLE, PRGIDPK_SOLANA_LOADER_BPF_UPGRADEABLE, "BPFLoaderUpgradeab1e11111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_COMPUTE_BUDGET, PRGIDPK_SOLANA_COMPUTE_BUDGET, "ComputeBudget111111111111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_CONFIG, PRGIDPK_SOLANA_CONFIG, "Config1111111111111111111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_PRECOMPILE_ED25519, PRGIDPK_SOLANA_PRECOMPILE_ED25519, "Ed25519SigVerify111111111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_FEATURE, PRGIDPK_SOLANA_FEATURE, "Feature111111111111111111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_LOADER_V4, PRGIDPK_SOLANA_LOADER_V4, "LoaderV411111111111111111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_LOADER_NATIVE, PRGIDPK_SOLANA_LOADER_NATIVE, "NativeLoader1111111111111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_PRECOMPILE_SECP256K1, PRGIDPK_SOLANA_PRECOMPILE_SECP256K1, "KeccakSecp256k11111111111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_PRECOMPILE_SECP256R1, PRGIDPK_SOLANA_PRECOMPILE_SECP256R1, "Secp256r1SigVerify1111111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_SLASHING, PRGIDPK_SOLANA_SLASHING, "S1ashing11111111111111111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_STAKE, PRGIDPK_SOLANA_STAKE, "Stake11111111111111111111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_SYSTEM, PRGIDPK_SOLANA_SYSTEM, "11111111111111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_VOTE, PRGIDPK_SOLANA_VOTE, "Vote111111111111111111111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_ZK_ELGAMAL_PROOF, PRGIDPK_SOLANA_ZK_ELGAMAL_PROOF, "ZkE1Gama1Proof11111111111111111111111111111"); +crate::declare_program_id!(PRGID_SOLANA_ZK_TOKEN_PROOF, PRGIDPK_SOLANA_ZK_TOKEN_PROOF, "ZkTokenProof1111111111111111111111111111111"); + +/// Technical classification of a registered Program ID. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum ProgramIdKind { + /// A regular executable program belonging to the registered protocol surface. + Program, + /// A Solana program loader. + Loader, + /// A runtime precompile exposed through a Program ID. + Precompile, + /// An enshrined on-chain program deployed as part of the Solana protocol. + EnshrinedProgram, +} + +/// Immutable descriptor for one KSP-owned Program ID. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct ProgramIdEntry { + code: &'static str, + name: &'static str, + program_id: &'static str, + pubkey: crate::Pubkey, + domain: &'static str, + family: &'static str, + protocol: &'static str, + subfamily: std::option::Option<&'static str>, + program_version: std::option::Option<&'static str>, + kind: crate::ProgramIdKind, +} + +impl ProgramIdEntry { + const fn new(code: &'static str, name: &'static str, program_id: &'static str, pubkey: crate::Pubkey) -> Self { + return Self { + code, + name, + program_id, + pubkey, + domain: "", + family: "", + protocol: "", + subfamily: std::option::Option::None, + program_version: std::option::Option::None, + kind: crate::ProgramIdKind::Program, + }; + } + + const fn with_taxonomy( + mut self, + domain: &'static str, + family: &'static str, + protocol: &'static str, + subfamily: std::option::Option<&'static str>, + program_version: std::option::Option<&'static str>, + kind: crate::ProgramIdKind, + ) -> Self { + self.domain = domain; + self.family = family; + self.protocol = protocol; + self.subfamily = subfamily; + self.program_version = program_version; + self.kind = kind; + return self; + } + + /// Returns the stable KSP machine-readable code of this entry. + #[must_use] + pub const fn code(&self) -> &'static str { + return self.code; + } + + /// Returns the human-readable program name. + #[must_use] + pub const fn name(&self) -> &'static str { + return self.name; + } + + /// Returns the canonical Base58 Program ID. + #[must_use] + pub const fn program_id(&self) -> &'static str { + return self.program_id; + } + + /// Returns the typed Solana Program ID. + #[must_use] + pub const fn pubkey(&self) -> crate::Pubkey { + return self.pubkey; + } + + /// Returns the broad functional domain. + #[must_use] + pub const fn domain(&self) -> &'static str { + return self.domain; + } + + /// Returns the functional family within the domain. + #[must_use] + pub const fn family(&self) -> &'static str { + return self.family; + } + + /// Returns the owning protocol or project identifier. + #[must_use] + pub const fn protocol(&self) -> &'static str { + return self.protocol; + } + + /// Returns the optional architectural branch or product subfamily. + #[must_use] + pub const fn subfamily(&self) -> std::option::Option<&'static str> { + return self.subfamily; + } + + /// Returns the optional public generation of this program lineage. + #[must_use] + pub const fn program_version(&self) -> std::option::Option<&'static str> { + return self.program_version; + } + + /// Returns the technical Program ID classification. + #[must_use] + pub const fn kind(&self) -> crate::ProgramIdKind { + return self.kind; + } +} + +/// Borrowed filter used to select Program IDs from the canonical KSP registry. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct ProgramIdFilter<'a> { + domain: std::option::Option<&'a str>, + family: std::option::Option<&'a str>, + protocol: std::option::Option<&'a str>, + subfamily: std::option::Option<&'a str>, + program_version: std::option::Option<&'a str>, + kind: std::option::Option, +} + +impl<'a> ProgramIdFilter<'a> { + /// Creates an empty filter matching every registered Program ID. + #[must_use] + pub const fn new() -> Self { + return Self { + domain: std::option::Option::None, + family: std::option::Option::None, + protocol: std::option::Option::None, + subfamily: std::option::Option::None, + program_version: std::option::Option::None, + kind: std::option::Option::None, + }; + } + + /// Restricts the filter to one functional domain. + #[must_use] + pub const fn with_domain(mut self, domain: &'a str) -> Self { + self.domain = std::option::Option::Some(domain); + return self; + } + + /// Restricts the filter to one functional family. + #[must_use] + pub const fn with_family(mut self, family: &'a str) -> Self { + self.family = std::option::Option::Some(family); + return self; + } + + /// Restricts the filter to one protocol or project. + #[must_use] + pub const fn with_protocol(mut self, protocol: &'a str) -> Self { + self.protocol = std::option::Option::Some(protocol); + return self; + } + + /// Restricts the filter to one architectural subfamily. + #[must_use] + pub const fn with_subfamily(mut self, subfamily: &'a str) -> Self { + self.subfamily = std::option::Option::Some(subfamily); + return self; + } + + /// Restricts the filter to one public program generation. + #[must_use] + pub const fn with_program_version(mut self, program_version: &'a str) -> Self { + self.program_version = std::option::Option::Some(program_version); + return self; + } + + /// Restricts the filter to one technical Program ID kind. + #[must_use] + pub const fn with_kind(mut self, kind: crate::ProgramIdKind) -> Self { + self.kind = std::option::Option::Some(kind); + return self; + } + + fn matches(&self, entry: &crate::ProgramIdEntry) -> bool { + if let std::option::Option::Some(domain) = self.domain + && entry.domain() != domain + { + return false; + } + if let std::option::Option::Some(family) = self.family + && entry.family() != family + { + return false; + } + if let std::option::Option::Some(protocol) = self.protocol + && entry.protocol() != protocol + { + return false; + } + if let std::option::Option::Some(subfamily) = self.subfamily + && entry.subfamily() != std::option::Option::Some(subfamily) + { + return false; + } + if let std::option::Option::Some(program_version) = self.program_version + && entry.program_version() != std::option::Option::Some(program_version) + { + return false; + } + if let std::option::Option::Some(kind) = self.kind + && entry.kind() != kind + { + return false; + } + return true; + } +} + +const PROGRAM_ID_ENTRIES: &[crate::ProgramIdEntry] = &[ + crate::ProgramIdEntry::new( + "solana.address_lookup_table", + "Address Lookup Table Program", + crate::PRGID_SOLANA_ADDRESS_LOOKUP_TABLE, + crate::PRGIDPK_SOLANA_ADDRESS_LOOKUP_TABLE, + ) + .with_taxonomy(DOMAIN_SOLANA, FAMILY_RUNTIME, PROTOCOL_SOLANA, std::option::Option::None, std::option::Option::None, crate::ProgramIdKind::Program), + crate::ProgramIdEntry::new("solana.loader.bpf.v1", "Deprecated BPF Loader", crate::PRGID_SOLANA_LOADER_BPF_V1, crate::PRGIDPK_SOLANA_LOADER_BPF_V1) + .with_taxonomy( + DOMAIN_SOLANA, + FAMILY_LOADER, + PROTOCOL_SOLANA, + std::option::Option::Some("bpf"), + std::option::Option::Some("v1"), + crate::ProgramIdKind::Loader, + ), + crate::ProgramIdEntry::new("solana.loader.bpf.v2", "BPF Loader v2", crate::PRGID_SOLANA_LOADER_BPF_V2, crate::PRGIDPK_SOLANA_LOADER_BPF_V2).with_taxonomy( + DOMAIN_SOLANA, + FAMILY_LOADER, + PROTOCOL_SOLANA, + std::option::Option::Some("bpf"), + std::option::Option::Some("v2"), + crate::ProgramIdKind::Loader, + ), + crate::ProgramIdEntry::new( + "solana.loader.bpf_upgradeable", + "Upgradeable BPF Loader", + crate::PRGID_SOLANA_LOADER_BPF_UPGRADEABLE, + crate::PRGIDPK_SOLANA_LOADER_BPF_UPGRADEABLE, + ) + .with_taxonomy( + DOMAIN_SOLANA, + FAMILY_LOADER, + PROTOCOL_SOLANA, + std::option::Option::Some("bpf"), + std::option::Option::None, + crate::ProgramIdKind::Loader, + ), + crate::ProgramIdEntry::new("solana.compute_budget", "Compute Budget Program", crate::PRGID_SOLANA_COMPUTE_BUDGET, crate::PRGIDPK_SOLANA_COMPUTE_BUDGET) + .with_taxonomy(DOMAIN_SOLANA, FAMILY_RUNTIME, PROTOCOL_SOLANA, std::option::Option::None, std::option::Option::None, crate::ProgramIdKind::Program), + crate::ProgramIdEntry::new("solana.config", "Config Program", crate::PRGID_SOLANA_CONFIG, crate::PRGIDPK_SOLANA_CONFIG).with_taxonomy( + DOMAIN_SOLANA, + FAMILY_RUNTIME, + PROTOCOL_SOLANA, + std::option::Option::None, + std::option::Option::None, + crate::ProgramIdKind::Program, + ), + crate::ProgramIdEntry::new( + "solana.precompile.ed25519", + "Ed25519 Signature Verification Precompile", + crate::PRGID_SOLANA_PRECOMPILE_ED25519, + crate::PRGIDPK_SOLANA_PRECOMPILE_ED25519, + ) + .with_taxonomy( + DOMAIN_SOLANA, + FAMILY_PRECOMPILE, + PROTOCOL_SOLANA, + std::option::Option::Some("ed25519"), + std::option::Option::None, + crate::ProgramIdKind::Precompile, + ), + crate::ProgramIdEntry::new("solana.feature", "Feature Program", crate::PRGID_SOLANA_FEATURE, crate::PRGIDPK_SOLANA_FEATURE).with_taxonomy( + DOMAIN_SOLANA, + FAMILY_RUNTIME, + PROTOCOL_SOLANA, + std::option::Option::None, + std::option::Option::None, + crate::ProgramIdKind::Program, + ), + crate::ProgramIdEntry::new("solana.loader.v4", "Loader v4", crate::PRGID_SOLANA_LOADER_V4, crate::PRGIDPK_SOLANA_LOADER_V4).with_taxonomy( + DOMAIN_SOLANA, + FAMILY_LOADER, + PROTOCOL_SOLANA, + std::option::Option::None, + std::option::Option::Some("v4"), + crate::ProgramIdKind::Loader, + ), + crate::ProgramIdEntry::new("solana.loader.native", "Native Loader", crate::PRGID_SOLANA_LOADER_NATIVE, crate::PRGIDPK_SOLANA_LOADER_NATIVE).with_taxonomy( + DOMAIN_SOLANA, + FAMILY_LOADER, + PROTOCOL_SOLANA, + std::option::Option::Some("native"), + std::option::Option::None, + crate::ProgramIdKind::Loader, + ), + crate::ProgramIdEntry::new( + "solana.precompile.secp256k1", + "Secp256k1 Signature Verification Precompile", + crate::PRGID_SOLANA_PRECOMPILE_SECP256K1, + crate::PRGIDPK_SOLANA_PRECOMPILE_SECP256K1, + ) + .with_taxonomy( + DOMAIN_SOLANA, + FAMILY_PRECOMPILE, + PROTOCOL_SOLANA, + std::option::Option::Some("secp256k1"), + std::option::Option::None, + crate::ProgramIdKind::Precompile, + ), + crate::ProgramIdEntry::new( + "solana.precompile.secp256r1", + "Secp256r1 Signature Verification Precompile", + crate::PRGID_SOLANA_PRECOMPILE_SECP256R1, + crate::PRGIDPK_SOLANA_PRECOMPILE_SECP256R1, + ) + .with_taxonomy( + DOMAIN_SOLANA, + FAMILY_PRECOMPILE, + PROTOCOL_SOLANA, + std::option::Option::Some("secp256r1"), + std::option::Option::None, + crate::ProgramIdKind::Precompile, + ), + crate::ProgramIdEntry::new("solana.slashing", "Slashing Program", crate::PRGID_SOLANA_SLASHING, crate::PRGIDPK_SOLANA_SLASHING).with_taxonomy( + DOMAIN_SOLANA, + FAMILY_CONSENSUS, + PROTOCOL_SOLANA, + std::option::Option::None, + std::option::Option::None, + crate::ProgramIdKind::EnshrinedProgram, + ), + crate::ProgramIdEntry::new("solana.stake", "Stake Program", crate::PRGID_SOLANA_STAKE, crate::PRGIDPK_SOLANA_STAKE).with_taxonomy( + DOMAIN_SOLANA, + FAMILY_CONSENSUS, + PROTOCOL_SOLANA, + std::option::Option::None, + std::option::Option::None, + crate::ProgramIdKind::Program, + ), + crate::ProgramIdEntry::new("solana.system", "System Program", crate::PRGID_SOLANA_SYSTEM, crate::PRGIDPK_SOLANA_SYSTEM).with_taxonomy( + DOMAIN_SOLANA, + FAMILY_RUNTIME, + PROTOCOL_SOLANA, + std::option::Option::None, + std::option::Option::None, + crate::ProgramIdKind::Program, + ), + crate::ProgramIdEntry::new("solana.vote", "Vote Program", crate::PRGID_SOLANA_VOTE, crate::PRGIDPK_SOLANA_VOTE).with_taxonomy( + DOMAIN_SOLANA, + FAMILY_CONSENSUS, + PROTOCOL_SOLANA, + std::option::Option::None, + std::option::Option::None, + crate::ProgramIdKind::Program, + ), + crate::ProgramIdEntry::new( + "solana.proof.zk_elgamal", + "ZK ElGamal Proof Program", + crate::PRGID_SOLANA_ZK_ELGAMAL_PROOF, + crate::PRGIDPK_SOLANA_ZK_ELGAMAL_PROOF, + ) + .with_taxonomy( + DOMAIN_SOLANA, + FAMILY_PROOF, + PROTOCOL_SOLANA, + std::option::Option::Some("zk_elgamal"), + std::option::Option::None, + crate::ProgramIdKind::Program, + ), + crate::ProgramIdEntry::new("solana.proof.zk_token", "ZK Token Proof Program", crate::PRGID_SOLANA_ZK_TOKEN_PROOF, crate::PRGIDPK_SOLANA_ZK_TOKEN_PROOF) + .with_taxonomy( + DOMAIN_SOLANA, + FAMILY_PROOF, + PROTOCOL_SOLANA, + std::option::Option::Some("zk_token"), + std::option::Option::None, + crate::ProgramIdKind::Program, + ), +]; + +/// Returns the canonical KSP Program ID registry. +#[must_use] +pub const fn entries() -> &'static [crate::ProgramIdEntry] { + return PROGRAM_ID_ENTRIES; +} + +/// Returns a lazy view of Program IDs matching all configured filter axes. +pub fn program_ids<'a>(filter: crate::ProgramIdFilter<'a>) -> impl std::iter::Iterator + 'a { + return PROGRAM_ID_ENTRIES.iter().filter(move |entry| filter.matches(entry)); +} + +/// Returns all Solana core/native Program IDs, including loaders, precompiles and enshrined programs. +pub fn native_program_ids() -> impl std::iter::Iterator { + return crate::program_ids(crate::ProgramIdFilter::new().with_domain(DOMAIN_SOLANA).with_protocol(PROTOCOL_SOLANA)); +} + +/// Returns a lazy view of Program IDs belonging to one functional domain. +pub fn program_ids_by_domain<'a>(domain: &'a str) -> impl std::iter::Iterator + 'a { + return crate::program_ids(crate::ProgramIdFilter::new().with_domain(domain)); +} + +/// Returns a lazy view of Program IDs belonging to one functional family. +pub fn program_ids_by_family<'a>(family: &'a str) -> impl std::iter::Iterator + 'a { + return crate::program_ids(crate::ProgramIdFilter::new().with_family(family)); +} + +/// Returns a lazy view of Program IDs belonging to one protocol or project. +pub fn program_ids_by_protocol<'a>(protocol: &'a str) -> impl std::iter::Iterator + 'a { + return crate::program_ids(crate::ProgramIdFilter::new().with_protocol(protocol)); +} + +/// Finds one registered Program ID by its canonical Base58 representation. +#[must_use] +pub fn find_program_id(program_id: &str) -> std::option::Option<&'static crate::ProgramIdEntry> { + return PROGRAM_ID_ENTRIES.iter().find(|entry| entry.program_id() == program_id); +} + +/// Finds one registered Program ID by its typed Solana representation. +#[must_use] +pub fn find_program_pubkey(program_id: &crate::Pubkey) -> std::option::Option<&'static crate::ProgramIdEntry> { + return PROGRAM_ID_ENTRIES.iter().find(|entry| entry.pubkey() == *program_id); +} + +#[cfg(test)] +#[path = "../unit_tests/program_ids.rs"] +mod tests; diff --git a/crates/ksp-core-lib/tests/public_api.rs b/crates/ksp-core-lib/tests/public_api.rs index ea7fa2c..a4b2cf0 100644 --- a/crates/ksp-core-lib/tests/public_api.rs +++ b/crates/ksp-core-lib/tests/public_api.rs @@ -1,7 +1,9 @@ // file: crates/ksp-core-lib/tests/public_api.rs -// version: 2 +// version: 3 -//! Integration tests for the public `ksp-core-lib` error contract. +//! Integration tests for the public `ksp-core-lib` contracts. + +ksp_core_lib::declare_program_id!(TEST_PRGID_SYSTEM, TEST_PRGIDPK_SYSTEM, "11111111111111111111111111111111"); fn public_result() -> ksp_core_lib::Result<()> { let error = ksp_core_lib::Error::new(ksp_core_lib::ErrorCode::new("consumer", "failed"), "consumer failure").with_context("operation", "public_api"); @@ -22,3 +24,42 @@ fn error_contract_is_consumable_from_crate_root() { assert_eq!(std::string::ToString::to_string(&error), "consumer.failed: consumer failure"); return; } + +#[test] +fn program_id_contract_is_consumable_from_crate_root() { + assert_eq!(TEST_PRGID_SYSTEM, ksp_core_lib::PRGID_SOLANA_SYSTEM); + assert_eq!(TEST_PRGIDPK_SYSTEM, ksp_core_lib::PRGIDPK_SOLANA_SYSTEM); + assert_eq!(ksp_core_lib::entries().len(), 18); + assert_eq!(ksp_core_lib::native_program_ids().count(), 18); + let system = ksp_core_lib::find_program_id(ksp_core_lib::PRGID_SOLANA_SYSTEM); + let system = match system { + std::option::Option::Some(entry) => entry, + std::option::Option::None => return, + }; + assert_eq!(system.code(), "solana.system"); + assert_eq!(system.domain(), "solana"); + assert_eq!(system.family(), "runtime"); + assert_eq!(system.protocol(), "solana"); + assert_eq!(system.pubkey(), ksp_core_lib::PRGIDPK_SOLANA_SYSTEM); + return; +} + +#[test] +fn program_id_registry_supports_public_taxonomy_filters() { + let loader_count = ksp_core_lib::program_ids_by_family("loader").count(); + let precompile_count = ksp_core_lib::program_ids_by_family("precompile").count(); + let bpf_v2_count = ksp_core_lib::program_ids( + ksp_core_lib::ProgramIdFilter::new() + .with_domain("solana") + .with_family("loader") + .with_protocol("solana") + .with_subfamily("bpf") + .with_program_version("v2") + .with_kind(ksp_core_lib::ProgramIdKind::Loader), + ) + .count(); + assert_eq!(loader_count, 5); + assert_eq!(precompile_count, 3); + assert_eq!(bpf_v2_count, 1); + return; +} diff --git a/crates/ksp-core-lib/unit_tests/program_ids.rs b/crates/ksp-core-lib/unit_tests/program_ids.rs new file mode 100644 index 0000000..07ea716 --- /dev/null +++ b/crates/ksp-core-lib/unit_tests/program_ids.rs @@ -0,0 +1,89 @@ +// file: crates/ksp-core-lib/unit_tests/program_ids.rs +// version: 1 + +fn assert_program_id_types(program_id: &'static str, pubkey: crate::Pubkey) { + assert_eq!(pubkey, crate::Pubkey::from_str_const(program_id)); + return; +} + +#[test] +fn declared_program_ids_have_matching_text_and_pubkey_forms() { + assert_program_id_types(crate::PRGID_SOLANA_SYSTEM, crate::PRGIDPK_SOLANA_SYSTEM); + assert_program_id_types(crate::PRGID_SOLANA_STAKE, crate::PRGIDPK_SOLANA_STAKE); + assert_program_id_types(crate::PRGID_SOLANA_VOTE, crate::PRGIDPK_SOLANA_VOTE); + assert_program_id_types(crate::PRGID_SOLANA_SLASHING, crate::PRGIDPK_SOLANA_SLASHING); + return; +} + +#[test] +fn registry_contains_the_eighteen_core_program_ids() { + assert_eq!(crate::entries().len(), 18); + assert_eq!(crate::native_program_ids().count(), 18); + return; +} + +#[test] +fn registry_codes_program_ids_and_pubkeys_are_unique() { + for left_index in 0..crate::entries().len() { + for right_index in (left_index + 1)..crate::entries().len() { + let left = &crate::entries()[left_index]; + let right = &crate::entries()[right_index]; + assert_ne!(left.code(), right.code()); + assert_ne!(left.program_id(), right.program_id()); + assert_ne!(left.pubkey(), right.pubkey()); + } + } + return; +} + +#[test] +fn registry_pubkeys_match_their_owned_base58_values() { + for entry in crate::entries() { + assert_eq!(entry.pubkey(), crate::Pubkey::from_str_const(entry.program_id())); + } + return; +} + +#[test] +fn filters_combine_domain_family_protocol_subfamily_version_and_kind() { + let bpf_v2 = crate::program_ids( + crate::ProgramIdFilter::new() + .with_domain("solana") + .with_family("loader") + .with_protocol("solana") + .with_subfamily("bpf") + .with_program_version("v2") + .with_kind(crate::ProgramIdKind::Loader), + ) + .collect::>(); + assert_eq!(bpf_v2.len(), 1); + assert_eq!(bpf_v2[0].program_id(), crate::PRGID_SOLANA_LOADER_BPF_V2); + return; +} + +#[test] +fn family_views_cover_expected_core_groups() { + assert_eq!(crate::program_ids_by_family("runtime").count(), 5); + assert_eq!(crate::program_ids_by_family("consensus").count(), 3); + assert_eq!(crate::program_ids_by_family("loader").count(), 5); + assert_eq!(crate::program_ids_by_family("precompile").count(), 3); + assert_eq!(crate::program_ids_by_family("proof").count(), 2); + return; +} + +#[test] +fn direct_lookup_supports_text_and_typed_program_ids() { + let by_text = crate::find_program_id(crate::PRGID_SOLANA_SLASHING); + let by_pubkey = crate::find_program_pubkey(&crate::PRGIDPK_SOLANA_SLASHING); + assert_eq!(by_text.map(crate::ProgramIdEntry::code), std::option::Option::Some("solana.slashing")); + assert_eq!(by_pubkey.map(crate::ProgramIdEntry::code), std::option::Option::Some("solana.slashing")); + return; +} + +#[test] +fn non_program_well_known_accounts_are_absent() { + assert!(crate::find_program_id("1nc1nerator11111111111111111111111111111111").is_none()); + assert!(crate::find_program_id("StakeConfig11111111111111111111111111111111").is_none()); + assert!(crate::find_program_id("SysvarC1ock11111111111111111111111111111111").is_none()); + return; +} diff --git a/deltas/0.1.1/pre.003.md b/deltas/0.1.1/pre.003.md new file mode 100644 index 0000000..f327be5 --- /dev/null +++ b/deltas/0.1.1/pre.003.md @@ -0,0 +1,284 @@ + + + +# Delta `0.1.1-pre.003` — Pubkey + Program IDs + +## Statut + +Tranche fonctionnelle `0.1.1-pre.003` préparée après validation réussie par le user de `0.1.1-pre.002-fix.001`. + +La base utilisateur observée avant ce delta utilise : + +```text +workspace.package.version = "0.1.1-pre.2.fix.1" +``` + +Le présent delta ouvre : + +```text +workspace.package.version = "0.1.1-pre.3" +``` + +## Validation de la base précédente + +Le user a exécuté avec succès avant ce delta : + +```bash +cargo fmt --all +cargo check --workspace +cargo clippy --workspace --all-targets +cargo test --workspace +``` + +Les six tests unitaires Error et le test d'intégration public Error passent sans warning dans cette validation. + +## Audit Solana/Anza revérifié + +Au 2026-08-14 : + +- `solana-pubkey 4.3.0` est la version publiée courante observée sur crates.io ; +- son MSRV publié est Rust `1.89.0` ; +- la validation précédente du user expose une génération Clippy `rust-1.94.0`, donc la toolchain observée satisfait ce MSRV ; +- `Pubkey` reste la façade de compatibilité officielle sur l'`Address` Solana actuel ; +- `Pubkey::from_str_const` permet le décodage Base58 compile-time nécessaire à la macro KSP ; +- `solana-sdk-ids` est consulté uniquement comme source d'audit et n'entre pas dans le graphe Cargo KSP. + +Sources externes revérifiées pendant cette tranche : + +- crates.io / docs.rs pour `solana-pubkey 4.3.0` ; +- `anza-xyz/solana-sdk`, `sdk-ids/src/lib.rs`, pour les identifiants fondamentaux actuellement publiés ; +- SIMD-0204 et la documentation Anza pour le Slashing Program `S1ashing11111111111111111111111111111111111`. + +## Dépendance Core + +`crates/ksp-core-lib/Cargo.toml` ajoute uniquement : + +```toml +solana-pubkey = { version = "4.3.0", default-features = false } +``` + +Aucun codec wire, client RPC, umbrella SDK ou registre `solana-sdk-ids` n'est ajouté. + +`ksp_core_lib::Pubkey` réexporte `solana_pubkey::Pubkey` depuis la façade Core. + +## Macro KSP de Program ID + +La macro publique : + +```text +ksp_core_lib::declare_program_id! +``` + +possède la chaîne Base58 une seule fois et produit simultanément : + +```text +PRGID_* : &'static str +PRGIDPK_* : Pubkey +``` + +La représentation typée est construite avec `Pubkey::from_str_const`, sans parsing runtime, `unwrap`, `expect`, `panic` ni opérateur `?`. + +La macro ne génère pas de symboles génériques `ID`, `id()` ou `check_id()` et peut donc être utilisée plusieurs fois dans une même crate/module. + +## Première surface Program IDs Core + +La tranche fixe le premier registre à 18 Program IDs. + +Les 17 valeurs de la surface officielle actuelle Anza `solana-sdk-ids` sont possédées localement par KSP : + +1. Address Lookup Table ; +2. BPF Loader historique v1 ; +3. BPF Loader v2 ; +4. BPF Loader Upgradeable ; +5. Compute Budget ; +6. Config ; +7. Ed25519 precompile ; +8. Feature ; +9. Loader v4 ; +10. Native Loader ; +11. Secp256k1 precompile ; +12. Secp256r1 precompile ; +13. Stake ; +14. System ; +15. Vote ; +16. ZK ElGamal Proof ; +17. ZK Token Proof. + +Le dix-huitième identifiant est : + +```text +PRGID_SOLANA_SLASHING = "S1ashing11111111111111111111111111111111111" +``` + +Son statut de programme enshrined et son adresse sont confirmés séparément par SIMD-0204/Anza ; sa présence ne dépend donc pas de l'ancien registre bot3. + +Les sysvars, `StakeConfig`, l'incinerator et les autres well-known accounts restent exclus du registre Program IDs. + +## Nomenclature publique + +Chaque entrée possède une paire `PRGID_*` / `PRGIDPK_*` au crate-root. + +Exemples : + +```text +PRGID_SOLANA_SYSTEM +PRGIDPK_SOLANA_SYSTEM + +PRGID_SOLANA_LOADER_BPF_V1 +PRGIDPK_SOLANA_LOADER_BPF_V1 + +PRGID_SOLANA_PRECOMPILE_ED25519 +PRGIDPK_SOLANA_PRECOMPILE_ED25519 + +PRGID_SOLANA_SLASHING +PRGIDPK_SOLANA_SLASHING +``` + +Le suffixe est strictement identique entre les deux représentations. + +## Registre canonique + +`ProgramIdEntry` porte : + +```text +code +name +program_id +pubkey +domain +family +protocol +subfamily +program_version +kind +``` + +Les axes fonctionnels restent extensibles sous forme de chaînes. Aucun enum central fermé des domaines, familles ou protocoles futurs n'est créé. + +`ProgramIdKind` est limité à la classification technique : + +```text +Program +Loader +Precompile +EnshrinedProgram +``` + +La première taxonomie Core utilise : + +```text +domain = solana +protocol = solana + +family = runtime +family = consensus +family = loader +family = precompile +family = proof +``` + +`program_version` reste distinct de `subfamily`. Les BPF loaders v1/v2 et Loader v4 utilisent l'axe version ; la branche BPF utilise séparément `subfamily = bpf`. + +## API de recherche et vues + +La façade expose : + +```text +entries() +program_ids(filter) +native_program_ids() +program_ids_by_domain(...) +program_ids_by_family(...) +program_ids_by_protocol(...) +find_program_id(...) +find_program_pubkey(...) +``` + +`ProgramIdFilter` peut combiner : + +```text +domain +family +protocol +subfamily +program_version +kind +``` + +Toutes les vues sont construites à partir du registre canonique unique. Elles retournent des iterators paresseux sans dupliquer un tableau statique par catégorie. + +Une future fonction `amm_program_ids()` pourra donc devenir une vue `family = amm` sans changement structurel de `ProgramIdEntry`. + +## Tests ajoutés + +`unit_tests/program_ids.rs` vérifie notamment : + +- cohérence texte/`Pubkey` des déclarations ; +- présence exacte des 18 Program IDs Core ; +- unicité des codes, Base58 et `Pubkey` ; +- correspondance de chaque `Pubkey` avec la Base58 KSP ; +- intersection des six axes de filtre ; +- tailles attendues des familles Core actuelles ; +- recherche texte et `Pubkey` ; +- absence de l'incinerator, `StakeConfig` et du Clock sysvar. + +`tests/public_api.rs` vérifie en consommateur externe : + +- la macro `declare_program_id!` ; +- les constantes `PRGID_*` / `PRGIDPK_*` ; +- `Pubkey` ; +- les vues du registre ; +- les getters de `ProgramIdEntry` ; +- le filtrage public combiné. + +## Documentation + +`docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md` passe en version documentaire 6 afin de : + +- fixer l'inventaire Core à 18 IDs ; +- tracer la vérification du Slashing Program ; +- enregistrer `solana-pubkey 4.3.0` comme dépendance effectivement introduite ; +- fermer la question de MSRV de `pre.003` ; +- fixer `ProgramIdKind` et les retours iterator des vues ; +- compléter l'API publique réellement implémentée. + +## Hors scope préservé + +Cette tranche n'ajoute toujours pas : + +- SPL Token/Token-2022/ATA/Memo ; +- Metaplex ou DEX ; +- well-known accounts ; +- codecs Borsh/Wincode ; +- decoder/executor/IDL ; +- RPC/WS ; +- Wallet ; +- Logging ; +- Config ; +- Store ; +- applications. + +## Validations à exécuter sur le dépôt cible + +Cette livraison ne déclare aucune validation Cargo non exécutée dans l'environnement de génération. + +Après application : + +```bash +cargo fmt --all +cargo check --workspace +cargo test --workspace +cargo clippy --workspace --all-targets +cargo tree -p ksp-core-lib +cargo tree -p ksp-core-lib -d +``` + +Les deux commandes `cargo tree` doivent notamment confirmer l'absence de `solana-sdk-ids` et permettre de contrôler le graphe/features réellement résolus. + +## Suite + +Si les validations sont propres, la tranche suivante reste : + +```text +0.1.1-pre.004 — intégration Core + audits +``` diff --git a/docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md b/docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md index d1a9c35..3bb1894 100644 --- a/docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md +++ b/docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md @@ -1,5 +1,5 @@ - + # Plan KSP 0.1.1 — Core foundation @@ -127,11 +127,18 @@ ksp_core_lib::Result ksp_core_lib::Pubkey ksp_core_lib::ProgramIdEntry ksp_core_lib::ProgramIdFilter +ksp_core_lib::ProgramIdKind ksp_core_lib::entries ksp_core_lib::program_ids +ksp_core_lib::program_ids_by_domain +ksp_core_lib::program_ids_by_family +ksp_core_lib::program_ids_by_protocol ksp_core_lib::native_program_ids ksp_core_lib::find_program_id +ksp_core_lib::find_program_pubkey ksp_core_lib::declare_program_id! +ksp_core_lib::PRGID_* +ksp_core_lib::PRGIDPK_* ``` Les modules d'implémentation restent privés conformément aux règles Rust du dépôt. @@ -213,13 +220,13 @@ Ne pas ajouter : - crates RPC/client ; - Borsh/Wincode/Serde pour une hypothétique future surface wire. -La génération actuelle de `solana-pubkey` requiert Rust 1.89.0. Avant son ajout en `pre.003`, la toolchain réelle du dépôt devra donc être vérifiée. Une toolchain plus ancienne ne doit pas conduire silencieusement à choisir une vieille génération Solana uniquement pour contourner cette exigence. +La génération retenue de `solana-pubkey` requiert Rust 1.89.0. La validation `pre.002-fix.001` a été exécutée avec une génération Clippy Rust 1.94.0, donc la toolchain observée satisfait ce MSRV. Une future révision ne devra pas revenir silencieusement à une vieille génération Solana uniquement pour contourner une exigence de toolchain. ## Program IDs retenus pour la première surface Core -Le nombre exact de Program IDs de la première surface Core n'est plus figé à `17`. +`pre.003` fixe la première surface Core à **18 Program IDs**. -`pre.001` retient les familles fondamentales suivantes comme inventaire de départ à vérifier une dernière fois contre les sources officielles au moment de `pre.003` : +Les 17 identifiants fondamentaux exposés par la surface officielle actuelle `solana-sdk-ids` sont recopiés comme valeurs KSP sans créer de dépendance Cargo vers cette crate : - System ; - Stake ; @@ -228,13 +235,16 @@ Le nombre exact de Program IDs de la première surface Core n'est plus figé à - Feature ; - Compute Budget ; - Address Lookup Table ; -- loaders BPF historiques/actuels et Loader v4 ; +- BPF Loader historique v1 ; +- BPF Loader v2 ; +- BPF Loader Upgradeable ; +- Loader v4 ; - Native Loader ; - précompiles Ed25519, Secp256k1 et Secp256r1 ; -- programmes ZK fondamentaux actuellement exposés par la surface Anza/Solana ; -- tout programme natif/historique supplémentaire réellement encore pertinent pour la frontière Core. +- ZK ElGamal Proof ; +- ZK Token Proof. -L'ancien `ks-program-ids` de bot3 est utilisé comme inventaire historique complémentaire, pas comme source de vérité. Il rappelle notamment une entrée `slashing` dans son ensemble `native_program_ids()`. Sa présence dans la première surface KSP doit être décidée à partir de son statut officiel réel au moment de `pre.003`, plutôt que déduite d'un nombre figé ou d'une ancienne liste. +Le dix-huitième identifiant est le Slashing Program `S1ashing11111111111111111111111111111111111`. Il n'est pas encore publié dans `solana-sdk-ids`, mais son statut de programme enshrined et son adresse sont confirmés par SIMD-0204 et la documentation Anza. L'ancien `ks-program-ids` de bot3 avait déjà cette entrée ; `pre.003` ne la conserve toutefois qu'après cette revérification officielle indépendante. ### Exclusions volontaires de `0.1.1` @@ -374,7 +384,7 @@ L'audit de l'ancien registre bot3 et des IDLs archivées montre qu'un seul axe h - `kind` : classification technique nécessaire aux vues Core telles que les programmes natifs/loaders/précompiles ; - le code KSP unique, la chaîne Base58 `PRGID_*` et le `Pubkey` `PRGIDPK_*` restent les identités de l'entrée. -Les vocabulaires exacts de `domain`, `family`, `protocol`, `subfamily` et `kind` doivent rester extensibles. Core ne doit pas créer une enum fermée contenant tous les futurs protocoles Solana. Des identifiants statiques/constantes KSP ou des newtypes légers peuvent être utilisés ; le choix syntaxique exact est finalisé en `pre.003`. +Les vocabulaires de `domain`, `family`, `protocol`, `subfamily` et `program_version` restent des chaînes extensibles : Core ne crée aucune enum fermée des futurs protocoles Solana. `kind` est volontairement une petite enum technique `ProgramIdKind` (`Program`, `Loader`, `Precompile`, `EnshrinedProgram`) parce qu'elle décrit la nature de l'entrée plutôt qu'un catalogue de protocoles. `family = amm` est retenu comme famille agrégatrice future pour les modèles AMM. Les variantes `cpmm`, `clmm`, `dlmm`, `damm`, `stable_swap`, `weighted_swap`, `gamma`, `ssl` ou équivalentes appartiennent au niveau `subfamily` lorsqu'elles représentent réellement une branche architecturale du protocole. Cela permettra à une future vue `amm_program_ids()` de retrouver l'ensemble de ces programmes au lieu de limiter la recherche à l'ancien préfixe bot3 `AMM_*`. @@ -454,7 +464,7 @@ amm_program_ids() Cette fonction devra être une vue de la classification canonique (`family = amm`) et non un second registre manuel. `0.1.1` ne crée pas un helper AMM vide puisque les Program IDs AMM restent hors scope de la release, mais son ajout futur ne doit nécessiter aucune refonte de `ProgramIdEntry`. -Les vues filtrées peuvent retourner un iterator/view au lieu d'un `&'static [ProgramIdEntry]` si cela évite de dupliquer des tableaux statiques. Le contrat exact de retour est décidé en `pre.003` en privilégiant une API stable et sans allocation inutile. +Les vues filtrées retournent des iterators paresseux sur le registre canonique et n'allouent pas de collection intermédiaire. `entries()` reste la vue exhaustive sous forme de slice statique. `native_program_ids()` et les helpers `program_ids_by_domain(...)`, `program_ids_by_family(...)` et `program_ids_by_protocol(...)` sont des vues de cette même source. `registered_program_ids()` de bot3 reste considéré comme un alias redondant de `entries()` et n'est pas repris automatiquement. Les well-known accounts suivent un registre/naming distinct lorsqu'ils deviennent nécessaires. @@ -597,17 +607,17 @@ Aucune dépendance Solana n'est nécessaire à cette tranche. Objectifs : - revérifier les versions Solana/Anza au jour de l'implémentation ; -- vérifier `rustc` par rapport au MSRV de la génération retenue ; +- vérifier la toolchain observée par rapport au MSRV de la génération retenue ; - ajouter `solana-pubkey` uniquement au propriétaire `ksp-core-lib` ; - réexporter `Pubkey` ; - implémenter `declare_program_id!` et la paire `PRGID_*` / `PRGIDPK_*` ; -- finaliser l'inventaire des Program IDs fondamentaux à partir des sources officielles actuelles ; -- implémenter `ProgramIdEntry`, `ProgramIdFilter`, `entries()`, `program_ids(...)`, `native_program_ids()` et `find_program_id()` ; +- finaliser à 18 l'inventaire des Program IDs fondamentaux à partir des sources officielles actuelles ; +- implémenter `ProgramIdEntry`, `ProgramIdFilter`, `ProgramIdKind`, `entries()`, `program_ids(...)`, les vues domain/family/protocol, `native_program_ids()`, `find_program_id()` et `find_program_pubkey()` ; - implémenter la taxonomie extensible `domain` / `family` / `protocol` / `subfamily` / `program_version` / `kind` sans enum centrale fermée des protocoles ; - garantir que les futurs helpers spécialisés comme `amm_program_ids()` puissent être des vues du registre canonique sans duplication ; -- ajouter les tests de conformité, d'unicité et de filtrage ; +- ajouter les tests de conformité, d'unicité, de filtrage et de façade publique ; - confirmer l'absence totale de dépendance `solana-sdk-ids` ; -- auditer le graphe/features réels. +- auditer le graphe/features réels après validation Cargo par le user. ### `0.1.1-pre.004` — intégration Core + audits @@ -657,7 +667,6 @@ Un `pre.NNN-fix.NNN` corrige la tranche correspondante sans réécrire son histo ## Questions ouvertes non bloquantes -- Revérifier en `pre.003` la version publiée de `solana-pubkey`, son API compile-time pertinente et le MSRV officiel au jour du code. - Décider en `pre.005`, à partir de l'API réellement stabilisée, si un `README.md`/`USAGE.md` de crate apporte suffisamment de valeur pour être créé maintenant. Aucune de ces questions ne justifie d'élargir le périmètre fonctionnel de `0.1.1`.