Files
khadhroony-bot3/kb-lib/src/decoder/spl/token/decoder.rs
2026-07-30 10:27:06 +02:00

514 lines
20 KiB
Rust

// file: kb-lib/src/decoder/spl/token/decoder.rs
// version: 4
//! Exact classic SPL Token dispatch for the common decode pipeline.
const TOKEN_SURFACES: &[crate::DcApiDecoderSurface] = &[crate::DcApiDecoderSurface {
program_id: kb_program_ids::SPL_TOKEN_PROGRAM_ID,
surface_code: crate::DC_SPL_TOKEN_SURFACE_CODE,
priority: 100,
}];
const PROGRAM_IDS: &[&str] = &[kb_program_ids::SPL_TOKEN_PROGRAM_ID];
/// Exact decoder for the classic SPL Token program.
#[derive(Clone, Debug, Default)]
pub struct DcSplTokenDecoder;
impl crate::DcApiProtocolDecoder for crate::DcSplTokenDecoder {
fn decoder_name(&self) -> &'static str {
return "kb-lib.decoder.spl.token";
}
fn decoder_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn program_ids(&self) -> &'static [&'static str] {
return PROGRAM_IDS;
}
fn supports_observation(
&self,
observation: &crate::MdProgramObservation,
) -> crate::DcApiDecoderSupport {
return if crate::DcApiProtocolDecoder::handles_program_id(self, &observation.program_id) {
crate::DcApiDecoderSupport::Yes
} else {
crate::DcApiDecoderSupport::No
};
}
fn decode_observation(
&self,
_observation: &crate::MdProgramObservation,
) -> kb_core::Result<std::vec::Vec<crate::MdDecodedProtocolEvent>> {
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::DcApiInstructionDecoder for crate::DcSplTokenDecoder {
fn identity(&self) -> crate::DcApiDecoderIdentity {
return crate::DcApiDecoderIdentity {
name: "spl.token".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn surfaces(&self) -> &'static [crate::DcApiDecoderSurface] {
return TOKEN_SURFACES;
}
fn coverage(&self) -> std::vec::Vec<crate::DcApiDecoderCoverageDeclaration> {
return crate::DC_SPL_TOKEN_INSTRUCTION_ENTRIES
.iter()
.map(|(tag, code, historical)| {
return crate::DcApiDecoderCoverageDeclaration {
program_id: kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string(),
surface_code: std::option::Option::Some(
crate::DC_SPL_TOKEN_SURFACE_CODE.to_string(),
),
entry_kind: crate::DcApiDecoderCoverageEntryKind::Instruction,
entry_code: (*code).to_string(),
discriminator_hex: std::option::Option::Some(format!("{tag:02x}")),
historical: *historical,
};
})
.collect();
}
fn recognize(
&self,
input: &crate::MdCoreInstructionReplayInput,
) -> crate::DcApiDecoderRecognition {
if input.program_id != kb_program_ids::SPL_TOKEN_PROGRAM_ID {
return crate::DcApiDecoderRecognition::incompatible();
}
let tag = crate::decoder_spl_token_payload_tag(input);
let entry = tag.and_then(crate::decoder_spl_token_entry_for_tag);
return crate::DcApiDecoderRecognition::compatible(
entry.is_some(),
100,
std::option::Option::Some(crate::DC_SPL_TOKEN_SURFACE_CODE.to_string()),
entry.map(|(_, code, _)| return code.to_string()),
tag.map(|value| return format!("{value:02x}")),
);
}
fn decode(
&self,
input: &crate::MdCoreInstructionReplayInput,
) -> crate::DcApiDecoderExecutionResult {
if input.program_id != kb_program_ids::SPL_TOKEN_PROGRAM_ID {
return crate::DcApiDecoderExecutionResult::unsupported(std::option::Option::None);
}
let result = crate::decoder_spl_token_decode(input);
if matches!(
result.status,
crate::DcApiDecoderOutcomeStatus::Failed
| crate::DcApiDecoderOutcomeStatus::Unsupported
) {
tracing::error!(
target: crate::TRACING_TARGET_DECODER_SPL_TOKEN,
action = "decode_failure",
signature = %input.signature,
slot = input.slot,
instruction_path = %input.instruction_path,
program_id = %input.program_id,
processor_name = "spl.token",
processor_version = env!("CARGO_PKG_VERSION"),
input_key = %input.replay_input_key,
payload_hash = ?input.instruction_payload_hash,
result_status = ?result.status,
diagnostics = ?result.diagnostics,
"classic SPL Token instruction was not decoded successfully"
);
}
tracing::debug!(
target: crate::TRACING_TARGET_DECODER_SPL_TOKEN,
action = "decode",
signature = %input.signature,
instruction_path = %input.instruction_path,
transaction_failed = input.transaction_failed,
result_status = ?result.status,
observation_count = result.observations.len(),
"classic SPL Token instruction decode completed"
);
return result;
}
}
#[cfg(test)]
mod tests {
use base64::Engine; // rust-rules: trait-import
fn input(
program_id: &str,
payload: &[u8],
accounts: serde_json::Value,
keys: serde_json::Value,
failed: bool,
path: &str,
) -> crate::MdCoreInstructionReplayInput {
let result = crate::MdCoreInstructionReplayInput::new(
format!("signature:{path}"),
"signature",
42,
path,
program_id,
failed,
if failed {
std::option::Option::Some(serde_json::json!({"InstructionError":[0,"Custom"]}))
} else {
std::option::Option::None
},
keys,
accounts,
std::option::Option::Some(serde_json::json!({
"dataBase64": base64::engine::general_purpose::STANDARD.encode(payload)
})),
std::option::Option::None,
serde_json::json!([]),
serde_json::json!([]),
serde_json::json!([]),
serde_json::json!([]),
);
return match result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("Token replay input failed: {error}"),
};
}
fn minimal_wire(tag: u8) -> std::vec::Vec<u8> {
return match tag {
0 | 20 => {
let mut value = std::vec![tag, 9];
value.extend_from_slice(&[1; 32]);
value.push(0);
value
},
2 | 19 => std::vec![tag, 1],
3 | 4 | 7 | 8 | 23 => {
let mut value = std::vec![tag];
value.extend_from_slice(&1_u64.to_le_bytes());
value
},
6 => std::vec![tag, 0, 0],
12 | 13 | 14 | 15 => {
let mut value = std::vec![tag];
value.extend_from_slice(&1_u64.to_le_bytes());
value.push(9);
value
},
16 | 18 => {
let mut value = std::vec![tag];
value.extend_from_slice(&[2; 32]);
value
},
24 => std::vec![tag, b'1'],
45 => std::vec![tag, 0],
255 => std::vec![tag, 0, 1, 1],
_ => std::vec![tag],
};
}
fn account(position: usize, signer: bool, writable: bool) -> serde_json::Value {
return serde_json::json!({
"position": position,
"accountIndex": position,
"accountKey": format!("account{position}"),
"signer": signer,
"writable": writable,
"source": "static",
});
}
#[test]
fn exact_support_program_id_and_interface_match() {
assert_eq!(spl_token_interface::ID.to_string(), kb_program_ids::SPL_TOKEN_PROGRAM_ID);
let decoder = crate::DcSplTokenDecoder;
assert_eq!(crate::DcApiInstructionDecoder::surfaces(&decoder).len(), 1);
let observation = crate::MdProgramObservation {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(1),
instruction_path: crate::MdInstructionPath("0".to_string()),
program_id: crate::MdProgramId(kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string()),
discriminator_8: std::option::Option::None,
data_len: 1,
accounts_len: 0,
failed: false,
};
assert_eq!(
crate::DcApiProtocolDecoder::supports_observation(&decoder, &observation),
crate::DcApiDecoderSupport::Yes
);
}
#[test]
fn coverage_matrix_and_compiled_entries_are_equal() {
let matrix: serde_json::Value = match serde_json::from_str(include_str!(
"../../../../../test-fixtures/contract-matrices/SPL_TOKEN_MATRIX.json"
)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("SPL Token matrix is invalid: {error}"),
};
let rows = match matrix.get("instructions").and_then(serde_json::Value::as_array) {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("SPL Token matrix has no instructions"),
};
let matrix_entries = rows
.iter()
.map(|row| {
return (
row.get("tag").and_then(serde_json::Value::as_u64).unwrap_or(u64::MAX) as u8,
row.get("name").and_then(serde_json::Value::as_str).unwrap_or(""),
);
})
.collect::<std::vec::Vec<_>>();
let compiled_entries = crate::DC_SPL_TOKEN_INSTRUCTION_ENTRIES
.iter()
.map(|(tag, code, _)| return (*tag, *code))
.collect::<std::vec::Vec<_>>();
assert_eq!(matrix_entries, compiled_entries);
assert_eq!(crate::DcApiInstructionDecoder::coverage(&crate::DcSplTokenDecoder).len(), 28);
}
#[test]
fn cluster_evidence_scopes_recent_instruction_deployment_exactly() {
let matrix: serde_json::Value = match serde_json::from_str(include_str!(
"../../../../../test-fixtures/contract-matrices/SPL_TOKEN_MATRIX.json"
)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("SPL Token matrix is invalid: {error}"),
};
assert_eq!(
matrix["deploymentAudit"]["mainnet"]["status"],
"real_corpus_decode_and_materialization_validated"
);
assert_eq!(matrix["corpusAudit"]["mainnet"]["invariants"]["uncommittedOutputs"], 0);
assert_eq!(matrix["corpusAudit"]["mainnet"]["invariants"]["secondReplaySelected"], 0);
assert_eq!(matrix["corpusAudit"]["devnet"]["recentInstructionProbe"]["submission"], false);
assert_eq!(
matrix["corpusAudit"]["devnet"]["recentInstructionProbe"]["batch"]["success"],
true
);
assert_eq!(
matrix["corpusAudit"]["devnet"]["recentInstructionProbe"]["batch"]["computeUnits"],
270
);
assert_eq!(
matrix["corpusAudit"]["devnet"]["recentInstructionProbe"]["unwrapLamports"]["success"],
true
);
assert_eq!(
matrix["corpusAudit"]["devnet"]["recentInstructionProbe"]["unwrapLamports"]["computeUnits"],
140
);
let instructions = match matrix.get("instructions").and_then(serde_json::Value::as_array) {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("SPL Token matrix has no instructions"),
};
for recent_name in ["unwrap_lamports", "batch"] {
let recent = match instructions.iter().find(|row| {
return row.get("name").and_then(serde_json::Value::as_str)
== std::option::Option::Some(recent_name);
}) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
panic!("recent SPL Token instruction {recent_name} is absent")
},
};
assert_eq!(recent["executorSupport"]["constructibility"], "official_builder_proven");
assert_eq!(
recent["executorSupport"]["clusterDeployment"],
"devnet_simulation_succeeded_2026-07-16"
);
assert_eq!(recent["clusterStatus"]["localnet"], "not_tested");
assert_eq!(recent["clusterStatus"]["devnet"], "simulation_succeeded_2026-07-16");
assert_eq!(recent["clusterStatus"]["mainnet"], "not_tested");
assert_eq!(recent["realSignatures"].as_array().map(std::vec::Vec::len), Some(0));
}
}
#[test]
fn every_published_tag_decodes_and_matches_official_unpack() {
for (tag, code, _) in crate::DC_SPL_TOKEN_INSTRUCTION_ENTRIES {
let wire = minimal_wire(*tag);
let official = spl_token_interface::instruction::TokenInstruction::unpack(&wire);
assert!(official.is_ok(), "official unpack rejected tag {tag}");
let input = input(
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
wire.as_slice(),
serde_json::json!([]),
serde_json::json!([]),
false,
"0",
);
let result = crate::DcApiInstructionDecoder::decode(&crate::DcSplTokenDecoder, &input);
assert_eq!(result.status, crate::DcApiDecoderOutcomeStatus::Decoded);
assert_eq!(result.recognized_entry_code.as_deref(), std::option::Option::Some(*code));
}
}
#[test]
fn failed_transfer_is_an_exact_uncommitted_inner_intent_without_invented_mint() {
let mut wire = std::vec![3];
wire.extend_from_slice(&u64::MAX.to_le_bytes());
let accounts = serde_json::json!([
{"position":0,"accountIndex":0,"accountKey":"source"},
{"position":1,"accountIndex":1,"accountKey":"destination"},
{"position":2,"accountIndex":2,"accountKey":"authority"}
]);
let keys = serde_json::json!([
{"accountIndex":0,"accountKey":"source","signer":false,"writable":true,"source":"static"},
{"accountIndex":1,"accountKey":"destination","signer":false,"writable":true,"source":"static"},
{"accountIndex":2,"accountKey":"authority","signer":true,"writable":false,"source":"static"}
]);
let input = input(
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
wire.as_slice(),
accounts,
keys,
true,
"2/1",
);
let result = crate::DcApiInstructionDecoder::decode(&crate::DcSplTokenDecoder, &input);
assert_eq!(result.status, crate::DcApiDecoderOutcomeStatus::Decoded);
assert!(!result.observations[0].observation_committed);
assert_eq!(
result.observations[0].payload_json["parameters"]["amountRaw"],
u64::MAX.to_string()
);
assert_eq!(result.observations[0].payload_json["inference"]["mintInvented"], false);
assert_eq!(
result.observations[0].event.source_kind,
crate::MdEventSourceKind::InnerInstruction
);
}
#[test]
fn suffix_unknown_empty_truncated_and_invalid_utf8_are_bounded() {
let mut transfer = minimal_wire(3);
transfer.extend_from_slice(&[0xaa, 0xbb]);
let suffix = input(
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
transfer.as_slice(),
serde_json::json!([]),
serde_json::json!([]),
false,
"0",
);
let suffix_result =
crate::DcApiInstructionDecoder::decode(&crate::DcSplTokenDecoder, &suffix);
assert_eq!(suffix_result.status, crate::DcApiDecoderOutcomeStatus::Decoded);
assert_eq!(suffix_result.observations[0].payload_json["wire"]["suffixLengthBytes"], 2);
for wire in [std::vec::Vec::new(), std::vec![3, 1], std::vec![24, 0xff], std::vec![255]] {
let input = input(
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
wire.as_slice(),
serde_json::json!([]),
serde_json::json!([]),
false,
"0",
);
let result = crate::DcApiInstructionDecoder::decode(&crate::DcSplTokenDecoder, &input);
assert_eq!(result.status, crate::DcApiDecoderOutcomeStatus::Failed);
assert!(result.observations.is_empty());
}
let unknown = input(
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
&[44],
serde_json::json!([]),
serde_json::json!([]),
false,
"0",
);
let result = crate::DcApiInstructionDecoder::decode(&crate::DcSplTokenDecoder, &unknown);
assert_eq!(result.status, crate::DcApiDecoderOutcomeStatus::Unsupported);
assert!(!result.diagnostics.is_empty());
}
#[test]
fn batch_preserves_order_slices_paths_and_rejects_nested_batch() {
let mut batch = std::vec![255, 0, 9, 3];
batch.extend_from_slice(&1_u64.to_le_bytes());
batch.extend_from_slice(&[0, 1, 17]);
let cinput = input(
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
batch.as_slice(),
serde_json::json!([]),
serde_json::json!([]),
false,
"4/2",
);
let result = crate::DcApiInstructionDecoder::decode(&crate::DcSplTokenDecoder, &cinput);
assert_eq!(result.status, crate::DcApiDecoderOutcomeStatus::Decoded);
assert_eq!(result.observations.len(), 3);
assert_eq!(result.observations[1].event.instruction_path.0, "4/2/batch/0");
assert_eq!(result.observations[2].event.instruction_path.0, "4/2/batch/1");
assert_eq!(result.observations[0].payload_json["parameters"]["subInstructionCount"], 2);
let nested = input(
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
&[255, 0, 1, 255],
serde_json::json!([]),
serde_json::json!([]),
false,
"0",
);
let nested_result =
crate::DcApiInstructionDecoder::decode(&crate::DcSplTokenDecoder, &nested);
assert_eq!(nested_result.status, crate::DcApiDecoderOutcomeStatus::Failed);
}
#[test]
fn authority_forms_and_multisig_threshold_diagnostics_are_explicit() {
let wire = minimal_wire(3);
let simple_accounts = serde_json::json!([
{"position":0,"accountIndex":0,"accountKey":"account0"},
{"position":1,"accountIndex":1,"accountKey":"account1"},
{"position":2,"accountIndex":2,"accountKey":"account2"}
]);
let simple_keys = serde_json::json!([
account(0, false, true),
account(1, false, true),
account(2, true, false)
]);
let simple = input(
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
wire.as_slice(),
simple_accounts.clone(),
simple_keys,
false,
"0",
);
let simple_result =
crate::DcApiInstructionDecoder::decode(&crate::DcSplTokenDecoder, &simple);
assert_eq!(simple_result.observations[0].payload_json["authority"]["form"], "single");
let mut multisig_accounts = simple_accounts.as_array().cloned().unwrap_or_default();
multisig_accounts
.push(serde_json::json!({"position":3,"accountIndex":3,"accountKey":"account3"}));
let multisig_keys = serde_json::json!([
account(0, false, true),
account(1, false, true),
account(2, false, false),
account(3, true, false)
]);
let multisig = input(
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
wire.as_slice(),
serde_json::Value::Array(multisig_accounts),
multisig_keys,
false,
"0",
);
let multisig_result =
crate::DcApiInstructionDecoder::decode(&crate::DcSplTokenDecoder, &multisig);
assert_eq!(multisig_result.observations[0].payload_json["authority"]["form"], "multisig");
assert_eq!(
multisig_result.observations[0].payload_json["authority"]["statefulMultisigValidation"],
"requires_multisig_account_snapshot"
);
}
}