167 lines
5.4 KiB
Markdown
167 lines
5.4 KiB
Markdown
<!-- file: crates/ksp-program-api/USAGE.md -->
|
|
<!-- version: 4 -->
|
|
|
|
# Usage de ksp-program-api
|
|
|
|
Cette page décrit la surface publique candidate de `0.2.14`. Utiliser uniquement les exports du crate-root ; aucun module interne ne fait partie du contrat consommable.
|
|
|
|
## Construire un input Program avec la façade
|
|
|
|
```rust
|
|
let program_id = ksp_program_api::Pubkey::new_from_array([1_u8; 32]);
|
|
let account_id = ksp_program_api::Pubkey::new_from_array([2_u8; 32]);
|
|
let account = ksp_program_api::ProgramAccountMeta::readonly(account_id, true);
|
|
|
|
let instruction = ksp_program_api::ProgramInstruction::try_new(
|
|
program_id,
|
|
std::vec![account],
|
|
std::vec![0x01_u8, 0x02, 0x03],
|
|
);
|
|
|
|
assert!(instruction.is_ok());
|
|
```
|
|
|
|
`Pubkey`, `ProgramAccountMeta` et `ProgramInstruction` conservent leur ownership Core/Interface. Les bornes `255` account metas et `10_240` bytes de data sont appliquées par Interface avant l'entrée dans le decoder.
|
|
|
|
## Implémenter un decoder externe
|
|
|
|
Le type décodé reste entièrement possédé par la crate d'implémentation :
|
|
|
|
```rust
|
|
struct ExternalDecodedInstruction {
|
|
opcode: u8,
|
|
}
|
|
|
|
struct ExternalDecoder {
|
|
program_ids: [ksp_program_api::Pubkey; 1],
|
|
}
|
|
|
|
impl ksp_program_api::ProgramInstructionDecoder for ExternalDecoder {
|
|
type Decoded = ExternalDecodedInstruction;
|
|
|
|
fn program_ids(&self) -> &[ksp_program_api::Pubkey] {
|
|
return &self.program_ids;
|
|
}
|
|
|
|
fn recognize(
|
|
&self,
|
|
instruction: &ksp_program_api::ProgramInstruction,
|
|
) -> ksp_program_api::ProgramInstructionRecognition {
|
|
if instruction.program_id() != &self.program_ids[0] {
|
|
return ksp_program_api::ProgramInstructionRecognition::NoMatch;
|
|
}
|
|
|
|
if instruction.data().first() == std::option::Option::Some(&0x2A_u8) {
|
|
return ksp_program_api::ProgramInstructionRecognition::ExactMatch;
|
|
}
|
|
|
|
return ksp_program_api::ProgramInstructionRecognition::ProgramMatch;
|
|
}
|
|
|
|
fn decode(
|
|
&self,
|
|
instruction: &ksp_program_api::ProgramInstruction,
|
|
) -> ksp_program_api::Result<ksp_program_api::ProgramInstructionDecodeOutcome<Self::Decoded>> {
|
|
let opcode = match instruction.data().first() {
|
|
std::option::Option::Some(value) if *value == 0x2A_u8 => *value,
|
|
_ => {
|
|
return std::result::Result::Ok(
|
|
ksp_program_api::ProgramInstructionDecodeOutcome::Unsupported,
|
|
);
|
|
}
|
|
};
|
|
|
|
return std::result::Result::Ok(
|
|
ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(
|
|
ExternalDecodedInstruction { opcode },
|
|
),
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
Aucun `ksp-program-lib`, enum centrale, `Any`, JSON ou codec n'est nécessaire. Le Program ID peut être un `Pubkey` opaque non enregistré par Core.
|
|
|
|
## Sélection explicite
|
|
|
|
La sélection reste distincte du décodage :
|
|
|
|
```rust
|
|
let recognition = ksp_program_api::ProgramInstructionDecoder::recognize(
|
|
&decoder,
|
|
&instruction,
|
|
);
|
|
|
|
match recognition {
|
|
ksp_program_api::ProgramInstructionRecognition::NoMatch => {}
|
|
ksp_program_api::ProgramInstructionRecognition::ProgramMatch => {}
|
|
ksp_program_api::ProgramInstructionRecognition::ExactMatch => {}
|
|
_ => {}
|
|
}
|
|
```
|
|
|
|
L'enum est `#[non_exhaustive]`. `ExactMatch` exprime l'affirmation du decoder. `decode` n'est pas un substitut à `recognize` : il traite une instruction déjà sélectionnée pour cette implémentation.
|
|
|
|
## Outcome et erreur
|
|
|
|
```rust
|
|
let outcome = ksp_program_api::ProgramInstructionDecoder::decode(
|
|
&decoder,
|
|
&instruction,
|
|
)?;
|
|
|
|
match outcome {
|
|
ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(value) => {
|
|
let _opcode = value.opcode;
|
|
}
|
|
ksp_program_api::ProgramInstructionDecodeOutcome::Unsupported => {}
|
|
_ => {}
|
|
}
|
|
```
|
|
|
|
Une erreur réelle est un `Err(ksp_program_api::Error)`. `Unsupported` n'est pas une deuxième forme d'erreur : il indique qu'une instruction reconnue n'est volontairement pas décodée par cette capability.
|
|
|
|
Program API ne recopie automatiquement ni le payload de l'instruction ni les account metas dans l'erreur. Une implémentation externe reste responsable des messages/contextes qu'elle construit explicitement.
|
|
|
|
## Output sans bounds implicites
|
|
|
|
L'associated type `Decoded` n'impose pas `Debug`, `Clone`, `Send` ou `Sync`. Les supertraits `Send + Sync` s'appliquent au decoder lui-même, pas à la valeur décodée :
|
|
|
|
```rust
|
|
struct LocalDecoded(std::rc::Rc<std::cell::Cell<u8>>);
|
|
```
|
|
|
|
Un decoder peut utiliser ce type comme `Decoded` tant que son propre état satisfait `Send + Sync`.
|
|
|
|
## Debug sûr
|
|
|
|
`ProgramInstructionDecodeOutcome<Decoded>` possède un `Debug` volontairement opaque :
|
|
|
|
```rust
|
|
struct SecretDecoded;
|
|
|
|
let outcome = ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(SecretDecoded);
|
|
assert_eq!(std::format!("{outcome:?}"), "Decoded");
|
|
```
|
|
|
|
`SecretDecoded` n'a pas besoin d'implémenter `Debug` et sa valeur n'est jamais rendue par l'outcome.
|
|
|
|
## Ce qui n'est pas simulé côté consumer
|
|
|
|
Il n'existe dans `0.2.14` aucun :
|
|
|
|
```text
|
|
registry de decoders
|
|
composition dyn hétérogène
|
|
identity/version/coverage descriptor
|
|
priority/conflict policy
|
|
payload canonique D3
|
|
ProgramAccountDecoder / Event / ReturnData
|
|
ProgramExecutionPreparer
|
|
execution policy
|
|
serde / JSON / codec
|
|
logging / runtime réseau
|
|
```
|
|
|
|
Ces surfaces ne doivent pas être recréées localement comme si elles faisaient déjà partie du contrat commun. Elles attendent les vertical slices qui justifieront leurs invariants réels.
|