// file: kb-lib/src/materializer/compliance/audit.rs // version: 7 //! Stable compliance-audit projections for committed native bytecode mutations and execution profiles. const ACCEPTED_FAMILIES: &[crate::EventFamily] = &[crate::EventFamily::Audit]; const COMPUTE_BUDGET_SURFACE: &str = "solana_native_compute_budget"; const COMPUTE_BUDGET_ENTRIES: &[&str] = &[ "request_units_deprecated", "request_heap_frame", "set_compute_unit_limit", "set_compute_unit_price", "set_loaded_accounts_data_size_limit", ]; const BPF_LOADER_DEPRECATED_SURFACE: &str = "solana_native_bpf_loader_deprecated"; const BPF_LOADER_SURFACE: &str = "solana_native_bpf_loader"; const BPF_LOADER_UPGRADEABLE_SURFACE: &str = "solana_native_bpf_loader_upgradeable"; const LOADER_V4_SURFACE: &str = "solana_native_loader_v4"; const WRITE_ENTRIES: &[&str] = &["write"]; const LOADER_V4_COPY_ENTRIES: &[&str] = &["copy"]; #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum ProjectionKind { ProgramByteWrite(&'static str), ProgramByteCopy, ComputeBudgetProfile, } impl ProjectionKind { fn output_key(self, surface_code: &str, operation: &str) -> std::string::String { return match self { Self::ProgramByteWrite(_) | Self::ProgramByteCopy => { format!("program_loader_code:{surface_code}:{operation}:0") }, Self::ComputeBudgetProfile => "transaction_compute_budget_profile:0".to_string(), }; } fn loader_surface(self) -> &'static str { return match self { Self::ProgramByteWrite(surface) => surface, Self::ProgramByteCopy => LOADER_V4_SURFACE, Self::ComputeBudgetProfile => COMPUTE_BUDGET_SURFACE, }; } } /// Stable compliance-audit materializer for native bytecode mutations and execution profiles. #[derive(Clone, Debug, Default)] pub struct ComplianceAuditMaterializer; impl crate::Materializer for crate::ComplianceAuditMaterializer { fn materializer_name(&self) -> &'static str { return "kb_materializer_compliance_audit"; } fn materializer_version(&self) -> &'static str { return env!("CARGO_PKG_VERSION"); } fn accepts_event(&self, event: &crate::DecodedProtocolEvent) -> bool { return event.event_family == crate::EventFamily::Audit && projection(event.surface_code.0.as_str(), event.event_name.0.as_str()).is_some(); } fn materialize_event( &self, event: &crate::DecodedProtocolEvent, ) -> kb_core::Result> { if !crate::Materializer::accepts_event(self, event) { return std::result::Result::Ok(std::vec::Vec::new()); } return std::result::Result::Ok(std::vec::Vec::new()); } } impl crate::EventMaterializer for crate::ComplianceAuditMaterializer { fn identity(&self) -> crate::MaterializerIdentity { return crate::MaterializerIdentity { name: "solana_native_compliance_audit".to_string(), version: env!("CARGO_PKG_VERSION").to_string(), }; } fn accepted_families(&self) -> &'static [crate::EventFamily] { return ACCEPTED_FAMILIES; } fn accepts_observation(&self, observation: &crate::DecodedObservation) -> bool { return observation.event.event_family == crate::EventFamily::Audit && projection( observation.event.surface_code.0.as_str(), observation.event.event_name.0.as_str(), ) .is_some(); } fn transaction_policy( &self, _family: crate::EventFamily, ) -> crate::MaterializationTransactionPolicy { return crate::MaterializationTransactionPolicy::SuccessfulOrFailedAudit; } fn materialize( &self, observation: &crate::DecodedObservation, ) -> crate::MaterializerExecutionResult { let projection = match projection( observation.event.surface_code.0.as_str(), observation.event.event_name.0.as_str(), ) { std::option::Option::Some(value) => value, std::option::Option::None => { tracing::debug!( target: crate::MATERIALIZER_COMPLIANCE_AUDIT_TRACING_TARGET, action = "materialize_compliance_audit", surface_code = %observation.event.surface_code.0.as_str(), entry_code = %observation.event.event_name.0.as_str(), accepted = false, "ignore observation outside supported native compliance-audit projections" ); return crate::MaterializerExecutionResult::ignored(); }, }; if observation.event.event_family != crate::EventFamily::Audit { return crate::MaterializerExecutionResult::ignored(); } if projection != ProjectionKind::ComputeBudgetProfile && (observation.transaction_failed || !observation.observation_committed) { tracing::debug!( target: crate::MATERIALIZER_COMPLIANCE_AUDIT_TRACING_TARGET, action = "materialize_compliance_audit", surface_code = %observation.event.surface_code.0.as_str(), entry_code = %observation.event.event_name.0.as_str(), committed = observation.observation_committed, transaction_failed = observation.transaction_failed, "refuse uncommitted native bytecode mutation observation" ); return crate::MaterializerExecutionResult::refused( "failed_transaction_compliance_audit_refused", "failed or uncommitted native observations cannot create committed bytecode mutation outputs", ); } let accounts = observation .payload_json .get("accounts") .cloned() .unwrap_or(serde_json::Value::Null); let parameters = observation .payload_json .get("parameters") .cloned() .unwrap_or(serde_json::Value::Null); let operation = observation.event.event_name.0.clone(); if projection == ProjectionKind::ComputeBudgetProfile { return materialize_compute_budget_profile(observation, parameters, operation); } let output = crate::MaterializedOutput { output_key: projection .output_key(observation.event.surface_code.0.as_str(), operation.as_str()), family: crate::MaterializedEventFamily::ComplianceAudit, payload_json: serde_json::json!({ "projectionVersion": 1, "projectionSemantics": "committed_instruction_compliance_audit_event", "domain": "program_loader_code_mutation", "loaderSurface": projection.loader_surface(), "operation": operation, "programId": observation.event.program_id.0.clone(), "signature": observation.event.signature.0.clone(), "slot": observation.event.slot.0, "instructionPath": observation.event.instruction_path.0.clone(), "transactionSucceeded": true, "accounts": accounts, "parameters": parameters, "projection": projection_details(projection, &accounts, ¶meters), }), }; tracing::debug!( target: crate::MATERIALIZER_COMPLIANCE_AUDIT_TRACING_TARGET, action = "materialize_compliance_audit", surface_code = %observation.event.surface_code.0.as_str(), entry_code = %observation.event.event_name.0.as_str(), loader_surface = projection.loader_surface(), output_count = 1_usize, "materialize committed native bytecode mutation observation" ); return crate::MaterializerExecutionResult { status: crate::MaterializerOutcomeStatus::Inserted, outputs: std::vec![output], diagnostics: std::vec::Vec::new(), }; } } fn materialize_compute_budget_profile( observation: &crate::DecodedObservation, parameters: serde_json::Value, operation: std::string::String, ) -> crate::MaterializerExecutionResult { let profile = parameters.get("transactionProfile").cloned().unwrap_or(serde_json::Value::Null); if !compute_budget_profile_emits(&profile) { return crate::MaterializerExecutionResult::ignored(); } let output = crate::MaterializedOutput { output_key: "transaction_compute_budget_profile:0".to_string(), family: crate::MaterializedEventFamily::ComplianceAudit, payload_json: serde_json::json!({ "projectionVersion": 1, "projectionSemantics": "transaction_compute_budget_profile", "domain": "transaction_compute_budget_profile", "operation": operation, "programId": observation.event.program_id.0.clone(), "signature": observation.event.signature.0.clone(), "slot": observation.event.slot.0, "instructionPath": observation.event.instruction_path.0.clone(), "transactionSucceeded": !observation.transaction_failed, "profile": profile, "profileSource": { "surfaceCode": observation.event.surface_code.0.clone(), "entryCode": observation.event.event_name.0.clone(), "eventKey": observation.event_key.clone() }, }), }; tracing::debug!( target: crate::MATERIALIZER_COMPLIANCE_AUDIT_TRACING_TARGET, action = "materialize_compute_budget_profile", surface_code = %observation.event.surface_code.0.as_str(), entry_code = %observation.event.event_name.0.as_str(), output_count = 1_usize, transaction_failed = observation.transaction_failed, "materialize native Compute Budget transaction profile" ); return crate::MaterializerExecutionResult { status: crate::MaterializerOutcomeStatus::Inserted, outputs: std::vec![output], diagnostics: std::vec::Vec::new(), }; } fn compute_budget_profile_emits(profile: &serde_json::Value) -> bool { return profile .get("currentInstructionIsProfileEmitter") .and_then(serde_json::Value::as_bool) == std::option::Option::Some(true); } fn projection(surface_code: &str, entry_code: &str) -> std::option::Option { if surface_code == COMPUTE_BUDGET_SURFACE && contains(COMPUTE_BUDGET_ENTRIES, entry_code) { return std::option::Option::Some(ProjectionKind::ComputeBudgetProfile); } if contains(WRITE_ENTRIES, entry_code) { for supported in [ BPF_LOADER_DEPRECATED_SURFACE, BPF_LOADER_SURFACE, BPF_LOADER_UPGRADEABLE_SURFACE, LOADER_V4_SURFACE, ] { if surface_code == supported { return std::option::Option::Some(ProjectionKind::ProgramByteWrite(supported)); } } } if surface_code == LOADER_V4_SURFACE && contains(LOADER_V4_COPY_ENTRIES, entry_code) { return std::option::Option::Some(ProjectionKind::ProgramByteCopy); } return std::option::Option::None; } fn projection_details( projection: ProjectionKind, accounts: &serde_json::Value, parameters: &serde_json::Value, ) -> serde_json::Value { return match projection { ProjectionKind::ProgramByteWrite(_) => serde_json::json!({ "targetAccount": first_account_key_for_roles( accounts, &["program_account", "buffer_account"], ), "authorityAccount": first_account_key_for_roles( accounts, &["authority", "buffer_authority"], ), "offset": parameters.get("offset").cloned().unwrap_or(serde_json::Value::Null), "byteLength": parameters.get("byteLength").cloned().unwrap_or(serde_json::Value::Null), "bytesSha256": parameters.get("bytesSha256").cloned().unwrap_or(serde_json::Value::Null), "bytesPrefixHex": parameters.get("bytesPrefixHex").cloned().unwrap_or(serde_json::Value::Null), "stateTransition": "program_bytes_written", "codeBytesMaterialized": false, "transactionFinalAccountStateCaptured": false, }), ProjectionKind::ProgramByteCopy => serde_json::json!({ "targetAccount": account_key_for_role(accounts, "program_account"), "authorityAccount": account_key_for_role(accounts, "authority"), "sourceProgramAccount": account_key_for_role(accounts, "source_program_account"), "destinationOffset": parameters.get("destinationOffset").cloned().unwrap_or(serde_json::Value::Null), "sourceOffset": parameters.get("sourceOffset").cloned().unwrap_or(serde_json::Value::Null), "byteLength": parameters.get("length").cloned().unwrap_or(serde_json::Value::Null), "stateTransition": "program_bytes_copied", "codeBytesMaterialized": false, "transactionFinalAccountStateCaptured": false, }), ProjectionKind::ComputeBudgetProfile => serde_json::Value::Null, }; } fn first_account_key_for_roles(accounts: &serde_json::Value, roles: &[&str]) -> serde_json::Value { for role in roles { let value = account_key_for_role(accounts, role); if !value.is_null() { return value; } } return serde_json::Value::Null; } fn account_key_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value { let account_key = accounts.as_array().and_then(|values| { return values.iter().find_map(|value| { if value.get("role").and_then(serde_json::Value::as_str) != std::option::Option::Some(role) { return std::option::Option::None; } return value .get("accountKey") .and_then(serde_json::Value::as_str) .map(|account_key| return account_key.to_string()); }); }); return match account_key { std::option::Option::Some(value) => serde_json::Value::String(value), std::option::Option::None => serde_json::Value::Null, }; } fn contains(entries: &[&str], entry_code: &str) -> bool { return entries.iter().any(|entry| return *entry == entry_code); } #[cfg(test)] mod tests { fn observation( surface_code: &str, entry_code: &str, transaction_failed: bool, accounts: serde_json::Value, parameters: serde_json::Value, ) -> crate::DecodedObservation { return crate::DecodedObservation { event_key: format!("{entry_code}:0"), event: crate::DecodedProtocolEvent { signature: crate::Signature("signature".to_string()), slot: crate::Slot(42), instruction_path: crate::InstructionPath("0".to_string()), program_id: crate::ProgramId("program-id".to_string()), protocol_code: crate::ProtocolCode("solana_native".to_string()), surface_code: crate::SurfaceCode(surface_code.to_string()), event_code: crate::EventCode(format!("{surface_code}.{entry_code}")), event_name: crate::EventName(entry_code.to_string()), event_family: crate::EventFamily::Audit, source_kind: crate::EventSourceKind::Instruction, confidence: crate::DecoderConfidence::ManualExact, }, payload_json: serde_json::json!({"accounts": accounts, "parameters": parameters}), transaction_failed, transaction_error: if transaction_failed { std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) } else { std::option::Option::None }, observation_committed: !transaction_failed, proof: crate::DecoderProof { kind: crate::DecoderProofKind::Manual, confidence: crate::DecoderConfidence::ManualExact, evidence: std::vec!["fixture".to_string()], }, }; } fn account(role: &str, account_key: &str) -> serde_json::Value { return serde_json::json!({"role": role, "accountKey": account_key}); } #[test] fn loader_write_variants_create_bounded_code_audits() { let materializer = crate::ComplianceAuditMaterializer; for (surface, target_role, authority_role) in [ (super::BPF_LOADER_DEPRECATED_SURFACE, "program_account", "missing"), (super::BPF_LOADER_SURFACE, "program_account", "missing"), (super::BPF_LOADER_UPGRADEABLE_SURFACE, "buffer_account", "buffer_authority"), (super::LOADER_V4_SURFACE, "program_account", "authority"), ] { let observation = observation( surface, "write", false, serde_json::json!([ account(target_role, "target111"), account(authority_role, "authority111"), ]), serde_json::json!({ "offset": 7, "byteLength": 32, "bytesSha256": "abc", "bytesPrefixHex": "0102", }), ); let result = crate::EventMaterializer::materialize(&materializer, &observation); assert_eq!(result.status, crate::MaterializerOutcomeStatus::Inserted); assert_eq!(result.outputs[0].family, crate::MaterializedEventFamily::ComplianceAudit); assert_eq!(result.outputs[0].payload_json["projection"]["targetAccount"], "target111"); assert_eq!(result.outputs[0].payload_json["projection"]["bytesSha256"], "abc"); assert_eq!( result.outputs[0].payload_json["projection"]["codeBytesMaterialized"], false ); } } #[test] fn loader_v4_copy_preserves_source_and_bounded_ranges() { let materializer = crate::ComplianceAuditMaterializer; let observation = observation( super::LOADER_V4_SURFACE, "copy", false, serde_json::json!([ account("program_account", "target111"), account("authority", "authority111"), account("source_program_account", "source111"), ]), serde_json::json!({"destinationOffset": 8, "sourceOffset": 4, "length": 16}), ); let result = crate::EventMaterializer::materialize(&materializer, &observation); assert_eq!( result.outputs[0].output_key, "program_loader_code:solana_native_loader_v4:copy:0" ); assert_eq!( result.outputs[0].payload_json["projection"]["sourceProgramAccount"], "source111" ); assert_eq!(result.outputs[0].payload_json["projection"]["byteLength"], 16); } #[test] fn compute_budget_profile_is_emitted_only_by_profile_emitter() { let materializer = crate::ComplianceAuditMaterializer; let emitter = observation( super::COMPUTE_BUDGET_SURFACE, "set_compute_unit_limit", false, serde_json::json!([]), serde_json::json!({ "transactionProfile": { "currentInstructionIsProfileEmitter": true, "profileEmitterInstructionPath": "0", "computeBudgetInstructionCount": 2, "effectiveValues": { "set_compute_unit_limit": { "value": 200000, "sourceInstructionPath": "0" } } } }), ); let emitted = crate::EventMaterializer::materialize(&materializer, &emitter); assert_eq!(emitted.status, crate::MaterializerOutcomeStatus::Inserted); assert_eq!(emitted.outputs[0].output_key, "transaction_compute_budget_profile:0"); assert_eq!(emitted.outputs[0].family, crate::MaterializedEventFamily::ComplianceAudit); assert_eq!(emitted.outputs[0].payload_json["profile"]["computeBudgetInstructionCount"], 2); let non_emitter = observation( super::COMPUTE_BUDGET_SURFACE, "set_compute_unit_price", false, serde_json::json!([]), serde_json::json!({ "transactionProfile": { "currentInstructionIsProfileEmitter": false, "profileEmitterInstructionPath": "0" } }), ); let ignored = crate::EventMaterializer::materialize(&materializer, &non_emitter); assert_eq!(ignored.status, crate::MaterializerOutcomeStatus::Ignored); } #[test] fn failed_compute_budget_profile_remains_audit_materializable() { let materializer = crate::ComplianceAuditMaterializer; let observation = observation( super::COMPUTE_BUDGET_SURFACE, "set_compute_unit_price", true, serde_json::json!([]), serde_json::json!({ "transactionProfile": { "currentInstructionIsProfileEmitter": true, "profileEmitterInstructionPath": "0", "computeBudgetInstructionCount": 1 } }), ); let result = crate::EventMaterializer::materialize(&materializer, &observation); assert_eq!(result.status, crate::MaterializerOutcomeStatus::Inserted); assert_eq!(result.outputs[0].payload_json["transactionSucceeded"], false); } #[test] fn failed_code_mutation_is_refused() { let materializer = crate::ComplianceAuditMaterializer; let observation = observation( super::LOADER_V4_SURFACE, "write", true, serde_json::json!([account("program_account", "target111")]), serde_json::json!({"offset": 0, "byteLength": 1, "bytesSha256": "abc"}), ); let result = crate::EventMaterializer::materialize(&materializer, &observation); assert_eq!(result.status, crate::MaterializerOutcomeStatus::Refused); assert!(result.outputs.is_empty()); } #[test] fn unrelated_audit_observation_is_ignored() { let materializer = crate::ComplianceAuditMaterializer; let observation = observation( "other_surface", "write", false, serde_json::json!([]), serde_json::json!({}), ); let result = crate::EventMaterializer::materialize(&materializer, &observation); assert_eq!(result.status, crate::MaterializerOutcomeStatus::Ignored); } #[test] fn output_serialization_is_deterministic() { let materializer = crate::ComplianceAuditMaterializer; let observation = observation( super::BPF_LOADER_UPGRADEABLE_SURFACE, "write", false, serde_json::json!([ account("buffer_account", "buffer111"), account("buffer_authority", "authority111"), ]), serde_json::json!({ "offset": 0, "byteLength": 2, "bytesSha256": "abc", "bytesPrefixHex": "0102", }), ); let first = crate::EventMaterializer::materialize(&materializer, &observation); let second = crate::EventMaterializer::materialize(&materializer, &observation); let first_json = match serde_json::to_string(&first) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { panic!("first compliance serialization failed: {error}") }, }; let second_json = match serde_json::to_string(&second) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { panic!("second compliance serialization failed: {error}") }, }; assert_eq!(first_json, second_json); } }