v0.4.8-pre.008

This commit is contained in:
2026-08-06 06:12:54 +02:00
parent 44088c71c9
commit f576fbade2
52 changed files with 3592 additions and 420 deletions

View File

@@ -1,8 +1,17 @@
<!-- file: kb-pipeline/CHANGELOG.md -->
<!-- version: 15 -->
<!-- version: 17 -->
# CHANGELOG — kb-pipeline
## 0.4.8-pre.008 — orchestration Solana Program Metadata généraliste
- ajoute les lectures de comptes `Buffer` et `Metadata` bornées, avec décodage et projection autoritative délégués à `kb-lib` ;
- ajoute le préflight stateful des neuf opérations stables, les preuves de préfinancement rent et lévaluation commune par `ExSafetyChecker` ;
- ajoute la readiness simulation-first, la résolution des signers et les postconditions confirmées sans transformer une absence de lecture en succès ;
- publie la matrice `METADATA_SOLANA_PROGRAM_PIPELINE_MATRIX.json` et maintient une frontière stricte avec les fixtures et scénarios Devnet de `kb-pipeline-demo-scenarios` ;
- renomme les modules internes selon la hiérarchie de domaine : `metadata_*` pour Metaplex et Solana Program Metadata, `spl_*` pour ATA, ElGamal Registry, Token et Token-2022, tout en conservant les fonctions publiques existantes ;
- remplace les assertions runtime sur constantes du test dAPI externe par des assertions `const`, afin de rester propre sous Clippy.
## 0.4.7-pre.016 — clôture documentaire Metaplex
- aligne la documentation du pipeline sur la qualification finale des preuves réseau ;

View File

@@ -1,5 +1,5 @@
<!-- file: kb-pipeline/README.md -->
<!-- version: 5 -->
<!-- version: 7 -->
# kb-pipeline
@@ -15,26 +15,34 @@
- lectures stateful et corrélations ;
- préflights dexécution ;
- résolution des signers ;
- orchestration simulation-first, confirmation, soumission et postvalidation ;
- orchestration simulation-first, contrôles de readiness et postvalidation ;
- diagnostics stables exploitables par des applications ou scénarios.
Pour Metaplex Token Metadata, la crate expose les lectures stateful bornées, le préflight, la validation de readiness et lagrégation des postconditions. Elle ne contient ni fixture Devnet, ni wallet de démonstration, ni séquence métier spécifique à un test.
Pour Metaplex Token Metadata, la crate expose les lectures stateful bornées, le préflight, la validation de readiness et lagrégation des postconditions.
Pour Solana Program Metadata, elle expose les mêmes frontières réutilisables pour les neuf opérations stables : lecture `Buffer`/`Metadata`, préflight stateful, gouvernance `ExSafetyChecker`, readiness simulation-first et postconditions. Elle ne contient ni fixture Devnet, ni wallet de démonstration, ni séquence métier spécifique à un test.
## Surface publique principale
- campagnes de backfill et progression ;
- extraction Core et decode replay ;
- inspections stateful Solana Core et SPL ;
- `read_metaplex_token_metadata_stateful_account` ;
- `read_metaplex_token_metadata_stateful_snapshot` ;
- `inspect_metaplex_token_metadata_preflight` ;
- `validate_metaplex_token_metadata_execution_readiness` ;
- `summarize_metaplex_token_metadata_postconditions`.
- `summarize_metaplex_token_metadata_postconditions` ;
- `read_solana_program_metadata_stateful_snapshot` ;
- `inspect_solana_program_metadata_preflight` ;
- `validate_solana_program_metadata_execution_readiness` ;
- `inspect_solana_program_metadata_post_execution`.
Les fichiers internes suivent la même nomenclature que `kb-lib` : préfixe `metadata_` pour les programmes metadata, préfixe `spl_` pour les programmes SPL et préfixe `solana_` uniquement pour les contrats Solana Core transversaux. Les noms des fonctions publiques sont conservés pour éviter une rupture dAPI.
Voir [USAGE.md](USAGE.md) pour les exemples et invariants.
## Relations avec le workspace
La crate coordonne `kb-onchain-transport`, `kb-store`, `kb-lib`, `kb-core` et `kb-program-ids`. Elle ne dépend jamais de `kb-pipeline-demo-scenarios` ni de `kb-app-demo-desktop`.
La crate coordonne `kb-onchain-transport`, `kb-store`, `kb-lib`, `kb-core` et `kb-program-ids`. Elle ne dépend jamais de `kb-pipeline-demo-scenarios` ni de `kb-app-demo-desktop`. Les fixtures, wallets temporaires et appels RPC de démonstration appartiennent aux scénarios ou aux applications qui consomment ces contrats.
## Documentation

View File

@@ -1,8 +1,14 @@
<!-- file: kb-pipeline/TODO.md -->
<!-- version: 11 -->
<!-- version: 12 -->
# TODO — kb-pipeline
## Validation réseau Solana Program Metadata
- [ ] `0.4.8-pre.009` - valider les neuf opérations sur Devnet depuis `kb-pipeline-demo-scenarios`, avec fixture native, simulation, soumission confirmée, lectures avant/après et replay.
- [ ] `0.4.8-pre.009` - vérifier depuis `demo_execution_metadata` que les scénarios desktop consomment les mêmes runners réutilisables sans dupliquer la logique du pipeline.
- [ ] Validation historique - exécuter `demo_backfill`, Core extraction et decode replay sur des signatures `ProgM6…` représentatives, puis contrôler les sorties matérialisées.
## Réaudit ultérieur des surfaces historiques
- [ ] Version à déterminer - après le réaudit des exécuteurs Solana Core et SPL dans `kb-lib`, vérifier que le pipeline orchestre sans filtrage implicite les opérations courantes et les opérations obsolètes explicitement approuvées.

View File

@@ -1,5 +1,5 @@
<!-- file: kb-pipeline/USAGE.md -->
<!-- version: 6 -->
<!-- version: 8 -->
# Utilisation de kb-pipeline
@@ -152,6 +152,57 @@ where
Les rapports stateful ne constituent jamais une autorisation implicite dexécution.
## Nomenclature interne des modules
Les modules privés sont organisés par domaine :
- `metadata_metaplex_token_metadata_*` ;
- `metadata_solana_program_*` ;
- `spl_ata_stateful` et `spl_elgamal_registry_stateful` ;
- `spl_token_stateful` et `spl_token_2022_*` ;
- `solana_stateful` uniquement pour les primitives Solana Core partagées.
Cette réorganisation ne renomme pas les fonctions et structures publiques existantes.
## Valider historiquement Solana Program Metadata
La validation historique reste un enchaînement de surfaces généralistes, pas un scénario Devnet :
1. `demo_backfill` hydrate un échantillon borné de signatures confirmées ;
2. lextraction Core résout les instructions et comptes ;
3. `demo_decode_replay` sélectionne le Program ID `ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S` et le décodeur `metadata.solana_program_metadata` ;
4. `materializeAfterDecode` active `materializer.metadata.solana_program_metadata` ;
5. la requête bornée des événements matérialisés permet de vérifier les faits produits.
Le registre générique du desktop fournit désormais ce décodeur et ce matérialiseur. `kb-pipeline-demo-scenarios` nintervient pas dans ce parcours historique.
## Lire et prévalider Solana Program Metadata
```rust
async fn read_program_metadata(
pool: &kb_onchain_transport::HttpEndpointPool,
account: kb_lib::MdPubkey,
) -> kb_core::Result<kb_pipeline::SolanaProgramMetadataStatefulReadResult> {
let request = kb_pipeline::SolanaProgramMetadataStatefulReadRequest {
query_role: "rpc".to_string(),
account,
expected_state: kb_pipeline::SolanaProgramMetadataExpectedAccountState::Any,
min_context_slot: std::option::Option::None,
max_data_bytes: kb_pipeline::MAX_SOLANA_PROGRAM_METADATA_STATEFUL_ACCOUNT_BYTES,
};
let result = kb_pipeline::read_solana_program_metadata_stateful_snapshot(pool, &request).await;
return match result {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(error) => std::result::Result::Err(error),
};
}
```
Le préflight reçoit lintent typé, le plan produit par `ExMetadataSolanaProgramMetadataExecutor`, les snapshots confirmés et, pour les opérations qui allouent ou agrandissent un compte, une observation explicite du minimum de rent. Il refuse un plan `Deny`, mais les opérations dangereuses restent exécutables lorsquelles sont explicitement approuvées et que lenveloppe commune de sécurité est satisfaite.
Après simulation du message exact, `validate_solana_program_metadata_execution_readiness` vérifie les signers et lautorisation de soumission. Après confirmation, `inspect_solana_program_metadata_post_execution` compare les lectures avant/après pour les neuf opérations.
## Observateurs
Les campagnes longues exposent des traits dobservation distincts pour le backfill, lextraction Core et le replay. Lobservateur peut publier la progression et participer à lannulation coopérative selon le contrat concerné.

View File

@@ -1,5 +1,5 @@
// file: kb-pipeline/src/lib.rs
// version: 18
// version: 20
#![forbid(unsafe_code)]
#![deny(unreachable_pub)]
@@ -11,20 +11,23 @@ mod backfill;
mod constants;
mod core_extraction;
mod decode_replay;
mod metadata_metaplex_token_metadata_execution_orchestration;
mod metadata_metaplex_token_metadata_preflight;
mod metadata_metaplex_token_metadata_stateful;
mod metadata_solana_program_execution_orchestration;
mod metadata_solana_program_preflight;
mod metadata_solana_program_stateful;
mod plan;
mod solana_ata_stateful;
mod solana_elgamal_registry_stateful;
mod solana_metaplex_token_metadata_execution_orchestration;
mod solana_metaplex_token_metadata_preflight;
mod solana_metaplex_token_metadata_stateful;
mod solana_stateful;
mod solana_token_2022_correlation;
mod solana_token_2022_crypto_preflight;
mod solana_token_2022_execution_orchestration;
mod solana_token_2022_preflight;
mod solana_token_2022_proof_orchestration;
mod solana_token_2022_stateful;
mod solana_token_stateful;
mod spl_ata_stateful;
mod spl_elgamal_registry_stateful;
mod spl_token_2022_correlation;
mod spl_token_2022_crypto_preflight;
mod spl_token_2022_execution_orchestration;
mod spl_token_2022_preflight;
mod spl_token_2022_proof_orchestration;
mod spl_token_2022_stateful;
mod spl_token_stateful;
/// Address category used by one targeted backfill campaign.
pub use self::backfill::BackfillAddressKind;
@@ -90,74 +93,88 @@ pub use self::decode_replay::INSTRUCTION_DECODE_STAGE;
pub use self::decode_replay::execute_decode_replay;
/// Creates one stable process-local decode campaign identifier.
pub use self::decode_replay::new_decode_campaign_id;
/// Maximum number of resolved signers accepted by one Metaplex execution envelope.
pub use self::metadata_metaplex_token_metadata_execution_orchestration::MAX_METAPLEX_TOKEN_METADATA_EXECUTION_SIGNERS;
/// Deterministic Metaplex execution-readiness report.
pub use self::metadata_metaplex_token_metadata_execution_orchestration::MetaplexTokenMetadataExecutionReadinessReport;
/// Complete Metaplex execution-readiness request.
pub use self::metadata_metaplex_token_metadata_execution_orchestration::MetaplexTokenMetadataExecutionReadinessRequest;
/// Result of one Metaplex stateful postcondition.
pub use self::metadata_metaplex_token_metadata_execution_orchestration::MetaplexTokenMetadataPostconditionStatus;
/// Aggregates Metaplex postconditions without inventing success.
pub use self::metadata_metaplex_token_metadata_execution_orchestration::summarize_metaplex_token_metadata_postconditions;
/// Validates the complete Metaplex execution envelope before signing.
pub use self::metadata_metaplex_token_metadata_execution_orchestration::validate_metaplex_token_metadata_execution_readiness;
/// Maximum number of correlated Metaplex snapshots accepted by one preflight.
pub use self::metadata_metaplex_token_metadata_preflight::MAX_METAPLEX_TOKEN_METADATA_PREFLIGHT_ACCOUNTS;
/// Deterministic Metaplex preflight report.
pub use self::metadata_metaplex_token_metadata_preflight::MetaplexTokenMetadataPreflightReport;
/// Complete Metaplex preflight request.
pub use self::metadata_metaplex_token_metadata_preflight::MetaplexTokenMetadataPreflightRequest;
/// Inspects one prepared Metaplex plan against confirmed state snapshots.
pub use self::metadata_metaplex_token_metadata_preflight::inspect_metaplex_token_metadata_preflight;
/// Maximum complete Metaplex account data accepted by one RPC read.
pub use self::metadata_metaplex_token_metadata_stateful::MAX_METAPLEX_TOKEN_METADATA_ACCOUNT_BYTES;
/// Supported Metaplex account category for one stateful read.
pub use self::metadata_metaplex_token_metadata_stateful::MetaplexTokenMetadataAccountKind;
/// One bounded Metaplex stateful read request.
pub use self::metadata_metaplex_token_metadata_stateful::MetaplexTokenMetadataStatefulReadRequest;
/// One bounded Metaplex stateful read result.
pub use self::metadata_metaplex_token_metadata_stateful::MetaplexTokenMetadataStatefulReadResult;
/// Canonical bounded Metaplex state snapshot.
pub use self::metadata_metaplex_token_metadata_stateful::MetaplexTokenMetadataStatefulSnapshot;
/// Validates one RPC account response and delegates parsing to `kb-lib`.
pub use self::metadata_metaplex_token_metadata_stateful::materialize_metaplex_token_metadata_account_info_result;
/// Reads one bounded Metaplex Token Metadata account.
pub use self::metadata_metaplex_token_metadata_stateful::read_metaplex_token_metadata_stateful_snapshot;
/// Maximum number of resolved signers accepted by one Program Metadata execution envelope.
pub use self::metadata_solana_program_execution_orchestration::MAX_SOLANA_PROGRAM_METADATA_EXECUTION_SIGNERS;
/// Complete Program Metadata execution-readiness report.
pub use self::metadata_solana_program_execution_orchestration::SolanaProgramMetadataExecutionReadinessReport;
/// Complete Program Metadata execution-readiness request.
pub use self::metadata_solana_program_execution_orchestration::SolanaProgramMetadataExecutionReadinessRequest;
/// Complete Program Metadata post-execution report.
pub use self::metadata_solana_program_execution_orchestration::SolanaProgramMetadataPostExecutionReport;
/// Complete Program Metadata post-execution request.
pub use self::metadata_solana_program_execution_orchestration::SolanaProgramMetadataPostExecutionRequest;
/// One Program Metadata postcondition.
pub use self::metadata_solana_program_execution_orchestration::SolanaProgramMetadataPostcondition;
/// Program Metadata postcondition status.
pub use self::metadata_solana_program_execution_orchestration::SolanaProgramMetadataPostconditionStatus;
/// Inspects confirmed Program Metadata post-execution state.
pub use self::metadata_solana_program_execution_orchestration::inspect_solana_program_metadata_post_execution;
/// Aggregates Program Metadata postconditions without invented success.
pub use self::metadata_solana_program_execution_orchestration::summarize_solana_program_metadata_postconditions;
/// Validates the complete Program Metadata execution envelope before signing.
pub use self::metadata_solana_program_execution_orchestration::validate_solana_program_metadata_execution_readiness;
/// Maximum number of bounded account snapshots accepted by one Program Metadata preflight.
pub use self::metadata_solana_program_preflight::MAX_SOLANA_PROGRAM_METADATA_PREFLIGHT_ACCOUNTS;
/// Deterministic Program Metadata preflight report.
pub use self::metadata_solana_program_preflight::SolanaProgramMetadataPreflightReport;
/// Complete Program Metadata preflight request.
pub use self::metadata_solana_program_preflight::SolanaProgramMetadataPreflightRequest;
/// One Program Metadata rent-exemption observation.
pub use self::metadata_solana_program_preflight::SolanaProgramMetadataRentObservation;
/// Inspects one Program Metadata plan against confirmed state.
pub use self::metadata_solana_program_preflight::inspect_solana_program_metadata_preflight;
/// Maximum complete Program Metadata account data accepted by one RPC read.
pub use self::metadata_solana_program_stateful::MAX_SOLANA_PROGRAM_METADATA_STATEFUL_ACCOUNT_BYTES;
/// Expected Program Metadata account state.
pub use self::metadata_solana_program_stateful::SolanaProgramMetadataExpectedAccountState;
/// Observed Program Metadata account state.
pub use self::metadata_solana_program_stateful::SolanaProgramMetadataObservedAccountState;
/// One bounded Program Metadata stateful read request.
pub use self::metadata_solana_program_stateful::SolanaProgramMetadataStatefulReadRequest;
/// One bounded Program Metadata stateful read result.
pub use self::metadata_solana_program_stateful::SolanaProgramMetadataStatefulReadResult;
/// Validates one Program Metadata RPC response and materializes its account state.
pub use self::metadata_solana_program_stateful::materialize_solana_program_metadata_account_info_result;
/// Reads one bounded Program Metadata account.
pub use self::metadata_solana_program_stateful::read_solana_program_metadata_stateful_snapshot;
/// Pipeline stage identifier.
pub use self::plan::PipelineStage;
/// Replay selection scope.
pub use self::plan::ReplayScope;
/// Stateful invariants observed after one confirmed Associated Token Account execution.
pub use self::solana_ata_stateful::SplAssociatedTokenAccountPostExecutionReport;
/// One machine-readable Associated Token Account stateful check.
pub use self::solana_ata_stateful::SplAssociatedTokenAccountStatefulCheck;
/// One contextual Associated Token Account stateful fact.
pub use self::solana_ata_stateful::SplAssociatedTokenAccountStatefulFact;
/// Complete Associated Token Account stateful readiness report.
pub use self::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessReport;
/// Complete request for one Associated Token Account stateful readiness inspection.
pub use self::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessRequest;
/// Associated Token Account stateful readiness outcome.
pub use self::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessStatus;
/// Verifies final Associated Token Account relationships after confirmed execution.
pub use self::solana_ata_stateful::inspect_spl_associated_token_account_post_execution;
/// Inspects state required before simulating one Associated Token Account operation.
pub use self::solana_ata_stateful::inspect_spl_associated_token_account_stateful_readiness;
/// Migrated ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES contract.
pub use self::solana_elgamal_registry_stateful::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES;
/// Migrated ElGamalRegistryStatefulReadRequest contract.
pub use self::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadRequest;
/// Migrated ElGamalRegistryStatefulReadResult contract.
pub use self::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadResult;
/// Migrated ElGamalRegistryStatefulSnapshot contract.
pub use self::solana_elgamal_registry_stateful::ElGamalRegistryStatefulSnapshot;
/// Migrated materialize_elgamal_registry_account_info_result contract.
pub use self::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result;
/// Migrated materialize_elgamal_registry_stateful_snapshot contract.
pub use self::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot;
/// Migrated read_elgamal_registry_stateful_snapshot contract.
pub use self::solana_elgamal_registry_stateful::read_elgamal_registry_stateful_snapshot;
/// Maximum number of resolved signers accepted by one Metaplex execution envelope.
pub use self::solana_metaplex_token_metadata_execution_orchestration::MAX_METAPLEX_TOKEN_METADATA_EXECUTION_SIGNERS;
/// Deterministic Metaplex execution-readiness report.
pub use self::solana_metaplex_token_metadata_execution_orchestration::MetaplexTokenMetadataExecutionReadinessReport;
/// Complete Metaplex execution-readiness request.
pub use self::solana_metaplex_token_metadata_execution_orchestration::MetaplexTokenMetadataExecutionReadinessRequest;
/// Result of one Metaplex stateful postcondition.
pub use self::solana_metaplex_token_metadata_execution_orchestration::MetaplexTokenMetadataPostconditionStatus;
/// Aggregates Metaplex postconditions without inventing success.
pub use self::solana_metaplex_token_metadata_execution_orchestration::summarize_metaplex_token_metadata_postconditions;
/// Validates the complete Metaplex execution envelope before signing.
pub use self::solana_metaplex_token_metadata_execution_orchestration::validate_metaplex_token_metadata_execution_readiness;
/// Maximum number of correlated Metaplex snapshots accepted by one preflight.
pub use self::solana_metaplex_token_metadata_preflight::MAX_METAPLEX_TOKEN_METADATA_PREFLIGHT_ACCOUNTS;
/// Deterministic Metaplex preflight report.
pub use self::solana_metaplex_token_metadata_preflight::MetaplexTokenMetadataPreflightReport;
/// Complete Metaplex preflight request.
pub use self::solana_metaplex_token_metadata_preflight::MetaplexTokenMetadataPreflightRequest;
/// Inspects one prepared Metaplex plan against confirmed state snapshots.
pub use self::solana_metaplex_token_metadata_preflight::inspect_metaplex_token_metadata_preflight;
/// Maximum complete Metaplex account data accepted by one RPC read.
pub use self::solana_metaplex_token_metadata_stateful::MAX_METAPLEX_TOKEN_METADATA_ACCOUNT_BYTES;
/// Supported Metaplex account category for one stateful read.
pub use self::solana_metaplex_token_metadata_stateful::MetaplexTokenMetadataAccountKind;
/// One bounded Metaplex stateful read request.
pub use self::solana_metaplex_token_metadata_stateful::MetaplexTokenMetadataStatefulReadRequest;
/// One bounded Metaplex stateful read result.
pub use self::solana_metaplex_token_metadata_stateful::MetaplexTokenMetadataStatefulReadResult;
/// Canonical bounded Metaplex state snapshot.
pub use self::solana_metaplex_token_metadata_stateful::MetaplexTokenMetadataStatefulSnapshot;
/// Validates one RPC account response and delegates parsing to `kb-lib`.
pub use self::solana_metaplex_token_metadata_stateful::materialize_metaplex_token_metadata_account_info_result;
/// Reads one bounded Metaplex Token Metadata account.
pub use self::solana_metaplex_token_metadata_stateful::read_metaplex_token_metadata_stateful_snapshot;
/// One machine-readable native Solana stateful readiness check.
pub use self::solana_stateful::SolanaCoreStatefulCheck;
/// One contextual fact measured during native Solana stateful readiness.
@@ -170,94 +187,124 @@ pub use self::solana_stateful::SolanaCoreStatefulReadinessRequest;
pub use self::solana_stateful::SolanaCoreStatefulReadinessStatus;
/// Inspects state required before simulating one native Solana operation.
pub use self::solana_stateful::inspect_solana_core_stateful_readiness;
/// Stateful invariants observed after one confirmed Associated Token Account execution.
pub use self::spl_ata_stateful::SplAssociatedTokenAccountPostExecutionReport;
/// One machine-readable Associated Token Account stateful check.
pub use self::spl_ata_stateful::SplAssociatedTokenAccountStatefulCheck;
/// One contextual Associated Token Account stateful fact.
pub use self::spl_ata_stateful::SplAssociatedTokenAccountStatefulFact;
/// Complete Associated Token Account stateful readiness report.
pub use self::spl_ata_stateful::SplAssociatedTokenAccountStatefulReadinessReport;
/// Complete request for one Associated Token Account stateful readiness inspection.
pub use self::spl_ata_stateful::SplAssociatedTokenAccountStatefulReadinessRequest;
/// Associated Token Account stateful readiness outcome.
pub use self::spl_ata_stateful::SplAssociatedTokenAccountStatefulReadinessStatus;
/// Verifies final Associated Token Account relationships after confirmed execution.
pub use self::spl_ata_stateful::inspect_spl_associated_token_account_post_execution;
/// Inspects state required before simulating one Associated Token Account operation.
pub use self::spl_ata_stateful::inspect_spl_associated_token_account_stateful_readiness;
/// Migrated ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES contract.
pub use self::spl_elgamal_registry_stateful::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES;
/// Migrated ElGamalRegistryStatefulReadRequest contract.
pub use self::spl_elgamal_registry_stateful::ElGamalRegistryStatefulReadRequest;
/// Migrated ElGamalRegistryStatefulReadResult contract.
pub use self::spl_elgamal_registry_stateful::ElGamalRegistryStatefulReadResult;
/// Migrated ElGamalRegistryStatefulSnapshot contract.
pub use self::spl_elgamal_registry_stateful::ElGamalRegistryStatefulSnapshot;
/// Migrated materialize_elgamal_registry_account_info_result contract.
pub use self::spl_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result;
/// Migrated materialize_elgamal_registry_stateful_snapshot contract.
pub use self::spl_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot;
/// Migrated read_elgamal_registry_stateful_snapshot contract.
pub use self::spl_elgamal_registry_stateful::read_elgamal_registry_stateful_snapshot;
/// Correlation outcome between one committed instruction fact and one final state snapshot.
pub use self::solana_token_2022_correlation::Token2022CorrelationStatus;
pub use self::spl_token_2022_correlation::Token2022CorrelationStatus;
/// Deterministic correlation report for one instruction output and one final snapshot.
pub use self::solana_token_2022_correlation::Token2022StateCorrelationReport;
pub use self::spl_token_2022_correlation::Token2022StateCorrelationReport;
/// Correlates one materialized instruction fact with one authoritative Token-2022 snapshot.
pub use self::solana_token_2022_correlation::correlate_token_2022_instruction_with_snapshot;
pub use self::spl_token_2022_correlation::correlate_token_2022_instruction_with_snapshot;
/// Maximum number of distinct proof context-state accounts accepted by one preflight.
pub use self::solana_token_2022_crypto_preflight::MAX_TOKEN_2022_PROOF_CONTEXTS;
pub use self::spl_token_2022_crypto_preflight::MAX_TOKEN_2022_PROOF_CONTEXTS;
/// Token-2022 cryptographic preflight report.
pub use self::solana_token_2022_crypto_preflight::Token2022CryptographicPreflightReport;
pub use self::spl_token_2022_crypto_preflight::Token2022CryptographicPreflightReport;
/// Token-2022 cryptographic preflight request.
pub use self::solana_token_2022_crypto_preflight::Token2022CryptographicPreflightRequest;
pub use self::spl_token_2022_crypto_preflight::Token2022CryptographicPreflightRequest;
/// One validated proof context report.
pub use self::solana_token_2022_crypto_preflight::Token2022ProofContextReport;
pub use self::spl_token_2022_crypto_preflight::Token2022ProofContextReport;
/// One required proof context-state account.
pub use self::solana_token_2022_crypto_preflight::Token2022ProofContextRequirement;
pub use self::spl_token_2022_crypto_preflight::Token2022ProofContextRequirement;
/// Encoded proof context metadata bytes.
pub use self::solana_token_2022_crypto_preflight::ZK_PROOF_CONTEXT_META_BYTES;
pub use self::spl_token_2022_crypto_preflight::ZK_PROOF_CONTEXT_META_BYTES;
/// Inspects bounded Token-2022 cryptographic proof contexts.
pub use self::solana_token_2022_crypto_preflight::inspect_token_2022_cryptographic_preflight;
pub use self::spl_token_2022_crypto_preflight::inspect_token_2022_cryptographic_preflight;
/// Maximum number of distinct signers accepted by one Token-2022 execution envelope.
pub use self::solana_token_2022_execution_orchestration::MAX_TOKEN_2022_EXECUTION_SIGNERS;
pub use self::spl_token_2022_execution_orchestration::MAX_TOKEN_2022_EXECUTION_SIGNERS;
/// One explicit stateful postcondition retained after Token-2022 execution.
pub use self::solana_token_2022_execution_orchestration::Token2022ExecutionPostcondition;
pub use self::spl_token_2022_execution_orchestration::Token2022ExecutionPostcondition;
/// Result of one stateful postcondition after Token-2022 execution.
pub use self::solana_token_2022_execution_orchestration::Token2022ExecutionPostconditionStatus;
pub use self::spl_token_2022_execution_orchestration::Token2022ExecutionPostconditionStatus;
/// Deterministic Token-2022 execution-readiness report.
pub use self::solana_token_2022_execution_orchestration::Token2022ExecutionReadinessReport;
pub use self::spl_token_2022_execution_orchestration::Token2022ExecutionReadinessReport;
/// Complete deterministic Token-2022 execution-readiness request.
pub use self::solana_token_2022_execution_orchestration::Token2022ExecutionReadinessRequest;
pub use self::spl_token_2022_execution_orchestration::Token2022ExecutionReadinessRequest;
/// Aggregates Token-2022 postconditions without inventing success.
pub use self::solana_token_2022_execution_orchestration::summarize_token_2022_postconditions;
pub use self::spl_token_2022_execution_orchestration::summarize_token_2022_postconditions;
/// Validates the complete Token-2022 execution envelope before signing.
pub use self::solana_token_2022_execution_orchestration::validate_token_2022_execution_readiness;
pub use self::spl_token_2022_execution_orchestration::validate_token_2022_execution_readiness;
/// Migrated MAX_TOKEN_2022_PREFLIGHT_ACCOUNTS contract.
pub use self::solana_token_2022_preflight::MAX_TOKEN_2022_PREFLIGHT_ACCOUNTS;
pub use self::spl_token_2022_preflight::MAX_TOKEN_2022_PREFLIGHT_ACCOUNTS;
/// Migrated MAX_TOKEN_2022_PREFLIGHT_TOTAL_BYTES contract.
pub use self::solana_token_2022_preflight::MAX_TOKEN_2022_PREFLIGHT_TOTAL_BYTES;
pub use self::spl_token_2022_preflight::MAX_TOKEN_2022_PREFLIGHT_TOTAL_BYTES;
/// Migrated Token2022PreflightAccountReport contract.
pub use self::solana_token_2022_preflight::Token2022PreflightAccountReport;
pub use self::spl_token_2022_preflight::Token2022PreflightAccountReport;
/// Migrated Token2022PreflightReport contract.
pub use self::solana_token_2022_preflight::Token2022PreflightReport;
pub use self::spl_token_2022_preflight::Token2022PreflightReport;
/// Migrated Token2022PreflightRequest contract.
pub use self::solana_token_2022_preflight::Token2022PreflightRequest;
pub use self::spl_token_2022_preflight::Token2022PreflightRequest;
/// Migrated Token2022PreflightRequirement contract.
pub use self::solana_token_2022_preflight::Token2022PreflightRequirement;
pub use self::spl_token_2022_preflight::Token2022PreflightRequirement;
/// Migrated inspect_token_2022_preflight contract.
pub use self::solana_token_2022_preflight::inspect_token_2022_preflight;
pub use self::spl_token_2022_preflight::inspect_token_2022_preflight;
/// Maximum number of proofs accepted for one Token-2022 operation.
pub use self::solana_token_2022_proof_orchestration::MAX_TOKEN_2022_OPERATION_PROOFS;
pub use self::spl_token_2022_proof_orchestration::MAX_TOKEN_2022_OPERATION_PROOFS;
/// Token-2022 proof orchestration report.
pub use self::solana_token_2022_proof_orchestration::Token2022ProofOrchestrationReport;
pub use self::spl_token_2022_proof_orchestration::Token2022ProofOrchestrationReport;
/// Token-2022 proof orchestration request.
pub use self::solana_token_2022_proof_orchestration::Token2022ProofOrchestrationRequest;
pub use self::spl_token_2022_proof_orchestration::Token2022ProofOrchestrationRequest;
/// Validates mixed inline and context-state proof orchestration.
pub use self::solana_token_2022_proof_orchestration::orchestrate_token_2022_proofs;
pub use self::spl_token_2022_proof_orchestration::orchestrate_token_2022_proofs;
/// Migrated MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES contract.
pub use self::solana_token_2022_stateful::MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES;
pub use self::spl_token_2022_stateful::MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES;
/// Migrated Token2022StatefulContext contract.
pub use self::solana_token_2022_stateful::Token2022StatefulContext;
pub use self::spl_token_2022_stateful::Token2022StatefulContext;
/// Migrated Token2022StatefulReadRequest contract.
pub use self::solana_token_2022_stateful::Token2022StatefulReadRequest;
pub use self::spl_token_2022_stateful::Token2022StatefulReadRequest;
/// Migrated Token2022StatefulReadResult contract.
pub use self::solana_token_2022_stateful::Token2022StatefulReadResult;
pub use self::spl_token_2022_stateful::Token2022StatefulReadResult;
/// Migrated Token2022StatefulSnapshotBundle contract.
pub use self::solana_token_2022_stateful::Token2022StatefulSnapshotBundle;
pub use self::spl_token_2022_stateful::Token2022StatefulSnapshotBundle;
/// Migrated materialize_parsed_token_2022_stateful_snapshot contract.
pub use self::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot;
pub use self::spl_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot;
/// Migrated materialize_parsed_token_2022_stateful_snapshot_with_context contract.
pub use self::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot_with_context;
pub use self::spl_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot_with_context;
/// Migrated materialize_token_2022_account_info_result contract.
pub use self::solana_token_2022_stateful::materialize_token_2022_account_info_result;
pub use self::spl_token_2022_stateful::materialize_token_2022_account_info_result;
/// Migrated materialize_token_2022_stateful_snapshot contract.
pub use self::solana_token_2022_stateful::materialize_token_2022_stateful_snapshot;
pub use self::spl_token_2022_stateful::materialize_token_2022_stateful_snapshot;
/// Migrated read_token_2022_stateful_snapshot contract.
pub use self::solana_token_2022_stateful::read_token_2022_stateful_snapshot;
pub use self::spl_token_2022_stateful::read_token_2022_stateful_snapshot;
/// One machine-readable classic SPL Token stateful check.
pub use self::solana_token_stateful::SplTokenStatefulCheck;
pub use self::spl_token_stateful::SplTokenStatefulCheck;
/// One contextual classic SPL Token stateful fact.
pub use self::solana_token_stateful::SplTokenStatefulFact;
pub use self::spl_token_stateful::SplTokenStatefulFact;
/// Complete classic SPL Token stateful readiness report.
pub use self::solana_token_stateful::SplTokenStatefulReadinessReport;
pub use self::spl_token_stateful::SplTokenStatefulReadinessReport;
/// Complete request for one classic SPL Token stateful readiness inspection.
pub use self::solana_token_stateful::SplTokenStatefulReadinessRequest;
pub use self::spl_token_stateful::SplTokenStatefulReadinessRequest;
/// Classic SPL Token stateful readiness outcome.
pub use self::solana_token_stateful::SplTokenStatefulReadinessStatus;
pub use self::spl_token_stateful::SplTokenStatefulReadinessStatus;
/// Inspects Localnet or Devnet classic SPL Token state required before simulation.
pub use self::solana_token_stateful::inspect_spl_token_stateful_readiness;
pub use self::spl_token_stateful::inspect_spl_token_stateful_readiness;
/// Canonical tracing target for pipeline orchestration.
pub(crate) use self::constants::TRACING_TARGET;

View File

@@ -1,5 +1,5 @@
// file: kb-pipeline/src/solana_metaplex_token_metadata_execution_orchestration.rs
// version: 1
// file: kb-pipeline/src/metadata_metaplex_token_metadata_execution_orchestration.rs
// version: 2
//! Simulation-first Metaplex Token Metadata execution orchestration.

View File

@@ -1,5 +1,5 @@
// file: kb-pipeline/src/solana_metaplex_token_metadata_preflight.rs
// version: 2
// file: kb-pipeline/src/metadata_metaplex_token_metadata_preflight.rs
// version: 3
//! Stateful Metaplex Token Metadata preflight contracts.

View File

@@ -1,5 +1,5 @@
// file: kb-pipeline/src/solana_metaplex_token_metadata_stateful.rs
// version: 3
// file: kb-pipeline/src/metadata_metaplex_token_metadata_stateful.rs
// version: 4
//! Bounded Metaplex Token Metadata account reads and canonical state projections.

View File

@@ -0,0 +1,831 @@
// file: kb-pipeline/src/metadata_solana_program_execution_orchestration.rs
// version: 2
//! Simulation-first Solana Program Metadata execution orchestration and postconditions.
/// Maximum number of resolved signers accepted by one Program Metadata envelope.
pub const MAX_SOLANA_PROGRAM_METADATA_EXECUTION_SIGNERS: usize = 16;
/// Complete request checked before signing or submitting one Program Metadata transaction.
#[derive(Clone, Debug, PartialEq)]
pub struct SolanaProgramMetadataExecutionReadinessRequest {
/// Exact prepared execution plan.
pub plan: kb_lib::ExApiPreparedExecutionPlan,
/// Successful stateful preflight report.
pub preflight: crate::SolanaProgramMetadataPreflightReport,
/// Exact compiled message hash.
pub message_hash: std::string::String,
/// Hash retained by simulation evidence.
pub simulated_message_hash: std::string::String,
/// Whether simulation was performed.
pub simulated: bool,
/// Whether simulation succeeded.
pub simulation_succeeded: bool,
/// Public keys available to sign.
pub resolved_signers: std::vec::Vec<kb_lib::MdPubkey>,
/// Whether submission was requested.
pub submit: bool,
/// Whether the operator confirmed submission.
pub operator_confirmed: bool,
}
/// Deterministic readiness report emitted before signing.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct SolanaProgramMetadataExecutionReadinessReport {
/// Stable operation code.
pub operation_code: std::string::String,
/// Exact message hash bound to simulation.
pub message_hash: std::string::String,
/// Confirmed preflight context slot.
pub context_slot: u64,
/// Deduplicated required signers.
pub required_signers: std::vec::Vec<kb_lib::MdPubkey>,
/// Whether signing and submission are authorized.
pub send_authorized: bool,
/// Ordered successful checks.
pub checks: std::vec::Vec<std::string::String>,
}
/// Result of one Program Metadata postcondition.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum SolanaProgramMetadataPostconditionStatus {
/// Final state confirms the expected effect.
Confirmed,
/// Final state contradicts the expected effect.
Contradicted,
/// No stateful assertion applies.
NotApplicable,
}
/// One machine-readable Program Metadata postcondition.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct SolanaProgramMetadataPostcondition {
/// Stable check code.
pub code: std::string::String,
/// Account inspected by the check.
pub account: kb_lib::MdPubkey,
/// Terminal status.
pub status: crate::SolanaProgramMetadataPostconditionStatus,
/// Human-readable diagnostic.
pub diagnostic: std::string::String,
}
/// Complete request for one confirmed Program Metadata post-execution inspection.
#[derive(Clone, Debug, PartialEq)]
pub struct SolanaProgramMetadataPostExecutionRequest {
/// Exact operation that was submitted.
pub operation: kb_lib::ExMetadataSpmOperation,
/// Confirmed snapshots captured before execution.
pub before: std::vec::Vec<crate::SolanaProgramMetadataStatefulReadResult>,
/// Confirmed snapshots captured after execution.
pub after: std::vec::Vec<crate::SolanaProgramMetadataStatefulReadResult>,
}
/// Aggregated Program Metadata post-execution report.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct SolanaProgramMetadataPostExecutionReport {
/// Stable operation code.
pub operation_code: std::string::String,
/// Aggregate status without invented success.
pub status: crate::SolanaProgramMetadataPostconditionStatus,
/// Individual postconditions.
pub postconditions: std::vec::Vec<crate::SolanaProgramMetadataPostcondition>,
}
/// Validates simulation, preflight, signer and submission policies.
pub fn validate_solana_program_metadata_execution_readiness(
request: &crate::SolanaProgramMetadataExecutionReadinessRequest,
) -> kb_core::Result<crate::SolanaProgramMetadataExecutionReadinessReport> {
if request.plan.operation_code != request.preflight.operation_code {
return std::result::Result::Err(kb_core::Error::new(
"solana_program_metadata_execution_operation_mismatch",
"Program Metadata plan and preflight operation codes must match",
));
}
if request.preflight.safety.decision == kb_lib::ExSafetyDecision::Deny {
return std::result::Result::Err(kb_core::Error::new(
"solana_program_metadata_execution_safety_denied",
"Program Metadata preflight safety decision denied execution",
));
}
if request.preflight.safety.decision == kb_lib::ExSafetyDecision::RequireConfirmation
&& !request.operator_confirmed
{
return std::result::Result::Err(kb_core::Error::new(
"solana_program_metadata_execution_safety_confirmation_required",
"Program Metadata safety policy requires explicit operator confirmation",
));
}
if request.message_hash.trim().is_empty()
|| request.message_hash != request.simulated_message_hash
|| !request.simulated
|| !request.simulation_succeeded
{
return std::result::Result::Err(kb_core::Error::new(
"solana_program_metadata_execution_simulation_required",
"Program Metadata execution requires successful exact-message simulation",
));
}
if request.resolved_signers.len() > crate::MAX_SOLANA_PROGRAM_METADATA_EXECUTION_SIGNERS {
return std::result::Result::Err(kb_core::Error::new(
"solana_program_metadata_execution_signer_limit_exceeded",
"Program Metadata execution signer limit exceeded",
));
}
let resolved = request
.resolved_signers
.iter()
.map(|item| return item.0.clone())
.collect::<std::collections::BTreeSet<std::string::String>>();
let mut required = std::collections::BTreeMap::<std::string::String, kb_lib::MdPubkey>::new();
for signer in &request.plan.required_signers {
if !resolved.contains(signer.pubkey.0.as_str()) {
return std::result::Result::Err(kb_core::Error::new(
"solana_program_metadata_execution_signer_unresolved",
format!("Program Metadata signer {} is unresolved", signer.pubkey.0),
));
}
required
.entry(signer.pubkey.0.clone())
.or_insert_with(|| return signer.pubkey.clone());
}
if request.submit && !request.operator_confirmed {
return std::result::Result::Err(kb_core::Error::new(
"solana_program_metadata_execution_confirmation_required",
"Program Metadata submission requires explicit operator confirmation",
));
}
if request.submit && request.plan.policy.dry_run {
return std::result::Result::Err(kb_core::Error::new(
"solana_program_metadata_execution_dry_run_blocks_submission",
"Program Metadata dry-run plan cannot be submitted",
));
}
tracing::debug!(
target: crate::TRACING_TARGET,
action = "validate_solana_program_metadata_execution_readiness",
operation_code = %request.plan.operation_code,
context_slot = request.preflight.context_slot,
signer_count = required.len(),
submit = request.submit,
send_authorized = request.submit && request.operator_confirmed && !request.plan.policy.dry_run,
"validated Solana Program Metadata execution readiness"
);
return std::result::Result::Ok(crate::SolanaProgramMetadataExecutionReadinessReport {
operation_code: request.plan.operation_code.clone(),
message_hash: request.message_hash.clone(),
context_slot: request.preflight.context_slot,
required_signers: required.into_values().collect(),
send_authorized: request.submit
&& request.operator_confirmed
&& !request.plan.policy.dry_run,
checks: vec![
"preflight_bound_to_operation".to_string(),
"common_safety_allowed".to_string(),
"simulation_bound_to_exact_message".to_string(),
"all_required_signers_resolved".to_string(),
"submission_policy_consistent".to_string(),
],
});
}
/// Inspects confirmed state after one Program Metadata execution.
pub fn inspect_solana_program_metadata_post_execution(
request: &crate::SolanaProgramMetadataPostExecutionRequest,
) -> kb_core::Result<crate::SolanaProgramMetadataPostExecutionReport> {
if let std::result::Result::Err(error) =
validate_snapshot_set(request.before.as_slice(), "before")
{
return std::result::Result::Err(error);
}
if let std::result::Result::Err(error) =
validate_snapshot_set(request.after.as_slice(), "after")
{
return std::result::Result::Err(error);
}
let target = operation_target(&request.operation).clone();
let before = find_snapshot(request.before.as_slice(), &target);
let after = find_snapshot(request.after.as_slice(), &target);
let postcondition = evaluate_operation_postcondition(
&request.operation,
before,
after,
request.before.as_slice(),
);
let status =
summarize_solana_program_metadata_postconditions(std::slice::from_ref(&postcondition));
tracing::debug!(
target: crate::TRACING_TARGET,
action = "inspect_solana_program_metadata_post_execution",
operation_code = request.operation.operation_code(),
target_account = %target.0,
status = ?status,
"inspected Solana Program Metadata post-execution state"
);
return std::result::Result::Ok(crate::SolanaProgramMetadataPostExecutionReport {
operation_code: request.operation.operation_code().to_string(),
status,
postconditions: vec![postcondition],
});
}
/// Aggregates Program Metadata postconditions without converting absence into success.
pub fn summarize_solana_program_metadata_postconditions(
postconditions: &[crate::SolanaProgramMetadataPostcondition],
) -> crate::SolanaProgramMetadataPostconditionStatus {
if postconditions.iter().any(|item| {
return item.status == crate::SolanaProgramMetadataPostconditionStatus::Contradicted;
}) {
return crate::SolanaProgramMetadataPostconditionStatus::Contradicted;
}
if postconditions.iter().any(|item| {
return item.status == crate::SolanaProgramMetadataPostconditionStatus::Confirmed;
}) {
return crate::SolanaProgramMetadataPostconditionStatus::Confirmed;
}
return crate::SolanaProgramMetadataPostconditionStatus::NotApplicable;
}
fn validate_snapshot_set(
snapshots: &[crate::SolanaProgramMetadataStatefulReadResult],
phase: &str,
) -> kb_core::Result<()> {
if snapshots.len() > crate::MAX_SOLANA_PROGRAM_METADATA_PREFLIGHT_ACCOUNTS {
return std::result::Result::Err(kb_core::Error::new(
"solana_program_metadata_post_execution_account_limit_exceeded",
format!("Program Metadata {phase} snapshot limit exceeded"),
));
}
let mut accounts = std::collections::BTreeSet::new();
for snapshot in snapshots {
if snapshot.commitment != "confirmed" {
return std::result::Result::Err(kb_core::Error::new(
"solana_program_metadata_post_execution_commitment_mismatch",
format!("Program Metadata {phase} snapshots must use confirmed commitment"),
));
}
if !accounts.insert(snapshot.account.0.as_str()) {
return std::result::Result::Err(kb_core::Error::new(
"solana_program_metadata_post_execution_duplicate_account",
format!("duplicate Program Metadata {phase} snapshot for {}", snapshot.account.0),
));
}
}
return std::result::Result::Ok(());
}
fn operation_target(operation: &kb_lib::ExMetadataSpmOperation) -> &kb_lib::MdPubkey {
return match operation {
kb_lib::ExMetadataSpmOperation::Write { buffer, .. }
| kb_lib::ExMetadataSpmOperation::Allocate { buffer, .. } => buffer,
kb_lib::ExMetadataSpmOperation::Initialize { metadata, .. }
| kb_lib::ExMetadataSpmOperation::SetData { metadata, .. }
| kb_lib::ExMetadataSpmOperation::SetImmutable { metadata, .. } => metadata,
kb_lib::ExMetadataSpmOperation::SetAuthority { account, .. }
| kb_lib::ExMetadataSpmOperation::Trim { account, .. }
| kb_lib::ExMetadataSpmOperation::Close { account, .. }
| kb_lib::ExMetadataSpmOperation::Extend { account, .. } => account,
};
}
fn find_snapshot<'a>(
snapshots: &'a [crate::SolanaProgramMetadataStatefulReadResult],
account: &kb_lib::MdPubkey,
) -> std::option::Option<&'a crate::SolanaProgramMetadataStatefulReadResult> {
return snapshots.iter().find(|value| return value.account == *account);
}
fn evaluate_operation_postcondition(
operation: &kb_lib::ExMetadataSpmOperation,
before: std::option::Option<&crate::SolanaProgramMetadataStatefulReadResult>,
after: std::option::Option<&crate::SolanaProgramMetadataStatefulReadResult>,
before_all: &[crate::SolanaProgramMetadataStatefulReadResult],
) -> crate::SolanaProgramMetadataPostcondition {
let target = operation_target(operation).clone();
let confirmed = match operation {
kb_lib::ExMetadataSpmOperation::Write { offset, source, .. } => {
write_postcondition(*offset, source, after, before_all)
},
kb_lib::ExMetadataSpmOperation::Initialize {
program,
canonical,
seed,
encoding,
compression,
format,
data_source,
data,
..
} => initialize_postcondition(
program,
*canonical,
seed.as_slice(),
*encoding,
*compression,
*format,
*data_source,
data.as_ref(),
after,
),
kb_lib::ExMetadataSpmOperation::SetAuthority { new_authority, .. } => {
authority_postcondition(new_authority.as_ref(), after)
},
kb_lib::ExMetadataSpmOperation::SetData {
encoding, compression, format, source, ..
} => set_data_postcondition(
*encoding,
*compression,
*format,
source,
before,
after,
before_all,
),
kb_lib::ExMetadataSpmOperation::SetImmutable { .. } => match after {
std::option::Option::Some(value) => matches!(
&value.state,
crate::SolanaProgramMetadataObservedAccountState::Metadata(metadata) if !metadata.mutable
),
std::option::Option::None => false,
},
kb_lib::ExMetadataSpmOperation::Trim { .. } => trim_postcondition(before, after),
kb_lib::ExMetadataSpmOperation::Close { .. } => match after {
std::option::Option::Some(value) => {
matches!(&value.state, crate::SolanaProgramMetadataObservedAccountState::Missing)
},
std::option::Option::None => false,
},
kb_lib::ExMetadataSpmOperation::Allocate { authority, seed, canonical, .. } => {
allocate_postcondition(authority, seed.as_deref(), *canonical, after)
},
kb_lib::ExMetadataSpmOperation::Extend { length, .. } => {
extend_postcondition(*length, before, after)
},
};
let status = if after.is_none() {
crate::SolanaProgramMetadataPostconditionStatus::NotApplicable
} else if confirmed {
crate::SolanaProgramMetadataPostconditionStatus::Confirmed
} else {
crate::SolanaProgramMetadataPostconditionStatus::Contradicted
};
return crate::SolanaProgramMetadataPostcondition {
code: format!("{}.state", operation.operation_code()),
account: target,
status,
diagnostic: match status {
crate::SolanaProgramMetadataPostconditionStatus::Confirmed => {
"confirmed state matches the expected Program Metadata effect".to_string()
},
crate::SolanaProgramMetadataPostconditionStatus::Contradicted => {
"confirmed state contradicts the expected Program Metadata effect".to_string()
},
crate::SolanaProgramMetadataPostconditionStatus::NotApplicable => {
"no confirmed after-state snapshot was supplied".to_string()
},
},
};
}
fn write_postcondition(
offset: u32,
source: &kb_lib::ExMetadataSpmWriteSource,
after: std::option::Option<&crate::SolanaProgramMetadataStatefulReadResult>,
before_all: &[crate::SolanaProgramMetadataStatefulReadResult],
) -> bool {
let buffer = match after.map(|value| return &value.state) {
std::option::Option::Some(crate::SolanaProgramMetadataObservedAccountState::Buffer(
value,
)) => value,
_ => return false,
};
let start = match usize::try_from(offset) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return false,
};
let expected = match source {
kb_lib::ExMetadataSpmWriteSource::Inline { data } => data.as_slice(),
kb_lib::ExMetadataSpmWriteSource::Buffer { source_buffer } => {
let source_snapshot = match find_snapshot(before_all, source_buffer) {
std::option::Option::Some(value) => value,
std::option::Option::None => return false,
};
match &source_snapshot.state {
crate::SolanaProgramMetadataObservedAccountState::Buffer(value) => {
value.data.as_slice()
},
_ => return false,
}
},
};
let end = match start.checked_add(expected.len()) {
std::option::Option::Some(value) => value,
std::option::Option::None => return false,
};
return buffer.data.get(start..end) == std::option::Option::Some(expected);
}
#[allow(clippy::too_many_arguments)]
fn initialize_postcondition(
program: &kb_lib::MdPubkey,
canonical: bool,
seed: &[u8],
encoding: kb_lib::ExMetadataSpmEncoding,
compression: kb_lib::ExMetadataSpmCompression,
format: kb_lib::ExMetadataSpmFormat,
data_source: kb_lib::ExMetadataSpmDataSource,
data: std::option::Option<&kb_lib::ExMetadataSpmDataInput>,
after: std::option::Option<&crate::SolanaProgramMetadataStatefulReadResult>,
) -> bool {
let metadata = match after.map(|value| return &value.state) {
std::option::Option::Some(crate::SolanaProgramMetadataObservedAccountState::Metadata(
value,
)) => value,
_ => return false,
};
if metadata.program.as_str() != program.0.as_str()
|| metadata.canonical != canonical
|| &metadata.seed.as_bytes()[..] != seed
|| metadata.encoding.wire_value() != encoding.wire_value()
|| metadata.compression.wire_value() != compression.wire_value()
|| metadata.format.wire_value() != format.wire_value()
{
return false;
}
let expected_source = match data {
std::option::Option::Some(value) => value.data_source(),
std::option::Option::None => data_source,
};
if metadata.data_source.wire_value() != expected_source.wire_value() {
return false;
}
return match data {
std::option::Option::Some(value) => data_matches(value, &metadata.data),
std::option::Option::None => true,
};
}
fn authority_postcondition(
new_authority: std::option::Option<&kb_lib::MdPubkey>,
after: std::option::Option<&crate::SolanaProgramMetadataStatefulReadResult>,
) -> bool {
let observed = match after.map(|value| return &value.state) {
std::option::Option::Some(crate::SolanaProgramMetadataObservedAccountState::Buffer(
value,
)) => value.authority.as_deref(),
std::option::Option::Some(crate::SolanaProgramMetadataObservedAccountState::Metadata(
value,
)) => value.authority.as_deref(),
_ => return false,
};
return observed == new_authority.map(|value| return value.0.as_str());
}
fn set_data_postcondition(
encoding: kb_lib::ExMetadataSpmEncoding,
compression: kb_lib::ExMetadataSpmCompression,
format: kb_lib::ExMetadataSpmFormat,
source: &kb_lib::ExMetadataSpmSetDataSource,
before: std::option::Option<&crate::SolanaProgramMetadataStatefulReadResult>,
after: std::option::Option<&crate::SolanaProgramMetadataStatefulReadResult>,
before_all: &[crate::SolanaProgramMetadataStatefulReadResult],
) -> bool {
let metadata = match after.map(|value| return &value.state) {
std::option::Option::Some(crate::SolanaProgramMetadataObservedAccountState::Metadata(
value,
)) => value,
_ => return false,
};
if metadata.encoding.wire_value() != encoding.wire_value()
|| metadata.compression.wire_value() != compression.wire_value()
|| metadata.format.wire_value() != format.wire_value()
{
return false;
}
return match source {
kb_lib::ExMetadataSpmSetDataSource::PreserveExisting => {
match before.map(|value| return &value.state) {
std::option::Option::Some(
crate::SolanaProgramMetadataObservedAccountState::Metadata(value),
) => metadata.data == value.data && metadata.data_source == value.data_source,
_ => false,
}
},
kb_lib::ExMetadataSpmSetDataSource::Inline { data } => data_matches(data, &metadata.data),
kb_lib::ExMetadataSpmSetDataSource::Buffer { buffer, data_source } => {
if metadata.data_source.wire_value() != data_source.wire_value() {
return false;
}
let source_snapshot = match find_snapshot(before_all, buffer) {
std::option::Option::Some(value) => value,
std::option::Option::None => return false,
};
let bytes = match &source_snapshot.state {
crate::SolanaProgramMetadataObservedAccountState::Buffer(value) => {
value.data.as_slice()
},
_ => return false,
};
return match &metadata.data {
kb_lib::DcMetadataSpmData::Direct(value) => value.as_slice() == bytes,
kb_lib::DcMetadataSpmData::Url(value) => value.as_bytes() == bytes,
kb_lib::DcMetadataSpmData::External(value) => &value.to_wire_bytes()[..] == bytes,
};
},
};
}
fn trim_postcondition(
before: std::option::Option<&crate::SolanaProgramMetadataStatefulReadResult>,
after: std::option::Option<&crate::SolanaProgramMetadataStatefulReadResult>,
) -> bool {
let before = match before {
std::option::Option::Some(value) => value,
std::option::Option::None => return false,
};
let after = match after {
std::option::Option::Some(value) => value,
std::option::Option::None => return false,
};
let same_kind = before.state.code() == after.state.code();
let size_reduced = match (before.space, after.space) {
(std::option::Option::Some(before_space), std::option::Option::Some(after_space)) => {
after_space <= before_space
},
_ => false,
};
return same_kind && size_reduced;
}
fn allocate_postcondition(
authority: &kb_lib::MdPubkey,
seed: std::option::Option<&[u8]>,
canonical: bool,
after: std::option::Option<&crate::SolanaProgramMetadataStatefulReadResult>,
) -> bool {
let buffer = match after.map(|value| return &value.state) {
std::option::Option::Some(crate::SolanaProgramMetadataObservedAccountState::Buffer(
value,
)) => value,
_ => return false,
};
if buffer.authority.as_deref() != std::option::Option::Some(authority.0.as_str()) {
return false;
}
return match seed {
std::option::Option::Some(value) => {
buffer.canonical == canonical && &buffer.seed.as_bytes()[..] == value
},
std::option::Option::None => !buffer.canonical,
};
}
fn extend_postcondition(
length: u16,
before: std::option::Option<&crate::SolanaProgramMetadataStatefulReadResult>,
after: std::option::Option<&crate::SolanaProgramMetadataStatefulReadResult>,
) -> bool {
let before = match before {
std::option::Option::Some(value) => value,
std::option::Option::None => return false,
};
let after = match after {
std::option::Option::Some(value) => value,
std::option::Option::None => return false,
};
let expected = match before.space.and_then(|value| return value.checked_add(u64::from(length)))
{
std::option::Option::Some(value) => value,
std::option::Option::None => return false,
};
return before.state.code() == after.state.code()
&& after.space == std::option::Option::Some(expected);
}
fn data_matches(
input: &kb_lib::ExMetadataSpmDataInput,
observed: &kb_lib::DcMetadataSpmData,
) -> bool {
return match (input, observed) {
(
kb_lib::ExMetadataSpmDataInput::Direct { bytes },
kb_lib::DcMetadataSpmData::Direct(value),
) => bytes == value,
(kb_lib::ExMetadataSpmDataInput::Url { url }, kb_lib::DcMetadataSpmData::Url(value)) => {
url == value
},
(
kb_lib::ExMetadataSpmDataInput::External { address, offset, length },
kb_lib::DcMetadataSpmData::External(value),
) => {
value.address.to_string().as_str() == address.0.as_str()
&& value.offset == *offset
&& value.length.map(std::num::NonZeroU32::get) == *length
},
_ => false,
};
}
#[cfg(test)]
mod tests {
fn pubkey(byte: u8) -> kb_lib::MdPubkey {
return kb_lib::MdPubkey(solana_pubkey::Pubkey::new_from_array([byte; 32]).to_string());
}
#[test]
fn readiness_requires_exact_simulation_and_resolved_signers() {
let signer = pubkey(1);
let plan = kb_lib::ExApiPreparedExecutionPlan {
executor_name: "kb-lib.executor.metadata.solana_program_metadata".to_string(),
executor_version: "0.4.8".to_string(),
intent_id: "spm-readiness".to_string(),
operation_code: kb_lib::EX_METADATA_SPM_WRITE_OPERATION.to_string(),
fee_payer: signer.clone(),
instructions: vec![kb_lib::ExApiPlannedInstruction {
program_id: kb_lib::MdProgramId(
kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID.to_string(),
),
operation_code: kb_lib::EX_METADATA_SPM_WRITE_OPERATION.to_string(),
accounts: vec![],
data: vec![0],
}],
required_signers: vec![kb_lib::ExApiRequiredSigner {
pubkey: signer.clone(),
role: "authority".to_string(),
}],
policy: kb_lib::ExApiExecutionPolicy {
dry_run: false,
..kb_lib::ExApiExecutionPolicy::default()
},
requested_spend_lamports: 0,
requested_compute_unit_price_micro_lamports: std::option::Option::None,
};
let request = crate::SolanaProgramMetadataExecutionReadinessRequest {
plan,
preflight: crate::SolanaProgramMetadataPreflightReport {
operation_code: kb_lib::EX_METADATA_SPM_WRITE_OPERATION.to_string(),
target_account: pubkey(2),
context_slot: 99,
inspected_accounts: vec![],
safety: kb_lib::ExSafetyEvaluation {
decision: kb_lib::ExSafetyDecision::Allow,
violations: vec![],
},
runtime_authority_validation_required: false,
checks: vec![],
},
message_hash: "message".to_string(),
simulated_message_hash: "message".to_string(),
simulated: true,
simulation_succeeded: true,
resolved_signers: vec![signer],
submit: true,
operator_confirmed: true,
};
let report = crate::validate_solana_program_metadata_execution_readiness(&request);
assert!(report.is_ok());
if let std::result::Result::Ok(value) = report {
assert!(value.send_authorized);
assert_eq!(value.context_slot, 99);
}
}
#[test]
fn pipeline_matrix_is_machine_readable_and_matches_nine_operations() {
let matrix_result = serde_json::from_str::<serde_json::Value>(include_str!(
"../../test-fixtures/contract-matrices/METADATA_SOLANA_PROGRAM_PIPELINE_MATRIX.json"
));
assert!(matrix_result.is_ok());
if let std::result::Result::Ok(matrix) = matrix_result {
assert_eq!(
matrix["programId"],
kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID
);
assert_eq!(
matrix["operations"].as_array().map(std::vec::Vec::len),
std::option::Option::Some(kb_lib::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES.len())
);
assert_eq!(matrix["pipelineCrate"], "kb-pipeline");
assert_eq!(matrix["demoScenarioCrate"], "kb-pipeline-demo-scenarios");
}
}
#[test]
fn every_stable_operation_has_one_explicit_postcondition_path() {
let account = pubkey(4);
let authority = pubkey(5);
let program = pubkey(6);
let destination = pubkey(7);
let operations = vec![
kb_lib::ExMetadataSpmOperation::Write {
buffer: account.clone(),
authority: authority.clone(),
offset: 0,
source: kb_lib::ExMetadataSpmWriteSource::Inline { data: vec![1] },
},
kb_lib::ExMetadataSpmOperation::Initialize {
metadata: account.clone(),
authority: authority.clone(),
program: program.clone(),
program_data: std::option::Option::None,
canonical: false,
seed: vec![0_u8; kb_lib::DC_METADATA_SPM_SEED_BYTES],
encoding: kb_lib::ExMetadataSpmEncoding::Utf8,
compression: kb_lib::ExMetadataSpmCompression::None,
format: kb_lib::ExMetadataSpmFormat::Json,
data_source: kb_lib::ExMetadataSpmDataSource::Direct,
data: std::option::Option::Some(kb_lib::ExMetadataSpmDataInput::Direct {
bytes: vec![1],
}),
allocate_account: true,
},
kb_lib::ExMetadataSpmOperation::SetAuthority {
account: account.clone(),
authority: authority.clone(),
new_authority: std::option::Option::Some(pubkey(8)),
program_context: std::option::Option::None,
},
kb_lib::ExMetadataSpmOperation::SetData {
metadata: account.clone(),
authority: authority.clone(),
encoding: kb_lib::ExMetadataSpmEncoding::Utf8,
compression: kb_lib::ExMetadataSpmCompression::None,
format: kb_lib::ExMetadataSpmFormat::Json,
source: kb_lib::ExMetadataSpmSetDataSource::PreserveExisting,
program_context: std::option::Option::None,
},
kb_lib::ExMetadataSpmOperation::SetImmutable {
metadata: account.clone(),
authority: authority.clone(),
program_context: std::option::Option::None,
},
kb_lib::ExMetadataSpmOperation::Trim {
account: account.clone(),
authority: authority.clone(),
destination: destination.clone(),
program_context: std::option::Option::None,
},
kb_lib::ExMetadataSpmOperation::Close {
account: account.clone(),
authority: authority.clone(),
destination,
program_context: std::option::Option::None,
},
kb_lib::ExMetadataSpmOperation::Allocate {
buffer: account.clone(),
authority: authority.clone(),
seed: std::option::Option::None,
program_context: std::option::Option::None,
canonical: false,
allocate_account: true,
},
kb_lib::ExMetadataSpmOperation::Extend {
account,
authority,
length: 1,
program_context: std::option::Option::None,
},
];
let mut codes = operations
.iter()
.map(|operation| return operation.operation_code())
.collect::<std::vec::Vec<_>>();
codes.sort();
let mut expected = kb_lib::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES.to_vec();
expected.sort();
assert_eq!(codes, expected);
for operation in operations {
let report = crate::inspect_solana_program_metadata_post_execution(
&crate::SolanaProgramMetadataPostExecutionRequest {
operation,
before: vec![],
after: vec![],
},
);
assert!(report.is_ok());
if let std::result::Result::Ok(value) = report {
assert_eq!(
value.status,
crate::SolanaProgramMetadataPostconditionStatus::NotApplicable
);
assert_eq!(value.postconditions.len(), 1);
}
}
}
#[test]
fn postcondition_summary_never_invents_success() {
let account = pubkey(3);
let item = crate::SolanaProgramMetadataPostcondition {
code: "spm.none".to_string(),
account,
status: crate::SolanaProgramMetadataPostconditionStatus::NotApplicable,
diagnostic: "missing after state".to_string(),
};
assert_eq!(
crate::summarize_solana_program_metadata_postconditions(&[item]),
crate::SolanaProgramMetadataPostconditionStatus::NotApplicable
);
}
}

View File

@@ -0,0 +1,761 @@
// file: kb-pipeline/src/metadata_solana_program_preflight.rs
// version: 2
//! Stateful preflight for Solana Program Metadata execution plans.
/// Maximum number of bounded account snapshots accepted by one preflight.
pub const MAX_SOLANA_PROGRAM_METADATA_PREFLIGHT_ACCOUNTS: usize = 16;
/// One rent-exemption observation supplied by the caller for a growth operation.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct SolanaProgramMetadataRentObservation {
/// Account whose allocation or growth is being validated.
pub account: kb_lib::MdPubkey,
/// Target account-data size used for the rent query.
pub target_space: u64,
/// Lamports observed on the account before execution.
pub observed_lamports: u64,
/// Minimum lamports returned for `target_space`.
pub required_lamports: u64,
}
impl crate::SolanaProgramMetadataRentObservation {
/// Returns whether the account is sufficiently pre-funded.
pub const fn is_satisfied(&self) -> bool {
return self.observed_lamports >= self.required_lamports;
}
}
/// Complete stateful preflight request for one prepared Program Metadata plan.
#[derive(Clone, Debug, PartialEq)]
pub struct SolanaProgramMetadataPreflightRequest {
/// Exact typed intent used to build the plan.
pub intent: kb_lib::ExMetadataSpmExecutionIntent,
/// Exact prepared plan produced by `kb-lib`.
pub plan: kb_lib::ExApiPreparedExecutionPlan,
/// Confirmed account snapshots observed before simulation.
pub before: std::vec::Vec<crate::SolanaProgramMetadataStatefulReadResult>,
/// Explicit rent observations required by account growth operations.
pub rent_observations: std::vec::Vec<crate::SolanaProgramMetadataRentObservation>,
}
/// Deterministic Program Metadata preflight report.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct SolanaProgramMetadataPreflightReport {
/// Stable operation code.
pub operation_code: std::string::String,
/// Primary account mutated by the instruction.
pub target_account: kb_lib::MdPubkey,
/// Highest confirmed context slot across supplied snapshots.
pub context_slot: u64,
/// Canonical accounts inspected by the preflight.
pub inspected_accounts: std::vec::Vec<kb_lib::MdPubkey>,
/// Safety decision applied before simulation.
pub safety: kb_lib::ExSafetyEvaluation,
/// Whether the on-chain program must validate an upgrade authority at runtime.
pub runtime_authority_validation_required: bool,
/// Ordered successful checks.
pub checks: std::vec::Vec<std::string::String>,
}
/// Validates one Program Metadata plan against confirmed pre-execution state.
pub fn inspect_solana_program_metadata_preflight(
request: &crate::SolanaProgramMetadataPreflightRequest,
) -> kb_core::Result<crate::SolanaProgramMetadataPreflightReport> {
if request.before.len() > crate::MAX_SOLANA_PROGRAM_METADATA_PREFLIGHT_ACCOUNTS {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_account_limit_exceeded",
"Solana Program Metadata preflight account limit exceeded",
));
}
let operation_code = request.intent.operation.operation_code();
if request.plan.operation_code != operation_code {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_operation_mismatch",
"Program Metadata intent and plan operation codes must match",
));
}
if request.plan.instructions.len() != 1 {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_instruction_count_invalid",
"Program Metadata plans must contain exactly one instruction",
));
}
let instruction = match request.plan.instructions.first() {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_instruction_missing",
"Program Metadata plan instruction is missing",
));
},
};
if instruction.program_id.0 != kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID
|| instruction.operation_code != operation_code
|| !kb_lib::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES.contains(&operation_code)
{
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_instruction_contract_mismatch",
"Program Metadata plan does not preserve the exact program and operation contract",
));
}
if request.intent.operation.requires_explicit_approval()
&& !request.intent.allow_destructive_operation
{
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_destructive_approval_required",
"destructive Program Metadata execution requires explicit approval",
));
}
let safety = match kb_lib::ExSafetyChecker.evaluate_prepared_plan(&request.plan) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if safety.decision == kb_lib::ExSafetyDecision::Deny {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_safety_denied",
safety_violation_codes(safety.violations.as_slice()),
));
}
let mut snapshots = std::collections::BTreeMap::<
std::string::String,
&crate::SolanaProgramMetadataStatefulReadResult,
>::new();
let mut context_slot = 0_u64;
for snapshot in &request.before {
if snapshot.commitment != "confirmed" {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_commitment_mismatch",
"Program Metadata stateful snapshots must use confirmed commitment",
));
}
if snapshots.insert(snapshot.account.0.clone(), snapshot).is_some() {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_duplicate_account",
format!("duplicate Program Metadata snapshot for {}", snapshot.account.0),
));
}
context_slot = context_slot.max(snapshot.context_slot);
}
let target_account = operation_target(&request.intent.operation).clone();
let target = match snapshots.get(target_account.0.as_str()) {
std::option::Option::Some(value) => *value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_target_snapshot_missing",
format!("missing preflight snapshot for {}", target_account.0),
));
},
};
if let std::result::Result::Err(error) =
validate_target_state(&request.intent.operation, target)
{
return std::result::Result::Err(error);
}
if let std::result::Result::Err(error) =
validate_source_state(&request.intent.operation, &snapshots, target)
{
return std::result::Result::Err(error);
}
let runtime_authority_validation_required =
match validate_authority_state(&request.intent.operation, target) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if let std::result::Result::Err(error) = validate_rent_observations(
&request.intent.operation,
target,
&snapshots,
request.rent_observations.as_slice(),
) {
return std::result::Result::Err(error);
}
let inspected_accounts = snapshots
.values()
.map(|value| return value.account.clone())
.collect::<std::vec::Vec<_>>();
tracing::debug!(
target: crate::TRACING_TARGET,
action = "inspect_solana_program_metadata_preflight",
operation_code,
target_account = %target_account.0,
context_slot,
inspected_account_count = inspected_accounts.len(),
runtime_authority_validation_required,
safety_decision = ?safety.decision,
"validated Solana Program Metadata stateful preflight"
);
return std::result::Result::Ok(crate::SolanaProgramMetadataPreflightReport {
operation_code: operation_code.to_string(),
target_account,
context_slot,
inspected_accounts,
safety,
runtime_authority_validation_required,
checks: vec![
"program_id_exact".to_string(),
"operation_code_exact".to_string(),
"single_instruction_plan".to_string(),
"confirmed_state_snapshots".to_string(),
"target_state_compatible".to_string(),
"source_state_compatible".to_string(),
"authority_contract_checked".to_string(),
"rent_prefunding_checked_when_required".to_string(),
"common_safety_checked_before_simulation".to_string(),
],
});
}
fn operation_target(operation: &kb_lib::ExMetadataSpmOperation) -> &kb_lib::MdPubkey {
return match operation {
kb_lib::ExMetadataSpmOperation::Write { buffer, .. }
| kb_lib::ExMetadataSpmOperation::Allocate { buffer, .. } => buffer,
kb_lib::ExMetadataSpmOperation::Initialize { metadata, .. }
| kb_lib::ExMetadataSpmOperation::SetData { metadata, .. }
| kb_lib::ExMetadataSpmOperation::SetImmutable { metadata, .. } => metadata,
kb_lib::ExMetadataSpmOperation::SetAuthority { account, .. }
| kb_lib::ExMetadataSpmOperation::Trim { account, .. }
| kb_lib::ExMetadataSpmOperation::Close { account, .. }
| kb_lib::ExMetadataSpmOperation::Extend { account, .. } => account,
};
}
fn validate_target_state(
operation: &kb_lib::ExMetadataSpmOperation,
target: &crate::SolanaProgramMetadataStatefulReadResult,
) -> kb_core::Result<()> {
let valid = match operation {
kb_lib::ExMetadataSpmOperation::Write { .. } => {
matches!(&target.state, crate::SolanaProgramMetadataObservedAccountState::Buffer(_))
},
kb_lib::ExMetadataSpmOperation::Initialize { data, .. } => {
if data.is_some() {
is_prefunded_uninitialized(target)
} else {
matches!(&target.state, crate::SolanaProgramMetadataObservedAccountState::Buffer(_))
}
},
kb_lib::ExMetadataSpmOperation::SetData { .. }
| kb_lib::ExMetadataSpmOperation::SetImmutable { .. } => {
matches!(&target.state, crate::SolanaProgramMetadataObservedAccountState::Metadata(_))
},
kb_lib::ExMetadataSpmOperation::Allocate { .. } => is_prefunded_uninitialized(target),
kb_lib::ExMetadataSpmOperation::SetAuthority { .. }
| kb_lib::ExMetadataSpmOperation::Trim { .. }
| kb_lib::ExMetadataSpmOperation::Close { .. }
| kb_lib::ExMetadataSpmOperation::Extend { .. } => matches!(
&target.state,
crate::SolanaProgramMetadataObservedAccountState::Buffer(_)
| crate::SolanaProgramMetadataObservedAccountState::Metadata(_)
),
};
if !valid {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_target_state_invalid",
format!(
"operation {} cannot use target state {}",
operation.operation_code(),
target.state.code()
),
));
}
return std::result::Result::Ok(());
}
fn is_prefunded_uninitialized(target: &crate::SolanaProgramMetadataStatefulReadResult) -> bool {
return match &target.state {
crate::SolanaProgramMetadataObservedAccountState::Uninitialized {
owner,
lamports,
space: _,
} => {
*lamports > 0
&& (owner.0 == kb_program_ids::SYSTEM_PROGRAM_ID
|| owner.0 == kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID)
},
_ => false,
};
}
fn validate_source_state(
operation: &kb_lib::ExMetadataSpmOperation,
snapshots: &std::collections::BTreeMap<
std::string::String,
&crate::SolanaProgramMetadataStatefulReadResult,
>,
target: &crate::SolanaProgramMetadataStatefulReadResult,
) -> kb_core::Result<()> {
match operation {
kb_lib::ExMetadataSpmOperation::Write { offset, source, .. } => {
let target_buffer = match &target.state {
crate::SolanaProgramMetadataObservedAccountState::Buffer(value) => value,
_ => return std::result::Result::Ok(()),
};
let source_length = match source {
kb_lib::ExMetadataSpmWriteSource::Inline { data } => data.len(),
kb_lib::ExMetadataSpmWriteSource::Buffer { source_buffer } => {
let source_snapshot = match snapshots.get(source_buffer.0.as_str()) {
std::option::Option::Some(value) => *value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_source_buffer_missing",
format!("missing source Buffer snapshot for {}", source_buffer.0),
));
},
};
match &source_snapshot.state {
crate::SolanaProgramMetadataObservedAccountState::Buffer(value) => {
value.data.len()
},
_ => {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_source_buffer_invalid",
"Write source account must be an initialized Buffer",
));
},
}
},
};
let offset = match usize::try_from(*offset) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_write_offset_invalid",
error.to_string(),
));
},
};
if offset.saturating_add(source_length) > target_buffer.allocated_data_bytes {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_write_out_of_bounds",
"Write source exceeds the allocated Buffer capacity",
));
}
},
kb_lib::ExMetadataSpmOperation::SetData {
source: kb_lib::ExMetadataSpmSetDataSource::Buffer { buffer, .. },
..
} => {
let source_snapshot = match snapshots.get(buffer.0.as_str()) {
std::option::Option::Some(value) => *value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_source_buffer_missing",
format!("missing source Buffer snapshot for {}", buffer.0),
));
},
};
if !matches!(
&source_snapshot.state,
crate::SolanaProgramMetadataObservedAccountState::Buffer(_)
) {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_source_buffer_invalid",
"SetData source account must be an initialized Buffer",
));
}
},
_ => {},
}
return std::result::Result::Ok(());
}
fn validate_authority_state(
operation: &kb_lib::ExMetadataSpmOperation,
target: &crate::SolanaProgramMetadataStatefulReadResult,
) -> kb_core::Result<bool> {
let (authority, has_program_context) = match operation {
kb_lib::ExMetadataSpmOperation::Write { authority: _, .. } => {
return std::result::Result::Ok(false);
},
kb_lib::ExMetadataSpmOperation::Initialize { canonical, .. } => {
return std::result::Result::Ok(*canonical);
},
kb_lib::ExMetadataSpmOperation::Allocate { seed, canonical, .. } => {
return std::result::Result::Ok(seed.is_some() && *canonical);
},
kb_lib::ExMetadataSpmOperation::SetAuthority { authority, program_context, .. }
| kb_lib::ExMetadataSpmOperation::SetData { authority, program_context, .. }
| kb_lib::ExMetadataSpmOperation::SetImmutable { authority, program_context, .. }
| kb_lib::ExMetadataSpmOperation::Trim { authority, program_context, .. }
| kb_lib::ExMetadataSpmOperation::Close { authority, program_context, .. }
| kb_lib::ExMetadataSpmOperation::Extend { authority, program_context, .. } => {
(authority, program_context.is_some())
},
};
let (current_authority, canonical) = match &target.state {
crate::SolanaProgramMetadataObservedAccountState::Buffer(value) => {
(value.authority.as_deref(), value.canonical)
},
crate::SolanaProgramMetadataObservedAccountState::Metadata(value) => {
(value.authority.as_deref(), value.canonical)
},
_ => return std::result::Result::Ok(false),
};
if current_authority == std::option::Option::Some(authority.0.as_str()) {
return std::result::Result::Ok(false);
}
if canonical && has_program_context {
return std::result::Result::Ok(true);
}
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_authority_mismatch",
"the supplied authority does not match account state and no canonical program context was provided",
));
}
fn validate_rent_observations(
operation: &kb_lib::ExMetadataSpmOperation,
target: &crate::SolanaProgramMetadataStatefulReadResult,
snapshots: &std::collections::BTreeMap<
std::string::String,
&crate::SolanaProgramMetadataStatefulReadResult,
>,
observations: &[crate::SolanaProgramMetadataRentObservation],
) -> kb_core::Result<()> {
let expected_space = match expected_rent_target_space(operation, target, snapshots) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let expected_space = match expected_space {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Ok(()),
};
let observation = match observations.iter().find(|value| return value.account == target.account)
{
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_rent_observation_missing",
format!("missing rent observation for {}", target.account.0),
));
},
};
let observed_lamports = match target.lamports {
std::option::Option::Some(value) => value,
std::option::Option::None => 0,
};
if observation.target_space != expected_space
|| observation.observed_lamports != observed_lamports
|| !observation.is_satisfied()
{
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_rent_not_satisfied",
format!(
"account {} has {} lamports but requires {} for exact target space {}",
observation.account.0,
observation.observed_lamports,
observation.required_lamports,
expected_space
),
));
}
return std::result::Result::Ok(());
}
fn expected_rent_target_space(
operation: &kb_lib::ExMetadataSpmOperation,
target: &crate::SolanaProgramMetadataStatefulReadResult,
snapshots: &std::collections::BTreeMap<
std::string::String,
&crate::SolanaProgramMetadataStatefulReadResult,
>,
) -> kb_core::Result<std::option::Option<u64>> {
let header = match u64::try_from(kb_lib::DC_METADATA_SPM_METADATA_HEADER_BYTES) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_header_size_overflow",
error.to_string(),
));
},
};
return match operation {
kb_lib::ExMetadataSpmOperation::Initialize {
data: std::option::Option::Some(data),
..
} => add_metadata_header(header, metadata_input_wire_len(data)),
kb_lib::ExMetadataSpmOperation::SetData {
source: kb_lib::ExMetadataSpmSetDataSource::PreserveExisting,
..
} => std::result::Result::Ok(std::option::Option::None),
kb_lib::ExMetadataSpmOperation::SetData {
source: kb_lib::ExMetadataSpmSetDataSource::Inline { data },
..
} => add_metadata_header(header, metadata_input_wire_len(data)),
kb_lib::ExMetadataSpmOperation::SetData {
source: kb_lib::ExMetadataSpmSetDataSource::Buffer { buffer, .. },
..
} => {
let source = match snapshots.get(buffer.0.as_str()) {
std::option::Option::Some(value) => *value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_source_buffer_missing",
format!("missing source Buffer snapshot for {}", buffer.0),
));
},
};
let length = match &source.state {
crate::SolanaProgramMetadataObservedAccountState::Buffer(value) => {
match u64::try_from(value.data.len()) {
std::result::Result::Ok(length) => length,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_source_length_overflow",
error.to_string(),
));
},
}
},
_ => {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_source_buffer_invalid",
"SetData source account must be an initialized Buffer",
));
},
};
return add_metadata_header(header, std::result::Result::Ok(length));
},
kb_lib::ExMetadataSpmOperation::Allocate { .. } => {
let current = match target.space {
std::option::Option::Some(value) => value,
std::option::Option::None => 0,
};
std::result::Result::Ok(std::option::Option::Some(current.max(header)))
},
kb_lib::ExMetadataSpmOperation::Extend { length, .. } => {
let current = match target.space {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_target_space_missing",
"Extend requires the current account space",
));
},
};
return match current.checked_add(u64::from(*length)) {
std::option::Option::Some(value) => {
std::result::Result::Ok(std::option::Option::Some(value))
},
std::option::Option::None => std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_target_space_overflow",
"Extend target space overflowed u64",
)),
};
},
_ => std::result::Result::Ok(std::option::Option::None),
};
}
fn metadata_input_wire_len(data: &kb_lib::ExMetadataSpmDataInput) -> kb_core::Result<u64> {
let length = match data {
kb_lib::ExMetadataSpmDataInput::Direct { bytes } => bytes.len(),
kb_lib::ExMetadataSpmDataInput::Url { url } => url.len(),
kb_lib::ExMetadataSpmDataInput::External { .. } => {
kb_lib::DC_METADATA_SPM_EXTERNAL_DATA_BYTES
},
};
return match u64::try_from(length) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_data_length_overflow",
error.to_string(),
)),
};
}
fn add_metadata_header(
header: u64,
data_length: kb_core::Result<u64>,
) -> kb_core::Result<std::option::Option<u64>> {
let data_length = match data_length {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return match header.checked_add(data_length) {
std::option::Option::Some(value) => {
std::result::Result::Ok(std::option::Option::Some(value))
},
std::option::Option::None => std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_preflight_target_space_overflow",
"Program Metadata target space overflowed u64",
)),
};
}
fn safety_violation_codes(violations: &[kb_lib::ExSafetyViolation]) -> std::string::String {
if violations.is_empty() {
return "Program Metadata execution safety denied without a diagnostic".to_string();
}
return violations
.iter()
.map(|value| return value.code.as_str())
.collect::<std::vec::Vec<_>>()
.join(",");
}
#[cfg(test)]
mod tests {
fn pubkey(value: u8) -> kb_lib::MdPubkey {
return kb_lib::MdPubkey(solana_pubkey::Pubkey::new_from_array([value; 32]).to_string());
}
fn policy(authority: &kb_lib::MdPubkey) -> kb_lib::ExApiExecutionPolicy {
return kb_lib::ExApiExecutionPolicy {
cost_limit: kb_lib::ExApiExecutionCostLimit {
max_spend_lamports: std::option::Option::Some(0),
max_fee_lamports: std::option::Option::Some(20_000),
max_compute_unit_price_micro_lamports: std::option::Option::None,
},
authorized_signers: vec![authority.clone()],
post_execution_validation: kb_lib::ExApiPostExecutionValidationPolicy {
canonical_insert_required: true,
core_extraction_required: true,
decode_replay_required: true,
materialization_required: true,
},
..kb_lib::ExApiExecutionPolicy::default()
};
}
fn buffer_read(
account: kb_lib::MdPubkey,
authority: kb_lib::MdPubkey,
) -> crate::SolanaProgramMetadataStatefulReadResult {
return crate::SolanaProgramMetadataStatefulReadResult {
commitment: "confirmed".to_string(),
context_slot: 42,
account: account.clone(),
lamports: std::option::Option::Some(1_000),
owner: std::option::Option::Some(kb_lib::MdProgramId(
kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID.to_string(),
)),
space: std::option::Option::Some(100),
state: crate::SolanaProgramMetadataObservedAccountState::Buffer(
kb_lib::DcMetadataSpmBufferAccountSnapshot {
account: account.0,
program: std::option::Option::None,
authority: std::option::Option::Some(authority.0),
canonical: false,
seed: kb_lib::DcMetadataSpmSeed::default(),
data: vec![0_u8; 4],
allocated_data_bytes: 4,
},
),
materialized_output: std::option::Option::None,
};
}
#[test]
fn write_preflight_requires_buffer_capacity_and_common_safety() {
let authority = pubkey(2);
let buffer = pubkey(3);
let intent = kb_lib::ExMetadataSpmExecutionIntent {
intent_id: "spm-preflight-write".to_string(),
fee_payer: authority.clone(),
policy: policy(&authority),
allow_destructive_operation: true,
operation: kb_lib::ExMetadataSpmOperation::Write {
buffer: buffer.clone(),
authority: authority.clone(),
offset: 2,
source: kb_lib::ExMetadataSpmWriteSource::Inline { data: vec![8, 9] },
},
};
let plan = kb_lib::ExApiTypedInstructionExecutor::build_prepared_plan(
&kb_lib::ExMetadataSolanaProgramMetadataExecutor,
&intent,
);
assert!(plan.is_ok());
let plan = match plan {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let report = crate::inspect_solana_program_metadata_preflight(
&crate::SolanaProgramMetadataPreflightRequest {
intent,
plan,
before: vec![buffer_read(buffer, authority)],
rent_observations: vec![],
},
);
assert!(report.is_ok());
if let std::result::Result::Ok(value) = report {
assert_eq!(value.operation_code, kb_lib::EX_METADATA_SPM_WRITE_OPERATION);
assert_eq!(value.safety.decision, kb_lib::ExSafetyDecision::Allow);
}
}
#[test]
fn allocate_requires_vacancy_and_explicit_rent_evidence() {
let authority = pubkey(4);
let buffer = authority.clone();
let intent = kb_lib::ExMetadataSpmExecutionIntent {
intent_id: "spm-preflight-allocate".to_string(),
fee_payer: authority.clone(),
policy: policy(&authority),
allow_destructive_operation: false,
operation: kb_lib::ExMetadataSpmOperation::Allocate {
buffer: buffer.clone(),
authority: authority.clone(),
seed: std::option::Option::None,
program_context: std::option::Option::None,
canonical: false,
allocate_account: true,
},
};
let plan = kb_lib::ExApiTypedInstructionExecutor::build_prepared_plan(
&kb_lib::ExMetadataSolanaProgramMetadataExecutor,
&intent,
);
assert!(plan.is_ok());
let plan = match plan {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let before = crate::SolanaProgramMetadataStatefulReadResult {
commitment: "confirmed".to_string(),
context_slot: 9,
account: buffer.clone(),
lamports: std::option::Option::Some(1_000),
owner: std::option::Option::Some(kb_lib::MdProgramId(
kb_program_ids::SYSTEM_PROGRAM_ID.to_string(),
)),
space: std::option::Option::Some(0),
state: crate::SolanaProgramMetadataObservedAccountState::Uninitialized {
owner: kb_lib::MdProgramId(kb_program_ids::SYSTEM_PROGRAM_ID.to_string()),
lamports: 1_000,
space: 0,
},
materialized_output: std::option::Option::None,
};
let missing_rent = crate::inspect_solana_program_metadata_preflight(
&crate::SolanaProgramMetadataPreflightRequest {
intent: intent.clone(),
plan: plan.clone(),
before: vec![before.clone()],
rent_observations: vec![],
},
);
assert!(missing_rent.is_err());
let target_space = match u64::try_from(kb_lib::DC_METADATA_SPM_BUFFER_HEADER_BYTES) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let report = crate::inspect_solana_program_metadata_preflight(
&crate::SolanaProgramMetadataPreflightRequest {
intent,
plan,
before: vec![before],
rent_observations: vec![crate::SolanaProgramMetadataRentObservation {
account: buffer,
target_space,
observed_lamports: 1_000,
required_lamports: 900,
}],
},
);
assert!(report.is_ok());
}
}

View File

@@ -0,0 +1,425 @@
// file: kb-pipeline/src/metadata_solana_program_stateful.rs
// version: 2
//! Bounded Solana Program Metadata account reads and authoritative state projections.
/// Maximum complete Solana Program Metadata account data accepted by one RPC read.
pub const MAX_SOLANA_PROGRAM_METADATA_STATEFUL_ACCOUNT_BYTES: usize =
kb_lib::DC_METADATA_SPM_MAX_ACCOUNT_BYTES;
/// Expected state of one Solana Program Metadata account read.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum SolanaProgramMetadataExpectedAccountState {
/// Accept every supported state.
Any,
/// Accept an absent or zeroed account before initialization.
Vacant,
/// Require an absent account.
Missing,
/// Require an initialized Buffer account.
Buffer,
/// Require an initialized Metadata account.
Metadata,
}
/// Supported state observed for one Solana Program Metadata account.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "state", content = "snapshot", rename_all = "snake_case")]
pub enum SolanaProgramMetadataObservedAccountState {
/// The address does not currently exist.
Missing,
/// The address exists but contains only zeroed or empty data and is not initialized.
Uninitialized {
/// Current owner before Program Metadata assignment.
owner: kb_lib::MdProgramId,
/// Current lamports retained by the account.
lamports: u64,
/// Current allocated account-data bytes.
space: u64,
},
/// Decoded Program Metadata Buffer account.
Buffer(kb_lib::DcMetadataSpmBufferAccountSnapshot),
/// Decoded Program Metadata Metadata account.
Metadata(kb_lib::DcMetadataSpmMetadataAccountSnapshot),
}
impl crate::SolanaProgramMetadataObservedAccountState {
/// Returns the stable lowercase state code.
pub const fn code(&self) -> &'static str {
return match self {
Self::Missing => "missing",
Self::Uninitialized { owner: _, lamports: _, space: _ } => "uninitialized",
Self::Buffer(_) => "buffer",
Self::Metadata(_) => "metadata",
};
}
/// Returns whether the state is absent or uninitialized.
pub const fn is_vacant(&self) -> bool {
return matches!(
self,
Self::Missing | Self::Uninitialized { owner: _, lamports: _, space: _ }
);
}
}
/// One bounded Solana Program Metadata account read request.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct SolanaProgramMetadataStatefulReadRequest {
/// Endpoint role used for the HTTP RPC request.
pub query_role: std::string::String,
/// Account address to inspect.
pub account: kb_lib::MdPubkey,
/// Expected account state.
pub expected_state: crate::SolanaProgramMetadataExpectedAccountState,
/// Optional minimum RPC context slot.
pub min_context_slot: std::option::Option<u64>,
/// Maximum decoded account bytes accepted from the endpoint.
pub max_data_bytes: usize,
}
impl crate::SolanaProgramMetadataStatefulReadRequest {
/// Validates the bounded request.
pub fn validate(&self) -> kb_core::Result<()> {
if self.query_role.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"Solana Program Metadata stateful query_role must not be empty",
));
}
let account_result = kb_onchain_transport::validate_solana_pubkey_text(
self.account.0.as_str(),
"Solana Program Metadata stateful account",
);
if let std::result::Result::Err(error) = account_result {
return std::result::Result::Err(error);
}
if self.max_data_bytes == 0
|| self.max_data_bytes > crate::MAX_SOLANA_PROGRAM_METADATA_STATEFUL_ACCOUNT_BYTES
{
return std::result::Result::Err(kb_core::Error::config(format!(
"Solana Program Metadata stateful max_data_bytes must be between 1 and {}",
crate::MAX_SOLANA_PROGRAM_METADATA_STATEFUL_ACCOUNT_BYTES
)));
}
return std::result::Result::Ok(());
}
}
/// One bounded RPC read and its validated Program Metadata state.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct SolanaProgramMetadataStatefulReadResult {
/// Commitment used by the read.
pub commitment: std::string::String,
/// Context slot returned by the endpoint.
pub context_slot: u64,
/// Account address requested by the caller.
pub account: kb_lib::MdPubkey,
/// Current lamports when the account exists.
pub lamports: std::option::Option<u64>,
/// Current owner when the account exists.
pub owner: std::option::Option<kb_lib::MdProgramId>,
/// Current account-data allocation when the account exists.
pub space: std::option::Option<u64>,
/// Validated account state.
pub state: crate::SolanaProgramMetadataObservedAccountState,
/// Authoritative materialized projection for Buffer or Metadata state.
pub materialized_output: std::option::Option<kb_lib::MtApiMaterializedOutput>,
}
/// Reads one bounded Solana Program Metadata account.
pub async fn read_solana_program_metadata_stateful_snapshot(
pool: &kb_onchain_transport::HttpEndpointPool,
request: &crate::SolanaProgramMetadataStatefulReadRequest,
) -> kb_core::Result<crate::SolanaProgramMetadataStatefulReadResult> {
if let std::result::Result::Err(error) = request.validate() {
return std::result::Result::Err(error);
}
let config = match kb_onchain_transport::GetAccountInfoConfig::new_with_data(
kb_onchain_transport::RpcCommitmentLevel::Confirmed,
request.min_context_slot,
request.max_data_bytes,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let result = match pool
.get_account_info_for_role(request.query_role.as_str(), &request.account, &config)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return crate::materialize_solana_program_metadata_account_info_result(request, &result);
}
/// Validates one RPC account response, decodes its state and emits the authoritative projection.
pub fn materialize_solana_program_metadata_account_info_result(
request: &crate::SolanaProgramMetadataStatefulReadRequest,
result: &kb_onchain_transport::AccountInfoResult,
) -> kb_core::Result<crate::SolanaProgramMetadataStatefulReadResult> {
if let std::result::Result::Err(error) = request.validate() {
return std::result::Result::Err(error);
}
if let std::option::Option::Some(minimum) = request.min_context_slot {
if result.context.slot < minimum {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_stateful_context_slot_too_old",
"Solana Program Metadata account context slot is below the requested minimum",
));
}
}
let output = match result.account.as_ref() {
std::option::Option::None => crate::SolanaProgramMetadataStatefulReadResult {
commitment: "confirmed".to_string(),
context_slot: result.context.slot,
account: request.account.clone(),
lamports: std::option::Option::None,
owner: std::option::Option::None,
space: std::option::Option::None,
state: crate::SolanaProgramMetadataObservedAccountState::Missing,
materialized_output: std::option::Option::None,
},
std::option::Option::Some(account) => {
if account.executable {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_stateful_account_executable",
"Solana Program Metadata state accounts must not be executable",
));
}
let data_len = match u64::try_from(account.data.len()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_stateful_account_length_overflow",
error.to_string(),
));
},
};
if account.space != data_len || account.data.len() > request.max_data_bytes {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_stateful_account_data_invalid",
"Solana Program Metadata account data is incomplete or above the configured bound",
));
}
let zeroed = account.data.iter().all(|byte| return *byte == 0);
if account.data.is_empty() || zeroed {
crate::SolanaProgramMetadataStatefulReadResult {
commitment: "confirmed".to_string(),
context_slot: result.context.slot,
account: request.account.clone(),
lamports: std::option::Option::Some(account.lamports),
owner: std::option::Option::Some(account.owner.clone()),
space: std::option::Option::Some(account.space),
state: crate::SolanaProgramMetadataObservedAccountState::Uninitialized {
owner: account.owner.clone(),
lamports: account.lamports,
space: account.space,
},
materialized_output: std::option::Option::None,
}
} else {
if account.owner.0 != kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_stateful_owner_mismatch",
"initialized Solana Program Metadata state must be owned by ProgM6",
));
}
let snapshot = match kb_lib::decoder_metadata_solana_program_metadata_decode_account(
request.account.0.as_str(),
account.owner.0.as_str(),
account.data.as_slice(),
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_stateful_decode_failed",
error.to_string(),
));
},
};
let materialized = match kb_lib::materializer_metadata_materialize_solana_program_metadata_account_snapshot(
result.context.slot,
&snapshot,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_stateful_materialization_failed",
error,
));
},
};
let state = match snapshot {
kb_lib::DcMetadataSpmAccountSnapshot::Buffer(value) => {
crate::SolanaProgramMetadataObservedAccountState::Buffer(value)
},
kb_lib::DcMetadataSpmAccountSnapshot::Metadata(value) => {
crate::SolanaProgramMetadataObservedAccountState::Metadata(value)
},
};
crate::SolanaProgramMetadataStatefulReadResult {
commitment: "confirmed".to_string(),
context_slot: result.context.slot,
account: request.account.clone(),
lamports: std::option::Option::Some(account.lamports),
owner: std::option::Option::Some(account.owner.clone()),
space: std::option::Option::Some(account.space),
state,
materialized_output: std::option::Option::Some(materialized),
}
}
},
};
if !expected_state_matches(request.expected_state, &output.state) {
return std::result::Result::Err(kb_core::Error::new(
"metadata_solana_program_stateful_expected_state_mismatch",
format!(
"expected {:?}, observed {} for {}",
request.expected_state,
output.state.code(),
request.account.0
),
));
}
tracing::debug!(
target: crate::TRACING_TARGET,
action = "read_solana_program_metadata_state",
account = %request.account.0,
context_slot = result.context.slot,
observed_state = output.state.code(),
materialized = output.materialized_output.is_some(),
"validated Solana Program Metadata account state"
);
return std::result::Result::Ok(output);
}
fn expected_state_matches(
expected: crate::SolanaProgramMetadataExpectedAccountState,
observed: &crate::SolanaProgramMetadataObservedAccountState,
) -> bool {
return match expected {
crate::SolanaProgramMetadataExpectedAccountState::Any => true,
crate::SolanaProgramMetadataExpectedAccountState::Vacant => observed.is_vacant(),
crate::SolanaProgramMetadataExpectedAccountState::Missing => {
matches!(observed, crate::SolanaProgramMetadataObservedAccountState::Missing)
},
crate::SolanaProgramMetadataExpectedAccountState::Buffer => {
matches!(observed, crate::SolanaProgramMetadataObservedAccountState::Buffer(_))
},
crate::SolanaProgramMetadataExpectedAccountState::Metadata => {
matches!(observed, crate::SolanaProgramMetadataObservedAccountState::Metadata(_))
},
};
}
#[cfg(test)]
mod tests {
fn request(
expected_state: crate::SolanaProgramMetadataExpectedAccountState,
) -> crate::SolanaProgramMetadataStatefulReadRequest {
return crate::SolanaProgramMetadataStatefulReadRequest {
query_role: "http_queries".to_string(),
account: kb_lib::MdPubkey(
solana_pubkey::Pubkey::new_from_array([3_u8; 32]).to_string(),
),
expected_state,
min_context_slot: std::option::Option::None,
max_data_bytes: 1024,
};
}
#[test]
fn missing_and_zeroed_accounts_are_distinguished_and_bounded() {
let missing = kb_onchain_transport::AccountInfoResult {
context: kb_onchain_transport::RpcResponseContext {
slot: 10,
api_version: std::option::Option::None,
},
account: std::option::Option::None,
};
let missing_result = crate::materialize_solana_program_metadata_account_info_result(
&request(crate::SolanaProgramMetadataExpectedAccountState::Vacant),
&missing,
);
assert!(matches!(
missing_result.as_ref().map(|value| return &value.state),
std::result::Result::Ok(crate::SolanaProgramMetadataObservedAccountState::Missing)
));
let zeroed = kb_onchain_transport::AccountInfoResult {
context: kb_onchain_transport::RpcResponseContext {
slot: 11,
api_version: std::option::Option::None,
},
account: std::option::Option::Some(kb_onchain_transport::AccountInfoValue {
lamports: 100,
owner: kb_lib::MdProgramId(kb_program_ids::SYSTEM_PROGRAM_ID.to_string()),
executable: false,
rent_epoch: 0,
space: 96,
data: vec![0_u8; 96],
}),
};
let zeroed_result = crate::materialize_solana_program_metadata_account_info_result(
&request(crate::SolanaProgramMetadataExpectedAccountState::Vacant),
&zeroed,
);
assert!(matches!(
zeroed_result.as_ref().map(|value| return &value.state),
std::result::Result::Ok(
crate::SolanaProgramMetadataObservedAccountState::Uninitialized { .. }
)
));
}
#[test]
fn buffer_state_is_decoded_and_materialized_authoritatively() {
let program = solana_pubkey::Pubkey::new_from_array([4_u8; 32]);
let authority = solana_pubkey::Pubkey::new_from_array([5_u8; 32]);
let mut data = vec![0_u8; kb_lib::DC_METADATA_SPM_BUFFER_HEADER_BYTES];
data[0] = kb_lib::DcMetadataSpmAccountDiscriminator::Buffer.wire_value();
data[1..33].copy_from_slice(&program.to_bytes());
data[33..65].copy_from_slice(&authority.to_bytes());
data[65] = 1;
data[66..82].fill(7);
data.extend_from_slice(&[8_u8, 9_u8]);
let space = match u64::try_from(data.len()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let result = kb_onchain_transport::AccountInfoResult {
context: kb_onchain_transport::RpcResponseContext {
slot: 12,
api_version: std::option::Option::None,
},
account: std::option::Option::Some(kb_onchain_transport::AccountInfoValue {
lamports: 1_000,
owner: kb_lib::MdProgramId(
kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID.to_string(),
),
executable: false,
rent_epoch: 0,
space,
data,
}),
};
let output = crate::materialize_solana_program_metadata_account_info_result(
&request(crate::SolanaProgramMetadataExpectedAccountState::Buffer),
&result,
);
assert!(output.is_ok());
if let std::result::Result::Ok(value) = output {
assert!(matches!(
value.state,
crate::SolanaProgramMetadataObservedAccountState::Buffer(_)
));
assert_eq!(
value
.materialized_output
.as_ref()
.map(|item| return item.payload_json["projectionKind"].clone()),
std::option::Option::Some(serde_json::json!("buffer_state"))
);
}
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-pipeline/src/solana_ata_stateful.rs
// version: 3
// file: kb-pipeline/src/spl_ata_stateful.rs
// version: 4
//! Stateful Localnet and Devnet readiness checks for ATA operations.

View File

@@ -1,5 +1,5 @@
// file: kb-pipeline/src/solana_elgamal_registry_stateful.rs
// version: 4
// file: kb-pipeline/src/spl_elgamal_registry_stateful.rs
// version: 5
//! Contextual SPL ElGamal registry account-state validation and materialization routing.

View File

@@ -1,5 +1,5 @@
// file: kb-pipeline/src/solana_token_2022_correlation.rs
// version: 2
// file: kb-pipeline/src/spl_token_2022_correlation.rs
// version: 3
//! Deterministic Token-2022 instruction-to-state correlation.

View File

@@ -1,5 +1,5 @@
// file: kb-pipeline/src/solana_token_2022_crypto_preflight.rs
// version: 3
// file: kb-pipeline/src/spl_token_2022_crypto_preflight.rs
// version: 4
//! Bounded cryptographic preflight for Token-2022 proof context-state accounts.

View File

@@ -1,5 +1,5 @@
// file: kb-pipeline/src/solana_token_2022_execution_orchestration.rs
// version: 3
// file: kb-pipeline/src/spl_token_2022_execution_orchestration.rs
// version: 4
//! Final Token-2022 execution-readiness and stateful postcondition contracts.

View File

@@ -1,5 +1,5 @@
// file: kb-pipeline/src/solana_token_2022_preflight.rs
// version: 3
// file: kb-pipeline/src/spl_token_2022_preflight.rs
// version: 4
//! Bounded Token-2022 stateful preflight orchestration.

View File

@@ -1,5 +1,5 @@
// file: kb-pipeline/src/solana_token_2022_proof_orchestration.rs
// version: 4
// file: kb-pipeline/src/spl_token_2022_proof_orchestration.rs
// version: 5
//! Deterministic orchestration contract for mixed Token-2022 proof locations.

View File

@@ -1,5 +1,5 @@
// file: kb-pipeline/src/solana_token_2022_stateful.rs
// version: 7
// file: kb-pipeline/src/spl_token_2022_stateful.rs
// version: 8
//! Contextual Token-2022 account-state validation and materialization routing.

View File

@@ -1,5 +1,5 @@
// file: kb-pipeline/src/solana_token_stateful.rs
// version: 1
// file: kb-pipeline/src/spl_token_stateful.rs
// version: 2
//! Stateful Localnet and Devnet readiness checks for classic SPL Token operations.

View File

@@ -0,0 +1,31 @@
// file: kb-pipeline/tests/external_metadata_solana_program_pipeline_api.rs
// version: 2
//! External-only coverage for the public Solana Program Metadata pipeline contract.
#[test]
fn external_consumers_use_only_public_program_metadata_pipeline_exports() {
let account = kb_lib::MdPubkey(solana_pubkey::Pubkey::new_from_array([7_u8; 32]).to_string());
let request = kb_pipeline::SolanaProgramMetadataStatefulReadRequest {
query_role: "rpc".to_string(),
account: account.clone(),
expected_state: kb_pipeline::SolanaProgramMetadataExpectedAccountState::Any,
min_context_slot: std::option::Option::None,
max_data_bytes: kb_pipeline::MAX_SOLANA_PROGRAM_METADATA_STATEFUL_ACCOUNT_BYTES,
};
assert!(request.validate().is_ok());
let postcondition = kb_pipeline::SolanaProgramMetadataPostcondition {
code: "metadata.solana_program_metadata.external_api".to_string(),
account,
status: kb_pipeline::SolanaProgramMetadataPostconditionStatus::NotApplicable,
diagnostic: "external API coverage".to_string(),
};
assert_eq!(
kb_pipeline::summarize_solana_program_metadata_postconditions(&[postcondition]),
kb_pipeline::SolanaProgramMetadataPostconditionStatus::NotApplicable
);
const {
assert!(kb_pipeline::MAX_SOLANA_PROGRAM_METADATA_EXECUTION_SIGNERS > 0);
assert!(kb_pipeline::MAX_SOLANA_PROGRAM_METADATA_PREFLIGHT_ACCOUNTS > 0);
}
}