v0.4.8-pre.008

This commit is contained in:
2026-08-06 06:12:54 +02:00
parent 44088c71c9
commit f576fbade2
52 changed files with 3592 additions and 420 deletions

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/metadata/solana_program_metadata/instruction.rs
// version: 4
// version: 5
//! Exact contextual instruction decoding for Solana Program Metadata.
@@ -627,9 +627,15 @@ fn validate_accounts(
));
},
};
if account.signer != contract.signer || account.writable != contract.writable {
if contract.signer && !account.signer {
return std::result::Result::Err(format!(
"{} account {position} ({}) has invalid signer/writable flags",
"{} account {position} ({}) is missing the required signer privilege",
parsed.code, contract.role
));
}
if contract.writable && !account.writable {
return std::result::Result::Err(format!(
"{} account {position} ({}) is missing the required writable privilege",
parsed.code, contract.role
));
}
@@ -1160,6 +1166,115 @@ mod tests {
}
}
#[test]
fn transaction_wide_privilege_supersets_are_accepted() {
let cases = [
(
vec![0, 4, 0, 0, 0, 1, 2],
vec![
("buffer", false, true),
("authority", true, true),
(placeholder(), false, false),
],
"write",
),
(
[vec![1], vec![0; 16], vec![1, 0, 1, 0], vec![1]].concat(),
vec![
("metadata", false, true),
("authority", true, true),
("program", false, false),
(placeholder(), false, false),
(placeholder(), false, false),
],
"initialize",
),
(
vec![2, 0],
vec![
("account", false, true),
("authority", true, true),
(placeholder(), false, false),
(placeholder(), false, false),
],
"set_authority",
),
(
vec![3, 1, 0, 1],
vec![
("metadata", false, true),
("authority", true, true),
(placeholder(), false, true),
(placeholder(), false, false),
(placeholder(), false, false),
],
"set_data",
),
(
vec![4],
vec![
("metadata", false, true),
("authority", true, true),
(placeholder(), false, false),
(placeholder(), false, false),
],
"set_immutable",
),
(
vec![5],
vec![
("account", false, true),
("authority", true, true),
(placeholder(), false, false),
(placeholder(), false, false),
("destination", false, true),
("SysvarRent111111111111111111111111111111111", false, false),
],
"trim",
),
(
vec![6],
vec![
("account", false, true),
("authority", true, true),
(placeholder(), false, false),
(placeholder(), false, false),
("destination", false, true),
],
"close",
),
(
[vec![7], vec![8; 16]].concat(),
vec![
("buffer", false, true),
("authority", true, true),
("program", false, false),
(placeholder(), false, false),
(placeholder(), false, false),
],
"allocate",
),
(
vec![8, 32, 0],
vec![
("account", false, true),
("authority", true, true),
(placeholder(), false, false),
(placeholder(), false, false),
],
"extend",
),
];
for (payload, accounts, expected) in cases {
let result = decode(payload.as_slice(), accounts.as_slice());
assert_eq!(result.status, crate::DcApiDecoderOutcomeStatus::Decoded, "{expected}");
assert_eq!(
result.recognized_entry_code.as_deref(),
std::option::Option::Some(expected)
);
}
}
#[test]
fn source_selection_and_account_flags_fail_closed() {
let invalid_write = decode(
@@ -1199,6 +1314,16 @@ mod tests {
],
);
assert_eq!(invalid_flags.status, crate::DcApiDecoderOutcomeStatus::Failed);
let missing_signer = decode(
&[8, 1, 0],
&[
("account", false, true),
("authority", false, true),
(placeholder(), false, false),
(placeholder(), false, false),
],
);
assert_eq!(missing_signer.status, crate::DcApiDecoderOutcomeStatus::Failed);
}
#[test]

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/spl/token_2022/wire.rs
// version: 2
// version: 3
//! Bounded Token-2022 wire, account and authority decoding.
@@ -2532,10 +2532,7 @@ fn event_family(code: &str) -> crate::MdEventFamily {
| "initialize_multisig2"
| "initialize_permanent_delegate"
| "initialize_interest_bearing_mint"
| "update_interest_bearing_rate"
| "update_token_metadata_authority"
| "update_token_group_authority"
| "update_token_group_max_size" => crate::MdEventFamily::Admin,
| "update_interest_bearing_rate" => crate::MdEventFamily::Admin,
"initialize_mint"
| "initialize_mint2"
| "mint_to"
@@ -2564,8 +2561,11 @@ fn event_family(code: &str) -> crate::MdEventFamily {
"initialize_token_metadata"
| "update_token_metadata_field"
| "remove_token_metadata_key"
| "update_token_metadata_authority"
| "emit_token_metadata"
| "initialize_token_group"
| "update_token_group_max_size"
| "update_token_group_authority"
| "initialize_token_group_member" => crate::MdEventFamily::Metadata,
"batch" | "get_account_data_size" | "amount_to_ui_amount" | "ui_amount_to_amount" => {
crate::MdEventFamily::Audit
@@ -2608,3 +2608,23 @@ fn failed(
}],
};
}
#[cfg(test)]
mod metadata_family_tests {
#[test]
fn every_embedded_metadata_and_group_instruction_uses_metadata_family() {
for operation in [
"initialize_token_metadata",
"update_token_metadata_field",
"remove_token_metadata_key",
"update_token_metadata_authority",
"emit_token_metadata",
"initialize_token_group",
"update_token_group_max_size",
"update_token_group_authority",
"initialize_token_group_member",
] {
assert_eq!(super::event_family(operation), crate::MdEventFamily::Metadata);
}
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/lib.rs
// version: 36
// version: 37
//! Consolidated decoder, executor, materializer and shared model library.
#![warn(missing_docs)]
@@ -1439,8 +1439,12 @@ pub use self::materializer::MtLifecycleMaterializer;
pub use self::materializer::MtLiquidityMaterializer;
/// Exposes the legacy common business materializer trait.
pub use self::materializer::MtMaterializer;
/// Compatibility wrapper for the Solana Program Metadata materializer.
pub use self::materializer::MtMetadataMaterializer;
/// Metaplex Token Metadata instruction materializer.
pub use self::materializer::MtMetadataMetaplexTokenMetadataMaterializer;
/// Solana Program Metadata instruction materializer.
pub use self::materializer::MtMetadataSolanaProgramMaterializer;
/// SPL Token-2022 embedded metadata and group instruction materializer.
pub use self::materializer::MtMetadataToken2022Materializer;
/// Reserved NFT materializer.
pub use self::materializer::MtNftMaterializer;
/// Reserved oracle materializer.
@@ -1457,8 +1461,6 @@ pub use self::materializer::MtRewardsMaterializer;
pub use self::materializer::MtRiskMaterializer;
/// Reserved routing materializer.
pub use self::materializer::MtRoutingMaterializer;
/// Stable Solana Program Metadata instruction materializer.
pub use self::materializer::MtSolanaProgramMetadataMaterializer;
/// Stable native stake and vote materializer.
pub use self::materializer::MtStakingMaterializer;
/// Stable Token, ATA and Token-2022 account materializer.
@@ -2250,22 +2252,28 @@ pub(crate) use self::materializer::MT_LIQUIDITY_ACCEPTED_FAMILIES;
pub(crate) use self::materializer::MT_LIQUIDITY_COMPONENT_NAME;
/// Crate-root access to `MT_LIQUIDITY_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_LIQUIDITY_PROCESSOR_NAME;
/// Crate-root access to `MT_METADATA_METAPLEX_TOKEN_METADATA_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_METADATA_METAPLEX_TOKEN_METADATA_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME;
/// Crate-root access to `MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME;
/// Crate-root access to `MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION;
/// Crate-root access to `MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME;
/// Crate-root access to `MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME;
/// Crate-root access to `MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION;
/// Crate-root access to `MT_METADATA_SOLANA_PROGRAM_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_METADATA_SOLANA_PROGRAM_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_METADATA_SOLANA_PROGRAM_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_METADATA_SOLANA_PROGRAM_COMPONENT_NAME;
/// Crate-root access to `MT_METADATA_SOLANA_PROGRAM_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_METADATA_SOLANA_PROGRAM_PROCESSOR_NAME;
/// Crate-root access to `MT_METADATA_SOLANA_PROGRAM_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_METADATA_SOLANA_PROGRAM_PROJECTION_VERSION;
/// Crate-root access to `MT_METADATA_TOKEN_2022_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_METADATA_TOKEN_2022_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_METADATA_TOKEN_2022_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_METADATA_TOKEN_2022_COMPONENT_NAME;
/// Crate-root access to `MT_METADATA_TOKEN_2022_INSTRUCTION_ENTRIES`.
pub(crate) use self::materializer::MT_METADATA_TOKEN_2022_INSTRUCTION_ENTRIES;
/// Crate-root access to `MT_METADATA_TOKEN_2022_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_METADATA_TOKEN_2022_PROCESSOR_NAME;
/// Crate-root access to `MT_METADATA_TOKEN_2022_PROJECTION_VERSION`.
@@ -2406,8 +2414,8 @@ pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_FEES;
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_LIFECYCLE;
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA;
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM;
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022;
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_RISK`.

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer.rs
// version: 14
// version: 15
//! Consolidated materializer modules.
@@ -73,10 +73,12 @@ pub use self::lending::MtLendingMaterializer;
pub use self::lifecycle::MtLifecycleMaterializer;
/// Reserved liquidity materializer.
pub use self::liquidity::MtLiquidityMaterializer;
/// Compatibility wrapper for the Solana Program Metadata materializer.
pub use self::metadata::MtMetadataMaterializer;
/// Stable Solana Program Metadata instruction materializer.
pub use self::metadata::MtSolanaProgramMetadataMaterializer;
/// Metaplex Token Metadata instruction materializer.
pub use self::metadata::MtMetadataMetaplexTokenMetadataMaterializer;
/// Solana Program Metadata instruction materializer.
pub use self::metadata::MtMetadataSolanaProgramMaterializer;
/// SPL Token-2022 embedded metadata and group instruction materializer.
pub use self::metadata::MtMetadataToken2022Materializer;
/// Materializes one canonical non-Metadata Metaplex account snapshot.
pub use self::metadata::materializer_metadata_materialize_metaplex_account_snapshot;
/// Materializes one canonical Metaplex Metadata account snapshot.
@@ -272,30 +274,36 @@ pub(crate) use self::liquidity::MT_LIQUIDITY_ACCEPTED_FAMILIES;
pub(crate) use self::liquidity::MT_LIQUIDITY_COMPONENT_NAME;
/// Crate-visible `MT_LIQUIDITY_PROCESSOR_NAME` component constant.
pub(crate) use self::liquidity::MT_LIQUIDITY_PROCESSOR_NAME;
/// Crate-visible `MT_METADATA_METAPLEX_TOKEN_METADATA_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::metadata::MT_METADATA_METAPLEX_TOKEN_METADATA_ACCEPTED_FAMILIES;
/// Crate-visible `MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME` component constant.
pub(crate) use self::metadata::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME;
/// Crate-visible `MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME` component constant.
pub(crate) use self::metadata::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME;
/// Crate-visible `MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION` component constant.
pub(crate) use self::metadata::MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION;
/// Crate-visible `MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES;
/// Crate-visible `MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME` component constant.
pub(crate) use self::metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME;
/// Crate-visible `MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME` component constant.
pub(crate) use self::metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME;
/// Crate-visible `MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION` component constant.
pub(crate) use self::metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION;
/// Crate-visible `MT_METADATA_SOLANA_PROGRAM_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::metadata::MT_METADATA_SOLANA_PROGRAM_ACCEPTED_FAMILIES;
/// Crate-visible `MT_METADATA_SOLANA_PROGRAM_COMPONENT_NAME` component constant.
pub(crate) use self::metadata::MT_METADATA_SOLANA_PROGRAM_COMPONENT_NAME;
/// Crate-visible `MT_METADATA_SOLANA_PROGRAM_PROCESSOR_NAME` component constant.
pub(crate) use self::metadata::MT_METADATA_SOLANA_PROGRAM_PROCESSOR_NAME;
/// Crate-visible `MT_METADATA_SOLANA_PROGRAM_PROJECTION_VERSION` component constant.
pub(crate) use self::metadata::MT_METADATA_SOLANA_PROGRAM_PROJECTION_VERSION;
/// Crate-visible `MT_METADATA_TOKEN_2022_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::metadata::MT_METADATA_TOKEN_2022_ACCEPTED_FAMILIES;
/// Crate-visible `MT_METADATA_TOKEN_2022_COMPONENT_NAME` component constant.
pub(crate) use self::metadata::MT_METADATA_TOKEN_2022_COMPONENT_NAME;
/// Crate-visible `MT_METADATA_TOKEN_2022_INSTRUCTION_ENTRIES` component constant.
pub(crate) use self::metadata::MT_METADATA_TOKEN_2022_INSTRUCTION_ENTRIES;
/// Crate-visible `MT_METADATA_TOKEN_2022_PROCESSOR_NAME` component constant.
pub(crate) use self::metadata::MT_METADATA_TOKEN_2022_PROCESSOR_NAME;
/// Crate-visible `MT_METADATA_TOKEN_2022_PROJECTION_VERSION` component constant.
pub(crate) use self::metadata::MT_METADATA_TOKEN_2022_PROJECTION_VERSION;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA` component constant.
pub(crate) use self::metadata::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA` component constant.
pub(crate) use self::metadata::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM` component constant.
pub(crate) use self::metadata::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022` component constant.
pub(crate) use self::metadata::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022;
/// Crate-visible `MT_NFT_ACCEPTED_FAMILIES` component constant.
@@ -491,10 +499,22 @@ mod tests {
crate::MT_LIQUIDITY_PROCESSOR_NAME,
),
(
&crate::MtSolanaProgramMetadataMaterializer,
&crate::MtSolanaProgramMetadataMaterializer,
crate::MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME,
crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME,
&crate::MtMetadataMetaplexTokenMetadataMaterializer,
&crate::MtMetadataMetaplexTokenMetadataMaterializer,
crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
),
(
&crate::MtMetadataSolanaProgramMaterializer,
&crate::MtMetadataSolanaProgramMaterializer,
crate::MT_METADATA_SOLANA_PROGRAM_COMPONENT_NAME,
crate::MT_METADATA_SOLANA_PROGRAM_PROCESSOR_NAME,
),
(
&crate::MtMetadataToken2022Materializer,
&crate::MtMetadataToken2022Materializer,
crate::MT_METADATA_TOKEN_2022_COMPONENT_NAME,
crate::MT_METADATA_TOKEN_2022_PROCESSOR_NAME,
),
(
&crate::MtNftMaterializer,
@@ -583,22 +603,6 @@ mod tests {
];
}
fn state_only_materializer_contracts() -> &'static [(&'static str, &'static str, &'static str)]
{
return &[
(
crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
crate::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA,
),
(
crate::MT_METADATA_TOKEN_2022_COMPONENT_NAME,
crate::MT_METADATA_TOKEN_2022_PROCESSOR_NAME,
crate::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022,
),
];
}
#[test]
fn every_materializer_uses_the_component_and_processor_name_convention() {
for (legacy, contextual, component_name, processor_name) in materializer_contracts() {
@@ -615,18 +619,6 @@ mod tests {
}
}
#[test]
fn state_only_materializers_keep_runtime_processor_and_tracing_identity_aligned() {
for (component_name, processor_name, tracing_target) in state_only_materializer_contracts()
{
assert_eq!(*tracing_target, *component_name);
assert_eq!(
component_name.strip_prefix("kb-lib."),
std::option::Option::Some(*processor_name)
);
}
}
#[test]
fn reserved_materializers_are_empty_shells() {
let materializers: &[&dyn crate::MtApiEventMaterializer] = &[

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/metadata.rs
// version: 8
// version: 9
//! `metadata` materializer family.
@@ -7,23 +7,27 @@ mod metaplex_token_metadata;
mod solana_program_metadata;
mod token_2022;
/// Metaplex Token Metadata instruction materializer.
pub use self::metaplex_token_metadata::MtMetadataMetaplexTokenMetadataMaterializer;
/// Materializes one canonical non-Metadata Metaplex account snapshot.
pub use self::metaplex_token_metadata::materializer_metadata_materialize_metaplex_account_snapshot;
/// Materializes one canonical Metaplex Metadata account snapshot.
pub use self::metaplex_token_metadata::materializer_metadata_materialize_metaplex_metadata_snapshot;
/// Compatibility materializer retaining the historical generic metadata type name.
pub use self::solana_program_metadata::MtMetadataMaterializer;
/// Stable Solana Program Metadata instruction materializer.
pub use self::solana_program_metadata::MtSolanaProgramMetadataMaterializer;
/// Solana Program Metadata instruction materializer.
pub use self::solana_program_metadata::MtMetadataSolanaProgramMaterializer;
/// Materializes one decoded Solana Program Metadata account snapshot.
pub use self::solana_program_metadata::materializer_metadata_materialize_solana_program_metadata_account_snapshot;
/// Materializes one bounded Solana Program Metadata Buffer account snapshot.
pub use self::solana_program_metadata::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot;
/// Materializes one bounded Solana Program Metadata Metadata account snapshot.
pub use self::solana_program_metadata::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot;
/// SPL Token-2022 embedded metadata and group instruction materializer.
pub use self::token_2022::MtMetadataToken2022Materializer;
/// Materializes Token-2022 embedded metadata and group snapshots.
pub use self::token_2022::materializer_metadata_materialize_token_2022_snapshot;
/// Event families accepted by the Metaplex Token Metadata instruction materializer.
pub(crate) use self::metaplex_token_metadata::MT_METADATA_METAPLEX_TOKEN_METADATA_ACCEPTED_FAMILIES;
/// Stable runtime component name for Metaplex Token Metadata projections.
pub(crate) use self::metaplex_token_metadata::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME;
/// Stable processor name for Metaplex Token Metadata projections.
@@ -33,17 +37,21 @@ pub(crate) use self::metaplex_token_metadata::MT_METADATA_METAPLEX_TOKEN_METADAT
/// Canonical tracing target for Metaplex Token Metadata materialization.
pub(crate) use self::metaplex_token_metadata::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA;
/// Event families accepted by the Solana Program Metadata instruction materializer.
pub(crate) use self::solana_program_metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES;
pub(crate) use self::solana_program_metadata::MT_METADATA_SOLANA_PROGRAM_ACCEPTED_FAMILIES;
/// Stable runtime component name for Solana Program Metadata projections.
pub(crate) use self::solana_program_metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME;
pub(crate) use self::solana_program_metadata::MT_METADATA_SOLANA_PROGRAM_COMPONENT_NAME;
/// Stable processor name for Solana Program Metadata projections.
pub(crate) use self::solana_program_metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME;
pub(crate) use self::solana_program_metadata::MT_METADATA_SOLANA_PROGRAM_PROCESSOR_NAME;
/// Stable projection schema version for Solana Program Metadata outputs.
pub(crate) use self::solana_program_metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION;
pub(crate) use self::solana_program_metadata::MT_METADATA_SOLANA_PROGRAM_PROJECTION_VERSION;
/// Canonical tracing target for Solana Program Metadata materialization.
pub(crate) use self::solana_program_metadata::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;
pub(crate) use self::solana_program_metadata::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM;
/// Event families accepted by the Token-2022 metadata instruction materializer.
pub(crate) use self::token_2022::MT_METADATA_TOKEN_2022_ACCEPTED_FAMILIES;
/// Stable runtime component name for Token-2022 embedded metadata projections.
pub(crate) use self::token_2022::MT_METADATA_TOKEN_2022_COMPONENT_NAME;
/// Exact embedded Token Metadata and Token Group instruction ownership.
pub(crate) use self::token_2022::MT_METADATA_TOKEN_2022_INSTRUCTION_ENTRIES;
/// Stable processor name for Token-2022 embedded metadata projections.
pub(crate) use self::token_2022::MT_METADATA_TOKEN_2022_PROCESSOR_NAME;
/// Stable Token-2022 embedded metadata projection version.

View File

@@ -1,17 +1,22 @@
// file: kb-lib/src/materializer/metadata/metaplex_token_metadata.rs
// version: 2
// version: 3
//! Metaplex Token Metadata account-state materializer.
mod account;
mod constants;
mod materializer;
mod state;
/// Materializes one canonical non-Metadata Metaplex account snapshot.
pub use self::account::materializer_metadata_materialize_metaplex_account_snapshot;
/// Metaplex Token Metadata instruction materializer.
pub use self::materializer::MtMetadataMetaplexTokenMetadataMaterializer;
/// Materializes one canonical Metaplex Metadata account snapshot.
pub use self::state::materializer_metadata_materialize_metaplex_metadata_snapshot;
/// Event families accepted by the Metaplex Token Metadata instruction materializer.
pub(crate) use self::constants::MT_METADATA_METAPLEX_TOKEN_METADATA_ACCEPTED_FAMILIES;
/// Stable runtime component name for Metaplex Token Metadata projections.
pub(crate) use self::constants::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME;
/// Stable processor name for Metaplex Token Metadata projections.

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/metadata/metaplex_token_metadata/constants.rs
// version: 4
// version: 6
//! Local constants for the metadata metaplex_token_metadata materializer.
@@ -9,6 +9,9 @@ pub(crate) const MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME: &str =
/// Stable processor name persisted by metadata projections.
pub(crate) const MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME: &str =
"materializer.metadata.metaplex_token_metadata";
/// Event families accepted by the Metaplex Token Metadata instruction materializer.
pub(crate) const MT_METADATA_METAPLEX_TOKEN_METADATA_ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
&[crate::MdEventFamily::Metadata];
/// Stable projection contract version.
pub(crate) const MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for the metadata metaplex_token_metadata materializer.

View File

@@ -0,0 +1,234 @@
// file: kb-lib/src/materializer/metadata/metaplex_token_metadata/materializer.rs
// version: 1
//! Metaplex Token Metadata instruction materialization.
/// Metaplex Token Metadata instruction materializer.
#[derive(Clone, Debug, Default)]
pub struct MtMetadataMetaplexTokenMetadataMaterializer;
impl crate::MtMaterializer for crate::MtMetadataMetaplexTokenMetadataMaterializer {
fn materializer_name(&self) -> &'static str {
return crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME;
}
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::Metadata
&& event.program_id.0 == kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID
&& event.surface_code.0 == crate::DC_METADATA_MTM_SURFACE_CODE
&& instruction_contract(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![crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::Metadata,
}]);
}
}
impl crate::MtApiEventMaterializer for crate::MtMetadataMetaplexTokenMetadataMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return crate::MT_METADATA_METAPLEX_TOKEN_METADATA_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return observation.event.event_family == crate::MdEventFamily::Metadata
&& observation.event.program_id.0
== kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID
&& observation.event.surface_code.0 == crate::DC_METADATA_MTM_SURFACE_CODE
&& instruction_contract(observation.event.event_name.0.as_str()).is_some()
&& payload_contract_matches(observation);
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
if !crate::MtApiEventMaterializer::accepts_observation(self, observation) {
return crate::MtApiMaterializerExecutionResult::ignored();
}
if observation.transaction_failed || !observation.observation_committed {
return crate::MtApiMaterializerExecutionResult::refused(
"failed_transaction_metaplex_token_metadata_refused",
"failed or uncommitted Metaplex Token Metadata instructions cannot create metadata outputs",
);
}
let operation = observation.event.event_name.0.as_str();
let (_, historical) = match instruction_contract(operation) {
std::option::Option::Some(value) => value,
std::option::Option::None => return crate::MtApiMaterializerExecutionResult::ignored(),
};
let deprecated = operation.starts_with("deprecated_");
let output = crate::MtApiMaterializedOutput {
output_key: format!(
"metaplex_token_metadata_instruction:{}:{}:{}",
observation.event.signature.0, observation.event.instruction_path.0, operation
),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION,
"domain": "metaplex_token_metadata",
"projectionKind": "committed_instruction_fact",
"projectionSemantics": "committed_instruction_fact_non_authoritative_state",
"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(),
"sourceKind": observation.event.source_kind,
"transactionSucceeded": true,
"historical": historical,
"deprecated": deprecated,
"authoritativeFinalStateCaptured": false,
"stateProjectionAuthority": "post_execution_account_snapshot",
"parameters": observation.payload_json.get("parameters").cloned().unwrap_or(serde_json::Value::Null),
"accounts": observation.payload_json.get("accounts").cloned().unwrap_or(serde_json::Value::Null),
"decodedPayload": observation.payload_json.clone(),
"provenance": {
"processorName": crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"decoderProof": observation.proof.clone()
}
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA,
component_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
processor_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
action = "materialize_metaplex_token_metadata_instruction",
operation = operation,
historical = historical,
deprecated = deprecated,
signature = %observation.event.signature.0,
instruction_path = %observation.event.instruction_path.0,
output_count = 1_usize,
"materialize committed Metaplex Token Metadata instruction"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
}
fn instruction_contract(operation: &str) -> std::option::Option<(u8, bool)> {
return crate::DC_METADATA_MTM_INSTRUCTION_ENTRIES
.iter()
.find(|(_, code, _)| return *code == operation)
.map(|(discriminator, _, historical)| return (*discriminator, *historical));
}
fn payload_contract_matches(observation: &crate::DcApiDecodedObservation) -> bool {
let instruction =
observation.payload_json.get("instruction").and_then(serde_json::Value::as_str);
let program_id = observation.payload_json.get("programId").and_then(serde_json::Value::as_str);
return instruction == std::option::Option::Some(observation.event.event_name.0.as_str())
&& program_id
== std::option::Option::Some(
kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID,
);
}
#[cfg(test)]
mod tests {
fn observation(operation: &str, committed: bool) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: format!("metaplex_token_metadata:{operation}:0"),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature111".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("0".to_string()),
program_id: crate::MdProgramId(
kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID.to_string(),
),
protocol_code: crate::MdProtocolCode(
crate::DC_METADATA_MTM_SURFACE_CODE.to_string(),
),
surface_code: crate::MdSurfaceCode(crate::DC_METADATA_MTM_SURFACE_CODE.to_string()),
event_code: crate::MdEventCode(format!(
"{}.{}",
crate::DC_METADATA_MTM_SURFACE_CODE,
operation
)),
event_name: crate::MdEventName(operation.to_string()),
event_family: crate::MdEventFamily::Metadata,
source_kind: crate::MdEventSourceKind::Instruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({
"programId": kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID,
"instruction": operation,
"parameters": {},
"accounts": []
}),
transaction_failed: !committed,
transaction_error: std::option::Option::None,
observation_committed: committed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["wire".to_string()],
},
};
}
#[test]
fn every_decoded_instruction_has_one_metadata_owner() {
let materializer = crate::MtMetadataMetaplexTokenMetadataMaterializer;
for (_, operation, historical) in crate::DC_METADATA_MTM_INSTRUCTION_ENTRIES.iter().copied()
{
let value = observation(operation, true);
assert!(crate::MtApiEventMaterializer::accepts_observation(&materializer, &value));
let result = crate::MtApiEventMaterializer::materialize(&materializer, &value);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs.len(), 1);
assert_eq!(result.outputs[0].payload_json["operation"], operation);
assert_eq!(result.outputs[0].payload_json["historical"], historical);
assert_eq!(
result.outputs[0].payload_json["deprecated"],
operation.starts_with("deprecated_")
);
}
}
#[test]
fn failed_and_foreign_observations_do_not_create_outputs() {
let materializer = crate::MtMetadataMetaplexTokenMetadataMaterializer;
let failed = observation("create_metadata_account_v3", false);
let refused = crate::MtApiEventMaterializer::materialize(&materializer, &failed);
assert_eq!(refused.status, crate::MtApiMaterializerOutcomeStatus::Refused);
let mut foreign = observation("create_metadata_account_v3", true);
foreign.event.program_id =
crate::MdProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string());
let ignored = crate::MtApiEventMaterializer::materialize(&materializer, &foreign);
assert_eq!(ignored.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/metadata/solana_program_metadata.rs
// version: 2
// version: 3
//! Solana Program Metadata materialization surface.
@@ -7,10 +7,8 @@ mod constants;
mod materializer;
mod state;
/// Compatibility materializer retaining the historical generic metadata type name.
pub use self::materializer::MtMetadataMaterializer;
/// Stable Solana Program Metadata instruction materializer.
pub use self::materializer::MtSolanaProgramMetadataMaterializer;
/// Solana Program Metadata instruction materializer.
pub use self::materializer::MtMetadataSolanaProgramMaterializer;
/// Materializes one decoded Solana Program Metadata account snapshot.
pub use self::state::materializer_metadata_materialize_solana_program_metadata_account_snapshot;
/// Materializes one bounded Solana Program Metadata Buffer account snapshot.
@@ -19,12 +17,12 @@ pub use self::state::materializer_metadata_materialize_solana_program_metadata_b
pub use self::state::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot;
/// Event families accepted by the Solana Program Metadata instruction materializer.
pub(crate) use self::constants::MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES;
pub(crate) use self::constants::MT_METADATA_SOLANA_PROGRAM_ACCEPTED_FAMILIES;
/// Stable runtime component name for Solana Program Metadata projections.
pub(crate) use self::constants::MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME;
pub(crate) use self::constants::MT_METADATA_SOLANA_PROGRAM_COMPONENT_NAME;
/// Stable processor name for Solana Program Metadata projections.
pub(crate) use self::constants::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME;
pub(crate) use self::constants::MT_METADATA_SOLANA_PROGRAM_PROCESSOR_NAME;
/// Stable projection schema version for Solana Program Metadata outputs.
pub(crate) use self::constants::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION;
pub(crate) use self::constants::MT_METADATA_SOLANA_PROGRAM_PROJECTION_VERSION;
/// Canonical tracing target for Solana Program Metadata materialization.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM;

View File

@@ -4,15 +4,15 @@
//! Local constants for the Solana Program Metadata materializer.
/// Stable runtime component name for Solana Program Metadata materialization.
pub(crate) const MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME: &str =
pub(crate) const MT_METADATA_SOLANA_PROGRAM_COMPONENT_NAME: &str =
"kb-lib.materializer.metadata.solana_program_metadata";
/// Stable processor name persisted by Solana Program Metadata projections.
pub(crate) const MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME: &str =
pub(crate) const MT_METADATA_SOLANA_PROGRAM_PROCESSOR_NAME: &str =
"materializer.metadata.solana_program_metadata";
/// Stable projection schema version for Solana Program Metadata outputs.
pub(crate) const MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION: u32 = 1;
pub(crate) const MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
pub(crate) const MT_METADATA_SOLANA_PROGRAM_PROJECTION_VERSION: u32 = 1;
pub(crate) const MT_METADATA_SOLANA_PROGRAM_ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
&[crate::MdEventFamily::Metadata];
/// Canonical tracing target for Solana Program Metadata materialization.
pub(crate) const TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA: &str =
pub(crate) const TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM: &str =
"kb-lib.materializer.metadata.solana_program_metadata";

View File

@@ -1,19 +1,15 @@
// file: kb-lib/src/materializer/metadata/solana_program_metadata/materializer.rs
// version: 4
// version: 5
//! Committed Solana Program Metadata instruction projections.
/// Stable Solana Program Metadata instruction materializer.
#[derive(Clone, Debug, Default)]
pub struct MtSolanaProgramMetadataMaterializer;
pub struct MtMetadataSolanaProgramMaterializer;
/// Compatibility materializer retaining the historical generic metadata type name.
#[derive(Clone, Debug, Default)]
pub struct MtMetadataMaterializer;
impl crate::MtMaterializer for crate::MtSolanaProgramMetadataMaterializer {
impl crate::MtMaterializer for crate::MtMetadataSolanaProgramMaterializer {
fn materializer_name(&self) -> &'static str {
return crate::MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME;
return crate::MT_METADATA_SOLANA_PROGRAM_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -45,84 +41,16 @@ impl crate::MtMaterializer for crate::MtSolanaProgramMetadataMaterializer {
}
}
impl crate::MtMaterializer for crate::MtMetadataMaterializer {
fn materializer_name(&self) -> &'static str {
return crate::MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return crate::MtMaterializer::accepts_event(
&crate::MtSolanaProgramMetadataMaterializer,
event,
);
}
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return crate::MtMaterializer::materialize_event(
&crate::MtSolanaProgramMetadataMaterializer,
event,
);
}
}
impl crate::MtApiEventMaterializer for crate::MtMetadataMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiEventMaterializer::identity(
&crate::MtSolanaProgramMetadataMaterializer,
);
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return crate::MtApiEventMaterializer::accepted_families(
&crate::MtSolanaProgramMetadataMaterializer,
);
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return crate::MtApiEventMaterializer::accepts_observation(
&crate::MtSolanaProgramMetadataMaterializer,
observation,
);
}
fn transaction_policy(
&self,
family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiEventMaterializer::transaction_policy(
&crate::MtSolanaProgramMetadataMaterializer,
family,
);
}
fn materialize(
&self,
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
return crate::MtApiEventMaterializer::materialize(
&crate::MtSolanaProgramMetadataMaterializer,
observation,
);
}
}
impl crate::MtApiEventMaterializer for crate::MtSolanaProgramMetadataMaterializer {
impl crate::MtApiEventMaterializer for crate::MtMetadataSolanaProgramMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME.to_string(),
name: crate::MT_METADATA_SOLANA_PROGRAM_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return crate::MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES;
return crate::MT_METADATA_SOLANA_PROGRAM_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
@@ -165,7 +93,7 @@ impl crate::MtApiEventMaterializer for crate::MtSolanaProgramMetadataMaterialize
),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION,
"projectionVersion": crate::MT_METADATA_SOLANA_PROGRAM_PROJECTION_VERSION,
"domain": "solana_program_metadata",
"projectionKind": "committed_instruction_mutation",
"projectionSemantics": "committed_instruction_fact_non_authoritative_state",
@@ -183,14 +111,14 @@ impl crate::MtApiEventMaterializer for crate::MtSolanaProgramMetadataMaterialize
"externalContentFetched": false,
"externalAccountRead": false,
"provenance": {
"processorName": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME,
"processorName": crate::MT_METADATA_SOLANA_PROGRAM_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"decoderProof": observation.proof.clone()
}
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA,
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM,
action = "materialize_solana_program_metadata_instruction",
operation = operation,
signature = %observation.event.signature.0,
@@ -355,9 +283,9 @@ mod tests {
#[test]
fn every_stable_instruction_has_one_materialized_mutation_fact() {
let materializer = crate::MtSolanaProgramMetadataMaterializer;
let materializer = crate::MtMetadataSolanaProgramMaterializer;
let identity = crate::MtApiEventMaterializer::identity(&materializer);
assert_eq!(identity.name, crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME);
assert_eq!(identity.name, crate::MT_METADATA_SOLANA_PROGRAM_PROCESSOR_NAME);
for (_, operation) in crate::DC_METADATA_SPM_INSTRUCTION_ENTRIES.iter().copied() {
let value = observation(operation, true);
assert!(crate::MtApiEventMaterializer::accepts_observation(&materializer, &value));
@@ -369,14 +297,14 @@ mod tests {
assert_eq!(result.outputs[0].payload_json["projection"]["replaced"], false);
assert_eq!(
result.outputs[0].payload_json["provenance"]["processorName"],
crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME
crate::MT_METADATA_SOLANA_PROGRAM_PROCESSOR_NAME
);
}
}
#[test]
fn failed_instruction_is_refused_and_foreign_surface_is_ignored() {
let materializer = crate::MtSolanaProgramMetadataMaterializer;
let materializer = crate::MtMetadataSolanaProgramMaterializer;
let failed = observation("set_data", false);
let failure = crate::MtApiEventMaterializer::materialize(&materializer, &failed);
assert_eq!(failure.status, crate::MtApiMaterializerOutcomeStatus::Refused);
@@ -424,7 +352,7 @@ mod tests {
#[test]
fn trim_keeps_prestable_alias_as_provenance_only() {
let materializer = crate::MtSolanaProgramMetadataMaterializer;
let materializer = crate::MtMetadataSolanaProgramMaterializer;
let value = observation("trim", true);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &value);
assert_eq!(

View File

@@ -63,7 +63,7 @@ pub fn materializer_metadata_materialize_solana_program_metadata_buffer_snapshot
output_key: format!("solana_program_metadata_account_state:{}", snapshot.account),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION,
"projectionVersion": crate::MT_METADATA_SOLANA_PROGRAM_PROJECTION_VERSION,
"domain": "solana_program_metadata",
"projectionKind": "buffer_state",
"projectionSemantics": "authoritative_bounded_owned_account_snapshot",
@@ -83,7 +83,7 @@ pub fn materializer_metadata_materialize_solana_program_metadata_buffer_snapshot
"externalAccountRead": false,
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
"provenance": {
"processorName": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME,
"processorName": crate::MT_METADATA_SOLANA_PROGRAM_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"source": "bounded_solana_program_metadata_account_decoder"
}
@@ -150,7 +150,7 @@ pub fn materializer_metadata_materialize_solana_program_metadata_metadata_snapsh
output_key: format!("solana_program_metadata_account_state:{}", snapshot.account),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION,
"projectionVersion": crate::MT_METADATA_SOLANA_PROGRAM_PROJECTION_VERSION,
"domain": "solana_program_metadata",
"projectionKind": "metadata_state",
"projectionSemantics": "authoritative_bounded_owned_account_snapshot",
@@ -177,7 +177,7 @@ pub fn materializer_metadata_materialize_solana_program_metadata_metadata_snapsh
"externalAccountRead": false,
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
"provenance": {
"processorName": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME,
"processorName": crate::MT_METADATA_SOLANA_PROGRAM_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"source": "bounded_solana_program_metadata_account_decoder"
}

View File

@@ -1,16 +1,23 @@
// file: kb-lib/src/materializer/metadata/token_2022.rs
// version: 2
// version: 3
//! Embedded SPL Token-2022 metadata materialization surface.
mod constants;
mod materializer;
mod state;
/// SPL Token-2022 embedded metadata and group instruction materializer.
pub use self::materializer::MtMetadataToken2022Materializer;
/// Materializes Token-2022 embedded metadata and group snapshots.
pub use self::state::materializer_metadata_materialize_token_2022_snapshot;
/// Event families accepted by the Token-2022 metadata instruction materializer.
pub(crate) use self::constants::MT_METADATA_TOKEN_2022_ACCEPTED_FAMILIES;
/// Stable runtime component name for Token-2022 embedded metadata projections.
pub(crate) use self::constants::MT_METADATA_TOKEN_2022_COMPONENT_NAME;
/// Exact embedded Token Metadata and Token Group instruction ownership.
pub(crate) use self::constants::MT_METADATA_TOKEN_2022_INSTRUCTION_ENTRIES;
/// Stable processor name for Token-2022 embedded metadata projections.
pub(crate) use self::constants::MT_METADATA_TOKEN_2022_PROCESSOR_NAME;
/// Stable Token-2022 embedded metadata projection version.

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/metadata/token_2022/constants.rs
// version: 3
// version: 4
//! Local constants for the metadata token_2022 materializer.
@@ -8,6 +8,21 @@ pub(crate) const MT_METADATA_TOKEN_2022_COMPONENT_NAME: &str =
"kb-lib.materializer.metadata.token_2022";
/// Stable processor name persisted by metadata projections.
pub(crate) const MT_METADATA_TOKEN_2022_PROCESSOR_NAME: &str = "materializer.metadata.token_2022";
/// Event families accepted by the Token-2022 metadata instruction materializer.
pub(crate) const MT_METADATA_TOKEN_2022_ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
&[crate::MdEventFamily::Metadata];
/// Exact embedded Token Metadata and Token Group instruction ownership.
pub(crate) const MT_METADATA_TOKEN_2022_INSTRUCTION_ENTRIES: &[&str] = &[
"initialize_token_metadata",
"update_token_metadata_field",
"remove_token_metadata_key",
"update_token_metadata_authority",
"emit_token_metadata",
"initialize_token_group",
"update_token_group_max_size",
"update_token_group_authority",
"initialize_token_group_member",
];
/// Stable projection contract version.
pub(crate) const MT_METADATA_TOKEN_2022_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for the metadata token_2022 materializer.

View File

@@ -0,0 +1,232 @@
// file: kb-lib/src/materializer/metadata/token_2022/materializer.rs
// version: 1
//! SPL Token-2022 embedded metadata and group instruction materialization.
/// SPL Token-2022 embedded metadata and group instruction materializer.
#[derive(Clone, Debug, Default)]
pub struct MtMetadataToken2022Materializer;
impl crate::MtMaterializer for crate::MtMetadataToken2022Materializer {
fn materializer_name(&self) -> &'static str {
return crate::MT_METADATA_TOKEN_2022_COMPONENT_NAME;
}
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::Metadata
&& event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
&& event.surface_code.0 == crate::DC_SPL_TOKEN_2022_SURFACE_CODE
&& crate::MT_METADATA_TOKEN_2022_INSTRUCTION_ENTRIES
.iter()
.any(|code| return *code == event.event_name.0.as_str());
}
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![crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::Metadata,
}]);
}
}
impl crate::MtApiEventMaterializer for crate::MtMetadataToken2022Materializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_METADATA_TOKEN_2022_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return crate::MT_METADATA_TOKEN_2022_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return observation.event.event_family == crate::MdEventFamily::Metadata
&& observation.event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
&& observation.event.surface_code.0 == crate::DC_SPL_TOKEN_2022_SURFACE_CODE
&& crate::MT_METADATA_TOKEN_2022_INSTRUCTION_ENTRIES
.iter()
.any(|code| return *code == observation.event.event_name.0.as_str())
&& payload_contract_matches(observation);
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
if !crate::MtApiEventMaterializer::accepts_observation(self, observation) {
return crate::MtApiMaterializerExecutionResult::ignored();
}
if observation.transaction_failed || !observation.observation_committed {
return crate::MtApiMaterializerExecutionResult::refused(
"failed_transaction_token_2022_metadata_refused",
"failed or uncommitted Token-2022 metadata instructions cannot create metadata outputs",
);
}
let operation = observation.event.event_name.0.as_str();
let output = crate::MtApiMaterializedOutput {
output_key: format!(
"token_2022_metadata_instruction:{}:{}:{}",
observation.event.signature.0, observation.event.instruction_path.0, operation
),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_METADATA_TOKEN_2022_PROJECTION_VERSION,
"domain": "token_2022_metadata",
"projectionKind": projection_kind(operation),
"projectionSemantics": "committed_instruction_fact_non_authoritative_state",
"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(),
"sourceKind": observation.event.source_kind,
"transactionSucceeded": true,
"authoritativeFinalStateCaptured": false,
"stateProjectionAuthority": "post_execution_token_2022_mint_snapshot",
"parameters": observation.payload_json.get("parameters").cloned().unwrap_or(serde_json::Value::Null),
"accounts": observation.payload_json.get("accounts").cloned().unwrap_or(serde_json::Value::Null),
"authority": observation.payload_json.get("authority").cloned().unwrap_or(serde_json::Value::Null),
"decodedPayload": observation.payload_json.clone(),
"provenance": {
"processorName": crate::MT_METADATA_TOKEN_2022_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"decoderProof": observation.proof.clone()
}
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022,
component_name = crate::MT_METADATA_TOKEN_2022_COMPONENT_NAME,
processor_name = crate::MT_METADATA_TOKEN_2022_PROCESSOR_NAME,
action = "materialize_token_2022_metadata_instruction",
operation = operation,
signature = %observation.event.signature.0,
instruction_path = %observation.event.instruction_path.0,
output_count = 1_usize,
"materialize committed Token-2022 metadata instruction"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
}
fn payload_contract_matches(observation: &crate::DcApiDecodedObservation) -> bool {
let instruction =
observation.payload_json.get("instruction").and_then(serde_json::Value::as_str);
let program_id = observation.payload_json.get("programId").and_then(serde_json::Value::as_str);
return instruction == std::option::Option::Some(observation.event.event_name.0.as_str())
&& program_id == std::option::Option::Some(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID);
}
fn projection_kind(operation: &str) -> &'static str {
return match operation {
"initialize_token_metadata" => "metadata_initialized",
"update_token_metadata_field" => "metadata_field_updated",
"remove_token_metadata_key" => "metadata_key_removed",
"update_token_metadata_authority" => "metadata_authority_updated",
"emit_token_metadata" => "metadata_emitted",
"initialize_token_group" => "token_group_initialized",
"update_token_group_max_size" => "token_group_max_size_updated",
"update_token_group_authority" => "token_group_authority_updated",
"initialize_token_group_member" => "token_group_member_initialized",
_ => "unknown",
};
}
#[cfg(test)]
mod tests {
fn observation(operation: &str, committed: bool) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: format!("token_2022:{operation}:0"),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature111".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("0".to_string()),
program_id: crate::MdProgramId(
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string(),
),
protocol_code: crate::MdProtocolCode(
crate::DC_SPL_TOKEN_2022_SURFACE_CODE.to_string(),
),
surface_code: crate::MdSurfaceCode(
crate::DC_SPL_TOKEN_2022_SURFACE_CODE.to_string(),
),
event_code: crate::MdEventCode(format!(
"{}.{}",
crate::DC_SPL_TOKEN_2022_SURFACE_CODE,
operation
)),
event_name: crate::MdEventName(operation.to_string()),
event_family: crate::MdEventFamily::Metadata,
source_kind: crate::MdEventSourceKind::Instruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({
"programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"instruction": operation,
"parameters": {},
"accounts": [],
"authority": null
}),
transaction_failed: !committed,
transaction_error: std::option::Option::None,
observation_committed: committed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["wire".to_string()],
},
};
}
#[test]
fn every_embedded_metadata_and_group_instruction_has_one_owner() {
let materializer = crate::MtMetadataToken2022Materializer;
for operation in crate::MT_METADATA_TOKEN_2022_INSTRUCTION_ENTRIES {
let value = observation(operation, true);
assert!(crate::MtApiEventMaterializer::accepts_observation(&materializer, &value));
let result = crate::MtApiEventMaterializer::materialize(&materializer, &value);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs.len(), 1);
assert_eq!(result.outputs[0].payload_json["operation"], *operation);
}
}
#[test]
fn failed_and_non_metadata_observations_do_not_create_outputs() {
let materializer = crate::MtMetadataToken2022Materializer;
let failed = observation("initialize_token_metadata", false);
let refused = crate::MtApiEventMaterializer::materialize(&materializer, &failed);
assert_eq!(refused.status, crate::MtApiMaterializerOutcomeStatus::Refused);
let mut unrelated = observation("initialize_token_metadata", true);
unrelated.event.event_name = crate::MdEventName("transfer_checked".to_string());
unrelated.payload_json["instruction"] =
serde_json::Value::String("transfer_checked".to_string());
let ignored = crate::MtApiEventMaterializer::materialize(&materializer, &unrelated);
assert_eq!(ignored.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
}