v0.4.8-pre.008
This commit is contained in:
@@ -1,8 +1,19 @@
|
||||
<!-- file: kb-lib/CHANGELOG.md -->
|
||||
<!-- version: 28 -->
|
||||
<!-- version: 30 -->
|
||||
|
||||
# CHANGELOG — kb-lib
|
||||
|
||||
## 0.4.8-pre.008 — matérialiseurs metadata spécialisés
|
||||
|
||||
- corrige le décodeur Solana Program Metadata pour interpréter les flags `signer` et `writable` du replay comme des privilèges globaux de message : les privilèges requis sont désormais des minima, et les sur-ensembles légitimes dus au fee payer ou à d’autres instructions ne sont plus rejetés ;
|
||||
- ajoute une régression couvrant les neuf instructions avec une autorité signer et writable au niveau du message, tout en conservant le refus des privilèges requis manquants ;
|
||||
- corrige la déclaration Rust de `MT_METADATA_METAPLEX_TOKEN_METADATA_ACCEPTED_FAMILIES` livrée dans `pre.008-fix-001`.
|
||||
- remplace le type ambigu `MtSolanaProgramMetadataMaterializer` par `MtMetadataSolanaProgramMaterializer` et retire le wrapper générique `MtMetadataMaterializer` ;
|
||||
- ajoute `MtMetadataMetaplexTokenMetadataMaterializer` et `MtMetadataToken2022Materializer`, chacun avec ses deux contrats, son identité, son tracing target et un ownership instructionnel exact ;
|
||||
- conserve séparément les APIs de snapshots Metaplex, Solana Program Metadata et Token-2022, sans confondre fait d’instruction et état autoritatif ;
|
||||
- classe les neuf instructions incorporées Token Metadata et Token Group sous la famille `Metadata`, afin qu’elles soient possédées par le matérialiseur metadata spécialisé plutôt que par un composant d’administration générique ;
|
||||
- étend les tests de façade publique et les contrats d’inventaire aux trois matérialiseurs metadata spécialisés.
|
||||
|
||||
## 0.4.8-pre.007 — exécuteur Solana Program Metadata et structure Metaplex
|
||||
|
||||
- ajoute `ExMetadataSolanaProgramMetadataExecutor`, les intents typés, les helpers de PDA et les builders wire exacts des neuf instructions stables `0..8` ;
|
||||
@@ -24,9 +35,9 @@
|
||||
- remplace les implémentations génériques `core.rs` par des `materializer.rs`, transforme les fichiers parents en façades pures et déplace toutes les familles, surfaces, tables et identités vers les `constants.rs` réexportés jusqu’à la racine de crate ;
|
||||
- rend les 14 matérialisateurs réservés réellement neutres sur les deux traits : famille vide constante, refus legacy, liste d’événements vide et résultat contextuel `Ignored` ;
|
||||
- aligne les routes de logging d’exemple sur `compliance.audit`, `transaction.annotations` et `token.accounts`, et ajoute des audits mécaniques empêchant la réintroduction de noms, constantes ou façades non conformes ;
|
||||
- conserve `MtMetadataMaterializer` comme wrapper de compatibilité du composant canonique `MtSolanaProgramMetadataMaterializer`, sans second `processorName` ni second propriétaire de faits.
|
||||
- prépare la spécialisation complète des matérialiseurs metadata, finalisée en `0.4.8-pre.008` sans wrapper générique ambigu.
|
||||
- corrige les warnings de contrats opérationnels en ajoutant des événements `tracing` réels aux matérialisations `fees`, Metaplex Token Metadata et metadata Token-2022, avec `component_name`, `processor_name`, statut, identité de compte ou d’instruction et nombre de sorties ;
|
||||
- consomme désormais l’identité runtime `MT_METADATA_TOKEN_2022_COMPONENT_NAME` dans l’implémentation state-only au lieu de la limiter aux réexports, et étend les audits pour refuser tout tracing target opérationnel déclaré mais inutilisé ainsi que toute identité de composant absente de son implémentation.
|
||||
- consomme désormais l’identité runtime `MT_METADATA_TOKEN_2022_COMPONENT_NAME` dans les opérations de snapshot, puis étend les audits pour refuser tout tracing target opérationnel déclaré mais inutilisé ainsi que toute identité de composant absente de son implémentation.
|
||||
|
||||
## 0.4.8-pre.006 — matérialisation Solana Program Metadata
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-lib/README.md -->
|
||||
<!-- version: 25 -->
|
||||
<!-- version: 26 -->
|
||||
|
||||
# kb-lib
|
||||
|
||||
@@ -48,7 +48,7 @@ L’arbre `src/materializer` suit un contrat homogène :
|
||||
- une identité runtime `kb-lib.materializer.*` distincte du `processorName` persisté `materializer.*` ;
|
||||
- des coquilles réservées réellement inactives sur les deux contrats legacy et contextuel.
|
||||
|
||||
L’inventaire audité comprend 25 composants nommés : neuf matérialisateurs actifs, quatorze coquilles réservées et deux composants metadata state-only. Le détail est consigné dans [l’audit de convention des matérialisateurs](../docs/audits/V0_4_8_PRE_007_MATERIALIZER_CONVENTION_AUDIT.md).
|
||||
L’inventaire audité comprend 25 composants nommés : onze matérialisateurs actifs et quatorze coquilles réservées. Les composants metadata Metaplex Token Metadata, Solana Program Metadata et Token-2022 possèdent chacun un type spécialisé, une identité propre et leurs APIs de snapshots lorsque leur état on-chain l’exige. Le détail est consigné dans [l’audit de convention des matérialisateurs](../docs/audits/V0_4_8_PRE_007_MATERIALIZER_CONVENTION_AUDIT.md).
|
||||
|
||||
## Responsabilités
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-lib/USAGE.md -->
|
||||
<!-- version: 13 -->
|
||||
<!-- version: 14 -->
|
||||
|
||||
# Utilisation de kb-lib
|
||||
|
||||
@@ -153,10 +153,16 @@ println!("status={:?}", result.status);
|
||||
|
||||
Le tag `5` est toujours publié comme `trim`. Son ancien nom pré-stable `withdraw_excess_lamports` est conservé uniquement comme provenance, car aucune release stable ne l’expose comme instruction distincte. Les URL et références externes restent des données on-chain ; le décodeur ne réalise aucun fetch.
|
||||
|
||||
Le matérialiseur `MtMetadataMaterializer` accepte les neuf observations d’instructions de la surface exacte `metadata.solana_program_metadata`. Il refuse les transactions échouées et produit un fait de mutation par instruction. Ces faits ne remplacent pas un snapshot de compte : l’état final doit être matérialisé depuis la lecture post-exécution.
|
||||
Les trois domaines metadata disposent de matérialiseurs instructionnels spécialisés :
|
||||
|
||||
- `MtMetadataMetaplexTokenMetadataMaterializer` pour les observations Metaplex de famille metadata ;
|
||||
- `MtMetadataSolanaProgramMaterializer` pour les neuf instructions stables de `ProgM6…` ;
|
||||
- `MtMetadataToken2022Materializer` pour les instructions incorporées Token Metadata et Token Group de Token-2022.
|
||||
|
||||
Chaque type possède son propre `componentName`, son propre `processorName`, ses familles acceptées et un ownership exact. Les transactions échouées sont refusées. Les faits d’instructions ne remplacent jamais les snapshots autoritatifs lus après exécution.
|
||||
|
||||
```rust
|
||||
let materializer = kb_lib::MtMetadataMaterializer;
|
||||
let materializer = kb_lib::MtMetadataSolanaProgramMaterializer;
|
||||
if kb_lib::MtApiEventMaterializer::accepts_observation(&materializer, &observation) {
|
||||
let result = kb_lib::MtApiEventMaterializer::materialize(&materializer, &observation);
|
||||
println!("materialization status={:?}", result.status);
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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] = &[
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
232
kb-lib/src/materializer/metadata/token_2022/materializer.rs
Normal file
232
kb-lib/src/materializer/metadata/token_2022/materializer.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-lib/tests/external_materializer_api.rs
|
||||
// version: 7
|
||||
// version: 8
|
||||
|
||||
//! Downstream-style compilation contract for externally implemented materializers.
|
||||
|
||||
@@ -50,8 +50,9 @@ fn external_materializer_uses_only_the_public_kb_lib_contract() {
|
||||
assert_event_materializer::<kb_lib::MtGovernanceMaterializer>();
|
||||
assert_event_materializer::<kb_lib::MtLendingMaterializer>();
|
||||
assert_event_materializer::<kb_lib::MtLifecycleMaterializer>();
|
||||
assert_event_materializer::<kb_lib::MtMetadataMaterializer>();
|
||||
assert_event_materializer::<kb_lib::MtSolanaProgramMetadataMaterializer>();
|
||||
assert_event_materializer::<kb_lib::MtMetadataMetaplexTokenMetadataMaterializer>();
|
||||
assert_event_materializer::<kb_lib::MtMetadataSolanaProgramMaterializer>();
|
||||
assert_event_materializer::<kb_lib::MtMetadataToken2022Materializer>();
|
||||
assert_event_materializer::<kb_lib::MtLiquidityMaterializer>();
|
||||
assert_event_materializer::<kb_lib::MtNftMaterializer>();
|
||||
assert_event_materializer::<kb_lib::MtOracleMaterializer>();
|
||||
@@ -71,8 +72,9 @@ fn external_materializer_uses_only_the_public_kb_lib_contract() {
|
||||
assert_legacy_materializer::<kb_lib::MtGovernanceMaterializer>();
|
||||
assert_legacy_materializer::<kb_lib::MtLendingMaterializer>();
|
||||
assert_legacy_materializer::<kb_lib::MtLiquidityMaterializer>();
|
||||
assert_legacy_materializer::<kb_lib::MtMetadataMaterializer>();
|
||||
assert_legacy_materializer::<kb_lib::MtSolanaProgramMetadataMaterializer>();
|
||||
assert_legacy_materializer::<kb_lib::MtMetadataMetaplexTokenMetadataMaterializer>();
|
||||
assert_legacy_materializer::<kb_lib::MtMetadataSolanaProgramMaterializer>();
|
||||
assert_legacy_materializer::<kb_lib::MtMetadataToken2022Materializer>();
|
||||
assert_legacy_materializer::<kb_lib::MtNftMaterializer>();
|
||||
assert_legacy_materializer::<kb_lib::MtOracleMaterializer>();
|
||||
assert_legacy_materializer::<kb_lib::MtOrderbookMaterializer>();
|
||||
|
||||
Reference in New Issue
Block a user