v0.1.0-pre.015
This commit is contained in:
@@ -1,68 +1,307 @@
|
||||
// file: kb-lib/src/executor/solana/core.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
//! Reserved executor for `solana_core`.
|
||||
//! Safe typed executor for supported Solana core operations.
|
||||
|
||||
/// Reserved executor for the `solana_core` program surface.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ExSolanaCoreExecutor;
|
||||
mod address_lookup_table;
|
||||
mod builder;
|
||||
mod constants;
|
||||
mod executor;
|
||||
mod intent;
|
||||
mod loader_v3;
|
||||
mod loader_v4;
|
||||
mod native_admin;
|
||||
mod precompiles;
|
||||
mod slashing;
|
||||
mod stake;
|
||||
mod validation;
|
||||
mod vote;
|
||||
mod zk_elgamal;
|
||||
|
||||
impl crate::ExApiInstructionExecutor for crate::ExSolanaCoreExecutor {
|
||||
fn executor_name(&self) -> &'static str {
|
||||
return "kb_executor_solana_core";
|
||||
}
|
||||
/// Exposes the Solana core executor.
|
||||
pub use self::executor::ExSolanaCoreExecutor;
|
||||
/// Exposes the Address Lookup Table close operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_CLOSE_OPERATION;
|
||||
/// Exposes the Address Lookup Table create operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_CREATE_OPERATION;
|
||||
/// Exposes the Address Lookup Table deactivate operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_DEACTIVATE_OPERATION;
|
||||
/// Exposes the Address Lookup Table extend operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_EXTEND_OPERATION;
|
||||
/// Exposes the Address Lookup Table freeze operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_FREEZE_OPERATION;
|
||||
/// Exposes the Compute Budget heap-frame operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_COMPUTE_BUDGET_REQUEST_HEAP_FRAME_OPERATION;
|
||||
/// Exposes the Compute Budget loaded-account data-size operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_COMPUTE_BUDGET_SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT_OPERATION;
|
||||
/// Exposes the Compute Budget unit-limit operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_COMPUTE_BUDGET_SET_UNIT_LIMIT_OPERATION;
|
||||
/// Exposes the Compute Budget unit-price operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_COMPUTE_BUDGET_SET_UNIT_PRICE_OPERATION;
|
||||
/// Exposes the Config account creation operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_CONFIG_CREATE_ACCOUNT_OPERATION;
|
||||
/// Exposes the Config store operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_CONFIG_STORE_OPERATION;
|
||||
/// Exposes the Ed25519 inline verification operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_ED25519_VERIFY_INLINE_OPERATION;
|
||||
/// Exposes the Ed25519 offset-table verification operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_ED25519_VERIFY_OFFSETS_OPERATION;
|
||||
/// Exposes the feature activation operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_FEATURE_ACTIVATE_OPERATION;
|
||||
/// Exposes the feature pending-activation revoke operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_FEATURE_REVOKE_PENDING_ACTIVATION_OPERATION;
|
||||
/// Exposes Loader v3 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V3_CLOSE_ANY_OPERATION;
|
||||
/// Exposes Loader v3 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V3_CLOSE_OPERATION;
|
||||
/// Exposes Loader v3 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V3_CREATE_BUFFER_OPERATION;
|
||||
/// Exposes Loader v3 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V3_DEPLOY_WITH_MAX_PROGRAM_LEN_OPERATION;
|
||||
/// Exposes Loader v3 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V3_EXTEND_PROGRAM_OPERATION;
|
||||
/// Exposes Loader v3 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V3_SET_BUFFER_AUTHORITY_CHECKED_OPERATION;
|
||||
/// Exposes Loader v3 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V3_SET_BUFFER_AUTHORITY_OPERATION;
|
||||
/// Exposes Loader v3 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V3_SET_UPGRADE_AUTHORITY_CHECKED_OPERATION;
|
||||
/// Exposes Loader v3 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V3_SET_UPGRADE_AUTHORITY_OPERATION;
|
||||
/// Exposes Loader v3 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V3_UPGRADE_OPERATION;
|
||||
/// Exposes Loader v3 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V3_WRITE_OPERATION;
|
||||
/// Exposes Loader v4 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V4_COPY_OPERATION;
|
||||
/// Exposes Loader v4 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V4_CREATE_BUFFER_OPERATION;
|
||||
/// Exposes Loader v4 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V4_DEPLOY_FROM_SOURCE_OPERATION;
|
||||
/// Exposes Loader v4 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V4_DEPLOY_OPERATION;
|
||||
/// Exposes Loader v4 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V4_FINALIZE_OPERATION;
|
||||
/// Exposes Loader v4 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V4_RETRACT_OPERATION;
|
||||
/// Exposes Loader v4 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V4_SET_PROGRAM_LENGTH_OPERATION;
|
||||
/// Exposes Loader v4 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V4_TRANSFER_AUTHORITY_OPERATION;
|
||||
/// Exposes Loader v4 operation codes.
|
||||
pub use self::intent::EX_SOLANA_CORE_LOADER_V4_WRITE_OPERATION;
|
||||
/// Exposes every stable callable Solana core operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_OPERATION_CODES;
|
||||
/// Exposes the secp256k1 inline verification operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SECP256K1_VERIFY_INLINE_OPERATION;
|
||||
/// Exposes the secp256k1 offset-table verification operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SECP256K1_VERIFY_OFFSETS_OPERATION;
|
||||
/// Exposes the secp256r1 inline verification operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SECP256R1_VERIFY_INLINE_OPERATION;
|
||||
/// Exposes the secp256r1 offset-table verification operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SECP256R1_VERIFY_OFFSETS_OPERATION;
|
||||
/// Exposes the Slashing violation-report close operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SLASHING_CLOSE_VIOLATION_REPORT_OPERATION;
|
||||
/// Exposes the Slashing duplicate-block proof submission operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SLASHING_SUBMIT_DUPLICATE_BLOCK_PROOF_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_AUTHORIZE_CHECKED_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_AUTHORIZE_CHECKED_WITH_SEED_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_AUTHORIZE_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_AUTHORIZE_WITH_SEED_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_CREATE_ACCOUNT_AND_DELEGATE_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_CREATE_ACCOUNT_CHECKED_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_CREATE_ACCOUNT_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_CREATE_ACCOUNT_WITH_SEED_AND_DELEGATE_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_CREATE_ACCOUNT_WITH_SEED_CHECKED_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_CREATE_ACCOUNT_WITH_SEED_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_DEACTIVATE_DELINQUENT_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_DEACTIVATE_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_DELEGATE_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_GET_MINIMUM_DELEGATION_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_INITIALIZE_CHECKED_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_INITIALIZE_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_MERGE_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_MOVE_LAMPORTS_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_MOVE_STAKE_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_SET_LOCKUP_CHECKED_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_SET_LOCKUP_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_SPLIT_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_SPLIT_WITH_SEED_OPERATION;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::EX_SOLANA_CORE_STAKE_WITHDRAW_OPERATION;
|
||||
/// Exposes the System allocate operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_ALLOCATE_OPERATION;
|
||||
/// Exposes the System seeded allocate operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_ALLOCATE_WITH_SEED_OPERATION;
|
||||
/// Exposes the System assign operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_ASSIGN_OPERATION;
|
||||
/// Exposes the System seeded assign operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_ASSIGN_WITH_SEED_OPERATION;
|
||||
/// Exposes the System prefunded create-account operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_CREATE_ACCOUNT_ALLOW_PREFUND_OPERATION;
|
||||
/// Exposes the System create-account operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_CREATE_ACCOUNT_OPERATION;
|
||||
/// Exposes the System seeded create-account operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_CREATE_ACCOUNT_WITH_SEED_OPERATION;
|
||||
/// Exposes the durable nonce advance operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_NONCE_ADVANCE_OPERATION;
|
||||
/// Exposes the durable nonce authorize operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_NONCE_AUTHORIZE_OPERATION;
|
||||
/// Exposes the durable nonce account creation operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_NONCE_CREATE_OPERATION;
|
||||
/// Exposes the seeded durable nonce account creation operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_NONCE_CREATE_WITH_SEED_OPERATION;
|
||||
/// Exposes the durable nonce initialize operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_NONCE_INITIALIZE_OPERATION;
|
||||
/// Exposes the durable nonce upgrade operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_NONCE_UPGRADE_OPERATION;
|
||||
/// Exposes the durable nonce withdraw operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_NONCE_WITHDRAW_OPERATION;
|
||||
/// Exposes the System multi-transfer operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_TRANSFER_MANY_OPERATION;
|
||||
/// Exposes the System transfer operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_TRANSFER_OPERATION;
|
||||
/// Exposes the System seeded transfer operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_SYSTEM_TRANSFER_WITH_SEED_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_AUTHORIZE_CHECKED_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_AUTHORIZE_CHECKED_WITH_SEED_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_AUTHORIZE_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_AUTHORIZE_WITH_SEED_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_COMPACT_UPDATE_VOTE_STATE_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_COMPACT_UPDATE_VOTE_STATE_SWITCH_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_CREATE_ACCOUNT_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_CREATE_ACCOUNT_V2_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_CREATE_ACCOUNT_WITH_SEED_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_CREATE_ACCOUNT_WITH_SEED_V2_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_DEPOSIT_DELEGATOR_REWARDS_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_INITIALIZE_ACCOUNT_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_INITIALIZE_ACCOUNT_V2_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_SUBMIT_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_SWITCH_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_TOWER_SYNC_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_TOWER_SYNC_SWITCH_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_UPDATE_COMMISSION_BPS_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_UPDATE_COMMISSION_COLLECTOR_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_UPDATE_COMMISSION_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_UPDATE_VALIDATOR_IDENTITY_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_UPDATE_VOTE_STATE_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_UPDATE_VOTE_STATE_SWITCH_OPERATION;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::EX_SOLANA_CORE_VOTE_WITHDRAW_OPERATION;
|
||||
/// Exposes the ZK ElGamal context-state close operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_ZK_ELGAMAL_CLOSE_CONTEXT_STATE_OPERATION;
|
||||
/// Exposes the ZK ElGamal proof-account verification operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_ZK_ELGAMAL_VERIFY_FROM_ACCOUNT_OPERATION;
|
||||
/// Exposes the ZK ElGamal inline proof verification operation code.
|
||||
pub use self::intent::EX_SOLANA_CORE_ZK_ELGAMAL_VERIFY_INLINE_OPERATION;
|
||||
/// Exposes one Config Program key entry.
|
||||
pub use self::intent::ExSolanaCoreConfigKey;
|
||||
/// Exposes one Ed25519 verification offset record.
|
||||
pub use self::intent::ExSolanaCoreEd25519VerificationOffsets;
|
||||
/// Exposes the complete typed Solana core intent.
|
||||
pub use self::intent::ExSolanaCoreExecutionIntent;
|
||||
/// Exposes typed Solana core operation arguments.
|
||||
pub use self::intent::ExSolanaCoreOperation;
|
||||
/// Exposes one secp256k1 verification offset record.
|
||||
pub use self::intent::ExSolanaCoreSecp256k1VerificationOffsets;
|
||||
/// Exposes one secp256r1 verification offset record.
|
||||
pub use self::intent::ExSolanaCoreSecp256r1VerificationOffsets;
|
||||
/// Exposes Stake Program operation codes and the authority-role selector.
|
||||
pub use self::intent::ExSolanaCoreStakeAuthorizationKind;
|
||||
/// Exposes one recipient in a System multi-transfer.
|
||||
pub use self::intent::ExSolanaCoreSystemTransferRecipient;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::ExSolanaCoreVoteAuthorizationKind;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::ExSolanaCoreVoteCommissionKind;
|
||||
/// Exposes Vote Program operation codes and typed Vote selectors.
|
||||
pub use self::intent::ExSolanaCoreVoteLockout;
|
||||
/// Exposes optional ZK ElGamal context-state accounts.
|
||||
pub use self::intent::ExSolanaCoreZkElGamalContextState;
|
||||
/// Exposes the official ZK ElGamal proof kinds.
|
||||
pub use self::intent::ExSolanaCoreZkElGamalProofType;
|
||||
|
||||
fn executor_version(&self) -> &'static str {
|
||||
return env!("CARGO_PKG_VERSION");
|
||||
}
|
||||
|
||||
fn program_ids(&self) -> &'static [&'static str] {
|
||||
return &[
|
||||
kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID,
|
||||
kb_program_ids::BPF_LOADER_DEPRECATED_PROGRAM_ID,
|
||||
kb_program_ids::BPF_LOADER_PROGRAM_ID,
|
||||
kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID,
|
||||
kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID,
|
||||
kb_program_ids::CONFIG_PROGRAM_ID,
|
||||
kb_program_ids::ED25519_PROGRAM_ID,
|
||||
kb_program_ids::FEATURE_PROGRAM_ID,
|
||||
kb_program_ids::LOADER_V4_PROGRAM_ID,
|
||||
kb_program_ids::NATIVE_LOADER_PROGRAM_ID,
|
||||
kb_program_ids::SECP256K1_PROGRAM_ID,
|
||||
kb_program_ids::SECP256R1_PROGRAM_ID,
|
||||
kb_program_ids::SLASHING_PROGRAM_ID,
|
||||
kb_program_ids::STAKE_PROGRAM_ID,
|
||||
kb_program_ids::SYSTEM_PROGRAM_ID,
|
||||
kb_program_ids::VOTE_PROGRAM_ID,
|
||||
kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID,
|
||||
kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID,
|
||||
];
|
||||
}
|
||||
|
||||
fn supports_request(
|
||||
&self,
|
||||
request: &crate::ExApiExecutionRequest,
|
||||
) -> crate::ExApiExecutionSupport {
|
||||
if crate::ExApiInstructionExecutor::handles_program_id(self, &request.program_id) {
|
||||
return crate::ExApiExecutionSupport::Maybe;
|
||||
}
|
||||
return crate::ExApiExecutionSupport::No;
|
||||
}
|
||||
|
||||
fn build_plan(
|
||||
&self,
|
||||
_request: &crate::ExApiExecutionRequest,
|
||||
) -> kb_core::Result<crate::ExApiExecutionPlan> {
|
||||
let payload_json = match crate::executor_api_serialize_payload_json(
|
||||
&serde_json::json!({"status":"reserved_executor","surface":"solana_core"}),
|
||||
) {
|
||||
std::result::Result::Ok(serialized) => serialized,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(crate::ExApiExecutionPlan {
|
||||
executor_name: "kb_executor_solana_core".to_string(),
|
||||
instruction_count: 0,
|
||||
payload_json,
|
||||
});
|
||||
}
|
||||
}
|
||||
/// Crate-root access to `build_prepared_plan` from `address_lookup_table`.
|
||||
pub(crate) use self::address_lookup_table::executor_solana_core_address_lookup_table_build_prepared_plan;
|
||||
/// Crate-root access to `build_prepared_plan` from `builder`.
|
||||
pub(crate) use self::builder::executor_solana_core_builder_build_prepared_plan;
|
||||
/// Crate-root access to `finalize_prepared_plan` from `builder`.
|
||||
pub(crate) use self::builder::executor_solana_core_finalize_prepared_plan;
|
||||
/// Canonical tracing target for this crate.
|
||||
pub(crate) use self::constants::EX_SOLANA_CORE_TRACING_TARGET;
|
||||
/// Crate-root access to `build_prepared_plan` from `loader_v3`.
|
||||
pub(crate) use self::loader_v3::executor_solana_core_loader_v3_build_prepared_plan;
|
||||
/// Crate-root access to `build_prepared_plan` from `loader_v4`.
|
||||
pub(crate) use self::loader_v4::executor_solana_core_loader_v4_build_prepared_plan;
|
||||
/// Crate-root access to `build_prepared_plan` from `native_admin`.
|
||||
pub(crate) use self::native_admin::executor_solana_core_native_admin_build_prepared_plan;
|
||||
/// Crate-root access to `build_prepared_plan` from `precompiles`.
|
||||
pub(crate) use self::precompiles::executor_solana_core_precompiles_build_prepared_plan;
|
||||
/// Crate-root access to `build_prepared_plan` from `slashing`.
|
||||
pub(crate) use self::slashing::executor_solana_core_slashing_build_prepared_plan;
|
||||
/// Crate-root access to `build_prepared_plan` from `stake`.
|
||||
pub(crate) use self::stake::executor_solana_core_stake_build_prepared_plan;
|
||||
/// Builds one typed validation error without duplicating domain-local wrappers.
|
||||
pub(crate) use self::validation::executor_solana_core_invalid;
|
||||
/// Parses one canonical native program identifier.
|
||||
pub(crate) use self::validation::executor_solana_core_parse_native_program_id;
|
||||
/// Parses one model public key with the canonical execution error contract.
|
||||
pub(crate) use self::validation::executor_solana_core_parse_pubkey;
|
||||
/// Validates that two model public keys identify distinct accounts.
|
||||
pub(crate) use self::validation::executor_solana_core_validate_distinct_pubkeys;
|
||||
/// Validates an exact byte-slice length while preserving the caller error code.
|
||||
pub(crate) use self::validation::executor_solana_core_validate_exact_length;
|
||||
/// Validates that an unsigned execution amount is strictly positive.
|
||||
pub(crate) use self::validation::executor_solana_core_validate_positive_u64;
|
||||
/// Validates one `Pubkey::create_with_seed` derivation with caller-owned error codes.
|
||||
pub(crate) use self::validation::executor_solana_core_validate_seeded_address;
|
||||
/// Crate-root access to `build_prepared_plan` from `vote`.
|
||||
pub(crate) use self::vote::executor_solana_core_vote_build_prepared_plan;
|
||||
/// Crate-root access to `build_prepared_plan` from `zk_elgamal`.
|
||||
pub(crate) use self::zk_elgamal::executor_solana_core_zk_elgamal_build_prepared_plan;
|
||||
|
||||
594
kb-lib/src/executor/solana/core/address_lookup_table.rs
Normal file
594
kb-lib/src/executor/solana/core/address_lookup_table.rs
Normal file
@@ -0,0 +1,594 @@
|
||||
// file: kb-lib/src/executor/solana/core/address_lookup_table.rs
|
||||
// version: 1
|
||||
|
||||
//! Official Address Lookup Table instruction builders.
|
||||
|
||||
pub(crate) fn executor_solana_core_address_lookup_table_build_prepared_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
return match &intent.operation {
|
||||
crate::ExSolanaCoreOperation::AddressLookupTableCreate {
|
||||
authority,
|
||||
payer,
|
||||
recent_slot,
|
||||
} => build_create_plan(intent, fee_payer, authority, payer, *recent_slot),
|
||||
crate::ExSolanaCoreOperation::AddressLookupTableExtend {
|
||||
lookup_table,
|
||||
authority,
|
||||
payer,
|
||||
new_addresses,
|
||||
} => build_extend_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
lookup_table,
|
||||
authority,
|
||||
payer.as_ref(),
|
||||
new_addresses.as_slice(),
|
||||
),
|
||||
crate::ExSolanaCoreOperation::AddressLookupTableFreeze { lookup_table, authority } => {
|
||||
build_freeze_plan(intent, fee_payer, lookup_table, authority)
|
||||
},
|
||||
crate::ExSolanaCoreOperation::AddressLookupTableDeactivate { lookup_table, authority } => {
|
||||
build_deactivate_plan(intent, fee_payer, lookup_table, authority)
|
||||
},
|
||||
crate::ExSolanaCoreOperation::AddressLookupTableClose {
|
||||
lookup_table,
|
||||
authority,
|
||||
recipient,
|
||||
} => build_close_plan(intent, fee_payer, lookup_table, authority, recipient),
|
||||
_ => std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_address_lookup_table_operation_mismatch",
|
||||
"Address Lookup Table builder received a non-ALT operation",
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
fn build_create_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
authority: &crate::MdPubkey,
|
||||
payer: &crate::MdPubkey,
|
||||
recent_slot: u64,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
let authority_address = match crate::executor_solana_core_parse_pubkey(authority, "authority") {
|
||||
std::result::Result::Ok(pubkey) => pubkey,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let payer_address = match crate::executor_solana_core_parse_pubkey(payer, "payer") {
|
||||
std::result::Result::Ok(pubkey) => pubkey,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let (instruction, lookup_table_address) =
|
||||
solana_address_lookup_table_interface::instruction::create_lookup_table(
|
||||
authority_address,
|
||||
payer_address,
|
||||
recent_slot,
|
||||
);
|
||||
if lookup_table_address == authority_address || lookup_table_address == payer_address {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_address_lookup_table_derived_address_conflict",
|
||||
"derived lookup table address must differ from its authority and payer",
|
||||
));
|
||||
}
|
||||
return crate::executor_solana_core_finalize_prepared_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_CREATE_OPERATION,
|
||||
vec![instruction],
|
||||
0,
|
||||
std::option::Option::None,
|
||||
vec![(payer.clone(), std::string::String::from("lookup_table_payer"))],
|
||||
);
|
||||
}
|
||||
|
||||
fn build_extend_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
lookup_table: &crate::MdPubkey,
|
||||
authority: &crate::MdPubkey,
|
||||
payer: std::option::Option<&crate::MdPubkey>,
|
||||
new_addresses: &[crate::MdPubkey],
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
if new_addresses.is_empty() {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_address_lookup_table_extend_empty",
|
||||
"Address Lookup Table extension requires at least one address",
|
||||
));
|
||||
}
|
||||
if new_addresses.len()
|
||||
> solana_address_lookup_table_interface::state::LOOKUP_TABLE_MAX_ADDRESSES
|
||||
{
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_address_lookup_table_extend_too_many_addresses",
|
||||
format!(
|
||||
"Address Lookup Table extension cannot contain more than {} addresses",
|
||||
solana_address_lookup_table_interface::state::LOOKUP_TABLE_MAX_ADDRESSES
|
||||
),
|
||||
));
|
||||
}
|
||||
let lookup_table_address =
|
||||
match crate::executor_solana_core_parse_pubkey(lookup_table, "lookup_table") {
|
||||
std::result::Result::Ok(pubkey) => pubkey,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let authority_address = match crate::executor_solana_core_parse_pubkey(authority, "authority") {
|
||||
std::result::Result::Ok(pubkey) => pubkey,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
match crate::executor_solana_core_validate_distinct_pubkeys(
|
||||
lookup_table,
|
||||
authority,
|
||||
"execution_address_lookup_table_authority_is_table",
|
||||
"lookup table account and authority must differ",
|
||||
) {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let payer_address = match payer {
|
||||
std::option::Option::Some(value) => {
|
||||
match crate::executor_solana_core_validate_distinct_pubkeys(
|
||||
lookup_table,
|
||||
value,
|
||||
"execution_address_lookup_table_payer_is_table",
|
||||
"lookup table account and rent payer must differ",
|
||||
) {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match crate::executor_solana_core_parse_pubkey(value, "payer") {
|
||||
std::result::Result::Ok(pubkey) => std::option::Option::Some(pubkey),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
},
|
||||
std::option::Option::None => std::option::Option::None,
|
||||
};
|
||||
let mut parsed_addresses = std::vec::Vec::with_capacity(new_addresses.len());
|
||||
for (index, address) in new_addresses.iter().enumerate() {
|
||||
let parsed = match crate::executor_solana_core_parse_pubkey(
|
||||
address,
|
||||
format!("new_addresses[{index}]").as_str(),
|
||||
) {
|
||||
std::result::Result::Ok(pubkey) => pubkey,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
parsed_addresses.push(parsed);
|
||||
}
|
||||
let instruction = solana_address_lookup_table_interface::instruction::extend_lookup_table(
|
||||
lookup_table_address,
|
||||
authority_address,
|
||||
payer_address,
|
||||
parsed_addresses,
|
||||
);
|
||||
let mut signer_roles =
|
||||
vec![(authority.clone(), std::string::String::from("lookup_table_authority"))];
|
||||
if let std::option::Option::Some(payer_pubkey) = payer {
|
||||
signer_roles.push((payer_pubkey.clone(), std::string::String::from("lookup_table_payer")));
|
||||
}
|
||||
return crate::executor_solana_core_finalize_prepared_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_EXTEND_OPERATION,
|
||||
vec![instruction],
|
||||
0,
|
||||
std::option::Option::None,
|
||||
signer_roles,
|
||||
);
|
||||
}
|
||||
|
||||
fn build_freeze_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
lookup_table: &crate::MdPubkey,
|
||||
authority: &crate::MdPubkey,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
let (lookup_table_address, authority_address) =
|
||||
match parse_authority_pair(lookup_table, authority) {
|
||||
std::result::Result::Ok(pair) => pair,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instruction = solana_address_lookup_table_interface::instruction::freeze_lookup_table(
|
||||
lookup_table_address,
|
||||
authority_address,
|
||||
);
|
||||
return crate::executor_solana_core_finalize_prepared_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_FREEZE_OPERATION,
|
||||
vec![instruction],
|
||||
0,
|
||||
std::option::Option::None,
|
||||
vec![(authority.clone(), std::string::String::from("lookup_table_authority"))],
|
||||
);
|
||||
}
|
||||
|
||||
fn build_deactivate_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
lookup_table: &crate::MdPubkey,
|
||||
authority: &crate::MdPubkey,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
let (lookup_table_address, authority_address) =
|
||||
match parse_authority_pair(lookup_table, authority) {
|
||||
std::result::Result::Ok(pair) => pair,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instruction = solana_address_lookup_table_interface::instruction::deactivate_lookup_table(
|
||||
lookup_table_address,
|
||||
authority_address,
|
||||
);
|
||||
return crate::executor_solana_core_finalize_prepared_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_DEACTIVATE_OPERATION,
|
||||
vec![instruction],
|
||||
0,
|
||||
std::option::Option::None,
|
||||
vec![(authority.clone(), std::string::String::from("lookup_table_authority"))],
|
||||
);
|
||||
}
|
||||
|
||||
fn build_close_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
lookup_table: &crate::MdPubkey,
|
||||
authority: &crate::MdPubkey,
|
||||
recipient: &crate::MdPubkey,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
let (lookup_table_address, authority_address) =
|
||||
match parse_authority_pair(lookup_table, authority) {
|
||||
std::result::Result::Ok(pair) => pair,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
match crate::executor_solana_core_validate_distinct_pubkeys(
|
||||
lookup_table,
|
||||
recipient,
|
||||
"execution_address_lookup_table_close_recipient_is_table",
|
||||
"lookup table account and close recipient must differ",
|
||||
) {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let recipient_address = match crate::executor_solana_core_parse_pubkey(recipient, "recipient") {
|
||||
std::result::Result::Ok(pubkey) => pubkey,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instruction = solana_address_lookup_table_interface::instruction::close_lookup_table(
|
||||
lookup_table_address,
|
||||
authority_address,
|
||||
recipient_address,
|
||||
);
|
||||
return crate::executor_solana_core_finalize_prepared_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_CLOSE_OPERATION,
|
||||
vec![instruction],
|
||||
0,
|
||||
std::option::Option::None,
|
||||
vec![(authority.clone(), std::string::String::from("lookup_table_authority"))],
|
||||
);
|
||||
}
|
||||
|
||||
fn parse_authority_pair(
|
||||
lookup_table: &crate::MdPubkey,
|
||||
authority: &crate::MdPubkey,
|
||||
) -> kb_core::Result<(solana_pubkey::Pubkey, solana_pubkey::Pubkey)> {
|
||||
match crate::executor_solana_core_validate_distinct_pubkeys(
|
||||
lookup_table,
|
||||
authority,
|
||||
"execution_address_lookup_table_authority_is_table",
|
||||
"lookup table account and authority must differ",
|
||||
) {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let lookup_table_address =
|
||||
match crate::executor_solana_core_parse_pubkey(lookup_table, "lookup_table") {
|
||||
std::result::Result::Ok(pubkey) => pubkey,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let authority_address = match crate::executor_solana_core_parse_pubkey(authority, "authority") {
|
||||
std::result::Result::Ok(pubkey) => pubkey,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok((lookup_table_address, authority_address));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr; // rust-rules: trait-import
|
||||
|
||||
fn pubkey(value: &str) -> crate::MdPubkey {
|
||||
return crate::MdPubkey(std::string::String::from(value));
|
||||
}
|
||||
|
||||
fn parsed(value: &str) -> solana_pubkey::Pubkey {
|
||||
return solana_pubkey::Pubkey::from_str(value)
|
||||
.unwrap_or_else(|error| panic!("invalid fixture: {error}"));
|
||||
}
|
||||
|
||||
fn policy(signers: &[&str]) -> crate::ExApiExecutionPolicy {
|
||||
return crate::ExApiExecutionPolicy {
|
||||
cost_limit: crate::ExApiExecutionCostLimit {
|
||||
max_spend_lamports: std::option::Option::Some(10_000_000),
|
||||
max_fee_lamports: std::option::Option::Some(10_000),
|
||||
max_compute_unit_price_micro_lamports: std::option::Option::Some(100),
|
||||
},
|
||||
authorized_signers: signers.iter().map(|value| return pubkey(value)).collect(),
|
||||
..crate::ExApiExecutionPolicy::default()
|
||||
};
|
||||
}
|
||||
|
||||
fn assert_matches_official(
|
||||
plan: &crate::ExApiPreparedExecutionPlan,
|
||||
expected: &solana_instruction::Instruction,
|
||||
) {
|
||||
assert_eq!(plan.instructions.len(), 1);
|
||||
let actual = &plan.instructions[0];
|
||||
assert_eq!(actual.program_id.0, expected.program_id.to_string());
|
||||
assert_eq!(actual.data, expected.data);
|
||||
assert_eq!(actual.accounts.len(), expected.accounts.len());
|
||||
for (actual_account, expected_account) in
|
||||
actual.accounts.iter().zip(expected.accounts.iter())
|
||||
{
|
||||
assert_eq!(actual_account.pubkey.0, expected_account.pubkey.to_string());
|
||||
assert_eq!(actual_account.is_signer, expected_account.is_signer);
|
||||
assert_eq!(actual_account.is_writable, expected_account.is_writable);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_lookup_table_matches_official_builder_and_signers() {
|
||||
let authority = kb_program_ids::VOTE_PROGRAM_ID;
|
||||
let payer = kb_program_ids::STAKE_PROGRAM_ID;
|
||||
let recent_slot = 42;
|
||||
let intent = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("alt-create-1"),
|
||||
fee_payer: pubkey(payer),
|
||||
policy: policy(&[payer]),
|
||||
operation: crate::ExSolanaCoreOperation::AddressLookupTableCreate {
|
||||
authority: pubkey(authority),
|
||||
payer: pubkey(payer),
|
||||
recent_slot,
|
||||
},
|
||||
};
|
||||
let plan = crate::executor_solana_core_builder_build_prepared_plan(&intent)
|
||||
.unwrap_or_else(|error| panic!("unexpected error: {error}"));
|
||||
let (expected, expected_address) =
|
||||
solana_address_lookup_table_interface::instruction::create_lookup_table(
|
||||
parsed(authority),
|
||||
parsed(payer),
|
||||
recent_slot,
|
||||
);
|
||||
assert_matches_official(&plan, &expected);
|
||||
assert_eq!(
|
||||
plan.operation_code,
|
||||
crate::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_CREATE_OPERATION
|
||||
);
|
||||
assert_eq!(plan.instructions[0].accounts[0].pubkey.0, expected_address.to_string());
|
||||
assert_eq!(plan.required_signers.len(), 1);
|
||||
assert_eq!(plan.required_signers[0].pubkey.0, payer);
|
||||
assert!(!plan.instructions[0].accounts[1].is_signer);
|
||||
assert_eq!(plan.requested_spend_lamports, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extend_lookup_table_matches_official_builder_with_optional_payer() {
|
||||
let authority = parsed(kb_program_ids::STAKE_PROGRAM_ID);
|
||||
let payer = parsed(kb_program_ids::VOTE_PROGRAM_ID);
|
||||
let (lookup_table, _bump) =
|
||||
solana_address_lookup_table_interface::instruction::derive_lookup_table_address(
|
||||
&authority, 100,
|
||||
);
|
||||
let addresses = vec![
|
||||
parsed(kb_program_ids::SYSTEM_PROGRAM_ID),
|
||||
parsed(kb_program_ids::CONFIG_PROGRAM_ID),
|
||||
];
|
||||
let authority_text = authority.to_string();
|
||||
let payer_text = payer.to_string();
|
||||
let lookup_table_text = lookup_table.to_string();
|
||||
let new_addresses = addresses
|
||||
.iter()
|
||||
.map(|address| {
|
||||
return pubkey(address.to_string().as_str());
|
||||
})
|
||||
.collect::<std::vec::Vec<_>>();
|
||||
let funded_intent = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("alt-extend-funded"),
|
||||
fee_payer: pubkey(payer_text.as_str()),
|
||||
policy: policy(&[payer_text.as_str(), authority_text.as_str()]),
|
||||
operation: crate::ExSolanaCoreOperation::AddressLookupTableExtend {
|
||||
lookup_table: pubkey(lookup_table_text.as_str()),
|
||||
authority: pubkey(authority_text.as_str()),
|
||||
payer: std::option::Option::Some(pubkey(payer_text.as_str())),
|
||||
new_addresses: new_addresses.clone(),
|
||||
},
|
||||
};
|
||||
let funded_plan = crate::executor_solana_core_builder_build_prepared_plan(&funded_intent)
|
||||
.unwrap_or_else(|error| panic!("unexpected error: {error}"));
|
||||
let funded_expected =
|
||||
solana_address_lookup_table_interface::instruction::extend_lookup_table(
|
||||
lookup_table,
|
||||
authority,
|
||||
std::option::Option::Some(payer),
|
||||
addresses.clone(),
|
||||
);
|
||||
assert_matches_official(&funded_plan, &funded_expected);
|
||||
assert_eq!(funded_plan.required_signers.len(), 2);
|
||||
assert_eq!(
|
||||
funded_plan.operation_code,
|
||||
crate::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_EXTEND_OPERATION
|
||||
);
|
||||
|
||||
let unfunded_intent = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("alt-extend-prefunded"),
|
||||
fee_payer: pubkey(authority_text.as_str()),
|
||||
policy: policy(&[authority_text.as_str()]),
|
||||
operation: crate::ExSolanaCoreOperation::AddressLookupTableExtend {
|
||||
lookup_table: pubkey(lookup_table_text.as_str()),
|
||||
authority: pubkey(authority_text.as_str()),
|
||||
payer: std::option::Option::None,
|
||||
new_addresses,
|
||||
},
|
||||
};
|
||||
let unfunded_plan =
|
||||
crate::executor_solana_core_builder_build_prepared_plan(&unfunded_intent)
|
||||
.unwrap_or_else(|error| panic!("unexpected error: {error}"));
|
||||
let unfunded_expected =
|
||||
solana_address_lookup_table_interface::instruction::extend_lookup_table(
|
||||
lookup_table,
|
||||
authority,
|
||||
std::option::Option::None,
|
||||
addresses,
|
||||
);
|
||||
assert_matches_official(&unfunded_plan, &unfunded_expected);
|
||||
assert_eq!(unfunded_plan.required_signers.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_table_authority_lifecycle_matches_official_builders() {
|
||||
let authority = parsed(kb_program_ids::VOTE_PROGRAM_ID);
|
||||
let recipient = parsed(kb_program_ids::CONFIG_PROGRAM_ID);
|
||||
let (lookup_table, _bump) =
|
||||
solana_address_lookup_table_interface::instruction::derive_lookup_table_address(
|
||||
&authority, 200,
|
||||
);
|
||||
let authority_text = authority.to_string();
|
||||
let recipient_text = recipient.to_string();
|
||||
let lookup_table_text = lookup_table.to_string();
|
||||
let variants = [
|
||||
(
|
||||
crate::ExSolanaCoreOperation::AddressLookupTableFreeze {
|
||||
lookup_table: pubkey(lookup_table_text.as_str()),
|
||||
authority: pubkey(authority_text.as_str()),
|
||||
},
|
||||
crate::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_FREEZE_OPERATION,
|
||||
solana_address_lookup_table_interface::instruction::freeze_lookup_table(
|
||||
lookup_table,
|
||||
authority,
|
||||
),
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreOperation::AddressLookupTableDeactivate {
|
||||
lookup_table: pubkey(lookup_table_text.as_str()),
|
||||
authority: pubkey(authority_text.as_str()),
|
||||
},
|
||||
crate::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_DEACTIVATE_OPERATION,
|
||||
solana_address_lookup_table_interface::instruction::deactivate_lookup_table(
|
||||
lookup_table,
|
||||
authority,
|
||||
),
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreOperation::AddressLookupTableClose {
|
||||
lookup_table: pubkey(lookup_table_text.as_str()),
|
||||
authority: pubkey(authority_text.as_str()),
|
||||
recipient: pubkey(recipient_text.as_str()),
|
||||
},
|
||||
crate::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_CLOSE_OPERATION,
|
||||
solana_address_lookup_table_interface::instruction::close_lookup_table(
|
||||
lookup_table,
|
||||
authority,
|
||||
recipient,
|
||||
),
|
||||
),
|
||||
];
|
||||
for (operation, operation_code, expected) in variants {
|
||||
let intent = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: format!("alt-{operation_code}"),
|
||||
fee_payer: pubkey(authority_text.as_str()),
|
||||
policy: policy(&[authority_text.as_str()]),
|
||||
operation,
|
||||
};
|
||||
let plan = crate::executor_solana_core_builder_build_prepared_plan(&intent)
|
||||
.unwrap_or_else(|error| panic!("unexpected error: {error}"));
|
||||
assert_matches_official(&plan, &expected);
|
||||
assert_eq!(plan.operation_code, operation_code);
|
||||
assert_eq!(plan.required_signers.len(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_table_invalid_contracts_are_rejected() {
|
||||
let authority = parsed(kb_program_ids::VOTE_PROGRAM_ID);
|
||||
let (lookup_table, _bump) =
|
||||
solana_address_lookup_table_interface::instruction::derive_lookup_table_address(
|
||||
&authority, 300,
|
||||
);
|
||||
let authority_text = authority.to_string();
|
||||
let lookup_table_text = lookup_table.to_string();
|
||||
let empty = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("alt-empty"),
|
||||
fee_payer: pubkey(authority_text.as_str()),
|
||||
policy: policy(&[authority_text.as_str()]),
|
||||
operation: crate::ExSolanaCoreOperation::AddressLookupTableExtend {
|
||||
lookup_table: pubkey(lookup_table_text.as_str()),
|
||||
authority: pubkey(authority_text.as_str()),
|
||||
payer: std::option::Option::None,
|
||||
new_addresses: std::vec::Vec::new(),
|
||||
},
|
||||
};
|
||||
let empty_error = crate::executor_solana_core_builder_build_prepared_plan(&empty)
|
||||
.expect_err("empty extension must fail");
|
||||
assert_eq!(empty_error.code(), "execution_address_lookup_table_extend_empty");
|
||||
|
||||
let too_many_addresses = (0
|
||||
..=solana_address_lookup_table_interface::state::LOOKUP_TABLE_MAX_ADDRESSES)
|
||||
.map(|_| {
|
||||
return pubkey(kb_program_ids::SYSTEM_PROGRAM_ID);
|
||||
})
|
||||
.collect::<std::vec::Vec<_>>();
|
||||
let too_many = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("alt-too-many"),
|
||||
fee_payer: pubkey(authority_text.as_str()),
|
||||
policy: policy(&[authority_text.as_str()]),
|
||||
operation: crate::ExSolanaCoreOperation::AddressLookupTableExtend {
|
||||
lookup_table: pubkey(lookup_table_text.as_str()),
|
||||
authority: pubkey(authority_text.as_str()),
|
||||
payer: std::option::Option::None,
|
||||
new_addresses: too_many_addresses,
|
||||
},
|
||||
};
|
||||
let too_many_error = crate::executor_solana_core_builder_build_prepared_plan(&too_many)
|
||||
.expect_err("oversized extension must fail");
|
||||
assert_eq!(
|
||||
too_many_error.code(),
|
||||
"execution_address_lookup_table_extend_too_many_addresses"
|
||||
);
|
||||
|
||||
let same_authority = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("alt-same-authority"),
|
||||
fee_payer: pubkey(lookup_table_text.as_str()),
|
||||
policy: policy(&[lookup_table_text.as_str()]),
|
||||
operation: crate::ExSolanaCoreOperation::AddressLookupTableFreeze {
|
||||
lookup_table: pubkey(lookup_table_text.as_str()),
|
||||
authority: pubkey(lookup_table_text.as_str()),
|
||||
},
|
||||
};
|
||||
let same_authority_error =
|
||||
crate::executor_solana_core_builder_build_prepared_plan(&same_authority)
|
||||
.expect_err("table cannot be its own authority signer");
|
||||
assert_eq!(
|
||||
same_authority_error.code(),
|
||||
"execution_address_lookup_table_authority_is_table"
|
||||
);
|
||||
|
||||
let same_recipient = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("alt-same-recipient"),
|
||||
fee_payer: pubkey(authority_text.as_str()),
|
||||
policy: policy(&[authority_text.as_str()]),
|
||||
operation: crate::ExSolanaCoreOperation::AddressLookupTableClose {
|
||||
lookup_table: pubkey(lookup_table_text.as_str()),
|
||||
authority: pubkey(authority_text.as_str()),
|
||||
recipient: pubkey(lookup_table_text.as_str()),
|
||||
},
|
||||
};
|
||||
let same_recipient_error =
|
||||
crate::executor_solana_core_builder_build_prepared_plan(&same_recipient)
|
||||
.expect_err("table cannot receive its own closed lamports");
|
||||
assert_eq!(
|
||||
same_recipient_error.code(),
|
||||
"execution_address_lookup_table_close_recipient_is_table"
|
||||
);
|
||||
}
|
||||
}
|
||||
2223
kb-lib/src/executor/solana/core/builder.rs
Normal file
2223
kb-lib/src/executor/solana/core/builder.rs
Normal file
File diff suppressed because it is too large
Load Diff
7
kb-lib/src/executor/solana/core/constants.rs
Normal file
7
kb-lib/src/executor/solana/core/constants.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
// file: kb-lib/src/executor/solana/core/constants.rs
|
||||
// version: 1
|
||||
|
||||
//! Local constants for the `kb_executor_solana_core` crate. Program identifiers live in `kb_program_ids`.
|
||||
|
||||
/// Canonical tracing target for this crate.
|
||||
pub(crate) const EX_SOLANA_CORE_TRACING_TARGET: &str = "kb-lib.executor.solana.core";
|
||||
658
kb-lib/src/executor/solana/core/executor.rs
Normal file
658
kb-lib/src/executor/solana/core/executor.rs
Normal file
@@ -0,0 +1,658 @@
|
||||
// file: kb-lib/src/executor/solana/core/executor.rs
|
||||
// version: 1
|
||||
|
||||
//! Exact capability dispatch and typed plan construction for Solana core.
|
||||
|
||||
/// Solana core executor implementation.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ExSolanaCoreExecutor;
|
||||
|
||||
impl crate::ExSolanaCoreExecutor {
|
||||
fn exact_capability(
|
||||
&self,
|
||||
program_id: &crate::MdProgramId,
|
||||
operation_code: &str,
|
||||
) -> crate::ExApiExecutionCapability {
|
||||
if program_id.0.as_str() == kb_program_ids::SYSTEM_PROGRAM_ID {
|
||||
return match operation_code {
|
||||
crate::EX_SOLANA_CORE_SYSTEM_TRANSFER_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SYSTEM_TRANSFER_MANY_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SYSTEM_TRANSFER_WITH_SEED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SYSTEM_CREATE_ACCOUNT_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SYSTEM_CREATE_ACCOUNT_WITH_SEED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SYSTEM_CREATE_ACCOUNT_ALLOW_PREFUND_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SYSTEM_ALLOCATE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SYSTEM_ALLOCATE_WITH_SEED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SYSTEM_ASSIGN_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SYSTEM_ASSIGN_WITH_SEED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SYSTEM_NONCE_CREATE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SYSTEM_NONCE_CREATE_WITH_SEED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SYSTEM_NONCE_INITIALIZE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SYSTEM_NONCE_ADVANCE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SYSTEM_NONCE_AUTHORIZE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SYSTEM_NONCE_WITHDRAW_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SYSTEM_NONCE_UPGRADE_OPERATION => {
|
||||
crate::ExApiExecutionCapability::supported(operation_code)
|
||||
},
|
||||
_ => crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_system_operation_unsupported",
|
||||
format!("System operation {operation_code} is not implemented"),
|
||||
),
|
||||
};
|
||||
}
|
||||
if program_id.0.as_str() == kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID {
|
||||
return match operation_code {
|
||||
crate::EX_SOLANA_CORE_COMPUTE_BUDGET_REQUEST_HEAP_FRAME_OPERATION
|
||||
| crate::EX_SOLANA_CORE_COMPUTE_BUDGET_SET_UNIT_LIMIT_OPERATION
|
||||
| crate::EX_SOLANA_CORE_COMPUTE_BUDGET_SET_UNIT_PRICE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_COMPUTE_BUDGET_SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT_OPERATION => {
|
||||
crate::ExApiExecutionCapability::supported(operation_code)
|
||||
},
|
||||
_ => crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_compute_budget_operation_unsupported",
|
||||
format!("Compute Budget operation {operation_code} is not implemented"),
|
||||
),
|
||||
};
|
||||
}
|
||||
if program_id.0.as_str() == kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID {
|
||||
return match operation_code {
|
||||
crate::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_CREATE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_EXTEND_OPERATION
|
||||
| crate::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_FREEZE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_DEACTIVATE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_CLOSE_OPERATION => {
|
||||
crate::ExApiExecutionCapability::supported(operation_code)
|
||||
},
|
||||
_ => crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_address_lookup_table_operation_unsupported",
|
||||
format!("Address Lookup Table operation {operation_code} is not implemented"),
|
||||
),
|
||||
};
|
||||
}
|
||||
if program_id.0.as_str() == kb_program_ids::ED25519_PROGRAM_ID {
|
||||
return match operation_code {
|
||||
crate::EX_SOLANA_CORE_ED25519_VERIFY_INLINE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_ED25519_VERIFY_OFFSETS_OPERATION => {
|
||||
crate::ExApiExecutionCapability::supported(operation_code)
|
||||
},
|
||||
_ => crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_ed25519_operation_unsupported",
|
||||
format!("Ed25519 operation {operation_code} is not implemented"),
|
||||
),
|
||||
};
|
||||
}
|
||||
if program_id.0.as_str() == kb_program_ids::SECP256K1_PROGRAM_ID {
|
||||
return match operation_code {
|
||||
crate::EX_SOLANA_CORE_SECP256K1_VERIFY_INLINE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SECP256K1_VERIFY_OFFSETS_OPERATION => {
|
||||
crate::ExApiExecutionCapability::supported(operation_code)
|
||||
},
|
||||
_ => crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_secp256k1_operation_unsupported",
|
||||
format!("secp256k1 operation {operation_code} is not implemented"),
|
||||
),
|
||||
};
|
||||
}
|
||||
if program_id.0.as_str() == kb_program_ids::SECP256R1_PROGRAM_ID {
|
||||
return match operation_code {
|
||||
crate::EX_SOLANA_CORE_SECP256R1_VERIFY_INLINE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SECP256R1_VERIFY_OFFSETS_OPERATION => {
|
||||
crate::ExApiExecutionCapability::supported(operation_code)
|
||||
},
|
||||
_ => crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_secp256r1_operation_unsupported",
|
||||
format!("secp256r1 operation {operation_code} is not implemented"),
|
||||
),
|
||||
};
|
||||
}
|
||||
if program_id.0.as_str() == kb_program_ids::STAKE_PROGRAM_ID {
|
||||
return match operation_code {
|
||||
crate::EX_SOLANA_CORE_STAKE_INITIALIZE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_INITIALIZE_CHECKED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_CREATE_ACCOUNT_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_CREATE_ACCOUNT_WITH_SEED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_CREATE_ACCOUNT_CHECKED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_CREATE_ACCOUNT_WITH_SEED_CHECKED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_SPLIT_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_SPLIT_WITH_SEED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_MERGE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_CREATE_ACCOUNT_AND_DELEGATE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_CREATE_ACCOUNT_WITH_SEED_AND_DELEGATE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_AUTHORIZE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_AUTHORIZE_CHECKED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_AUTHORIZE_WITH_SEED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_AUTHORIZE_CHECKED_WITH_SEED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_DELEGATE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_WITHDRAW_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_DEACTIVATE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_SET_LOCKUP_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_SET_LOCKUP_CHECKED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_GET_MINIMUM_DELEGATION_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_DEACTIVATE_DELINQUENT_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_MOVE_STAKE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_STAKE_MOVE_LAMPORTS_OPERATION => {
|
||||
crate::ExApiExecutionCapability::supported(operation_code)
|
||||
},
|
||||
_ => crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_stake_operation_unsupported",
|
||||
format!("Stake operation {operation_code} is not implemented"),
|
||||
),
|
||||
};
|
||||
}
|
||||
if program_id.0.as_str() == kb_program_ids::VOTE_PROGRAM_ID {
|
||||
return match operation_code {
|
||||
crate::EX_SOLANA_CORE_VOTE_INITIALIZE_ACCOUNT_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_INITIALIZE_ACCOUNT_V2_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_CREATE_ACCOUNT_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_CREATE_ACCOUNT_WITH_SEED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_CREATE_ACCOUNT_V2_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_CREATE_ACCOUNT_WITH_SEED_V2_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_AUTHORIZE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_AUTHORIZE_CHECKED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_AUTHORIZE_WITH_SEED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_AUTHORIZE_CHECKED_WITH_SEED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_SUBMIT_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_SWITCH_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_WITHDRAW_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_UPDATE_VALIDATOR_IDENTITY_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_UPDATE_COMMISSION_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_UPDATE_VOTE_STATE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_UPDATE_VOTE_STATE_SWITCH_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_COMPACT_UPDATE_VOTE_STATE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_COMPACT_UPDATE_VOTE_STATE_SWITCH_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_TOWER_SYNC_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_TOWER_SYNC_SWITCH_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_UPDATE_COMMISSION_COLLECTOR_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_UPDATE_COMMISSION_BPS_OPERATION
|
||||
| crate::EX_SOLANA_CORE_VOTE_DEPOSIT_DELEGATOR_REWARDS_OPERATION => {
|
||||
crate::ExApiExecutionCapability::supported(operation_code)
|
||||
},
|
||||
_ => crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_vote_operation_unsupported",
|
||||
format!("Vote operation {operation_code} is not implemented"),
|
||||
),
|
||||
};
|
||||
}
|
||||
if program_id.0.as_str() == kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID {
|
||||
return match operation_code {
|
||||
crate::EX_SOLANA_CORE_LOADER_V3_CREATE_BUFFER_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V3_WRITE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V3_DEPLOY_WITH_MAX_PROGRAM_LEN_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V3_UPGRADE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V3_SET_BUFFER_AUTHORITY_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V3_SET_BUFFER_AUTHORITY_CHECKED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V3_SET_UPGRADE_AUTHORITY_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V3_SET_UPGRADE_AUTHORITY_CHECKED_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V3_CLOSE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V3_CLOSE_ANY_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V3_EXTEND_PROGRAM_OPERATION => {
|
||||
crate::ExApiExecutionCapability::supported(operation_code)
|
||||
},
|
||||
_ => crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_loader_v3_operation_unsupported",
|
||||
format!("Loader v3 operation {operation_code} is not implemented"),
|
||||
),
|
||||
};
|
||||
}
|
||||
if program_id.0.as_str() == kb_program_ids::BPF_LOADER_DEPRECATED_PROGRAM_ID
|
||||
|| program_id.0.as_str() == kb_program_ids::BPF_LOADER_PROGRAM_ID
|
||||
{
|
||||
return crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_legacy_bpf_loader_management_disabled",
|
||||
"BPF Loader v1/v2 management is historical and disabled on current clusters; the surfaces remain decode-only",
|
||||
);
|
||||
}
|
||||
if program_id.0.as_str() == kb_program_ids::LOADER_V4_PROGRAM_ID {
|
||||
return match operation_code {
|
||||
crate::EX_SOLANA_CORE_LOADER_V4_CREATE_BUFFER_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V4_SET_PROGRAM_LENGTH_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V4_WRITE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V4_COPY_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V4_DEPLOY_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V4_DEPLOY_FROM_SOURCE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V4_RETRACT_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V4_TRANSFER_AUTHORITY_OPERATION
|
||||
| crate::EX_SOLANA_CORE_LOADER_V4_FINALIZE_OPERATION => {
|
||||
crate::ExApiExecutionCapability::supported(operation_code)
|
||||
},
|
||||
_ => crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_loader_v4_operation_unsupported",
|
||||
format!("Loader v4 operation {operation_code} is not implemented"),
|
||||
),
|
||||
};
|
||||
}
|
||||
if program_id.0.as_str() == kb_program_ids::NATIVE_LOADER_PROGRAM_ID {
|
||||
return crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_native_loader_validator_only",
|
||||
"Native Loader deployment is a validator software upgrade path and has no client instruction builder",
|
||||
);
|
||||
}
|
||||
if program_id.0.as_str() == kb_program_ids::CONFIG_PROGRAM_ID {
|
||||
return match operation_code {
|
||||
crate::EX_SOLANA_CORE_CONFIG_CREATE_ACCOUNT_OPERATION
|
||||
| crate::EX_SOLANA_CORE_CONFIG_STORE_OPERATION => {
|
||||
crate::ExApiExecutionCapability::supported(operation_code)
|
||||
},
|
||||
_ => crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_config_operation_unsupported",
|
||||
format!("Config operation {operation_code} is not implemented"),
|
||||
),
|
||||
};
|
||||
}
|
||||
if program_id.0.as_str() == kb_program_ids::FEATURE_PROGRAM_ID {
|
||||
return match operation_code {
|
||||
crate::EX_SOLANA_CORE_FEATURE_ACTIVATE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_FEATURE_REVOKE_PENDING_ACTIVATION_OPERATION => {
|
||||
crate::ExApiExecutionCapability::supported(operation_code)
|
||||
},
|
||||
_ => crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_feature_operation_unsupported",
|
||||
format!("Feature operation {operation_code} is not implemented"),
|
||||
),
|
||||
};
|
||||
}
|
||||
if program_id.0.as_str() == kb_program_ids::SLASHING_PROGRAM_ID {
|
||||
return match operation_code {
|
||||
crate::EX_SOLANA_CORE_SLASHING_CLOSE_VIOLATION_REPORT_OPERATION
|
||||
| crate::EX_SOLANA_CORE_SLASHING_SUBMIT_DUPLICATE_BLOCK_PROOF_OPERATION => {
|
||||
crate::ExApiExecutionCapability::supported(operation_code)
|
||||
},
|
||||
_ => crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_slashing_operation_unsupported",
|
||||
format!("Slashing operation {operation_code} is not implemented"),
|
||||
),
|
||||
};
|
||||
}
|
||||
if program_id.0.as_str() == kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID {
|
||||
return match operation_code {
|
||||
crate::EX_SOLANA_CORE_ZK_ELGAMAL_VERIFY_INLINE_OPERATION
|
||||
| crate::EX_SOLANA_CORE_ZK_ELGAMAL_VERIFY_FROM_ACCOUNT_OPERATION
|
||||
| crate::EX_SOLANA_CORE_ZK_ELGAMAL_CLOSE_CONTEXT_STATE_OPERATION => {
|
||||
crate::ExApiExecutionCapability::supported(operation_code)
|
||||
},
|
||||
_ => crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_zk_elgamal_operation_unsupported",
|
||||
format!("ZK ElGamal operation {operation_code} is not implemented"),
|
||||
),
|
||||
};
|
||||
}
|
||||
if program_id.0.as_str() == kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID {
|
||||
return crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_historical_zk_token_proof_decode_only",
|
||||
"the historical ZK Token Proof program is retained for decoding only and has no current callable runtime surface",
|
||||
);
|
||||
}
|
||||
if crate::ExApiInstructionExecutor::handles_program_id(self, program_id) {
|
||||
return crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_native_program_deferred",
|
||||
format!(
|
||||
"program {} is classified but intentionally has no executable operation in 0.4.2",
|
||||
program_id.0
|
||||
),
|
||||
);
|
||||
}
|
||||
return crate::ExApiExecutionCapability::unsupported(
|
||||
"execution_program_not_owned",
|
||||
format!("program {} is not owned by kb_executor_solana_core", program_id.0),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::ExApiTypedInstructionExecutor for crate::ExSolanaCoreExecutor {
|
||||
type Intent = crate::ExSolanaCoreExecutionIntent;
|
||||
|
||||
fn capability(
|
||||
&self,
|
||||
program_id: &crate::MdProgramId,
|
||||
operation_code: &str,
|
||||
) -> crate::ExApiExecutionCapability {
|
||||
return self.exact_capability(program_id, operation_code);
|
||||
}
|
||||
|
||||
fn build_prepared_plan(
|
||||
&self,
|
||||
intent: &Self::Intent,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
let program_id =
|
||||
crate::MdProgramId(std::string::String::from(intent.operation.program_id()));
|
||||
return match self.exact_capability(&program_id, intent.operation.operation_code()) {
|
||||
crate::ExApiExecutionCapability::Supported { operation_code: _ } => {
|
||||
crate::executor_solana_core_builder_build_prepared_plan(intent)
|
||||
},
|
||||
crate::ExApiExecutionCapability::Unsupported { reason_code, reason } => {
|
||||
std::result::Result::Err(kb_core::Error::new(reason_code, reason))
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::ExApiInstructionExecutor for crate::ExSolanaCoreExecutor {
|
||||
fn executor_name(&self) -> &'static str {
|
||||
return "kb_executor_solana_core";
|
||||
}
|
||||
|
||||
fn executor_version(&self) -> &'static str {
|
||||
return env!("CARGO_PKG_VERSION");
|
||||
}
|
||||
|
||||
fn program_ids(&self) -> &'static [&'static str] {
|
||||
return &[
|
||||
kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID,
|
||||
kb_program_ids::BPF_LOADER_DEPRECATED_PROGRAM_ID,
|
||||
kb_program_ids::BPF_LOADER_PROGRAM_ID,
|
||||
kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID,
|
||||
kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID,
|
||||
kb_program_ids::CONFIG_PROGRAM_ID,
|
||||
kb_program_ids::ED25519_PROGRAM_ID,
|
||||
kb_program_ids::FEATURE_PROGRAM_ID,
|
||||
kb_program_ids::LOADER_V4_PROGRAM_ID,
|
||||
kb_program_ids::NATIVE_LOADER_PROGRAM_ID,
|
||||
kb_program_ids::SLASHING_PROGRAM_ID,
|
||||
kb_program_ids::SECP256K1_PROGRAM_ID,
|
||||
kb_program_ids::SECP256R1_PROGRAM_ID,
|
||||
kb_program_ids::STAKE_PROGRAM_ID,
|
||||
kb_program_ids::SYSTEM_PROGRAM_ID,
|
||||
kb_program_ids::VOTE_PROGRAM_ID,
|
||||
kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID,
|
||||
kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID,
|
||||
];
|
||||
}
|
||||
|
||||
fn supports_request(
|
||||
&self,
|
||||
request: &crate::ExApiExecutionRequest,
|
||||
) -> crate::ExApiExecutionSupport {
|
||||
return match self.exact_capability(&request.program_id, &request.operation_code) {
|
||||
crate::ExApiExecutionCapability::Supported { operation_code: _ } => {
|
||||
crate::ExApiExecutionSupport::Yes
|
||||
},
|
||||
crate::ExApiExecutionCapability::Unsupported { reason_code: _, reason: _ } => {
|
||||
crate::ExApiExecutionSupport::No
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn build_plan(
|
||||
&self,
|
||||
request: &crate::ExApiExecutionRequest,
|
||||
) -> kb_core::Result<crate::ExApiExecutionPlan> {
|
||||
match self.exact_capability(&request.program_id, &request.operation_code) {
|
||||
crate::ExApiExecutionCapability::Supported { operation_code: _ } => {},
|
||||
crate::ExApiExecutionCapability::Unsupported { reason_code, reason } => {
|
||||
return std::result::Result::Err(kb_core::Error::new(reason_code, reason));
|
||||
},
|
||||
}
|
||||
let intent =
|
||||
match serde_json::from_str::<crate::ExSolanaCoreExecutionIntent>(&request.payload_json)
|
||||
{
|
||||
std::result::Result::Ok(intent) => intent,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_solana_core_intent_deserialize_failed",
|
||||
error.to_string(),
|
||||
));
|
||||
},
|
||||
};
|
||||
if intent.operation.operation_code() != request.operation_code.as_str() {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_operation_code_mismatch",
|
||||
format!(
|
||||
"request operation {} does not match typed intent operation {}",
|
||||
request.operation_code,
|
||||
intent.operation.operation_code()
|
||||
),
|
||||
));
|
||||
}
|
||||
if intent.operation.program_id() != request.program_id.0.as_str() {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_program_id_mismatch",
|
||||
format!(
|
||||
"request program {} does not match typed intent program {}",
|
||||
request.program_id.0,
|
||||
intent.operation.program_id()
|
||||
),
|
||||
));
|
||||
}
|
||||
let prepared =
|
||||
match crate::ExApiTypedInstructionExecutor::build_prepared_plan(self, &intent) {
|
||||
std::result::Result::Ok(plan) => plan,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let payload_json = match serde_json::to_value(&prepared) {
|
||||
std::result::Result::Ok(value) => {
|
||||
match crate::executor_api_serialize_payload_json(&value) {
|
||||
std::result::Result::Ok(serialized) => serialized,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(error);
|
||||
},
|
||||
}
|
||||
},
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_solana_core_plan_serialize_failed",
|
||||
error.to_string(),
|
||||
));
|
||||
},
|
||||
};
|
||||
return std::result::Result::Ok(crate::ExApiExecutionPlan {
|
||||
executor_name: std::string::String::from("kb_executor_solana_core"),
|
||||
instruction_count: prepared.instructions.len(),
|
||||
payload_json,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn executor_programs_match_native_registry_without_spl_memo() {
|
||||
let executor = crate::ExSolanaCoreExecutor;
|
||||
let executor_ids = crate::ExApiInstructionExecutor::program_ids(&executor);
|
||||
let native_ids = kb_program_ids::native_program_ids()
|
||||
.iter()
|
||||
.map(|entry| return entry.program_id())
|
||||
.collect::<std::collections::BTreeSet<_>>();
|
||||
let actual_ids = executor_ids.iter().copied().collect::<std::collections::BTreeSet<_>>();
|
||||
assert_eq!(actual_ids, native_ids);
|
||||
assert!(!actual_ids.contains(kb_program_ids::SPL_MEMO_V1_PROGRAM_ID));
|
||||
assert!(!actual_ids.contains(kb_program_ids::SPL_MEMO_V3_PROGRAM_ID));
|
||||
assert!(!actual_ids.contains(kb_program_ids::SPL_MEMO_V4_PROGRAM_ID));
|
||||
assert!(!actual_ids.contains(kb_program_ids::STAKE_CONFIG_ACCOUNT_ID));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn capabilities_are_exact_and_never_maybe() {
|
||||
let executor = crate::ExSolanaCoreExecutor;
|
||||
let transfer = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(kb_program_ids::SYSTEM_PROGRAM_ID)),
|
||||
crate::EX_SOLANA_CORE_SYSTEM_TRANSFER_OPERATION,
|
||||
);
|
||||
assert!(transfer.is_supported());
|
||||
let seeded_transfer = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(kb_program_ids::SYSTEM_PROGRAM_ID)),
|
||||
crate::EX_SOLANA_CORE_SYSTEM_TRANSFER_WITH_SEED_OPERATION,
|
||||
);
|
||||
assert!(seeded_transfer.is_supported());
|
||||
let prefund = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(kb_program_ids::SYSTEM_PROGRAM_ID)),
|
||||
crate::EX_SOLANA_CORE_SYSTEM_CREATE_ACCOUNT_ALLOW_PREFUND_OPERATION,
|
||||
);
|
||||
assert!(prefund.is_supported());
|
||||
let nonce = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(kb_program_ids::SYSTEM_PROGRAM_ID)),
|
||||
crate::EX_SOLANA_CORE_SYSTEM_NONCE_ADVANCE_OPERATION,
|
||||
);
|
||||
assert!(nonce.is_supported());
|
||||
let transfer_many = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(kb_program_ids::SYSTEM_PROGRAM_ID)),
|
||||
crate::EX_SOLANA_CORE_SYSTEM_TRANSFER_MANY_OPERATION,
|
||||
);
|
||||
assert!(transfer_many.is_supported());
|
||||
let heap_frame = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(
|
||||
kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID,
|
||||
)),
|
||||
crate::EX_SOLANA_CORE_COMPUTE_BUDGET_REQUEST_HEAP_FRAME_OPERATION,
|
||||
);
|
||||
assert!(heap_frame.is_supported());
|
||||
let loaded_accounts_data_size = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(
|
||||
kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID,
|
||||
)),
|
||||
crate::EX_SOLANA_CORE_COMPUTE_BUDGET_SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT_OPERATION,
|
||||
);
|
||||
assert!(loaded_accounts_data_size.is_supported());
|
||||
let lookup_table_create = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(
|
||||
kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID,
|
||||
)),
|
||||
crate::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_CREATE_OPERATION,
|
||||
);
|
||||
assert!(lookup_table_create.is_supported());
|
||||
let ed25519 = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(kb_program_ids::ED25519_PROGRAM_ID)),
|
||||
crate::EX_SOLANA_CORE_ED25519_VERIFY_INLINE_OPERATION,
|
||||
);
|
||||
assert!(ed25519.is_supported());
|
||||
let secp256k1 = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(kb_program_ids::SECP256K1_PROGRAM_ID)),
|
||||
crate::EX_SOLANA_CORE_SECP256K1_VERIFY_OFFSETS_OPERATION,
|
||||
);
|
||||
assert!(secp256k1.is_supported());
|
||||
let secp256r1 = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(kb_program_ids::SECP256R1_PROGRAM_ID)),
|
||||
crate::EX_SOLANA_CORE_SECP256R1_VERIFY_INLINE_OPERATION,
|
||||
);
|
||||
assert!(secp256r1.is_supported());
|
||||
let config = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(kb_program_ids::CONFIG_PROGRAM_ID)),
|
||||
crate::EX_SOLANA_CORE_CONFIG_STORE_OPERATION,
|
||||
);
|
||||
assert!(config.is_supported());
|
||||
let feature = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(kb_program_ids::FEATURE_PROGRAM_ID)),
|
||||
crate::EX_SOLANA_CORE_FEATURE_ACTIVATE_OPERATION,
|
||||
);
|
||||
assert!(feature.is_supported());
|
||||
let slashing = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(kb_program_ids::SLASHING_PROGRAM_ID)),
|
||||
crate::EX_SOLANA_CORE_SLASHING_SUBMIT_DUPLICATE_BLOCK_PROOF_OPERATION,
|
||||
);
|
||||
assert!(slashing.is_supported());
|
||||
let zk_elgamal = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(
|
||||
kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID,
|
||||
)),
|
||||
crate::EX_SOLANA_CORE_ZK_ELGAMAL_VERIFY_INLINE_OPERATION,
|
||||
);
|
||||
assert!(zk_elgamal.is_supported());
|
||||
let historical_zk = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(
|
||||
kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID,
|
||||
)),
|
||||
"solana_core.zk_token.verify_zero_balance",
|
||||
);
|
||||
assert!(matches!(
|
||||
historical_zk,
|
||||
crate::ExApiExecutionCapability::Unsupported { reason_code, reason: _ }
|
||||
if reason_code == "execution_historical_zk_token_proof_decode_only"
|
||||
));
|
||||
let stake = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(kb_program_ids::STAKE_PROGRAM_ID)),
|
||||
crate::EX_SOLANA_CORE_STAKE_DELEGATE_OPERATION,
|
||||
);
|
||||
assert!(stake.is_supported());
|
||||
let vote = crate::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&crate::MdProgramId(std::string::String::from(kb_program_ids::VOTE_PROGRAM_ID)),
|
||||
crate::EX_SOLANA_CORE_VOTE_TOWER_SYNC_SWITCH_OPERATION,
|
||||
);
|
||||
assert!(vote.is_supported());
|
||||
let legacy_supported = crate::ExApiInstructionExecutor::supports_request(
|
||||
&executor,
|
||||
&crate::ExApiExecutionRequest {
|
||||
program_id: crate::MdProgramId(std::string::String::from(
|
||||
kb_program_ids::SYSTEM_PROGRAM_ID,
|
||||
)),
|
||||
operation_code: std::string::String::from(
|
||||
crate::EX_SOLANA_CORE_SYSTEM_TRANSFER_OPERATION,
|
||||
),
|
||||
payload_json: std::string::String::new(),
|
||||
},
|
||||
);
|
||||
assert_eq!(legacy_supported, crate::ExApiExecutionSupport::Yes);
|
||||
let legacy_nonce = crate::ExApiInstructionExecutor::supports_request(
|
||||
&executor,
|
||||
&crate::ExApiExecutionRequest {
|
||||
program_id: crate::MdProgramId(std::string::String::from(
|
||||
kb_program_ids::SYSTEM_PROGRAM_ID,
|
||||
)),
|
||||
operation_code: std::string::String::from(
|
||||
crate::EX_SOLANA_CORE_SYSTEM_NONCE_ADVANCE_OPERATION,
|
||||
),
|
||||
payload_json: std::string::String::new(),
|
||||
},
|
||||
);
|
||||
assert_eq!(legacy_nonce, crate::ExApiExecutionSupport::Yes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_request_builds_the_same_typed_plan_payload() {
|
||||
let payer = crate::MdPubkey(std::string::String::from(kb_program_ids::VOTE_PROGRAM_ID));
|
||||
let intent = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("legacy-transfer"),
|
||||
fee_payer: payer.clone(),
|
||||
policy: crate::ExApiExecutionPolicy {
|
||||
cost_limit: crate::ExApiExecutionCostLimit {
|
||||
max_spend_lamports: std::option::Option::Some(10),
|
||||
max_fee_lamports: std::option::Option::Some(10_000),
|
||||
max_compute_unit_price_micro_lamports: std::option::Option::Some(1),
|
||||
},
|
||||
authorized_signers: vec![payer.clone()],
|
||||
..crate::ExApiExecutionPolicy::default()
|
||||
},
|
||||
operation: crate::ExSolanaCoreOperation::SystemTransfer {
|
||||
from: payer,
|
||||
to: crate::MdPubkey(std::string::String::from(kb_program_ids::STAKE_PROGRAM_ID)),
|
||||
lamports: 10,
|
||||
},
|
||||
};
|
||||
let payload_json = serde_json::to_string(&intent)
|
||||
.unwrap_or_else(|error| panic!("unexpected serialization error: {error}"));
|
||||
let plan = crate::ExApiInstructionExecutor::build_plan(
|
||||
&crate::ExSolanaCoreExecutor,
|
||||
&crate::ExApiExecutionRequest {
|
||||
program_id: crate::MdProgramId(std::string::String::from(
|
||||
kb_program_ids::SYSTEM_PROGRAM_ID,
|
||||
)),
|
||||
operation_code: std::string::String::from(
|
||||
crate::EX_SOLANA_CORE_SYSTEM_TRANSFER_OPERATION,
|
||||
),
|
||||
payload_json,
|
||||
},
|
||||
)
|
||||
.unwrap_or_else(|error| panic!("unexpected build error: {error}"));
|
||||
assert_eq!(plan.instruction_count, 1);
|
||||
let typed: crate::ExApiPreparedExecutionPlan = serde_json::from_str(&plan.payload_json)
|
||||
.unwrap_or_else(|error| panic!("unexpected plan payload: {error}"));
|
||||
assert_eq!(typed.intent_id, "legacy-transfer");
|
||||
assert_eq!(typed.instructions.len(), 1);
|
||||
}
|
||||
}
|
||||
2604
kb-lib/src/executor/solana/core/intent.rs
Normal file
2604
kb-lib/src/executor/solana/core/intent.rs
Normal file
File diff suppressed because it is too large
Load Diff
1139
kb-lib/src/executor/solana/core/loader_v3.rs
Normal file
1139
kb-lib/src/executor/solana/core/loader_v3.rs
Normal file
File diff suppressed because it is too large
Load Diff
890
kb-lib/src/executor/solana/core/loader_v4.rs
Normal file
890
kb-lib/src/executor/solana/core/loader_v4.rs
Normal file
@@ -0,0 +1,890 @@
|
||||
// file: kb-lib/src/executor/solana/core/loader_v4.rs
|
||||
// version: 1
|
||||
|
||||
//! Exact Loader v4 client plans without activating the interface's `bincode` helpers.
|
||||
|
||||
#[cfg(test)]
|
||||
const SOURCE: &str = "solana-loader-v4-interface@3.1.0";
|
||||
const MAX_WRITE_BYTES: usize = 1_024;
|
||||
const WRITE_TAG: u32 = 0;
|
||||
const COPY_TAG: u32 = 1;
|
||||
const SET_PROGRAM_LENGTH_TAG: u32 = 2;
|
||||
const DEPLOY_TAG: u32 = 3;
|
||||
const RETRACT_TAG: u32 = 4;
|
||||
const TRANSFER_AUTHORITY_TAG: u32 = 5;
|
||||
const FINALIZE_TAG: u32 = 6;
|
||||
|
||||
pub(crate) fn executor_solana_core_loader_v4_build_prepared_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
return match &intent.operation {
|
||||
crate::ExSolanaCoreOperation::LoaderV4CreateBuffer {
|
||||
payer,
|
||||
buffer,
|
||||
authority,
|
||||
recipient,
|
||||
lamports,
|
||||
new_size,
|
||||
} => build_create_buffer_plan(
|
||||
intent, fee_payer, payer, buffer, authority, recipient, *lamports, *new_size,
|
||||
),
|
||||
crate::ExSolanaCoreOperation::LoaderV4SetProgramLength {
|
||||
program,
|
||||
authority,
|
||||
recipient,
|
||||
new_size,
|
||||
} => build_set_program_length_plan(
|
||||
intent, fee_payer, program, authority, recipient, *new_size,
|
||||
),
|
||||
crate::ExSolanaCoreOperation::LoaderV4Write { program, authority, offset, bytes } => {
|
||||
build_write_plan(intent, fee_payer, program, authority, *offset, bytes.as_slice())
|
||||
},
|
||||
crate::ExSolanaCoreOperation::LoaderV4Copy {
|
||||
program,
|
||||
authority,
|
||||
source_program,
|
||||
destination_offset,
|
||||
source_offset,
|
||||
length,
|
||||
} => build_copy_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
program,
|
||||
authority,
|
||||
source_program,
|
||||
*destination_offset,
|
||||
*source_offset,
|
||||
*length,
|
||||
),
|
||||
crate::ExSolanaCoreOperation::LoaderV4Deploy { program, authority } => {
|
||||
build_authority_unit_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_LOADER_V4_DEPLOY_OPERATION,
|
||||
program,
|
||||
authority,
|
||||
DEPLOY_TAG,
|
||||
)
|
||||
},
|
||||
crate::ExSolanaCoreOperation::LoaderV4DeployFromSource {
|
||||
program,
|
||||
authority,
|
||||
source_program,
|
||||
} => build_deploy_from_source_plan(intent, fee_payer, program, authority, source_program),
|
||||
crate::ExSolanaCoreOperation::LoaderV4Retract { program, authority } => {
|
||||
build_authority_unit_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_LOADER_V4_RETRACT_OPERATION,
|
||||
program,
|
||||
authority,
|
||||
RETRACT_TAG,
|
||||
)
|
||||
},
|
||||
crate::ExSolanaCoreOperation::LoaderV4TransferAuthority {
|
||||
program,
|
||||
current_authority,
|
||||
new_authority,
|
||||
} => build_transfer_authority_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
program,
|
||||
current_authority,
|
||||
new_authority,
|
||||
),
|
||||
crate::ExSolanaCoreOperation::LoaderV4Finalize {
|
||||
program,
|
||||
authority,
|
||||
next_version_program,
|
||||
} => build_finalize_plan(intent, fee_payer, program, authority, next_version_program),
|
||||
_ => std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_loader_v4_operation_dispatch_invalid",
|
||||
format!(
|
||||
"operation {} was dispatched to the Loader v4 builder",
|
||||
intent.operation.operation_code()
|
||||
),
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
fn build_create_buffer_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
payer: &crate::MdPubkey,
|
||||
buffer: &crate::MdPubkey,
|
||||
authority: &crate::MdPubkey,
|
||||
recipient: &crate::MdPubkey,
|
||||
lamports: u64,
|
||||
new_size: u32,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
if lamports == 0 {
|
||||
return crate::executor_solana_core_invalid(
|
||||
"execution_loader_v4_buffer_lamports_zero",
|
||||
"Loader v4 buffer creation requires positive lamports",
|
||||
);
|
||||
}
|
||||
if new_size == 0 {
|
||||
return crate::executor_solana_core_invalid(
|
||||
"execution_loader_v4_buffer_size_zero",
|
||||
"Loader v4 buffer creation requires a positive initial size",
|
||||
);
|
||||
}
|
||||
match validate_size(new_size) {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match distinct(payer, buffer, "payer", "buffer") {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match distinct(buffer, recipient, "buffer", "recipient") {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let payer_address = match crate::executor_solana_core_parse_pubkey(payer, "payer") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let buffer_address = match crate::executor_solana_core_parse_pubkey(buffer, "buffer") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let authority_address = match crate::executor_solana_core_parse_pubkey(authority, "authority") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let recipient_address = match crate::executor_solana_core_parse_pubkey(recipient, "recipient") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instructions = vec![
|
||||
solana_system_interface::instruction::create_account(
|
||||
&payer_address,
|
||||
&buffer_address,
|
||||
lamports,
|
||||
0,
|
||||
&solana_sdk_ids::loader_v4::ID,
|
||||
),
|
||||
set_program_length_instruction(
|
||||
&buffer_address,
|
||||
&authority_address,
|
||||
&recipient_address,
|
||||
new_size,
|
||||
),
|
||||
];
|
||||
return crate::executor_solana_core_finalize_prepared_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_LOADER_V4_CREATE_BUFFER_OPERATION,
|
||||
instructions,
|
||||
lamports,
|
||||
std::option::Option::None,
|
||||
vec![
|
||||
(payer.clone(), std::string::String::from("buffer_payer")),
|
||||
(buffer.clone(), std::string::String::from("new_buffer_account")),
|
||||
(authority.clone(), std::string::String::from("program_authority")),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
fn build_set_program_length_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
program: &crate::MdPubkey,
|
||||
authority: &crate::MdPubkey,
|
||||
recipient: &crate::MdPubkey,
|
||||
new_size: u32,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
match validate_size(new_size) {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match distinct(program, recipient, "program", "recipient") {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let program_address = match crate::executor_solana_core_parse_pubkey(program, "program") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let authority_address = match crate::executor_solana_core_parse_pubkey(authority, "authority") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let recipient_address = match crate::executor_solana_core_parse_pubkey(recipient, "recipient") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instruction = set_program_length_instruction(
|
||||
&program_address,
|
||||
&authority_address,
|
||||
&recipient_address,
|
||||
new_size,
|
||||
);
|
||||
return finalize_single(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_LOADER_V4_SET_PROGRAM_LENGTH_OPERATION,
|
||||
instruction,
|
||||
vec![(authority.clone(), std::string::String::from("program_authority"))],
|
||||
);
|
||||
}
|
||||
|
||||
fn build_write_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
program: &crate::MdPubkey,
|
||||
authority: &crate::MdPubkey,
|
||||
offset: u32,
|
||||
bytes: &[u8],
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
if bytes.is_empty() || bytes.len() > MAX_WRITE_BYTES {
|
||||
return crate::executor_solana_core_invalid(
|
||||
"execution_loader_v4_write_size_invalid",
|
||||
format!("Loader v4 write payload must contain between 1 and {} bytes", MAX_WRITE_BYTES),
|
||||
);
|
||||
}
|
||||
let byte_length = match u32::try_from(bytes.len()) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_error) => {
|
||||
return crate::executor_solana_core_invalid(
|
||||
"execution_loader_v4_write_size_overflow",
|
||||
"Loader v4 write payload length does not fit u32",
|
||||
);
|
||||
},
|
||||
};
|
||||
if offset.checked_add(byte_length).is_none() {
|
||||
return crate::executor_solana_core_invalid(
|
||||
"execution_loader_v4_write_range_overflow",
|
||||
"Loader v4 write offset plus payload length exceeds u32",
|
||||
);
|
||||
}
|
||||
let program_address = match crate::executor_solana_core_parse_pubkey(program, "program") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let authority_address = match crate::executor_solana_core_parse_pubkey(authority, "authority") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instruction = solana_instruction::Instruction {
|
||||
program_id: solana_sdk_ids::loader_v4::ID,
|
||||
accounts: vec![
|
||||
solana_instruction::AccountMeta::new(program_address, false),
|
||||
solana_instruction::AccountMeta::new_readonly(authority_address, true),
|
||||
],
|
||||
data: write_data(offset, bytes),
|
||||
};
|
||||
return finalize_single(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_LOADER_V4_WRITE_OPERATION,
|
||||
instruction,
|
||||
vec![(authority.clone(), std::string::String::from("program_authority"))],
|
||||
);
|
||||
}
|
||||
|
||||
fn build_copy_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
program: &crate::MdPubkey,
|
||||
authority: &crate::MdPubkey,
|
||||
source_program: &crate::MdPubkey,
|
||||
destination_offset: u32,
|
||||
source_offset: u32,
|
||||
length: u32,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
if length == 0 {
|
||||
return crate::executor_solana_core_invalid(
|
||||
"execution_loader_v4_copy_length_zero",
|
||||
"Loader v4 copy requires a positive byte length",
|
||||
);
|
||||
}
|
||||
if destination_offset.checked_add(length).is_none()
|
||||
|| source_offset.checked_add(length).is_none()
|
||||
{
|
||||
return crate::executor_solana_core_invalid(
|
||||
"execution_loader_v4_copy_range_overflow",
|
||||
"Loader v4 copy offset plus length exceeds u32",
|
||||
);
|
||||
}
|
||||
match distinct(program, source_program, "program", "source_program") {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let program_address = match crate::executor_solana_core_parse_pubkey(program, "program") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let authority_address = match crate::executor_solana_core_parse_pubkey(authority, "authority") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let source_address =
|
||||
match crate::executor_solana_core_parse_pubkey(source_program, "source_program") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instruction = solana_instruction::Instruction {
|
||||
program_id: solana_sdk_ids::loader_v4::ID,
|
||||
accounts: vec![
|
||||
solana_instruction::AccountMeta::new(program_address, false),
|
||||
solana_instruction::AccountMeta::new_readonly(authority_address, true),
|
||||
solana_instruction::AccountMeta::new_readonly(source_address, false),
|
||||
],
|
||||
data: copy_data(destination_offset, source_offset, length),
|
||||
};
|
||||
return finalize_single(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_LOADER_V4_COPY_OPERATION,
|
||||
instruction,
|
||||
vec![(authority.clone(), std::string::String::from("program_authority"))],
|
||||
);
|
||||
}
|
||||
|
||||
fn build_authority_unit_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
operation_code: &str,
|
||||
program: &crate::MdPubkey,
|
||||
authority: &crate::MdPubkey,
|
||||
tag: u32,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
let program_address = match crate::executor_solana_core_parse_pubkey(program, "program") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let authority_address = match crate::executor_solana_core_parse_pubkey(authority, "authority") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instruction = solana_instruction::Instruction {
|
||||
program_id: solana_sdk_ids::loader_v4::ID,
|
||||
accounts: vec![
|
||||
solana_instruction::AccountMeta::new(program_address, false),
|
||||
solana_instruction::AccountMeta::new_readonly(authority_address, true),
|
||||
],
|
||||
data: tag.to_le_bytes().to_vec(),
|
||||
};
|
||||
return finalize_single(
|
||||
intent,
|
||||
fee_payer,
|
||||
operation_code,
|
||||
instruction,
|
||||
vec![(authority.clone(), std::string::String::from("program_authority"))],
|
||||
);
|
||||
}
|
||||
|
||||
fn build_deploy_from_source_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
program: &crate::MdPubkey,
|
||||
authority: &crate::MdPubkey,
|
||||
source_program: &crate::MdPubkey,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
match distinct(program, source_program, "program", "source_program") {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let program_address = match crate::executor_solana_core_parse_pubkey(program, "program") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let authority_address = match crate::executor_solana_core_parse_pubkey(authority, "authority") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let source_address =
|
||||
match crate::executor_solana_core_parse_pubkey(source_program, "source_program") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instruction = solana_instruction::Instruction {
|
||||
program_id: solana_sdk_ids::loader_v4::ID,
|
||||
accounts: vec![
|
||||
solana_instruction::AccountMeta::new(program_address, false),
|
||||
solana_instruction::AccountMeta::new_readonly(authority_address, true),
|
||||
solana_instruction::AccountMeta::new(source_address, false),
|
||||
],
|
||||
data: DEPLOY_TAG.to_le_bytes().to_vec(),
|
||||
};
|
||||
return finalize_single(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_LOADER_V4_DEPLOY_FROM_SOURCE_OPERATION,
|
||||
instruction,
|
||||
vec![(authority.clone(), std::string::String::from("program_authority"))],
|
||||
);
|
||||
}
|
||||
|
||||
fn build_transfer_authority_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
program: &crate::MdPubkey,
|
||||
current_authority: &crate::MdPubkey,
|
||||
new_authority: &crate::MdPubkey,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
match distinct(current_authority, new_authority, "current_authority", "new_authority") {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let program_address = match crate::executor_solana_core_parse_pubkey(program, "program") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let current_address =
|
||||
match crate::executor_solana_core_parse_pubkey(current_authority, "current_authority") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let new_address = match crate::executor_solana_core_parse_pubkey(new_authority, "new_authority")
|
||||
{
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instruction = solana_instruction::Instruction {
|
||||
program_id: solana_sdk_ids::loader_v4::ID,
|
||||
accounts: vec![
|
||||
solana_instruction::AccountMeta::new(program_address, false),
|
||||
solana_instruction::AccountMeta::new_readonly(current_address, true),
|
||||
solana_instruction::AccountMeta::new_readonly(new_address, true),
|
||||
],
|
||||
data: TRANSFER_AUTHORITY_TAG.to_le_bytes().to_vec(),
|
||||
};
|
||||
return finalize_single(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_LOADER_V4_TRANSFER_AUTHORITY_OPERATION,
|
||||
instruction,
|
||||
vec![
|
||||
(
|
||||
current_authority.clone(),
|
||||
std::string::String::from("current_program_authority"),
|
||||
),
|
||||
(new_authority.clone(), std::string::String::from("new_program_authority")),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
fn build_finalize_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
program: &crate::MdPubkey,
|
||||
authority: &crate::MdPubkey,
|
||||
next_version_program: &crate::MdPubkey,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
let program_address = match crate::executor_solana_core_parse_pubkey(program, "program") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let authority_address = match crate::executor_solana_core_parse_pubkey(authority, "authority") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let next_version_address = match crate::executor_solana_core_parse_pubkey(
|
||||
next_version_program,
|
||||
"next_version_program",
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instruction = solana_instruction::Instruction {
|
||||
program_id: solana_sdk_ids::loader_v4::ID,
|
||||
accounts: vec![
|
||||
solana_instruction::AccountMeta::new(program_address, false),
|
||||
solana_instruction::AccountMeta::new_readonly(authority_address, true),
|
||||
solana_instruction::AccountMeta::new_readonly(next_version_address, false),
|
||||
],
|
||||
data: FINALIZE_TAG.to_le_bytes().to_vec(),
|
||||
};
|
||||
return finalize_single(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_LOADER_V4_FINALIZE_OPERATION,
|
||||
instruction,
|
||||
vec![(authority.clone(), std::string::String::from("program_authority"))],
|
||||
);
|
||||
}
|
||||
|
||||
fn set_program_length_instruction(
|
||||
program: &solana_pubkey::Pubkey,
|
||||
authority: &solana_pubkey::Pubkey,
|
||||
recipient: &solana_pubkey::Pubkey,
|
||||
new_size: u32,
|
||||
) -> solana_instruction::Instruction {
|
||||
let mut data = SET_PROGRAM_LENGTH_TAG.to_le_bytes().to_vec();
|
||||
data.extend_from_slice(&new_size.to_le_bytes());
|
||||
return solana_instruction::Instruction {
|
||||
program_id: solana_sdk_ids::loader_v4::ID,
|
||||
accounts: vec![
|
||||
solana_instruction::AccountMeta::new(*program, false),
|
||||
solana_instruction::AccountMeta::new_readonly(*authority, true),
|
||||
solana_instruction::AccountMeta::new(*recipient, false),
|
||||
],
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
fn write_data(offset: u32, bytes: &[u8]) -> std::vec::Vec<u8> {
|
||||
let mut data = WRITE_TAG.to_le_bytes().to_vec();
|
||||
data.extend_from_slice(&offset.to_le_bytes());
|
||||
data.extend_from_slice(&(bytes.len() as u64).to_le_bytes());
|
||||
data.extend_from_slice(bytes);
|
||||
return data;
|
||||
}
|
||||
|
||||
fn copy_data(destination_offset: u32, source_offset: u32, length: u32) -> std::vec::Vec<u8> {
|
||||
let mut data = COPY_TAG.to_le_bytes().to_vec();
|
||||
data.extend_from_slice(&destination_offset.to_le_bytes());
|
||||
data.extend_from_slice(&source_offset.to_le_bytes());
|
||||
data.extend_from_slice(&length.to_le_bytes());
|
||||
return data;
|
||||
}
|
||||
|
||||
fn finalize_single(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
operation_code: &str,
|
||||
instruction: solana_instruction::Instruction,
|
||||
signer_roles: std::vec::Vec<(crate::MdPubkey, std::string::String)>,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
return crate::executor_solana_core_finalize_prepared_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
operation_code,
|
||||
vec![instruction],
|
||||
0,
|
||||
std::option::Option::None,
|
||||
signer_roles,
|
||||
);
|
||||
}
|
||||
|
||||
fn validate_size(new_size: u32) -> kb_core::Result<()> {
|
||||
if u64::from(new_size) > solana_system_interface::MAX_PERMITTED_DATA_LENGTH {
|
||||
return crate::executor_solana_core_invalid(
|
||||
"execution_loader_v4_program_size_out_of_range",
|
||||
format!(
|
||||
"Loader v4 program size must not exceed {} bytes",
|
||||
solana_system_interface::MAX_PERMITTED_DATA_LENGTH
|
||||
),
|
||||
);
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
fn distinct(
|
||||
first: &crate::MdPubkey,
|
||||
second: &crate::MdPubkey,
|
||||
first_name: &str,
|
||||
second_name: &str,
|
||||
) -> kb_core::Result<()> {
|
||||
return crate::executor_solana_core_validate_distinct_pubkeys(
|
||||
first,
|
||||
second,
|
||||
"execution_loader_v4_accounts_not_distinct",
|
||||
format!("Loader v4 {first_name} and {second_name} accounts must be distinct"),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
fn model(value: solana_pubkey::Pubkey) -> crate::MdPubkey {
|
||||
return crate::MdPubkey(value.to_string());
|
||||
}
|
||||
|
||||
fn policy(signers: &[solana_pubkey::Pubkey]) -> crate::ExApiExecutionPolicy {
|
||||
return crate::ExApiExecutionPolicy {
|
||||
cost_limit: crate::ExApiExecutionCostLimit {
|
||||
max_spend_lamports: std::option::Option::Some(20_000_000),
|
||||
max_fee_lamports: std::option::Option::Some(1_000_000),
|
||||
max_compute_unit_price_micro_lamports: std::option::Option::Some(100),
|
||||
},
|
||||
authorized_signers: signers.iter().map(|value| return model(*value)).collect(),
|
||||
..crate::ExApiExecutionPolicy::default()
|
||||
};
|
||||
}
|
||||
|
||||
fn intent(
|
||||
operation: crate::ExSolanaCoreOperation,
|
||||
fee_payer: solana_pubkey::Pubkey,
|
||||
signers: &[solana_pubkey::Pubkey],
|
||||
) -> crate::ExSolanaCoreExecutionIntent {
|
||||
return crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("loader-v4-test"),
|
||||
fee_payer: model(fee_payer),
|
||||
policy: policy(signers),
|
||||
operation,
|
||||
};
|
||||
}
|
||||
|
||||
fn build(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
let executor = crate::ExSolanaCoreExecutor;
|
||||
return crate::ExApiTypedInstructionExecutor::build_prepared_plan(&executor, intent);
|
||||
}
|
||||
|
||||
fn build_or_panic(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
) -> crate::ExApiPreparedExecutionPlan {
|
||||
return match build(intent) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("Loader v4 test plan failed: {error}"),
|
||||
};
|
||||
}
|
||||
|
||||
fn assert_account(
|
||||
account: &crate::ExApiPlannedAccount,
|
||||
expected: solana_pubkey::Pubkey,
|
||||
signer: bool,
|
||||
writable: bool,
|
||||
) {
|
||||
assert_eq!(account.pubkey.0, expected.to_string());
|
||||
assert_eq!(account.is_signer, signer);
|
||||
assert_eq!(account.is_writable, writable);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_buffer_preserves_official_compound_order_wire_and_spend() {
|
||||
let payer = solana_pubkey::Pubkey::new_unique();
|
||||
let buffer = solana_pubkey::Pubkey::new_unique();
|
||||
let authority = solana_pubkey::Pubkey::new_unique();
|
||||
let recipient = solana_pubkey::Pubkey::new_unique();
|
||||
let intent = intent(
|
||||
crate::ExSolanaCoreOperation::LoaderV4CreateBuffer {
|
||||
payer: model(payer),
|
||||
buffer: model(buffer),
|
||||
authority: model(authority),
|
||||
recipient: model(recipient),
|
||||
lamports: 1_000_000,
|
||||
new_size: 4096,
|
||||
},
|
||||
payer,
|
||||
&[payer, buffer, authority],
|
||||
);
|
||||
let plan = build_or_panic(&intent);
|
||||
assert_eq!(plan.instructions.len(), 2);
|
||||
assert_eq!(
|
||||
plan.instructions[0].program_id.0,
|
||||
solana_sdk_ids::system_program::ID.to_string()
|
||||
);
|
||||
assert_eq!(plan.instructions[1].program_id.0, solana_sdk_ids::loader_v4::ID.to_string());
|
||||
assert_eq!(
|
||||
plan.instructions[1].data,
|
||||
[
|
||||
super::SET_PROGRAM_LENGTH_TAG.to_le_bytes().as_slice(),
|
||||
4096_u32.to_le_bytes().as_slice(),
|
||||
]
|
||||
.concat()
|
||||
);
|
||||
assert_account(&plan.instructions[1].accounts[0], buffer, false, true);
|
||||
assert_account(&plan.instructions[1].accounts[1], authority, true, false);
|
||||
assert_account(&plan.instructions[1].accounts[2], recipient, false, true);
|
||||
assert_eq!(plan.requested_spend_lamports, 1_000_000);
|
||||
assert_eq!(plan.required_signers.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_copy_and_set_length_preserve_exact_bincode_wire_contract() {
|
||||
let fee_payer = solana_pubkey::Pubkey::new_unique();
|
||||
let program = solana_pubkey::Pubkey::new_unique();
|
||||
let authority = solana_pubkey::Pubkey::new_unique();
|
||||
let source = solana_pubkey::Pubkey::new_unique();
|
||||
let recipient = solana_pubkey::Pubkey::new_unique();
|
||||
let write_intent = intent(
|
||||
crate::ExSolanaCoreOperation::LoaderV4Write {
|
||||
program: model(program),
|
||||
authority: model(authority),
|
||||
offset: 7,
|
||||
bytes: vec![1, 2, 3],
|
||||
},
|
||||
fee_payer,
|
||||
&[fee_payer, authority],
|
||||
);
|
||||
let write_plan = build_or_panic(&write_intent);
|
||||
assert_eq!(
|
||||
write_plan.instructions[0].data,
|
||||
[
|
||||
super::WRITE_TAG.to_le_bytes().as_slice(),
|
||||
7_u32.to_le_bytes().as_slice(),
|
||||
3_u64.to_le_bytes().as_slice(),
|
||||
&[1, 2, 3],
|
||||
]
|
||||
.concat()
|
||||
);
|
||||
let copy_intent = intent(
|
||||
crate::ExSolanaCoreOperation::LoaderV4Copy {
|
||||
program: model(program),
|
||||
authority: model(authority),
|
||||
source_program: model(source),
|
||||
destination_offset: 11,
|
||||
source_offset: 13,
|
||||
length: 17,
|
||||
},
|
||||
fee_payer,
|
||||
&[fee_payer, authority],
|
||||
);
|
||||
let copy_plan = build_or_panic(©_intent);
|
||||
assert_eq!(
|
||||
copy_plan.instructions[0].data,
|
||||
[
|
||||
super::COPY_TAG.to_le_bytes().as_slice(),
|
||||
11_u32.to_le_bytes().as_slice(),
|
||||
13_u32.to_le_bytes().as_slice(),
|
||||
17_u32.to_le_bytes().as_slice(),
|
||||
]
|
||||
.concat()
|
||||
);
|
||||
assert_account(©_plan.instructions[0].accounts[2], source, false, false);
|
||||
let length_intent = intent(
|
||||
crate::ExSolanaCoreOperation::LoaderV4SetProgramLength {
|
||||
program: model(program),
|
||||
authority: model(authority),
|
||||
recipient: model(recipient),
|
||||
new_size: 0,
|
||||
},
|
||||
fee_payer,
|
||||
&[fee_payer, authority],
|
||||
);
|
||||
let length_plan = build_or_panic(&length_intent);
|
||||
assert_eq!(
|
||||
length_plan.instructions[0].data,
|
||||
[
|
||||
super::SET_PROGRAM_LENGTH_TAG.to_le_bytes().as_slice(),
|
||||
0_u32.to_le_bytes().as_slice(),
|
||||
]
|
||||
.concat()
|
||||
);
|
||||
assert_eq!(length_plan.requested_spend_lamports, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deploy_retract_transfer_and_finalize_preserve_official_accounts_and_tags() {
|
||||
let fee_payer = solana_pubkey::Pubkey::new_unique();
|
||||
let program = solana_pubkey::Pubkey::new_unique();
|
||||
let authority = solana_pubkey::Pubkey::new_unique();
|
||||
let source = solana_pubkey::Pubkey::new_unique();
|
||||
let new_authority = solana_pubkey::Pubkey::new_unique();
|
||||
let next_version = solana_pubkey::Pubkey::new_unique();
|
||||
let fixtures = [
|
||||
(
|
||||
crate::ExSolanaCoreOperation::LoaderV4Deploy {
|
||||
program: model(program),
|
||||
authority: model(authority),
|
||||
},
|
||||
super::DEPLOY_TAG,
|
||||
vec![fee_payer, authority],
|
||||
2,
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreOperation::LoaderV4DeployFromSource {
|
||||
program: model(program),
|
||||
authority: model(authority),
|
||||
source_program: model(source),
|
||||
},
|
||||
super::DEPLOY_TAG,
|
||||
vec![fee_payer, authority],
|
||||
3,
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreOperation::LoaderV4Retract {
|
||||
program: model(program),
|
||||
authority: model(authority),
|
||||
},
|
||||
super::RETRACT_TAG,
|
||||
vec![fee_payer, authority],
|
||||
2,
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreOperation::LoaderV4TransferAuthority {
|
||||
program: model(program),
|
||||
current_authority: model(authority),
|
||||
new_authority: model(new_authority),
|
||||
},
|
||||
super::TRANSFER_AUTHORITY_TAG,
|
||||
vec![fee_payer, authority, new_authority],
|
||||
3,
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreOperation::LoaderV4Finalize {
|
||||
program: model(program),
|
||||
authority: model(authority),
|
||||
next_version_program: model(next_version),
|
||||
},
|
||||
super::FINALIZE_TAG,
|
||||
vec![fee_payer, authority],
|
||||
3,
|
||||
),
|
||||
];
|
||||
for (operation, tag, signers, account_count) in fixtures {
|
||||
let intent = intent(operation, fee_payer, signers.as_slice());
|
||||
let plan = build_or_panic(&intent);
|
||||
assert_eq!(plan.instructions[0].data, tag.to_le_bytes().to_vec());
|
||||
assert_eq!(plan.instructions[0].accounts.len(), account_count);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn malformed_sizes_ranges_and_authorities_are_rejected() {
|
||||
let fee_payer = solana_pubkey::Pubkey::new_unique();
|
||||
let program = solana_pubkey::Pubkey::new_unique();
|
||||
let authority = solana_pubkey::Pubkey::new_unique();
|
||||
let recipient = solana_pubkey::Pubkey::new_unique();
|
||||
let empty_write = intent(
|
||||
crate::ExSolanaCoreOperation::LoaderV4Write {
|
||||
program: model(program),
|
||||
authority: model(authority),
|
||||
offset: 0,
|
||||
bytes: std::vec::Vec::new(),
|
||||
},
|
||||
fee_payer,
|
||||
&[fee_payer, authority],
|
||||
);
|
||||
assert!(build(&empty_write).is_err());
|
||||
let zero_copy = intent(
|
||||
crate::ExSolanaCoreOperation::LoaderV4Copy {
|
||||
program: model(program),
|
||||
authority: model(authority),
|
||||
source_program: model(recipient),
|
||||
destination_offset: 0,
|
||||
source_offset: 0,
|
||||
length: 0,
|
||||
},
|
||||
fee_payer,
|
||||
&[fee_payer, authority],
|
||||
);
|
||||
assert!(build(&zero_copy).is_err());
|
||||
let same_authority = intent(
|
||||
crate::ExSolanaCoreOperation::LoaderV4TransferAuthority {
|
||||
program: model(program),
|
||||
current_authority: model(authority),
|
||||
new_authority: model(authority),
|
||||
},
|
||||
fee_payer,
|
||||
&[fee_payer, authority],
|
||||
);
|
||||
assert!(build(&same_authority).is_err());
|
||||
let zero_buffer = intent(
|
||||
crate::ExSolanaCoreOperation::LoaderV4CreateBuffer {
|
||||
payer: model(fee_payer),
|
||||
buffer: model(program),
|
||||
authority: model(authority),
|
||||
recipient: model(recipient),
|
||||
lamports: 1,
|
||||
new_size: 0,
|
||||
},
|
||||
fee_payer,
|
||||
&[fee_payer, program, authority],
|
||||
);
|
||||
assert!(build(&zero_buffer).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loader_v4_wire_source_is_explicit_and_stable() {
|
||||
assert_eq!(super::SOURCE, "solana-loader-v4-interface@3.1.0");
|
||||
assert_eq!(super::WRITE_TAG, 0);
|
||||
assert_eq!(super::FINALIZE_TAG, 6);
|
||||
}
|
||||
}
|
||||
549
kb-lib/src/executor/solana/core/native_admin.rs
Normal file
549
kb-lib/src/executor/solana/core/native_admin.rs
Normal file
@@ -0,0 +1,549 @@
|
||||
// file: kb-lib/src/executor/solana/core/native_admin.rs
|
||||
// version: 1
|
||||
|
||||
//! Config Program and Feature Program instruction builders.
|
||||
|
||||
const MAX_CONFIG_KEYS: usize = 128;
|
||||
const MAX_CONFIG_INSTRUCTION_DATA_BYTES: usize = u16::MAX as usize;
|
||||
|
||||
pub(crate) fn executor_solana_core_native_admin_build_prepared_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
return match &intent.operation {
|
||||
crate::ExSolanaCoreOperation::ConfigCreateAccount {
|
||||
from,
|
||||
config_account,
|
||||
lamports,
|
||||
max_config_data_space,
|
||||
max_keys,
|
||||
initial_data,
|
||||
} => build_config_create_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
from,
|
||||
config_account,
|
||||
*lamports,
|
||||
*max_config_data_space,
|
||||
max_keys.as_slice(),
|
||||
initial_data.as_slice(),
|
||||
),
|
||||
crate::ExSolanaCoreOperation::ConfigStore {
|
||||
config_account,
|
||||
config_account_is_signer,
|
||||
keys,
|
||||
data,
|
||||
} => build_config_store_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
config_account,
|
||||
*config_account_is_signer,
|
||||
keys.as_slice(),
|
||||
data.as_slice(),
|
||||
),
|
||||
crate::ExSolanaCoreOperation::FeatureActivate {
|
||||
feature_account,
|
||||
funding_account,
|
||||
lamports,
|
||||
} => build_feature_activate_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
feature_account,
|
||||
funding_account,
|
||||
*lamports,
|
||||
),
|
||||
crate::ExSolanaCoreOperation::FeatureRevokePendingActivation { feature_account } => {
|
||||
build_feature_revoke_plan(intent, fee_payer, feature_account)
|
||||
},
|
||||
_ => std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_native_admin_operation_mismatch",
|
||||
"Config/Feature builder received an unrelated Solana core operation",
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
fn build_config_create_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
from: &crate::MdPubkey,
|
||||
config_account: &crate::MdPubkey,
|
||||
lamports: u64,
|
||||
max_config_data_space: u64,
|
||||
max_keys: &[crate::ExSolanaCoreConfigKey],
|
||||
initial_data: &[u8],
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
if lamports == 0 {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_config_create_lamports_zero",
|
||||
"Config account creation requires a positive rent-exempt lamport amount",
|
||||
));
|
||||
}
|
||||
if from == config_account {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_config_create_same_account",
|
||||
"Config funding and new account addresses must differ",
|
||||
));
|
||||
}
|
||||
let from_address = match crate::executor_solana_core_parse_pubkey(from, "from") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let config_address =
|
||||
match crate::executor_solana_core_parse_pubkey(config_account, "config_account") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let encoded_max_keys = match encode_config_keys(max_keys) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let initial_data_len = initial_data.len() as u64;
|
||||
if initial_data_len > max_config_data_space {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_config_initial_data_exceeds_max_space",
|
||||
"serialized initial Config data exceeds max_config_data_space",
|
||||
));
|
||||
}
|
||||
let space = match max_config_data_space.checked_add(encoded_max_keys.len() as u64) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_config_space_overflow",
|
||||
"Config account space exceeds u64",
|
||||
));
|
||||
},
|
||||
};
|
||||
if space > solana_system_interface::MAX_PERMITTED_DATA_LENGTH {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_config_space_out_of_range",
|
||||
format!(
|
||||
"Config account space must not exceed {} bytes",
|
||||
solana_system_interface::MAX_PERMITTED_DATA_LENGTH
|
||||
),
|
||||
));
|
||||
}
|
||||
let config_program = match crate::executor_solana_core_parse_native_program_id(
|
||||
kb_program_ids::CONFIG_PROGRAM_ID,
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let create = solana_system_interface::instruction::create_account(
|
||||
&from_address,
|
||||
&config_address,
|
||||
lamports,
|
||||
space,
|
||||
&config_program,
|
||||
);
|
||||
let mut initialize_data = vec![0_u8];
|
||||
initialize_data.extend_from_slice(initial_data);
|
||||
let initialize = solana_instruction::Instruction {
|
||||
program_id: config_program,
|
||||
accounts: vec![solana_instruction::AccountMeta::new(config_address, true)],
|
||||
data: initialize_data,
|
||||
};
|
||||
return crate::executor_solana_core_finalize_prepared_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_CONFIG_CREATE_ACCOUNT_OPERATION,
|
||||
vec![create, initialize],
|
||||
lamports,
|
||||
std::option::Option::None,
|
||||
vec![
|
||||
(from.clone(), std::string::String::from("config_funding_account")),
|
||||
(config_account.clone(), std::string::String::from("new_config_account")),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
fn build_config_store_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
config_account: &crate::MdPubkey,
|
||||
config_account_is_signer: bool,
|
||||
keys: &[crate::ExSolanaCoreConfigKey],
|
||||
data: &[u8],
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
let config_address =
|
||||
match crate::executor_solana_core_parse_pubkey(config_account, "config_account") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut instruction_data = match encode_config_keys(keys) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let total_length = match instruction_data.len().checked_add(data.len()) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_config_store_data_overflow",
|
||||
"Config store instruction data length overflows usize",
|
||||
));
|
||||
},
|
||||
};
|
||||
if total_length > MAX_CONFIG_INSTRUCTION_DATA_BYTES {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_config_store_data_too_large",
|
||||
format!(
|
||||
"Config store instruction data must not exceed {MAX_CONFIG_INSTRUCTION_DATA_BYTES} bytes"
|
||||
),
|
||||
));
|
||||
}
|
||||
instruction_data.extend_from_slice(data);
|
||||
let mut accounts =
|
||||
vec![solana_instruction::AccountMeta::new(config_address, config_account_is_signer)];
|
||||
let mut signer_roles = std::vec::Vec::new();
|
||||
if config_account_is_signer {
|
||||
signer_roles
|
||||
.push((config_account.clone(), std::string::String::from("config_account_authority")));
|
||||
}
|
||||
for key in keys {
|
||||
if !key.is_signer || key.pubkey == *config_account {
|
||||
continue;
|
||||
}
|
||||
let key_address = match crate::executor_solana_core_parse_pubkey(&key.pubkey, "config_key")
|
||||
{
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
accounts.push(solana_instruction::AccountMeta::new(key_address, true));
|
||||
signer_roles.push((key.pubkey.clone(), std::string::String::from("config_key_authority")));
|
||||
}
|
||||
if signer_roles.is_empty() {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_config_store_signer_missing",
|
||||
"Config store requires the config account or at least one declared key signer",
|
||||
));
|
||||
}
|
||||
let program_id = match crate::executor_solana_core_parse_native_program_id(
|
||||
kb_program_ids::CONFIG_PROGRAM_ID,
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instruction = solana_instruction::Instruction {
|
||||
program_id,
|
||||
accounts,
|
||||
data: instruction_data,
|
||||
};
|
||||
return crate::executor_solana_core_finalize_prepared_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_CONFIG_STORE_OPERATION,
|
||||
vec![instruction],
|
||||
0,
|
||||
std::option::Option::None,
|
||||
signer_roles,
|
||||
);
|
||||
}
|
||||
|
||||
fn build_feature_activate_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
feature_account: &crate::MdPubkey,
|
||||
funding_account: &crate::MdPubkey,
|
||||
lamports: u64,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
if lamports == 0 {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_feature_activation_lamports_zero",
|
||||
"feature activation requires a positive rent-exempt lamport amount",
|
||||
));
|
||||
}
|
||||
if feature_account == funding_account {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_feature_activation_same_account",
|
||||
"feature and funding accounts must differ",
|
||||
));
|
||||
}
|
||||
let feature_address =
|
||||
match crate::executor_solana_core_parse_pubkey(feature_account, "feature_account") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let funding_address =
|
||||
match crate::executor_solana_core_parse_pubkey(funding_account, "funding_account") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let feature_program = match crate::executor_solana_core_parse_native_program_id(
|
||||
kb_program_ids::FEATURE_PROGRAM_ID,
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instructions = vec![
|
||||
solana_system_interface::instruction::transfer(
|
||||
&funding_address,
|
||||
&feature_address,
|
||||
lamports,
|
||||
),
|
||||
solana_system_interface::instruction::allocate(
|
||||
&feature_address,
|
||||
solana_feature_gate_interface::state::Feature::size_of() as u64,
|
||||
),
|
||||
solana_system_interface::instruction::assign(&feature_address, &feature_program),
|
||||
];
|
||||
return crate::executor_solana_core_finalize_prepared_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_FEATURE_ACTIVATE_OPERATION,
|
||||
instructions,
|
||||
lamports,
|
||||
std::option::Option::None,
|
||||
vec![
|
||||
(funding_account.clone(), std::string::String::from("feature_funding_account")),
|
||||
(feature_account.clone(), std::string::String::from("new_feature_account")),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
fn build_feature_revoke_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
feature_account: &crate::MdPubkey,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
let feature_address =
|
||||
match crate::executor_solana_core_parse_pubkey(feature_account, "feature_account") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let feature_program = match crate::executor_solana_core_parse_native_program_id(
|
||||
kb_program_ids::FEATURE_PROGRAM_ID,
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let incinerator = match crate::executor_solana_core_parse_native_program_id(
|
||||
kb_program_ids::INCINERATOR_PROGRAM_ID,
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instruction = solana_instruction::Instruction {
|
||||
program_id: feature_program,
|
||||
accounts: vec![
|
||||
solana_instruction::AccountMeta::new(feature_address, true),
|
||||
solana_instruction::AccountMeta::new(incinerator, false),
|
||||
solana_instruction::AccountMeta::new_readonly(
|
||||
solana_sdk_ids::system_program::id(),
|
||||
false,
|
||||
),
|
||||
],
|
||||
data: vec![0],
|
||||
};
|
||||
return crate::executor_solana_core_finalize_prepared_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_FEATURE_REVOKE_PENDING_ACTIVATION_OPERATION,
|
||||
vec![instruction],
|
||||
0,
|
||||
std::option::Option::None,
|
||||
vec![(feature_account.clone(), std::string::String::from("pending_feature_account"))],
|
||||
);
|
||||
}
|
||||
|
||||
fn encode_config_keys(keys: &[crate::ExSolanaCoreConfigKey]) -> kb_core::Result<std::vec::Vec<u8>> {
|
||||
if keys.len() > MAX_CONFIG_KEYS {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_config_keys_too_many",
|
||||
format!("Config key list must not exceed {MAX_CONFIG_KEYS} entries"),
|
||||
));
|
||||
}
|
||||
let mut seen = std::collections::BTreeSet::new();
|
||||
let mut encoded = encode_compact_u16(keys.len() as u16);
|
||||
for key in keys {
|
||||
if !seen.insert(key.pubkey.0.clone()) {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_config_key_duplicate",
|
||||
format!("Config key {} is declared more than once", key.pubkey.0),
|
||||
));
|
||||
}
|
||||
let address = match crate::executor_solana_core_parse_pubkey(&key.pubkey, "config_key") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
encoded.extend_from_slice(address.as_ref());
|
||||
encoded.push(u8::from(key.is_signer));
|
||||
}
|
||||
return std::result::Result::Ok(encoded);
|
||||
}
|
||||
|
||||
fn encode_compact_u16(value: u16) -> std::vec::Vec<u8> {
|
||||
if value < 128 {
|
||||
return vec![value as u8];
|
||||
}
|
||||
if value < 16_384 {
|
||||
return vec![((value & 0x7f) as u8) | 0x80, (value >> 7) as u8];
|
||||
}
|
||||
return vec![
|
||||
((value & 0x7f) as u8) | 0x80,
|
||||
(((value >> 7) & 0x7f) as u8) | 0x80,
|
||||
(value >> 14) as u8,
|
||||
];
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr; // rust-rules: trait-import
|
||||
|
||||
fn pubkey(value: solana_pubkey::Pubkey) -> crate::MdPubkey {
|
||||
return crate::MdPubkey(value.to_string());
|
||||
}
|
||||
|
||||
fn policy(signers: &[crate::MdPubkey], max_spend: u64) -> crate::ExApiExecutionPolicy {
|
||||
return crate::ExApiExecutionPolicy {
|
||||
authorized_signers: signers.to_vec(),
|
||||
cost_limit: crate::ExApiExecutionCostLimit {
|
||||
max_spend_lamports: std::option::Option::Some(max_spend),
|
||||
max_fee_lamports: std::option::Option::Some(100_000),
|
||||
max_compute_unit_price_micro_lamports: std::option::Option::Some(10),
|
||||
},
|
||||
..crate::ExApiExecutionPolicy::default()
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_store_matches_the_official_wire_contract() {
|
||||
let config = solana_pubkey::Pubkey::new_unique();
|
||||
let authority = solana_pubkey::Pubkey::new_unique();
|
||||
let payer = pubkey(solana_pubkey::Pubkey::new_unique());
|
||||
let intent = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("config-store"),
|
||||
fee_payer: payer.clone(),
|
||||
policy: policy(&[payer, pubkey(authority)], 0),
|
||||
operation: crate::ExSolanaCoreOperation::ConfigStore {
|
||||
config_account: pubkey(config),
|
||||
config_account_is_signer: false,
|
||||
keys: vec![crate::ExSolanaCoreConfigKey {
|
||||
pubkey: pubkey(authority),
|
||||
is_signer: true,
|
||||
}],
|
||||
data: vec![9, 8, 7],
|
||||
},
|
||||
};
|
||||
let plan = crate::executor_solana_core_builder_build_prepared_plan(&intent)
|
||||
.unwrap_or_else(|error| panic!("unexpected error: {error}"));
|
||||
let instruction = &plan.instructions[0];
|
||||
let mut expected = vec![1];
|
||||
expected.extend_from_slice(authority.as_ref());
|
||||
expected.push(1);
|
||||
expected.extend_from_slice(&[9, 8, 7]);
|
||||
assert_eq!(instruction.data, expected);
|
||||
assert_eq!(instruction.accounts.len(), 2);
|
||||
assert!(instruction.accounts[1].is_signer);
|
||||
assert!(instruction.accounts[1].is_writable);
|
||||
assert_eq!(plan.operation_code, crate::EX_SOLANA_CORE_CONFIG_STORE_OPERATION);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_create_uses_official_space_and_initialization_order() {
|
||||
let funding = solana_pubkey::Pubkey::new_unique();
|
||||
let config = solana_pubkey::Pubkey::new_unique();
|
||||
let max_key = solana_pubkey::Pubkey::new_unique();
|
||||
let payer = pubkey(solana_pubkey::Pubkey::new_unique());
|
||||
let intent = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("config-create"),
|
||||
fee_payer: payer.clone(),
|
||||
policy: policy(&[payer, pubkey(funding), pubkey(config)], 500),
|
||||
operation: crate::ExSolanaCoreOperation::ConfigCreateAccount {
|
||||
from: pubkey(funding),
|
||||
config_account: pubkey(config),
|
||||
lamports: 500,
|
||||
max_config_data_space: 64,
|
||||
max_keys: vec![crate::ExSolanaCoreConfigKey {
|
||||
pubkey: pubkey(max_key),
|
||||
is_signer: true,
|
||||
}],
|
||||
initial_data: vec![0, 0, 0, 0],
|
||||
},
|
||||
};
|
||||
let plan = crate::executor_solana_core_builder_build_prepared_plan(&intent)
|
||||
.unwrap_or_else(|error| panic!("unexpected error: {error}"));
|
||||
assert_eq!(plan.instructions.len(), 2);
|
||||
assert_eq!(plan.instructions[1].data, vec![0, 0, 0, 0, 0]);
|
||||
assert_eq!(plan.requested_spend_lamports, 500);
|
||||
assert_eq!(plan.required_signers.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn feature_operations_match_the_official_instruction_contracts() {
|
||||
let feature = solana_pubkey::Pubkey::new_unique();
|
||||
let funding = solana_pubkey::Pubkey::new_unique();
|
||||
let payer = pubkey(solana_pubkey::Pubkey::new_unique());
|
||||
let activate = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("feature-activate"),
|
||||
fee_payer: payer.clone(),
|
||||
policy: policy(&[payer.clone(), pubkey(feature), pubkey(funding)], 600),
|
||||
operation: crate::ExSolanaCoreOperation::FeatureActivate {
|
||||
feature_account: pubkey(feature),
|
||||
funding_account: pubkey(funding),
|
||||
lamports: 600,
|
||||
},
|
||||
};
|
||||
let activate_plan = crate::executor_solana_core_builder_build_prepared_plan(&activate)
|
||||
.unwrap_or_else(|error| panic!("unexpected error: {error}"));
|
||||
assert_eq!(activate_plan.instructions.len(), 3);
|
||||
assert_eq!(activate_plan.instructions[0].program_id.0, kb_program_ids::SYSTEM_PROGRAM_ID);
|
||||
assert_eq!(activate_plan.requested_spend_lamports, 600);
|
||||
|
||||
let revoke = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("feature-revoke"),
|
||||
fee_payer: payer.clone(),
|
||||
policy: policy(&[payer, pubkey(feature)], 0),
|
||||
operation: crate::ExSolanaCoreOperation::FeatureRevokePendingActivation {
|
||||
feature_account: pubkey(feature),
|
||||
},
|
||||
};
|
||||
let revoke_plan = crate::executor_solana_core_builder_build_prepared_plan(&revoke)
|
||||
.unwrap_or_else(|error| panic!("unexpected error: {error}"));
|
||||
assert_eq!(revoke_plan.instructions[0].data, vec![0]);
|
||||
assert_eq!(revoke_plan.instructions[0].accounts.len(), 3);
|
||||
assert_eq!(
|
||||
revoke_plan.instructions[0].accounts[1].pubkey.0,
|
||||
kb_program_ids::INCINERATOR_PROGRAM_ID
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn malformed_config_and_feature_contracts_are_rejected() {
|
||||
let same = pubkey(solana_pubkey::Pubkey::new_unique());
|
||||
let intent = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("feature-invalid"),
|
||||
fee_payer: same.clone(),
|
||||
policy: policy(std::slice::from_ref(&same), 0),
|
||||
operation: crate::ExSolanaCoreOperation::FeatureActivate {
|
||||
feature_account: same.clone(),
|
||||
funding_account: same,
|
||||
lamports: 0,
|
||||
},
|
||||
};
|
||||
assert!(crate::executor_solana_core_builder_build_prepared_plan(&intent).is_err());
|
||||
|
||||
let config = solana_pubkey::Pubkey::new_unique();
|
||||
let payer = pubkey(solana_pubkey::Pubkey::new_unique());
|
||||
let no_signer = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("config-no-signer"),
|
||||
fee_payer: payer.clone(),
|
||||
policy: policy(&[payer], 0),
|
||||
operation: crate::ExSolanaCoreOperation::ConfigStore {
|
||||
config_account: pubkey(config),
|
||||
config_account_is_signer: false,
|
||||
keys: std::vec::Vec::new(),
|
||||
data: vec![1],
|
||||
},
|
||||
};
|
||||
assert!(crate::executor_solana_core_builder_build_prepared_plan(&no_signer).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn official_program_ids_parse() {
|
||||
assert_eq!(
|
||||
solana_pubkey::Pubkey::from_str(kb_program_ids::CONFIG_PROGRAM_ID)
|
||||
.unwrap_or_else(|error| panic!("invalid id: {error}"))
|
||||
.to_string(),
|
||||
kb_program_ids::CONFIG_PROGRAM_ID
|
||||
);
|
||||
}
|
||||
}
|
||||
1247
kb-lib/src/executor/solana/core/precompiles.rs
Normal file
1247
kb-lib/src/executor/solana/core/precompiles.rs
Normal file
File diff suppressed because it is too large
Load Diff
499
kb-lib/src/executor/solana/core/slashing.rs
Normal file
499
kb-lib/src/executor/solana/core/slashing.rs
Normal file
@@ -0,0 +1,499 @@
|
||||
// file: kb-lib/src/executor/solana/core/slashing.rs
|
||||
// version: 1
|
||||
|
||||
//! Stateless Slashing Program builders and the atomic duplicate-block proof plan.
|
||||
|
||||
const HASH_BYTES: usize = 32;
|
||||
const SIGNATURE_BYTES: usize = 64;
|
||||
const DUPLICATE_BLOCK_PROOF_DATA_BYTES: usize = 305;
|
||||
const DUPLICATE_BLOCK_PROOF_TYPE: u8 = 1;
|
||||
|
||||
pub(crate) fn executor_solana_core_slashing_build_prepared_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
return match &intent.operation {
|
||||
crate::ExSolanaCoreOperation::SlashingCloseViolationReport {
|
||||
report_account,
|
||||
destination,
|
||||
} => build_close_plan(intent, fee_payer, report_account, destination),
|
||||
crate::ExSolanaCoreOperation::SlashingSubmitDuplicateBlockProof {
|
||||
proof_account,
|
||||
report_account,
|
||||
funder,
|
||||
report_lamports,
|
||||
proof_offset,
|
||||
slot,
|
||||
node_pubkey,
|
||||
reporter,
|
||||
destination,
|
||||
shred_1_merkle_root,
|
||||
shred_1_signature,
|
||||
shred_2_merkle_root,
|
||||
shred_2_signature,
|
||||
} => build_duplicate_block_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
proof_account,
|
||||
report_account,
|
||||
funder.as_ref(),
|
||||
*report_lamports,
|
||||
*proof_offset,
|
||||
*slot,
|
||||
node_pubkey,
|
||||
reporter,
|
||||
destination,
|
||||
shred_1_merkle_root.as_slice(),
|
||||
shred_1_signature.as_slice(),
|
||||
shred_2_merkle_root.as_slice(),
|
||||
shred_2_signature.as_slice(),
|
||||
),
|
||||
_ => std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_slashing_operation_mismatch",
|
||||
"Slashing builder received an unrelated Solana core operation",
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
fn build_close_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
report_account: &crate::MdPubkey,
|
||||
destination: &crate::MdPubkey,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
if report_account == destination {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_slashing_close_same_account",
|
||||
"Slashing report and destination accounts must differ",
|
||||
));
|
||||
}
|
||||
let report = match crate::executor_solana_core_parse_pubkey(report_account, "report_account") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let destination_address =
|
||||
match crate::executor_solana_core_parse_pubkey(destination, "destination") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let program_id = match crate::executor_solana_core_parse_native_program_id(
|
||||
kb_program_ids::SLASHING_PROGRAM_ID,
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instruction = solana_instruction::Instruction {
|
||||
program_id,
|
||||
accounts: vec![
|
||||
solana_instruction::AccountMeta::new(report, false),
|
||||
solana_instruction::AccountMeta::new(destination_address, false),
|
||||
],
|
||||
data: vec![0],
|
||||
};
|
||||
return crate::executor_solana_core_finalize_prepared_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_SLASHING_CLOSE_VIOLATION_REPORT_OPERATION,
|
||||
vec![instruction],
|
||||
0,
|
||||
std::option::Option::None,
|
||||
std::vec::Vec::new(),
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_duplicate_block_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
proof_account: &crate::MdPubkey,
|
||||
report_account: &crate::MdPubkey,
|
||||
funder: std::option::Option<&crate::MdPubkey>,
|
||||
report_lamports: u64,
|
||||
proof_offset: u64,
|
||||
slot: u64,
|
||||
node_pubkey: &crate::MdPubkey,
|
||||
reporter: &crate::MdPubkey,
|
||||
destination: &crate::MdPubkey,
|
||||
shred_1_merkle_root: &[u8],
|
||||
shred_1_signature: &[u8],
|
||||
shred_2_merkle_root: &[u8],
|
||||
shred_2_signature: &[u8],
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
if intent.policy.blockhash.kind != crate::ExApiExecutionBlockhashKind::Latest {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_slashing_duplicate_block_requires_latest_blockhash",
|
||||
"duplicate-block proof submission must remain the first three transaction instructions and cannot use durable-nonce prepending",
|
||||
));
|
||||
}
|
||||
if report_lamports == 0 {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_slashing_report_lamports_zero",
|
||||
"duplicate-block proof submission requires a positive measured report-account rent amount",
|
||||
));
|
||||
}
|
||||
if report_account == destination {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_slashing_destination_is_report",
|
||||
"Slashing report PDA and close destination must differ",
|
||||
));
|
||||
}
|
||||
match crate::executor_solana_core_validate_exact_length(
|
||||
shred_1_merkle_root,
|
||||
HASH_BYTES,
|
||||
"execution_slashing_first_root_length_invalid",
|
||||
"first shred Merkle root",
|
||||
) {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match crate::executor_solana_core_validate_exact_length(
|
||||
shred_2_merkle_root,
|
||||
HASH_BYTES,
|
||||
"execution_slashing_second_root_length_invalid",
|
||||
"second shred Merkle root",
|
||||
) {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match crate::executor_solana_core_validate_exact_length(
|
||||
shred_1_signature,
|
||||
SIGNATURE_BYTES,
|
||||
"execution_slashing_first_signature_length_invalid",
|
||||
"first shred signature",
|
||||
) {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match crate::executor_solana_core_validate_exact_length(
|
||||
shred_2_signature,
|
||||
SIGNATURE_BYTES,
|
||||
"execution_slashing_second_signature_length_invalid",
|
||||
"second shred signature",
|
||||
) {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let proof = match crate::executor_solana_core_parse_pubkey(proof_account, "proof_account") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let report = match crate::executor_solana_core_parse_pubkey(report_account, "report_account") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let node = match crate::executor_solana_core_parse_pubkey(node_pubkey, "node_pubkey") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let reporter_address = match crate::executor_solana_core_parse_pubkey(reporter, "reporter") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let destination_address =
|
||||
match crate::executor_solana_core_parse_pubkey(destination, "destination") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let funding_key = match funder {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => reporter,
|
||||
};
|
||||
let funding_address = match crate::executor_solana_core_parse_pubkey(funding_key, "funder") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let slashing_program = match crate::executor_solana_core_parse_native_program_id(
|
||||
kb_program_ids::SLASHING_PROGRAM_ID,
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let slot_bytes = slot.to_le_bytes();
|
||||
let proof_type = [DUPLICATE_BLOCK_PROOF_TYPE];
|
||||
let (derived_report, _bump) = solana_pubkey::Pubkey::find_program_address(
|
||||
&[node.as_ref(), slot_bytes.as_slice(), proof_type.as_slice()],
|
||||
&slashing_program,
|
||||
);
|
||||
if derived_report != report {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_slashing_report_pda_mismatch",
|
||||
format!("supplied report account {report} does not match derived PDA {derived_report}"),
|
||||
));
|
||||
}
|
||||
if funding_address == report {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_slashing_funder_is_report",
|
||||
"Slashing report PDA cannot fund itself",
|
||||
));
|
||||
}
|
||||
let slashing_data = duplicate_block_instruction_data(
|
||||
proof_offset,
|
||||
slot,
|
||||
&node,
|
||||
&reporter_address,
|
||||
&destination_address,
|
||||
shred_1_merkle_root,
|
||||
shred_1_signature,
|
||||
shred_2_merkle_root,
|
||||
shred_2_signature,
|
||||
);
|
||||
if slashing_data.len() != DUPLICATE_BLOCK_PROOF_DATA_BYTES {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_slashing_duplicate_block_internal_size_invalid",
|
||||
"constructed duplicate-block instruction data has an invalid size",
|
||||
));
|
||||
}
|
||||
let transfer =
|
||||
solana_system_interface::instruction::transfer(&funding_address, &report, report_lamports);
|
||||
let sigverify = match duplicate_block_sigverify_instruction() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let slashing = solana_instruction::Instruction {
|
||||
program_id: slashing_program,
|
||||
accounts: vec![
|
||||
solana_instruction::AccountMeta::new_readonly(proof, false),
|
||||
solana_instruction::AccountMeta::new(report, false),
|
||||
solana_instruction::AccountMeta::new_readonly(
|
||||
solana_sdk_ids::sysvar::instructions::id(),
|
||||
false,
|
||||
),
|
||||
solana_instruction::AccountMeta::new_readonly(
|
||||
solana_sdk_ids::system_program::id(),
|
||||
false,
|
||||
),
|
||||
],
|
||||
data: slashing_data,
|
||||
};
|
||||
return crate::executor_solana_core_finalize_prepared_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_SLASHING_SUBMIT_DUPLICATE_BLOCK_PROOF_OPERATION,
|
||||
vec![transfer, sigverify, slashing],
|
||||
report_lamports,
|
||||
std::option::Option::None,
|
||||
vec![(funding_key.clone(), std::string::String::from("slashing_report_funder"))],
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn duplicate_block_instruction_data(
|
||||
proof_offset: u64,
|
||||
slot: u64,
|
||||
node: &solana_pubkey::Pubkey,
|
||||
reporter: &solana_pubkey::Pubkey,
|
||||
destination: &solana_pubkey::Pubkey,
|
||||
shred_1_merkle_root: &[u8],
|
||||
shred_1_signature: &[u8],
|
||||
shred_2_merkle_root: &[u8],
|
||||
shred_2_signature: &[u8],
|
||||
) -> std::vec::Vec<u8> {
|
||||
let mut data = std::vec::Vec::with_capacity(DUPLICATE_BLOCK_PROOF_DATA_BYTES);
|
||||
data.push(1);
|
||||
data.extend_from_slice(&proof_offset.to_le_bytes());
|
||||
data.extend_from_slice(&slot.to_le_bytes());
|
||||
data.extend_from_slice(node.as_ref());
|
||||
data.extend_from_slice(reporter.as_ref());
|
||||
data.extend_from_slice(destination.as_ref());
|
||||
data.extend_from_slice(shred_1_merkle_root);
|
||||
data.extend_from_slice(shred_1_signature);
|
||||
data.extend_from_slice(shred_2_merkle_root);
|
||||
data.extend_from_slice(shred_2_signature);
|
||||
return data;
|
||||
}
|
||||
|
||||
fn duplicate_block_sigverify_instruction() -> kb_core::Result<solana_instruction::Instruction> {
|
||||
let records = [
|
||||
crate::ExSolanaCoreEd25519VerificationOffsets {
|
||||
signature_offset: 145,
|
||||
signature_instruction_index: 2,
|
||||
public_key_offset: 17,
|
||||
public_key_instruction_index: 2,
|
||||
message_data_offset: 113,
|
||||
message_data_size: HASH_BYTES as u16,
|
||||
message_instruction_index: 2,
|
||||
},
|
||||
crate::ExSolanaCoreEd25519VerificationOffsets {
|
||||
signature_offset: 241,
|
||||
signature_instruction_index: 2,
|
||||
public_key_offset: 17,
|
||||
public_key_instruction_index: 2,
|
||||
message_data_offset: 209,
|
||||
message_data_size: HASH_BYTES as u16,
|
||||
message_instruction_index: 2,
|
||||
},
|
||||
];
|
||||
let mut data = vec![2, 0];
|
||||
for record in records {
|
||||
data.extend_from_slice(&record.signature_offset.to_le_bytes());
|
||||
data.extend_from_slice(&record.signature_instruction_index.to_le_bytes());
|
||||
data.extend_from_slice(&record.public_key_offset.to_le_bytes());
|
||||
data.extend_from_slice(&record.public_key_instruction_index.to_le_bytes());
|
||||
data.extend_from_slice(&record.message_data_offset.to_le_bytes());
|
||||
data.extend_from_slice(&record.message_data_size.to_le_bytes());
|
||||
data.extend_from_slice(&record.message_instruction_index.to_le_bytes());
|
||||
}
|
||||
let program_id = match crate::executor_solana_core_parse_native_program_id(
|
||||
kb_program_ids::ED25519_PROGRAM_ID,
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(solana_instruction::Instruction {
|
||||
program_id,
|
||||
accounts: std::vec::Vec::new(),
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr; // rust-rules: trait-import
|
||||
|
||||
fn model(value: solana_pubkey::Pubkey) -> crate::MdPubkey {
|
||||
return crate::MdPubkey(value.to_string());
|
||||
}
|
||||
|
||||
fn policy(signers: &[crate::MdPubkey], spend: u64) -> crate::ExApiExecutionPolicy {
|
||||
return crate::ExApiExecutionPolicy {
|
||||
authorized_signers: signers.to_vec(),
|
||||
blockhash: crate::ExApiExecutionBlockhashPolicy {
|
||||
kind: crate::ExApiExecutionBlockhashKind::Latest,
|
||||
max_age_slots: std::option::Option::Some(100),
|
||||
nonce_account: std::option::Option::None,
|
||||
nonce_authority: std::option::Option::None,
|
||||
},
|
||||
cost_limit: crate::ExApiExecutionCostLimit {
|
||||
max_spend_lamports: std::option::Option::Some(spend),
|
||||
max_fee_lamports: std::option::Option::Some(100_000),
|
||||
max_compute_unit_price_micro_lamports: std::option::Option::Some(10),
|
||||
},
|
||||
..crate::ExApiExecutionPolicy::default()
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_block_plan_matches_official_order_offsets_and_wire() {
|
||||
let proof = solana_pubkey::Pubkey::new_unique();
|
||||
let node = solana_pubkey::Pubkey::new_unique();
|
||||
let reporter = solana_pubkey::Pubkey::new_unique();
|
||||
let destination = solana_pubkey::Pubkey::new_unique();
|
||||
let slot = 42_u64;
|
||||
let program = solana_pubkey::Pubkey::from_str(kb_program_ids::SLASHING_PROGRAM_ID)
|
||||
.unwrap_or_else(|error| panic!("invalid id: {error}"));
|
||||
let slot_bytes = slot.to_le_bytes();
|
||||
let (report, _bump) = solana_pubkey::Pubkey::find_program_address(
|
||||
&[node.as_ref(), slot_bytes.as_slice(), &[1]],
|
||||
&program,
|
||||
);
|
||||
let payer = model(solana_pubkey::Pubkey::new_unique());
|
||||
let intent = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("slashing-duplicate"),
|
||||
fee_payer: payer.clone(),
|
||||
policy: policy(&[payer, model(reporter)], 1_000),
|
||||
operation: crate::ExSolanaCoreOperation::SlashingSubmitDuplicateBlockProof {
|
||||
proof_account: model(proof),
|
||||
report_account: model(report),
|
||||
funder: std::option::Option::None,
|
||||
report_lamports: 1_000,
|
||||
proof_offset: 34,
|
||||
slot,
|
||||
node_pubkey: model(node),
|
||||
reporter: model(reporter),
|
||||
destination: model(destination),
|
||||
shred_1_merkle_root: vec![1; 32],
|
||||
shred_1_signature: vec![2; 64],
|
||||
shred_2_merkle_root: vec![3; 32],
|
||||
shred_2_signature: vec![4; 64],
|
||||
},
|
||||
};
|
||||
let plan = crate::executor_solana_core_builder_build_prepared_plan(&intent)
|
||||
.unwrap_or_else(|error| panic!("unexpected error: {error}"));
|
||||
assert_eq!(plan.instructions.len(), 3);
|
||||
assert_eq!(plan.instructions[0].program_id.0, kb_program_ids::SYSTEM_PROGRAM_ID);
|
||||
assert_eq!(plan.instructions[1].program_id.0, kb_program_ids::ED25519_PROGRAM_ID);
|
||||
assert_eq!(plan.instructions[2].program_id.0, kb_program_ids::SLASHING_PROGRAM_ID);
|
||||
assert_eq!(plan.instructions[2].data.len(), 305);
|
||||
assert_eq!(&plan.instructions[2].data[1..9], &34_u64.to_le_bytes());
|
||||
assert_eq!(&plan.instructions[2].data[9..17], &slot.to_le_bytes());
|
||||
assert_eq!(&plan.instructions[1].data[2..4], &145_u16.to_le_bytes());
|
||||
assert_eq!(&plan.instructions[1].data[4..6], &2_u16.to_le_bytes());
|
||||
assert_eq!(plan.requested_spend_lamports, 1_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_close_has_no_instruction_signer() {
|
||||
let report = solana_pubkey::Pubkey::new_unique();
|
||||
let destination = solana_pubkey::Pubkey::new_unique();
|
||||
let payer = model(solana_pubkey::Pubkey::new_unique());
|
||||
let intent = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("slashing-close"),
|
||||
fee_payer: payer.clone(),
|
||||
policy: policy(&[payer], 0),
|
||||
operation: crate::ExSolanaCoreOperation::SlashingCloseViolationReport {
|
||||
report_account: model(report),
|
||||
destination: model(destination),
|
||||
},
|
||||
};
|
||||
let plan = crate::executor_solana_core_builder_build_prepared_plan(&intent)
|
||||
.unwrap_or_else(|error| panic!("unexpected error: {error}"));
|
||||
assert_eq!(plan.instructions[0].data, vec![0]);
|
||||
assert!(plan.instructions[0].accounts.iter().all(|account| return !account.is_signer));
|
||||
assert_eq!(plan.required_signers.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_block_rejects_wrong_pda_sizes_and_durable_nonce() {
|
||||
let key = model(solana_pubkey::Pubkey::new_unique());
|
||||
let mut tpolicy = policy(std::slice::from_ref(&key), 1);
|
||||
tpolicy.blockhash.kind = crate::ExApiExecutionBlockhashKind::DurableNonce;
|
||||
tpolicy.blockhash.max_age_slots = std::option::Option::None;
|
||||
tpolicy.blockhash.nonce_account = std::option::Option::Some(key.clone());
|
||||
tpolicy.blockhash.nonce_authority = std::option::Option::Some(key.clone());
|
||||
let intent = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("slashing-invalid"),
|
||||
fee_payer: key.clone(),
|
||||
policy: tpolicy,
|
||||
operation: crate::ExSolanaCoreOperation::SlashingSubmitDuplicateBlockProof {
|
||||
proof_account: key.clone(),
|
||||
report_account: key.clone(),
|
||||
funder: std::option::Option::None,
|
||||
report_lamports: 1,
|
||||
proof_offset: 0,
|
||||
slot: 0,
|
||||
node_pubkey: key.clone(),
|
||||
reporter: key.clone(),
|
||||
destination: key.clone(),
|
||||
shred_1_merkle_root: vec![0; 31],
|
||||
shred_1_signature: vec![0; 64],
|
||||
shred_2_merkle_root: vec![0; 32],
|
||||
shred_2_signature: vec![0; 64],
|
||||
},
|
||||
};
|
||||
assert!(crate::executor_solana_core_builder_build_prepared_plan(&intent).is_err());
|
||||
let destination_is_report = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("slashing-same-destination"),
|
||||
fee_payer: key.clone(),
|
||||
policy: policy(std::slice::from_ref(&key), 1),
|
||||
operation: crate::ExSolanaCoreOperation::SlashingSubmitDuplicateBlockProof {
|
||||
proof_account: key.clone(),
|
||||
report_account: key.clone(),
|
||||
funder: std::option::Option::None,
|
||||
report_lamports: 1,
|
||||
proof_offset: 0,
|
||||
slot: 0,
|
||||
node_pubkey: key.clone(),
|
||||
reporter: key.clone(),
|
||||
destination: key,
|
||||
shred_1_merkle_root: vec![0; 32],
|
||||
shred_1_signature: vec![0; 64],
|
||||
shred_2_merkle_root: vec![0; 32],
|
||||
shred_2_signature: vec![0; 64],
|
||||
},
|
||||
};
|
||||
assert!(
|
||||
crate::executor_solana_core_builder_build_prepared_plan(&destination_is_report)
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
}
|
||||
2021
kb-lib/src/executor/solana/core/stake.rs
Normal file
2021
kb-lib/src/executor/solana/core/stake.rs
Normal file
File diff suppressed because it is too large
Load Diff
216
kb-lib/src/executor/solana/core/validation.rs
Normal file
216
kb-lib/src/executor/solana/core/validation.rs
Normal file
@@ -0,0 +1,216 @@
|
||||
// file: kb-lib/src/executor/solana/core/validation.rs
|
||||
// version: 1
|
||||
|
||||
//! Shared parsing and validation helpers for native execution plans.
|
||||
|
||||
use std::str::FromStr; // rust-rules: trait-import
|
||||
|
||||
/// Parses one model public key with the canonical execution error contract.
|
||||
pub(crate) fn executor_solana_core_parse_pubkey(
|
||||
pubkey: &crate::MdPubkey,
|
||||
field_name: &str,
|
||||
) -> kb_core::Result<solana_pubkey::Pubkey> {
|
||||
return match solana_pubkey::Pubkey::from_str(pubkey.0.as_str()) {
|
||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_pubkey_invalid",
|
||||
format!("invalid {field_name} public key {}: {error}", pubkey.0),
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
/// Parses one canonical native program identifier.
|
||||
pub(crate) fn executor_solana_core_parse_native_program_id(
|
||||
value: &str,
|
||||
) -> kb_core::Result<solana_pubkey::Pubkey> {
|
||||
return match solana_pubkey::Pubkey::from_str(value) {
|
||||
std::result::Result::Ok(program_id) => std::result::Result::Ok(program_id),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_native_program_id_invalid",
|
||||
format!("invalid native program id {value}: {error}"),
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
/// Validates that two model public keys identify distinct accounts.
|
||||
pub(crate) fn executor_solana_core_validate_distinct_pubkeys(
|
||||
first: &crate::MdPubkey,
|
||||
second: &crate::MdPubkey,
|
||||
code: &str,
|
||||
message: impl std::convert::Into<std::string::String>,
|
||||
) -> kb_core::Result<()> {
|
||||
if first == second {
|
||||
return std::result::Result::Err(kb_core::Error::new(code, message));
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
/// Builds one typed validation error without duplicating domain-local wrappers.
|
||||
pub(crate) fn executor_solana_core_invalid<T>(
|
||||
code: &str,
|
||||
message: impl std::convert::Into<std::string::String>,
|
||||
) -> kb_core::Result<T> {
|
||||
return std::result::Result::Err(kb_core::Error::new(code, message));
|
||||
}
|
||||
|
||||
/// Validates that an unsigned execution amount is strictly positive.
|
||||
pub(crate) fn executor_solana_core_validate_positive_u64(
|
||||
value: u64,
|
||||
code: &str,
|
||||
message: &str,
|
||||
) -> kb_core::Result<()> {
|
||||
if value == 0 {
|
||||
return std::result::Result::Err(kb_core::Error::new(code, message));
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
/// Validates one `Pubkey::create_with_seed` derivation with caller-owned error codes.
|
||||
pub(crate) fn executor_solana_core_validate_seeded_address(
|
||||
address: &solana_pubkey::Pubkey,
|
||||
base: &solana_pubkey::Pubkey,
|
||||
seed: &str,
|
||||
owner: &solana_pubkey::Pubkey,
|
||||
field_name: &str,
|
||||
invalid_seed_code: &str,
|
||||
mismatch_code: &str,
|
||||
) -> kb_core::Result<()> {
|
||||
let derived = match solana_pubkey::Pubkey::create_with_seed(base, seed, owner) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
invalid_seed_code,
|
||||
format!("cannot derive {field_name} from the supplied seed: {error}"),
|
||||
));
|
||||
},
|
||||
};
|
||||
if derived != *address {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
mismatch_code,
|
||||
format!("{field_name} {address} does not match derived address {derived}"),
|
||||
));
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
/// Validates an exact byte-slice length while preserving the caller error code.
|
||||
pub(crate) fn executor_solana_core_validate_exact_length(
|
||||
value: &[u8],
|
||||
expected: usize,
|
||||
code: &str,
|
||||
label: &str,
|
||||
) -> kb_core::Result<()> {
|
||||
if value.len() != expected {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
code,
|
||||
format!("{label} must contain exactly {expected} bytes"),
|
||||
));
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr; // rust-rules: trait-import
|
||||
|
||||
fn model(value: solana_pubkey::Pubkey) -> crate::MdPubkey {
|
||||
return crate::MdPubkey(value.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pubkey_parser_preserves_the_canonical_error_contract() {
|
||||
let address = solana_pubkey::Pubkey::new_unique();
|
||||
let parsed = crate::executor_solana_core_parse_pubkey(&model(address), "account");
|
||||
match parsed {
|
||||
std::result::Result::Ok(value) => assert_eq!(value, address),
|
||||
std::result::Result::Err(error) => panic!("valid public key rejected: {error}"),
|
||||
}
|
||||
let invalid = crate::executor_solana_core_parse_pubkey(
|
||||
&crate::MdPubkey(std::string::String::from("not-base58")),
|
||||
"account",
|
||||
);
|
||||
assert!(invalid.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_program_id_parser_accepts_official_ids_and_rejects_invalid_text() {
|
||||
let parsed =
|
||||
crate::executor_solana_core_parse_native_program_id(kb_program_ids::SYSTEM_PROGRAM_ID);
|
||||
let expected = solana_pubkey::Pubkey::from_str(kb_program_ids::SYSTEM_PROGRAM_ID)
|
||||
.unwrap_or_else(|error| panic!("invalid fixture: {error}"));
|
||||
match parsed {
|
||||
std::result::Result::Ok(value) => assert_eq!(value, expected),
|
||||
std::result::Result::Err(error) => panic!("official program id rejected: {error}"),
|
||||
}
|
||||
assert!(crate::executor_solana_core_parse_native_program_id("invalid").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shared_scalar_and_account_validations_preserve_caller_codes() {
|
||||
let first = model(solana_pubkey::Pubkey::new_unique());
|
||||
let second = model(solana_pubkey::Pubkey::new_unique());
|
||||
assert!(
|
||||
crate::executor_solana_core_validate_distinct_pubkeys(
|
||||
&first,
|
||||
&second,
|
||||
"distinct",
|
||||
"must differ",
|
||||
)
|
||||
.is_ok()
|
||||
);
|
||||
assert!(
|
||||
crate::executor_solana_core_validate_distinct_pubkeys(
|
||||
&first,
|
||||
&first,
|
||||
"distinct",
|
||||
"must differ",
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
assert!(
|
||||
crate::executor_solana_core_validate_positive_u64(1, "positive", "must be positive")
|
||||
.is_ok()
|
||||
);
|
||||
assert!(
|
||||
crate::executor_solana_core_validate_positive_u64(0, "positive", "must be positive")
|
||||
.is_err()
|
||||
);
|
||||
let invalid: kb_core::Result<()> =
|
||||
crate::executor_solana_core_invalid("invalid", "shared validation error");
|
||||
assert!(invalid.is_err());
|
||||
let base = solana_pubkey::Pubkey::new_unique();
|
||||
let owner = solana_pubkey::Pubkey::new_unique();
|
||||
let derived = solana_pubkey::Pubkey::create_with_seed(&base, "seed", &owner)
|
||||
.unwrap_or_else(|error| panic!("invalid fixture: {error}"));
|
||||
assert!(
|
||||
crate::executor_solana_core_validate_seeded_address(
|
||||
&derived, &base, "seed", &owner, "derived", "seed", "mismatch",
|
||||
)
|
||||
.is_ok()
|
||||
);
|
||||
assert!(
|
||||
crate::executor_solana_core_validate_seeded_address(
|
||||
&solana_pubkey::Pubkey::new_unique(),
|
||||
&base,
|
||||
"seed",
|
||||
&owner,
|
||||
"derived",
|
||||
"seed",
|
||||
"mismatch",
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exact_length_validation_accepts_only_the_requested_shape() {
|
||||
assert!(
|
||||
crate::executor_solana_core_validate_exact_length(&[1_u8, 2_u8], 2, "length", "value")
|
||||
.is_ok()
|
||||
);
|
||||
assert!(
|
||||
crate::executor_solana_core_validate_exact_length(&[1_u8], 2, "length", "value")
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
}
|
||||
2358
kb-lib/src/executor/solana/core/vote.rs
Normal file
2358
kb-lib/src/executor/solana/core/vote.rs
Normal file
File diff suppressed because it is too large
Load Diff
451
kb-lib/src/executor/solana/core/zk_elgamal.rs
Normal file
451
kb-lib/src/executor/solana/core/zk_elgamal.rs
Normal file
@@ -0,0 +1,451 @@
|
||||
// file: kb-lib/src/executor/solana/core/zk_elgamal.rs
|
||||
// version: 1
|
||||
|
||||
//! Native ZK ElGamal Proof instruction builders.
|
||||
|
||||
pub(crate) fn executor_solana_core_zk_elgamal_build_prepared_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
return match &intent.operation {
|
||||
crate::ExSolanaCoreOperation::ZkElGamalVerifyInline {
|
||||
proof_type,
|
||||
proof_data,
|
||||
context_state,
|
||||
} => build_inline_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
*proof_type,
|
||||
proof_data.as_slice(),
|
||||
context_state.as_ref(),
|
||||
),
|
||||
crate::ExSolanaCoreOperation::ZkElGamalVerifyFromAccount {
|
||||
proof_type,
|
||||
proof_account,
|
||||
proof_offset,
|
||||
context_state,
|
||||
} => build_account_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
*proof_type,
|
||||
proof_account,
|
||||
*proof_offset,
|
||||
context_state.as_ref(),
|
||||
),
|
||||
crate::ExSolanaCoreOperation::ZkElGamalCloseContextState {
|
||||
context_state,
|
||||
destination,
|
||||
authority,
|
||||
} => build_close_plan(intent, fee_payer, context_state, destination, authority),
|
||||
_ => std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_zk_elgamal_operation_mismatch",
|
||||
"ZK ElGamal builder received an unrelated Solana core operation",
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
fn build_inline_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
proof_type: crate::ExSolanaCoreZkElGamalProofType,
|
||||
proof_data: &[u8],
|
||||
context_state: std::option::Option<&crate::ExSolanaCoreZkElGamalContextState>,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
let expected = proof_type.proof_data_size();
|
||||
if proof_data.len() != expected {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_zk_elgamal_proof_size_invalid",
|
||||
format!(
|
||||
"inline ZK ElGamal proof requires exactly {expected} bytes but received {}",
|
||||
proof_data.len()
|
||||
),
|
||||
));
|
||||
}
|
||||
let accounts = match context_accounts(context_state) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut data = std::vec::Vec::with_capacity(1 + proof_data.len());
|
||||
data.push(proof_type.discriminator());
|
||||
data.extend_from_slice(proof_data);
|
||||
let instruction = match instruction(accounts, data) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return crate::executor_solana_core_finalize_prepared_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_ZK_ELGAMAL_VERIFY_INLINE_OPERATION,
|
||||
vec![instruction],
|
||||
0,
|
||||
std::option::Option::None,
|
||||
std::vec::Vec::new(),
|
||||
);
|
||||
}
|
||||
|
||||
fn build_account_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
proof_type: crate::ExSolanaCoreZkElGamalProofType,
|
||||
proof_account: &crate::MdPubkey,
|
||||
proof_offset: u32,
|
||||
context_state: std::option::Option<&crate::ExSolanaCoreZkElGamalContextState>,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
let proof_address =
|
||||
match crate::executor_solana_core_parse_pubkey(proof_account, "proof_account") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut accounts = vec![solana_instruction::AccountMeta::new_readonly(proof_address, false)];
|
||||
let context_accounts = match context_accounts(context_state) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if let std::option::Option::Some(context) = context_state {
|
||||
if context.account == *proof_account {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_zk_elgamal_proof_is_context",
|
||||
"proof and context-state accounts must differ",
|
||||
));
|
||||
}
|
||||
}
|
||||
accounts.extend(context_accounts);
|
||||
let mut data = vec![proof_type.discriminator()];
|
||||
data.extend_from_slice(&proof_offset.to_le_bytes());
|
||||
let instruction = match instruction(accounts, data) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return crate::executor_solana_core_finalize_prepared_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_ZK_ELGAMAL_VERIFY_FROM_ACCOUNT_OPERATION,
|
||||
vec![instruction],
|
||||
0,
|
||||
std::option::Option::None,
|
||||
std::vec::Vec::new(),
|
||||
);
|
||||
}
|
||||
|
||||
fn build_close_plan(
|
||||
intent: &crate::ExSolanaCoreExecutionIntent,
|
||||
fee_payer: &solana_pubkey::Pubkey,
|
||||
context_state: &crate::MdPubkey,
|
||||
destination: &crate::MdPubkey,
|
||||
authority: &crate::MdPubkey,
|
||||
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
|
||||
if context_state == destination {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_zk_elgamal_close_same_account",
|
||||
"ZK ElGamal context-state and destination accounts must differ",
|
||||
));
|
||||
}
|
||||
let context_address =
|
||||
match crate::executor_solana_core_parse_pubkey(context_state, "context_state") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let destination_address =
|
||||
match crate::executor_solana_core_parse_pubkey(destination, "destination") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let authority_address = match crate::executor_solana_core_parse_pubkey(authority, "authority") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instruction = match instruction(
|
||||
vec![
|
||||
solana_instruction::AccountMeta::new(context_address, false),
|
||||
solana_instruction::AccountMeta::new(destination_address, false),
|
||||
solana_instruction::AccountMeta::new_readonly(authority_address, true),
|
||||
],
|
||||
vec![0],
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return crate::executor_solana_core_finalize_prepared_plan(
|
||||
intent,
|
||||
fee_payer,
|
||||
crate::EX_SOLANA_CORE_ZK_ELGAMAL_CLOSE_CONTEXT_STATE_OPERATION,
|
||||
vec![instruction],
|
||||
0,
|
||||
std::option::Option::None,
|
||||
vec![(authority.clone(), std::string::String::from("zk_elgamal_context_authority"))],
|
||||
);
|
||||
}
|
||||
|
||||
fn context_accounts(
|
||||
context_state: std::option::Option<&crate::ExSolanaCoreZkElGamalContextState>,
|
||||
) -> kb_core::Result<std::vec::Vec<solana_instruction::AccountMeta>> {
|
||||
return match context_state {
|
||||
std::option::Option::Some(context) => {
|
||||
if context.account == context.authority {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"execution_zk_elgamal_context_authority_is_account",
|
||||
"ZK ElGamal context-state account and authority must differ",
|
||||
));
|
||||
}
|
||||
let account =
|
||||
match crate::executor_solana_core_parse_pubkey(&context.account, "context_state") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let authority = match crate::executor_solana_core_parse_pubkey(
|
||||
&context.authority,
|
||||
"context_authority",
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
std::result::Result::Ok(vec![
|
||||
solana_instruction::AccountMeta::new(account, false),
|
||||
solana_instruction::AccountMeta::new_readonly(authority, false),
|
||||
])
|
||||
},
|
||||
std::option::Option::None => std::result::Result::Ok(std::vec::Vec::new()),
|
||||
};
|
||||
}
|
||||
|
||||
fn instruction(
|
||||
accounts: std::vec::Vec<solana_instruction::AccountMeta>,
|
||||
data: std::vec::Vec<u8>,
|
||||
) -> kb_core::Result<solana_instruction::Instruction> {
|
||||
let program_id = match crate::executor_solana_core_parse_native_program_id(
|
||||
kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID,
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(solana_instruction::Instruction { program_id, accounts, data });
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
fn model(value: solana_pubkey::Pubkey) -> crate::MdPubkey {
|
||||
return crate::MdPubkey(value.to_string());
|
||||
}
|
||||
|
||||
fn policy(signers: &[crate::MdPubkey]) -> crate::ExApiExecutionPolicy {
|
||||
return crate::ExApiExecutionPolicy {
|
||||
authorized_signers: signers.to_vec(),
|
||||
..crate::ExApiExecutionPolicy::default()
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inline_and_account_modes_match_official_layouts() {
|
||||
let payer = model(solana_pubkey::Pubkey::new_unique());
|
||||
let proof_type = crate::ExSolanaCoreZkElGamalProofType::PubkeyValidity;
|
||||
let inline = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("zk-inline"),
|
||||
fee_payer: payer.clone(),
|
||||
policy: policy(std::slice::from_ref(&payer)),
|
||||
operation: crate::ExSolanaCoreOperation::ZkElGamalVerifyInline {
|
||||
proof_type,
|
||||
proof_data: vec![0; proof_type.proof_data_size()],
|
||||
context_state: std::option::Option::None,
|
||||
},
|
||||
};
|
||||
let inline_plan = crate::executor_solana_core_builder_build_prepared_plan(&inline)
|
||||
.unwrap_or_else(|error| panic!("unexpected error: {error}"));
|
||||
assert_eq!(inline_plan.instructions[0].data[0], 4);
|
||||
assert_eq!(inline_plan.instructions[0].data.len(), 1 + proof_type.proof_data_size());
|
||||
assert!(inline_plan.instructions[0].accounts.is_empty());
|
||||
|
||||
let proof_account = solana_pubkey::Pubkey::new_unique();
|
||||
let context = solana_pubkey::Pubkey::new_unique();
|
||||
let authority = solana_pubkey::Pubkey::new_unique();
|
||||
let account = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("zk-account"),
|
||||
fee_payer: payer.clone(),
|
||||
policy: policy(&[payer]),
|
||||
operation: crate::ExSolanaCoreOperation::ZkElGamalVerifyFromAccount {
|
||||
proof_type: crate::ExSolanaCoreZkElGamalProofType::ZeroCiphertext,
|
||||
proof_account: model(proof_account),
|
||||
proof_offset: 44,
|
||||
context_state: std::option::Option::Some(
|
||||
crate::ExSolanaCoreZkElGamalContextState {
|
||||
account: model(context),
|
||||
authority: model(authority),
|
||||
},
|
||||
),
|
||||
},
|
||||
};
|
||||
let account_plan = crate::executor_solana_core_builder_build_prepared_plan(&account)
|
||||
.unwrap_or_else(|error| panic!("unexpected error: {error}"));
|
||||
assert_eq!(account_plan.instructions[0].data, vec![1, 44, 0, 0, 0]);
|
||||
assert_eq!(account_plan.instructions[0].accounts.len(), 3);
|
||||
assert!(!account_plan.instructions[0].accounts[0].is_writable);
|
||||
assert!(account_plan.instructions[0].accounts[1].is_writable);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_proof_type_has_the_official_discriminator_and_nonzero_size() {
|
||||
let variants = [
|
||||
crate::ExSolanaCoreZkElGamalProofType::ZeroCiphertext,
|
||||
crate::ExSolanaCoreZkElGamalProofType::CiphertextCiphertextEquality,
|
||||
crate::ExSolanaCoreZkElGamalProofType::CiphertextCommitmentEquality,
|
||||
crate::ExSolanaCoreZkElGamalProofType::PubkeyValidity,
|
||||
crate::ExSolanaCoreZkElGamalProofType::PercentageWithCap,
|
||||
crate::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU64,
|
||||
crate::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU128,
|
||||
crate::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU256,
|
||||
crate::ExSolanaCoreZkElGamalProofType::GroupedCiphertext2HandlesValidity,
|
||||
crate::ExSolanaCoreZkElGamalProofType::BatchedGroupedCiphertext2HandlesValidity,
|
||||
crate::ExSolanaCoreZkElGamalProofType::GroupedCiphertext3HandlesValidity,
|
||||
crate::ExSolanaCoreZkElGamalProofType::BatchedGroupedCiphertext3HandlesValidity,
|
||||
];
|
||||
for (index, proof_type) in variants.iter().enumerate() {
|
||||
assert_eq!(proof_type.discriminator(), (index + 1) as u8);
|
||||
assert!(proof_type.proof_data_size() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_context_state_size_matches_the_official_generic_state() {
|
||||
let variants = [
|
||||
(
|
||||
crate::ExSolanaCoreZkElGamalProofType::ZeroCiphertext,
|
||||
std::mem::size_of::<
|
||||
solana_zk_elgamal_proof_interface::state::ProofContextState<
|
||||
solana_zk_elgamal_proof_interface::proof_data::ZeroCiphertextProofContext,
|
||||
>,
|
||||
>(),
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreZkElGamalProofType::CiphertextCiphertextEquality,
|
||||
std::mem::size_of::<
|
||||
solana_zk_elgamal_proof_interface::state::ProofContextState<
|
||||
solana_zk_elgamal_proof_interface::proof_data::CiphertextCiphertextEqualityProofContext,
|
||||
>,
|
||||
>(),
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreZkElGamalProofType::CiphertextCommitmentEquality,
|
||||
std::mem::size_of::<
|
||||
solana_zk_elgamal_proof_interface::state::ProofContextState<
|
||||
solana_zk_elgamal_proof_interface::proof_data::CiphertextCommitmentEqualityProofContext,
|
||||
>,
|
||||
>(),
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreZkElGamalProofType::PubkeyValidity,
|
||||
std::mem::size_of::<
|
||||
solana_zk_elgamal_proof_interface::state::ProofContextState<
|
||||
solana_zk_elgamal_proof_interface::proof_data::PubkeyValidityProofContext,
|
||||
>,
|
||||
>(),
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreZkElGamalProofType::PercentageWithCap,
|
||||
std::mem::size_of::<
|
||||
solana_zk_elgamal_proof_interface::state::ProofContextState<
|
||||
solana_zk_elgamal_proof_interface::proof_data::PercentageWithCapProofContext,
|
||||
>,
|
||||
>(),
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU64,
|
||||
std::mem::size_of::<
|
||||
solana_zk_elgamal_proof_interface::state::ProofContextState<
|
||||
solana_zk_elgamal_proof_interface::proof_data::BatchedRangeProofContext,
|
||||
>,
|
||||
>(),
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU128,
|
||||
std::mem::size_of::<
|
||||
solana_zk_elgamal_proof_interface::state::ProofContextState<
|
||||
solana_zk_elgamal_proof_interface::proof_data::BatchedRangeProofContext,
|
||||
>,
|
||||
>(),
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreZkElGamalProofType::BatchedRangeProofU256,
|
||||
std::mem::size_of::<
|
||||
solana_zk_elgamal_proof_interface::state::ProofContextState<
|
||||
solana_zk_elgamal_proof_interface::proof_data::BatchedRangeProofContext,
|
||||
>,
|
||||
>(),
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreZkElGamalProofType::GroupedCiphertext2HandlesValidity,
|
||||
std::mem::size_of::<
|
||||
solana_zk_elgamal_proof_interface::state::ProofContextState<
|
||||
solana_zk_elgamal_proof_interface::proof_data::GroupedCiphertext2HandlesValidityProofContext,
|
||||
>,
|
||||
>(),
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreZkElGamalProofType::BatchedGroupedCiphertext2HandlesValidity,
|
||||
std::mem::size_of::<
|
||||
solana_zk_elgamal_proof_interface::state::ProofContextState<
|
||||
solana_zk_elgamal_proof_interface::proof_data::BatchedGroupedCiphertext2HandlesValidityProofContext,
|
||||
>,
|
||||
>(),
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreZkElGamalProofType::GroupedCiphertext3HandlesValidity,
|
||||
std::mem::size_of::<
|
||||
solana_zk_elgamal_proof_interface::state::ProofContextState<
|
||||
solana_zk_elgamal_proof_interface::proof_data::GroupedCiphertext3HandlesValidityProofContext,
|
||||
>,
|
||||
>(),
|
||||
),
|
||||
(
|
||||
crate::ExSolanaCoreZkElGamalProofType::BatchedGroupedCiphertext3HandlesValidity,
|
||||
std::mem::size_of::<
|
||||
solana_zk_elgamal_proof_interface::state::ProofContextState<
|
||||
solana_zk_elgamal_proof_interface::proof_data::BatchedGroupedCiphertext3HandlesValidityProofContext,
|
||||
>,
|
||||
>(),
|
||||
),
|
||||
];
|
||||
for (proof_type, expected) in variants {
|
||||
assert_eq!(proof_type.context_state_size(), expected);
|
||||
assert!(expected > 33);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn close_context_state_matches_official_accounts_and_signer() {
|
||||
let context = solana_pubkey::Pubkey::new_unique();
|
||||
let destination = solana_pubkey::Pubkey::new_unique();
|
||||
let authority = solana_pubkey::Pubkey::new_unique();
|
||||
let payer = model(solana_pubkey::Pubkey::new_unique());
|
||||
let intent = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("zk-close"),
|
||||
fee_payer: payer.clone(),
|
||||
policy: policy(&[payer, model(authority)]),
|
||||
operation: crate::ExSolanaCoreOperation::ZkElGamalCloseContextState {
|
||||
context_state: model(context),
|
||||
destination: model(destination),
|
||||
authority: model(authority),
|
||||
},
|
||||
};
|
||||
let plan = crate::executor_solana_core_builder_build_prepared_plan(&intent)
|
||||
.unwrap_or_else(|error| panic!("unexpected error: {error}"));
|
||||
assert_eq!(plan.instructions[0].data, vec![0]);
|
||||
assert_eq!(plan.instructions[0].accounts.len(), 3);
|
||||
assert!(plan.instructions[0].accounts[2].is_signer);
|
||||
assert_eq!(plan.required_signers.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn malformed_proof_shapes_are_rejected() {
|
||||
let payer = model(solana_pubkey::Pubkey::new_unique());
|
||||
let intent = crate::ExSolanaCoreExecutionIntent {
|
||||
intent_id: std::string::String::from("zk-invalid"),
|
||||
fee_payer: payer.clone(),
|
||||
policy: policy(&[payer]),
|
||||
operation: crate::ExSolanaCoreOperation::ZkElGamalVerifyInline {
|
||||
proof_type: crate::ExSolanaCoreZkElGamalProofType::ZeroCiphertext,
|
||||
proof_data: vec![0],
|
||||
context_state: std::option::Option::None,
|
||||
},
|
||||
};
|
||||
assert!(crate::executor_solana_core_builder_build_prepared_plan(&intent).is_err());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user