v0.1.0-pre.034

This commit is contained in:
2026-07-25 12:02:05 +02:00
parent 9ad47de757
commit 9100fece5b
4 changed files with 965 additions and 0 deletions

View File

@@ -13,8 +13,11 @@ mod core_extraction;
mod decode_replay;
mod plan;
mod solana_elgamal_registry_stateful;
mod solana_token2022_correlation;
mod solana_token2022_crypto_preflight;
mod solana_token2022_devnet_scenarios;
mod solana_token2022_preflight;
mod solana_token2022_proof_orchestration;
mod solana_token2022_stateful;
mod solana_token2022_validation;
@@ -100,6 +103,26 @@ pub use self::solana_elgamal_registry_stateful::materialize_elgamal_registry_acc
pub use self::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot;
/// Migrated read_elgamal_registry_stateful_snapshot contract.
pub use self::solana_elgamal_registry_stateful::read_elgamal_registry_stateful_snapshot;
/// Correlation outcome between one committed instruction fact and one final state snapshot.
pub use self::solana_token2022_correlation::Token2022CorrelationStatus;
/// Deterministic correlation report for one instruction output and one final snapshot.
pub use self::solana_token2022_correlation::Token2022StateCorrelationReport;
/// Correlates one materialized instruction fact with one authoritative Token-2022 snapshot.
pub use self::solana_token2022_correlation::correlate_token2022_instruction_with_snapshot;
/// Maximum number of distinct proof context-state accounts accepted by one preflight.
pub use self::solana_token2022_crypto_preflight::MAX_TOKEN2022_PROOF_CONTEXTS;
/// Token-2022 cryptographic preflight report.
pub use self::solana_token2022_crypto_preflight::Token2022CryptographicPreflightReport;
/// Token-2022 cryptographic preflight request.
pub use self::solana_token2022_crypto_preflight::Token2022CryptographicPreflightRequest;
/// One validated proof context report.
pub use self::solana_token2022_crypto_preflight::Token2022ProofContextReport;
/// One required proof context-state account.
pub use self::solana_token2022_crypto_preflight::Token2022ProofContextRequirement;
/// Encoded proof context metadata bytes.
pub use self::solana_token2022_crypto_preflight::ZK_PROOF_CONTEXT_META_BYTES;
/// Inspects bounded Token-2022 cryptographic proof contexts.
pub use self::solana_token2022_crypto_preflight::inspect_token2022_cryptographic_preflight;
/// Stable category of one independent Devnet validation scenario.
pub use self::solana_token2022_devnet_scenarios::DevnetSplValidationFamily;
/// Current implementation status of one Devnet validation scenario.
@@ -122,6 +145,14 @@ pub use self::solana_token2022_preflight::Token2022PreflightRequest;
pub use self::solana_token2022_preflight::Token2022PreflightRequirement;
/// Migrated inspect_token2022_preflight contract.
pub use self::solana_token2022_preflight::inspect_token2022_preflight;
/// Maximum number of proofs accepted for one Token-2022 operation.
pub use self::solana_token2022_proof_orchestration::MAX_TOKEN2022_OPERATION_PROOFS;
/// Token-2022 proof orchestration report.
pub use self::solana_token2022_proof_orchestration::Token2022ProofOrchestrationReport;
/// Token-2022 proof orchestration request.
pub use self::solana_token2022_proof_orchestration::Token2022ProofOrchestrationRequest;
/// Validates mixed inline and context-state proof orchestration.
pub use self::solana_token2022_proof_orchestration::orchestrate_token2022_proofs;
/// Migrated MAX_TOKEN2022_STATEFUL_ACCOUNT_BYTES contract.
pub use self::solana_token2022_stateful::MAX_TOKEN2022_STATEFUL_ACCOUNT_BYTES;
/// Migrated Token2022StatefulContext contract.

View File

@@ -0,0 +1,242 @@
// file: kb-pipeline/src/solana_token2022_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_lib::MdPubkey,
/// 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_token2022_instruction_with_snapshot(
correlation_key: &str,
account: &kb_lib::MdPubkey,
instruction_output: &kb_lib::MtApiMaterializedOutput,
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(
"token2022_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(
"token2022_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_lib::MtApiMaterializedOutput {
let mut payload = serde_json::Map::new();
payload.insert(field.to_string(), serde_json::Value::String(value.to_string()));
return kb_lib::MtApiMaterializedOutput {
output_key: "fact:0".to_string(),
family: kb_lib::MdMaterializedEventFamily::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_lib::MdPubkey(snapshot.account_key.clone());
let confirmed = super::correlate_token2022_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_token2022_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_lib::MdPubkey(snapshot.account_key.clone());
let report = super::correlate_token2022_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_lib::MdPubkey(snapshot.account_key.clone());
let empty = super::correlate_token2022_instruction_with_snapshot(
"",
&account,
&output("riskKind", "required_transfer_memos_enabled"),
&snapshot,
);
assert!(empty.is_err());
let wrong = kb_lib::MdPubkey(solana_pubkey::Pubkey::new_from_array([8u8; 32]).to_string());
let mismatch = super::correlate_token2022_instruction_with_snapshot(
"corr:mismatch",
&wrong,
&output("riskKind", "required_transfer_memos_enabled"),
&snapshot,
);
assert!(mismatch.is_err());
}
}

View File

@@ -0,0 +1,319 @@
// file: kb-pipeline/src/solana_token2022_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_TOKEN2022_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_lib::MdPubkey,
/// Exact proof kind expected by the Token-2022 builder.
pub proof_type: kb_lib::ExSolanaCoreZkElGamalProofType,
/// Optional authority retained by the context-state account.
pub expected_authority: std::option::Option<kb_lib::MdPubkey>,
}
/// 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_lib::MdPubkey,
/// 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_token2022_cryptographic_preflight(
pool: &kb_onchain_transport::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_onchain_transport::GetAccountInfoConfig::new_with_data(
kb_onchain_transport::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_TOKEN2022_PROOF_CONTEXTS {
return std::result::Result::Err(kb_core::Error::new(
"token2022_proof_context_limit_exceeded",
format!(
"Token-2022 cryptographic preflight accepts at most {} proof contexts",
crate::MAX_TOKEN2022_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(
"token2022_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_onchain_transport::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(
"token2022_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(
"token2022_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(
"token2022_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(
"token2022_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(
"token2022_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(
"token2022_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_lib::MdPubkey {
return kb_lib::MdPubkey(bs58::encode([byte; 32]).into_string());
}
fn requirement(
account: kb_lib::MdPubkey,
proof_type: kb_lib::ExSolanaCoreZkElGamalProofType,
) -> 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_lib::ExSolanaCoreZkElGamalProofType,
) -> kb_onchain_transport::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_onchain_transport::AccountInfoResult {
context: kb_onchain_transport::RpcResponseContext {
slot: 77,
api_version: std::option::Option::None,
},
account: std::option::Option::Some(kb_onchain_transport::AccountInfoValue {
lamports: 1,
owner: kb_lib::MdProgramId(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_lib::ExSolanaCoreZkElGamalProofType::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 = super::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_lib::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU128,
),
],
};
assert!(super::validate_proof_context_requirements(&conflict).is_err());
}
#[test]
fn proof_context_validation_checks_owner_size_type_and_authority() {
let proof_type = kb_lib::ExSolanaCoreZkElGamalProofType::CiphertextCiphertextEquality;
let context_requirement = requirement(pubkey(1), proof_type);
let result = account_result(proof_type);
let report = super::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_lib::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU128);
assert!(super::validate_proof_context_account(&wrong_type, &result,).is_err());
}
}

View File

@@ -0,0 +1,373 @@
// file: kb-pipeline/src/solana_token2022_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_TOKEN2022_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_lib::ExSplTokenConfidentialProofReference>,
/// Optional authority expected on every context-state account.
pub expected_context_authority: std::option::Option<kb_lib::MdPubkey>,
/// 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_token2022_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_TOKEN2022_OPERATION_PROOFS {
return std::result::Result::Err(kb_core::Error::new(
"token2022_operation_proof_limit_exceeded",
format!(
"Token-2022 proof orchestration accepts at most {} proofs",
crate::MAX_TOKEN2022_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(
"token2022_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(
"token2022_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_lib::ExSplTokenConfidentialProofLocation::InstructionOffset { offset } => {
if !offsets.insert(*offset) {
return std::result::Result::Err(kb_core::Error::new(
"token2022_duplicate_inline_proof_offset",
format!(
"Token-2022 operation {} reuses inline proof offset {offset}",
request.operation_code
),
));
}
inline_offsets.push(*offset);
},
kb_lib::ExSplTokenConfidentialProofLocation::ContextStateAccount { account } => {
if !context_accounts.insert(account.0.clone()) {
return std::result::Result::Err(kb_core::Error::new(
"token2022_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(
"token2022_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(
"token2022_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_lib::ExSplTokenConfidentialProofKind) -> &'static str {
return match kind {
kb_lib::ExSplTokenConfidentialProofKind::PubkeyValidity => "pubkey_validity",
kb_lib::ExSplTokenConfidentialProofKind::ZeroCiphertext => "zero_ciphertext",
kb_lib::ExSplTokenConfidentialProofKind::CiphertextCommitmentEquality => {
"ciphertext_commitment_equality"
},
kb_lib::ExSplTokenConfidentialProofKind::CiphertextCiphertextEquality => {
"ciphertext_ciphertext_equality"
},
kb_lib::ExSplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity => {
"batched_grouped_ciphertext_3_handles_validity"
},
kb_lib::ExSplTokenConfidentialProofKind::BatchedGroupedCiphertext2HandlesValidity => {
"batched_grouped_ciphertext_2_handles_validity"
},
kb_lib::ExSplTokenConfidentialProofKind::PercentageWithFee => "percentage_with_fee",
kb_lib::ExSplTokenConfidentialProofKind::BatchedRangeProofU64 => "batched_range_proof_u64",
kb_lib::ExSplTokenConfidentialProofKind::BatchedRangeProofU128 => {
"batched_range_proof_u128"
},
kb_lib::ExSplTokenConfidentialProofKind::BatchedRangeProofU256 => {
"batched_range_proof_u256"
},
};
}
fn proof_kind_to_zk_type(
kind: kb_lib::ExSplTokenConfidentialProofKind,
) -> kb_lib::ExSolanaCoreZkElGamalProofType {
return match kind {
kb_lib::ExSplTokenConfidentialProofKind::PubkeyValidity => {
kb_lib::ExSolanaCoreZkElGamalProofType::PubkeyValidity
},
kb_lib::ExSplTokenConfidentialProofKind::ZeroCiphertext => {
kb_lib::ExSolanaCoreZkElGamalProofType::ZeroCiphertext
},
kb_lib::ExSplTokenConfidentialProofKind::CiphertextCommitmentEquality => {
kb_lib::ExSolanaCoreZkElGamalProofType::CiphertextCommitmentEquality
},
kb_lib::ExSplTokenConfidentialProofKind::CiphertextCiphertextEquality => {
kb_lib::ExSolanaCoreZkElGamalProofType::CiphertextCiphertextEquality
},
kb_lib::ExSplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity => {
kb_lib::ExSolanaCoreZkElGamalProofType::BatchedGroupedCiphertext3HandlesValidity
},
kb_lib::ExSplTokenConfidentialProofKind::BatchedGroupedCiphertext2HandlesValidity => {
kb_lib::ExSolanaCoreZkElGamalProofType::BatchedGroupedCiphertext2HandlesValidity
},
kb_lib::ExSplTokenConfidentialProofKind::PercentageWithFee => {
kb_lib::ExSolanaCoreZkElGamalProofType::PercentageWithCap
},
kb_lib::ExSplTokenConfidentialProofKind::BatchedRangeProofU64 => {
kb_lib::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU64
},
kb_lib::ExSplTokenConfidentialProofKind::BatchedRangeProofU128 => {
kb_lib::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU128
},
kb_lib::ExSplTokenConfidentialProofKind::BatchedRangeProofU256 => {
kb_lib::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU256
},
};
}
#[cfg(test)]
mod tests {
fn pubkey(byte: u8) -> kb_lib::MdPubkey {
return kb_lib::MdPubkey(bs58::encode([byte; 32]).into_string());
}
fn context_report(
account: kb_lib::MdPubkey,
proof_type: kb_lib::ExSolanaCoreZkElGamalProofType,
) -> 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_token2022.confidential_transfer".to_string(),
proofs: vec![
kb_lib::ExSplTokenConfidentialProofReference {
kind: kb_lib::ExSplTokenConfidentialProofKind::CiphertextCommitmentEquality,
location: kb_lib::ExSplTokenConfidentialProofLocation::InstructionOffset {
offset: 1,
},
},
kb_lib::ExSplTokenConfidentialProofReference {
kind: kb_lib::ExSplTokenConfidentialProofKind::BatchedRangeProofU128,
location: kb_lib::ExSplTokenConfidentialProofLocation::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_lib::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU128,
)],
};
let report = crate::orchestrate_token2022_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_token2022.empty_confidential_account".to_string(),
proofs: vec![kb_lib::ExSplTokenConfidentialProofReference {
kind: kb_lib::ExSplTokenConfidentialProofKind::ZeroCiphertext,
location: kb_lib::ExSplTokenConfidentialProofLocation::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_lib::ExSolanaCoreZkElGamalProofType::ZeroCiphertext,
)],
};
let report = crate::orchestrate_token2022_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_lib::ExSplTokenConfidentialProofReference {
kind: kb_lib::ExSplTokenConfidentialProofKind::ZeroCiphertext,
location: kb_lib::ExSplTokenConfidentialProofLocation::InstructionOffset {
offset: 1,
},
},
kb_lib::ExSplTokenConfidentialProofReference {
kind: kb_lib::ExSplTokenConfidentialProofKind::BatchedRangeProofU64,
location: kb_lib::ExSplTokenConfidentialProofLocation::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_token2022_proofs(&duplicate_offset, &empty).is_err());
let mut unconfirmed = duplicate_offset;
unconfirmed.proofs.clear();
unconfirmed.submit = true;
assert!(crate::orchestrate_token2022_proofs(&unconfirmed, &empty).is_err());
}
}