v0.1.0-pre.011

This commit is contained in:
2026-07-24 14:23:58 +02:00
parent 62bc0ffb44
commit 42e8a203ec
460 changed files with 12005 additions and 7020 deletions

View File

@@ -1,10 +1,641 @@
// file: kb-lib/src/materializer/risk/core.rs
// version: 1
// version: 2
//! Migration boundary for legacy crate `kb_materializer_risk`.
//! Stable fact-only risk projections for committed SPL Token and ATA observations.
/// Legacy crate name retained for migration and compatibility tracking.
pub const LEGACY_CRATE: &str = "kb_materializer_risk";
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::TokenAccount,
crate::MdEventFamily::TokenMint,
crate::MdEventFamily::Admin,
crate::MdEventFamily::Lifecycle,
crate::MdEventFamily::Audit,
];
const RISK_ENTRIES: &[&str] = &[
"approve",
"approve_checked",
"revoke",
"freeze_account",
"thaw_account",
"set_authority",
"initialize_multisig",
"initialize_multisig2",
"initialize_default_account_state",
"update_default_account_state",
"enable_required_transfer_memos",
"disable_required_transfer_memos",
"enable_cpi_guard",
"disable_cpi_guard",
"initialize_non_transferable_mint",
"initialize_pausable_config",
"pause",
"resume",
"initialize_permissioned_burn",
"enable_confidential_credits",
"disable_confidential_credits",
"enable_non_confidential_credits",
"disable_non_confidential_credits",
];
/// Current porting status.
pub const MIGRATION_STATUS: &str = "source-preserved-pending-port";
/// Stable fact-only classic SPL Token risk materializer.
#[derive(Clone, Debug, Default)]
pub struct MtRiskMaterializer;
impl crate::MtMaterializer for crate::MtRiskMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb_materializer_risk";
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return accepts_event(event);
}
fn materialize_event(
&self,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtRiskMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_RISK_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return accepts_event(&observation.event);
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
if !accepts_event(&observation.event) {
return crate::MtApiMaterializerExecutionResult::ignored();
}
if observation.transaction_failed || !observation.observation_committed {
return crate::MtApiMaterializerExecutionResult::refused(
"uncommitted_token_risk_fact_refused",
"failed or uncommitted SPL Token or ATA observations cannot create risk facts",
);
}
let ata = is_ata_recovery(&observation.event);
if ata && !ata_addresses_valid(&observation.payload_json) {
return crate::MtApiMaterializerExecutionResult::refused(
"invalid_nested_ata_risk_fact_refused",
"nested ATA risk fact requires all three canonical address validations",
);
}
let operation = observation.event.event_name.0.as_str();
let parameters = observation
.payload_json
.get("parameters")
.cloned()
.unwrap_or(serde_json::Value::Null);
let accounts = observation
.payload_json
.get("accounts")
.cloned()
.unwrap_or(serde_json::Value::Null);
let authority = observation
.payload_json
.get("authority")
.cloned()
.unwrap_or(serde_json::Value::Null);
let addresses = observation
.payload_json
.get("addresses")
.cloned()
.unwrap_or(serde_json::Value::Null);
let risk_kind = match risk_kind(operation, &parameters, &accounts) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return crate::MtApiMaterializerExecutionResult::ignored();
},
};
let output = crate::MtApiMaterializedOutput {
output_key: if ata {
format!("ata_risk_fact:{risk_kind}:0")
} else {
format!("spl_token_risk_fact:{risk_kind}:0")
},
family: crate::MdMaterializedEventFamily::Risk,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_RISK_PROJECTION_VERSION,
"domain": if ata { "spl_associated_token_account_risk_fact" } else { "spl_token_risk_fact" },
"riskKind": risk_kind,
"operation": operation,
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"programId": observation.event.program_id.0.clone(),
"parameters": parameters,
"accounts": accounts,
"authority": authority,
"addresses": addresses,
"interpretation": {
"score": serde_json::Value::Null,
"severity": serde_json::Value::Null,
"factOnly": true
},
"committed": true,
"provenance": {
"processorName": crate::MT_RISK_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"sourceEventKey": observation.event_key.clone()
}
}),
};
tracing::debug!(target: crate::MT_RISK_TRACING_TARGET, action = "materialize_token_risk_fact", risk_kind, operation, ata, "materialize committed SPL Token or ATA risk fact");
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
}
fn accepts_event(event: &crate::MdDecodedProtocolEvent) -> bool {
let token_program = (event.program_id.0 == kb_program_ids::SPL_TOKEN_PROGRAM_ID
&& event.surface_code.0 == "spl_token")
|| (event.program_id.0 == kb_program_ids::SPL_TOKEN2022_PROGRAM_ID
&& event.surface_code.0 == "spl_token2022");
let token_risk = token_program && RISK_ENTRIES.contains(&event.event_name.0.as_str());
return ACCEPTED_FAMILIES.contains(&event.event_family)
&& (token_risk || is_ata_recovery(event));
}
fn is_ata_recovery(event: &crate::MdDecodedProtocolEvent) -> bool {
return event.program_id.0 == kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID
&& event.surface_code.0 == "spl_associated_token_account"
&& event.event_family == crate::MdEventFamily::Lifecycle
&& event.event_name.0 == "recover_nested";
}
fn ata_addresses_valid(payload: &serde_json::Value) -> bool {
let addresses = match payload.get("addresses") {
std::option::Option::Some(value) => value,
std::option::Option::None => return false,
};
let token_supported = addresses
.get("tokenProgram")
.and_then(|value| return value.get("supported"))
.and_then(serde_json::Value::as_bool)
== std::option::Option::Some(true);
let token_program_id = addresses
.get("tokenProgram")
.and_then(|value| return value.get("programId"))
.and_then(serde_json::Value::as_str);
let token_program_exact = matches!(
token_program_id,
std::option::Option::Some(kb_program_ids::SPL_TOKEN_PROGRAM_ID)
| std::option::Option::Some(kb_program_ids::SPL_TOKEN2022_PROGRAM_ID)
);
return token_supported
&& token_program_exact
&& [
"ownerAssociatedTokenAccount",
"nestedAssociatedTokenAccount",
"walletNestedMintAssociatedTokenAccount",
]
.iter()
.all(|field| {
let address = match addresses.get(*field) {
std::option::Option::Some(value) => value,
std::option::Option::None => return false,
};
return address
.get("observed")
.and_then(serde_json::Value::as_str)
.is_some_and(|value| return !value.is_empty())
&& address.get("valid").and_then(serde_json::Value::as_bool)
== std::option::Option::Some(true);
});
}
fn risk_kind(
operation: &str,
parameters: &serde_json::Value,
accounts: &serde_json::Value,
) -> std::option::Option<&'static str> {
return match operation {
"recover_nested" => std::option::Option::Some("nested_ata_anti_pattern_recovered"),
"approve" | "approve_checked" => std::option::Option::Some("delegation_allowance_granted"),
"revoke" => std::option::Option::Some("delegation_revoked"),
"freeze_account" => std::option::Option::Some("account_frozen"),
"thaw_account" => std::option::Option::Some("account_thawed"),
"set_authority"
if parameters.get("newAuthority").is_some_and(serde_json::Value::is_null) =>
{
std::option::Option::Some("authority_revoked")
},
"set_authority" => std::option::Option::Some("authority_changed"),
"initialize_default_account_state" | "update_default_account_state" => {
std::option::Option::Some("default_account_state_configured")
},
"enable_required_transfer_memos" => {
std::option::Option::Some("required_transfer_memos_enabled")
},
"disable_required_transfer_memos" => {
std::option::Option::Some("required_transfer_memos_disabled")
},
"enable_cpi_guard" => std::option::Option::Some("cpi_guard_enabled"),
"disable_cpi_guard" => std::option::Option::Some("cpi_guard_disabled"),
"initialize_non_transferable_mint" => {
std::option::Option::Some("non_transferable_mint_configured")
},
"initialize_pausable_config" => std::option::Option::Some("pausable_mint_configured"),
"pause" => std::option::Option::Some("mint_activity_paused"),
"resume" => std::option::Option::Some("mint_activity_resumed"),
"initialize_permissioned_burn" => std::option::Option::Some("permissioned_burn_configured"),
"enable_confidential_credits" => std::option::Option::Some("confidential_credits_enabled"),
"disable_confidential_credits" => {
std::option::Option::Some("confidential_credits_disabled")
},
"enable_non_confidential_credits" => {
std::option::Option::Some("non_confidential_credits_enabled")
},
"disable_non_confidential_credits" => {
std::option::Option::Some("non_confidential_credits_disabled")
},
"initialize_multisig" | "initialize_multisig2" => {
let m = parameters.get("m").and_then(serde_json::Value::as_u64).unwrap_or(0);
let n = role_count(accounts, "multisig_member");
if m == 1 && n > 1 {
std::option::Option::Some("weak_multisig_threshold")
} else {
std::option::Option::None
}
},
_ => std::option::Option::None,
};
}
fn role_count(accounts: &serde_json::Value, role: &str) -> usize {
return accounts
.as_array()
.map(|rows| {
return rows
.iter()
.filter(|row| {
return row.get("role").and_then(serde_json::Value::as_str)
== std::option::Option::Some(role);
})
.count();
})
.unwrap_or(0);
}
#[cfg(test)]
mod tests {
fn observation(
entry: &str,
family: crate::MdEventFamily,
parameters: serde_json::Value,
failed: bool,
) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: format!("token:{entry}:0"),
event: crate::MdDecodedProtocolEvent {
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()),
protocol_code: crate::MdProtocolCode("spl_token".to_string()),
surface_code: crate::MdSurfaceCode("spl_token".to_string()),
event_code: crate::MdEventCode(format!("spl_token.{entry}")),
event_name: crate::MdEventName(entry.to_string()),
event_family: family,
source_kind: crate::MdEventSourceKind::Instruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({"parameters":parameters,"accounts":[{"role":"multisig_member"},{"role":"multisig_member"}],"authority":{}}),
transaction_failed: failed,
transaction_error: std::option::Option::None,
observation_committed: !failed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["fixture".to_string()],
},
};
}
fn token2022_observation(
entry: &str,
family: crate::MdEventFamily,
parameters: serde_json::Value,
failed: bool,
) -> crate::DcApiDecodedObservation {
let mut observation = observation(entry, family, parameters, failed);
observation.event.program_id =
crate::MdProgramId(kb_program_ids::SPL_TOKEN2022_PROGRAM_ID.to_string());
observation.event.protocol_code = crate::MdProtocolCode("spl_token2022".to_string());
observation.event.surface_code = crate::MdSurfaceCode("spl_token2022".to_string());
observation.event.event_code = crate::MdEventCode(format!("spl_token2022.{entry}"));
observation.event_key = format!("token2022:{entry}:0");
return observation;
}
fn nested_ata_observation(failed: bool, valid: bool) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: "ata:recover_nested:0".to_string(),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(2),
instruction_path: crate::MdInstructionPath("1/0".to_string()),
program_id: crate::MdProgramId(
kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID.to_string(),
),
protocol_code: crate::MdProtocolCode("spl_associated_token_account".to_string()),
surface_code: crate::MdSurfaceCode("spl_associated_token_account".to_string()),
event_code: crate::MdEventCode(
"spl_associated_token_account.recover_nested".to_string(),
),
event_name: crate::MdEventName("recover_nested".to_string()),
event_family: crate::MdEventFamily::Lifecycle,
source_kind: crate::MdEventSourceKind::InnerInstruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({
"addresses": {
"tokenProgram": {
"programId": kb_program_ids::SPL_TOKEN2022_PROGRAM_ID,
"kind": "token2022",
"supported": true
},
"ownerAssociatedTokenAccount": {"observed":"ownerAta","valid":valid},
"nestedAssociatedTokenAccount": {"observed":"nestedAta","valid":valid},
"walletNestedMintAssociatedTokenAccount": {"observed":"destinationAta","valid":valid}
}
}),
transaction_failed: failed,
transaction_error: std::option::Option::None,
observation_committed: !failed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["fixture".to_string()],
},
};
}
#[test]
fn approval_and_authority_revocation_are_fact_only_without_score() {
for (entry, family, parameters, expected) in [
(
"approve_checked",
crate::MdEventFamily::TokenAccount,
serde_json::json!({"amountRaw":"10"}),
"delegation_allowance_granted",
),
(
"set_authority",
crate::MdEventFamily::Admin,
serde_json::json!({"newAuthority":null}),
"authority_revoked",
),
] {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtRiskMaterializer,
&observation(entry, family, parameters, false),
);
assert_eq!(result.outputs[0].payload_json["riskKind"], expected);
assert!(result.outputs[0].payload_json["interpretation"]["score"].is_null());
}
}
#[test]
fn token2022_revocation_is_owned_once_without_an_invented_score() {
let observation = token2022_observation(
"set_authority",
crate::MdEventFamily::Admin,
serde_json::json!({"newAuthority":null}),
false,
);
let result =
crate::MtApiEventMaterializer::materialize(&crate::MtRiskMaterializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs.len(), 1);
assert_eq!(result.outputs[0].payload_json["riskKind"], "authority_revoked");
assert!(result.outputs[0].payload_json["interpretation"]["score"].is_null());
}
#[test]
fn only_structurally_weak_multisig_creates_risk_fact() {
let weak = observation(
"initialize_multisig2",
crate::MdEventFamily::Admin,
serde_json::json!({"m":1}),
false,
);
let strong = observation(
"initialize_multisig2",
crate::MdEventFamily::Admin,
serde_json::json!({"m":2}),
false,
);
assert_eq!(
crate::MtApiEventMaterializer::materialize(&crate::MtRiskMaterializer, &weak).status,
crate::MtApiMaterializerOutcomeStatus::Inserted
);
assert_eq!(
crate::MtApiEventMaterializer::materialize(&crate::MtRiskMaterializer, &strong).status,
crate::MtApiMaterializerOutcomeStatus::Ignored
);
}
#[test]
fn failed_risk_fact_is_refused() {
let failed = observation(
"freeze_account",
crate::MdEventFamily::TokenAccount,
serde_json::json!({}),
true,
);
assert_eq!(
crate::MtApiEventMaterializer::materialize(&crate::MtRiskMaterializer, &failed).status,
crate::MtApiMaterializerOutcomeStatus::Refused
);
}
#[test]
fn committed_nested_ata_recovery_creates_one_fact_without_score() {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtRiskMaterializer,
&nested_ata_observation(false, true),
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs.len(), 1);
assert_eq!(
result.outputs[0].output_key,
"ata_risk_fact:nested_ata_anti_pattern_recovered:0"
);
assert_eq!(result.outputs[0].payload_json["riskKind"], "nested_ata_anti_pattern_recovered");
assert_eq!(
result.outputs[0].payload_json["domain"],
"spl_associated_token_account_risk_fact"
);
assert!(result.outputs[0].payload_json["interpretation"]["score"].is_null());
assert!(result.outputs[0].payload_json["interpretation"]["severity"].is_null());
}
#[test]
fn failed_or_invalid_nested_ata_recovery_creates_no_risk_fact() {
for observation in
[nested_ata_observation(true, true), nested_ata_observation(false, false)]
{
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtRiskMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
assert!(result.outputs.is_empty());
}
}
#[test]
fn ata_matrix_declares_the_single_compiled_risk_fact() {
let matrix: serde_json::Value = match serde_json::from_str(include_str!(
"../../../../docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json"
)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("ATA matrix is invalid: {error}"),
};
let instructions = match matrix["instructions"].as_array() {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("ATA matrix instructions are absent"),
};
let recover = match instructions.iter().find(|row| {
return row.get("name").and_then(serde_json::Value::as_str)
== std::option::Option::Some("recover_nested");
}) {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("RecoverNested matrix entry is absent"),
};
assert!(recover["authorizedProjections"].as_array().is_some_and(|values| {
return values.iter().any(|value| {
return value == "risk:nested_ata_anti_pattern_recovered";
});
}));
assert_eq!(
matrix["materializationContract"]["projectionOwners"][1]["facts"],
serde_json::json!(["nested_ata_anti_pattern_recovered"])
);
}
#[test]
fn token2022_compliance_toggles_are_projected_as_fact_only_risk_rows() {
for (entry, expected) in [
("initialize_default_account_state", "default_account_state_configured"),
("enable_required_transfer_memos", "required_transfer_memos_enabled"),
("disable_required_transfer_memos", "required_transfer_memos_disabled"),
("enable_cpi_guard", "cpi_guard_enabled"),
("disable_cpi_guard", "cpi_guard_disabled"),
] {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtRiskMaterializer,
&token2022_observation(
entry,
crate::MdEventFamily::Audit,
serde_json::json!({}),
false,
),
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs[0].payload_json["riskKind"], expected);
assert_eq!(result.outputs[0].payload_json["interpretation"]["factOnly"], true);
assert!(result.outputs[0].payload_json["interpretation"]["score"].is_null());
}
}
#[test]
fn token2022_restriction_and_pause_facts_have_unique_stable_kinds() {
for (entry, family, expected) in [
(
"initialize_non_transferable_mint",
crate::MdEventFamily::TokenMint,
"non_transferable_mint_configured",
),
(
"initialize_pausable_config",
crate::MdEventFamily::TokenAccount,
"pausable_mint_configured",
),
("pause", crate::MdEventFamily::TokenAccount, "mint_activity_paused"),
("resume", crate::MdEventFamily::TokenAccount, "mint_activity_resumed"),
(
"initialize_permissioned_burn",
crate::MdEventFamily::TokenAccount,
"permissioned_burn_configured",
),
] {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtRiskMaterializer,
&token2022_observation(entry, family, serde_json::json!({}), false),
);
assert_eq!(result.outputs[0].payload_json["riskKind"], expected);
}
}
#[test]
fn confidential_credit_toggles_are_fact_only_and_keep_wire_parameters() {
for (entry, credit_kind, enabled, expected) in [
(
"enable_confidential_credits",
"confidential",
true,
"confidential_credits_enabled",
),
(
"disable_confidential_credits",
"confidential",
false,
"confidential_credits_disabled",
),
(
"enable_non_confidential_credits",
"non_confidential",
true,
"non_confidential_credits_enabled",
),
(
"disable_non_confidential_credits",
"non_confidential",
false,
"non_confidential_credits_disabled",
),
] {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtRiskMaterializer,
&token2022_observation(
entry,
crate::MdEventFamily::TokenAccount,
serde_json::json!({
"extension":"confidential_transfer",
"creditKind":credit_kind,
"enabled":enabled,
}),
false,
),
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs.len(), 1);
assert_eq!(result.outputs[0].payload_json["riskKind"], expected);
assert_eq!(result.outputs[0].payload_json["parameters"]["creditKind"], credit_kind);
assert_eq!(result.outputs[0].payload_json["parameters"]["enabled"], enabled);
assert_eq!(result.outputs[0].payload_json["interpretation"]["factOnly"], true);
assert!(result.outputs[0].payload_json["interpretation"]["score"].is_null());
}
}
}