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

View File

@@ -0,0 +1,501 @@
// file: kb_executor_spl_memo/src/builder.rs
// version: 6
//! Official SPL Memo instruction builder and conservative plan validation.
use std::str::FromStr; // rust-rules: trait-import
pub(crate) fn build_prepared_plan(
intent: &crate::SplMemoExecutionIntent,
) -> kb_core::Result<kb_execution_api::PreparedExecutionPlan> {
if intent.intent_id.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_memo_intent_id_empty",
"SPL Memo execution intent id must not be empty",
));
}
let fee_payer = match parse_pubkey(&intent.fee_payer, "fee_payer") {
std::result::Result::Ok(pubkey) => pubkey,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
match validate_policy(intent) {
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
let (generation, message, signers) = match &intent.operation {
crate::SplMemoOperation::AddMemo { generation, message, signers } => {
(*generation, message, signers)
},
};
if message.len() > crate::MAX_MEMO_MESSAGE_BYTES {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_memo_payload_too_large",
format!(
"SPL Memo payload length {} exceeds the executor limit {}",
message.len(),
crate::MAX_MEMO_MESSAGE_BYTES
),
));
}
if signers.len() > crate::MAX_MEMO_SIGNERS {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_memo_signer_limit_exceeded",
format!(
"SPL Memo signer occurrence count {} exceeds the executor limit {}",
signers.len(),
crate::MAX_MEMO_SIGNERS
),
));
}
let program_id = match solana_pubkey::Pubkey::from_str(generation.program_id()) {
std::result::Result::Ok(pubkey) => pubkey,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_memo_program_id_invalid",
error.to_string(),
));
},
};
let mut signer_pubkeys = std::vec::Vec::with_capacity(signers.len());
for signer in signers {
let pubkey = match parse_pubkey(&signer.pubkey, "memo_signer") {
std::result::Result::Ok(pubkey) => pubkey,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
signer_pubkeys.push(pubkey);
}
let signer_refs = signer_pubkeys.iter().collect::<std::vec::Vec<_>>();
let instruction = spl_memo_interface::instruction::build_memo(
&program_id,
message.as_bytes(),
signer_refs.as_slice(),
);
let planned_instruction = planned_instruction(crate::SPL_MEMO_ADD_MEMO_OPERATION, &instruction);
let required_signers = required_signers(&intent.fee_payer, signers.as_slice());
tracing::debug!(
target: crate::TRACING_TARGET,
action = "build_prepared_plan",
intent_id = %intent.intent_id,
operation_code = crate::SPL_MEMO_ADD_MEMO_OPERATION,
program_id = generation.program_id(),
payload_length = message.len(),
signer_occurrence_count = signers.len(),
required_signer_count = required_signers.len(),
dry_run = intent.policy.dry_run,
"built SPL Memo execution plan"
);
return std::result::Result::Ok(kb_execution_api::PreparedExecutionPlan {
executor_name: std::string::String::from("kb_executor_spl_memo"),
executor_version: std::string::String::from(env!("CARGO_PKG_VERSION")),
intent_id: intent.intent_id.clone(),
operation_code: std::string::String::from(crate::SPL_MEMO_ADD_MEMO_OPERATION),
fee_payer: kb_model::Pubkey(fee_payer.to_string()),
instructions: vec![planned_instruction],
required_signers,
policy: intent.policy.clone(),
requested_spend_lamports: 0,
requested_compute_unit_price_micro_lamports: std::option::Option::None,
});
}
fn validate_policy(intent: &crate::SplMemoExecutionIntent) -> kb_core::Result<()> {
if intent.policy.simulation != kb_execution_api::ExecutionSimulationPolicy::Required {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_memo_simulation_required",
"SPL Memo execution requires simulation before signing or sending",
));
}
match intent.policy.cost_limit.max_fee_lamports {
std::option::Option::Some(limit) if limit > 0 => {},
std::option::Option::Some(_) | std::option::Option::None => {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_memo_fee_limit_missing",
"SPL Memo execution requires a positive transaction fee ceiling",
));
},
}
let validation = &intent.policy.post_execution_validation;
if !validation.canonical_insert_required
|| !validation.core_extraction_required
|| !validation.decode_replay_required
|| !validation.materialization_required
{
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_memo_post_validation_required",
"SPL Memo execution requires canonical insertion, core extraction, decode replay and materialization validation",
));
}
return std::result::Result::Ok(());
}
fn parse_pubkey(pubkey: &kb_model::Pubkey, field: &str) -> kb_core::Result<solana_pubkey::Pubkey> {
if pubkey.0.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_memo_pubkey_empty",
format!("SPL Memo {field} public key must not be empty"),
));
}
return match solana_pubkey::Pubkey::from_str(pubkey.0.as_str()) {
std::result::Result::Ok(parsed) => std::result::Result::Ok(parsed),
std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new(
"execution_spl_memo_pubkey_invalid",
format!("invalid SPL Memo {field} public key: {error}"),
)),
};
}
fn planned_instruction(
operation_code: &str,
instruction: &solana_instruction::Instruction,
) -> kb_execution_api::PlannedInstruction {
return kb_execution_api::PlannedInstruction {
program_id: kb_model::ProgramId(instruction.program_id.to_string()),
operation_code: std::string::String::from(operation_code),
accounts: instruction
.accounts
.iter()
.map(|account| {
return kb_execution_api::PlannedAccount {
pubkey: kb_model::Pubkey(account.pubkey.to_string()),
is_signer: account.is_signer,
is_writable: account.is_writable,
};
})
.collect(),
data: instruction.data.clone(),
};
}
fn required_signers(
fee_payer: &kb_model::Pubkey,
signers: &[crate::SplMemoSigner],
) -> std::vec::Vec<kb_execution_api::RequiredSigner> {
let mut required = vec![kb_execution_api::RequiredSigner {
pubkey: fee_payer.clone(),
role: std::string::String::from("fee_payer"),
}];
for signer in signers {
if required.iter().any(|candidate| return candidate.pubkey == signer.pubkey) {
continue;
}
required.push(kb_execution_api::RequiredSigner {
pubkey: signer.pubkey.clone(),
role: std::string::String::from("memo_signer"),
});
}
return required;
}
#[cfg(test)]
pub(crate) mod tests {
use std::str::FromStr; // rust-rules: trait-import
fn pubkey(value: &str) -> kb_model::Pubkey {
return kb_model::Pubkey(std::string::String::from(value));
}
fn intent(
generation: crate::SplMemoGeneration,
message: &str,
signers: &[&str],
dry_run: bool,
) -> crate::SplMemoExecutionIntent {
let fee_payer = kb_program_ids::SYSTEM_PROGRAM_ID;
let mut authorized_signers = vec![pubkey(fee_payer)];
for signer in signers {
let candidate = pubkey(signer);
if !authorized_signers.contains(&candidate) {
authorized_signers.push(candidate);
}
}
return crate::SplMemoExecutionIntent {
intent_id: std::string::String::from("memo-intent-1"),
fee_payer: pubkey(fee_payer),
policy: kb_execution_api::ExecutionPolicy {
cost_limit: kb_execution_api::ExecutionCostLimit {
max_spend_lamports: std::option::Option::Some(0),
max_fee_lamports: std::option::Option::Some(10_000),
max_compute_unit_price_micro_lamports: std::option::Option::None,
},
authorized_signers,
dry_run,
post_execution_validation: kb_execution_api::PostExecutionValidationPolicy {
canonical_insert_required: true,
core_extraction_required: true,
decode_replay_required: true,
materialization_required: true,
},
..kb_execution_api::ExecutionPolicy::default()
},
operation: crate::SplMemoOperation::AddMemo {
generation,
message: std::string::String::from(message),
signers: signers
.iter()
.map(|value| {
return crate::SplMemoSigner { pubkey: pubkey(value) };
})
.collect(),
},
};
}
fn assert_matches_official(
plan: &kb_execution_api::PreparedExecutionPlan,
generation: crate::SplMemoGeneration,
message: &str,
signers: &[&str],
) {
let program_id = solana_pubkey::Pubkey::from_str(generation.program_id())
.unwrap_or_else(|error| panic!("invalid program fixture: {error}"));
let signer_pubkeys = signers
.iter()
.map(|value| {
return solana_pubkey::Pubkey::from_str(value)
.unwrap_or_else(|error| panic!("invalid signer fixture: {error}"));
})
.collect::<std::vec::Vec<_>>();
let signer_refs = signer_pubkeys.iter().collect::<std::vec::Vec<_>>();
let official = spl_memo_interface::instruction::build_memo(
&program_id,
message.as_bytes(),
signer_refs.as_slice(),
);
assert_eq!(plan.instructions.len(), 1);
let actual = &plan.instructions[0];
assert_eq!(actual.program_id.0, official.program_id.to_string());
assert_eq!(actual.data, official.data);
assert_eq!(actual.accounts.len(), official.accounts.len());
for (actual_account, official_account) in
actual.accounts.iter().zip(official.accounts.iter())
{
assert_eq!(actual_account.pubkey.0, official_account.pubkey.to_string());
assert_eq!(actual_account.is_signer, official_account.is_signer);
assert_eq!(actual_account.is_writable, official_account.is_writable);
}
}
#[test]
fn current_generation_matches_the_official_explicit_program_builder() {
let generation = crate::SplMemoGeneration::V4;
let intent = intent(generation, "memo 🐆", &[kb_program_ids::VOTE_PROGRAM_ID], true);
let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan(
&crate::SplMemoExecutor,
&intent,
)
.unwrap_or_else(|error| panic!("Memo plan failed: {error}"));
assert_matches_official(&plan, generation, "memo 🐆", &[kb_program_ids::VOTE_PROGRAM_ID]);
}
#[test]
fn empty_payload_and_zero_signers_are_constructible() {
let intent = intent(crate::SplMemoGeneration::V4, "", &[], false);
let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan(
&crate::SplMemoExecutor,
&intent,
)
.unwrap_or_else(|error| panic!("empty Memo plan failed: {error}"));
assert!(plan.instructions[0].data.is_empty());
assert!(plan.instructions[0].accounts.is_empty());
assert_eq!(plan.required_signers.len(), 1);
assert_eq!(plan.requested_spend_lamports, 0);
}
#[test]
fn duplicate_signers_remain_ordered_readonly_accounts_but_required_signers_are_unique() {
let first = kb_program_ids::VOTE_PROGRAM_ID;
let second = kb_program_ids::STAKE_PROGRAM_ID;
let intent =
intent(crate::SplMemoGeneration::V4, "ordered", &[first, second, first], false);
let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan(
&crate::SplMemoExecutor,
&intent,
)
.unwrap_or_else(|error| panic!("ordered Memo plan failed: {error}"));
let accounts = &plan.instructions[0].accounts;
assert_eq!(accounts.len(), 3);
assert_eq!(accounts[0].pubkey.0, first);
assert_eq!(accounts[1].pubkey.0, second);
assert_eq!(accounts[2].pubkey.0, first);
assert!(accounts.iter().all(|account| return account.is_signer));
assert!(accounts.iter().all(|account| return !account.is_writable));
assert_eq!(plan.required_signers.len(), 3);
}
#[test]
fn prepared_plan_passes_common_pre_simulation_safety() {
let intent = intent(
crate::SplMemoGeneration::V4,
"safe plan",
&[kb_program_ids::VOTE_PROGRAM_ID],
true,
);
let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan(
&crate::SplMemoExecutor,
&intent,
)
.unwrap_or_else(|error| panic!("safe Memo plan failed: {error}"));
let evaluation = kb_execution_safety::ExecutionSafetyChecker
.evaluate_prepared_plan(&plan)
.unwrap_or_else(|error| panic!("safety evaluation failed: {error}"));
assert_eq!(evaluation.decision, kb_execution_safety::ExecutionSafetyDecision::Allow);
assert!(evaluation.violations.is_empty());
}
#[test]
fn current_generation_accepts_non_dry_run_plans() {
let generation = crate::SplMemoGeneration::V4;
let intent = intent(generation, "dry", &[], false);
let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan(
&crate::SplMemoExecutor,
&intent,
)
.unwrap_or_else(|error| panic!("non-dry-run Memo plan failed: {error}"));
assert!(!plan.policy.dry_run);
assert_eq!(plan.instructions[0].program_id.0, generation.program_id());
}
#[test]
fn rejects_oversize_payload_and_optional_simulation() {
let mut oversize = intent(
crate::SplMemoGeneration::V4,
std::string::String::from_utf8(vec![b'a'; crate::MAX_MEMO_MESSAGE_BYTES + 1])
.unwrap_or_else(|error| panic!("fixture creation failed: {error}"))
.as_str(),
&[],
true,
);
let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan(
&crate::SplMemoExecutor,
&oversize,
)
.expect_err("oversize payload must fail");
assert_eq!(error.code(), "execution_spl_memo_payload_too_large");
oversize.operation = crate::SplMemoOperation::AddMemo {
generation: crate::SplMemoGeneration::V4,
message: std::string::String::from("valid"),
signers: std::vec::Vec::new(),
};
oversize.policy.simulation = kb_execution_api::ExecutionSimulationPolicy::Optional;
let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan(
&crate::SplMemoExecutor,
&oversize,
)
.expect_err("optional simulation must fail");
assert_eq!(error.code(), "execution_spl_memo_simulation_required");
}
#[test]
fn every_cluster_is_constructible_and_mainnet_remains_governed_by_common_safety() {
for cluster in [
kb_execution_api::ExecutionCluster::Localnet,
kb_execution_api::ExecutionCluster::Devnet,
kb_execution_api::ExecutionCluster::Testnet,
kb_execution_api::ExecutionCluster::Mainnet,
] {
let mut intent = intent(crate::SplMemoGeneration::V4, "universal", &[], false);
intent.policy.cluster.expected_cluster = cluster;
let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan(
&crate::SplMemoExecutor,
&intent,
)
.unwrap_or_else(|error| panic!("cluster plan failed: {error}"));
assert_eq!(plan.policy.cluster.expected_cluster, cluster);
if cluster == kb_execution_api::ExecutionCluster::Mainnet {
let evaluation = kb_execution_safety::ExecutionSafetyChecker
.evaluate_prepared_plan(&plan)
.unwrap_or_else(|error| panic!("Mainnet safety evaluation failed: {error}"));
assert_eq!(evaluation.decision, kb_execution_safety::ExecutionSafetyDecision::Deny);
assert!(evaluation.violations.iter().any(|violation| {
return violation.code == "execution_mainnet_disabled";
}));
let mut explicitly_enabled = plan.clone();
explicitly_enabled.policy.cluster.allow_mainnet = true;
explicitly_enabled.policy.cluster.mainnet_confirmation = true;
let enabled_evaluation = kb_execution_safety::ExecutionSafetyChecker
.evaluate_prepared_plan(&explicitly_enabled)
.unwrap_or_else(|error| {
panic!("enabled Mainnet safety evaluation failed: {error}")
});
assert_eq!(
enabled_evaluation.decision,
kb_execution_safety::ExecutionSafetyDecision::Allow
);
}
}
}
#[test]
fn rejects_missing_fee_cap_incomplete_post_validation_and_signer_overflow() {
let mut intent = intent(crate::SplMemoGeneration::V4, "policy", &[], true);
intent.policy.cost_limit.max_fee_lamports = std::option::Option::None;
let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan(
&crate::SplMemoExecutor,
&intent,
)
.expect_err("missing fee cap must fail");
assert_eq!(error.code(), "execution_spl_memo_fee_limit_missing");
intent.policy.cost_limit.max_fee_lamports = std::option::Option::Some(10_000);
intent.policy.post_execution_validation.materialization_required = false;
let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan(
&crate::SplMemoExecutor,
&intent,
)
.expect_err("incomplete post validation must fail");
assert_eq!(error.code(), "execution_spl_memo_post_validation_required");
intent.policy.post_execution_validation.materialization_required = true;
intent.operation = crate::SplMemoOperation::AddMemo {
generation: crate::SplMemoGeneration::V4,
message: std::string::String::from("bounded"),
signers: (0..=crate::MAX_MEMO_SIGNERS)
.map(|_| {
return crate::SplMemoSigner {
pubkey: pubkey(kb_program_ids::VOTE_PROGRAM_ID),
};
})
.collect(),
};
let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan(
&crate::SplMemoExecutor,
&intent,
)
.expect_err("signer overflow must fail");
assert_eq!(error.code(), "execution_spl_memo_signer_limit_exceeded");
}
#[test]
fn machine_readable_matrix_matches_executor_policy() {
let parsed = serde_json::from_str::<serde_json::Value>(include_str!(
"../../docs/SPL_MEMO_MATRIX.json"
))
.unwrap_or_else(|error| panic!("Memo matrix parsing failed: {error}"));
let contract = parsed
.get("executorContract")
.unwrap_or_else(|| panic!("Memo executor contract is missing"));
assert_eq!(
contract.get("operationCode").and_then(serde_json::Value::as_str),
std::option::Option::Some(crate::SPL_MEMO_ADD_MEMO_OPERATION)
);
assert_eq!(
contract.get("maximumMessageBytes").and_then(serde_json::Value::as_u64),
std::option::Option::Some(crate::MAX_MEMO_MESSAGE_BYTES as u64)
);
assert_eq!(
contract.get("maximumSignerOccurrences").and_then(serde_json::Value::as_u64),
std::option::Option::Some(crate::MAX_MEMO_SIGNERS as u64)
);
assert_eq!(
contract.get("allClustersConstructible").and_then(serde_json::Value::as_bool),
std::option::Option::Some(true)
);
assert_eq!(
contract
.get("historicalGenerationSendEnabled")
.and_then(serde_json::Value::as_bool),
std::option::Option::Some(false)
);
}
}

View File

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

View File

@@ -0,0 +1,318 @@
// file: kb_executor_spl_memo/src/executor.rs
// version: 4
//! Exact capability dispatch and typed plan construction for SPL Memo.
/// SPL Memo executor implementation.
#[derive(Clone, Debug, Default)]
pub struct SplMemoExecutor;
impl crate::SplMemoExecutor {
fn exact_capability(
&self,
program_id: &kb_model::ProgramId,
operation_code: &str,
) -> kb_execution_api::ExecutionCapability {
let generation = match crate::SplMemoGeneration::from_program_id(program_id.0.as_str()) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return kb_execution_api::ExecutionCapability::unsupported(
"execution_spl_memo_program_not_owned",
format!("program {} is not owned by kb_executor_spl_memo", program_id.0),
);
},
};
if operation_code != crate::SPL_MEMO_ADD_MEMO_OPERATION {
return kb_execution_api::ExecutionCapability::unsupported(
"execution_spl_memo_operation_unsupported",
format!("SPL Memo operation {operation_code} is not implemented"),
);
}
if generation != crate::SplMemoGeneration::V4 {
return kb_execution_api::ExecutionCapability::unsupported(
"execution_spl_memo_historical_generation_decode_only",
format!(
"SPL Memo generation {generation:?} is historical decode-only; only current or experimental generations are executable"
),
);
}
return kb_execution_api::ExecutionCapability::supported(operation_code);
}
}
impl kb_execution_api::TypedInstructionExecutor for crate::SplMemoExecutor {
type Intent = crate::SplMemoExecutionIntent;
fn capability(
&self,
program_id: &kb_model::ProgramId,
operation_code: &str,
) -> kb_execution_api::ExecutionCapability {
return self.exact_capability(program_id, operation_code);
}
fn build_prepared_plan(
&self,
intent: &Self::Intent,
) -> kb_core::Result<kb_execution_api::PreparedExecutionPlan> {
let program_id = kb_model::ProgramId(std::string::String::from(
intent.operation.generation().program_id(),
));
return match self.exact_capability(&program_id, intent.operation.operation_code()) {
kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => {
crate::build_prepared_plan(intent)
},
kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason } => {
std::result::Result::Err(kb_core::Error::new(reason_code, reason))
},
};
}
}
impl kb_execution_api::InstructionExecutor for crate::SplMemoExecutor {
fn executor_name(&self) -> &'static str {
return "kb_executor_spl_memo";
}
fn executor_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn program_ids(&self) -> &'static [&'static str] {
return &[
kb_program_ids::SPL_MEMO_V1_PROGRAM_ID,
kb_program_ids::SPL_MEMO_V3_PROGRAM_ID,
kb_program_ids::SPL_MEMO_V4_PROGRAM_ID,
];
}
fn supports_request(
&self,
request: &kb_execution_api::ExecutionRequest,
) -> kb_execution_api::ExecutionSupport {
return match self.exact_capability(&request.program_id, &request.operation_code) {
kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => {
kb_execution_api::ExecutionSupport::Yes
},
kb_execution_api::ExecutionCapability::Unsupported { reason_code: _, reason: _ } => {
kb_execution_api::ExecutionSupport::No
},
};
}
fn build_plan(
&self,
request: &kb_execution_api::ExecutionRequest,
) -> kb_core::Result<kb_execution_api::ExecutionPlan> {
match self.exact_capability(&request.program_id, &request.operation_code) {
kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => {},
kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason } => {
return std::result::Result::Err(kb_core::Error::new(reason_code, reason));
},
}
let intent =
match serde_json::from_str::<crate::SplMemoExecutionIntent>(&request.payload_json) {
std::result::Result::Ok(intent) => intent,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_memo_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.generation().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.generation().program_id()
),
));
}
let prepared =
match kb_execution_api::TypedInstructionExecutor::build_prepared_plan(self, &intent) {
std::result::Result::Ok(plan) => plan,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let payload_value = match serde_json::to_value(&prepared) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_memo_plan_serialize_failed",
error.to_string(),
));
},
};
let payload_json = match kb_execution_api::serialize_payload_json(&payload_value) {
std::result::Result::Ok(serialized) => serialized,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(kb_execution_api::ExecutionPlan {
executor_name: std::string::String::from("kb_executor_spl_memo"),
instruction_count: prepared.instructions.len(),
payload_json,
});
}
}
#[cfg(test)]
mod tests {
fn request(
program_id: &str,
operation_code: &str,
payload_json: std::string::String,
) -> kb_execution_api::ExecutionRequest {
return kb_execution_api::ExecutionRequest {
program_id: kb_model::ProgramId(program_id.to_string()),
operation_code: operation_code.to_string(),
payload_json,
};
}
fn pubkey(value: &str) -> kb_model::Pubkey {
return kb_model::Pubkey(std::string::String::from(value));
}
fn intent(
generation: crate::SplMemoGeneration,
message: &str,
signers: &[&str],
dry_run: bool,
) -> crate::SplMemoExecutionIntent {
let fee_payer = kb_program_ids::SYSTEM_PROGRAM_ID;
let mut authorized_signers = vec![pubkey(fee_payer)];
for signer in signers {
let candidate = pubkey(signer);
if !authorized_signers.contains(&candidate) {
authorized_signers.push(candidate);
}
}
return crate::SplMemoExecutionIntent {
intent_id: std::string::String::from("memo-intent-1"),
fee_payer: pubkey(fee_payer),
policy: kb_execution_api::ExecutionPolicy {
cost_limit: kb_execution_api::ExecutionCostLimit {
max_spend_lamports: std::option::Option::Some(0),
max_fee_lamports: std::option::Option::Some(10_000),
max_compute_unit_price_micro_lamports: std::option::Option::None,
},
authorized_signers,
dry_run,
post_execution_validation: kb_execution_api::PostExecutionValidationPolicy {
canonical_insert_required: true,
core_extraction_required: true,
decode_replay_required: true,
materialization_required: true,
},
..kb_execution_api::ExecutionPolicy::default()
},
operation: crate::SplMemoOperation::AddMemo {
generation,
message: std::string::String::from(message),
signers: signers
.iter()
.map(|value| {
return crate::SplMemoSigner { pubkey: pubkey(value) };
})
.collect(),
},
};
}
#[test]
fn exact_capabilities_support_only_current_v4_add_memo() {
let executor = crate::SplMemoExecutor;
for program_id in
[kb_program_ids::SPL_MEMO_V1_PROGRAM_ID, kb_program_ids::SPL_MEMO_V3_PROGRAM_ID]
{
let request = request(
program_id,
crate::SPL_MEMO_ADD_MEMO_OPERATION,
std::string::String::from("{}"),
);
assert_eq!(
kb_execution_api::InstructionExecutor::supports_request(&executor, &request),
kb_execution_api::ExecutionSupport::No
);
}
let current = request(
kb_program_ids::SPL_MEMO_V4_PROGRAM_ID,
crate::SPL_MEMO_ADD_MEMO_OPERATION,
std::string::String::from("{}"),
);
assert_eq!(
kb_execution_api::InstructionExecutor::supports_request(&executor, &current),
kb_execution_api::ExecutionSupport::Yes
);
let unsupported_operation = request(
kb_program_ids::SPL_MEMO_V4_PROGRAM_ID,
"spl_memo.remove_memo",
std::string::String::from("{}"),
);
assert_eq!(
kb_execution_api::InstructionExecutor::supports_request(
&executor,
&unsupported_operation,
),
kb_execution_api::ExecutionSupport::No
);
let foreign = request(
kb_program_ids::SYSTEM_PROGRAM_ID,
crate::SPL_MEMO_ADD_MEMO_OPERATION,
std::string::String::from("{}"),
);
assert_eq!(
kb_execution_api::InstructionExecutor::supports_request(&executor, &foreign),
kb_execution_api::ExecutionSupport::No
);
}
#[test]
fn generic_request_roundtrips_the_typed_plan() {
let intent = intent(
crate::SplMemoGeneration::V4,
"generic",
&[kb_program_ids::VOTE_PROGRAM_ID],
false,
);
let payload_json = serde_json::to_string(&intent)
.unwrap_or_else(|error| panic!("intent serialization failed: {error}"));
let request = request(
kb_program_ids::SPL_MEMO_V4_PROGRAM_ID,
crate::SPL_MEMO_ADD_MEMO_OPERATION,
payload_json,
);
let plan =
kb_execution_api::InstructionExecutor::build_plan(&crate::SplMemoExecutor, &request)
.unwrap_or_else(|error| panic!("generic plan failed: {error}"));
assert_eq!(plan.instruction_count, 1);
let prepared =
serde_json::from_str::<kb_execution_api::PreparedExecutionPlan>(&plan.payload_json)
.unwrap_or_else(|error| panic!("prepared plan parsing failed: {error}"));
assert_eq!(prepared.instructions[0].data, b"generic");
}
#[test]
fn historical_generations_are_decode_only_with_stable_reason() {
for generation in [crate::SplMemoGeneration::V1, crate::SplMemoGeneration::V3] {
let intent = intent(generation, "historical", &[], true);
let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan(
&crate::SplMemoExecutor,
&intent,
)
.expect_err("historical Memo generation must not build a plan");
assert_eq!(error.code(), "execution_spl_memo_historical_generation_decode_only");
}
}
}

View File

@@ -0,0 +1,112 @@
// file: kb_executor_spl_memo/src/intent.rs
// version: 3
//! Typed SPL Memo execution intents.
use ts_rs::TS; // rust-rules: derive-import
/// Stable operation code for adding one SPL Memo annotation.
pub const SPL_MEMO_ADD_MEMO_OPERATION: &str = "spl_memo.add_memo";
/// Conservative UTF-8 payload bound used before transaction assembly.
pub const MAX_MEMO_MESSAGE_BYTES: usize = 566;
/// Conservative bound on ordered signer occurrences accepted by one intent.
pub const MAX_MEMO_SIGNERS: usize = 32;
/// Exact SPL Memo program generation.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_executor_spl_memo/intent/SplMemoGeneration.ts"
)]
pub enum SplMemoGeneration {
/// Historical v1 program, whose runtime ignores supplied accounts.
V1,
/// Historical v3 program, whose runtime requires every supplied account to sign.
V3,
/// Current v4 program, whose runtime requires every supplied account to sign.
V4,
}
impl crate::SplMemoGeneration {
/// Returns the exact Program ID for this generation.
pub fn program_id(&self) -> &'static str {
return match self {
Self::V1 => kb_program_ids::SPL_MEMO_V1_PROGRAM_ID,
Self::V3 => kb_program_ids::SPL_MEMO_V3_PROGRAM_ID,
Self::V4 => kb_program_ids::SPL_MEMO_V4_PROGRAM_ID,
};
}
pub(crate) fn from_program_id(program_id: &str) -> std::option::Option<Self> {
return match program_id {
kb_program_ids::SPL_MEMO_V1_PROGRAM_ID => std::option::Option::Some(Self::V1),
kb_program_ids::SPL_MEMO_V3_PROGRAM_ID => std::option::Option::Some(Self::V3),
kb_program_ids::SPL_MEMO_V4_PROGRAM_ID => std::option::Option::Some(Self::V4),
_ => std::option::Option::None,
};
}
}
/// One ordered signer account supplied to the Memo instruction.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_executor_spl_memo/intent/SplMemoSigner.ts"
)]
pub struct SplMemoSigner {
/// Signer public key; duplicates are preserved in instruction order.
pub pubkey: kb_model::Pubkey,
}
/// Typed SPL Memo operation arguments.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[serde(tag = "operation", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_executor_spl_memo/intent/SplMemoOperation.ts"
)]
pub enum SplMemoOperation {
/// Add one exact UTF-8 Memo payload with ordered readonly signer accounts.
AddMemo {
/// Exact Memo program generation.
generation: crate::SplMemoGeneration,
/// UTF-8 text whose bytes become the complete instruction payload.
message: std::string::String,
/// Ordered signer accounts. Duplicates are preserved.
signers: std::vec::Vec<crate::SplMemoSigner>,
},
}
impl crate::SplMemoOperation {
/// Returns the stable operation code.
pub fn operation_code(&self) -> &'static str {
return match self {
Self::AddMemo { .. } => crate::SPL_MEMO_ADD_MEMO_OPERATION,
};
}
/// Returns the exact requested program generation.
pub fn generation(&self) -> crate::SplMemoGeneration {
return match self {
Self::AddMemo { generation, .. } => *generation,
};
}
}
/// Complete typed intent accepted by the SPL Memo executor.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_executor_spl_memo/intent/SplMemoExecutionIntent.ts"
)]
pub struct SplMemoExecutionIntent {
/// Stable caller-provided identifier used for logs and replay correlation.
pub intent_id: std::string::String,
/// Transaction fee payer.
pub fee_payer: kb_model::Pubkey,
/// Conservative execution policy.
pub policy: kb_execution_api::ExecutionPolicy,
/// Typed Memo operation and arguments.
pub operation: crate::SplMemoOperation,
}

View File

@@ -0,0 +1,34 @@
// file: kb_executor_spl_memo/src/lib.rs
// version: 6
//! Executor crate for `spl_memo`.
#![warn(missing_docs)]
#![deny(unreachable_pub)]
#![forbid(unsafe_code)]
mod builder;
mod constants;
mod executor;
mod intent;
/// Crate-root access to `build_prepared_plan` from `builder`.
pub(crate) use crate::builder::build_prepared_plan;
/// Canonical tracing target for this crate.
pub(crate) use crate::constants::TRACING_TARGET;
/// Exposes the SPL Memo executor type implemented by this crate.
pub use crate::executor::SplMemoExecutor;
/// Maximum UTF-8 memo payload accepted by this executor.
pub use crate::intent::MAX_MEMO_MESSAGE_BYTES;
/// Maximum ordered signer occurrences accepted by this executor.
pub use crate::intent::MAX_MEMO_SIGNERS;
/// Stable operation code for adding one SPL Memo annotation.
pub use crate::intent::SPL_MEMO_ADD_MEMO_OPERATION;
/// Exposes the typed SPL Memo execution intent.
pub use crate::intent::SplMemoExecutionIntent;
/// Exposes the exact SPL Memo program generation.
pub use crate::intent::SplMemoGeneration;
/// Exposes the typed SPL Memo operation.
pub use crate::intent::SplMemoOperation;
/// Exposes one ordered SPL Memo signer declaration.
pub use crate::intent::SplMemoSigner;