v0.4.8-pre.007
This commit is contained in:
@@ -1,577 +1,37 @@
|
||||
// file: kb-lib/src/materializer/compliance/audit.rs
|
||||
// version: 10
|
||||
// version: 11
|
||||
|
||||
//! Stable compliance-audit projections for committed native bytecode mutations and execution profiles.
|
||||
//! Native compliance-audit materializer component.
|
||||
|
||||
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[crate::MdEventFamily::Audit];
|
||||
const COMPUTE_BUDGET_SURFACE: &str = "solana.core.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.core.bpf_loader_deprecated";
|
||||
const BPF_LOADER_SURFACE: &str = "solana.core.bpf_loader";
|
||||
const BPF_LOADER_UPGRADEABLE_SURFACE: &str = "solana.core.bpf_loader_upgradeable";
|
||||
const LOADER_V4_SURFACE: &str = "solana.core.loader_v4";
|
||||
const WRITE_ENTRIES: &[&str] = &["write"];
|
||||
const LOADER_V4_COPY_ENTRIES: &[&str] = &["copy"];
|
||||
mod constants;
|
||||
mod materializer;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum ProjectionKind {
|
||||
ProgramByteWrite(&'static str),
|
||||
ProgramByteCopy,
|
||||
ComputeBudgetProfile,
|
||||
}
|
||||
/// Stable native compliance audit materializer.
|
||||
pub use self::materializer::MtComplianceAuditMaterializer;
|
||||
|
||||
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 MtComplianceAuditMaterializer;
|
||||
|
||||
impl crate::MtMaterializer for crate::MtComplianceAuditMaterializer {
|
||||
fn materializer_name(&self) -> &'static str {
|
||||
return "kb-lib.materializer.compliance.audit";
|
||||
}
|
||||
|
||||
fn materializer_version(&self) -> &'static str {
|
||||
return env!("CARGO_PKG_VERSION");
|
||||
}
|
||||
|
||||
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
|
||||
return event.event_family == crate::MdEventFamily::Audit
|
||||
&& projection(event.surface_code.0.as_str(), event.event_name.0.as_str()).is_some();
|
||||
}
|
||||
|
||||
fn materialize_event(
|
||||
&self,
|
||||
event: &crate::MdDecodedProtocolEvent,
|
||||
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
||||
if !crate::MtMaterializer::accepts_event(self, event) {
|
||||
return std::result::Result::Ok(std::vec::Vec::new());
|
||||
}
|
||||
return std::result::Result::Ok(std::vec::Vec::new());
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::MtApiEventMaterializer for crate::MtComplianceAuditMaterializer {
|
||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||
return crate::MtApiMaterializerIdentity {
|
||||
name: "materializer.compliance.audit".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 observation.event.event_family == crate::MdEventFamily::Audit
|
||||
&& projection(
|
||||
observation.event.surface_code.0.as_str(),
|
||||
observation.event.event_name.0.as_str(),
|
||||
)
|
||||
.is_some();
|
||||
}
|
||||
|
||||
fn transaction_policy(
|
||||
&self,
|
||||
_family: crate::MdEventFamily,
|
||||
) -> crate::MtApiMaterializationTransactionPolicy {
|
||||
return crate::MtApiMaterializationTransactionPolicy::SuccessfulOrFailedAudit;
|
||||
}
|
||||
|
||||
fn materialize(
|
||||
&self,
|
||||
observation: &crate::DcApiDecodedObservation,
|
||||
) -> crate::MtApiMaterializerExecutionResult {
|
||||
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::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
|
||||
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::MtApiMaterializerExecutionResult::ignored();
|
||||
},
|
||||
};
|
||||
if observation.event.event_family != crate::MdEventFamily::Audit {
|
||||
return crate::MtApiMaterializerExecutionResult::ignored();
|
||||
}
|
||||
if projection != ProjectionKind::ComputeBudgetProfile
|
||||
&& (observation.transaction_failed || !observation.observation_committed)
|
||||
{
|
||||
tracing::debug!(
|
||||
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
|
||||
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::MtApiMaterializerExecutionResult::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::MtApiMaterializedOutput {
|
||||
output_key: projection
|
||||
.output_key(observation.event.surface_code.0.as_str(), operation.as_str()),
|
||||
family: crate::MdMaterializedEventFamily::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::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
|
||||
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::MtApiMaterializerExecutionResult {
|
||||
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
|
||||
outputs: std::vec![output],
|
||||
diagnostics: std::vec::Vec::new(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn materialize_compute_budget_profile(
|
||||
observation: &crate::DcApiDecodedObservation,
|
||||
parameters: serde_json::Value,
|
||||
operation: std::string::String,
|
||||
) -> crate::MtApiMaterializerExecutionResult {
|
||||
let profile = parameters.get("transactionProfile").cloned().unwrap_or(serde_json::Value::Null);
|
||||
if !compute_budget_profile_emits(&profile) {
|
||||
return crate::MtApiMaterializerExecutionResult::ignored();
|
||||
}
|
||||
let output = crate::MtApiMaterializedOutput {
|
||||
output_key: "transaction_compute_budget_profile:0".to_string(),
|
||||
family: crate::MdMaterializedEventFamily::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::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
|
||||
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::MtApiMaterializerExecutionResult {
|
||||
status: crate::MtApiMaterializerOutcomeStatus::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<ProjectionKind> {
|
||||
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::DcApiDecodedObservation {
|
||||
return crate::DcApiDecodedObservation {
|
||||
event_key: format!("{entry_code}:0"),
|
||||
event: crate::MdDecodedProtocolEvent {
|
||||
signature: crate::MdSignature("signature".to_string()),
|
||||
slot: crate::MdSlot(42),
|
||||
instruction_path: crate::MdInstructionPath("0".to_string()),
|
||||
program_id: crate::MdProgramId("program-id".to_string()),
|
||||
protocol_code: crate::MdProtocolCode("solana.core".to_string()),
|
||||
surface_code: crate::MdSurfaceCode(surface_code.to_string()),
|
||||
event_code: crate::MdEventCode(format!("{surface_code}.{entry_code}")),
|
||||
event_name: crate::MdEventName(entry_code.to_string()),
|
||||
event_family: crate::MdEventFamily::Audit,
|
||||
source_kind: crate::MdEventSourceKind::Instruction,
|
||||
confidence: crate::MdDecoderConfidence::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::DcApiDecoderProof {
|
||||
kind: crate::DcApiDecoderProofKind::Manual,
|
||||
confidence: crate::MdDecoderConfidence::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::MtComplianceAuditMaterializer;
|
||||
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::MtApiEventMaterializer::materialize(&materializer, &observation);
|
||||
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
|
||||
assert_eq!(result.outputs[0].family, crate::MdMaterializedEventFamily::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::MtComplianceAuditMaterializer;
|
||||
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::MtApiEventMaterializer::materialize(&materializer, &observation);
|
||||
assert_eq!(
|
||||
result.outputs[0].output_key,
|
||||
"program_loader_code:solana.core.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::MtComplianceAuditMaterializer;
|
||||
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::MtApiEventMaterializer::materialize(&materializer, &emitter);
|
||||
assert_eq!(emitted.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
|
||||
assert_eq!(emitted.outputs[0].output_key, "transaction_compute_budget_profile:0");
|
||||
assert_eq!(emitted.outputs[0].family, crate::MdMaterializedEventFamily::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::MtApiEventMaterializer::materialize(&materializer, &non_emitter);
|
||||
assert_eq!(ignored.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_compute_budget_profile_remains_audit_materializable() {
|
||||
let materializer = crate::MtComplianceAuditMaterializer;
|
||||
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::MtApiEventMaterializer::materialize(&materializer, &observation);
|
||||
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
|
||||
assert_eq!(result.outputs[0].payload_json["transactionSucceeded"], false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_code_mutation_is_refused() {
|
||||
let materializer = crate::MtComplianceAuditMaterializer;
|
||||
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::MtApiEventMaterializer::materialize(&materializer, &observation);
|
||||
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
|
||||
assert!(result.outputs.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unrelated_audit_observation_is_ignored() {
|
||||
let materializer = crate::MtComplianceAuditMaterializer;
|
||||
let observation = observation(
|
||||
"other_surface",
|
||||
"write",
|
||||
false,
|
||||
serde_json::json!([]),
|
||||
serde_json::json!({}),
|
||||
);
|
||||
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
|
||||
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn output_serialization_is_deterministic() {
|
||||
let materializer = crate::MtComplianceAuditMaterializer;
|
||||
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::MtApiEventMaterializer::materialize(&materializer, &observation);
|
||||
let second = crate::MtApiEventMaterializer::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);
|
||||
}
|
||||
}
|
||||
/// Crate-visible `MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES` component constant.
|
||||
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES;
|
||||
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE` component constant.
|
||||
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE;
|
||||
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE` component constant.
|
||||
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE;
|
||||
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE` component constant.
|
||||
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE;
|
||||
/// Stable runtime component name for the native compliance audit materializer.
|
||||
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_COMPONENT_NAME;
|
||||
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES` component constant.
|
||||
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES;
|
||||
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE` component constant.
|
||||
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE;
|
||||
/// Crate-visible `MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES` component constant.
|
||||
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES;
|
||||
/// Crate-visible `MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE` component constant.
|
||||
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE;
|
||||
/// Stable processor name for the native compliance audit materializer.
|
||||
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME;
|
||||
/// Stable projection version for native compliance audit outputs.
|
||||
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_PROJECTION_VERSION;
|
||||
/// Crate-visible `MT_COMPLIANCE_AUDIT_WRITE_ENTRIES` component constant.
|
||||
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_WRITE_ENTRIES;
|
||||
/// Canonical tracing target for the native compliance audit materializer.
|
||||
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT;
|
||||
|
||||
Reference in New Issue
Block a user