v0.2.13-pre.004
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 305
|
# version: 306
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"]
|
members = ["crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.2.13-pre.3"
|
version = "0.2.13-pre.4"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-interface-lib/src/lib.rs
|
// file: crates/ksp-interface-lib/src/lib.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![deny(unreachable_pub)]
|
#![deny(unreachable_pub)]
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
mod program_account_meta;
|
mod program_account_meta;
|
||||||
|
mod program_instruction;
|
||||||
|
|
||||||
/// Error code used when an Interface-owned Program instruction admission limit is exceeded.
|
/// Error code used when an Interface-owned Program instruction admission limit is exceeded.
|
||||||
pub use self::error::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED;
|
pub use self::error::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED;
|
||||||
@@ -21,5 +22,9 @@ pub use self::error::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED;
|
|||||||
pub use self::program_account_meta::MAX_PROGRAM_INSTRUCTION_ACCOUNTS;
|
pub use self::program_account_meta::MAX_PROGRAM_INSTRUCTION_ACCOUNTS;
|
||||||
/// Passive account metadata attached to one Program instruction.
|
/// Passive account metadata attached to one Program instruction.
|
||||||
pub use self::program_account_meta::ProgramAccountMeta;
|
pub use self::program_account_meta::ProgramAccountMeta;
|
||||||
|
/// Maximum opaque data payload admitted by one passive Program instruction contract.
|
||||||
|
pub use self::program_instruction::MAX_PROGRAM_INSTRUCTION_DATA_LEN;
|
||||||
|
/// Passive, bounded Program instruction wire contract.
|
||||||
|
pub use self::program_instruction::ProgramInstruction;
|
||||||
/// Canonical Solana account address primitive owned by `ksp-core-lib`.
|
/// Canonical Solana account address primitive owned by `ksp-core-lib`.
|
||||||
pub use ksp_core_lib::Pubkey;
|
pub use ksp_core_lib::Pubkey;
|
||||||
|
|||||||
81
crates/ksp-interface-lib/src/program_instruction.rs
Normal file
81
crates/ksp-interface-lib/src/program_instruction.rs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
// file: crates/ksp-interface-lib/src/program_instruction.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
/// Maximum opaque data payload admitted by one passive Program instruction contract.
|
||||||
|
pub const MAX_PROGRAM_INSTRUCTION_DATA_LEN: usize = 10 * 1024;
|
||||||
|
|
||||||
|
/// Passive, bounded Program instruction wire contract.
|
||||||
|
///
|
||||||
|
/// The instruction preserves the caller-provided Program identity, ordered
|
||||||
|
/// account metas and opaque data bytes without applying Program-specific
|
||||||
|
/// semantics. Construction enforces only the Interface-owned admission bounds.
|
||||||
|
pub struct ProgramInstruction {
|
||||||
|
program_id: crate::Pubkey,
|
||||||
|
accounts: std::vec::Vec<crate::ProgramAccountMeta>,
|
||||||
|
data: std::vec::Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProgramInstruction {
|
||||||
|
/// Creates one passive Program instruction after enforcing Interface admission bounds.
|
||||||
|
///
|
||||||
|
/// The provided vectors are consumed directly. Their account order and duplicates
|
||||||
|
/// are preserved exactly when construction succeeds.
|
||||||
|
pub fn try_new(program_id: crate::Pubkey, accounts: std::vec::Vec<crate::ProgramAccountMeta>, data: std::vec::Vec<u8>) -> ksp_core_lib::Result<Self> {
|
||||||
|
if accounts.len() > crate::MAX_PROGRAM_INSTRUCTION_ACCOUNTS {
|
||||||
|
return std::result::Result::Err(
|
||||||
|
ksp_core_lib::Error::new(
|
||||||
|
crate::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED,
|
||||||
|
"Program instruction account count exceeds the Interface admission limit",
|
||||||
|
)
|
||||||
|
.with_context("field", "accounts")
|
||||||
|
.with_context("actual_len", accounts.len().to_string())
|
||||||
|
.with_context("maximum_len", crate::MAX_PROGRAM_INSTRUCTION_ACCOUNTS.to_string()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if data.len() > crate::MAX_PROGRAM_INSTRUCTION_DATA_LEN {
|
||||||
|
return std::result::Result::Err(
|
||||||
|
ksp_core_lib::Error::new(
|
||||||
|
crate::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED,
|
||||||
|
"Program instruction data length exceeds the Interface admission limit",
|
||||||
|
)
|
||||||
|
.with_context("field", "data")
|
||||||
|
.with_context("actual_len", data.len().to_string())
|
||||||
|
.with_context("maximum_len", crate::MAX_PROGRAM_INSTRUCTION_DATA_LEN.to_string()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return std::result::Result::Ok(Self { program_id, accounts, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the ordered account metas exactly as admitted at construction.
|
||||||
|
#[must_use]
|
||||||
|
pub fn accounts(&self) -> &[crate::ProgramAccountMeta] {
|
||||||
|
return self.accounts.as_slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the opaque Program instruction data bytes.
|
||||||
|
#[must_use]
|
||||||
|
pub fn data(&self) -> &[u8] {
|
||||||
|
return self.data.as_slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the Program identity.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn program_id(&self) -> &crate::Pubkey {
|
||||||
|
return &self.program_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for ProgramInstruction {
|
||||||
|
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
return formatter
|
||||||
|
.debug_struct("ProgramInstruction")
|
||||||
|
.field("program_id", &self.program_id)
|
||||||
|
.field("account_count", &self.accounts.len())
|
||||||
|
.field("data_len", &self.data.len())
|
||||||
|
.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "../unit_tests/program_instruction.rs"]
|
||||||
|
mod tests;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-interface-lib/tests/dependency_boundary.rs
|
// file: crates/ksp-interface-lib/tests/dependency_boundary.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
//! Dependency and passive-surface canaries for the Interface foundation.
|
//! Dependency and passive-surface canaries for the Interface foundation.
|
||||||
|
|
||||||
@@ -45,15 +45,20 @@ fn pre_002_manifest_has_exact_core_only_runtime_dependency() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pre_003_surface_remains_passive_without_instruction_or_runtime_logging() {
|
fn pre_004_surface_remains_passive_without_codecs_or_runtime_logging() {
|
||||||
let crate_root = include_str!("../src/lib.rs");
|
let crate_root = include_str!("../src/lib.rs");
|
||||||
assert!(crate_root.contains("ProgramAccountMeta"));
|
assert!(crate_root.contains("ProgramAccountMeta"));
|
||||||
assert!(crate_root.contains("MAX_PROGRAM_INSTRUCTION_ACCOUNTS"));
|
assert!(crate_root.contains("MAX_PROGRAM_INSTRUCTION_ACCOUNTS"));
|
||||||
assert!(crate_root.contains("ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED"));
|
assert!(crate_root.contains("ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED"));
|
||||||
assert!(!crate_root.contains("ProgramInstruction"));
|
assert!(crate_root.contains("ProgramInstruction"));
|
||||||
|
assert!(crate_root.contains("MAX_PROGRAM_INSTRUCTION_DATA_LEN"));
|
||||||
assert!(!crate_root.contains("TRACING_TARGET"));
|
assert!(!crate_root.contains("TRACING_TARGET"));
|
||||||
assert!(!crate_root.contains("ksp_logging_lib"));
|
assert!(!crate_root.contains("ksp_logging_lib"));
|
||||||
assert!(!std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src/constants.rs").exists());
|
assert!(!std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src/constants.rs").exists());
|
||||||
|
let instruction_source = include_str!("../src/program_instruction.rs");
|
||||||
|
for forbidden in ["serde", "borsh", "bincode", "wincode", "solana_instruction", "ksp_logging_lib", "TRACING_TARGET"] {
|
||||||
|
assert!(!instruction_source.contains(forbidden), "forbidden Interface surface detected: {forbidden}");
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-interface-lib/tests/public_api.rs
|
// file: crates/ksp-interface-lib/tests/public_api.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
//! Integration canaries for the public `ksp-interface-lib` foundation.
|
//! Integration canaries for the public `ksp-interface-lib` foundation.
|
||||||
|
|
||||||
@@ -36,3 +36,20 @@ fn public_pre_003_interface_error_code_is_stable_and_core_owned() {
|
|||||||
assert_eq!(code.code(), "program_instruction_limit_exceeded");
|
assert_eq!(code.code(), "program_instruction_limit_exceeded");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn public_pre_004_program_instruction_contract_is_available_from_crate_root() {
|
||||||
|
let program_id = ksp_interface_lib::Pubkey::new_from_array([4_u8; 32]);
|
||||||
|
let account = ksp_interface_lib::ProgramAccountMeta::readonly(ksp_interface_lib::Pubkey::new_from_array([5_u8; 32]), true);
|
||||||
|
let instruction = ksp_interface_lib::ProgramInstruction::try_new(program_id, std::vec![account], std::vec![1_u8, 2, 3]);
|
||||||
|
assert!(instruction.is_ok());
|
||||||
|
let instruction = match instruction {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => return,
|
||||||
|
};
|
||||||
|
assert_eq!(instruction.program_id(), &program_id);
|
||||||
|
assert_eq!(instruction.accounts(), &[account]);
|
||||||
|
assert_eq!(instruction.data(), &[1_u8, 2, 3]);
|
||||||
|
assert_eq!(ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_DATA_LEN, 10_240);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|||||||
107
crates/ksp-interface-lib/unit_tests/program_instruction.rs
Normal file
107
crates/ksp-interface-lib/unit_tests/program_instruction.rs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
// file: crates/ksp-interface-lib/unit_tests/program_instruction.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
fn meta(byte: u8, writable: bool) -> crate::ProgramAccountMeta {
|
||||||
|
let pubkey = crate::Pubkey::new_from_array([byte; 32]);
|
||||||
|
if writable {
|
||||||
|
return crate::ProgramAccountMeta::writable(pubkey, false);
|
||||||
|
}
|
||||||
|
return crate::ProgramAccountMeta::readonly(pubkey, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn instruction_accepts_empty_accounts_and_data_with_opaque_program_id() {
|
||||||
|
let program_id = crate::Pubkey::new_from_array([0xD3_u8; 32]);
|
||||||
|
let instruction = crate::ProgramInstruction::try_new(program_id, std::vec::Vec::new(), std::vec::Vec::new());
|
||||||
|
assert!(instruction.is_ok());
|
||||||
|
let instruction = match instruction {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => return,
|
||||||
|
};
|
||||||
|
assert_eq!(instruction.program_id(), &program_id);
|
||||||
|
assert!(instruction.accounts().is_empty());
|
||||||
|
assert!(instruction.data().is_empty());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn instruction_preserves_account_order_duplicates_and_opaque_data() {
|
||||||
|
let program_id = crate::Pubkey::new_from_array([0x41_u8; 32]);
|
||||||
|
let first = meta(1, false);
|
||||||
|
let duplicate = meta(2, true);
|
||||||
|
let accounts = std::vec![first, duplicate, first];
|
||||||
|
let data = std::vec![0_u8, 1, 2, 0xFF];
|
||||||
|
let instruction = crate::ProgramInstruction::try_new(program_id, accounts, data.clone());
|
||||||
|
assert!(instruction.is_ok());
|
||||||
|
let instruction = match instruction {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => return,
|
||||||
|
};
|
||||||
|
assert_eq!(instruction.accounts(), &[first, duplicate, first]);
|
||||||
|
assert_eq!(instruction.data(), data.as_slice());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn instruction_accepts_account_limit_and_rejects_one_above_it() {
|
||||||
|
let program_id = crate::Pubkey::new_from_array([0x51_u8; 32]);
|
||||||
|
let account = meta(7, false);
|
||||||
|
let accepted = crate::ProgramInstruction::try_new(program_id, std::vec![account; crate::MAX_PROGRAM_INSTRUCTION_ACCOUNTS], std::vec::Vec::new());
|
||||||
|
assert!(accepted.is_ok());
|
||||||
|
let rejected = crate::ProgramInstruction::try_new(program_id, std::vec![account; crate::MAX_PROGRAM_INSTRUCTION_ACCOUNTS + 1], std::vec::Vec::new());
|
||||||
|
assert!(rejected.is_err());
|
||||||
|
let error = match rejected {
|
||||||
|
std::result::Result::Err(value) => value,
|
||||||
|
std::result::Result::Ok(_) => return,
|
||||||
|
};
|
||||||
|
assert_eq!(error.code(), crate::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED);
|
||||||
|
assert_eq!(error.message(), "Program instruction account count exceeds the Interface admission limit");
|
||||||
|
assert_eq!(error.context().len(), 3);
|
||||||
|
assert_eq!(error.context()[0].key(), "field");
|
||||||
|
assert_eq!(error.context()[0].value(), "accounts");
|
||||||
|
assert_eq!(error.context()[1].key(), "actual_len");
|
||||||
|
assert_eq!(error.context()[1].value(), "256");
|
||||||
|
assert_eq!(error.context()[2].key(), "maximum_len");
|
||||||
|
assert_eq!(error.context()[2].value(), "255");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn instruction_accepts_data_limit_and_rejects_one_byte_above_it() {
|
||||||
|
let program_id = crate::Pubkey::new_from_array([0x61_u8; 32]);
|
||||||
|
let accepted = crate::ProgramInstruction::try_new(program_id, std::vec::Vec::new(), std::vec![0xA5_u8; crate::MAX_PROGRAM_INSTRUCTION_DATA_LEN]);
|
||||||
|
assert!(accepted.is_ok());
|
||||||
|
let rejected = crate::ProgramInstruction::try_new(program_id, std::vec::Vec::new(), std::vec![0x5A_u8; crate::MAX_PROGRAM_INSTRUCTION_DATA_LEN + 1]);
|
||||||
|
assert!(rejected.is_err());
|
||||||
|
let error = match rejected {
|
||||||
|
std::result::Result::Err(value) => value,
|
||||||
|
std::result::Result::Ok(_) => return,
|
||||||
|
};
|
||||||
|
assert_eq!(error.code(), crate::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED);
|
||||||
|
assert_eq!(error.message(), "Program instruction data length exceeds the Interface admission limit");
|
||||||
|
assert_eq!(error.context().len(), 3);
|
||||||
|
assert_eq!(error.context()[0].value(), "data");
|
||||||
|
assert_eq!(error.context()[1].value(), "10241");
|
||||||
|
assert_eq!(error.context()[2].value(), "10240");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn instruction_debug_is_bounded_and_omits_accounts_and_payload_bytes() {
|
||||||
|
let program_id = crate::Pubkey::new_from_array([0x71_u8; 32]);
|
||||||
|
let payload = b"PAYLOAD_SENTINEL_NEVER_RENDER".to_vec();
|
||||||
|
let instruction = crate::ProgramInstruction::try_new(program_id, std::vec![meta(8, false), meta(9, true)], payload);
|
||||||
|
assert!(instruction.is_ok());
|
||||||
|
let instruction = match instruction {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => return,
|
||||||
|
};
|
||||||
|
let rendered = std::format!("{instruction:?}");
|
||||||
|
assert!(rendered.contains("ProgramInstruction"));
|
||||||
|
assert!(rendered.contains("account_count: 2"));
|
||||||
|
assert!(rendered.contains("data_len: 29"));
|
||||||
|
assert!(!rendered.contains("PAYLOAD_SENTINEL_NEVER_RENDER"));
|
||||||
|
assert!(!rendered.contains("is_signer"));
|
||||||
|
assert!(!rendered.contains("is_writable"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
295
deltas/0.2.13/pre.004.md
Normal file
295
deltas/0.2.13/pre.004.md
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
<!-- file: deltas/0.2.13/pre.004.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta `0.2.13-pre.004` — `ProgramInstruction` passif borné
|
||||||
|
|
||||||
|
## 1. Base requise
|
||||||
|
|
||||||
|
Cette tranche s'applique exclusivement sur :
|
||||||
|
|
||||||
|
```text
|
||||||
|
v0.2.12
|
||||||
|
+ 0.2.13-pre.001
|
||||||
|
+ 0.2.13-pre.002
|
||||||
|
+ 0.2.13-pre.003
|
||||||
|
```
|
||||||
|
|
||||||
|
La preuve opérateur fournie pour `pre.003` confirme :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo fmt --all PASS
|
||||||
|
python3 scripts/audit_rust_workspace_rules.py PASS
|
||||||
|
python3 scripts/audit_markdown_tables.py ... PASS
|
||||||
|
cargo check --workspace PASS
|
||||||
|
cargo clippy --workspace --all-targets PASS
|
||||||
|
cargo test -p ksp-interface-lib PASS
|
||||||
|
cargo tree -p ksp-interface-lib --edges normal inspecté
|
||||||
|
cargo tree --duplicates inspecté
|
||||||
|
```
|
||||||
|
|
||||||
|
`cargo test --workspace` n'apparaît pas dans cette preuve opérateur. La présente préparation n'en invente donc pas le résultat ; ce test devra être rejoué avec le gate de `pre.004` avant de déclarer la continuité workspace complète.
|
||||||
|
|
||||||
|
Le graphe ciblé reste strictement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
ksp-interface-lib
|
||||||
|
└── ksp-core-lib
|
||||||
|
└── solana-pubkey 4.3.0
|
||||||
|
```
|
||||||
|
|
||||||
|
La version workspace passe de :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.2.13-pre.3
|
||||||
|
```
|
||||||
|
|
||||||
|
à :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.2.13-pre.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Objectif
|
||||||
|
|
||||||
|
Matérialiser le second type du premier lot wire générique décidé en `pre.001` : une instruction Program passive `{ program_id, accounts, data }`, bornée à l'admission et sans comportement Program.
|
||||||
|
|
||||||
|
La tranche ajoute :
|
||||||
|
|
||||||
|
```text
|
||||||
|
ProgramInstruction
|
||||||
|
MAX_PROGRAM_INSTRUCTION_DATA_LEN = 10_240
|
||||||
|
ProgramInstruction::try_new(...)
|
||||||
|
accessors program_id/accounts/data
|
||||||
|
Debug résumé
|
||||||
|
unit/public canaries de limites, ordre et doublons
|
||||||
|
```
|
||||||
|
|
||||||
|
Elle n'ajoute pas :
|
||||||
|
|
||||||
|
```text
|
||||||
|
serde / serde_json
|
||||||
|
borsh / bincode / wincode
|
||||||
|
solana-instruction
|
||||||
|
codec ou discriminant Program spécifique
|
||||||
|
Program API / Program behavior
|
||||||
|
Transport / Store / Config / Wallet
|
||||||
|
ksp-logging-lib / constants.rs / TRACING_TARGET
|
||||||
|
runtime réseau
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. `ProgramInstruction`
|
||||||
|
|
||||||
|
Le type possède trois champs privés :
|
||||||
|
|
||||||
|
```text
|
||||||
|
program_id : Pubkey
|
||||||
|
accounts : Vec<ProgramAccountMeta>
|
||||||
|
data : Vec<u8>
|
||||||
|
```
|
||||||
|
|
||||||
|
Le constructeur public est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
ProgramInstruction::try_new(program_id, accounts, data)
|
||||||
|
-> ksp_core_lib::Result<ProgramInstruction>
|
||||||
|
```
|
||||||
|
|
||||||
|
Les accessors publics sont :
|
||||||
|
|
||||||
|
```text
|
||||||
|
program_id() -> &Pubkey
|
||||||
|
accounts() -> &[ProgramAccountMeta]
|
||||||
|
data() -> &[u8]
|
||||||
|
```
|
||||||
|
|
||||||
|
La construction consomme directement les `Vec` fournis. Aucun clone interne, conversion vers `solana-instruction` ou sérialisation n'est ajouté.
|
||||||
|
|
||||||
|
## 4. Bornes d'admission
|
||||||
|
|
||||||
|
Les deux plafonds sont désormais matérialisés :
|
||||||
|
|
||||||
|
```text
|
||||||
|
MAX_PROGRAM_INSTRUCTION_ACCOUNTS = 255
|
||||||
|
MAX_PROGRAM_INSTRUCTION_DATA_LEN = 10_240
|
||||||
|
```
|
||||||
|
|
||||||
|
`try_new` vérifie en `usize` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
accounts.len() <= 255
|
||||||
|
data.len() <= 10_240
|
||||||
|
```
|
||||||
|
|
||||||
|
Canaris exacts :
|
||||||
|
|
||||||
|
```text
|
||||||
|
255 accounts accepté
|
||||||
|
256 accounts refusé
|
||||||
|
10_240 octets data accepté
|
||||||
|
10_241 octets data refusé
|
||||||
|
```
|
||||||
|
|
||||||
|
Ces valeurs restent des bornes d'admission Interface fondées sur les plafonds Solana CPI audités en `pre.001`. Elles ne garantissent pas qu'une instruction rentre dans une transaction top-level complète.
|
||||||
|
|
||||||
|
## 5. Sémantique passive
|
||||||
|
|
||||||
|
Restent valides :
|
||||||
|
|
||||||
|
```text
|
||||||
|
accounts vides
|
||||||
|
data vide
|
||||||
|
Program Pubkey inconnu du registry Core
|
||||||
|
doublons d'accounts
|
||||||
|
ordre arbitraire des account metas
|
||||||
|
payload opaque quelconque sous la borne
|
||||||
|
```
|
||||||
|
|
||||||
|
Interface n'ajoute aucune politique signer/writable, aucun lookup de Program ID, aucune déduplication et aucune interprétation du payload.
|
||||||
|
|
||||||
|
## 6. Erreurs
|
||||||
|
|
||||||
|
Les deux violations de borne réutilisent :
|
||||||
|
|
||||||
|
```text
|
||||||
|
interface.program_instruction_limit_exceeded
|
||||||
|
```
|
||||||
|
|
||||||
|
via `ksp_core_lib::Error/Result`.
|
||||||
|
|
||||||
|
Le contexte d'erreur est limité à :
|
||||||
|
|
||||||
|
```text
|
||||||
|
field
|
||||||
|
actual_len
|
||||||
|
maximum_len
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucun octet `data`, aucun account arbitraire et aucun payload externe n'est copié dans l'erreur.
|
||||||
|
|
||||||
|
Le code conceptuel `invalid_program_instruction` reste absent : la foundation ne possède toujours aucun invariant de forme distinct des deux limites.
|
||||||
|
|
||||||
|
## 7. Debug borné
|
||||||
|
|
||||||
|
`ProgramInstruction` n'utilise pas un `derive(Debug)` qui imprimerait les deux `Vec`.
|
||||||
|
|
||||||
|
Son `Debug` manuel expose uniquement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
program_id
|
||||||
|
account_count
|
||||||
|
data_len
|
||||||
|
```
|
||||||
|
|
||||||
|
Les account metas et le payload sont volontairement absents du rendu.
|
||||||
|
|
||||||
|
## 8. Logging
|
||||||
|
|
||||||
|
La décision de `pre.002` reste inchangée : cette crate est passive.
|
||||||
|
|
||||||
|
Restent absents :
|
||||||
|
|
||||||
|
```text
|
||||||
|
ksp-logging-lib
|
||||||
|
src/constants.rs
|
||||||
|
TRACING_TARGET
|
||||||
|
tracing direct
|
||||||
|
```
|
||||||
|
|
||||||
|
Une erreur de validation de borne est une valeur retournée au caller, pas un événement runtime à logger depuis Interface.
|
||||||
|
|
||||||
|
## 9. Tests
|
||||||
|
|
||||||
|
Les unit tests de `ProgramInstruction` couvrent :
|
||||||
|
|
||||||
|
```text
|
||||||
|
accounts/data vides acceptés
|
||||||
|
Program Pubkey opaque accepté
|
||||||
|
ordre et doublons préservés
|
||||||
|
payload opaque préservé
|
||||||
|
255 / 256 accounts
|
||||||
|
10_240 / 10_241 octets data
|
||||||
|
code/message/contexte d'erreur sûr
|
||||||
|
Debug résumé sans payload ni account flags
|
||||||
|
```
|
||||||
|
|
||||||
|
`tests/public_api.rs` ajoute un canari de consommation crate-root de `ProgramInstruction` et de `MAX_PROGRAM_INSTRUCTION_DATA_LEN`.
|
||||||
|
|
||||||
|
`tests/dependency_boundary.rs` est avancé à `pre.004` et confirme toujours :
|
||||||
|
|
||||||
|
```text
|
||||||
|
ksp-core-lib seule dépendance normale
|
||||||
|
aucun serde/borsh/bincode/wincode
|
||||||
|
aucun solana-instruction
|
||||||
|
aucun logging/runtime
|
||||||
|
ProgramInstruction présent via façade explicite
|
||||||
|
```
|
||||||
|
|
||||||
|
## 10. Fichiers ajoutés
|
||||||
|
|
||||||
|
```text
|
||||||
|
crates/ksp-interface-lib/src/program_instruction.rs
|
||||||
|
crates/ksp-interface-lib/unit_tests/program_instruction.rs
|
||||||
|
deltas/0.2.13/pre.004.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 11. Fichiers modifiés
|
||||||
|
|
||||||
|
```text
|
||||||
|
Cargo.toml
|
||||||
|
crates/ksp-interface-lib/src/lib.rs
|
||||||
|
crates/ksp-interface-lib/tests/dependency_boundary.rs
|
||||||
|
crates/ksp-interface-lib/tests/public_api.rs
|
||||||
|
docs/plans/020-V0_2_13_INTERFACE_PLAN.md
|
||||||
|
docs/validation/016-V0_2_13_INTERFACE.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 12. Fichiers volontairement inchangés
|
||||||
|
|
||||||
|
```text
|
||||||
|
README.md
|
||||||
|
ROADMAP.md
|
||||||
|
CHANGELOG.md
|
||||||
|
.env.example
|
||||||
|
crates/ksp-interface-lib/Cargo.toml
|
||||||
|
crates/ksp-interface-lib/README.md
|
||||||
|
crates/ksp-interface-lib/USAGE.md
|
||||||
|
crates/ksp-interface-lib/src/error.rs
|
||||||
|
crates/ksp-interface-lib/src/program_account_meta.rs
|
||||||
|
crates/ksp-interface-lib/unit_tests/program_account_meta.rs
|
||||||
|
docs/architecture/**
|
||||||
|
prompts/**
|
||||||
|
config/**
|
||||||
|
```
|
||||||
|
|
||||||
|
## 13. Gate attendu
|
||||||
|
|
||||||
|
Après application de l'overlay :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fmt --all
|
||||||
|
python3 scripts/audit_rust_workspace_rules.py
|
||||||
|
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.13
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets
|
||||||
|
cargo test -p ksp-interface-lib
|
||||||
|
cargo test --workspace
|
||||||
|
cargo tree -p ksp-interface-lib --edges normal
|
||||||
|
cargo tree --duplicates
|
||||||
|
```
|
||||||
|
|
||||||
|
Le `cargo test --workspace` est particulièrement requis ici puisqu'il n'était pas présent dans la preuve opérateur fournie pour `pre.003`.
|
||||||
|
|
||||||
|
## 14. Suite autorisée
|
||||||
|
|
||||||
|
Après gate vert, `pre.005` doit rester une tranche de hardening :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cas adversariaux complémentaires
|
||||||
|
consumer externe
|
||||||
|
surface crate-root
|
||||||
|
firewall source/manifest
|
||||||
|
graphes Cargo
|
||||||
|
canari de complétude du premier lot
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucun second domaine wire, codec générique ou comportement Program ne doit être ajouté opportunément.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/plans/020-V0_2_13_INTERFACE_PLAN.md -->
|
<!-- file: docs/plans/020-V0_2_13_INTERFACE_PLAN.md -->
|
||||||
<!-- version: 3 -->
|
<!-- version: 4 -->
|
||||||
|
|
||||||
# Plan `0.2.13` — Interface / wire foundation
|
# Plan `0.2.13` — Interface / wire foundation
|
||||||
|
|
||||||
@@ -389,13 +389,13 @@ Créer la crate, l'ajouter au workspace, poser `lib.rs` avec exports explicites,
|
|||||||
|
|
||||||
### pre.003 — `ProgramAccountMeta` + bornes communes
|
### pre.003 — `ProgramAccountMeta` + bornes communes
|
||||||
|
|
||||||
**Statut : réalisé ; gate opérateur à confirmer.**
|
**Statut : réalisé ; fmt/audits/check/Clippy/tests Interface PASS, `cargo test --workspace` non fourni dans la preuve opérateur.**
|
||||||
|
|
||||||
Matérialiser la primitive de compte ordonné, la borne accounts, le modèle d'erreur Interface minimal et les unit/public canaries associés. `ProgramInstruction` et la borne data restent réservés à `pre.004`.
|
Matérialiser la primitive de compte ordonné, la borne accounts, le modèle d'erreur Interface minimal et les unit/public canaries associés. `ProgramInstruction` et la borne data restent réservés à `pre.004`.
|
||||||
|
|
||||||
### pre.004 — `ProgramInstruction` passif borné
|
### pre.004 — `ProgramInstruction` passif borné
|
||||||
|
|
||||||
**Statut : prévu**
|
**Statut : réalisé ; gate opérateur à confirmer.**
|
||||||
|
|
||||||
Ajouter l'instruction `{ program_id, accounts, data }`, constructeur validé, accessors, Debug résumé et canaris d'ordre/doublons/limites. Aucun serde/codec ou comportement Program.
|
Ajouter l'instruction `{ program_id, accounts, data }`, constructeur validé, accessors, Debug résumé et canaris d'ordre/doublons/limites. Aucun serde/codec ou comportement Program.
|
||||||
|
|
||||||
@@ -502,4 +502,36 @@ La constante `255` ferme le contrat public de borne accounts mais son enforcemen
|
|||||||
|
|
||||||
Une seule catégorie d'erreur Interface est introduite. `invalid_program_instruction` reste absent : aucun invariant de forme distinct des limites n'est encore matérialisé et le créer par symétrie serait prématuré. Les futures erreurs de borne doivent utiliser `ksp_core_lib::Error/Result` et ne projeter que des longueurs/plafonds sûrs.
|
Une seule catégorie d'erreur Interface est introduite. `invalid_program_instruction` reste absent : aucun invariant de forme distinct des limites n'est encore matérialisé et le créer par symétrie serait prématuré. Les futures erreurs de borne doivent utiliser `ksp_core_lib::Error/Result` et ne projeter que des longueurs/plafonds sûrs.
|
||||||
|
|
||||||
Le passage à `pre.004` reste conditionné au gate opérateur complet sur l'overlay `pre.003`.
|
La preuve opérateur fournie pour `pre.003` confirme fmt, audits, `cargo check`, Clippy, tests ciblés Interface et graphes Cargo. `cargo test --workspace` n'apparaît pas dans cette preuve ; il doit donc être rejoué au gate de `pre.004` avant de déclarer la continuité workspace complète.
|
||||||
|
|
||||||
|
## 18. État préparé `pre.004`
|
||||||
|
|
||||||
|
`pre.004` matérialise le second et dernier type du premier lot wire générique retenu :
|
||||||
|
|
||||||
|
```text
|
||||||
|
ProgramInstruction présent
|
||||||
|
fields privés
|
||||||
|
program_id Pubkey Core
|
||||||
|
accounts Vec<ProgramAccountMeta> consommé
|
||||||
|
data Vec<u8> opaque consommé
|
||||||
|
MAX_PROGRAM_INSTRUCTION_ACCOUNTS 255
|
||||||
|
MAX_PROGRAM_INSTRUCTION_DATA_LEN 10_240
|
||||||
|
try_new bornes accounts puis data
|
||||||
|
accounts 255 / 256 accepté / refusé
|
||||||
|
data 10_240 / 10_241 accepté / refusé
|
||||||
|
accounts/data vides acceptés
|
||||||
|
ordre/doublons préservés
|
||||||
|
Program Pubkey non registry accepté
|
||||||
|
Debug program_id/account_count/data_len uniquement
|
||||||
|
serde / borsh / bincode / wincode absents
|
||||||
|
solana-instruction absent
|
||||||
|
ksp-logging-lib / constants.rs / TRACING_TARGET absents
|
||||||
|
```
|
||||||
|
|
||||||
|
`ProgramInstruction::try_new` consomme les deux `Vec` fournis et compare leurs longueurs en `usize` avant stockage dans l'objet. La foundation n'ajoute aucun clone, cast étroit ou allocation de conversion. Une allocation éventuellement déjà réalisée par l'appelant pour construire le `Vec` reste naturellement hors du contrôle de l'API.
|
||||||
|
|
||||||
|
Les deux limites utilisent le code commun `interface.program_instruction_limit_exceeded`. Le contexte d'erreur est volontairement réduit à `field`, `actual_len` et `maximum_len`; aucune donnée du payload ni aucun account arbitraire n'est recopié dans le diagnostic.
|
||||||
|
|
||||||
|
Le `Debug` manuel n'imprime ni la collection accounts ni les octets `data`. Il expose uniquement l'identité publique du programme et les deux longueurs structurelles utiles au diagnostic.
|
||||||
|
|
||||||
|
Le passage à `pre.005` doit désormais se concentrer sur les canaris adversariaux/consumer externe/API/dependency hardening, sans ajouter un second domaine wire.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/validation/016-V0_2_13_INTERFACE.md -->
|
<!-- file: docs/validation/016-V0_2_13_INTERFACE.md -->
|
||||||
<!-- version: 3 -->
|
<!-- version: 4 -->
|
||||||
|
|
||||||
# Validation `0.2.13` — Interface / wire foundation
|
# Validation `0.2.13` — Interface / wire foundation
|
||||||
|
|
||||||
@@ -142,10 +142,10 @@ Si une nouvelle dépendance externe apparaît après `pre.001`, la présente mat
|
|||||||
|
|
||||||
| Famille | Attendu | Statut |
|
| Famille | Attendu | Statut |
|
||||||
|--------------------------|-------------------------------------------------|--------------|
|
|--------------------------|-------------------------------------------------|--------------|
|
||||||
| unit tests privés | account meta/borne puis instruction/adversarial | PARTIEL |
|
| unit tests privés | account meta/borne puis instruction/adversarial | PASS pre.004 |
|
||||||
| `tests/public_api.rs` | consommation crate-root uniquement | PASS pre.003 |
|
| `tests/public_api.rs` | consommation crate-root uniquement | PASS pre.004 |
|
||||||
| external consumer canary | surface utilisable hors modules privés | TODO |
|
| external consumer canary | surface utilisable hors modules privés | TODO |
|
||||||
| dependency boundary | firewall exact | PASS pre.002 |
|
| dependency boundary | firewall exact | PASS pre.004 |
|
||||||
| release completeness | inventaire exact, aucun domaine supplémentaire | TODO |
|
| release completeness | inventaire exact, aucun domaine supplémentaire | TODO |
|
||||||
| round-trip codec | aucun tant qu'aucun codec | N/A |
|
| round-trip codec | aucun tant qu'aucun codec | N/A |
|
||||||
| network smoke | aucun pour crate wire pure | N/A |
|
| network smoke | aucun pour crate wire pure | N/A |
|
||||||
@@ -234,3 +234,28 @@ Le passage à `pre.003` reste conditionné au gate opérateur complet sur l'over
|
|||||||
| dépendance normale | `ksp-core-lib` uniquement |
|
| dépendance normale | `ksp-core-lib` uniquement |
|
||||||
|
|
||||||
Le statut `PASS/PENDING` de la borne accounts signifie que la valeur publique est désormais figée à `255`, tandis que le rejet concret de `256` comptes sera testé lorsque la collection existera dans `ProgramInstruction`. Aucun faux wrapper de collection n'est créé dans `pre.003`.
|
Le statut `PASS/PENDING` de la borne accounts signifie que la valeur publique est désormais figée à `255`, tandis que le rejet concret de `256` comptes sera testé lorsque la collection existera dans `ProgramInstruction`. Aucun faux wrapper de collection n'est créé dans `pre.003`.
|
||||||
|
|
||||||
|
## 15. État préparé `pre.004`
|
||||||
|
|
||||||
|
| Critère | Statut |
|
||||||
|
|------------------------------------------------------|----------------------------------------------|
|
||||||
|
| `ProgramInstruction` crate-root | PASS structurel |
|
||||||
|
| champs privés | PASS |
|
||||||
|
| constructeur `try_new` | PASS |
|
||||||
|
| accessors `program_id/accounts/data` | PASS |
|
||||||
|
| `MAX_PROGRAM_INSTRUCTION_DATA_LEN == 10_240` | PASS unit + public API |
|
||||||
|
| accounts `255` accepté / `256` refusé | PASS unit |
|
||||||
|
| data `10_240` accepté / `10_241` refusé | PASS unit |
|
||||||
|
| accounts/data vides | PASS unit |
|
||||||
|
| ordre + doublons accounts | PASS unit |
|
||||||
|
| Program Pubkey opaque/non registry | PASS unit |
|
||||||
|
| erreur borne Core commune | PASS |
|
||||||
|
| contexte erreur limité aux longueurs/plafonds sûrs | PASS unit |
|
||||||
|
| Debug résumé sans accounts/payload | PASS unit |
|
||||||
|
| serde/codecs | ABSENTS |
|
||||||
|
| `solana-instruction` | ABSENT |
|
||||||
|
| logging/runtime | ABSENTS |
|
||||||
|
| dépendance normale | `ksp-core-lib` uniquement |
|
||||||
|
| `cargo test --workspace` sur preuve entrée `pre.003` | NON FOURNI — à rejouer au gate opérateur 004 |
|
||||||
|
|
||||||
|
La tranche ferme ainsi le premier lot générique `{ program_id, accounts, data }` sans ajouter de codec ni de comportement Program. Le hardening `pre.005` doit porter sur l'usage externe, les cas adversariaux et le firewall, pas sur une nouvelle famille wire.
|
||||||
|
|||||||
Reference in New Issue
Block a user