Files
khadhroony-bot3/kb-pipeline/src/solana_stateful.rs
2026-07-25 12:18:56 +02:00

1624 lines
62 KiB
Rust

// file: kb-pipeline/src/solana_stateful.rs
// version: 2
//! Stateful Localnet and Devnet readiness checks for native Solana execution plans.
use std::str::FromStr; // rust-rules: trait-import
const COMPLETE_ACCOUNT_DATA_LIMIT: usize = 65_536;
const ALT_META_SIZE: usize = 56;
const ALT_MAX_ADDRESSES: usize = 256;
const FEATURE_ACCOUNT_SIZE: u64 = 9;
const SLASHING_REPORT_HEADER_SIZE: usize = 114;
const MAX_SHRED_SIZE: usize = 1_232;
const ZK_CONTEXT_META_SIZE: usize = 33;
/// Stateful readiness outcome for one native Solana operation.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum SolanaCoreStatefulReadinessStatus {
/// The operation has no stateful preflight contract in this layer.
NotRequired,
/// Every stateful preflight check passed.
Ready,
/// At least one stateful preflight check failed.
Blocked,
}
/// One machine-readable stateful readiness check.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct SolanaCoreStatefulCheck {
/// Stable diagnostic code.
pub code: std::string::String,
/// Whether the check passed.
pub passed: bool,
/// Operator-readable explanation.
pub message: std::string::String,
}
/// One contextual fact measured during stateful readiness inspection.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct SolanaCoreStatefulFact {
/// Stable fact key.
pub key: std::string::String,
/// String representation of the measured value.
pub value: std::string::String,
}
/// Complete request for one native Solana stateful readiness inspection.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct SolanaCoreStatefulReadinessRequest {
/// HTTP endpoint role used for all state reads.
pub query_role: std::string::String,
/// Expected Localnet or Devnet cluster.
pub cluster: kb_lib::ExApiExecutionCluster,
/// Native operation whose external state must be inspected.
pub operation: kb_lib::ExSolanaCoreOperation,
}
/// Complete stateful readiness report produced before simulation.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct SolanaCoreStatefulReadinessReport {
/// Expected cluster.
pub cluster: kb_lib::ExApiExecutionCluster,
/// Stable native operation code.
pub operation_code: std::string::String,
/// Aggregate stateful readiness status.
pub status: crate::SolanaCoreStatefulReadinessStatus,
/// Highest contextual slot observed while reading accounts.
pub context_slot: std::option::Option<u64>,
/// Ordered stateful checks.
pub checks: std::vec::Vec<crate::SolanaCoreStatefulCheck>,
/// Ordered measured facts.
pub facts: std::vec::Vec<crate::SolanaCoreStatefulFact>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct AddressLookupTableState {
deactivation_slot: u64,
authority: std::option::Option<std::string::String>,
address_count: usize,
}
/// Inspects Localnet or Devnet state required before simulating one native operation.
pub async fn inspect_solana_core_stateful_readiness(
pool: &kb_onchain_transport::HttpEndpointPool,
request: &crate::SolanaCoreStatefulReadinessRequest,
) -> kb_core::Result<crate::SolanaCoreStatefulReadinessReport> {
if request.query_role.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::config(
"stateful execution readiness query_role must not be empty",
));
}
match request.cluster {
kb_lib::ExApiExecutionCluster::Localnet | kb_lib::ExApiExecutionCluster::Devnet => {},
kb_lib::ExApiExecutionCluster::Testnet | kb_lib::ExApiExecutionCluster::Mainnet => {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_cluster_unsupported",
"stateful native execution readiness is restricted to Localnet and Devnet",
));
},
}
let genesis = match 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),
};
match validate_cluster(request.cluster, &genesis) {
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
let mut report = new_report(request);
push_fact(&mut report, "genesis_hash", genesis.genesis_hash.as_str());
match &request.operation {
kb_lib::ExSolanaCoreOperation::AddressLookupTableCreate {
authority,
payer,
recent_slot,
} => {
match inspect_alt_create(pool, request, &mut report, authority, payer, *recent_slot)
.await
{
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => {
return std::result::Result::Err(error);
},
}
},
kb_lib::ExSolanaCoreOperation::AddressLookupTableExtend {
lookup_table,
authority,
payer,
new_addresses,
} => {
match inspect_alt_existing(
pool,
request,
&mut report,
lookup_table,
authority,
AltAction::Extend {
payer_present: payer.is_some(),
added_addresses: new_addresses.len(),
},
)
.await
{
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => {
return std::result::Result::Err(error);
},
}
},
kb_lib::ExSolanaCoreOperation::AddressLookupTableFreeze { lookup_table, authority } => {
match inspect_alt_existing(
pool,
request,
&mut report,
lookup_table,
authority,
AltAction::Freeze,
)
.await
{
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => {
return std::result::Result::Err(error);
},
}
},
kb_lib::ExSolanaCoreOperation::AddressLookupTableDeactivate { lookup_table, authority } => {
match inspect_alt_existing(
pool,
request,
&mut report,
lookup_table,
authority,
AltAction::Deactivate,
)
.await
{
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => {
return std::result::Result::Err(error);
},
}
},
kb_lib::ExSolanaCoreOperation::AddressLookupTableClose {
lookup_table, authority, ..
} => {
match inspect_alt_existing(
pool,
request,
&mut report,
lookup_table,
authority,
AltAction::Close,
)
.await
{
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => {
return std::result::Result::Err(error);
},
}
},
kb_lib::ExSolanaCoreOperation::ConfigCreateAccount {
config_account,
lamports,
max_config_data_space,
max_keys,
..
} => {
match inspect_config_create(
pool,
request,
&mut report,
config_account,
*lamports,
*max_config_data_space,
max_keys.len(),
)
.await
{
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => {
return std::result::Result::Err(error);
},
}
},
kb_lib::ExSolanaCoreOperation::ConfigStore { config_account, keys, data, .. } => {
match inspect_config_store(
pool,
request,
&mut report,
config_account,
keys.len(),
data.len(),
)
.await
{
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => {
return std::result::Result::Err(error);
},
}
},
kb_lib::ExSolanaCoreOperation::FeatureActivate { feature_account, lamports, .. } => {
match inspect_feature_activate(pool, request, &mut report, feature_account, *lamports)
.await
{
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => {
return std::result::Result::Err(error);
},
}
},
kb_lib::ExSolanaCoreOperation::FeatureRevokePendingActivation { feature_account } => {
match inspect_feature_revoke(pool, request, &mut report, feature_account).await {
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => {
return std::result::Result::Err(error);
},
}
},
kb_lib::ExSolanaCoreOperation::SlashingSubmitDuplicateBlockProof {
proof_account,
report_account,
report_lamports,
proof_offset,
slot,
..
} => {
match inspect_slashing_submit(
pool,
request,
&mut report,
proof_account,
report_account,
*report_lamports,
*proof_offset,
*slot,
)
.await
{
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => {
return std::result::Result::Err(error);
},
}
},
kb_lib::ExSolanaCoreOperation::SlashingCloseViolationReport {
report_account,
destination,
} => {
match inspect_slashing_close(pool, request, &mut report, report_account, destination)
.await
{
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => {
return std::result::Result::Err(error);
},
}
},
kb_lib::ExSolanaCoreOperation::ZkElGamalVerifyInline {
proof_type, context_state, ..
} => {
match inspect_zk_verify(
pool,
request,
&mut report,
*proof_type,
std::option::Option::None,
context_state.as_ref(),
)
.await
{
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => {
return std::result::Result::Err(error);
},
}
},
kb_lib::ExSolanaCoreOperation::ZkElGamalVerifyFromAccount {
proof_type,
proof_account,
proof_offset,
context_state,
} => {
match inspect_zk_verify(
pool,
request,
&mut report,
*proof_type,
std::option::Option::Some((proof_account, *proof_offset)),
context_state.as_ref(),
)
.await
{
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => {
return std::result::Result::Err(error);
},
}
},
kb_lib::ExSolanaCoreOperation::ZkElGamalCloseContextState {
context_state,
authority,
..
} => match inspect_zk_close(pool, request, &mut report, context_state, authority).await {
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => {
return std::result::Result::Err(error);
},
},
_ => {
report.status = crate::SolanaCoreStatefulReadinessStatus::NotRequired;
push_fact(&mut report, "stateful_policy", "not_required_for_operation");
return std::result::Result::Ok(report);
},
}
finalize_report(&mut report);
return std::result::Result::Ok(report);
}
fn new_report(
request: &crate::SolanaCoreStatefulReadinessRequest,
) -> crate::SolanaCoreStatefulReadinessReport {
return crate::SolanaCoreStatefulReadinessReport {
cluster: request.cluster,
operation_code: request.operation.operation_code().to_string(),
status: crate::SolanaCoreStatefulReadinessStatus::Ready,
context_slot: std::option::Option::None,
checks: std::vec::Vec::new(),
facts: std::vec::Vec::new(),
};
}
fn validate_cluster(
expected: kb_lib::ExApiExecutionCluster,
genesis: &kb_onchain_transport::GenesisHashResult,
) -> kb_core::Result<()> {
return match expected {
kb_lib::ExApiExecutionCluster::Devnet => {
if genesis.classified_cluster
== std::option::Option::Some(kb_lib::ExApiExecutionCluster::Devnet)
{
std::result::Result::Ok(())
} else {
std::result::Result::Err(kb_core::Error::new(
"execution_stateful_devnet_genesis_mismatch",
"stateful Devnet readiness requires the official Devnet genesis hash",
))
}
},
kb_lib::ExApiExecutionCluster::Localnet => {
if genesis.classified_cluster.is_none() {
std::result::Result::Ok(())
} else {
std::result::Result::Err(kb_core::Error::new(
"execution_stateful_localnet_public_cluster",
"Localnet readiness refuses endpoints classified as an official public cluster",
))
}
},
kb_lib::ExApiExecutionCluster::Testnet | kb_lib::ExApiExecutionCluster::Mainnet => {
std::result::Result::Err(kb_core::Error::new(
"execution_stateful_cluster_unsupported",
"stateful native execution readiness is restricted to Localnet and Devnet",
))
},
};
}
fn push_check(
report: &mut crate::SolanaCoreStatefulReadinessReport,
code: &str,
passed: bool,
message: impl std::convert::Into<std::string::String>,
) {
report.checks.push(crate::SolanaCoreStatefulCheck {
code: code.to_string(),
passed,
message: message.into(),
});
}
fn push_fact(
report: &mut crate::SolanaCoreStatefulReadinessReport,
key: &str,
value: impl std::fmt::Display,
) {
report.facts.push(crate::SolanaCoreStatefulFact {
key: key.to_string(),
value: value.to_string(),
});
}
fn update_context_slot(report: &mut crate::SolanaCoreStatefulReadinessReport, slot: u64) {
let current = match report.context_slot {
std::option::Option::Some(value) => value.max(slot),
std::option::Option::None => slot,
};
report.context_slot = std::option::Option::Some(current);
}
fn finalize_report(report: &mut crate::SolanaCoreStatefulReadinessReport) {
report.status = if report.checks.iter().all(|check| return check.passed) {
crate::SolanaCoreStatefulReadinessStatus::Ready
} else {
crate::SolanaCoreStatefulReadinessStatus::Blocked
};
}
async fn account_with_data(
pool: &kb_onchain_transport::HttpEndpointPool,
request: &crate::SolanaCoreStatefulReadinessRequest,
pubkey: &kb_lib::MdPubkey,
) -> kb_core::Result<kb_onchain_transport::AccountInfoResult> {
let config = match kb_onchain_transport::GetAccountInfoConfig::confirmed_with_data(
COMPLETE_ACCOUNT_DATA_LIMIT,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return pool
.get_account_info_for_role(request.query_role.as_str(), pubkey, &config)
.await;
}
async fn rent_minimum(
pool: &kb_onchain_transport::HttpEndpointPool,
request: &crate::SolanaCoreStatefulReadinessRequest,
data_length: u64,
) -> kb_core::Result<u64> {
let result = pool
.get_minimum_balance_for_rent_exemption_for_role(
request.query_role.as_str(),
data_length,
&kb_onchain_transport::GetMinimumBalanceForRentExemptionConfig::confirmed(),
)
.await;
return match result {
std::result::Result::Ok(value) => std::result::Result::Ok(value.minimum_balance_lamports),
std::result::Result::Err(error) => std::result::Result::Err(error),
};
}
async fn epoch_info(
pool: &kb_onchain_transport::HttpEndpointPool,
request: &crate::SolanaCoreStatefulReadinessRequest,
) -> kb_core::Result<kb_onchain_transport::EpochInfoResult> {
return pool
.get_epoch_info_for_role(
request.query_role.as_str(),
&kb_onchain_transport::GetEpochInfoConfig::confirmed(),
)
.await;
}
async fn inspect_alt_create(
pool: &kb_onchain_transport::HttpEndpointPool,
request: &crate::SolanaCoreStatefulReadinessRequest,
report: &mut crate::SolanaCoreStatefulReadinessReport,
authority: &kb_lib::MdPubkey,
payer: &kb_lib::MdPubkey,
recent_slot: u64,
) -> kb_core::Result<()> {
let authority_address = match solana_pubkey::Pubkey::from_str(authority.0.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::config(format!(
"invalid ALT authority: {error}"
)));
},
};
let payer_address = match solana_pubkey::Pubkey::from_str(payer.0.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::config(format!(
"invalid ALT payer: {error}"
)));
},
};
let (_, table_address) =
solana_address_lookup_table_interface::instruction::create_lookup_table(
authority_address,
payer_address,
recent_slot,
);
let table = kb_lib::MdPubkey(table_address.to_string());
let account_result = match account_with_data(pool, request, &table).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
update_context_slot(report, account_result.context.slot);
push_fact(report, "lookup_table", table.0.as_str());
push_check(
report,
"alt_create_target_absent",
account_result.account.is_none(),
"derived lookup table account must not already exist",
);
let epoch = match epoch_info(pool, request).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
push_fact(report, "absolute_slot", epoch.absolute_slot);
let last_valid =
solana_address_lookup_table_interface::state::estimate_last_valid_slot(recent_slot);
push_fact(report, "recent_slot_last_valid", last_valid);
push_check(
report,
"alt_create_recent_slot_not_future",
recent_slot <= epoch.absolute_slot,
"ALT recent slot must not be ahead of the current confirmed slot",
);
push_check(
report,
"alt_create_recent_slot_still_recent",
epoch.absolute_slot <= last_valid,
"ALT recent slot must remain in the runtime slot-hashes window",
);
let rent = match rent_minimum(pool, request, ALT_META_SIZE as u64).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
push_fact(report, "minimum_rent_lamports", rent);
return std::result::Result::Ok(());
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum AltAction {
Extend {
payer_present: bool,
added_addresses: usize,
},
Freeze,
Deactivate,
Close,
}
async fn inspect_alt_existing(
pool: &kb_onchain_transport::HttpEndpointPool,
request: &crate::SolanaCoreStatefulReadinessRequest,
report: &mut crate::SolanaCoreStatefulReadinessReport,
lookup_table: &kb_lib::MdPubkey,
authority: &kb_lib::MdPubkey,
action: AltAction,
) -> kb_core::Result<()> {
let account_result = match account_with_data(pool, request, lookup_table).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
update_context_slot(report, account_result.context.slot);
let account = match account_result.account {
std::option::Option::Some(value) => value,
std::option::Option::None => {
push_check(report, "alt_account_exists", false, "lookup table account does not exist");
return std::result::Result::Ok(());
},
};
push_check(
report,
"alt_owner_matches",
account.owner.0 == kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID,
"lookup table account must be owned by the Address Lookup Table program",
);
push_check(
report,
"alt_not_executable",
!account.executable,
"lookup table state account must not be executable",
);
let state = match parse_alt_state(account.data.as_slice()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
push_check(report, "alt_state_decodes", false, error.to_string());
return std::result::Result::Ok(());
},
};
push_fact(report, "alt_address_count", state.address_count);
push_fact(report, "alt_deactivation_slot", state.deactivation_slot);
push_check(
report,
"alt_authority_matches",
state.authority.as_deref() == std::option::Option::Some(authority.0.as_str()),
"lookup table authority must match the requested authority",
);
let active = state.deactivation_slot == u64::MAX;
match action {
AltAction::Extend { payer_present, added_addresses } => {
push_check(
report,
"alt_extend_active",
active,
"lookup table must be active before extension",
);
let target_count = state.address_count.saturating_add(added_addresses);
push_fact(report, "alt_target_address_count", target_count);
push_check(
report,
"alt_extend_capacity",
target_count <= ALT_MAX_ADDRESSES,
"lookup table cannot exceed 256 addresses",
);
let target_space = ALT_META_SIZE.saturating_add(target_count.saturating_mul(32));
let rent = match rent_minimum(pool, request, target_space as u64).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let top_up = rent.saturating_sub(account.lamports);
push_fact(report, "alt_target_space", target_space);
push_fact(report, "alt_rent_top_up_lamports", top_up);
push_check(
report,
"alt_extend_payer_available_when_needed",
top_up == 0 || payer_present,
"ALT extension requires the optional payer/system pair when rent top-up is needed",
);
},
AltAction::Freeze => {
push_check(
report,
"alt_freeze_active",
active,
"lookup table must be active before freezing",
);
push_check(
report,
"alt_freeze_non_empty",
state.address_count > 0,
"empty lookup tables cannot be frozen",
);
},
AltAction::Deactivate => {
push_check(
report,
"alt_deactivate_active",
active,
"lookup table must still be active before deactivation",
);
},
AltAction::Close => {
push_check(
report,
"alt_close_deactivated",
!active,
"lookup table must be deactivated before closing",
);
if !active {
let epoch = match epoch_info(pool, request).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let last_valid =
solana_address_lookup_table_interface::state::estimate_last_valid_slot(
state.deactivation_slot,
);
push_fact(report, "alt_close_last_valid_slot", last_valid);
push_check(
report,
"alt_close_cooldown_elapsed",
epoch.absolute_slot > last_valid,
"lookup table cooldown must be conservatively elapsed before close",
);
}
},
}
return std::result::Result::Ok(());
}
fn parse_alt_state(data: &[u8]) -> kb_core::Result<AddressLookupTableState> {
if data.len() < ALT_META_SIZE {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_alt_data_short",
"lookup table account data is shorter than the 56-byte metadata region",
));
}
let tag = match read_u32(data, 0, "ALT program state tag") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if tag != 1 {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_alt_uninitialized",
"lookup table account is not in the initialized LookupTable program state",
));
}
let deactivation_slot = match read_u64(data, 4, "ALT deactivation slot") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let authority = match data[21] {
0 => std::option::Option::None,
1 => {
let bytes = match data.get(22..54) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_alt_authority_short",
"lookup table authority bytes are truncated",
));
},
};
let array = match <[u8; 32]>::try_from(bytes) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_alt_authority_short",
"lookup table authority bytes are not exactly 32 bytes",
));
},
};
std::option::Option::Some(solana_pubkey::Pubkey::new_from_array(array).to_string())
},
value => {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_alt_authority_option_invalid",
format!("lookup table authority option tag must be 0 or 1 but received {value}"),
));
},
};
let address_bytes = data.len() - ALT_META_SIZE;
if address_bytes % 32 != 0 {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_alt_addresses_misaligned",
"lookup table address bytes must be aligned to 32-byte public keys",
));
}
let address_count = address_bytes / 32;
if address_count > ALT_MAX_ADDRESSES {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_alt_capacity_invalid",
"lookup table account contains more than 256 addresses",
));
}
return std::result::Result::Ok(AddressLookupTableState {
deactivation_slot,
authority,
address_count,
});
}
async fn inspect_config_create(
pool: &kb_onchain_transport::HttpEndpointPool,
request: &crate::SolanaCoreStatefulReadinessRequest,
report: &mut crate::SolanaCoreStatefulReadinessReport,
config_account: &kb_lib::MdPubkey,
lamports: u64,
max_data_space: u64,
max_key_count: usize,
) -> kb_core::Result<()> {
let account_result = match account_with_data(pool, request, config_account).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
update_context_slot(report, account_result.context.slot);
push_check(
report,
"config_create_target_absent",
account_result.account.is_none(),
"new Config account must not already exist",
);
let key_bytes =
compact_u16_length(max_key_count).saturating_add(max_key_count.saturating_mul(33));
let space = match max_data_space.checked_add(key_bytes as u64) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_config_space_overflow",
"Config account allocation length overflow",
));
},
};
let rent = match rent_minimum(pool, request, space).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
push_fact(report, "config_account_space", space);
push_fact(report, "minimum_rent_lamports", rent);
push_check(
report,
"config_create_rent_sufficient",
lamports >= rent,
"Config account lamports must meet the current rent-exempt minimum",
);
return std::result::Result::Ok(());
}
async fn inspect_config_store(
pool: &kb_onchain_transport::HttpEndpointPool,
request: &crate::SolanaCoreStatefulReadinessRequest,
report: &mut crate::SolanaCoreStatefulReadinessReport,
config_account: &kb_lib::MdPubkey,
key_count: usize,
data_length: usize,
) -> kb_core::Result<()> {
let account_result = match account_with_data(pool, request, config_account).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
update_context_slot(report, account_result.context.slot);
let account = match account_result.account {
std::option::Option::Some(value) => value,
std::option::Option::None => {
push_check(
report,
"config_store_account_exists",
false,
"Config account does not exist",
);
return std::result::Result::Ok(());
},
};
push_check(
report,
"config_store_owner_matches",
account.owner.0 == kb_program_ids::CONFIG_PROGRAM_ID,
"Config account must be owned by the Config program",
);
push_check(
report,
"config_store_not_executable",
!account.executable,
"Config state account must not be executable",
);
let required = compact_u16_length(key_count)
.saturating_add(key_count.saturating_mul(33))
.saturating_add(data_length);
push_fact(report, "config_required_space", required);
push_fact(report, "config_existing_space", account.space);
push_check(
report,
"config_store_space_sufficient",
account.space >= required as u64,
"Config account allocation must fit the complete encoded key list and opaque data",
);
return std::result::Result::Ok(());
}
async fn inspect_feature_activate(
pool: &kb_onchain_transport::HttpEndpointPool,
request: &crate::SolanaCoreStatefulReadinessRequest,
report: &mut crate::SolanaCoreStatefulReadinessReport,
feature_account: &kb_lib::MdPubkey,
lamports: u64,
) -> kb_core::Result<()> {
let account_result = match account_with_data(pool, request, feature_account).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
update_context_slot(report, account_result.context.slot);
let existing_lamports = match account_result.account.as_ref() {
std::option::Option::Some(account) => account.lamports,
std::option::Option::None => 0,
};
let reusable = account_result.account.as_ref().map(|account| {
return account.owner.0 == kb_program_ids::SYSTEM_PROGRAM_ID
&& !account.executable
&& account.space == 0
&& account.data.is_empty();
});
push_check(
report,
"feature_activate_target_available",
match reusable {
std::option::Option::Some(value) => value,
std::option::Option::None => true,
},
"feature target must be absent or a prefunded zero-space System account",
);
let rent = match rent_minimum(pool, request, FEATURE_ACCOUNT_SIZE).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let required_top_up = rent.saturating_sub(existing_lamports);
push_fact(report, "feature_existing_lamports", existing_lamports);
push_fact(report, "feature_required_top_up_lamports", required_top_up);
push_check(
report,
"feature_activate_rent_sufficient",
lamports >= required_top_up,
"feature activation transfer must cover the measured rent shortfall",
);
return std::result::Result::Ok(());
}
async fn inspect_feature_revoke(
pool: &kb_onchain_transport::HttpEndpointPool,
request: &crate::SolanaCoreStatefulReadinessRequest,
report: &mut crate::SolanaCoreStatefulReadinessReport,
feature_account: &kb_lib::MdPubkey,
) -> kb_core::Result<()> {
let account_result = match account_with_data(pool, request, feature_account).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
update_context_slot(report, account_result.context.slot);
let account = match account_result.account {
std::option::Option::Some(value) => value,
std::option::Option::None => {
push_check(
report,
"feature_revoke_account_exists",
false,
"feature account does not exist",
);
return std::result::Result::Ok(());
},
};
push_check(
report,
"feature_revoke_owner_matches",
account.owner.0 == kb_program_ids::FEATURE_PROGRAM_ID,
"feature account must be owned by the Feature program",
);
push_check(
report,
"feature_revoke_not_executable",
!account.executable,
"feature state account must not be executable",
);
let pending = account.data.len() == FEATURE_ACCOUNT_SIZE as usize
&& account.data.first() == std::option::Option::Some(&0);
push_check(
report,
"feature_revoke_pending",
pending,
"only a pending feature state encoded as Feature { activated_at: None } may be revoked",
);
return std::result::Result::Ok(());
}
async fn inspect_slashing_submit(
pool: &kb_onchain_transport::HttpEndpointPool,
request: &crate::SolanaCoreStatefulReadinessRequest,
report: &mut crate::SolanaCoreStatefulReadinessReport,
proof_account: &kb_lib::MdPubkey,
report_account: &kb_lib::MdPubkey,
report_lamports: u64,
proof_offset: u64,
slot: u64,
) -> kb_core::Result<()> {
let proof_result = match account_with_data(pool, request, proof_account).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
update_context_slot(report, proof_result.context.slot);
let proof = match proof_result.account {
std::option::Option::Some(value) => value,
std::option::Option::None => {
push_check(
report,
"slashing_proof_account_exists",
false,
"duplicate-block proof account does not exist",
);
return std::result::Result::Ok(());
},
};
push_check(
report,
"slashing_proof_not_executable",
!proof.executable,
"duplicate-block proof account must not be executable",
);
let packed_length = match duplicate_block_proof_length(proof.data.as_slice(), proof_offset) {
std::result::Result::Ok(value) => {
push_check(
report,
"slashing_proof_shape_valid",
true,
"duplicate-block proof contains two bounded length-prefixed shreds",
);
value
},
std::result::Result::Err(error) => {
push_check(report, "slashing_proof_shape_valid", false, error.to_string());
0
},
};
push_fact(report, "slashing_packed_proof_bytes", packed_length);
let report_result = match account_with_data(pool, request, report_account).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
update_context_slot(report, report_result.context.slot);
let report_reusable = report_result.account.as_ref().map(|account| {
return account.owner.0 == kb_program_ids::SYSTEM_PROGRAM_ID
&& !account.executable
&& account.space == 0
&& account.data.is_empty();
});
push_check(
report,
"slashing_report_target_available",
match report_reusable {
std::option::Option::Some(value) => value,
std::option::Option::None => true,
},
"violation report PDA must be absent or an empty prefunded System account",
);
let epoch = match epoch_info(pool, request).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
push_fact(report, "absolute_slot", epoch.absolute_slot);
push_fact(report, "slots_in_epoch", epoch.slots_in_epoch);
let age = epoch.absolute_slot.saturating_sub(slot);
push_check(
report,
"slashing_slot_not_future",
slot <= epoch.absolute_slot,
"reported duplicate-block slot must not be in the future",
);
push_check(
report,
"slashing_slot_within_one_epoch",
slot <= epoch.absolute_slot && age <= epoch.slots_in_epoch,
"duplicate-block proof must be submitted within one epoch worth of slots",
);
if packed_length > 0 {
let report_size = SLASHING_REPORT_HEADER_SIZE.saturating_add(packed_length);
let rent = match rent_minimum(pool, request, report_size as u64).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
push_fact(report, "slashing_report_space", report_size);
push_fact(report, "minimum_rent_lamports", rent);
push_check(
report,
"slashing_report_rent_sufficient",
report_lamports >= rent,
"report_lamports must cover the exact report header and retained proof bytes",
);
}
return std::result::Result::Ok(());
}
async fn inspect_slashing_close(
pool: &kb_onchain_transport::HttpEndpointPool,
request: &crate::SolanaCoreStatefulReadinessRequest,
report: &mut crate::SolanaCoreStatefulReadinessReport,
report_account: &kb_lib::MdPubkey,
destination: &kb_lib::MdPubkey,
) -> kb_core::Result<()> {
let account_result = match account_with_data(pool, request, report_account).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
update_context_slot(report, account_result.context.slot);
let account = match account_result.account {
std::option::Option::Some(value) => value,
std::option::Option::None => {
push_check(
report,
"slashing_report_exists",
false,
"violation report account does not exist",
);
return std::result::Result::Ok(());
},
};
push_check(
report,
"slashing_report_owner_matches",
account.owner.0 == kb_program_ids::SLASHING_PROGRAM_ID,
"violation report must be owned by the Slashing program",
);
push_check(
report,
"slashing_report_not_executable",
!account.executable,
"violation report state account must not be executable",
);
if account.data.len() < SLASHING_REPORT_HEADER_SIZE {
push_check(
report,
"slashing_report_decodes",
false,
"violation report is shorter than its 114-byte fixed header",
);
return std::result::Result::Ok(());
}
let version = account.data[0];
let retained_destination = match pubkey_text(&account.data[33..65]) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
push_check(report, "slashing_report_decodes", false, error.to_string());
return std::result::Result::Ok(());
},
};
let report_epoch = match read_u64(account.data.as_slice(), 65, "Slashing report epoch") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
push_fact(report, "slashing_report_version", version);
push_fact(report, "slashing_report_epoch", report_epoch);
push_check(
report,
"slashing_report_version_supported",
version == 1,
"violation report version must be 1",
);
push_check(
report,
"slashing_report_destination_matches",
retained_destination == destination.0,
"close destination must match the destination retained in the violation report",
);
let epoch = match epoch_info(pool, request).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
push_fact(report, "current_epoch", epoch.epoch);
push_check(
report,
"slashing_report_retention_elapsed",
epoch.epoch >= report_epoch.saturating_add(3),
"violation report must remain on-chain for at least three epochs",
);
return std::result::Result::Ok(());
}
async fn inspect_zk_verify(
pool: &kb_onchain_transport::HttpEndpointPool,
request: &crate::SolanaCoreStatefulReadinessRequest,
report: &mut crate::SolanaCoreStatefulReadinessReport,
proof_type: kb_lib::ExSolanaCoreZkElGamalProofType,
proof_account: std::option::Option<(&kb_lib::MdPubkey, u32)>,
context_state: std::option::Option<&kb_lib::ExSolanaCoreZkElGamalContextState>,
) -> kb_core::Result<()> {
if let std::option::Option::Some((account_pubkey, offset)) = proof_account {
let proof_result = match account_with_data(pool, request, account_pubkey).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
update_context_slot(report, proof_result.context.slot);
let proof = match proof_result.account {
std::option::Option::Some(value) => value,
std::option::Option::None => {
push_check(
report,
"zk_proof_account_exists",
false,
"ZK proof account does not exist",
);
return std::result::Result::Ok(());
},
};
push_check(
report,
"zk_proof_account_not_executable",
!proof.executable,
"ZK proof account must not be executable",
);
let end = (offset as usize).checked_add(proof_type.proof_data_size());
push_check(
report,
"zk_proof_range_available",
match end {
std::option::Option::Some(end) => end <= proof.data.len(),
std::option::Option::None => false,
},
"ZK proof account must contain the complete proof POD at the requested offset",
);
}
let context = match context_state {
std::option::Option::Some(value) => value,
std::option::Option::None => {
push_fact(report, "zk_context_state", "not_requested");
return std::result::Result::Ok(());
},
};
let context_result = match account_with_data(pool, request, &context.account).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
update_context_slot(report, context_result.context.slot);
let account = match context_result.account {
std::option::Option::Some(value) => value,
std::option::Option::None => {
push_check(
report,
"zk_context_account_exists",
false,
"preallocated ZK context-state account does not exist",
);
return std::result::Result::Ok(());
},
};
let expected_size = proof_type.context_state_size();
push_fact(report, "zk_context_expected_space", expected_size);
push_fact(report, "zk_context_existing_space", account.space);
push_check(
report,
"zk_context_owner_matches",
account.owner.0 == kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID,
"ZK context-state account must be owned by the ZK ElGamal Proof program",
);
push_check(
report,
"zk_context_not_executable",
!account.executable,
"ZK context-state account must not be executable",
);
push_check(
report,
"zk_context_space_exact",
account.space == expected_size as u64 && account.data.len() == expected_size,
"ZK context-state allocation must exactly match the official ProofContextState size",
);
push_check(
report,
"zk_context_uninitialized",
account.data.iter().all(|byte| return *byte == 0),
"preallocated ZK context-state account must still be zero-initialized",
);
let rent = match rent_minimum(pool, request, expected_size as u64).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
push_fact(report, "minimum_rent_lamports", rent);
push_check(
report,
"zk_context_rent_exempt",
account.lamports >= rent,
"preallocated ZK context-state account must be rent exempt",
);
return std::result::Result::Ok(());
}
async fn inspect_zk_close(
pool: &kb_onchain_transport::HttpEndpointPool,
request: &crate::SolanaCoreStatefulReadinessRequest,
report: &mut crate::SolanaCoreStatefulReadinessReport,
context_state: &kb_lib::MdPubkey,
authority: &kb_lib::MdPubkey,
) -> kb_core::Result<()> {
let account_result = match account_with_data(pool, request, context_state).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
update_context_slot(report, account_result.context.slot);
let account = match account_result.account {
std::option::Option::Some(value) => value,
std::option::Option::None => {
push_check(
report,
"zk_context_account_exists",
false,
"ZK context-state account does not exist",
);
return std::result::Result::Ok(());
},
};
push_check(
report,
"zk_context_owner_matches",
account.owner.0 == kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID,
"ZK context-state account must be owned by the ZK ElGamal Proof program",
);
push_check(
report,
"zk_context_not_executable",
!account.executable,
"ZK context-state account must not be executable",
);
if account.data.len() < ZK_CONTEXT_META_SIZE {
push_check(
report,
"zk_context_meta_decodes",
false,
"ZK context-state account is shorter than its 33-byte generic metadata",
);
return std::result::Result::Ok(());
}
let retained_authority = match pubkey_text(&account.data[..32]) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
push_check(report, "zk_context_meta_decodes", false, error.to_string());
return std::result::Result::Ok(());
},
};
let proof_type = proof_type_from_discriminator(account.data[32]);
push_check(
report,
"zk_context_authority_matches",
retained_authority == authority.0,
"close authority must match the authority retained in ProofContextState",
);
push_check(
report,
"zk_context_proof_type_supported",
proof_type.is_some(),
"ProofContextState must contain a supported proof type discriminator",
);
if let std::option::Option::Some(value) = proof_type {
let expected_size = value.context_state_size();
push_fact(report, "zk_context_proof_type", value.discriminator());
push_fact(report, "zk_context_expected_space", expected_size);
push_check(
report,
"zk_context_space_exact",
account.space == expected_size as u64 && account.data.len() == expected_size,
"stored ZK context-state allocation must match its proof type",
);
}
return std::result::Result::Ok(());
}
fn proof_type_from_discriminator(
discriminator: u8,
) -> std::option::Option<kb_lib::ExSolanaCoreZkElGamalProofType> {
return match discriminator {
1 => std::option::Option::Some(kb_lib::ExSolanaCoreZkElGamalProofType::ZeroCiphertext),
2 => std::option::Option::Some(
kb_lib::ExSolanaCoreZkElGamalProofType::CiphertextCiphertextEquality,
),
3 => std::option::Option::Some(
kb_lib::ExSolanaCoreZkElGamalProofType::CiphertextCommitmentEquality,
),
4 => std::option::Option::Some(kb_lib::ExSolanaCoreZkElGamalProofType::PubkeyValidity),
5 => std::option::Option::Some(kb_lib::ExSolanaCoreZkElGamalProofType::PercentageWithCap),
6 => {
std::option::Option::Some(kb_lib::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU64)
},
7 => {
std::option::Option::Some(kb_lib::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU128)
},
8 => {
std::option::Option::Some(kb_lib::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU256)
},
9 => std::option::Option::Some(
kb_lib::ExSolanaCoreZkElGamalProofType::GroupedCiphertext2HandlesValidity,
),
10 => std::option::Option::Some(
kb_lib::ExSolanaCoreZkElGamalProofType::BatchedGroupedCiphertext2HandlesValidity,
),
11 => std::option::Option::Some(
kb_lib::ExSolanaCoreZkElGamalProofType::GroupedCiphertext3HandlesValidity,
),
12 => std::option::Option::Some(
kb_lib::ExSolanaCoreZkElGamalProofType::BatchedGroupedCiphertext3HandlesValidity,
),
_ => std::option::Option::None,
};
}
fn duplicate_block_proof_length(data: &[u8], offset: u64) -> kb_core::Result<usize> {
let start = match usize::try_from(offset) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_slashing_offset_invalid",
"duplicate-block proof offset does not fit the local address space",
));
},
};
let first_length = match read_u32(data, start, "first duplicate-block shred length") {
std::result::Result::Ok(value) => value as usize,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if first_length == 0 || first_length > MAX_SHRED_SIZE {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_slashing_first_shred_length_invalid",
"first duplicate-block shred length must be between 1 and 1232 bytes",
));
}
let second_length_offset =
match start.checked_add(4).and_then(|value| return value.checked_add(first_length)) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_slashing_proof_overflow",
"duplicate-block proof length arithmetic overflow",
));
},
};
let second_length =
match read_u32(data, second_length_offset, "second duplicate-block shred length") {
std::result::Result::Ok(value) => value as usize,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if second_length == 0 || second_length > MAX_SHRED_SIZE {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_slashing_second_shred_length_invalid",
"second duplicate-block shred length must be between 1 and 1232 bytes",
));
}
let total = 4_usize
.checked_add(first_length)
.and_then(|value| return value.checked_add(4))
.and_then(|value| return value.checked_add(second_length));
let total = match total {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_slashing_proof_overflow",
"duplicate-block proof length arithmetic overflow",
));
},
};
let end = match start.checked_add(total) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_slashing_proof_overflow",
"duplicate-block proof end offset overflow",
));
},
};
if end > data.len() {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_slashing_proof_truncated",
"duplicate-block proof account does not contain both declared shreds",
));
}
return std::result::Result::Ok(total);
}
fn compact_u16_length(value: usize) -> usize {
if value < 0x80 {
return 1;
}
if value < 0x4000 {
return 2;
}
return 3;
}
fn read_u32(data: &[u8], offset: usize, label: &str) -> kb_core::Result<u32> {
let end = match offset.checked_add(4) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_integer_overflow",
format!("{label} offset overflow"),
));
},
};
let bytes = match data.get(offset..end) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_integer_truncated",
format!("{label} is truncated"),
));
},
};
let array = match <[u8; 4]>::try_from(bytes) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_integer_truncated",
format!("{label} is not exactly four bytes"),
));
},
};
return std::result::Result::Ok(u32::from_le_bytes(array));
}
fn read_u64(data: &[u8], offset: usize, label: &str) -> kb_core::Result<u64> {
let end = match offset.checked_add(8) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_integer_overflow",
format!("{label} offset overflow"),
));
},
};
let bytes = match data.get(offset..end) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_integer_truncated",
format!("{label} is truncated"),
));
},
};
let array = match <[u8; 8]>::try_from(bytes) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_integer_truncated",
format!("{label} is not exactly eight bytes"),
));
},
};
return std::result::Result::Ok(u64::from_le_bytes(array));
}
fn pubkey_text(bytes: &[u8]) -> kb_core::Result<std::string::String> {
let array = match <[u8; 32]>::try_from(bytes) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return std::result::Result::Err(kb_core::Error::new(
"execution_stateful_pubkey_length_invalid",
"stateful account field is not exactly 32 bytes",
));
},
};
return std::result::Result::Ok(solana_pubkey::Pubkey::new_from_array(array).to_string());
}
#[cfg(test)]
mod tests {
#[test]
fn cluster_policy_restricts_stateful_reads_to_localnet_and_devnet() {
let devnet = kb_onchain_transport::GenesisHashResult {
genesis_hash: std::string::String::from("devnet"),
classified_cluster: std::option::Option::Some(kb_lib::ExApiExecutionCluster::Devnet),
};
assert!(super::validate_cluster(kb_lib::ExApiExecutionCluster::Devnet, &devnet,).is_ok());
assert!(
super::validate_cluster(kb_lib::ExApiExecutionCluster::Localnet, &devnet,).is_err()
);
let local = kb_onchain_transport::GenesisHashResult {
genesis_hash: std::string::String::from("local"),
classified_cluster: std::option::Option::None,
};
assert!(super::validate_cluster(kb_lib::ExApiExecutionCluster::Localnet, &local,).is_ok());
}
#[test]
fn alt_state_parser_preserves_authority_lifecycle_and_capacity() {
let authority = solana_pubkey::Pubkey::new_unique();
let mut data = vec![0_u8; 56 + 64];
data[..4].copy_from_slice(&1_u32.to_le_bytes());
data[4..12].copy_from_slice(&u64::MAX.to_le_bytes());
data[21] = 1;
let authority_bytes = authority.to_bytes();
data[22..54].copy_from_slice(&authority_bytes);
let state = super::parse_alt_state(data.as_slice())
.unwrap_or_else(|error| panic!("unexpected ALT state error: {error}"));
assert_eq!(state.deactivation_slot, u64::MAX);
assert_eq!(state.authority, std::option::Option::Some(authority.to_string()));
assert_eq!(state.address_count, 2);
data.push(0);
assert!(super::parse_alt_state(data.as_slice()).is_err());
}
#[test]
fn slashing_proof_shape_is_bounded_and_exact() {
let mut data = std::vec::Vec::new();
data.extend_from_slice(&3_u32.to_le_bytes());
data.extend_from_slice(&[1, 2, 3]);
data.extend_from_slice(&2_u32.to_le_bytes());
data.extend_from_slice(&[4, 5]);
assert_eq!(
super::duplicate_block_proof_length(data.as_slice(), 0)
.unwrap_or_else(|error| panic!("unexpected proof shape error: {error}")),
13,
);
assert!(super::duplicate_block_proof_length(&data[..12], 0).is_err());
let mut too_large = std::vec::Vec::new();
too_large.extend_from_slice(&1_233_u32.to_le_bytes());
assert!(super::duplicate_block_proof_length(too_large.as_slice(), 0,).is_err());
}
#[test]
fn proof_discriminators_map_to_exact_context_sizes() {
for discriminator in 1_u8..=12 {
let proof_type = super::proof_type_from_discriminator(discriminator)
.unwrap_or_else(|| panic!("missing proof type {discriminator}"));
assert_eq!(proof_type.discriminator(), discriminator);
assert!(proof_type.context_state_size() > super::ZK_CONTEXT_META_SIZE);
}
assert_eq!(
kb_lib::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU64.context_state_size(),
kb_lib::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU256.context_state_size(),
);
assert!(super::proof_type_from_discriminator(0).is_none());
}
#[test]
fn compact_u16_length_matches_config_key_boundaries() {
assert_eq!(super::compact_u16_length(0), 1);
assert_eq!(super::compact_u16_length(127), 1);
assert_eq!(super::compact_u16_length(128), 2);
assert_eq!(super::compact_u16_length(16_384), 3);
}
}