This commit is contained in:
2026-07-23 16:37:12 +02:00
parent 99c345f2f2
commit 0da75c1311
2159 changed files with 230833 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
// file: kb_pipeline/src/constants.rs
// version: 1
//! Local constants for the `kb_pipeline` crate.
/// Canonical tracing target for this crate.
pub(crate) const TRACING_TARGET: &str = "kb_pipeline";

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,323 @@
// file: kb_pipeline/src/lib.rs
// version: 29
//! Pipeline orchestration primitives.
#![warn(missing_docs)]
#![deny(unreachable_pub)]
#![forbid(unsafe_code)]
mod backfill;
mod constants;
mod core_extraction;
mod decode_replay;
mod plan;
mod solana_ata_execution;
mod solana_ata_stateful;
mod solana_elgamal_registry_stateful;
mod solana_execution;
mod solana_memo_execution;
mod solana_stateful;
mod solana_token_2022_correlation;
mod solana_token_2022_crypto_preflight;
mod solana_token_2022_devnet_execution;
mod solana_token_2022_devnet_scenarios;
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_2022_validation;
mod solana_token_execution;
mod solana_token_lifecycle;
mod solana_token_stateful;
/// Canonical tracing target for this crate.
pub(crate) use crate::constants::TRACING_TARGET;
/// Address category used by targeted history backfills.
pub use crate::backfill::BackfillAddressKind;
/// Chronological direction relative to an anchor signature.
pub use crate::backfill::BackfillDirection;
/// Application progress and cancellation observer.
pub use crate::backfill::BackfillObserver;
/// Operator-visible backfill progress event.
pub use crate::backfill::BackfillProgressEvent;
/// Backfill progress severity.
pub use crate::backfill::BackfillProgressLevel;
/// Complete bounded backfill request.
pub use crate::backfill::BackfillRequest;
/// Candidate source used by one campaign.
pub use crate::backfill::BackfillSource;
/// Final backfill counters and pagination information.
pub use crate::backfill::BackfillSummary;
/// Executes one bounded HTTP backfill.
pub use crate::backfill::execute_http_backfill;
/// Stable canonical to core processor name.
pub use crate::core_extraction::CORE_EXTRACTION_PROCESSOR_NAME;
/// Current canonical to core extractor version.
pub use crate::core_extraction::CORE_EXTRACTION_PROCESSOR_VERSION;
/// Stable canonical to core processing stage.
pub use crate::core_extraction::CORE_EXTRACTION_STAGE;
/// Core extraction progress and cancellation observer.
pub use crate::core_extraction::CoreExtractionObserver;
/// Operator-visible core extraction progress event.
pub use crate::core_extraction::CoreExtractionProgressEvent;
/// Core extraction progress severity.
pub use crate::core_extraction::CoreExtractionProgressLevel;
/// Complete bounded core extraction request.
pub use crate::core_extraction::CoreExtractionRequest;
/// Core extraction source selection.
pub use crate::core_extraction::CoreExtractionSource;
/// Final core extraction counters.
pub use crate::core_extraction::CoreExtractionSummary;
/// Executes one bounded canonical transaction to core extraction campaign.
pub use crate::core_extraction::execute_core_extraction;
/// Extracts one canonical raw transaction into a complete core write bundle.
pub use crate::core_extraction::extract_raw_transaction_to_core;
/// Current common decode pipeline orchestration version.
pub use crate::decode_replay::DECODE_PIPELINE_VERSION;
/// Deterministic policy used when several decoders recognize one input.
pub use crate::decode_replay::DecodeDispatchPolicy;
/// Aggregated terminal counters for one decoder version.
pub use crate::decode_replay::DecodeProcessorSummary;
/// Progress and cooperative cancellation contract implemented by applications.
pub use crate::decode_replay::DecodeReplayObserver;
/// One operator-visible decode replay progress event.
pub use crate::decode_replay::DecodeReplayProgressEvent;
/// Decode replay progress severity.
pub use crate::decode_replay::DecodeReplayProgressLevel;
/// Complete bounded contextual decode replay request.
pub use crate::decode_replay::DecodeReplayRequest;
/// Final counters for one bounded contextual decode replay campaign.
pub use crate::decode_replay::DecodeReplaySummary;
/// Stable processing ledger stage used by decoded event materializers.
pub use crate::decode_replay::EVENT_MATERIALIZATION_STAGE;
/// Stable processing ledger stage used by contextual instruction decoders.
pub use crate::decode_replay::INSTRUCTION_DECODE_STAGE;
/// Executes one bounded contextual instruction decode and optional materialization campaign.
pub use crate::decode_replay::execute_decode_replay;
/// Creates one stable process-local contextual decode campaign identifier.
pub use crate::decode_replay::new_decode_campaign_id;
/// Pipeline stage identifier.
pub use crate::plan::PipelineStage;
/// Replay selection scope.
pub use crate::plan::ReplayScope;
/// Complete request for one Devnet ATA execution.
pub use crate::solana_ata_execution::DevnetSplAssociatedTokenAccountExecutionRequest;
/// Complete result of one Devnet ATA execution.
pub use crate::solana_ata_execution::DevnetSplAssociatedTokenAccountExecutionSummary;
/// Executes one Devnet ATA simulation or explicitly authorized submission.
pub use crate::solana_ata_execution::execute_devnet_spl_associated_token_account;
/// Simulates one Devnet ATA operation after stateful preflight.
pub use crate::solana_ata_execution::simulate_devnet_spl_associated_token_account;
/// Stateful ATA invariants observed after a confirmed execution.
pub use crate::solana_ata_stateful::SplAssociatedTokenAccountPostExecutionReport;
/// One ATA stateful readiness check.
pub use crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulCheck;
/// One ATA stateful readiness fact.
pub use crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulFact;
/// Complete ATA stateful readiness report.
pub use crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessReport;
/// Complete ATA stateful readiness request.
pub use crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessRequest;
/// ATA stateful readiness outcome.
pub use crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessStatus;
/// Verifies final ATA account relationships after a confirmed execution.
pub use crate::solana_ata_stateful::inspect_spl_associated_token_account_post_execution;
/// Inspects Localnet or Devnet ATA state required before simulation.
pub use crate::solana_ata_stateful::inspect_spl_associated_token_account_stateful_readiness;
/// Exact byte length of one SPL ElGamal registry account.
pub use crate::solana_elgamal_registry_stateful::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES;
/// One bounded SPL ElGamal registry RPC read request.
pub use crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadRequest;
/// One bounded registry RPC read and validated administrative projection.
pub use crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadResult;
/// One contextually validated SPL ElGamal registry state snapshot.
pub use crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulSnapshot;
/// Validates one complete RPC registry response before parsing and routing.
pub use crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result;
/// Parses, validates the official owner-derived PDA, and materializes one registry snapshot.
pub use crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot;
/// Reads, validates, and routes one exact SPL ElGamal registry account.
pub use crate::solana_elgamal_registry_stateful::read_elgamal_registry_stateful_snapshot;
/// Complete request for one bounded Devnet System transfer.
pub use crate::solana_execution::DevnetSystemTransferRequest;
/// Complete result of one Devnet System transfer and post-validation.
pub use crate::solana_execution::DevnetSystemTransferSummary;
/// No-op observer for execution orchestration.
pub use crate::solana_execution::NoopSolanaExecutionObserver;
/// Composite execution, backfill, extraction and decode observer.
pub use crate::solana_execution::SolanaExecutionObserver;
/// One operator-visible execution progress event.
pub use crate::solana_execution::SolanaExecutionProgressEvent;
/// Execution progress severity.
pub use crate::solana_execution::SolanaExecutionProgressLevel;
/// Executes one bounded Devnet System transfer and canonical replay validation.
pub use crate::solana_execution::execute_devnet_system_transfer;
/// Complete request for one SPL Memo v4 Devnet execution.
pub use crate::solana_memo_execution::DevnetMemoExecutionRequest;
/// Complete result of one SPL Memo v4 Devnet execution and post-validation.
pub use crate::solana_memo_execution::DevnetMemoExecutionSummary;
/// Executes one SPL Memo v4 Devnet simulation or explicitly authorized submission.
pub use crate::solana_memo_execution::execute_devnet_memo;
/// One machine-readable stateful readiness check.
pub use crate::solana_stateful::SolanaCoreStatefulCheck;
/// One contextual fact measured during stateful readiness inspection.
pub use crate::solana_stateful::SolanaCoreStatefulFact;
/// Complete stateful readiness report produced before simulation.
pub use crate::solana_stateful::SolanaCoreStatefulReadinessReport;
/// Complete request for one native Solana stateful readiness inspection.
pub use crate::solana_stateful::SolanaCoreStatefulReadinessRequest;
/// Stateful readiness outcome for one native Solana operation.
pub use crate::solana_stateful::SolanaCoreStatefulReadinessStatus;
/// Inspects Localnet or Devnet state required before simulation.
pub use crate::solana_stateful::inspect_solana_core_stateful_readiness;
/// Correlation outcome between one committed instruction fact and one final state snapshot.
pub use crate::solana_token_2022_correlation::Token2022CorrelationStatus;
/// Deterministic correlation report for one instruction output and one final snapshot.
pub use crate::solana_token_2022_correlation::Token2022StateCorrelationReport;
/// Correlates one materialized instruction fact with one authoritative Token-2022 snapshot.
pub use crate::solana_token_2022_correlation::correlate_token_2022_instruction_with_snapshot;
/// Maximum proof context-state accounts accepted by one Token-2022 cryptographic preflight.
pub use crate::solana_token_2022_crypto_preflight::MAX_TOKEN_2022_PROOF_CONTEXTS;
/// Complete Token-2022 cryptographic preflight report.
pub use crate::solana_token_2022_crypto_preflight::Token2022CryptographicPreflightReport;
/// One bounded Token-2022 cryptographic preflight request.
pub use crate::solana_token_2022_crypto_preflight::Token2022CryptographicPreflightRequest;
/// One validated proof context-state result.
pub use crate::solana_token_2022_crypto_preflight::Token2022ProofContextReport;
/// One exact pre-verified proof context-state requirement.
pub use crate::solana_token_2022_crypto_preflight::Token2022ProofContextRequirement;
/// Generic metadata bytes retained by every ZK proof context-state account.
pub use crate::solana_token_2022_crypto_preflight::ZK_PROOF_CONTEXT_META_BYTES;
/// Reads and validates pre-verified proof context-state accounts before simulation.
pub use crate::solana_token_2022_crypto_preflight::inspect_token_2022_cryptographic_preflight;
/// Complete request for one public Devnet Token-2022 execution.
pub use crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest;
/// Complete result of one public Devnet Token-2022 execution.
pub use crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionSummary;
/// Executes one public Devnet Token-2022 simulation or authorized submission.
pub use crate::solana_token_2022_devnet_execution::execute_devnet_spl_token_2022;
/// Simulates one public Devnet Token-2022 operation.
pub use crate::solana_token_2022_devnet_execution::simulate_devnet_spl_token_2022;
/// Stable family of one Devnet SPL validation scenario.
pub use crate::solana_token_2022_devnet_scenarios::DevnetSplValidationFamily;
/// Current implementation status of one Devnet SPL validation scenario.
pub use crate::solana_token_2022_devnet_scenarios::DevnetSplValidationImplementationStatus;
/// One independent Devnet validation scenario required by milestone 0.4.6.
pub use crate::solana_token_2022_devnet_scenarios::DevnetSplValidationScenario;
/// Returns the ordered Devnet SPL scenario inventory required by milestone 0.4.6.
pub use crate::solana_token_2022_devnet_scenarios::devnet_spl_validation_scenarios;
/// Maximum distinct signers accepted by one Token-2022 execution envelope.
pub use crate::solana_token_2022_execution_orchestration::MAX_TOKEN_2022_EXECUTION_SIGNERS;
/// One explicit stateful postcondition retained after confirmation.
pub use crate::solana_token_2022_execution_orchestration::Token2022ExecutionPostcondition;
/// Explicit result of one Token-2022 stateful postcondition.
pub use crate::solana_token_2022_execution_orchestration::Token2022ExecutionPostconditionStatus;
/// Deterministic report authorizing or refusing Token-2022 signing and submission.
pub use crate::solana_token_2022_execution_orchestration::Token2022ExecutionReadinessReport;
/// Complete request checked before Token-2022 transaction signing.
pub use crate::solana_token_2022_execution_orchestration::Token2022ExecutionReadinessRequest;
/// Aggregates explicit postconditions without inventing success.
pub use crate::solana_token_2022_execution_orchestration::summarize_token_2022_postconditions;
/// Validates exact-message simulation, preflights, signers, and submission policy.
pub use crate::solana_token_2022_execution_orchestration::validate_token_2022_execution_readiness;
/// Maximum distinct Token-2022 accounts accepted by one preflight inspection.
pub use crate::solana_token_2022_preflight::MAX_TOKEN_2022_PREFLIGHT_ACCOUNTS;
/// Maximum aggregate account-data budget accepted by one preflight inspection.
pub use crate::solana_token_2022_preflight::MAX_TOKEN_2022_PREFLIGHT_TOTAL_BYTES;
/// One validated account result in a Token-2022 preflight report.
pub use crate::solana_token_2022_preflight::Token2022PreflightAccountReport;
/// Complete bounded Token-2022 stateful preflight report.
pub use crate::solana_token_2022_preflight::Token2022PreflightReport;
/// One bounded Token-2022 preflight request.
pub use crate::solana_token_2022_preflight::Token2022PreflightRequest;
/// One exact Token-2022 account requirement for a stateful preflight.
pub use crate::solana_token_2022_preflight::Token2022PreflightRequirement;
/// Inspects all bounded Token-2022 state required before simulation.
pub use crate::solana_token_2022_preflight::inspect_token_2022_preflight;
/// Maximum proof references accepted by one confidential Token-2022 operation.
pub use crate::solana_token_2022_proof_orchestration::MAX_TOKEN_2022_OPERATION_PROOFS;
/// One deterministic mixed-proof orchestration report.
pub use crate::solana_token_2022_proof_orchestration::Token2022ProofOrchestrationReport;
/// One bounded mixed-proof orchestration request.
pub use crate::solana_token_2022_proof_orchestration::Token2022ProofOrchestrationRequest;
/// Validates mixed inline/context-state proofs before transaction assembly.
pub use crate::solana_token_2022_proof_orchestration::orchestrate_token_2022_proofs;
/// Maximum complete Token-2022 account data accepted by one bounded RPC read.
pub use crate::solana_token_2022_stateful::MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES;
/// Optional external identities required to validate cross-account Token-2022 state.
pub use crate::solana_token_2022_stateful::Token2022StatefulContext;
/// One bounded Token-2022 account read request.
pub use crate::solana_token_2022_stateful::Token2022StatefulReadRequest;
/// One bounded RPC read and its contextually validated projections.
pub use crate::solana_token_2022_stateful::Token2022StatefulReadResult;
/// One contextually validated Token-2022 state snapshot and its owned projections.
pub use crate::solana_token_2022_stateful::Token2022StatefulSnapshotBundle;
/// Contextually validates and materializes one already parsed Token-2022 account snapshot.
pub use crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot;
/// Contextually validates one parsed Token-2022 snapshot with external cross-account identities.
pub use crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot_with_context;
/// Validates one complete RPC account response before Token-2022 parsing and routing.
pub use crate::solana_token_2022_stateful::materialize_token_2022_account_info_result;
/// Parses, contextually validates, and materializes one bounded Token-2022 account snapshot.
pub use crate::solana_token_2022_stateful::materialize_token_2022_stateful_snapshot;
/// Reads, validates, parses, and routes one bounded Token-2022 account snapshot.
pub use crate::solana_token_2022_stateful::read_token_2022_stateful_snapshot;
/// Maximum evidence records retained per Token-2022 validation scenario.
pub use crate::solana_token_2022_validation::MAX_TOKEN_2022_VALIDATION_EVIDENCE;
/// Required environment for one Token-2022 validation scenario.
pub use crate::solana_token_2022_validation::Token2022ValidationEnvironment;
/// One bounded validation evidence record.
pub use crate::solana_token_2022_validation::Token2022ValidationEvidence;
/// Canonical machine-readable Token-2022 validation matrix.
pub use crate::solana_token_2022_validation::Token2022ValidationMatrix;
/// One scenario declared in the canonical Token-2022 validation matrix.
pub use crate::solana_token_2022_validation::Token2022ValidationMatrixScenario;
/// Complete Token-2022 milestone validation report.
pub use crate::solana_token_2022_validation::Token2022ValidationReport;
/// One declared Token-2022 validation scenario.
pub use crate::solana_token_2022_validation::Token2022ValidationScenario;
/// Exact observed status of one Token-2022 validation scenario.
pub use crate::solana_token_2022_validation::Token2022ValidationStatus;
/// Loads and validates the canonical Token-2022 validation matrix.
pub use crate::solana_token_2022_validation::load_token_2022_validation_matrix;
/// Validates the canonical Token-2022 validation matrix contract and evidence.
pub use crate::solana_token_2022_validation::validate_token_2022_validation_matrix;
/// Validates a bounded Token-2022 milestone evidence report.
pub use crate::solana_token_2022_validation::validate_token_2022_validation_report;
/// Complete request for one Devnet classic SPL Token execution.
pub use crate::solana_token_execution::DevnetSplTokenExecutionRequest;
/// Complete result of one Devnet classic SPL Token execution.
pub use crate::solana_token_execution::DevnetSplTokenExecutionSummary;
/// Executes one Devnet classic SPL Token simulation or authorized submission.
pub use crate::solana_token_execution::execute_devnet_spl_token;
/// Simulates one Devnet classic SPL Token operation after stateful preflight.
pub use crate::solana_token_execution::simulate_devnet_spl_token;
/// Explicit request to prepare fresh raw accounts for one SPL Token Devnet lifecycle.
pub use crate::solana_token_lifecycle::DevnetSplTokenLifecyclePreparationRequest;
/// One confirmed raw account creation retained by lifecycle preparation.
pub use crate::solana_token_lifecycle::DevnetSplTokenLifecyclePreparationStep;
/// Complete fresh-account preparation result for one SPL Token Devnet lifecycle.
pub use crate::solana_token_lifecycle::DevnetSplTokenLifecyclePreparationSummary;
/// Request for one destructive, explicitly authorized SPL Token Devnet lifecycle.
pub use crate::solana_token_lifecycle::DevnetSplTokenLifecycleRequest;
/// Result retained for one fully post-validated lifecycle transaction.
pub use crate::solana_token_lifecycle::DevnetSplTokenLifecycleStepSummary;
/// Complete result of the controlled SPL Token Devnet lifecycle.
pub use crate::solana_token_lifecycle::DevnetSplTokenLifecycleSummary;
/// Executes initialize, mint, transfer, approve/revoke, burn and close on Devnet.
pub use crate::solana_token_lifecycle::execute_devnet_spl_token_lifecycle;
/// Creates and verifies fresh raw Token-owned accounts for one Devnet lifecycle.
pub use crate::solana_token_lifecycle::prepare_devnet_spl_token_lifecycle_accounts;
/// One machine-readable classic SPL Token stateful check.
pub use crate::solana_token_stateful::SplTokenStatefulCheck;
/// One contextual fact measured during classic SPL Token state inspection.
pub use crate::solana_token_stateful::SplTokenStatefulFact;
/// Complete classic SPL Token stateful readiness report.
pub use crate::solana_token_stateful::SplTokenStatefulReadinessReport;
/// Complete request for one classic SPL Token stateful readiness inspection.
pub use crate::solana_token_stateful::SplTokenStatefulReadinessRequest;
/// Stateful readiness outcome for one classic SPL Token operation.
pub use crate::solana_token_stateful::SplTokenStatefulReadinessStatus;
/// Inspects Localnet or Devnet Token state before simulation.
pub use crate::solana_token_stateful::inspect_spl_token_stateful_readiness;

View File

@@ -0,0 +1,44 @@
// file: kb_pipeline/src/plan.rs
// version: 1
//! Pipeline planning primitives.
/// Pipeline stage identifier.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PipelineStage {
/// Ingest raw transactions.
Ingest,
/// Extract generic Solana structures.
Extract,
/// Build program observations.
Observe,
/// Decode protocol events.
Decode,
/// Materialize business events.
Materialize,
/// Aggregate materialized events.
Aggregate,
/// Validate outputs.
Validate,
}
/// Replay selection scope.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ReplayScope {
/// Optional module name.
pub module_name: std::option::Option<std::string::String>,
/// Optional module version.
pub module_version: std::option::Option<std::string::String>,
/// Optional program id.
pub program_id: std::option::Option<std::string::String>,
/// Optional surface code.
pub surface_code: std::option::Option<std::string::String>,
/// Optional 8-byte discriminator in hexadecimal.
pub discriminator_8: std::option::Option<std::string::String>,
/// Optional inclusive start slot.
pub slot_start: std::option::Option<u64>,
/// Optional inclusive end slot.
pub slot_end: std::option::Option<u64>,
/// Whether to ignore existing ledger rows.
pub force: bool,
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,406 @@
// file: kb_pipeline/src/solana_elgamal_registry_stateful.rs
// version: 3
//! Contextual SPL ElGamal registry account-state validation and materialization routing.
use std::str::FromStr; // rust-rules: trait-import
/// Exact byte length of one SPL ElGamal registry account.
pub const ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES: usize = 64;
/// One bounded SPL ElGamal registry RPC read request.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ElGamalRegistryStatefulReadRequest {
/// Endpoint role used for the HTTP RPC request.
pub query_role: std::string::String,
/// Canonical registry PDA.
pub registry_account: kb_model::Pubkey,
/// Optional minimum RPC context slot.
pub min_context_slot: std::option::Option<u64>,
}
/// One bounded registry RPC read and validated administrative projection.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct ElGamalRegistryStatefulReadResult {
/// Commitment used for the RPC read.
pub commitment: std::string::String,
/// Context slot returned by the endpoint.
pub context_slot: u64,
/// Complete validated registry snapshot.
pub snapshot: crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulSnapshot,
}
/// Reads, validates, and routes one exact SPL ElGamal registry account.
pub async fn read_elgamal_registry_stateful_snapshot(
pool: &kb_rpc::HttpEndpointPool,
request: &crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadRequest,
) -> kb_core::Result<crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadResult> {
if request.query_role.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"SPL ElGamal registry stateful read query_role must not be empty",
));
}
let config = match kb_rpc::GetAccountInfoConfig::new_with_data(
kb_rpc::RpcCommitmentLevel::Confirmed,
request.min_context_slot,
crate::solana_elgamal_registry_stateful::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_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.registry_account, &config)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(
request,
&result,
);
}
/// Validates one complete RPC registry response before parsing and routing.
pub fn materialize_elgamal_registry_account_info_result(
request: &crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadRequest,
result: &kb_rpc::AccountInfoResult,
) -> kb_core::Result<crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadResult> {
if let std::option::Option::Some(min_context_slot) = request.min_context_slot {
if result.context.slot < min_context_slot {
return std::result::Result::Err(kb_core::Error::new(
"elgamal_registry_stateful_context_slot_too_old",
format!(
"SPL ElGamal registry context slot {} is below requested minimum {min_context_slot}",
result.context.slot
),
));
}
}
let account = match result.account.as_ref() {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"elgamal_registry_stateful_account_missing",
format!(
"SPL ElGamal registry account {} does not exist",
request.registry_account.0
),
));
},
};
if account.executable {
return std::result::Result::Err(kb_core::Error::new(
"elgamal_registry_stateful_account_executable",
format!(
"SPL ElGamal registry state account {} must not be executable",
request.registry_account.0
),
));
}
if account.owner.0 != kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID {
return std::result::Result::Err(kb_core::Error::new(
"elgamal_registry_stateful_owner_mismatch",
format!(
"SPL ElGamal registry owner must be {}, got {}",
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID,
account.owner.0
),
));
}
if account.space
!= crate::solana_elgamal_registry_stateful::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES as u64
|| account.data.len()
!= crate::solana_elgamal_registry_stateful::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES
{
return std::result::Result::Err(kb_core::Error::new(
"elgamal_registry_stateful_account_length_invalid",
format!(
"SPL ElGamal registry account must report and return exactly {} bytes, got space {} and data {}",
crate::solana_elgamal_registry_stateful::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES,
account.space,
account.data.len()
),
));
}
let snapshot = match crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot(
request.registry_account.0.as_str(),
account.owner.0.as_str(),
result.context.slot,
account.data.as_slice(),
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"elgamal_registry_stateful_projection_failed",
error,
));
},
};
return std::result::Result::Ok(
crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadResult {
commitment: "confirmed".to_string(),
context_slot: result.context.slot,
snapshot,
},
);
}
/// One contextually validated SPL ElGamal registry state snapshot.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct ElGamalRegistryStatefulSnapshot {
/// Canonical registry PDA identity.
pub registry_account_key: std::string::String,
/// Wallet address stored as the registry owner.
pub owner: std::string::String,
/// Context slot associated with the account read.
pub slot: u64,
/// Processor-owned administrative projection.
pub output: kb_materializer_api::MaterializedOutput,
}
/// Parse, validate the official owner-derived PDA, and materialize one registry snapshot.
pub fn materialize_elgamal_registry_stateful_snapshot(
registry_account_key: &str,
owner_program_id: &str,
slot: u64,
data: &[u8],
) -> std::result::Result<
crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulSnapshot,
String,
> {
if owner_program_id != kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID {
return std::result::Result::Err(format!(
"SPL ElGamal registry snapshot owner must be {}, got {owner_program_id}",
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID
));
}
let state = match kb_decoder_spl_elgamal_registry::state::parse_elgamal_registry_state(data) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let owner = match solana_pubkey::Pubkey::from_str(state.owner.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!(
"SPL ElGamal registry owner is not a valid address: {error}"
));
},
};
let program_id = match solana_pubkey::Pubkey::from_str(
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!(
"Configured SPL ElGamal registry program ID is invalid: {error}"
));
},
};
let expected =
spl_elgamal_registry_interface::get_elgamal_registry_address(&owner, &program_id);
if expected.to_string() != registry_account_key {
return std::result::Result::Err(format!(
"SPL ElGamal registry account {registry_account_key} does not match owner-derived PDA {expected}"
));
}
let output = match kb_materializer_admin::materialize_elgamal_registry_state_snapshot(
registry_account_key,
slot,
&state,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(
crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulSnapshot {
registry_account_key: registry_account_key.to_string(),
owner: state.owner,
slot,
output,
},
);
}
#[cfg(test)]
mod tests {
use std::str::FromStr; // rust-rules: trait-import
fn registry_fixture(owner: &solana_pubkey::Pubkey) -> (std::string::String, [u8; 64]) {
let program_id = match solana_pubkey::Pubkey::from_str(
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => solana_pubkey::Pubkey::default(),
};
let registry =
spl_elgamal_registry_interface::get_elgamal_registry_address(owner, &program_id);
let mut data = [0u8; 64];
data[..32].copy_from_slice(owner.as_ref());
data[32..].copy_from_slice(&[9u8; 32]);
return (registry.to_string(), data);
}
#[test]
fn exact_owner_derived_registry_pda_routes_one_admin_snapshot() {
let owner = solana_pubkey::Pubkey::new_from_array([7u8; 32]);
let (registry, data) =
crate::solana_elgamal_registry_stateful::tests::registry_fixture(&owner);
let snapshot =
crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot(
registry.as_str(),
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID,
55,
&data,
);
assert_eq!(
snapshot.as_ref().map(|value| return value.owner.clone()),
std::result::Result::Ok(owner.to_string())
);
assert_eq!(
snapshot
.as_ref()
.map(|value| return value.output.payload_json["finalAccountStateCaptured"].clone()),
std::result::Result::Ok(serde_json::json!(true))
);
}
#[test]
fn foreign_owner_program_wrong_pda_and_invalid_length_fail_closed() {
let owner = solana_pubkey::Pubkey::new_from_array([8u8; 32]);
let (registry, data) =
crate::solana_elgamal_registry_stateful::tests::registry_fixture(&owner);
let foreign =
crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot(
registry.as_str(),
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
55,
&data,
);
assert!(foreign.is_err());
let wrong =
crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot(
solana_pubkey::Pubkey::new_from_array([10u8; 32]).to_string().as_str(),
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID,
55,
&data,
);
assert!(wrong.is_err());
let invalid =
crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot(
registry.as_str(),
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID,
55,
&data[..63],
);
assert!(invalid.is_err());
}
#[test]
fn exact_rpc_registry_routes_after_context_owner_and_length_validation() {
let owner = solana_pubkey::Pubkey::new_from_array([13u8; 32]);
let (registry, data) =
crate::solana_elgamal_registry_stateful::tests::registry_fixture(&owner);
let request = crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadRequest {
query_role: "execution".to_string(),
registry_account: kb_model::Pubkey(registry),
min_context_slot: std::option::Option::Some(70),
};
let result = kb_rpc::AccountInfoResult {
context: kb_rpc::RpcResponseContext {
slot: 71,
api_version: std::option::Option::None,
},
account: std::option::Option::Some(kb_rpc::AccountInfoValue {
lamports: 1,
owner: kb_model::ProgramId(
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID.to_string(),
),
executable: false,
rent_epoch: 0,
space: 64,
data: data.to_vec(),
}),
};
let materialized = crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(
&request,
&result,
);
assert_eq!(
materialized.as_ref().map(|value| return value.context_slot),
std::result::Result::Ok(71)
);
assert_eq!(
materialized.as_ref().map(|value| return value.snapshot.owner.clone()),
std::result::Result::Ok(owner.to_string())
);
}
#[test]
fn stale_missing_foreign_executable_and_wrong_length_registry_reads_fail_closed() {
let owner = solana_pubkey::Pubkey::new_from_array([14u8; 32]);
let (registry, data) =
crate::solana_elgamal_registry_stateful::tests::registry_fixture(&owner);
let request = crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadRequest {
query_role: "execution".to_string(),
registry_account: kb_model::Pubkey(registry),
min_context_slot: std::option::Option::Some(80),
};
let base = kb_rpc::AccountInfoValue {
lamports: 1,
owner: kb_model::ProgramId(
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID.to_string(),
),
executable: false,
rent_epoch: 0,
space: 64,
data: data.to_vec(),
};
let stale = kb_rpc::AccountInfoResult {
context: kb_rpc::RpcResponseContext {
slot: 79,
api_version: std::option::Option::None,
},
account: std::option::Option::Some(base.clone()),
};
assert!(crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(&request, &stale).is_err());
let missing = kb_rpc::AccountInfoResult {
context: kb_rpc::RpcResponseContext {
slot: 80,
api_version: std::option::Option::None,
},
account: std::option::Option::None,
};
assert!(crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(&request, &missing).is_err());
let mut foreign = base.clone();
foreign.owner = kb_model::ProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string());
let foreign = kb_rpc::AccountInfoResult {
context: kb_rpc::RpcResponseContext {
slot: 80,
api_version: std::option::Option::None,
},
account: std::option::Option::Some(foreign),
};
assert!(crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(&request, &foreign).is_err());
let mut executable = base.clone();
executable.executable = true;
let executable = kb_rpc::AccountInfoResult {
context: kb_rpc::RpcResponseContext {
slot: 80,
api_version: std::option::Option::None,
},
account: std::option::Option::Some(executable),
};
assert!(crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(&request, &executable).is_err());
let mut wrong_length = base;
wrong_length.data.pop();
let wrong_length = kb_rpc::AccountInfoResult {
context: kb_rpc::RpcResponseContext {
slot: 80,
api_version: std::option::Option::None,
},
account: std::option::Option::Some(wrong_length),
};
assert!(crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(&request, &wrong_length).is_err());
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,918 @@
// file: kb_pipeline/src/solana_memo_execution.rs
// version: 2
//! Devnet SPL Memo v4 execution with canonical post-validation.
/// Complete request for one SPL Memo v4 Devnet execution.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DevnetMemoExecutionRequest {
/// Stable caller-provided execution identifier.
pub intent_id: std::string::String,
/// Endpoint role used for cluster, balance, blockhash, fee and hydration calls.
pub query_role: std::string::String,
/// Endpoint role used for simulation, submission and confirmation polling.
pub transaction_role: std::string::String,
/// Exact UTF-8 Memo payload.
pub message: std::string::String,
/// Supplies the persistent Devnet fee payer as a Memo signer account.
pub include_wallet_as_memo_signer: bool,
/// Explicitly authorizes signing and submission after successful simulation.
pub submit: bool,
/// Explicit operator confirmation required by the active profile.
pub operator_confirmed: bool,
/// Number of `getTransaction` retries after the first hydration attempt.
pub post_validation_max_retries: u32,
/// Replaces existing core and decode outputs for the submitted signature.
pub force_post_validation_replay: bool,
}
impl DevnetMemoExecutionRequest {
/// Creates a conservative simulation-only Memo v4 request.
pub fn new(
intent_id: impl std::convert::Into<std::string::String>,
message: impl std::convert::Into<std::string::String>,
) -> Self {
return Self {
intent_id: intent_id.into(),
query_role: "http_queries".to_string(),
transaction_role: "http_transactions".to_string(),
message: message.into(),
include_wallet_as_memo_signer: true,
submit: false,
operator_confirmed: false,
post_validation_max_retries: 10,
force_post_validation_replay: false,
};
}
/// Validates request-local bounds independently from one profile.
pub fn validate(&self) -> kb_core::Result<()> {
if self.intent_id.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"Devnet Memo execution intent id must not be empty",
));
}
if self.query_role.trim().is_empty() || self.transaction_role.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"Devnet Memo execution endpoint roles must not be empty",
));
}
if self.message.len() > kb_executor_spl_memo::MAX_MEMO_MESSAGE_BYTES {
return std::result::Result::Err(kb_core::Error::config(format!(
"Devnet Memo payload length {} exceeds the executor limit {}",
self.message.len(),
kb_executor_spl_memo::MAX_MEMO_MESSAGE_BYTES
)));
}
if self.post_validation_max_retries > 20 {
return std::result::Result::Err(kb_core::Error::config(
"post-execution getTransaction retries must not exceed 20",
));
}
return std::result::Result::Ok(());
}
}
/// Complete result of one SPL Memo v4 Devnet execution.
#[derive(Clone, Debug, PartialEq)]
pub struct DevnetMemoExecutionSummary {
/// Profile used by the orchestration.
pub profile_name: std::string::String,
/// Exact classified cluster.
pub cluster: kb_execution_api::ExecutionCluster,
/// Genesis hash returned by the selected endpoint.
pub genesis_hash: std::string::String,
/// Non-secret persistent wallet description.
pub wallet: kb_wallet::WalletSummary,
/// Wallet balance observed before planning.
pub balance_lamports: u64,
/// Exact prepared Memo plan.
pub plan: kb_execution_api::PreparedExecutionPlan,
/// Recent blockhash used by the exact transaction.
pub latest_blockhash: kb_rpc::LatestBlockhashResult,
/// Fee estimate for the exact compiled message.
pub fee: kb_rpc::FeeForMessageResult,
/// Exact simulation result bound to the compiled message.
pub simulation: kb_execution_api::ExecutionSimulationResult,
/// Submission result when explicitly authorized.
pub send_result: std::option::Option<kb_execution_api::ExecutionSendResult>,
/// Confirmation result when submitted.
pub confirmation: std::option::Option<kb_execution_api::ExecutionConfirmationResult>,
/// Canonical hydration result for the exact signature.
pub backfill: std::option::Option<crate::BackfillSummary>,
/// Core extraction result for the exact signature.
pub core_extraction: std::option::Option<crate::CoreExtractionSummary>,
/// First Memo decode and materialization replay.
pub decode_replay: std::option::Option<crate::DecodeReplaySummary>,
/// Second replay proving that the same decoder version and input are idempotent.
pub idempotence_replay: std::option::Option<crate::DecodeReplaySummary>,
/// Exact persisted transaction annotation rows for the submitted signature.
pub annotations: std::vec::Vec<kb_store_core::MaterializedEventQueryRow>,
/// Aggregated post-execution validation diagnostic.
pub post_execution: std::option::Option<kb_execution_api::PostExecutionDiagnostic>,
}
/// Executes one Memo v4 Devnet simulation or explicitly authorized submission.
#[allow(clippy::too_many_arguments)]
pub async fn execute_devnet_memo<S, O>(
http_pool: &kb_rpc::HttpEndpointPool,
store: &S,
profile: &kb_config::ProfileConfig,
workspace_root: &std::path::Path,
request: &crate::DevnetMemoExecutionRequest,
decoders: &[std::sync::Arc<dyn kb_decoder_api::InstructionDecoder>],
materializers: &[std::sync::Arc<dyn kb_materializer_api::EventMaterializer>],
observer: &O,
) -> kb_core::Result<crate::DevnetMemoExecutionSummary>
where
S: kb_store_core::RawTransactionStore
+ kb_store_core::CoreExtractionStore
+ kb_store_core::DecodePipelineStore
+ Sync,
O: crate::SolanaExecutionObserver,
{
if let std::result::Result::Err(error) = request.validate() {
return std::result::Result::Err(error);
}
if let std::result::Result::Err(error) =
crate::solana_memo_execution::validate_profile(profile, request)
{
return std::result::Result::Err(error);
}
if let std::result::Result::Err(error) =
crate::solana_execution::ensure_not_cancelled(observer, "validate")
{
return std::result::Result::Err(error);
}
let genesis = match http_pool.get_genesis_hash_for_role(request.query_role.as_str()).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if genesis.classified_cluster
!= std::option::Option::Some(kb_execution_api::ExecutionCluster::Devnet)
{
return std::result::Result::Err(kb_core::Error::new(
"execution_cluster_mismatch",
format!(
"expected Devnet genesis hash but endpoint returned {} classified as {:?}",
genesis.genesis_hash, genesis.classified_cluster
),
));
}
let wallet = match crate::solana_execution::load_profile_wallet(profile, workspace_root).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let wallet_summary = wallet.summary();
let fee_payer = kb_model::Pubkey(wallet_summary.public_key.clone());
let balance = match http_pool
.get_balance_for_role(
request.query_role.as_str(),
&fee_payer,
&kb_rpc::GetBalanceConfig::confirmed(),
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if balance.lamports < profile.execution.max_fee_lamports {
return std::result::Result::Err(kb_core::Error::new(
"execution_balance_insufficient",
format!(
"Devnet wallet balance {} is below the configured fee ceiling {}",
balance.lamports, profile.execution.max_fee_lamports
),
));
}
let plan = match crate::solana_memo_execution::build_plan(profile, request, fee_payer.clone()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let plan_evaluation =
match kb_execution_safety::ExecutionSafetyChecker.evaluate_prepared_plan(&plan) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if plan_evaluation.decision == kb_execution_safety::ExecutionSafetyDecision::Deny {
return std::result::Result::Err(kb_core::Error::new(
"execution_plan_denied",
crate::solana_execution::violation_message(plan_evaluation.violations.as_slice()),
));
}
let latest_blockhash = match http_pool
.get_latest_blockhash_for_role(
request.query_role.as_str(),
&kb_rpc::GetLatestBlockhashConfig::confirmed(),
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let unsigned = match kb_execution_solana::build_legacy_transaction(
&plan,
latest_blockhash.blockhash.as_str(),
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let message_base64 = unsigned.message_base64();
let fee = match http_pool
.get_fee_for_message_for_role(
request.query_role.as_str(),
message_base64.as_str(),
&kb_rpc::GetFeeForMessageConfig::new(
kb_rpc::RpcCommitmentLevel::Confirmed,
std::option::Option::Some(latest_blockhash.context.slot),
),
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if fee.fee_lamports.is_none() {
return std::result::Result::Err(kb_core::Error::new(
"execution_fee_unavailable",
"getFeeForMessage returned null for the selected recent blockhash",
));
}
let unsigned_base64 = match unsigned.transaction_base64() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let simulation_config = match kb_rpc::SimulateTransactionConfig::new(
kb_rpc::RpcCommitmentLevel::Confirmed,
false,
false,
std::option::Option::Some(latest_blockhash.context.slot),
true,
std::option::Option::None,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
crate::solana_execution::emit(
observer,
crate::SolanaExecutionProgressLevel::Info,
"memo_simulation",
format!("simulating exact Memo message {}", unsigned.message_hash()),
std::option::Option::None,
);
let simulation_rpc = match http_pool
.simulate_transaction_for_role(
request.transaction_role.as_str(),
unsigned_base64.as_str(),
&simulation_config,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let simulation = simulation_rpc.to_execution_result(
kb_execution_api::ExecutionCluster::Devnet,
kb_execution_api::ExecutionBlockhashKind::Latest,
std::option::Option::Some(
simulation_rpc.context.slot.saturating_sub(latest_blockhash.context.slot),
),
std::option::Option::None,
std::option::Option::None,
std::option::Option::Some(&fee),
);
let evidence = unsigned.bind_simulation(simulation.clone());
let mut summary = crate::DevnetMemoExecutionSummary {
profile_name: profile.name.clone(),
cluster: kb_execution_api::ExecutionCluster::Devnet,
genesis_hash: genesis.genesis_hash,
wallet: wallet_summary,
balance_lamports: balance.lamports,
plan,
latest_blockhash,
fee,
simulation,
send_result: std::option::Option::None,
confirmation: std::option::Option::None,
backfill: std::option::Option::None,
core_extraction: std::option::Option::None,
decode_replay: std::option::Option::None,
idempotence_replay: std::option::Option::None,
annotations: std::vec::Vec::new(),
post_execution: std::option::Option::None,
};
if !request.submit {
return std::result::Result::Ok(summary);
}
if !summary.simulation.success {
return std::result::Result::Err(kb_core::Error::new(
"execution_simulation_failed",
crate::solana_execution::simulation_failure_message(&summary.simulation),
));
}
let send_evaluation = match kb_execution_safety::ExecutionSafetyChecker
.evaluate_send(&summary.plan, &summary.simulation)
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if send_evaluation.decision == kb_execution_safety::ExecutionSafetyDecision::Deny {
return std::result::Result::Err(kb_core::Error::new(
"execution_send_denied",
crate::solana_execution::violation_message(send_evaluation.violations.as_slice()),
));
}
let signed = match unsigned.sign_after_simulation(&evidence, &[wallet.as_signer()]) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if let std::result::Result::Err(error) = signed.verify_signatures() {
return std::result::Result::Err(error);
}
let signature = signed.primary_signature().clone();
let mut diagnostic = kb_execution_api::PostExecutionDiagnostic {
signature: signature.clone(),
canonical_inserted: false,
core_extracted: false,
decode_replayed: false,
materialized: false,
diagnostics: std::vec::Vec::new(),
};
let send_config = match kb_rpc::SendTransactionConfig::from_execution_config(
&profile.execution,
std::option::Option::Some(summary.latest_blockhash.context.slot),
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let signed_base64 = signed.transaction_base64();
let sent = match http_pool
.send_transaction_for_role(
request.transaction_role.as_str(),
signed_base64.as_str(),
&signature,
&send_config,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic.diagnostics.push(format!("Memo submission failed: {error}"));
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
},
};
summary.send_result = std::option::Option::Some(
sent.to_execution_result(kb_execution_api::ExecutionCluster::Devnet),
);
let confirmation_config = match kb_rpc::ConfirmTransactionConfig::from_execution_config(
&profile.execution,
std::option::Option::Some(summary.latest_blockhash.last_valid_block_height),
std::option::Option::Some(summary.latest_blockhash.context.slot),
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let confirmation = match http_pool
.confirm_transaction_for_roles(
request.transaction_role.as_str(),
request.query_role.as_str(),
kb_execution_api::ExecutionCluster::Devnet,
&signature,
&confirmation_config,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic.diagnostics.push(format!("Memo confirmation failed: {error}"));
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
},
};
let confirmation_status = confirmation.status;
summary.confirmation = std::option::Option::Some(confirmation);
if !matches!(
confirmation_status,
kb_execution_api::ExecutionConfirmationStatus::Confirmed
| kb_execution_api::ExecutionConfirmationStatus::Finalized
) {
diagnostic.diagnostics.push(format!(
"Memo post-validation stopped at confirmation status {confirmation_status:?}"
));
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
}
let backfill = match crate::solana_memo_execution::hydrate_signature(
http_pool, store, profile, request, observer, &signature,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic.diagnostics.push(format!("Memo hydration failed: {error}"));
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
},
};
diagnostic.canonical_inserted = crate::solana_memo_execution::canonical_available(&backfill);
summary.backfill = std::option::Option::Some(backfill);
if !diagnostic.canonical_inserted {
diagnostic
.diagnostics
.push("confirmed Memo transaction was unavailable for canonical hydration".to_string());
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
}
let extraction = match crate::execute_core_extraction(
store,
&crate::CoreExtractionRequest {
source: crate::CoreExtractionSource::Signatures(std::vec![signature.0.clone()]),
limit: 1,
max_concurrent_extractions: 1,
force_replay: request.force_post_validation_replay,
},
observer,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic.diagnostics.push(format!("Memo core extraction failed: {error}"));
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
},
};
diagnostic.core_extracted = extraction.failed == 0
&& !extraction.cancelled
&& extraction.selected == 1
&& extraction.extracted.saturating_add(extraction.skipped) >= 1;
summary.core_extraction = std::option::Option::Some(extraction);
if !diagnostic.core_extracted {
diagnostic.diagnostics.push("Memo core extraction did not complete".to_string());
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
}
let first_replay = match crate::solana_memo_execution::replay_memo(
store,
request,
&signature,
false,
decoders,
materializers,
observer,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic.diagnostics.push(format!("Memo decode replay failed: {error}"));
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
},
};
diagnostic.decode_replayed = crate::solana_memo_execution::decode_completed(&first_replay);
summary.decode_replay = std::option::Option::Some(first_replay);
let filter = match kb_store_core::MaterializedEventFilter::new(
std::option::Option::Some("transaction_annotations".to_string()),
std::option::Option::Some("transaction_annotation".to_string()),
std::option::Option::Some(signature.0.clone()),
8,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
summary.annotations =
match kb_store_core::DecodePipelineStore::list_materialized_events(store, &filter).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic.diagnostics.push(format!("Memo annotation query failed: {error}"));
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
},
};
diagnostic.materialized = diagnostic.decode_replayed
&& summary
.annotations
.iter()
.any(|row| return row.signature.as_str() == signature.0.as_str());
let second_replay = match crate::solana_memo_execution::replay_memo(
store,
request,
&signature,
true,
decoders,
materializers,
observer,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic.diagnostics.push(format!("Memo idempotence replay failed: {error}"));
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
},
};
let idempotent = second_replay.failed_inputs == 0
&& second_replay.processors.iter().all(|processor| {
return processor.failed == 0
&& processor.materialized_outputs == 0
&& processor.materialization_refused == 0;
});
summary.idempotence_replay = std::option::Option::Some(second_replay);
if !idempotent {
diagnostic
.diagnostics
.push("second Memo replay did not prove a clean idempotent skip".to_string());
} else if diagnostic.canonical_inserted
&& diagnostic.core_extracted
&& diagnostic.decode_replayed
&& diagnostic.materialized
{
diagnostic.diagnostics.push(
"Memo completed canonical hydration, core extraction, decode, annotation projection and idempotence validation"
.to_string(),
);
}
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
}
fn validate_profile(
profile: &kb_config::ProfileConfig,
request: &crate::DevnetMemoExecutionRequest,
) -> kb_core::Result<()> {
if profile.wallet.cluster != "devnet" {
return std::result::Result::Err(kb_core::Error::config(
"Memo Devnet orchestration requires a Devnet wallet profile",
));
}
if !profile.wallet.temporary_wallet_enabled || !profile.wallet.temporary_wallet_persist {
return std::result::Result::Err(kb_core::Error::config(
"Memo Devnet orchestration requires an enabled persistent temporary wallet",
));
}
if !profile.execution.require_simulation {
return std::result::Result::Err(kb_core::Error::config(
"Memo Devnet orchestration requires simulation",
));
}
if request.submit && !profile.wallet.devnet_send_enabled {
return std::result::Result::Err(kb_core::Error::config(
"Devnet transaction submission is disabled by the wallet profile",
));
}
if request.submit
&& profile.execution.require_operator_confirmation
&& !request.operator_confirmed
{
return std::result::Result::Err(kb_core::Error::config(
"Memo Devnet submission requires explicit operator confirmation",
));
}
return std::result::Result::Ok(());
}
fn build_plan(
profile: &kb_config::ProfileConfig,
request: &crate::DevnetMemoExecutionRequest,
fee_payer: kb_model::Pubkey,
) -> kb_core::Result<kb_execution_api::PreparedExecutionPlan> {
let signers = if request.include_wallet_as_memo_signer {
std::vec![kb_executor_spl_memo::SplMemoSigner { pubkey: fee_payer.clone() }]
} else {
std::vec::Vec::new()
};
let intent = kb_executor_spl_memo::SplMemoExecutionIntent {
intent_id: request.intent_id.clone(),
fee_payer: fee_payer.clone(),
policy: kb_execution_api::ExecutionPolicy {
cluster: kb_execution_api::ExecutionClusterPolicy {
expected_cluster: kb_execution_api::ExecutionCluster::Devnet,
allow_mainnet: false,
mainnet_confirmation: false,
},
simulation: kb_execution_api::ExecutionSimulationPolicy::Required,
blockhash: kb_execution_api::ExecutionBlockhashPolicy {
kind: kb_execution_api::ExecutionBlockhashKind::Latest,
max_age_slots: std::option::Option::Some(
profile.execution.recent_blockhash_max_age_slots,
),
nonce_account: std::option::Option::None,
nonce_authority: std::option::Option::None,
},
cost_limit: kb_execution_api::ExecutionCostLimit {
max_spend_lamports: std::option::Option::Some(0),
max_fee_lamports: std::option::Option::Some(profile.execution.max_fee_lamports),
max_compute_unit_price_micro_lamports: std::option::Option::Some(
profile.execution.max_compute_unit_price_micro_lamports,
),
},
authorized_signers: std::vec![fee_payer.clone()],
dry_run: !request.submit,
post_execution_validation: kb_execution_api::PostExecutionValidationPolicy {
canonical_insert_required: true,
core_extraction_required: true,
decode_replay_required: true,
materialization_required: true,
},
},
operation: kb_executor_spl_memo::SplMemoOperation::AddMemo {
generation: kb_executor_spl_memo::SplMemoGeneration::V4,
message: request.message.clone(),
signers,
},
};
return kb_execution_api::TypedInstructionExecutor::build_prepared_plan(
&kb_executor_spl_memo::SplMemoExecutor,
&intent,
);
}
async fn hydrate_signature<S, O>(
http_pool: &kb_rpc::HttpEndpointPool,
store: &S,
profile: &kb_config::ProfileConfig,
request: &crate::DevnetMemoExecutionRequest,
observer: &O,
signature: &kb_model::Signature,
) -> kb_core::Result<crate::BackfillSummary>
where
S: kb_store_core::RawTransactionStore + Sync,
O: crate::SolanaExecutionObserver,
{
let mut retry = 0_u32;
loop {
let result = match crate::execute_http_backfill(
http_pool,
store,
&crate::BackfillRequest {
role: request.query_role.clone(),
commitment: "confirmed".to_string(),
source: crate::BackfillSource::ExplicitSignatures(std::vec![signature.0.clone()]),
page_size: 1,
max_pages: 1,
max_concurrent_requests: 1,
max_retries: 0,
},
observer,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if crate::solana_memo_execution::canonical_available(&result)
|| retry >= request.post_validation_max_retries
|| observer.is_execution_cancelled()
{
return std::result::Result::Ok(result);
}
retry = retry.saturating_add(1);
tokio::time::sleep(std::time::Duration::from_millis(std::cmp::max(
profile.execution.confirmation_poll_interval_ms,
500,
)))
.await;
}
}
fn canonical_available(summary: &crate::BackfillSummary) -> bool {
return summary.failed == 0
&& summary.missing == 0
&& summary.candidates_completed == 1
&& summary.candidates_cancelled == 0
&& summary.candidates_not_started == 0
&& summary
.canonical_inserted
.saturating_add(summary.canonical_skipped)
.saturating_add(summary.existing_skipped)
>= 1;
}
async fn replay_memo<S, O>(
store: &S,
request: &crate::DevnetMemoExecutionRequest,
signature: &kb_model::Signature,
include_materialized_state: bool,
decoders: &[std::sync::Arc<dyn kb_decoder_api::InstructionDecoder>],
materializers: &[std::sync::Arc<dyn kb_materializer_api::EventMaterializer>],
observer: &O,
) -> kb_core::Result<crate::DecodeReplaySummary>
where
S: kb_store_core::DecodePipelineStore + Sync,
O: crate::SolanaExecutionObserver,
{
let mut states = std::vec![
kb_store_core::CoreInstructionProcessingState::Pending,
kb_store_core::CoreInstructionProcessingState::Failed,
kb_store_core::CoreInstructionProcessingState::ReplayRequested,
];
if include_materialized_state {
states.push(kb_store_core::CoreInstructionProcessingState::Materialized);
}
let selection = match kb_store_core::DecodeSelectionFilter::new(
std::vec![signature.0.clone()],
states,
std::option::Option::None,
std::option::Option::None,
std::vec![kb_program_ids::SPL_MEMO_V4_PROGRAM_ID.to_string()],
std::vec::Vec::new(),
false,
8,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return crate::execute_decode_replay(
store,
&crate::DecodeReplayRequest {
campaign_id: crate::new_decode_campaign_id(),
selection,
decoder_names: std::vec::Vec::new(),
dispatch_policy: crate::DecodeDispatchPolicy::HighestPriority,
max_concurrent_inputs: 1,
force_replay: if include_materialized_state {
false
} else {
request.force_post_validation_replay
},
force_replay_all_matching: false,
materialize_after_decode: true,
},
decoders,
materializers,
observer,
)
.await;
}
fn decode_completed(summary: &crate::DecodeReplaySummary) -> bool {
return summary.failed_inputs == 0
&& summary.unmatched == 0
&& !summary.cancelled
&& summary.completed >= 1
&& summary.processors.iter().all(|processor| {
return processor.failed == 0
&& processor.unsupported == 0
&& processor.materialization_refused == 0;
})
&& summary.processors.iter().map(|processor| return processor.decoded).sum::<u64>() >= 1;
}
#[cfg(test)]
mod tests {
fn local_devnet_profile() -> kb_config::ProfileConfig {
let config =
match kb_config::parse_config_json(include_str!("../../config/example.config.json")) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("example config parse failed: {error}"),
};
for profile in config.profiles {
if profile.name == "local_devnet" {
return profile;
}
}
panic!("local_devnet profile missing");
}
#[test]
fn request_is_simulation_only_and_bounded_by_default() {
let request = crate::DevnetMemoExecutionRequest::new("memo-1", "audit annotation");
assert!(!request.submit);
assert!(request.include_wallet_as_memo_signer);
assert!(request.validate().is_ok());
let oversized = "x".repeat(kb_executor_spl_memo::MAX_MEMO_MESSAGE_BYTES + 1);
assert!(crate::DevnetMemoExecutionRequest::new("memo-2", oversized).validate().is_err());
}
#[test]
fn exact_v4_plan_has_zero_spend_and_wallet_signer() {
let profile = crate::solana_memo_execution::tests::local_devnet_profile();
let fee_payer = kb_model::Pubkey(kb_program_ids::SYSTEM_PROGRAM_ID.to_string());
let mut request = crate::DevnetMemoExecutionRequest::new("memo-3", "hello");
request.submit = true;
request.operator_confirmed = true;
let plan =
match crate::solana_memo_execution::build_plan(&profile, &request, fee_payer.clone()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("Memo plan failed: {error}"),
};
assert_eq!(plan.requested_spend_lamports, 0);
assert_eq!(plan.fee_payer, fee_payer);
assert_eq!(plan.instructions.len(), 1);
assert_eq!(plan.instructions[0].program_id.0, kb_program_ids::SPL_MEMO_V4_PROGRAM_ID);
assert_eq!(plan.instructions[0].accounts.len(), 1);
assert!(plan.instructions[0].accounts[0].is_signer);
assert!(!plan.instructions[0].accounts[0].is_writable);
assert!(
kb_execution_safety::ExecutionSafetyChecker
.evaluate_prepared_plan(&plan)
.is_ok()
);
}
#[test]
fn submission_requires_profile_enablement_and_confirmation() {
let profile = crate::solana_memo_execution::tests::local_devnet_profile();
let mut request = crate::DevnetMemoExecutionRequest::new("memo-4", "hello");
assert!(crate::solana_memo_execution::validate_profile(&profile, &request).is_ok());
request.submit = true;
assert!(crate::solana_memo_execution::validate_profile(&profile, &request).is_err());
request.operator_confirmed = true;
assert!(crate::solana_memo_execution::validate_profile(&profile, &request).is_ok());
}
#[tokio::test]
async fn optional_devnet_memo_execution_from_env() {
if std::env::var("KB_DEVNET_MEMO_EXECUTION_TEST").ok().as_deref()
!= std::option::Option::Some("1")
{
return;
}
let database_url = match std::env::var("KB_POSTGRES_TEST_URL") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("KB_POSTGRES_TEST_URL is required: {error}");
},
};
let mut profile = crate::solana_memo_execution::tests::local_devnet_profile();
profile.database.backend = "postgres".to_string();
profile.database.postgres.url = database_url;
if let std::result::Result::Ok(directory) = std::env::var("KB_DEVNET_WALLET_DIR") {
profile.wallet.wallet_dir = directory;
}
let pool = match kb_rpc::HttpEndpointPool::from_profile(&profile) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("HTTP pool creation failed: {error}"),
};
let store = match kb_store_pg::PostgresStore::connect_from_profile_config(&profile).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("PostgreSQL connection failed: {error}"),
};
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
panic!("PostgreSQL schema initialization failed: {error}");
}
let mut request = crate::DevnetMemoExecutionRequest::new(
format!("devnet-memo-test-{}", uuid::Uuid::new_v4()),
format!("khadhroony-bot2 memo validation {}", uuid::Uuid::new_v4()),
);
request.post_validation_max_retries = 20;
if std::env::var("KB_DEVNET_MEMO_SUBMIT").ok().as_deref() == std::option::Option::Some("1")
{
request.submit = true;
request.operator_confirmed = true;
}
let decoders: std::vec::Vec<std::sync::Arc<dyn kb_decoder_api::InstructionDecoder>> =
std::vec![std::sync::Arc::new(kb_decoder_spl_memo::SplMemoDecoder)];
let materializers: std::vec::Vec<
std::sync::Arc<dyn kb_materializer_api::EventMaterializer>,
> = std::vec![std::sync::Arc::new(
kb_materializer_transaction_annotations::TransactionAnnotationMaterializer,
)];
let workspace_root = match std::path::Path::new(env!("CARGO_MANIFEST_DIR")).parent() {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("workspace root cannot be resolved"),
};
let summary = match crate::execute_devnet_memo(
&pool,
&store,
&profile,
workspace_root,
&request,
decoders.as_slice(),
materializers.as_slice(),
&crate::NoopSolanaExecutionObserver,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("Devnet Memo execution failed: {error}"),
};
assert!(summary.simulation.success);
if request.submit {
let post_execution = match summary.post_execution {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("Memo post-execution diagnostic missing"),
};
assert!(post_execution.canonical_inserted);
assert!(post_execution.core_extracted);
assert!(post_execution.decode_replayed);
assert!(post_execution.materialized);
assert!(!summary.annotations.is_empty());
let idempotence = match summary.idempotence_replay {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("Memo idempotence replay missing"),
};
assert_eq!(
idempotence
.processors
.iter()
.map(|processor| return processor.materialized_outputs)
.sum::<u64>(),
0
);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,242 @@
// file: kb_pipeline/src/solana_token_2022_correlation.rs
// version: 1
//! Deterministic Token-2022 instruction-to-state correlation.
/// Correlation outcome between one committed instruction fact and one final state snapshot.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum Token2022CorrelationStatus {
/// The final snapshot contains the extension expected by the instruction fact.
Confirmed,
/// The final snapshot does not contain the extension expected by the instruction fact.
Contradicted,
/// The instruction output is outside the supported correlation inventory.
NotApplicable,
}
/// Deterministic correlation report for one instruction output and one final snapshot.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct Token2022StateCorrelationReport {
/// Stable caller-provided correlation identity.
pub correlation_key: std::string::String,
/// Canonical account identity checked against the snapshot.
pub account: kb_model::Pubkey,
/// Instruction operation or risk fact used for correlation.
pub fact_code: std::string::String,
/// Extension expected from the fact when the fact is supported.
pub expected_extension: std::option::Option<std::string::String>,
/// Final correlation outcome.
pub status: Token2022CorrelationStatus,
/// Snapshot context slot.
pub snapshot_slot: u64,
/// Whether the snapshot contains the expected extension.
pub extension_present: std::option::Option<bool>,
/// Explicit semantic limitation of this correlation.
pub fact_only: bool,
}
/// Correlates one materialized instruction fact with one authoritative Token-2022 snapshot.
pub fn correlate_token_2022_instruction_with_snapshot(
correlation_key: &str,
account: &kb_model::Pubkey,
instruction_output: &kb_materializer_api::MaterializedOutput,
snapshot: &crate::Token2022StatefulSnapshotBundle,
) -> kb_core::Result<Token2022StateCorrelationReport> {
if correlation_key.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"Token-2022 correlation key must not be empty",
));
}
if snapshot.account_key != account.0 {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_correlation_account_mismatch",
format!(
"Token-2022 correlation expected account {}, got snapshot {}",
account.0, snapshot.account_key
),
));
}
let fact_code = match instruction_output.payload_json.get("riskKind") {
std::option::Option::Some(value) => value.as_str().unwrap_or("").to_string(),
std::option::Option::None => instruction_output
.payload_json
.get("operation")
.and_then(serde_json::Value::as_str)
.unwrap_or("")
.to_string(),
};
if fact_code.is_empty() {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_correlation_fact_missing",
"Token-2022 correlation requires riskKind or operation in the instruction output",
));
}
let expected_extension = expected_extension(fact_code.as_str());
let extension_present = expected_extension.map(|extension| {
return snapshot.extension_names.iter().any(|candidate| return candidate == extension);
});
let status = match extension_present {
std::option::Option::Some(true) => Token2022CorrelationStatus::Confirmed,
std::option::Option::Some(false) => Token2022CorrelationStatus::Contradicted,
std::option::Option::None => Token2022CorrelationStatus::NotApplicable,
};
return std::result::Result::Ok(Token2022StateCorrelationReport {
correlation_key: correlation_key.to_string(),
account: account.clone(),
fact_code,
expected_extension: expected_extension.map(str::to_string),
status,
snapshot_slot: snapshot.slot,
extension_present,
fact_only: true,
});
}
fn expected_extension(fact_code: &str) -> std::option::Option<&'static str> {
return match fact_code {
"default_account_state_configured"
| "initialize_default_account_state"
| "update_default_account_state" => std::option::Option::Some("default_account_state"),
"required_transfer_memos_enabled"
| "required_transfer_memos_disabled"
| "enable_required_transfer_memos"
| "disable_required_transfer_memos" => std::option::Option::Some("memo_transfer"),
"cpi_guard_enabled" | "cpi_guard_disabled" | "enable_cpi_guard" | "disable_cpi_guard" => {
std::option::Option::Some("cpi_guard")
},
"non_transferable_mint_configured" | "initialize_non_transferable_mint" => {
std::option::Option::Some("non_transferable")
},
"pausable_mint_configured"
| "mint_activity_paused"
| "mint_activity_resumed"
| "initialize_pausable_config"
| "pause"
| "resume" => std::option::Option::Some("pausable"),
"permissioned_burn_configured" | "initialize_permissioned_burn" => {
std::option::Option::Some("permissioned_burn")
},
"confidential_credits_enabled"
| "confidential_credits_disabled"
| "non_confidential_credits_enabled"
| "non_confidential_credits_disabled"
| "enable_confidential_credits"
| "disable_confidential_credits"
| "enable_non_confidential_credits"
| "disable_non_confidential_credits" => {
std::option::Option::Some("confidential_transfer_account")
},
"initialize_transfer_fee_config"
| "set_transfer_fee"
| "withdraw_withheld_tokens_from_mint"
| "harvest_withheld_tokens_to_mint" => std::option::Option::Some("transfer_fee_config"),
"withdraw_withheld_tokens_from_accounts" | "transfer_checked_with_fee" => {
std::option::Option::Some("transfer_fee_amount")
},
"initialize_confidential_transfer_fee_config"
| "withdraw_confidential_withheld_tokens_from_mint"
| "harvest_confidential_withheld_tokens_to_mint"
| "enable_confidential_harvest_to_mint"
| "disable_confidential_harvest_to_mint" => {
std::option::Option::Some("confidential_transfer_fee_config")
},
"withdraw_confidential_withheld_tokens_from_accounts"
| "transfer_confidential_tokens_with_fee" => {
std::option::Option::Some("confidential_transfer_fee_amount")
},
_ => std::option::Option::None,
};
}
#[cfg(test)]
mod tests {
fn snapshot(extension_names: &[&str]) -> crate::Token2022StatefulSnapshotBundle {
return crate::Token2022StatefulSnapshotBundle {
account_key: solana_pubkey::Pubkey::new_from_array([7u8; 32]).to_string(),
slot: 42,
state_kind: "mint".to_string(),
extension_names: extension_names
.iter()
.map(|value| return (*value).to_string())
.collect(),
outputs: std::vec::Vec::new(),
};
}
fn output(field: &str, value: &str) -> kb_materializer_api::MaterializedOutput {
let mut payload = serde_json::Map::new();
payload.insert(field.to_string(), serde_json::Value::String(value.to_string()));
return kb_materializer_api::MaterializedOutput {
output_key: "fact:0".to_string(),
family: kb_model::MaterializedEventFamily::Risk,
payload_json: serde_json::Value::Object(payload),
};
}
#[test]
fn known_fact_is_confirmed_or_contradicted_by_the_final_extension_inventory() {
let snapshot = snapshot(&["pausable"]);
let account = kb_model::Pubkey(snapshot.account_key.clone());
let confirmed = super::correlate_token_2022_instruction_with_snapshot(
"corr:pause",
&account,
&output("riskKind", "mint_activity_paused"),
&snapshot,
);
assert!(confirmed.is_ok());
if let std::result::Result::Ok(confirmed) = confirmed {
assert_eq!(confirmed.status, super::Token2022CorrelationStatus::Confirmed);
assert_eq!(confirmed.extension_present, std::option::Option::Some(true));
}
let contradicted = super::correlate_token_2022_instruction_with_snapshot(
"corr:fee",
&account,
&output("operation", "initialize_transfer_fee_config"),
&snapshot,
);
assert!(contradicted.is_ok());
if let std::result::Result::Ok(contradicted) = contradicted {
assert_eq!(contradicted.status, super::Token2022CorrelationStatus::Contradicted);
assert_eq!(contradicted.extension_present, std::option::Option::Some(false));
}
}
#[test]
fn unsupported_fact_is_explicitly_not_applicable_without_inventing_a_conclusion() {
let snapshot = snapshot(&[]);
let account = kb_model::Pubkey(snapshot.account_key.clone());
let report = super::correlate_token_2022_instruction_with_snapshot(
"corr:unknown",
&account,
&output("operation", "unknown_future_operation"),
&snapshot,
);
assert!(report.is_ok());
if let std::result::Result::Ok(report) = report {
assert_eq!(report.status, super::Token2022CorrelationStatus::NotApplicable);
assert_eq!(report.extension_present, std::option::Option::None);
assert!(report.fact_only);
}
}
#[test]
fn correlation_identity_and_account_mismatch_fail_closed() {
let snapshot = snapshot(&["memo_transfer"]);
let account = kb_model::Pubkey(snapshot.account_key.clone());
let empty = super::correlate_token_2022_instruction_with_snapshot(
"",
&account,
&output("riskKind", "required_transfer_memos_enabled"),
&snapshot,
);
assert!(empty.is_err());
let wrong = kb_model::Pubkey(solana_pubkey::Pubkey::new_from_array([8u8; 32]).to_string());
let mismatch = super::correlate_token_2022_instruction_with_snapshot(
"corr:mismatch",
&wrong,
&output("riskKind", "required_transfer_memos_enabled"),
&snapshot,
);
assert!(mismatch.is_err());
}
}

View File

@@ -0,0 +1,337 @@
// file: kb_pipeline/src/solana_token_2022_crypto_preflight.rs
// version: 2
//! Bounded cryptographic preflight for Token-2022 proof context-state accounts.
/// Maximum number of distinct proof context-state accounts accepted by one request.
pub const MAX_TOKEN_2022_PROOF_CONTEXTS: usize = 8;
/// Generic metadata bytes retained by every ZK ElGamal proof context-state account.
pub const ZK_PROOF_CONTEXT_META_BYTES: usize = 33;
/// One exact pre-verified proof context-state requirement.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Token2022ProofContextRequirement {
/// Stable semantic role used in diagnostics.
pub role: std::string::String,
/// Canonical proof context-state account.
pub account: kb_model::Pubkey,
/// Exact proof kind expected by the Token-2022 builder.
pub proof_type: kb_executor_solana_core::ZkElGamalProofType,
/// Optional authority retained by the context-state account.
pub expected_authority: std::option::Option<kb_model::Pubkey>,
}
/// One bounded cryptographic preflight request.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Token2022CryptographicPreflightRequest {
/// Endpoint role used by every account read.
pub query_role: std::string::String,
/// Optional minimum RPC context slot.
pub min_context_slot: std::option::Option<u64>,
/// Ordered proof context-state requirements.
pub proof_contexts: std::vec::Vec<Token2022ProofContextRequirement>,
}
/// One validated proof context-state result.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct Token2022ProofContextReport {
/// Stable semantic role.
pub role: std::string::String,
/// Canonical proof context-state account.
pub account: kb_model::Pubkey,
/// Official one-byte proof discriminator.
pub proof_discriminator: u8,
/// Exact context-state size for the proof type.
pub context_state_bytes: usize,
/// Context slot returned by the endpoint.
pub context_slot: u64,
/// Ordered successful checks.
pub checks: std::vec::Vec<std::string::String>,
}
/// Complete cryptographic preflight report.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct Token2022CryptographicPreflightReport {
/// Commitment used for all reads.
pub commitment: std::string::String,
/// Highest context slot observed across all reads.
pub context_slot: u64,
/// Ordered validated proof contexts.
pub proof_contexts: std::vec::Vec<Token2022ProofContextReport>,
}
/// Reads and validates all pre-verified proof context-state accounts required by one operation.
pub async fn inspect_token_2022_cryptographic_preflight(
pool: &kb_rpc::HttpEndpointPool,
request: &crate::Token2022CryptographicPreflightRequest,
) -> kb_core::Result<crate::Token2022CryptographicPreflightReport> {
let requirements = match validate_proof_context_requirements(request) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let mut context_slot = request.min_context_slot.unwrap_or(0);
let mut reports = std::vec::Vec::with_capacity(requirements.len());
for requirement in requirements {
let expected_size = requirement.proof_type.context_state_size();
let config = match kb_rpc::GetAccountInfoConfig::new_with_data(
kb_rpc::RpcCommitmentLevel::Confirmed,
request.min_context_slot,
expected_size,
) {
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(), &requirement.account, &config)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let report = match validate_proof_context_account(&requirement, &result) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
context_slot = context_slot.max(report.context_slot);
reports.push(report);
}
return std::result::Result::Ok(crate::Token2022CryptographicPreflightReport {
commitment: "confirmed".to_string(),
context_slot,
proof_contexts: reports,
});
}
fn validate_proof_context_requirements(
request: &crate::Token2022CryptographicPreflightRequest,
) -> kb_core::Result<std::vec::Vec<crate::Token2022ProofContextRequirement>> {
if request.query_role.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"Token-2022 cryptographic preflight query_role must not be empty",
));
}
if request.proof_contexts.len() > crate::MAX_TOKEN_2022_PROOF_CONTEXTS {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_proof_context_limit_exceeded",
format!(
"Token-2022 cryptographic preflight accepts at most {} proof contexts",
crate::MAX_TOKEN_2022_PROOF_CONTEXTS
),
));
}
let mut indexes = std::collections::BTreeMap::<std::string::String, usize>::new();
let mut unique = std::vec::Vec::<crate::Token2022ProofContextRequirement>::new();
for requirement in &request.proof_contexts {
if requirement.role.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"Token-2022 proof context role must not be empty",
));
}
if let std::option::Option::Some(index) = indexes.get(requirement.account.0.as_str()) {
if &unique[*index] != requirement {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_proof_context_conflicting_duplicate",
format!(
"Token-2022 proof context {} has conflicting requirements",
requirement.account.0
),
));
}
continue;
}
indexes.insert(requirement.account.0.clone(), unique.len());
unique.push(requirement.clone());
}
return std::result::Result::Ok(unique);
}
fn validate_proof_context_account(
requirement: &crate::Token2022ProofContextRequirement,
result: &kb_rpc::AccountInfoResult,
) -> kb_core::Result<crate::Token2022ProofContextReport> {
let account = match result.account.as_ref() {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_proof_context_missing",
format!(
"Token-2022 proof context account {} does not exist",
requirement.account.0
),
));
},
};
if account.owner.0 != kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_proof_context_owner_mismatch",
format!(
"Token-2022 proof context owner must be {}, got {}",
kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID,
account.owner.0
),
));
}
if account.executable {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_proof_context_executable",
"Token-2022 proof context account must not be executable",
));
}
let expected_size = requirement.proof_type.context_state_size();
if account.space != expected_size as u64 || account.data.len() != expected_size {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_proof_context_size_mismatch",
format!(
"Token-2022 proof context {} must contain exactly {expected_size} bytes, got space {} and data {}",
requirement.account.0,
account.space,
account.data.len()
),
));
}
if account.data.len() < crate::ZK_PROOF_CONTEXT_META_BYTES {
return std::result::Result::Err(kb_core::Error::invalid_state(
"Token-2022 proof context is shorter than its generic metadata",
));
}
let discriminator = account.data[32];
if discriminator != requirement.proof_type.discriminator() {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_proof_context_type_mismatch",
format!(
"Token-2022 proof context {} discriminator {} does not match expected {}",
requirement.account.0,
discriminator,
requirement.proof_type.discriminator()
),
));
}
let mut checks = std::vec![
"zk_program_owner".to_string(),
"not_executable".to_string(),
"exact_context_state_size".to_string(),
"proof_type_discriminator".to_string(),
];
if let std::option::Option::Some(expected_authority) = requirement.expected_authority.as_ref() {
let retained_authority = bs58::encode(&account.data[..32]).into_string();
if retained_authority != expected_authority.0 {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_proof_context_authority_mismatch",
format!(
"Token-2022 proof context {} retains authority {}, expected {}",
requirement.account.0, retained_authority, expected_authority.0
),
));
}
checks.push("retained_authority".to_string());
}
return std::result::Result::Ok(crate::Token2022ProofContextReport {
role: requirement.role.clone(),
account: requirement.account.clone(),
proof_discriminator: discriminator,
context_state_bytes: expected_size,
context_slot: result.context.slot,
checks,
});
}
#[cfg(test)]
mod tests {
fn pubkey(byte: u8) -> kb_model::Pubkey {
return kb_model::Pubkey(bs58::encode([byte; 32]).into_string());
}
fn requirement(
account: kb_model::Pubkey,
proof_type: kb_executor_solana_core::ZkElGamalProofType,
) -> crate::Token2022ProofContextRequirement {
return crate::Token2022ProofContextRequirement {
role: "equality_proof".to_string(),
account,
proof_type,
expected_authority: std::option::Option::Some(pubkey(9)),
};
}
fn account_result(
proof_type: kb_executor_solana_core::ZkElGamalProofType,
) -> kb_rpc::AccountInfoResult {
let mut data = std::vec![0u8; proof_type.context_state_size()];
data[..32].copy_from_slice(&[9u8; 32]);
data[32] = proof_type.discriminator();
return kb_rpc::AccountInfoResult {
context: kb_rpc::RpcResponseContext {
slot: 77,
api_version: std::option::Option::None,
},
account: std::option::Option::Some(kb_rpc::AccountInfoValue {
lamports: 1,
owner: kb_model::ProgramId(kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID.to_string()),
executable: false,
rent_epoch: 0,
space: data.len() as u64,
data,
}),
};
}
#[test]
fn exact_duplicates_are_deduplicated_and_conflicts_fail_closed() {
let item = requirement(
pubkey(1),
kb_executor_solana_core::ZkElGamalProofType::CiphertextCiphertextEquality,
);
let request = crate::Token2022CryptographicPreflightRequest {
query_role: "query".to_string(),
min_context_slot: std::option::Option::Some(7),
proof_contexts: std::vec![item.clone(), item.clone()],
};
let unique = crate::solana_token_2022_crypto_preflight::validate_proof_context_requirements(
&request,
);
assert_eq!(unique.as_ref().map(std::vec::Vec::len), std::result::Result::Ok(1));
let conflict = crate::Token2022CryptographicPreflightRequest {
query_role: "query".to_string(),
min_context_slot: std::option::Option::None,
proof_contexts: std::vec![
item,
requirement(
pubkey(1),
kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU128,
),
],
};
assert!(
crate::solana_token_2022_crypto_preflight::validate_proof_context_requirements(
&conflict
)
.is_err()
);
}
#[test]
fn proof_context_validation_checks_owner_size_type_and_authority() {
let proof_type = kb_executor_solana_core::ZkElGamalProofType::CiphertextCiphertextEquality;
let context_requirement = requirement(pubkey(1), proof_type);
let result = account_result(proof_type);
let report = crate::solana_token_2022_crypto_preflight::validate_proof_context_account(
&context_requirement,
&result,
);
assert_eq!(
report.as_ref().map(|value| return value.checks.len()),
std::result::Result::Ok(5)
);
let wrong_type = requirement(
pubkey(1),
kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU128,
);
assert!(
crate::solana_token_2022_crypto_preflight::validate_proof_context_account(
&wrong_type,
&result,
)
.is_err()
);
}
}

View File

@@ -0,0 +1,968 @@
// file: kb_pipeline/src/solana_token_2022_devnet_execution.rs
// version: 2
//! Devnet Token-2022 execution with stateful and canonical post-validation.
/// Complete request for one Devnet Token-2022 execution.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DevnetSplToken2022ExecutionRequest {
/// Stable caller-provided execution identifier.
pub intent_id: std::string::String,
/// Endpoint role used for state, balance, blockhash, fee and hydration calls.
pub query_role: std::string::String,
/// Endpoint role used for simulation, submission and confirmation polling.
pub transaction_role: std::string::String,
/// Exact typed Token-2022 operation.
pub operation: kb_executor_spl_token_2022::SplToken2022Operation,
/// Explicitly authorizes signing and submission after successful simulation.
pub submit: bool,
/// Explicit operator confirmation required by the active profile.
pub operator_confirmed: bool,
/// Number of `getTransaction` retries after the first hydration attempt.
pub post_validation_max_retries: u32,
/// Replaces existing core and decode outputs for the submitted signature.
pub force_post_validation_replay: bool,
}
impl crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest {
/// Creates a conservative simulation-only request.
pub fn new(
intent_id: impl std::convert::Into<std::string::String>,
operation: kb_executor_spl_token_2022::SplToken2022Operation,
) -> Self {
return Self {
intent_id: intent_id.into(),
query_role: "http_queries".to_string(),
transaction_role: "http_transactions".to_string(),
operation,
submit: false,
operator_confirmed: false,
post_validation_max_retries: 10,
force_post_validation_replay: false,
};
}
/// Validates request-local bounds independently from one profile.
pub fn validate(&self) -> kb_core::Result<()> {
if self.intent_id.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"Devnet Token-2022 execution intent id must not be empty",
));
}
if self.query_role.trim().is_empty() || self.transaction_role.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"Devnet Token-2022 execution endpoint roles must not be empty",
));
}
if self.post_validation_max_retries > 20 {
return std::result::Result::Err(kb_core::Error::config(
"post-execution getTransaction retries must not exceed 20",
));
}
return std::result::Result::Ok(());
}
}
/// Complete result of one Devnet Token-2022 execution.
#[derive(Clone, Debug, PartialEq)]
pub struct DevnetSplToken2022ExecutionSummary {
/// Profile used by the orchestration.
pub profile_name: std::string::String,
/// Exact classified cluster.
pub cluster: kb_execution_api::ExecutionCluster,
/// Genesis hash returned by the selected endpoint.
pub genesis_hash: std::string::String,
/// Non-secret persistent wallet description.
pub wallet: kb_wallet::WalletSummary,
/// Wallet balance observed before planning.
pub balance_lamports: u64,
/// Stateful readiness report produced before plan simulation.
pub stateful_preflight: crate::Token2022PreflightReport,
/// Exact prepared Token plan.
pub plan: kb_execution_api::PreparedExecutionPlan,
/// Recent blockhash used by the exact transaction.
pub latest_blockhash: kb_rpc::LatestBlockhashResult,
/// Fee estimate for the exact compiled message.
pub fee: kb_rpc::FeeForMessageResult,
/// Exact simulation result bound to the compiled message.
pub simulation: kb_execution_api::ExecutionSimulationResult,
/// Submission result when explicitly authorized.
pub send_result: std::option::Option<kb_execution_api::ExecutionSendResult>,
/// Confirmation result when submitted.
pub confirmation: std::option::Option<kb_execution_api::ExecutionConfirmationResult>,
/// Canonical hydration result for the exact signature.
pub backfill: std::option::Option<crate::BackfillSummary>,
/// Core extraction result for the exact signature.
pub core_extraction: std::option::Option<crate::CoreExtractionSummary>,
/// First Token decode and materialization replay.
pub decode_replay: std::option::Option<crate::DecodeReplaySummary>,
/// Second replay proving idempotence for the same decoder version and input.
pub idempotence_replay: std::option::Option<crate::DecodeReplaySummary>,
/// Exact materialized rows produced for the submitted Token transaction.
pub materializations: std::vec::Vec<kb_store_core::MaterializedEventQueryRow>,
/// Aggregated post-execution validation diagnostic.
pub post_execution: std::option::Option<kb_execution_api::PostExecutionDiagnostic>,
}
struct PreparedToken2022Execution {
wallet: kb_wallet::TemporaryWallet,
unsigned: kb_execution_solana::UnsignedSolanaTransaction,
evidence: kb_execution_solana::SolanaSimulationEvidence,
summary: crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionSummary,
}
/// Simulates one Devnet Token-2022 operation after stateful preflight.
pub async fn simulate_devnet_spl_token_2022<O>(
http_pool: &kb_rpc::HttpEndpointPool,
profile: &kb_config::ProfileConfig,
workspace_root: &std::path::Path,
request: &crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest,
observer: &O,
) -> kb_core::Result<crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionSummary>
where
O: crate::SolanaExecutionObserver,
{
if request.submit {
return std::result::Result::Err(kb_core::Error::config(
"simulate_devnet_spl_token_2022 requires submit=false",
));
}
let prepared = match crate::solana_token_2022_devnet_execution::prepare_execution(
http_pool,
profile,
workspace_root,
request,
observer,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(prepared.summary);
}
/// Executes one Devnet Token-2022 simulation or authorized submission.
#[allow(clippy::too_many_arguments)]
pub async fn execute_devnet_spl_token_2022<S, O>(
http_pool: &kb_rpc::HttpEndpointPool,
store: &S,
profile: &kb_config::ProfileConfig,
workspace_root: &std::path::Path,
request: &crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest,
decoders: &[std::sync::Arc<dyn kb_decoder_api::InstructionDecoder>],
materializers: &[std::sync::Arc<dyn kb_materializer_api::EventMaterializer>],
observer: &O,
) -> kb_core::Result<crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionSummary>
where
S: kb_store_core::RawTransactionStore
+ kb_store_core::CoreExtractionStore
+ kb_store_core::DecodePipelineStore
+ Sync,
O: crate::SolanaExecutionObserver,
{
let prepared = match crate::solana_token_2022_devnet_execution::prepare_execution(
http_pool,
profile,
workspace_root,
request,
observer,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if !request.submit {
return std::result::Result::Ok(prepared.summary);
}
if !prepared.summary.simulation.success {
return std::result::Result::Err(kb_core::Error::new(
"execution_simulation_failed",
crate::solana_execution::simulation_failure_message(&prepared.summary.simulation),
));
}
if let std::result::Result::Err(error) =
crate::solana_token_2022_devnet_execution::validate_profile_wallet_signers(
prepared.unsigned.required_signer_pubkeys(),
prepared.summary.wallet.public_key.as_str(),
)
{
return std::result::Result::Err(error);
}
let send_evaluation = match kb_execution_safety::ExecutionSafetyChecker
.evaluate_send(&prepared.summary.plan, &prepared.summary.simulation)
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if send_evaluation.decision == kb_execution_safety::ExecutionSafetyDecision::Deny {
return std::result::Result::Err(kb_core::Error::new(
"execution_send_denied",
crate::solana_execution::violation_message(send_evaluation.violations.as_slice()),
));
}
let signed = match prepared
.unsigned
.sign_after_simulation(&prepared.evidence, &[prepared.wallet.as_signer()])
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if let std::result::Result::Err(error) = signed.verify_signatures() {
return std::result::Result::Err(error);
}
let signature = signed.primary_signature().clone();
let mut summary = prepared.summary;
let mut diagnostic = kb_execution_api::PostExecutionDiagnostic {
signature: signature.clone(),
canonical_inserted: false,
core_extracted: false,
decode_replayed: false,
materialized: false,
diagnostics: std::vec::Vec::new(),
};
let send_config = match kb_rpc::SendTransactionConfig::from_execution_config(
&profile.execution,
std::option::Option::Some(summary.latest_blockhash.context.slot),
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let sent = match http_pool
.send_transaction_for_role(
request.transaction_role.as_str(),
signed.transaction_base64().as_str(),
&signature,
&send_config,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic.diagnostics.push(format!("Token-2022 submission failed: {error}"));
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
},
};
summary.send_result = std::option::Option::Some(
sent.to_execution_result(kb_execution_api::ExecutionCluster::Devnet),
);
let confirmation_config = match kb_rpc::ConfirmTransactionConfig::from_execution_config(
&profile.execution,
std::option::Option::Some(summary.latest_blockhash.last_valid_block_height),
std::option::Option::Some(summary.latest_blockhash.context.slot),
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let confirmation = match http_pool
.confirm_transaction_for_roles(
request.transaction_role.as_str(),
request.query_role.as_str(),
kb_execution_api::ExecutionCluster::Devnet,
&signature,
&confirmation_config,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic.diagnostics.push(format!("Token-2022 confirmation failed: {error}"));
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
},
};
let confirmation_status = confirmation.status;
summary.confirmation = std::option::Option::Some(confirmation);
if !matches!(
confirmation_status,
kb_execution_api::ExecutionConfirmationStatus::Confirmed
| kb_execution_api::ExecutionConfirmationStatus::Finalized
) {
diagnostic.diagnostics.push(format!(
"Token-2022 post-validation stopped at confirmation status {confirmation_status:?}"
));
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
}
let backfill = match crate::solana_token_execution::hydrate_signature(
http_pool,
store,
profile,
request.query_role.as_str(),
request.post_validation_max_retries,
observer,
&signature,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic.diagnostics.push(format!("Token-2022 hydration failed: {error}"));
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
},
};
diagnostic.canonical_inserted = crate::solana_token_execution::canonical_available(&backfill);
summary.backfill = std::option::Option::Some(backfill);
if !diagnostic.canonical_inserted {
diagnostic.diagnostics.push(
"confirmed Token-2022 transaction was unavailable for canonical hydration".to_string(),
);
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
}
let extraction = match crate::execute_core_extraction(
store,
&crate::CoreExtractionRequest {
source: crate::CoreExtractionSource::Signatures(std::vec![signature.0.clone()]),
limit: 1,
max_concurrent_extractions: 1,
force_replay: request.force_post_validation_replay,
},
observer,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic
.diagnostics
.push(format!("Token-2022 core extraction failed: {error}"));
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
},
};
diagnostic.core_extracted = extraction.failed == 0
&& !extraction.cancelled
&& extraction.selected == 1
&& extraction.extracted.saturating_add(extraction.skipped) >= 1;
summary.core_extraction = std::option::Option::Some(extraction);
if !diagnostic.core_extracted {
diagnostic
.diagnostics
.push("Token-2022 core extraction did not complete".to_string());
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
}
let first_replay = match crate::solana_token_execution::replay_program(
store,
&signature,
false,
request.force_post_validation_replay,
&[kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID],
decoders,
materializers,
observer,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic.diagnostics.push(format!("Token-2022 decode replay failed: {error}"));
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
},
};
diagnostic.decode_replayed = crate::solana_token_execution::decode_completed(&first_replay);
summary.decode_replay = std::option::Option::Some(first_replay);
let filter = match kb_store_core::MaterializedEventFilter::new(
std::option::Option::None,
std::option::Option::None,
std::option::Option::Some(signature.0.clone()),
64,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let rows =
match kb_store_core::DecodePipelineStore::list_materialized_events(store, &filter).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic
.diagnostics
.push(format!("Token-2022 materialization query failed: {error}"));
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
},
};
summary.materializations = rows
.into_iter()
.filter(|row| return row.source_decoder_name == "spl_token_2022")
.collect();
diagnostic.materialized =
!summary.plan.policy.post_execution_validation.materialization_required
|| !summary.materializations.is_empty();
let second_replay = match crate::solana_token_execution::replay_program(
store,
&signature,
true,
request.force_post_validation_replay,
&[kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID],
decoders,
materializers,
observer,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic
.diagnostics
.push(format!("Token-2022 idempotence replay failed: {error}"));
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
},
};
let idempotent = second_replay.failed_inputs == 0
&& second_replay.processors.iter().all(|processor| {
return processor.failed == 0
&& processor.materialized_outputs == 0
&& processor.materialization_refused == 0;
});
summary.idempotence_replay = std::option::Option::Some(second_replay);
if !idempotent {
diagnostic
.diagnostics
.push("second Token-2022 replay did not prove a clean idempotent skip".to_string());
} else if diagnostic.canonical_inserted
&& diagnostic.core_extracted
&& diagnostic.decode_replayed
&& diagnostic.materialized
{
diagnostic.diagnostics.push(
"Token-2022 completed canonical hydration, core extraction, decode, materialization and idempotence validation"
.to_string(),
);
}
summary.post_execution = std::option::Option::Some(diagnostic);
return std::result::Result::Ok(summary);
}
async fn prepare_execution<O>(
http_pool: &kb_rpc::HttpEndpointPool,
profile: &kb_config::ProfileConfig,
workspace_root: &std::path::Path,
request: &crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest,
observer: &O,
) -> kb_core::Result<crate::solana_token_2022_devnet_execution::PreparedToken2022Execution>
where
O: crate::SolanaExecutionObserver,
{
if let std::result::Result::Err(error) = request.validate() {
return std::result::Result::Err(error);
}
if let std::result::Result::Err(error) =
crate::solana_token_2022_devnet_execution::validate_profile(profile, request)
{
return std::result::Result::Err(error);
}
if let std::result::Result::Err(error) =
crate::solana_execution::ensure_not_cancelled(observer, "token_validate")
{
return std::result::Result::Err(error);
}
let genesis = match http_pool.get_genesis_hash_for_role(request.query_role.as_str()).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if genesis.classified_cluster
!= std::option::Option::Some(kb_execution_api::ExecutionCluster::Devnet)
{
return std::result::Result::Err(kb_core::Error::new(
"execution_cluster_mismatch",
format!(
"expected Devnet genesis hash but endpoint returned {} classified as {:?}",
genesis.genesis_hash, genesis.classified_cluster
),
));
}
let preflight_request =
match crate::solana_token_2022_devnet_execution::preflight_request(request) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let readiness = match crate::inspect_token_2022_preflight(http_pool, &preflight_request).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let wallet = match crate::solana_execution::load_profile_wallet(profile, workspace_root).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let wallet_summary = wallet.summary();
let fee_payer = kb_model::Pubkey(wallet_summary.public_key.clone());
let balance = match http_pool
.get_balance_for_role(
request.query_role.as_str(),
&fee_payer,
&kb_rpc::GetBalanceConfig::confirmed(),
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if balance.lamports < profile.execution.max_fee_lamports {
return std::result::Result::Err(kb_core::Error::new(
"execution_balance_insufficient",
format!(
"Devnet wallet balance {} is below the configured fee ceiling {}",
balance.lamports, profile.execution.max_fee_lamports
),
));
}
let plan = match crate::solana_token_2022_devnet_execution::build_plan(
profile,
request,
fee_payer.clone(),
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let plan_evaluation =
match kb_execution_safety::ExecutionSafetyChecker.evaluate_prepared_plan(&plan) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if plan_evaluation.decision == kb_execution_safety::ExecutionSafetyDecision::Deny {
return std::result::Result::Err(kb_core::Error::new(
"execution_plan_denied",
crate::solana_execution::violation_message(plan_evaluation.violations.as_slice()),
));
}
let latest_blockhash = match http_pool
.get_latest_blockhash_for_role(
request.query_role.as_str(),
&kb_rpc::GetLatestBlockhashConfig::confirmed(),
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let unsigned = match kb_execution_solana::build_legacy_transaction(
&plan,
latest_blockhash.blockhash.as_str(),
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let fee = match http_pool
.get_fee_for_message_for_role(
request.query_role.as_str(),
unsigned.message_base64().as_str(),
&kb_rpc::GetFeeForMessageConfig::new(
kb_rpc::RpcCommitmentLevel::Confirmed,
std::option::Option::Some(latest_blockhash.context.slot),
),
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if fee.fee_lamports.is_none() {
return std::result::Result::Err(kb_core::Error::new(
"execution_fee_unavailable",
"getFeeForMessage returned null for the selected recent blockhash",
));
}
let unsigned_base64 = match unsigned.transaction_base64() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let simulation_config = match kb_rpc::SimulateTransactionConfig::new(
kb_rpc::RpcCommitmentLevel::Confirmed,
false,
false,
std::option::Option::Some(latest_blockhash.context.slot),
true,
std::option::Option::None,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
crate::solana_execution::emit(
observer,
crate::SolanaExecutionProgressLevel::Info,
"spl_token_2022_simulation",
format!("simulating exact Token-2022 message {}", unsigned.message_hash()),
std::option::Option::None,
);
let simulation_rpc = match http_pool
.simulate_transaction_for_role(
request.transaction_role.as_str(),
unsigned_base64.as_str(),
&simulation_config,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let simulation = simulation_rpc.to_execution_result(
kb_execution_api::ExecutionCluster::Devnet,
kb_execution_api::ExecutionBlockhashKind::Latest,
std::option::Option::Some(
simulation_rpc.context.slot.saturating_sub(latest_blockhash.context.slot),
),
std::option::Option::None,
std::option::Option::None,
std::option::Option::Some(&fee),
);
let evidence = unsigned.bind_simulation(simulation.clone());
let summary = crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionSummary {
profile_name: profile.name.clone(),
cluster: kb_execution_api::ExecutionCluster::Devnet,
genesis_hash: genesis.genesis_hash,
wallet: wallet_summary,
balance_lamports: balance.lamports,
stateful_preflight: readiness,
plan,
latest_blockhash,
fee,
simulation,
send_result: std::option::Option::None,
confirmation: std::option::Option::None,
backfill: std::option::Option::None,
core_extraction: std::option::Option::None,
decode_replay: std::option::Option::None,
idempotence_replay: std::option::Option::None,
materializations: std::vec::Vec::new(),
post_execution: std::option::Option::None,
};
return std::result::Result::Ok(
crate::solana_token_2022_devnet_execution::PreparedToken2022Execution {
wallet,
unsigned,
evidence,
summary,
},
);
}
fn preflight_request(
request: &crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest,
) -> kb_core::Result<crate::Token2022PreflightRequest> {
let operation = match &request.operation {
kb_executor_spl_token_2022::SplToken2022Operation::Instruction { value } => value.as_ref(),
kb_executor_spl_token_2022::SplToken2022Operation::Batch { instructions: _ } => {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_token_2022_batch_not_supported",
"Devnet Token-2022 validation scenarios require one non-batch operation",
));
},
};
let mut requirements = std::vec::Vec::new();
match operation {
kb_executor_spl_token_2022::SplTokenSingleOperation::MintToChecked {
mint,
destination,
authority,
amount: _,
decimals,
} => {
requirements.push(mint_requirement("mint", mint, std::option::Option::Some(*decimals)));
requirements.push(account_requirement(
"destination",
destination,
mint,
std::option::Option::None,
));
if let std::result::Result::Err(error) = validate_single_authority(authority) {
return std::result::Result::Err(error);
}
},
kb_executor_spl_token_2022::SplTokenSingleOperation::TransferChecked {
source,
mint,
destination,
authority,
amount: _,
decimals,
} => {
requirements.push(account_requirement(
"source",
source,
mint,
std::option::Option::Some(authority.authority.clone()),
));
requirements.push(mint_requirement("mint", mint, std::option::Option::Some(*decimals)));
requirements.push(account_requirement(
"destination",
destination,
mint,
std::option::Option::None,
));
if let std::result::Result::Err(error) = validate_single_authority(authority) {
return std::result::Result::Err(error);
}
},
kb_executor_spl_token_2022::SplTokenSingleOperation::ApproveChecked {
source,
mint,
delegate: _,
authority,
amount: _,
decimals,
} => {
requirements.push(account_requirement(
"source",
source,
mint,
std::option::Option::Some(authority.authority.clone()),
));
requirements.push(mint_requirement("mint", mint, std::option::Option::Some(*decimals)));
if let std::result::Result::Err(error) = validate_single_authority(authority) {
return std::result::Result::Err(error);
}
},
kb_executor_spl_token_2022::SplTokenSingleOperation::Revoke { source, authority } => {
requirements.push(account_requirement(
"source",
source,
&kb_model::Pubkey(std::string::String::new()),
std::option::Option::Some(authority.authority.clone()),
));
requirements[0].expected_mint = std::option::Option::None;
if let std::result::Result::Err(error) = validate_single_authority(authority) {
return std::result::Result::Err(error);
}
},
kb_executor_spl_token_2022::SplTokenSingleOperation::BurnChecked {
source,
mint,
authority,
amount: _,
decimals,
} => {
requirements.push(account_requirement(
"source",
source,
mint,
std::option::Option::Some(authority.authority.clone()),
));
requirements.push(mint_requirement("mint", mint, std::option::Option::Some(*decimals)));
if let std::result::Result::Err(error) = validate_single_authority(authority) {
return std::result::Result::Err(error);
}
},
kb_executor_spl_token_2022::SplTokenSingleOperation::FreezeAccount {
account,
mint,
authority,
}
| kb_executor_spl_token_2022::SplTokenSingleOperation::ThawAccount {
account,
mint,
authority,
} => {
requirements.push(account_requirement(
"account",
account,
mint,
std::option::Option::None,
));
requirements.push(mint_requirement("mint", mint, std::option::Option::None));
if let std::result::Result::Err(error) = validate_single_authority(authority) {
return std::result::Result::Err(error);
}
},
kb_executor_spl_token_2022::SplTokenSingleOperation::CloseAccount {
account,
destination: _,
authority,
} => {
requirements.push(account_requirement(
"account",
account,
&kb_model::Pubkey(std::string::String::new()),
std::option::Option::Some(authority.authority.clone()),
));
requirements[0].expected_mint = std::option::Option::None;
if let std::result::Result::Err(error) = validate_single_authority(authority) {
return std::result::Result::Err(error);
}
},
_ => {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_token_2022_devnet_operation_unsupported",
format!(
"Token-2022 Devnet validation does not expose {}",
operation.operation_code()
),
));
},
}
return std::result::Result::Ok(crate::Token2022PreflightRequest {
query_role: request.query_role.clone(),
min_context_slot: std::option::Option::None,
max_accounts: crate::MAX_TOKEN_2022_PREFLIGHT_ACCOUNTS,
max_total_data_bytes: crate::MAX_TOKEN_2022_PREFLIGHT_TOTAL_BYTES,
requirements,
elgamal_registry: std::option::Option::None,
});
}
fn validate_single_authority(
authority: &kb_executor_spl_token_2022::SplTokenAuthority,
) -> kb_core::Result<()> {
if !authority.multisig_signers.is_empty() {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_token_2022_devnet_multisig_not_supported",
"the Devnet validation UI currently supports one profile-wallet authority",
));
}
return std::result::Result::Ok(());
}
fn mint_requirement(
role: &str,
mint: &kb_model::Pubkey,
decimals: std::option::Option<u8>,
) -> crate::Token2022PreflightRequirement {
return crate::Token2022PreflightRequirement {
role: role.to_string(),
account: mint.clone(),
kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint,
max_data_bytes: 65_536,
expected_mint: std::option::Option::None,
expected_owner: std::option::Option::None,
expected_decimals: decimals,
required_extensions: std::vec::Vec::new(),
context: crate::Token2022StatefulContext::default(),
};
}
fn account_requirement(
role: &str,
account: &kb_model::Pubkey,
mint: &kb_model::Pubkey,
owner: std::option::Option<kb_model::Pubkey>,
) -> crate::Token2022PreflightRequirement {
return crate::Token2022PreflightRequirement {
role: role.to_string(),
account: account.clone(),
kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Account,
max_data_bytes: 65_536,
expected_mint: std::option::Option::Some(mint.clone()),
expected_owner: owner,
expected_decimals: std::option::Option::None,
required_extensions: std::vec::Vec::new(),
context: crate::Token2022StatefulContext::default(),
};
}
fn validate_profile(
profile: &kb_config::ProfileConfig,
request: &crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest,
) -> kb_core::Result<()> {
if profile.wallet.cluster != "devnet" {
return std::result::Result::Err(kb_core::Error::config(
"Token-2022 Devnet orchestration requires a Devnet wallet profile",
));
}
if !profile.wallet.temporary_wallet_enabled || !profile.wallet.temporary_wallet_persist {
return std::result::Result::Err(kb_core::Error::config(
"Token-2022 Devnet orchestration requires an enabled persistent temporary wallet",
));
}
if !profile.execution.require_simulation {
return std::result::Result::Err(kb_core::Error::config(
"Token-2022 Devnet orchestration requires simulation",
));
}
if request.submit && !profile.wallet.devnet_send_enabled {
return std::result::Result::Err(kb_core::Error::config(
"Devnet transaction submission is disabled by the wallet profile",
));
}
if request.submit
&& profile.execution.require_operator_confirmation
&& !request.operator_confirmed
{
return std::result::Result::Err(kb_core::Error::config(
"Token-2022 Devnet submission requires explicit operator confirmation",
));
}
return std::result::Result::Ok(());
}
fn build_plan(
profile: &kb_config::ProfileConfig,
request: &crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest,
fee_payer: kb_model::Pubkey,
) -> kb_core::Result<kb_execution_api::PreparedExecutionPlan> {
let materialization_required =
crate::solana_token_2022_devnet_execution::operation_requires_materialization(
&request.operation,
);
let authorized_signers = std::vec![fee_payer.clone()];
let intent = kb_executor_spl_token_2022::SplToken2022ExecutionIntent {
intent_id: request.intent_id.clone(),
fee_payer,
policy: kb_execution_api::ExecutionPolicy {
cluster: kb_execution_api::ExecutionClusterPolicy {
expected_cluster: kb_execution_api::ExecutionCluster::Devnet,
allow_mainnet: false,
mainnet_confirmation: false,
},
simulation: kb_execution_api::ExecutionSimulationPolicy::Required,
blockhash: kb_execution_api::ExecutionBlockhashPolicy {
kind: kb_execution_api::ExecutionBlockhashKind::Latest,
max_age_slots: std::option::Option::Some(
profile.execution.recent_blockhash_max_age_slots,
),
nonce_account: std::option::Option::None,
nonce_authority: std::option::Option::None,
},
cost_limit: kb_execution_api::ExecutionCostLimit {
max_spend_lamports: std::option::Option::Some(
profile.execution.devnet_max_spend_lamports,
),
max_fee_lamports: std::option::Option::Some(profile.execution.max_fee_lamports),
max_compute_unit_price_micro_lamports: std::option::Option::Some(
profile.execution.max_compute_unit_price_micro_lamports,
),
},
authorized_signers,
dry_run: !request.submit,
post_execution_validation: kb_execution_api::PostExecutionValidationPolicy {
canonical_insert_required: true,
core_extraction_required: true,
decode_replay_required: true,
materialization_required,
},
},
operation: request.operation.clone(),
};
return kb_execution_api::TypedInstructionExecutor::build_prepared_plan(
&kb_executor_spl_token_2022::SplToken2022Executor,
&intent,
);
}
fn operation_requires_materialization(
_operation: &kb_executor_spl_token_2022::SplToken2022Operation,
) -> bool {
return true;
}
fn validate_profile_wallet_signers(
required_signers: &[std::string::String],
wallet_pubkey: &str,
) -> kb_core::Result<()> {
if required_signers.iter().any(|value| return value.as_str() != wallet_pubkey) {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_token_2022_external_signer_unavailable",
format!(
"the Devnet Token orchestrator can sign only with profile wallet {}; required signers are {}",
wallet_pubkey,
required_signers.join(",")
),
));
}
return std::result::Result::Ok(());
}

View File

@@ -0,0 +1,180 @@
// file: kb_pipeline/src/solana_token_2022_devnet_scenarios.rs
// version: 2
//! Stable Devnet validation scenarios required to close milestone 0.4.6.
/// Stable category of one independent Devnet validation scenario.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum DevnetSplValidationFamily {
/// Public Token-2022 state mutations without cryptographic proofs.
Token2022Public,
/// ElGamal Registry lifecycle kept technically separate from Token-2022.
ElGamalRegistry,
/// Token-2022 confidential operations requiring proof material.
Token2022Confidential,
}
/// Stable status of one scenario in the application validation workflow.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum DevnetSplValidationImplementationStatus {
/// The scenario can be simulated and submitted through the application.
Executable,
/// The typed backend exists but the application execution path remains to be connected.
BackendReady,
/// The scenario requires externally prepared proof material before execution.
ProofFixtureRequired,
}
/// One independent Devnet validation scenario exposed to applications.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct DevnetSplValidationScenario {
/// Stable identifier used by scripts, tests and the frontend.
pub id: std::string::String,
/// Operator-visible label.
pub label: std::string::String,
/// Scenario family.
pub family: crate::DevnetSplValidationFamily,
/// Stable executor operation code.
pub operation_code: std::string::String,
/// Current implementation status.
pub implementation_status: crate::DevnetSplValidationImplementationStatus,
/// Whether this scenario mutates on-chain state.
pub destructive: bool,
/// Whether explicit operator confirmation is mandatory before submission.
pub operator_confirmation_required: bool,
/// Whether cryptographic proof material is required.
pub proof_required: bool,
/// Ordered fixture variables required by the scenario.
pub required_fixture_variables: std::vec::Vec<std::string::String>,
}
/// Returns the complete ordered Devnet scenario inventory for milestone 0.4.6.
pub fn devnet_spl_validation_scenarios() -> std::vec::Vec<crate::DevnetSplValidationScenario> {
return vec![
public_scenario("token_2022_mint_to_checked", "Token-2022 MintToChecked", kb_executor_spl_token_2022::MINT_TO_CHECKED_OPERATION, &["TOKEN_2022_MINT", "TOKEN_2022_SOURCE", "TOKEN_2022_AUTHORITY", "TOKEN_2022_DECIMALS", "TOKEN_2022_MINT_AMOUNT_RAW"]),
public_scenario("token_2022_transfer_checked", "Token-2022 TransferChecked", kb_executor_spl_token_2022::TRANSFER_CHECKED_OPERATION, &["TOKEN_2022_SOURCE", "TOKEN_2022_MINT", "TOKEN_2022_DESTINATION", "TOKEN_2022_AUTHORITY", "TOKEN_2022_DECIMALS", "TOKEN_2022_TRANSFER_AMOUNT_RAW"]),
public_scenario("token_2022_approve_checked", "Token-2022 ApproveChecked", kb_executor_spl_token_2022::APPROVE_CHECKED_OPERATION, &["TOKEN_2022_SOURCE", "TOKEN_2022_MINT", "TOKEN_2022_DELEGATE", "TOKEN_2022_AUTHORITY", "TOKEN_2022_DECIMALS", "TOKEN_2022_APPROVE_AMOUNT_RAW"]),
public_scenario("token_2022_revoke", "Token-2022 Revoke", kb_executor_spl_token_2022::REVOKE_OPERATION, &["TOKEN_2022_SOURCE", "TOKEN_2022_AUTHORITY"]),
public_scenario("token_2022_burn_checked", "Token-2022 BurnChecked", kb_executor_spl_token_2022::BURN_CHECKED_OPERATION, &["TOKEN_2022_SOURCE", "TOKEN_2022_MINT", "TOKEN_2022_AUTHORITY", "TOKEN_2022_DECIMALS", "TOKEN_2022_BURN_AMOUNT_RAW"]),
public_scenario("token_2022_freeze_account", "Token-2022 FreezeAccount", kb_executor_spl_token_2022::FREEZE_ACCOUNT_OPERATION, &["TOKEN_2022_SOURCE", "TOKEN_2022_MINT", "TOKEN_2022_FREEZE_AUTHORITY"]),
public_scenario("token_2022_thaw_account", "Token-2022 ThawAccount", kb_executor_spl_token_2022::THAW_ACCOUNT_OPERATION, &["TOKEN_2022_SOURCE", "TOKEN_2022_MINT", "TOKEN_2022_FREEZE_AUTHORITY"]),
public_scenario("token_2022_close_destination", "Token-2022 CloseAccount destination", kb_executor_spl_token_2022::CLOSE_ACCOUNT_OPERATION, &["TOKEN_2022_DESTINATION", "KB_DEVNET_WALLET_ADDRESS", "TOKEN_2022_AUTHORITY"]),
registry_scenario("elgamal_registry_create", "ElGamal Registry CreateRegistry", "spl_elgamal_registry.create_registry", &["ELGAMAL_REGISTRY_ADDRESS", "ELGAMAL_PUBKEY_BASE64", "PUBKEY_VALIDITY_PROOF_CONTEXT"]),
registry_scenario("elgamal_registry_update", "ElGamal Registry UpdateRegistry", "spl_elgamal_registry.update_registry", &["ELGAMAL_REGISTRY_ADDRESS", "ELGAMAL_PUBKEY_BASE64", "PUBKEY_VALIDITY_PROOF_CONTEXT"]),
confidential_scenario("token_2022_configure_confidential_account", "Token-2022 ConfigureAccountWithRegistry", kb_executor_spl_token_2022::CONFIGURE_CONFIDENTIAL_TRANSFER_ACCOUNT_WITH_REGISTRY_OPERATION, &["TOKEN_2022_SOURCE", "TOKEN_2022_MINT", "ELGAMAL_REGISTRY_ADDRESS", "PUBKEY_VALIDITY_PROOF_CONTEXT"]),
];
}
fn public_scenario(
id: &str,
label: &str,
operation_code: &str,
variables: &[&str],
) -> crate::DevnetSplValidationScenario {
return scenario(
id,
label,
crate::DevnetSplValidationFamily::Token2022Public,
operation_code,
crate::DevnetSplValidationImplementationStatus::Executable,
true,
false,
variables,
);
}
fn registry_scenario(
id: &str,
label: &str,
operation_code: &str,
variables: &[&str],
) -> crate::DevnetSplValidationScenario {
return scenario(
id,
label,
crate::DevnetSplValidationFamily::ElGamalRegistry,
operation_code,
crate::DevnetSplValidationImplementationStatus::ProofFixtureRequired,
true,
true,
variables,
);
}
fn confidential_scenario(
id: &str,
label: &str,
operation_code: &str,
variables: &[&str],
) -> crate::DevnetSplValidationScenario {
return scenario(
id,
label,
crate::DevnetSplValidationFamily::Token2022Confidential,
operation_code,
crate::DevnetSplValidationImplementationStatus::ProofFixtureRequired,
true,
true,
variables,
);
}
fn scenario(
id: &str,
label: &str,
family: crate::DevnetSplValidationFamily,
operation_code: &str,
implementation_status: crate::DevnetSplValidationImplementationStatus,
destructive: bool,
proof_required: bool,
variables: &[&str],
) -> crate::DevnetSplValidationScenario {
return crate::DevnetSplValidationScenario {
id: id.to_string(),
label: label.to_string(),
family,
operation_code: operation_code.to_string(),
implementation_status,
destructive,
operator_confirmation_required: destructive,
proof_required,
required_fixture_variables: variables
.iter()
.map(|value| return (*value).to_string())
.collect(),
};
}
#[cfg(test)]
mod tests {
#[test]
fn milestone_inventory_is_stable_unique_and_keeps_registry_separate() {
let scenarios = crate::devnet_spl_validation_scenarios();
assert_eq!(scenarios.len(), 11);
let ids = scenarios
.iter()
.map(|scenario| return scenario.id.as_str())
.collect::<std::collections::BTreeSet<&str>>();
assert_eq!(ids.len(), scenarios.len());
assert!(
scenarios.iter().any(|scenario| return scenario.family
== crate::DevnetSplValidationFamily::ElGamalRegistry)
);
assert!(
scenarios
.iter()
.filter(|scenario| return scenario.family
== crate::DevnetSplValidationFamily::Token2022Public)
.all(|scenario| return !scenario.proof_required)
);
}
#[test]
fn every_mutating_scenario_requires_operator_confirmation() {
assert!(crate::devnet_spl_validation_scenarios().iter().all(|scenario| {
return !scenario.destructive || scenario.operator_confirmation_required;
}));
}
}

View File

@@ -0,0 +1,331 @@
// file: kb_pipeline/src/solana_token_2022_execution_orchestration.rs
// version: 3
//! Final Token-2022 execution-readiness and stateful postcondition contracts.
/// Maximum number of distinct transaction signers accepted by one Token-2022 execution envelope.
pub const MAX_TOKEN_2022_EXECUTION_SIGNERS: usize = 16;
/// Result of one stateful postcondition checked after a confirmed Token-2022 transaction.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Token2022ExecutionPostconditionStatus {
/// The observed final state confirms the expected operation effect.
Confirmed,
/// The observed final state contradicts the expected operation effect.
Contradicted,
/// The operation has no supported stateful postcondition in the current contract.
NotApplicable,
}
/// One explicit stateful postcondition retained by the execution orchestrator.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct Token2022ExecutionPostcondition {
/// Stable semantic role such as `mint`, `source`, `destination`, or `registry`.
pub role: std::string::String,
/// Canonical account whose final state was inspected.
pub account: kb_model::Pubkey,
/// Explicit result of the postcondition.
pub status: crate::Token2022ExecutionPostconditionStatus,
/// Bounded diagnostic explaining the result without retaining complete account data.
pub diagnostic: std::string::String,
}
/// Complete deterministic request checked before Token-2022 transaction signing.
#[derive(Clone, Debug, PartialEq)]
pub struct Token2022ExecutionReadinessRequest {
/// Exact prepared executor plan.
pub plan: kb_execution_api::PreparedExecutionPlan,
/// Exact hash of the compiled Solana message.
pub message_hash: std::string::String,
/// Hash retained by the simulation evidence.
pub simulated_message_hash: std::string::String,
/// Whether the exact compiled message was simulated.
pub simulated: bool,
/// Whether the exact simulation succeeded.
pub simulation_succeeded: bool,
/// Stateful Token-2022 account preflight report.
pub stateful_preflight: crate::Token2022PreflightReport,
/// Cryptographic proof context-state preflight report.
pub cryptographic_preflight: crate::Token2022CryptographicPreflightReport,
/// Ordered proof orchestration report.
pub proof_orchestration: crate::Token2022ProofOrchestrationReport,
/// Public keys actually available to sign the transaction.
pub resolved_signers: std::vec::Vec<kb_model::Pubkey>,
/// Whether submission was explicitly requested.
pub submit: bool,
/// Whether submission received explicit operator confirmation.
pub operator_confirmed: bool,
}
/// Deterministic execution-readiness report produced before signing.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct Token2022ExecutionReadinessReport {
/// Stable operation code.
pub operation_code: std::string::String,
/// Exact compiled message hash bound to simulation.
pub message_hash: std::string::String,
/// Highest RPC context slot across stateful and cryptographic preflights.
pub context_slot: u64,
/// Ordered signer public keys required by the plan.
pub required_signers: std::vec::Vec<kb_model::Pubkey>,
/// Whether transaction signing and submission are authorized.
pub send_authorized: bool,
/// Ordered successful checks.
pub checks: std::vec::Vec<std::string::String>,
}
/// Validates the complete Token-2022 execution envelope before transaction signing.
pub fn validate_token_2022_execution_readiness(
request: &crate::Token2022ExecutionReadinessRequest,
) -> kb_core::Result<crate::Token2022ExecutionReadinessReport> {
if request.plan.operation_code.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"Token-2022 execution plan operation_code must not be empty",
));
}
if request.plan.operation_code != request.proof_orchestration.operation_code {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_execution_operation_mismatch",
"Token-2022 plan and proof orchestration operation codes must match",
));
}
if request.message_hash.trim().is_empty()
|| request.simulated_message_hash.trim().is_empty()
|| request.message_hash != request.simulated_message_hash
{
return std::result::Result::Err(kb_core::Error::new(
"token_2022_execution_simulation_message_mismatch",
"Token-2022 simulation must be bound to the exact compiled message hash",
));
}
if !request.simulated || !request.simulation_succeeded {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_execution_simulation_required",
"Token-2022 execution requires one successful exact-message simulation",
));
}
if !request.proof_orchestration.simulation_required {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_execution_proof_policy_mismatch",
"Token-2022 proof orchestration must require simulation",
));
}
if request.stateful_preflight.commitment != "confirmed"
|| request.cryptographic_preflight.commitment != "confirmed"
{
return std::result::Result::Err(kb_core::Error::new(
"token_2022_execution_preflight_commitment_mismatch",
"Token-2022 stateful and cryptographic preflights must use confirmed commitment",
));
}
let required_signers = match validate_signers(
request.plan.required_signers.as_slice(),
request.resolved_signers.as_slice(),
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if request.submit && !request.operator_confirmed {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_execution_confirmation_required",
"Token-2022 submission requires explicit operator confirmation",
));
}
if request.submit && !request.proof_orchestration.send_authorized {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_execution_proof_send_not_authorized",
"Token-2022 proof orchestration did not authorize submission",
));
}
if request.submit && request.plan.policy.dry_run {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_execution_dry_run_blocks_submission",
"Token-2022 plan remains dry-run and cannot be submitted",
));
}
let context_slot = request
.stateful_preflight
.context_slot
.max(request.cryptographic_preflight.context_slot);
return std::result::Result::Ok(crate::Token2022ExecutionReadinessReport {
operation_code: request.plan.operation_code.clone(),
message_hash: request.message_hash.clone(),
context_slot,
required_signers,
send_authorized: request.submit
&& request.operator_confirmed
&& request.proof_orchestration.send_authorized
&& !request.plan.policy.dry_run,
checks: vec![
"operation_matches_proof_orchestration".to_string(),
"simulation_bound_to_exact_message".to_string(),
"confirmed_stateful_preflight".to_string(),
"confirmed_cryptographic_preflight".to_string(),
"all_required_signers_resolved".to_string(),
"submission_policy_consistent".to_string(),
],
});
}
/// Aggregates explicit postconditions without converting unsupported checks into success.
pub fn summarize_token_2022_postconditions(
postconditions: &[crate::Token2022ExecutionPostcondition],
) -> crate::Token2022ExecutionPostconditionStatus {
if postconditions.iter().any(|item| {
return item.status == crate::Token2022ExecutionPostconditionStatus::Contradicted;
}) {
return crate::Token2022ExecutionPostconditionStatus::Contradicted;
}
if postconditions.iter().any(|item| {
return item.status == crate::Token2022ExecutionPostconditionStatus::Confirmed;
}) {
return crate::Token2022ExecutionPostconditionStatus::Confirmed;
}
return crate::Token2022ExecutionPostconditionStatus::NotApplicable;
}
fn validate_signers(
required: &[kb_execution_api::RequiredSigner],
resolved: &[kb_model::Pubkey],
) -> kb_core::Result<std::vec::Vec<kb_model::Pubkey>> {
if required.len() > crate::MAX_TOKEN_2022_EXECUTION_SIGNERS
|| resolved.len() > crate::MAX_TOKEN_2022_EXECUTION_SIGNERS
{
return std::result::Result::Err(kb_core::Error::new(
"token_2022_execution_signer_limit_exceeded",
format!(
"Token-2022 execution accepts at most {} signers",
crate::MAX_TOKEN_2022_EXECUTION_SIGNERS
),
));
}
let mut required_unique =
std::collections::BTreeMap::<std::string::String, kb_model::Pubkey>::new();
for signer in required {
required_unique
.entry(signer.pubkey.0.clone())
.or_insert_with(|| return signer.pubkey.clone());
}
let resolved_set = resolved
.iter()
.map(|signer| return signer.0.clone())
.collect::<std::collections::BTreeSet<std::string::String>>();
for signer in required_unique.values() {
if !resolved_set.contains(signer.0.as_str()) {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_execution_signer_unresolved",
format!("Token-2022 required signer {} is unresolved", signer.0),
));
}
}
return std::result::Result::Ok(required_unique.into_values().collect());
}
#[cfg(test)]
mod tests {
fn pubkey(byte: u8) -> kb_model::Pubkey {
return kb_model::Pubkey(bs58::encode([byte; 32]).into_string());
}
fn request() -> crate::Token2022ExecutionReadinessRequest {
let signer = pubkey(1);
let policy = kb_execution_api::ExecutionPolicy {
dry_run: false,
..std::default::Default::default()
};
let plan = kb_execution_api::PreparedExecutionPlan {
executor_name: "kb_executor_spl_token_2022".to_string(),
executor_version: "0.4.6".to_string(),
intent_id: "intent".to_string(),
operation_code: "spl_token_2022.confidential_transfer".to_string(),
fee_payer: signer.clone(),
instructions: vec![],
required_signers: vec![kb_execution_api::RequiredSigner {
pubkey: signer.clone(),
role: "authority".to_string(),
}],
policy,
requested_spend_lamports: 0,
requested_compute_unit_price_micro_lamports: std::option::Option::None,
};
return crate::Token2022ExecutionReadinessRequest {
plan,
message_hash: "message-hash".to_string(),
simulated_message_hash: "message-hash".to_string(),
simulated: true,
simulation_succeeded: true,
stateful_preflight: crate::Token2022PreflightReport {
commitment: "confirmed".to_string(),
context_slot: 100,
requested_data_bytes: 0,
accounts: vec![],
elgamal_registry_validated: false,
},
cryptographic_preflight: crate::Token2022CryptographicPreflightReport {
commitment: "confirmed".to_string(),
context_slot: 101,
proof_contexts: vec![],
},
proof_orchestration: crate::Token2022ProofOrchestrationReport {
operation_code: "spl_token_2022.confidential_transfer".to_string(),
instructions_sysvar_required: true,
inline_offsets: vec![1],
context_requirements: vec![],
simulation_required: true,
send_authorized: true,
checks: vec![],
},
resolved_signers: vec![signer],
submit: true,
operator_confirmed: true,
};
}
#[test]
fn exact_message_preflights_and_signers_authorize_submission() {
let report = crate::validate_token_2022_execution_readiness(&request())
.expect("complete Token-2022 readiness must succeed");
assert!(report.send_authorized);
assert_eq!(report.context_slot, 101);
assert_eq!(report.required_signers.len(), 1);
}
#[test]
fn message_mismatch_missing_signer_and_dry_run_fail_closed() {
let mut mismatched = request();
mismatched.simulated_message_hash = "other".to_string();
assert!(crate::validate_token_2022_execution_readiness(&mismatched).is_err());
let mut missing = request();
missing.resolved_signers.clear();
assert!(crate::validate_token_2022_execution_readiness(&missing).is_err());
let mut dry_run = request();
dry_run.plan.policy.dry_run = true;
assert!(crate::validate_token_2022_execution_readiness(&dry_run).is_err());
}
#[test]
fn postcondition_summary_preserves_contradicted_and_not_applicable() {
let account = pubkey(2);
let not_applicable = crate::Token2022ExecutionPostcondition {
role: "mint".to_string(),
account: account.clone(),
status: crate::Token2022ExecutionPostconditionStatus::NotApplicable,
diagnostic: "no supported final-state assertion".to_string(),
};
assert_eq!(
crate::summarize_token_2022_postconditions(&[not_applicable]),
crate::Token2022ExecutionPostconditionStatus::NotApplicable
);
let contradicted = crate::Token2022ExecutionPostcondition {
role: "source".to_string(),
account,
status: crate::Token2022ExecutionPostconditionStatus::Contradicted,
diagnostic: "final extension inventory contradicts the expected effect".to_string(),
};
assert_eq!(
crate::summarize_token_2022_postconditions(&[contradicted]),
crate::Token2022ExecutionPostconditionStatus::Contradicted
);
}
}

View File

@@ -0,0 +1,445 @@
// file: kb_pipeline/src/solana_token_2022_preflight.rs
// version: 1
//! Bounded Token-2022 stateful preflight orchestration.
/// Maximum distinct Token-2022 accounts accepted by one preflight inspection.
pub const MAX_TOKEN_2022_PREFLIGHT_ACCOUNTS: usize = 16;
/// Maximum aggregate account-data budget accepted by one preflight inspection.
pub const MAX_TOKEN_2022_PREFLIGHT_TOTAL_BYTES: usize = 262_144;
/// One exact Token-2022 account requirement for a stateful preflight.
#[derive(Clone, Debug, PartialEq)]
pub struct Token2022PreflightRequirement {
/// Stable semantic role used in diagnostics.
pub role: std::string::String,
/// Canonical account address.
pub account: kb_model::Pubkey,
/// Expected Token-2022 state category.
pub kind: kb_decoder_spl_token_2022::state::Token2022StateKind,
/// Maximum complete account bytes accepted for this requirement.
pub max_data_bytes: usize,
/// Optional expected mint for a Token Account.
pub expected_mint: std::option::Option<kb_model::Pubkey>,
/// Optional expected owner for a Token Account.
pub expected_owner: std::option::Option<kb_model::Pubkey>,
/// Optional exact decimals expected for a Mint.
pub expected_decimals: std::option::Option<u8>,
/// Published extension names required on the account.
pub required_extensions: std::vec::Vec<std::string::String>,
/// Optional external identities needed by cross-account extensions.
pub context: crate::Token2022StatefulContext,
}
/// One bounded Token-2022 preflight request.
#[derive(Clone, Debug, PartialEq)]
pub struct Token2022PreflightRequest {
/// Endpoint role used by all RPC reads.
pub query_role: std::string::String,
/// Optional minimum context slot shared by all reads.
pub min_context_slot: std::option::Option<u64>,
/// Maximum distinct accounts accepted after deduplication.
pub max_accounts: usize,
/// Maximum aggregate requested account bytes.
pub max_total_data_bytes: usize,
/// Ordered account requirements.
pub requirements: std::vec::Vec<Token2022PreflightRequirement>,
/// Optional ElGamal registry read required by the operation.
pub elgamal_registry: std::option::Option<crate::ElGamalRegistryStatefulReadRequest>,
}
/// One validated account result in a Token-2022 preflight report.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct Token2022PreflightAccountReport {
/// Stable semantic role.
pub role: std::string::String,
/// Canonical account address.
pub account: kb_model::Pubkey,
/// State category observed after parsing.
pub state_kind: std::string::String,
/// Context slot returned by the endpoint.
pub context_slot: u64,
/// Ordered extension names observed on the account.
pub extension_names: std::vec::Vec<std::string::String>,
/// Ordered successful semantic checks.
pub checks: std::vec::Vec<std::string::String>,
}
/// Complete bounded Token-2022 stateful preflight report.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct Token2022PreflightReport {
/// Commitment used for all account reads.
pub commitment: std::string::String,
/// Highest context slot observed across all reads.
pub context_slot: u64,
/// Aggregate requested account-data budget.
pub requested_data_bytes: usize,
/// Ordered distinct Token-2022 account reports.
pub accounts: std::vec::Vec<Token2022PreflightAccountReport>,
/// Whether an ElGamal registry was required and validated.
pub elgamal_registry_validated: bool,
}
/// Inspects all bounded Token-2022 state required before simulation.
pub async fn inspect_token_2022_preflight(
pool: &kb_rpc::HttpEndpointPool,
request: &crate::Token2022PreflightRequest,
) -> kb_core::Result<crate::Token2022PreflightReport> {
let requirements = match validate_and_deduplicate_requirements(request) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let requested_data_bytes = requirements
.iter()
.fold(0usize, |total, requirement| {
return total.saturating_add(requirement.max_data_bytes);
})
.saturating_add(if request.elgamal_registry.is_some() {
crate::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES
} else {
0
});
let mut context_slot = request.min_context_slot.unwrap_or(0);
let mut accounts = std::vec::Vec::with_capacity(requirements.len());
for requirement in requirements {
let read_request = crate::Token2022StatefulReadRequest {
query_role: request.query_role.clone(),
account: requirement.account.clone(),
kind: requirement.kind,
min_context_slot: request.min_context_slot,
max_data_bytes: requirement.max_data_bytes,
context: requirement.context.clone(),
};
let read_result = match crate::read_token_2022_stateful_snapshot(pool, &read_request).await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let account_report = match validate_snapshot_requirement(&requirement, &read_result) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
context_slot = context_slot.max(account_report.context_slot);
accounts.push(account_report);
}
let mut elgamal_registry_validated = false;
if let std::option::Option::Some(registry_request) = request.elgamal_registry.as_ref() {
if registry_request.query_role != request.query_role {
return std::result::Result::Err(kb_core::Error::config(
"Token-2022 preflight ElGamal registry query role must match the shared query role",
));
}
let registry_result =
match crate::read_elgamal_registry_stateful_snapshot(pool, registry_request).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
context_slot = context_slot.max(registry_result.context_slot);
elgamal_registry_validated = true;
}
return std::result::Result::Ok(crate::Token2022PreflightReport {
commitment: "confirmed".to_string(),
context_slot,
requested_data_bytes,
accounts,
elgamal_registry_validated,
});
}
fn validate_and_deduplicate_requirements(
request: &crate::Token2022PreflightRequest,
) -> kb_core::Result<std::vec::Vec<crate::Token2022PreflightRequirement>> {
if request.query_role.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"Token-2022 preflight query_role must not be empty",
));
}
if request.max_accounts == 0 || request.max_accounts > crate::MAX_TOKEN_2022_PREFLIGHT_ACCOUNTS
{
return std::result::Result::Err(kb_core::Error::config(format!(
"Token-2022 preflight max_accounts must be between 1 and {}",
crate::MAX_TOKEN_2022_PREFLIGHT_ACCOUNTS
)));
}
if request.max_total_data_bytes == 0
|| request.max_total_data_bytes > crate::MAX_TOKEN_2022_PREFLIGHT_TOTAL_BYTES
{
return std::result::Result::Err(kb_core::Error::config(format!(
"Token-2022 preflight max_total_data_bytes must be between 1 and {}",
crate::MAX_TOKEN_2022_PREFLIGHT_TOTAL_BYTES
)));
}
if request.requirements.is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"Token-2022 preflight requires at least one account",
));
}
let mut indexes = std::collections::BTreeMap::<std::string::String, usize>::new();
let mut unique = std::vec::Vec::<crate::Token2022PreflightRequirement>::new();
for requirement in &request.requirements {
if requirement.role.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"Token-2022 preflight requirement role must not be empty",
));
}
if requirement.max_data_bytes == 0
|| requirement.max_data_bytes > crate::MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES
{
return std::result::Result::Err(kb_core::Error::config(format!(
"Token-2022 preflight account {} has invalid max_data_bytes {}",
requirement.account.0, requirement.max_data_bytes
)));
}
if let std::option::Option::Some(index) = indexes.get(requirement.account.0.as_str()) {
if &unique[*index] != requirement {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_preflight_conflicting_duplicate",
format!(
"Token-2022 preflight account {} has conflicting requirements",
requirement.account.0
),
));
}
continue;
}
indexes.insert(requirement.account.0.clone(), unique.len());
unique.push(requirement.clone());
}
let total_accounts =
unique
.len()
.saturating_add(if request.elgamal_registry.is_some() { 1 } else { 0 });
if total_accounts > request.max_accounts {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_preflight_account_limit_exceeded",
format!(
"Token-2022 preflight requires {total_accounts} distinct accounts above limit {}",
request.max_accounts
),
));
}
let requested_data_bytes = unique
.iter()
.fold(0usize, |total, requirement| {
return total.saturating_add(requirement.max_data_bytes);
})
.saturating_add(if request.elgamal_registry.is_some() {
crate::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES
} else {
0
});
if requested_data_bytes > request.max_total_data_bytes {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_preflight_data_budget_exceeded",
format!(
"Token-2022 preflight requests {requested_data_bytes} bytes above aggregate limit {}",
request.max_total_data_bytes
),
));
}
return std::result::Result::Ok(unique);
}
fn validate_snapshot_requirement(
requirement: &crate::Token2022PreflightRequirement,
result: &crate::Token2022StatefulReadResult,
) -> kb_core::Result<crate::Token2022PreflightAccountReport> {
let token_output = result
.snapshot
.outputs
.iter()
.find(|output| return output.payload_json["domain"] == "spl_token_2022_account_state");
let token_output = match token_output {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::invalid_state(
"Token-2022 preflight snapshot is missing its token account owner projection",
));
},
};
let base_fields = &token_output.payload_json["baseFields"];
let mut checks = std::vec![
"owner_program_id".to_string(),
"complete_account_data".to_string(),
"state_kind".to_string()
];
if let std::option::Option::Some(expected_mint) = requirement.expected_mint.as_ref() {
if base_fields["mint"].as_str() != std::option::Option::Some(expected_mint.0.as_str()) {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_preflight_mint_mismatch",
format!(
"Token-2022 account {} mint does not match expected {}",
requirement.account.0, expected_mint.0
),
));
}
checks.push("mint_identity".to_string());
}
if let std::option::Option::Some(expected_owner) = requirement.expected_owner.as_ref() {
if base_fields["owner"].as_str() != std::option::Option::Some(expected_owner.0.as_str()) {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_preflight_owner_mismatch",
format!(
"Token-2022 account {} owner does not match expected {}",
requirement.account.0, expected_owner.0
),
));
}
checks.push("token_account_owner".to_string());
}
if let std::option::Option::Some(expected_decimals) = requirement.expected_decimals {
if base_fields["decimals"].as_u64() != std::option::Option::Some(expected_decimals as u64) {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_preflight_decimals_mismatch",
format!(
"Token-2022 mint {} decimals do not match expected {expected_decimals}",
requirement.account.0
),
));
}
checks.push("mint_decimals".to_string());
}
for required_extension in &requirement.required_extensions {
if !result
.snapshot
.extension_names
.iter()
.any(|value| return value == required_extension)
{
return std::result::Result::Err(kb_core::Error::new(
"token_2022_preflight_extension_missing",
format!(
"Token-2022 account {} is missing required extension {required_extension}",
requirement.account.0
),
));
}
}
if !requirement.required_extensions.is_empty() {
checks.push("required_extensions".to_string());
}
return std::result::Result::Ok(crate::Token2022PreflightAccountReport {
role: requirement.role.clone(),
account: requirement.account.clone(),
state_kind: result.snapshot.state_kind.clone(),
context_slot: result.context_slot,
extension_names: result.snapshot.extension_names.clone(),
checks,
});
}
#[cfg(test)]
mod tests {
fn pubkey(byte: u8) -> kb_model::Pubkey {
return kb_model::Pubkey(bs58::encode([byte; 32]).into_string());
}
fn requirement(account: kb_model::Pubkey) -> crate::Token2022PreflightRequirement {
return crate::Token2022PreflightRequirement {
role: "source".to_string(),
account,
kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Account,
max_data_bytes: 512,
expected_mint: std::option::Option::None,
expected_owner: std::option::Option::None,
expected_decimals: std::option::Option::None,
required_extensions: std::vec::Vec::new(),
context: crate::Token2022StatefulContext::default(),
};
}
#[test]
fn exact_duplicate_requirements_are_deduplicated_and_bounded() {
let item = requirement(pubkey(1));
let request = crate::Token2022PreflightRequest {
query_role: "query".to_string(),
min_context_slot: std::option::Option::Some(7),
max_accounts: 1,
max_total_data_bytes: 512,
requirements: std::vec![item.clone(), item],
elgamal_registry: std::option::Option::None,
};
let result =
crate::solana_token_2022_preflight::validate_and_deduplicate_requirements(&request);
assert_eq!(result.as_ref().map(std::vec::Vec::len), std::result::Result::Ok(1));
}
#[test]
fn conflicting_duplicates_and_aggregate_budget_fail_closed() {
let first = requirement(pubkey(2));
let mut conflicting = first.clone();
conflicting.role = "destination".to_string();
let conflict_request = crate::Token2022PreflightRequest {
query_role: "query".to_string(),
min_context_slot: std::option::Option::None,
max_accounts: 2,
max_total_data_bytes: 1024,
requirements: std::vec![first, conflicting],
elgamal_registry: std::option::Option::None,
};
assert!(
crate::solana_token_2022_preflight::validate_and_deduplicate_requirements(
&conflict_request
)
.is_err()
);
let budget_request = crate::Token2022PreflightRequest {
query_role: "query".to_string(),
min_context_slot: std::option::Option::None,
max_accounts: 2,
max_total_data_bytes: 700,
requirements: std::vec![requirement(pubkey(3)), requirement(pubkey(4))],
elgamal_registry: std::option::Option::None,
};
assert!(
crate::solana_token_2022_preflight::validate_and_deduplicate_requirements(
&budget_request
)
.is_err()
);
}
#[test]
fn requirement_validation_checks_mint_owner_decimals_and_extensions() {
let account = pubkey(5);
let mint = pubkey(6);
let owner = pubkey(7);
let requirement = crate::Token2022PreflightRequirement {
role: "source".to_string(),
account: account.clone(),
kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Account,
max_data_bytes: 512,
expected_mint: std::option::Option::Some(mint.clone()),
expected_owner: std::option::Option::Some(owner.clone()),
expected_decimals: std::option::Option::None,
required_extensions: std::vec!["memo_transfer".to_string()],
context: crate::Token2022StatefulContext::default(),
};
let result = crate::Token2022StatefulReadResult {
commitment: "confirmed".to_string(),
context_slot: 42,
snapshot: crate::Token2022StatefulSnapshotBundle {
account_key: account.0.clone(),
slot: 42,
state_kind: "account".to_string(),
extension_names: std::vec!["memo_transfer".to_string()],
outputs: std::vec![kb_materializer_api::MaterializedOutput {
output_key: "state".to_string(),
family: kb_model::MaterializedEventFamily::TokenAccount,
payload_json: serde_json::json!({
"domain":"spl_token_2022_account_state",
"baseFields":{"mint":mint.0,"owner":owner.0}
}),
}],
},
};
let report = crate::solana_token_2022_preflight::validate_snapshot_requirement(
&requirement,
&result,
);
assert_eq!(
report.as_ref().map(|value| return value.checks.len()),
std::result::Result::Ok(6)
);
}
}

View File

@@ -0,0 +1,271 @@
// file: kb_pipeline/src/solana_token_2022_proof_orchestration.rs
// version: 2
//! Deterministic orchestration contract for mixed Token-2022 proof locations.
/// Maximum proof references accepted by one confidential Token-2022 operation.
pub const MAX_TOKEN_2022_OPERATION_PROOFS: usize = 5;
/// One bounded proof-orchestration request prepared before transaction assembly.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Token2022ProofOrchestrationRequest {
/// Stable executor operation code.
pub operation_code: std::string::String,
/// Exact ordered proof references required by the operation.
pub proofs: std::vec::Vec<kb_executor_spl_token_2022::SplTokenConfidentialProofReference>,
/// Optional authority expected on every context-state account.
pub expected_context_authority: std::option::Option<kb_model::Pubkey>,
/// Maximum compute-unit limit accepted for the future transaction.
pub compute_unit_limit: u32,
/// Maximum total fee accepted for the future transaction.
pub max_fee_lamports: u64,
/// Whether a future submission was explicitly requested.
pub submit: bool,
/// Whether the operator explicitly confirmed the future submission.
pub operator_confirmed: bool,
}
/// One deterministic mixed-proof orchestration result.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Token2022ProofOrchestrationReport {
/// Stable executor operation code.
pub operation_code: std::string::String,
/// Whether the instructions sysvar must be present.
pub instructions_sysvar_required: bool,
/// Ordered non-zero inline offsets.
pub inline_offsets: std::vec::Vec<i8>,
/// Ordered validated context-state requirements.
pub context_requirements: std::vec::Vec<crate::Token2022ProofContextRequirement>,
/// Simulation is always mandatory.
pub simulation_required: bool,
/// Whether the future send path is authorized by request-local policy.
pub send_authorized: bool,
/// Ordered successful checks.
pub checks: std::vec::Vec<std::string::String>,
}
/// Validates mixed inline/context-state proof orchestration before transaction assembly.
pub fn orchestrate_token_2022_proofs(
request: &crate::Token2022ProofOrchestrationRequest,
cryptographic_preflight: &crate::Token2022CryptographicPreflightReport,
) -> kb_core::Result<crate::Token2022ProofOrchestrationReport> {
if request.operation_code.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"Token-2022 proof orchestration operation_code must not be empty",
));
}
if request.proofs.len() > crate::MAX_TOKEN_2022_OPERATION_PROOFS {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_operation_proof_limit_exceeded",
format!(
"Token-2022 proof orchestration accepts at most {} proofs",
crate::MAX_TOKEN_2022_OPERATION_PROOFS
),
));
}
if request.compute_unit_limit == 0 || request.max_fee_lamports == 0 {
return std::result::Result::Err(kb_core::Error::config(
"Token-2022 proof orchestration requires non-zero compute and fee ceilings",
));
}
if request.submit && !request.operator_confirmed {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_submission_confirmation_required",
"Token-2022 submission requires explicit operator confirmation",
));
}
let mut kinds = std::collections::BTreeSet::<std::string::String>::new();
let mut offsets = std::collections::BTreeSet::<i8>::new();
let mut context_accounts = std::collections::BTreeSet::<std::string::String>::new();
let mut inline_offsets = std::vec::Vec::<i8>::new();
let mut context_requirements = std::vec::Vec::<crate::Token2022ProofContextRequirement>::new();
for proof in &request.proofs {
let kind_code = proof_kind_code(proof.kind).to_string();
if !kinds.insert(kind_code.clone()) {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_duplicate_proof_kind",
format!(
"Token-2022 operation {} contains duplicate proof kind {kind_code}",
request.operation_code
),
));
}
if let std::result::Result::Err(error) = proof.location.validate() {
return std::result::Result::Err(kb_core::Error::invalid_state(error));
}
match &proof.location {
kb_executor_spl_token_2022::SplTokenConfidentialProofLocation::InstructionOffset { offset } => {
if !offsets.insert(*offset) {
return std::result::Result::Err(kb_core::Error::new("token_2022_duplicate_inline_proof_offset", format!("Token-2022 operation {} reuses inline proof offset {offset}", request.operation_code)));
}
inline_offsets.push(*offset);
},
kb_executor_spl_token_2022::SplTokenConfidentialProofLocation::ContextStateAccount { account } => {
if !context_accounts.insert(account.0.clone()) {
return std::result::Result::Err(kb_core::Error::new("token_2022_duplicate_context_state_account", format!("Token-2022 operation {} reuses context-state account {}", request.operation_code, account.0)));
}
context_requirements.push(crate::Token2022ProofContextRequirement { role: kind_code, account: account.clone(), proof_type: proof_kind_to_zk_type(proof.kind), expected_authority: request.expected_context_authority.clone() });
},
}
}
if let std::result::Result::Err(error) =
validate_context_reports(context_requirements.as_slice(), cryptographic_preflight)
{
return std::result::Result::Err(error);
}
return std::result::Result::Ok(crate::Token2022ProofOrchestrationReport {
operation_code: request.operation_code.clone(),
instructions_sysvar_required: !inline_offsets.is_empty(),
inline_offsets,
context_requirements,
simulation_required: true,
send_authorized: request.submit && request.operator_confirmed,
checks: vec![
"ordered_unique_proof_kinds".to_string(),
"non_zero_unique_inline_offsets".to_string(),
"unique_context_state_accounts".to_string(),
"context_preflight_matches_operation".to_string(),
"simulation_required".to_string(),
"bounded_compute_and_fee".to_string(),
],
});
}
fn validate_context_reports(
requirements: &[crate::Token2022ProofContextRequirement],
report: &crate::Token2022CryptographicPreflightReport,
) -> kb_core::Result<()> {
if requirements.len() != report.proof_contexts.len() {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_context_preflight_count_mismatch",
format!(
"Token-2022 operation requires {} context states, preflight contains {}",
requirements.len(),
report.proof_contexts.len()
),
));
}
for (requirement, observed) in requirements.iter().zip(report.proof_contexts.iter()) {
if requirement.account != observed.account
|| requirement.proof_type.discriminator() != observed.proof_discriminator
{
return std::result::Result::Err(kb_core::Error::new(
"token_2022_context_preflight_mismatch",
format!(
"Token-2022 proof context {} does not match the ordered operation requirement",
observed.account.0
),
));
}
}
return std::result::Result::Ok(());
}
fn proof_kind_code(
kind: kb_executor_spl_token_2022::SplTokenConfidentialProofKind,
) -> &'static str {
return match kind {
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::PubkeyValidity => "pubkey_validity",
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::ZeroCiphertext => "zero_ciphertext",
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::CiphertextCommitmentEquality => "ciphertext_commitment_equality",
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::CiphertextCiphertextEquality => "ciphertext_ciphertext_equality",
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity => "batched_grouped_ciphertext_3_handles_validity",
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedGroupedCiphertext2HandlesValidity => "batched_grouped_ciphertext_2_handles_validity",
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::PercentageWithFee => "percentage_with_fee",
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedRangeProofU64 => "batched_range_proof_u64",
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedRangeProofU128 => "batched_range_proof_u128",
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedRangeProofU256 => "batched_range_proof_u256",
};
}
fn proof_kind_to_zk_type(
kind: kb_executor_spl_token_2022::SplTokenConfidentialProofKind,
) -> kb_executor_solana_core::ZkElGamalProofType {
return match kind {
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::PubkeyValidity => kb_executor_solana_core::ZkElGamalProofType::PubkeyValidity,
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::ZeroCiphertext => kb_executor_solana_core::ZkElGamalProofType::ZeroCiphertext,
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::CiphertextCommitmentEquality => kb_executor_solana_core::ZkElGamalProofType::CiphertextCommitmentEquality,
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::CiphertextCiphertextEquality => kb_executor_solana_core::ZkElGamalProofType::CiphertextCiphertextEquality,
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity => kb_executor_solana_core::ZkElGamalProofType::BatchedGroupedCiphertext3HandlesValidity,
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedGroupedCiphertext2HandlesValidity => kb_executor_solana_core::ZkElGamalProofType::BatchedGroupedCiphertext2HandlesValidity,
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::PercentageWithFee => kb_executor_solana_core::ZkElGamalProofType::PercentageWithCap,
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedRangeProofU64 => kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU64,
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedRangeProofU128 => kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU128,
kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedRangeProofU256 => kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU256,
};
}
#[cfg(test)]
mod tests {
fn pubkey(byte: u8) -> kb_model::Pubkey {
return kb_model::Pubkey(bs58::encode([byte; 32]).into_string());
}
fn context_report(
account: kb_model::Pubkey,
proof_type: kb_executor_solana_core::ZkElGamalProofType,
) -> crate::Token2022ProofContextReport {
return crate::Token2022ProofContextReport {
role: "proof".to_string(),
account,
proof_discriminator: proof_type.discriminator(),
context_state_bytes: proof_type.context_state_size(),
context_slot: 100,
checks: vec!["validated".to_string()],
};
}
#[test]
fn mixed_proofs_require_sysvar_and_preserve_ordered_contexts() {
let context = pubkey(1);
let request = crate::Token2022ProofOrchestrationRequest { operation_code: "spl_token_2022.confidential_transfer".to_string(), proofs: vec![kb_executor_spl_token_2022::SplTokenConfidentialProofReference { kind: kb_executor_spl_token_2022::SplTokenConfidentialProofKind::CiphertextCommitmentEquality, location: kb_executor_spl_token_2022::SplTokenConfidentialProofLocation::InstructionOffset { offset: 1 } }, kb_executor_spl_token_2022::SplTokenConfidentialProofReference { kind: kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedRangeProofU128, location: kb_executor_spl_token_2022::SplTokenConfidentialProofLocation::ContextStateAccount { account: context.clone() } }], expected_context_authority: std::option::Option::None, compute_unit_limit: 400_000, max_fee_lamports: 50_000, submit: false, operator_confirmed: false };
let preflight = crate::Token2022CryptographicPreflightReport {
commitment: "confirmed".to_string(),
context_slot: 100,
proof_contexts: vec![context_report(
context,
kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU128,
)],
};
let report = crate::orchestrate_token_2022_proofs(&request, &preflight)
.expect("mixed proof orchestration must succeed");
assert!(report.instructions_sysvar_required);
assert_eq!(report.inline_offsets, vec![1]);
assert_eq!(report.context_requirements.len(), 1);
assert!(report.simulation_required);
assert!(!report.send_authorized);
}
#[test]
fn context_only_proofs_do_not_require_instructions_sysvar() {
let context = pubkey(2);
let request = crate::Token2022ProofOrchestrationRequest { operation_code: "spl_token_2022.empty_confidential_account".to_string(), proofs: vec![kb_executor_spl_token_2022::SplTokenConfidentialProofReference { kind: kb_executor_spl_token_2022::SplTokenConfidentialProofKind::ZeroCiphertext, location: kb_executor_spl_token_2022::SplTokenConfidentialProofLocation::ContextStateAccount { account: context.clone() } }], expected_context_authority: std::option::Option::None, compute_unit_limit: 200_000, max_fee_lamports: 20_000, submit: true, operator_confirmed: true };
let preflight = crate::Token2022CryptographicPreflightReport {
commitment: "confirmed".to_string(),
context_slot: 100,
proof_contexts: vec![context_report(
context,
kb_executor_solana_core::ZkElGamalProofType::ZeroCiphertext,
)],
};
let report = crate::orchestrate_token_2022_proofs(&request, &preflight)
.expect("context-only orchestration must succeed");
assert!(!report.instructions_sysvar_required);
assert!(report.send_authorized);
}
#[test]
fn duplicate_offsets_contexts_and_unconfirmed_submission_fail_closed() {
let duplicate_offset = crate::Token2022ProofOrchestrationRequest { operation_code: "operation".to_string(), proofs: vec![kb_executor_spl_token_2022::SplTokenConfidentialProofReference { kind: kb_executor_spl_token_2022::SplTokenConfidentialProofKind::ZeroCiphertext, location: kb_executor_spl_token_2022::SplTokenConfidentialProofLocation::InstructionOffset { offset: 1 } }, kb_executor_spl_token_2022::SplTokenConfidentialProofReference { kind: kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedRangeProofU64, location: kb_executor_spl_token_2022::SplTokenConfidentialProofLocation::InstructionOffset { offset: 1 } }], expected_context_authority: std::option::Option::None, compute_unit_limit: 1, max_fee_lamports: 1, submit: false, operator_confirmed: false };
let empty = crate::Token2022CryptographicPreflightReport {
commitment: "confirmed".to_string(),
context_slot: 0,
proof_contexts: vec![],
};
assert!(crate::orchestrate_token_2022_proofs(&duplicate_offset, &empty).is_err());
let mut unconfirmed = duplicate_offset;
unconfirmed.proofs.clear();
unconfirmed.submit = true;
assert!(crate::orchestrate_token_2022_proofs(&unconfirmed, &empty).is_err());
}
}

View File

@@ -0,0 +1,803 @@
// file: kb_pipeline/src/solana_token_2022_stateful.rs
// version: 5
//! Contextual Token-2022 account-state validation and materialization routing.
/// Optional external identities required to validate cross-account Token-2022 state.
#[derive(Clone, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct Token2022StatefulContext {
/// Expected group account for one TokenGroupMember extension.
pub expected_group_address: std::option::Option<std::string::String>,
}
/// Maximum complete Token-2022 account data accepted by one bounded RPC read.
pub const MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES: usize = 65_536;
/// One bounded Token-2022 account read request.
#[derive(Clone, Debug, PartialEq)]
pub struct Token2022StatefulReadRequest {
/// Endpoint role used for the HTTP RPC request.
pub query_role: std::string::String,
/// Canonical account address.
pub account: kb_model::Pubkey,
/// Expected Token-2022 base-state category.
pub kind: kb_decoder_spl_token_2022::state::Token2022StateKind,
/// 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,
/// Optional external identities needed by cross-account extensions.
pub context: crate::solana_token_2022_stateful::Token2022StatefulContext,
}
/// One bounded RPC read and its contextually validated projections.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct Token2022StatefulReadResult {
/// Commitment used for the RPC read.
pub commitment: std::string::String,
/// Context slot returned by the endpoint.
pub context_slot: u64,
/// Complete validated snapshot bundle.
pub snapshot: crate::solana_token_2022_stateful::Token2022StatefulSnapshotBundle,
}
/// Reads, validates, parses, and routes one bounded Token-2022 account snapshot.
pub async fn read_token_2022_stateful_snapshot(
pool: &kb_rpc::HttpEndpointPool,
request: &crate::solana_token_2022_stateful::Token2022StatefulReadRequest,
) -> kb_core::Result<crate::solana_token_2022_stateful::Token2022StatefulReadResult> {
if request.query_role.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"Token-2022 stateful read query_role must not be empty",
));
}
if request.max_data_bytes == 0
|| request.max_data_bytes
> crate::solana_token_2022_stateful::MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES
{
return std::result::Result::Err(kb_core::Error::config(format!(
"Token-2022 stateful read max_data_bytes must be between 1 and {}",
crate::solana_token_2022_stateful::MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES
)));
}
let config = match kb_rpc::GetAccountInfoConfig::new_with_data(
kb_rpc::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::solana_token_2022_stateful::materialize_token_2022_account_info_result(
request, &result,
);
}
/// Validates one complete RPC account response before Token-2022 parsing and routing.
pub fn materialize_token_2022_account_info_result(
request: &crate::solana_token_2022_stateful::Token2022StatefulReadRequest,
result: &kb_rpc::AccountInfoResult,
) -> kb_core::Result<crate::solana_token_2022_stateful::Token2022StatefulReadResult> {
if request.max_data_bytes == 0
|| request.max_data_bytes
> crate::solana_token_2022_stateful::MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES
{
return std::result::Result::Err(kb_core::Error::config(format!(
"Token-2022 stateful read max_data_bytes must be between 1 and {}",
crate::solana_token_2022_stateful::MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES
)));
}
if let std::option::Option::Some(min_context_slot) = request.min_context_slot {
if result.context.slot < min_context_slot {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_stateful_context_slot_too_old",
format!(
"Token-2022 account context slot {} is below requested minimum {min_context_slot}",
result.context.slot
),
));
}
}
let account = match result.account.as_ref() {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_stateful_account_missing",
format!("Token-2022 account {} does not exist", request.account.0),
));
},
};
if account.executable {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_stateful_account_executable",
format!("Token-2022 state account {} must not be executable", request.account.0),
));
}
if account.owner.0 != kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_stateful_owner_mismatch",
format!(
"Token-2022 state account {} owner must be {}, got {}",
request.account.0,
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
account.owner.0
),
));
}
if account.space > request.max_data_bytes as u64 || account.data.len() > request.max_data_bytes
{
return std::result::Result::Err(kb_core::Error::new(
"token_2022_stateful_account_too_large",
format!(
"Token-2022 account {} reports {} bytes and returned {} bytes above limit {}",
request.account.0,
account.space,
account.data.len(),
request.max_data_bytes
),
));
}
if account.space != account.data.len() as u64 {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_stateful_account_data_incomplete",
format!(
"Token-2022 account {} reports {} bytes but returned {} decoded bytes",
request.account.0,
account.space,
account.data.len()
),
));
}
let state = match kb_decoder_spl_token_2022::state::parse_token_2022_state(
request.kind,
account.data.as_slice(),
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_stateful_parse_failed",
error,
));
},
};
let snapshot = match crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot_with_context(
request.account.0.as_str(),
result.context.slot,
&state,
&request.context,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_stateful_projection_failed",
error,
));
},
};
return std::result::Result::Ok(
crate::solana_token_2022_stateful::Token2022StatefulReadResult {
commitment: "confirmed".to_string(),
context_slot: result.context.slot,
snapshot,
},
);
}
/// One contextually validated Token-2022 state snapshot and its owned projections.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct Token2022StatefulSnapshotBundle {
/// Canonical account identity used for contextual checks and stable output keys.
pub account_key: std::string::String,
/// Context slot associated with the account read.
pub slot: u64,
/// Parsed base-state category.
pub state_kind: std::string::String,
/// Ordered published or future extension names retained by the parser.
pub extension_names: std::vec::Vec<std::string::String>,
/// Processor-owned projections routed without duplicate ownership.
pub outputs: std::vec::Vec<kb_materializer_api::MaterializedOutput>,
}
/// Parse, contextually validate, and materialize one bounded Token-2022 account snapshot.
pub fn materialize_token_2022_stateful_snapshot(
account_key: &str,
owner_program_id: &str,
slot: u64,
kind: kb_decoder_spl_token_2022::state::Token2022StateKind,
data: &[u8],
) -> std::result::Result<crate::solana_token_2022_stateful::Token2022StatefulSnapshotBundle, String>
{
if owner_program_id != kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID {
return std::result::Result::Err(format!(
"Token-2022 state snapshot owner must be {}, got {owner_program_id}",
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
));
}
let state = match kb_decoder_spl_token_2022::state::parse_token_2022_state(kind, data) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot_with_context(
account_key,
slot,
&state,
&crate::solana_token_2022_stateful::Token2022StatefulContext::default(),
);
}
/// Contextually validate and materialize one already parsed Token-2022 account snapshot.
pub fn materialize_parsed_token_2022_stateful_snapshot(
account_key: &str,
slot: u64,
state: &kb_decoder_spl_token_2022::state::Token2022State,
) -> std::result::Result<crate::solana_token_2022_stateful::Token2022StatefulSnapshotBundle, String>
{
return crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot_with_context(
account_key,
slot,
state,
&crate::solana_token_2022_stateful::Token2022StatefulContext::default(),
);
}
/// Contextually validate one parsed Token-2022 snapshot with external cross-account identities.
pub fn materialize_parsed_token_2022_stateful_snapshot_with_context(
account_key: &str,
slot: u64,
state: &kb_decoder_spl_token_2022::state::Token2022State,
context: &crate::solana_token_2022_stateful::Token2022StatefulContext,
) -> std::result::Result<crate::solana_token_2022_stateful::Token2022StatefulSnapshotBundle, String>
{
if account_key.trim().is_empty() {
return std::result::Result::Err(
"Token-2022 stateful snapshot requires a non-empty account key".to_string(),
);
}
let decoded = match bs58::decode(account_key).into_vec() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return std::result::Result::Err(
"Token-2022 stateful snapshot account key must be valid base58".to_string(),
);
},
};
if decoded.len() != 32 {
return std::result::Result::Err(format!(
"Token-2022 stateful snapshot account key must decode to 32 bytes, got {}",
decoded.len()
));
}
match crate::solana_token_2022_stateful::validate_embedded_mint_identity(account_key, state) {
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
match crate::solana_token_2022_stateful::validate_group_member_identity(state, context) {
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
let account_output = match kb_materializer_token_accounts::materialize_token_2022_state_snapshot(
account_key,
slot,
state,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let mut outputs = std::vec![account_output];
if state.kind == kb_decoder_spl_token_2022::state::Token2022StateKind::Mint {
let metadata_outputs =
match kb_materializer_metadata::materialize_token_2022_metadata_snapshot(
account_key,
slot,
state,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
outputs.extend(metadata_outputs);
}
let fee_outputs = match kb_materializer_fees::materialize_token_2022_fee_state_snapshots(
account_key,
slot,
state,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
outputs.extend(fee_outputs);
let admin_outputs = match kb_materializer_admin::materialize_token_2022_admin_state_snapshots(
account_key,
slot,
state,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
outputs.extend(admin_outputs);
let state_kind = match state.kind {
kb_decoder_spl_token_2022::state::Token2022StateKind::Mint => "mint",
kb_decoder_spl_token_2022::state::Token2022StateKind::Account => "account",
kb_decoder_spl_token_2022::state::Token2022StateKind::Multisig => "multisig",
};
return std::result::Result::Ok(
crate::solana_token_2022_stateful::Token2022StatefulSnapshotBundle {
account_key: account_key.to_string(),
slot,
state_kind: state_kind.to_string(),
extension_names: state
.extensions
.iter()
.map(|entry| return entry.extension_name.to_string())
.collect(),
outputs,
},
);
}
fn validate_embedded_mint_identity(
account_key: &str,
state: &kb_decoder_spl_token_2022::state::Token2022State,
) -> std::result::Result<(), String> {
for entry in &state.extensions {
if entry.extension_name != "token_metadata"
&& entry.extension_name != "token_group"
&& entry.extension_name != "token_group_member"
{
continue;
}
let embedded_mint = entry.value_fields.get("mint").and_then(serde_json::Value::as_str);
let embedded_mint = match embedded_mint {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(format!(
"Token-2022 {} extension requires a structured mint field",
entry.extension_name
));
},
};
if embedded_mint != account_key {
return std::result::Result::Err(format!(
"Token-2022 {} mint {} does not match account {account_key}",
entry.extension_name, embedded_mint
));
}
}
return std::result::Result::Ok(());
}
fn validate_group_member_identity(
state: &kb_decoder_spl_token_2022::state::Token2022State,
context: &crate::solana_token_2022_stateful::Token2022StatefulContext,
) -> std::result::Result<(), String> {
for entry in &state.extensions {
if entry.extension_name != "token_group_member" {
continue;
}
let group = entry.value_fields.get("group").and_then(serde_json::Value::as_str);
let group = match group {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(
"Token-2022 token_group_member extension requires a structured group field"
.to_string(),
);
},
};
let expected = match context.expected_group_address.as_deref() {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(
"Token-2022 token_group_member validation requires an expected group address"
.to_string(),
);
},
};
if group != expected {
return std::result::Result::Err(format!(
"Token-2022 token_group_member group {group} does not match expected group {expected}"
));
}
}
return std::result::Result::Ok(());
}
#[cfg(test)]
mod tests {
fn account_key(byte: u8) -> String {
return bs58::encode([byte; 32]).into_string();
}
fn mint_state(account_key: &str) -> kb_decoder_spl_token_2022::state::Token2022State {
return kb_decoder_spl_token_2022::state::Token2022State {
kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint,
base_fields: serde_json::json!({"supply":"1","decimals":0,"initialized":true}),
base_hex: "00".repeat(82),
account_type: std::option::Option::Some(1),
extensions: std::vec![
kb_decoder_spl_token_2022::state::Token2022TlvEntry {
extension_type: 19,
extension_name: "token_metadata",
value_hex: "01".to_string(),
value_fields: serde_json::json!({
"mint": account_key,
"name": "Token",
"symbol": "TOK",
"uri": "https://example.invalid/token.json"
}),
},
kb_decoder_spl_token_2022::state::Token2022TlvEntry {
extension_type: 20,
extension_name: "token_group",
value_hex: "02".to_string(),
value_fields: serde_json::json!({
"mint": account_key,
"size": "1",
"maxSize": "10"
}),
},
],
};
}
#[test]
fn mint_snapshot_routes_account_and_metadata_outputs_once() {
let account_key = account_key(7);
let state = mint_state(account_key.as_str());
let bundle =
crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot(
account_key.as_str(),
42,
&state,
);
assert_eq!(
bundle.as_ref().map(|value| return value.outputs.len()),
std::result::Result::Ok(3)
);
assert_eq!(
bundle.as_ref().map(|value| return value.state_kind.clone()),
std::result::Result::Ok("mint".to_string())
);
assert_eq!(
bundle.as_ref().map(|value| return value.extension_names.clone()),
std::result::Result::Ok(std::vec![
"token_metadata".to_string(),
"token_group".to_string()
])
);
}
#[test]
fn embedded_metadata_mint_must_match_the_account_identity() {
let account_address = account_key(8);
let mut state = mint_state(account_address.as_str());
state.extensions[0].value_fields["mint"] = serde_json::json!(account_key(9));
let result =
crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot(
account_address.as_str(),
42,
&state,
);
assert!(result.is_err());
}
#[test]
fn owner_and_account_identity_fail_closed_before_projection() {
let account_key = account_key(10);
let data = [0u8; 82];
let wrong_owner =
crate::solana_token_2022_stateful::materialize_token_2022_stateful_snapshot(
account_key.as_str(),
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
1,
kb_decoder_spl_token_2022::state::Token2022StateKind::Mint,
&data,
);
assert!(wrong_owner.is_err());
let state = mint_state(account_key.as_str());
let malformed_key =
crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot(
"not-base58-0",
1,
&state,
);
assert!(malformed_key.is_err());
}
#[test]
fn account_snapshot_has_no_metadata_projection() {
let account_key = account_key(11);
let state = kb_decoder_spl_token_2022::state::Token2022State {
kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Account,
base_fields: serde_json::json!({"amount":"0","state":"initialized"}),
base_hex: "00".repeat(165),
account_type: std::option::Option::None,
extensions: std::vec::Vec::new(),
};
let bundle =
crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot(
account_key.as_str(),
9,
&state,
);
assert_eq!(
bundle.as_ref().map(|value| return value.outputs.len()),
std::result::Result::Ok(1)
);
}
#[test]
fn group_member_requires_and_matches_external_group_identity() {
let account_address = account_key(12);
let group_address = account_key(13);
let state = kb_decoder_spl_token_2022::state::Token2022State {
kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint,
base_fields: serde_json::json!({"supply":"1","decimals":0,"initialized":true}),
base_hex: "00".repeat(82),
account_type: std::option::Option::Some(1),
extensions: std::vec![kb_decoder_spl_token_2022::state::Token2022TlvEntry {
extension_type: 23,
extension_name: "token_group_member",
value_hex: "03".to_string(),
value_fields: serde_json::json!({
"mint": account_address,
"group": group_address,
"memberNumber": "1"
}),
}],
};
let missing =
crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot(
account_address.as_str(),
44,
&state,
);
assert!(missing.is_err());
let wrong_context = crate::solana_token_2022_stateful::Token2022StatefulContext {
expected_group_address: std::option::Option::Some(account_key(14)),
};
let wrong = crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot_with_context(
account_address.as_str(),
44,
&state,
&wrong_context,
);
assert!(wrong.is_err());
let context = crate::solana_token_2022_stateful::Token2022StatefulContext {
expected_group_address: std::option::Option::Some(group_address),
};
let valid = crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot_with_context(
account_address.as_str(),
44,
&state,
&context,
);
assert_eq!(
valid.as_ref().map(|bundle| return bundle.outputs.len()),
std::result::Result::Ok(2)
);
}
#[test]
fn mint_and_account_fee_extensions_route_to_the_fee_owner_once() {
let mint_address = account_key(15);
let mint = kb_decoder_spl_token_2022::state::Token2022State {
kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint,
base_fields: serde_json::json!({"supply":"1","decimals":0,"initialized":true}),
base_hex: "00".repeat(82),
account_type: std::option::Option::Some(1),
extensions: std::vec![kb_decoder_spl_token_2022::state::Token2022TlvEntry {
extension_type: 1,
extension_name: "transfer_fee_config",
value_hex: "01".to_string(),
value_fields: serde_json::json!({"withheldAmount":"7"}),
}],
};
let mint_bundle =
crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot(
mint_address.as_str(),
50,
&mint,
);
assert_eq!(
mint_bundle.as_ref().map(|bundle| return bundle.outputs.len()),
std::result::Result::Ok(2)
);
assert_eq!(
mint_bundle
.as_ref()
.map(|bundle| return bundle.outputs[1].payload_json["provenance"]["processorName"]
.clone()),
std::result::Result::Ok(serde_json::json!("fees"))
);
let account_address = account_key(16);
let account = kb_decoder_spl_token_2022::state::Token2022State {
kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Account,
base_fields: serde_json::json!({"amount":"0","state":"initialized"}),
base_hex: "00".repeat(165),
account_type: std::option::Option::Some(2),
extensions: std::vec![kb_decoder_spl_token_2022::state::Token2022TlvEntry {
extension_type: 17,
extension_name: "confidential_transfer_fee_amount",
value_hex: "02".to_string(),
value_fields: serde_json::json!({"withheldAmount":"ciphertext"}),
}],
};
let account_bundle =
crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot(
account_address.as_str(),
51,
&account,
);
assert_eq!(
account_bundle.as_ref().map(|bundle| return bundle.outputs.len()),
std::result::Result::Ok(2)
);
assert_eq!(
account_bundle
.as_ref()
.map(|bundle| return bundle.outputs[1].payload_json["confidentialValuesDecrypted"]
.clone()),
std::result::Result::Ok(serde_json::json!(false))
);
}
#[test]
fn mint_admin_extensions_route_to_the_admin_owner_once() {
let account_key = account_key(12);
let mut state = mint_state(account_key.as_str());
state.extensions.push(kb_decoder_spl_token_2022::state::Token2022TlvEntry {
extension_type: 6,
extension_name: "default_account_state",
value_hex: "02".to_string(),
value_fields: serde_json::json!({"state": 2}),
});
let bundle =
crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot(
account_key.as_str(),
17,
&state,
);
assert_eq!(
bundle.as_ref().map(|value| return value.outputs.len()),
std::result::Result::Ok(4)
);
assert_eq!(
bundle.as_ref().map(|value| {
return value
.outputs
.iter()
.filter(|output| {
return output.payload_json["domain"]
== serde_json::json!("token_2022_extension_admin_state");
})
.count();
}),
std::result::Result::Ok(1)
);
}
#[test]
fn complete_rpc_account_routes_only_after_owner_size_and_context_validation() {
let account = crate::solana_token_2022_stateful::tests::account_key(21);
let request = crate::solana_token_2022_stateful::Token2022StatefulReadRequest {
query_role: "execution".to_string(),
account: kb_model::Pubkey(account.clone()),
kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint,
min_context_slot: std::option::Option::Some(40),
max_data_bytes: 82,
context: crate::solana_token_2022_stateful::Token2022StatefulContext::default(),
};
let result = kb_rpc::AccountInfoResult {
context: kb_rpc::RpcResponseContext {
slot: 41,
api_version: std::option::Option::None,
},
account: std::option::Option::Some(kb_rpc::AccountInfoValue {
lamports: 1,
owner: kb_model::ProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string()),
executable: false,
rent_epoch: 0,
space: 82,
data: std::vec![0; 82],
}),
};
let materialized =
crate::solana_token_2022_stateful::materialize_token_2022_account_info_result(
&request, &result,
);
assert_eq!(
materialized.as_ref().map(|value| return value.context_slot),
std::result::Result::Ok(41)
);
assert_eq!(
materialized.as_ref().map(|value| return value.snapshot.outputs.len()),
std::result::Result::Ok(1)
);
}
#[test]
fn incomplete_foreign_executable_and_stale_rpc_accounts_fail_closed() {
let account = crate::solana_token_2022_stateful::tests::account_key(22);
let request = crate::solana_token_2022_stateful::Token2022StatefulReadRequest {
query_role: "execution".to_string(),
account: kb_model::Pubkey(account),
kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint,
min_context_slot: std::option::Option::Some(50),
max_data_bytes: 82,
context: crate::solana_token_2022_stateful::Token2022StatefulContext::default(),
};
let base = kb_rpc::AccountInfoValue {
lamports: 1,
owner: kb_model::ProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string()),
executable: false,
rent_epoch: 0,
space: 82,
data: std::vec![0; 82],
};
let stale = kb_rpc::AccountInfoResult {
context: kb_rpc::RpcResponseContext {
slot: 49,
api_version: std::option::Option::None,
},
account: std::option::Option::Some(base.clone()),
};
assert!(
crate::solana_token_2022_stateful::materialize_token_2022_account_info_result(
&request, &stale
)
.is_err()
);
let mut foreign = base.clone();
foreign.owner = kb_model::ProgramId(kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string());
let foreign = kb_rpc::AccountInfoResult {
context: kb_rpc::RpcResponseContext {
slot: 50,
api_version: std::option::Option::None,
},
account: std::option::Option::Some(foreign),
};
assert!(
crate::solana_token_2022_stateful::materialize_token_2022_account_info_result(
&request, &foreign
)
.is_err()
);
let mut executable = base.clone();
executable.executable = true;
let executable = kb_rpc::AccountInfoResult {
context: kb_rpc::RpcResponseContext {
slot: 50,
api_version: std::option::Option::None,
},
account: std::option::Option::Some(executable),
};
assert!(
crate::solana_token_2022_stateful::materialize_token_2022_account_info_result(
&request,
&executable
)
.is_err()
);
let mut incomplete = base;
incomplete.data.pop();
let incomplete = kb_rpc::AccountInfoResult {
context: kb_rpc::RpcResponseContext {
slot: 50,
api_version: std::option::Option::None,
},
account: std::option::Option::Some(incomplete),
};
assert!(
crate::solana_token_2022_stateful::materialize_token_2022_account_info_result(
&request,
&incomplete
)
.is_err()
);
}
}

View File

@@ -0,0 +1,440 @@
// file: kb_pipeline/src/solana_token_2022_validation.rs
// version: 2
//! Machine-readable validation evidence contract for the Token-2022 milestone.
/// Maximum number of evidence records accepted in one validation report.
pub const MAX_TOKEN_2022_VALIDATION_EVIDENCE: usize = 64;
/// Required validation environment for one scenario.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Token2022ValidationEnvironment {
/// Deterministic offline fixtures and builder comparisons.
Offline,
/// A local validator under operator control.
Localnet,
/// Solana Devnet.
Devnet,
/// Scenario may run on Localnet or Devnet according to deployment availability.
LocalnetOrDevnet,
/// Mainnet observations without mutable execution.
MainnetObservation,
/// PostgreSQL persistence and replay validation.
Postgres,
/// Tauri application smoke validation.
Tauri,
}
/// Exact status of one validation scenario.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Token2022ValidationStatus {
/// Scenario is declared but has not been run.
NotRun,
/// Scenario was simulated without submission.
Simulated,
/// Scenario was submitted but confirmation evidence is incomplete.
Submitted,
/// Scenario was confirmed and its required postconditions passed.
Confirmed,
/// Scenario is not available in the selected environment.
Unavailable,
/// Scenario ran and failed.
Failed,
}
/// One bounded proof attached to a validation scenario.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct Token2022ValidationEvidence {
/// Stable evidence kind such as `signature`, `test_suite`, or `replay`.
pub kind: std::string::String,
/// Bounded evidence value.
pub value: std::string::String,
}
/// One declared Token-2022 validation scenario and its observed evidence.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct Token2022ValidationScenario {
/// Stable scenario identifier.
pub id: std::string::String,
/// Validation environment.
pub environment: crate::Token2022ValidationEnvironment,
/// Exact observed status.
pub status: crate::Token2022ValidationStatus,
/// Whether canonical hydration is required.
pub requires_canonical_hydration: bool,
/// Whether core extraction is required.
pub requires_core_extraction: bool,
/// Whether decode replay is required.
pub requires_decode_replay: bool,
/// Whether materialization is required.
pub requires_materialization: bool,
/// Whether a second idempotent replay is required.
pub requires_second_replay: bool,
/// Ordered evidence records.
pub evidence: std::vec::Vec<crate::Token2022ValidationEvidence>,
}
/// Machine-readable validation matrix loaded from the canonical JSON document.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Token2022ValidationMatrix {
/// Matrix schema version.
pub matrix_version: u32,
/// Milestone owning this matrix.
pub milestone: std::string::String,
/// Aggregate matrix status.
pub status: std::string::String,
/// Exact accepted status vocabulary.
pub status_vocabulary: std::vec::Vec<std::string::String>,
/// Scenarios in stable roadmap order.
pub scenarios: std::vec::Vec<crate::Token2022ValidationMatrixScenario>,
}
/// One scenario declared by the canonical validation matrix.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Token2022ValidationMatrixScenario {
/// Stable scenario identifier.
pub id: std::string::String,
/// Required execution environment.
pub environment: crate::Token2022ValidationEnvironment,
/// Exact observed status.
pub status: crate::Token2022ValidationStatus,
/// Evidence kinds required before this scenario may be confirmed.
pub required_evidence: std::vec::Vec<std::string::String>,
/// Observed bounded evidence.
#[serde(default)]
pub evidence: std::vec::Vec<crate::Token2022ValidationEvidence>,
}
/// Loads and validates the canonical Token-2022 validation matrix.
pub fn load_token_2022_validation_matrix() -> kb_core::Result<crate::Token2022ValidationMatrix> {
let parsed = match serde_json::from_str::<crate::Token2022ValidationMatrix>(include_str!(
"../../docs/SPL_TOKEN_2022_VALIDATION_MATRIX.json"
)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_validation_matrix_invalid_json",
format!("Token-2022 validation matrix JSON is invalid: {error}"),
));
},
};
if let std::result::Result::Err(error) = validate_token_2022_validation_matrix(&parsed) {
return std::result::Result::Err(error);
}
return std::result::Result::Ok(parsed);
}
/// Validates schema, scenario inventory, statuses, and observed evidence.
pub fn validate_token_2022_validation_matrix(
matrix: &crate::Token2022ValidationMatrix,
) -> kb_core::Result<()> {
if matrix.matrix_version != 2 || matrix.milestone != "0.4.6" {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_validation_matrix_contract_mismatch",
"Token-2022 validation matrix must use version 2 for milestone 0.4.6",
));
}
let expected_statuses =
["not_run", "simulated", "submitted", "confirmed", "unavailable", "failed"];
let actual_statuses = matrix
.status_vocabulary
.iter()
.map(|status| return status.as_str())
.collect::<std::vec::Vec<&str>>();
if actual_statuses.as_slice() != expected_statuses {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_validation_matrix_status_vocabulary_mismatch",
"Token-2022 validation status vocabulary differs from the compiled contract",
));
}
let expected_ids = [
"offline_full_regression",
"token_2022_public_devnet",
"elgamal_registry_devnet",
"confidential_transfer_localnet_or_devnet",
"confidential_mint_burn_localnet_or_devnet",
"permissioned_confidential_burn_localnet_or_devnet",
"mainnet_observation_corpus",
"postgres_double_replay",
"tauri_smoke",
];
let actual_ids = matrix
.scenarios
.iter()
.map(|scenario| return scenario.id.as_str())
.collect::<std::vec::Vec<&str>>();
if actual_ids.as_slice() != expected_ids {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_validation_matrix_scenario_inventory_mismatch",
"Token-2022 validation scenario inventory or order differs from the compiled contract",
));
}
for scenario in &matrix.scenarios {
if scenario.required_evidence.is_empty()
|| scenario.required_evidence.len() > crate::MAX_TOKEN_2022_VALIDATION_EVIDENCE
{
return std::result::Result::Err(kb_core::Error::new(
"token_2022_validation_matrix_required_evidence_invalid",
"Every Token-2022 validation scenario must declare bounded required evidence",
));
}
let mut required = std::collections::BTreeSet::<&str>::new();
for evidence_kind in &scenario.required_evidence {
if evidence_kind.trim().is_empty() || !required.insert(evidence_kind.as_str()) {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_validation_matrix_required_evidence_invalid",
"Required Token-2022 validation evidence must be non-empty and unique",
));
}
}
if scenario.status == crate::Token2022ValidationStatus::Confirmed {
let observed = scenario
.evidence
.iter()
.map(|evidence| return evidence.kind.as_str())
.collect::<std::collections::BTreeSet<&str>>();
if !required.iter().all(|kind| return observed.contains(kind)) {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_validation_matrix_confirmed_without_required_evidence",
format!(
"Confirmed Token-2022 validation scenario {} lacks required evidence",
scenario.id
),
));
}
} else if matches!(
scenario.status,
crate::Token2022ValidationStatus::NotRun
| crate::Token2022ValidationStatus::Unavailable
) && !scenario.evidence.is_empty()
{
return std::result::Result::Err(kb_core::Error::new(
"token_2022_validation_matrix_unobserved_with_evidence",
"Not-run or unavailable Token-2022 scenarios must not retain observed evidence",
));
}
}
return std::result::Result::Ok(());
}
/// Complete validation report checked before milestone closure.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct Token2022ValidationReport {
/// Scenarios in stable roadmap order.
pub scenarios: std::vec::Vec<crate::Token2022ValidationScenario>,
}
/// Validates that a milestone report is bounded, unique, and does not overclaim evidence.
pub fn validate_token_2022_validation_report(
report: &crate::Token2022ValidationReport,
) -> kb_core::Result<()> {
if report.scenarios.is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"Token-2022 validation report must contain at least one scenario",
));
}
let mut ids = std::collections::BTreeSet::<std::string::String>::new();
for scenario in &report.scenarios {
if scenario.id.trim().is_empty() || !ids.insert(scenario.id.clone()) {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_validation_scenario_identity_invalid",
"Token-2022 validation scenario ids must be non-empty and unique",
));
}
if scenario.evidence.len() > crate::MAX_TOKEN_2022_VALIDATION_EVIDENCE {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_validation_evidence_limit_exceeded",
format!(
"Token-2022 validation accepts at most {} evidence records per scenario",
crate::MAX_TOKEN_2022_VALIDATION_EVIDENCE
),
));
}
for evidence in &scenario.evidence {
if evidence.kind.trim().is_empty() || evidence.value.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_validation_evidence_invalid",
"Token-2022 validation evidence kind and value must be non-empty",
));
}
}
let completed = matches!(
scenario.status,
crate::Token2022ValidationStatus::Simulated
| crate::Token2022ValidationStatus::Submitted
| crate::Token2022ValidationStatus::Confirmed
| crate::Token2022ValidationStatus::Failed
);
if completed && scenario.evidence.is_empty() {
return std::result::Result::Err(kb_core::Error::new(
"token_2022_validation_completed_without_evidence",
"A completed Token-2022 validation scenario must retain evidence",
));
}
if scenario.status == crate::Token2022ValidationStatus::Confirmed
&& (scenario.requires_canonical_hydration
|| scenario.requires_core_extraction
|| scenario.requires_decode_replay
|| scenario.requires_materialization
|| scenario.requires_second_replay)
&& !has_pipeline_evidence(scenario)
{
return std::result::Result::Err(kb_core::Error::new(
"token_2022_validation_confirmed_without_pipeline_evidence",
"A confirmed end-to-end Token-2022 scenario must retain hydration, extraction, replay, materialization, and idempotence evidence",
));
}
}
return std::result::Result::Ok(());
}
fn has_pipeline_evidence(scenario: &crate::Token2022ValidationScenario) -> bool {
let kinds = scenario
.evidence
.iter()
.map(|evidence| return evidence.kind.as_str())
.collect::<std::collections::BTreeSet<&str>>();
if scenario.requires_canonical_hydration && !kinds.contains("canonical_hydration") {
return false;
}
if scenario.requires_core_extraction && !kinds.contains("core_extraction") {
return false;
}
if scenario.requires_decode_replay && !kinds.contains("decode_replay") {
return false;
}
if scenario.requires_materialization && !kinds.contains("materialization") {
return false;
}
if scenario.requires_second_replay && !kinds.contains("second_replay") {
return false;
}
return true;
}
#[cfg(test)]
mod tests {
fn scenario(status: crate::Token2022ValidationStatus) -> crate::Token2022ValidationScenario {
return crate::Token2022ValidationScenario {
id: "confidential_transfer_devnet".to_string(),
environment: crate::Token2022ValidationEnvironment::Devnet,
status,
requires_canonical_hydration: true,
requires_core_extraction: true,
requires_decode_replay: true,
requires_materialization: true,
requires_second_replay: true,
evidence: vec![
crate::Token2022ValidationEvidence {
kind: "signature".to_string(),
value: "signature".to_string(),
},
crate::Token2022ValidationEvidence {
kind: "canonical_hydration".to_string(),
value: "1".to_string(),
},
crate::Token2022ValidationEvidence {
kind: "core_extraction".to_string(),
value: "1".to_string(),
},
crate::Token2022ValidationEvidence {
kind: "decode_replay".to_string(),
value: "1".to_string(),
},
crate::Token2022ValidationEvidence {
kind: "materialization".to_string(),
value: "1".to_string(),
},
crate::Token2022ValidationEvidence {
kind: "second_replay".to_string(),
value: "0_new_outputs".to_string(),
},
],
};
}
#[test]
fn confirmed_end_to_end_scenario_requires_complete_pipeline_evidence() {
let report = crate::Token2022ValidationReport {
scenarios: vec![scenario(crate::Token2022ValidationStatus::Confirmed)],
};
assert!(crate::validate_token_2022_validation_report(&report).is_ok());
let mut incomplete = scenario(crate::Token2022ValidationStatus::Confirmed);
incomplete.evidence.retain(|evidence| return evidence.kind != "second_replay");
assert!(
crate::validate_token_2022_validation_report(&crate::Token2022ValidationReport {
scenarios: vec![incomplete]
})
.is_err()
);
}
#[test]
fn not_run_and_unavailable_scenarios_do_not_invent_evidence() {
let mut not_run = scenario(crate::Token2022ValidationStatus::NotRun);
not_run.evidence.clear();
let mut unavailable = scenario(crate::Token2022ValidationStatus::Unavailable);
unavailable.id = "permissioned_burn_devnet".to_string();
unavailable.evidence.clear();
assert!(
crate::validate_token_2022_validation_report(&crate::Token2022ValidationReport {
scenarios: vec![not_run, unavailable]
})
.is_ok()
);
}
#[test]
fn duplicate_ids_empty_evidence_and_completed_without_evidence_fail_closed() {
let first = scenario(crate::Token2022ValidationStatus::Confirmed);
let duplicate = first.clone();
assert!(
crate::validate_token_2022_validation_report(&crate::Token2022ValidationReport {
scenarios: vec![first, duplicate]
})
.is_err()
);
let mut failed = scenario(crate::Token2022ValidationStatus::Failed);
failed.evidence.clear();
assert!(
crate::validate_token_2022_validation_report(&crate::Token2022ValidationReport {
scenarios: vec![failed]
})
.is_err()
);
}
#[test]
fn canonical_matrix_matches_compiled_inventory_and_observed_offline_evidence() {
let matrix = crate::load_token_2022_validation_matrix();
assert!(matrix.is_ok());
let matrix = match matrix {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("unexpected matrix error: {error}"),
};
assert_eq!(matrix.scenarios.len(), 9);
assert_eq!(matrix.scenarios[0].status, crate::Token2022ValidationStatus::Confirmed);
assert_eq!(matrix.scenarios[0].evidence.len(), 4);
assert!(
matrix.scenarios[1..]
.iter()
.all(|scenario| return scenario.status == crate::Token2022ValidationStatus::NotRun)
);
}
#[test]
fn confirmed_matrix_scenario_requires_every_declared_evidence_kind() {
let matrix = crate::load_token_2022_validation_matrix();
assert!(matrix.is_ok());
let mut matrix = match matrix {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("unexpected matrix error: {error}"),
};
matrix.scenarios[0].evidence.retain(|evidence| return evidence.kind != "clippy");
assert!(crate::validate_token_2022_validation_matrix(&matrix).is_err());
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff