v0.4.8-pre.007

This commit is contained in:
2026-08-06 00:52:08 +02:00
parent 80c439c988
commit 44088c71c9
122 changed files with 8903 additions and 3914 deletions

View File

@@ -1,8 +1,33 @@
<!-- file: kb-lib/CHANGELOG.md -->
<!-- version: 23 -->
<!-- version: 28 -->
# CHANGELOG — kb-lib
## 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` ;
- conserve une sélection explicite canonical/non-canonical au lieu de linférer de la présence de `ProgramData`, pour couvrir aussi les contrats des loaders antérieurs ;
- conserve toutes les opérations courantes comme exécutables, y compris `Write`, `SetAuthority`, `SetData`, `SetImmutable`, `Trim` et `Close`, avec approbation destructive explicite plutôt quune suppression de capacité ;
- soumet chaque plan préparé au cadre commun `ExSafetyChecker` et impose simulation, signataires autorisés, plafonds et quatre postconditions canoniques ;
- ajoute la matrice dexécution, le test de parité des capacités et le test dAPI externe depuis la façade de crate ;
- retire lexécuteur Solana Program Metadata de linventaire réservé ;
- corrige le total attendu du test dinventaire des exécuteurs réservés de `104` à `103`, conformément au retrait de `ExMetadataSolanaProgramMetadataExecutor` de cet inventaire ;
- confirme que les neuf opérations stables sont courantes et quaucune annotation `#[deprecated]` nest justifiée par les sources officielles actuelles ;
- sépare les matérialisations Metaplex Token Metadata dans un sous-module dédié sans modifier les clés ni la sémantique métier des projections déjà livrées, et consigne le constat dans un audit structurel dédié ;
- corrige la confusion entre identité runtime `kb-lib.*` et `processorName` persisté : Metaplex utilise désormais `materializer.metadata.metaplex_token_metadata`, Solana Program Metadata `materializer.metadata.solana_program_metadata` et Token-2022 `materializer.metadata.token_2022` ;
- porte les schémas de projection Metaplex et Token-2022 à la version `2` pour refléter cette correction de provenance ;
- extrait la matérialisation Token-2022 de `metadata/core.rs` vers le composant dédié `metadata/token_2022/` sans modifier la fonction publique ni les clés de sortie ;
- confirme que les états `TokenMetadata`, `TokenGroup` et `TokenGroupMember` sont matérialisés, que les metadata pointers appartiennent déjà au matérialiseur dadministration et que les cinq faits dinstructions `spl-token-metadata-interface` restent à fermer en `pre.010` ;
- inscrit dans le TODO et le ROADMAP `0.5.x` un audit transversal des règles de complétude decoder/materializer/executor, de la sécurité et des statuts deprecated/replaced.
- harmonise les 25 composants de matérialisation autour des paires `MT_*_COMPONENT_NAME` / `MT_*_PROCESSOR_NAME`, avec relation exacte entre identité runtime `kb-lib.materializer.*` et processeur persisté `materializer.*` ;
- ajoute la provenance persistée manquante aux sorties `admin`, `compliance.audit`, `fees`, `lifecycle` et `staking`, puis porte leurs contrats à `projectionVersion = 2` ;
- 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 dexemple 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.
- 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 dinstruction et nombre de sorties ;
- consomme désormais lidentité runtime `MT_METADATA_TOKEN_2022_COMPONENT_NAME` dans limplé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.
## 0.4.8-pre.006 — matérialisation Solana Program Metadata
- active `MtMetadataMaterializer` comme matérialiseur contextualisé de la surface exacte `metadata.solana_program_metadata` ;
@@ -15,6 +40,7 @@
- ajoute les helpers publics de matérialisation détats et le test dAPI externe correspondant.
- corrige le helper de test de snapshot afin dutiliser les variantes wire existantes `Encoding::Utf8` et `Format::Json`.
- précise que les opérations destructrices, irréversibles ou écrasantes restent exécutables : leur progression vers simulation, signature et envoi doit être gouvernée par le cadre commun `ExSafetyChecker`, et non supprimée du périmètre de lexécuteur.
- validation reçue le 5 août 2026 : `cargo fmt`, `cargo check --workspace`, `cargo clippy --all-targets`, audit workspace et 667 tests unitaires `kb-lib` réussis, avec les tests dAPI externes et les doc-tests.
## 0.4.8-pre.005 — instructions Solana Program Metadata

View File

@@ -1,5 +1,5 @@
<!-- file: kb-lib/README.md -->
<!-- version: 23 -->
<!-- version: 25 -->
# kb-lib
@@ -20,10 +20,12 @@ Pour Solana Program Metadata, la surface publique fondatrice comprend :
- une politique explicite où les octets situés après `data_length` sont conservés comme capacité allouée et jamais assimilés au contenu logique ;
- les neuf instructions stables `Write`, `Initialize`, `SetAuthority`, `SetData`, `SetImmutable`, `Trim`, `Close`, `Allocate` et `Extend` ;
- les formes runtime actuelles non produites par le SDK, notamment `SetData` sans remplacement de données et les suffixes ignorés de `SetImmutable`, `Trim` et `Close` ;
- une frontière historique où lancien nom pré-stable `WithdrawExcessLamports` du tag `5` reste une provenance de `Trim`, sans dixième entrée de couverture.
- une frontière historique où lancien nom pré-stable `WithdrawExcessLamports` du tag `5` reste une provenance de `Trim`, sans dixième entrée de couverture ;
- une sélection canonical/non-canonical explicite dans les intents `Initialize` et `Allocate`, indépendante de la seule présence de `ProgramData` ;
- deux projections détat autoritatives pour `Buffer` et `Metadata`, avec conservation bornée du contenu on-chain ;
- neuf faits de mutation dinstructions committées, non autoritatifs vis-à-vis de létat final et réconciliables avec les snapshots post-exécution ;
- une classification où les neuf instructions stables sont courantes, sans opération officiellement remplacée ou obsolète à annoter `Deprecated`.
- une classification où les neuf instructions stables sont courantes, sans opération officiellement remplacée ou obsolète à annoter `Deprecated` ;
- un exécuteur typé couvrant les neuf opérations, avec dérivation des PDA, wire exact, comptes ordonnés, signataires, approbation explicite des mutations dangereuses et évaluation commune par `ExSafetyChecker`.
Pour Metaplex Token Metadata, elle fournit :
@@ -32,7 +34,21 @@ Pour Metaplex Token Metadata, elle fournit :
- 20 opérations courantes exécutables ;
- 15 opérations obsolètes encore constructibles, exposées comme dépréciées et soumises à une approbation explicite ;
- 22 versions remplacées conservées en décodage uniquement et redirigées vers leur remplacement canonique final ;
- une frontière explicite avec Bubblegum et avec les metadata incorporées de Token-2022.
- une frontière explicite avec Bubblegum et avec les metadata incorporées de Token-2022 ;
- un sous-module de matérialisation dédié `materializer/metadata/metaplex_token_metadata`, qui conserve les projections détat déjà livrées sans modifier leurs clés ni leur sémantique persistée.
## Architecture des matérialisateurs
Larbre `src/materializer` suit un contrat homogène :
- une façade limitée aux déclarations de sous-modules et réexports ;
- un `constants.rs` propriétaire des identités, familles, surfaces, versions de projection et tracing targets ;
- un `materializer.rs` pour les implémentations de traits ;
- des fichiers `state.rs` ou `account.rs` uniquement lorsque la projection stateful lexige ;
- 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.
Linventaire 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 [laudit de convention des matérialisateurs](../docs/audits/V0_4_8_PRE_007_MATERIALIZER_CONVENTION_AUDIT.md).
## Responsabilités
@@ -57,6 +73,7 @@ Pour Metaplex Token Metadata, elle fournit :
- contrats `ExApi*`, politiques de sécurité et exécuteurs `Ex*Executor` ;
- `ExMetadataMetaplexTokenMetadataExecutor`, `ExMetaplexTokenMetadataExecutionIntent` et `ExMetaplexTokenMetadataOperation` ;
- `DcMetadataSolanaProgramMetadataDecoder`, modèles `DcMetadataSpm*`, constantes `DC_METADATA_SPM_*`, fonctions `decoder_metadata_solana_program_metadata_decode_*_account` et helpers `materializer_metadata_materialize_solana_program_metadata_*_snapshot` ;
- `ExMetadataSolanaProgramMetadataExecutor`, `ExMetadataSpmExecutionIntent`, `ExMetadataSpmOperation`, les helpers de PDA et les constantes `EX_METADATA_SPM_*_OPERATION` ;
- modèles canoniques `Md*` réexportés par la façade.
Les exemples dappel et invariants sont documentés dans [USAGE.md](USAGE.md).
@@ -71,7 +88,7 @@ Les exemples dappel et invariants sont documentés dans [USAGE.md](USAGE.md).
La surface fonctionnelle Metaplex de la crate est achevée. Les campagnes Devnet restantes relèvent de `kb-pipeline-demo-scenarios` et de `kb-app-demo-desktop`, non dun manque de builder dans `kb-lib`.
Pour Solana Program Metadata, les modèles wire, les décodeurs bornés de comptes, le décodeur contextualisé et la matérialisation des deux comptes et des neuf instructions stables sont actifs. `Buffer` accepte tout reliquat comme données allouées sans inventer de PDA stable après changement dautorité ; `Metadata` valide la dérivation canonical ou non-canonical et distingue le contenu logique de la capacité ajoutée par `Extend`. Les instructions produisent des faits committés non autoritatifs ; les snapshots post-exécution restent la source de létat final. Les variantes qui dépendent de létat du compte restent des intentions wire jusquaux lectures stateful prévues, et aucune forme pré-stable nest déclarée compatible réseau sans preuve de cluster.
Pour Solana Program Metadata, les modèles wire, les décodeurs bornés de comptes, le décodeur contextualisé, la matérialisation des deux comptes et des neuf instructions stables, ainsi que lexécuteur typé des neuf opérations sont actifs. `Buffer` accepte tout reliquat comme données allouées sans inventer de PDA stable après changement dautorité ; `Metadata` valide la dérivation canonical ou non-canonical et distingue le contenu logique de la capacité ajoutée par `Extend`. Les instructions produisent des faits committés non autoritatifs ; les snapshots post-exécution restent la source de létat final. Les variantes qui dépendent de létat du compte restent des intentions wire jusquaux lectures stateful prévues, et aucune forme pré-stable nest déclarée compatible réseau sans preuve de cluster.
Le registre ElGamal nest pas déclaré validé réellement sur réseau.
@@ -84,4 +101,5 @@ Le registre ElGamal nest pas déclaré validé réellement sur réseau.
- [Matrice des comptes Solana Program Metadata](../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_ACCOUNT_MATRIX.json)
- [Matrice des instructions Solana Program Metadata](../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_INSTRUCTION_MATRIX.json)
- [Matrice de matérialisation Solana Program Metadata](../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_MATERIALIZATION_MATRIX.json)
- [Matrice dexécution Solana Program Metadata](../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_EXECUTOR_MATRIX.json)
- [Audit de linventaire et de lhistorique des instructions](../docs/audits/V0_4_8_PRE_005_SOLANA_PROGRAM_METADATA_INSTRUCTION_HISTORY_AUDIT.md)

View File

@@ -1,15 +1,17 @@
<!-- file: kb-lib/TODO.md -->
<!-- version: 13 -->
<!-- version: 14 -->
# TODO — kb-lib
## Réaudit ultérieur des exécuteurs déjà livrés
## `0.5.x` — audit transversal décodage, matérialisation, exécution et sécurité
- [ ] Version à déterminer - réauditer les exécuteurs Solana Core et SPL selon la règle distinguant les opérations courantes, remplacées et obsolètes encore constructibles.
- [ ] Solana Core - vérifier chaque génération historique ou obsolète officiellement constructible et ajouter les opérations dépréciées manquantes sans réexposer les versions remplacées intermédiaires.
- [ ] SPL Memo - réexaminer Memo v1 et Memo v3 et documenter leur statut exact de remplacement ou dobsolescence constructible.
- [ ] SPL Token, ATA, Token-2022 et registre ElGamal - appliquer la même classification opération par opération.
- [ ] Matrices - aligner statuts, annotations `#[deprecated]`, approbations opérateur et tests de parité des surfaces réauditées.
- [ ] Inventaire - recenser chaque surface courante, historique, remplacée et officiellement obsolète de tous les décodeurs déjà livrés.
- [ ] Décodage - vérifier que chaque décodeur couvre exhaustivement son contrat officiel, y compris les formes historiques qui restent observables.
- [ ] Matérialisation - vérifier que chaque état ou fait décodé possède exactement un propriétaire de matérialisation, sans omission ni double projection.
- [ ] Exécution - vérifier que chaque opération courante, destructive, irréversible ou écrasante officiellement constructible possède un builder et une capacité dexécution.
- [ ] Sécurité - vérifier que chaque plan exécutable traverse `ExSafetyChecker`, déclare ses signataires, plafonds, confirmations et postconditions, sans supprimer une opération au seul motif quelle est dangereuse.
- [ ] Dépréciation - annoter `#[deprecated]` les opérations officiellement obsolètes mais encore constructibles, conserver leur statut exécutable avec approbation explicite et laisser les versions officiellement remplacées en decode-only.
- [ ] Matrices et documentation - réconcilier statuts, fact ownership, capacités, approbations opérateur, tests de parité, README, USAGE et changelogs pour Solana Core, SPL, Metaplex et les surfaces ajoutées avant `0.5.x`.
## Report conditionnel — registre SPL ElGamal

View File

@@ -1,5 +1,5 @@
<!-- file: kb-lib/USAGE.md -->
<!-- version: 12 -->
<!-- version: 13 -->
# Utilisation de kb-lib
@@ -226,7 +226,8 @@ Les signatures exactes doivent être vérifiées lors de lutilisation : certa
- `ExSplTokenExecutor` ;
- `ExSplAssociatedTokenAccountExecutor` ;
- `ExSplToken2022Executor` ;
- `ExSplElgamalRegistryExecutor`.
- `ExSplElgamalRegistryExecutor` ;
- `ExMetadataSolanaProgramMetadataExecutor`.
```rust
use kb_lib::{ExApiTypedInstructionExecutor, ExSplMemoExecutor};
@@ -235,7 +236,6 @@ let executor = ExSplMemoExecutor;
let plan = ExApiTypedInstructionExecutor::build_prepared_plan(
&executor,
&intent,
&policy,
);
```
@@ -256,6 +256,41 @@ println!("required_signers={}", plan.required_signers.len());
Memo v1 et v3 restent non exécutables. Memo v4 est la seule génération exécutable.
## Exécution Solana Program Metadata
`ExMetadataSolanaProgramMetadataExecutor` couvre les neuf opérations stables `Write`, `Initialize`, `SetAuthority`, `SetData`, `SetImmutable`, `Trim`, `Close`, `Allocate` et `Extend`. Les helpers publics dérivent les PDA canonical et non-canonical ; lappelant doit toujours fournir et vérifier le compte attendu avant construction du plan.
Les mutations écrasantes, destructrices ou irréversibles ne sont pas retirées de lAPI. Elles exigent `allow_destructive_operation = true`, puis le plan complet est évalué par `ExSafetyChecker`. Cette approbation locale ne remplace ni la simulation RPC, ni la confirmation opérateur, ni les postconditions stateful.
```rust
let intent = kb_lib::ExMetadataSpmExecutionIntent {
intent_id: "extend-program-metadata".to_string(),
fee_payer: authority.clone(),
policy,
allow_destructive_operation: false,
operation: kb_lib::ExMetadataSpmOperation::Extend {
account: metadata_account,
authority,
length: 256,
program_context: std::option::Option::None,
},
};
let executor = kb_lib::ExMetadataSolanaProgramMetadataExecutor;
let plan = match kb_lib::ExApiTypedInstructionExecutor::build_prepared_plan(
&executor,
&intent,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
```
`Initialize` et `Allocate` ninventent pas le financement préalable nécessaire à lexemption de rent. La préparation de fixture ou lorchestration stateful doit calculer le financement depuis le RPC, préfinancer le PDA ou le compte keypair, puis simuler le message exact avant soumission.
Les contenus inline sont volontairement bornés à 1 024 octets par le workspace. Les contenus plus grands passent par un compte `Buffer` préfinancé, écrit par segments puis consommé par `Initialize` ou `SetData`.
Pour `Initialize` et les allocations de Buffer PDA, le booléen `canonical` est fourni explicitement. La présence de `program_data` ne suffit pas à déterminer la dérivation, car les programmes issus de loaders antérieurs peuvent être canoniques sans compte ProgramData.
## Exécution Metaplex Token Metadata
La surface publique repose sur :

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/metadata/metaplex_token_metadata/decoder.rs
// version: 31
// version: 32
//! Exact contextual dispatch for Metaplex Token Metadata.
@@ -144,9 +144,6 @@ impl crate::DcApiInstructionDecoder for crate::DcMetadataMetaplexTokenMetadataDe
mod tests {
use base64::Engine; // rust-rules: trait-import
const AUDIT_MATRIX_JSON: &str = include_str!(
"../../../../../test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_MATRIX.json"
);
const MAX_SDK_INSTRUCTION_MODULES: usize = 128;
const MIN_SDK_INSTRUCTION_MODULES: usize = 90;
const MIN_PDA_FAMILIES: usize = 8;
@@ -2023,7 +2020,9 @@ mod tests {
#[test]
fn audit_matrix_remains_machine_readable_and_bounded() {
let parsed = serde_json::from_str::<serde_json::Value>(AUDIT_MATRIX_JSON);
let parsed = serde_json::from_str::<serde_json::Value>(include_str!(
"../../../../../test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_MATRIX.json"
));
assert!(parsed.is_ok());
let matrix = match parsed {
std::result::Result::Ok(value) => value,
@@ -3053,7 +3052,9 @@ mod tests {
#[test]
fn pre_002_contract_closes_all_instruction_statuses_and_idl_accounts() {
let matrix: serde_json::Value = match serde_json::from_str(AUDIT_MATRIX_JSON) {
let matrix: serde_json::Value = match serde_json::from_str(include_str!(
"../../../../../test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_MATRIX.json"
)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("matrix parse failed: {error}"),
};
@@ -3093,7 +3094,9 @@ mod tests {
#[test]
fn pre_002_fact_ownership_is_unique_and_keeps_metadata_domains_separate() {
let matrix: serde_json::Value = match serde_json::from_str(AUDIT_MATRIX_JSON) {
let matrix: serde_json::Value = match serde_json::from_str(include_str!(
"../../../../../test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_MATRIX.json"
)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("matrix parse failed: {error}"),
};

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/metadata/solana_program_metadata/account.rs
// version: 2
// version: 3
//! Bounded account decoding for the Solana Program Metadata program.
@@ -61,66 +61,61 @@ pub enum DcMetadataSpmAccountDecodeError {
impl std::fmt::Display for DcMetadataSpmAccountDecodeError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
return match self {
Self::InvalidAccountKey => {
return formatter.write_str("program metadata account key is invalid");
},
Self::InvalidOwner => {
return formatter.write_str("program metadata account owner is invalid");
formatter.write_str("program metadata account key is invalid")
},
Self::InvalidOwner => formatter.write_str("program metadata account owner is invalid"),
Self::ForeignOwner => {
return formatter.write_str("program metadata account is owned by another program");
},
Self::EmptyAccountData => {
return formatter.write_str("program metadata account data is empty");
formatter.write_str("program metadata account is owned by another program")
},
Self::EmptyAccountData => formatter.write_str("program metadata account data is empty"),
Self::UninitializedAccount => {
return formatter.write_str("program metadata account is uninitialized");
formatter.write_str("program metadata account is uninitialized")
},
Self::AccountDataTooLarge { actual_bytes, max_bytes } => {
return write!(
write!(
formatter,
"program metadata account data length {actual_bytes} exceeds {max_bytes} bytes",
);
)
},
Self::TruncatedHeader { actual_bytes, minimum_bytes } => {
return write!(
write!(
formatter,
"program metadata account header length {actual_bytes} is below {minimum_bytes} bytes",
);
)
},
Self::UnexpectedAccountDiscriminator { expected, actual } => {
return write!(
write!(
formatter,
"program metadata account discriminator {actual:?} does not match {expected:?}",
);
)
},
Self::LogicalDataOutOfBounds { declared_bytes, available_bytes } => {
return write!(
write!(
formatter,
"program metadata logical data length {declared_bytes} exceeds {available_bytes} allocated bytes",
);
)
},
Self::LogicalDataLengthUnsupported { declared_bytes } => {
return write!(
write!(
formatter,
"program metadata logical data length {declared_bytes} cannot be represented on this architecture",
);
)
},
Self::MissingNonCanonicalAuthority => {
return formatter
.write_str("non-canonical program metadata account has no authority");
formatter.write_str("non-canonical program metadata account has no authority")
},
Self::InvalidMetadataPda => {
return formatter.write_str("program metadata account PDA is invalid");
formatter.write_str("program metadata account PDA is invalid")
},
Self::InvalidUrlUtf8 => {
return formatter.write_str("program metadata URL data is not valid UTF-8");
formatter.write_str("program metadata URL data is not valid UTF-8")
},
Self::Model(error) => {
return write!(formatter, "program metadata model conversion failed: {error}");
write!(formatter, "program metadata model conversion failed: {error}")
},
}
};
}
}
@@ -207,7 +202,7 @@ pub fn decoder_metadata_solana_program_metadata_decode_account(
return std::result::Result::Err(crate::DcMetadataSpmAccountDecodeError::Model(error));
},
};
match discriminator {
return match discriminator {
crate::DcMetadataSpmAccountDiscriminator::Buffer => {
let snapshot =
match crate::decoder_metadata_solana_program_metadata_decode_buffer_account(
@@ -216,7 +211,7 @@ pub fn decoder_metadata_solana_program_metadata_decode_account(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(crate::DcMetadataSpmAccountSnapshot::Buffer(snapshot));
std::result::Result::Ok(crate::DcMetadataSpmAccountSnapshot::Buffer(snapshot))
},
crate::DcMetadataSpmAccountDiscriminator::Metadata => {
let snapshot =
@@ -226,16 +221,12 @@ pub fn decoder_metadata_solana_program_metadata_decode_account(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(crate::DcMetadataSpmAccountSnapshot::Metadata(
snapshot,
));
std::result::Result::Ok(crate::DcMetadataSpmAccountSnapshot::Metadata(snapshot))
},
crate::DcMetadataSpmAccountDiscriminator::Empty => {
return std::result::Result::Err(
crate::DcMetadataSpmAccountDecodeError::UninitializedAccount,
);
std::result::Result::Err(crate::DcMetadataSpmAccountDecodeError::UninitializedAccount)
},
}
};
}
/// Decodes one Solana Program Metadata Buffer account without claiming a stable PDA after authority changes.
@@ -530,12 +521,12 @@ fn decoder_metadata_solana_program_metadata_decode_typed_data(
crate::DcMetadataSpmData::External(external)
},
};
match model.validate() {
std::result::Result::Ok(()) => return std::result::Result::Ok(model),
return match model.validate() {
std::result::Result::Ok(()) => std::result::Result::Ok(model),
std::result::Result::Err(error) => {
return std::result::Result::Err(crate::DcMetadataSpmAccountDecodeError::Model(error));
std::result::Result::Err(crate::DcMetadataSpmAccountDecodeError::Model(error))
},
}
};
}
fn decoder_metadata_solana_program_metadata_validate_metadata_pda(
@@ -576,12 +567,12 @@ fn decoder_metadata_solana_program_metadata_validate_metadata_pda(
#[cfg(test)]
mod tests {
fn metadata_program_id() -> std::option::Option<solana_pubkey::Pubkey> {
match <solana_pubkey::Pubkey as std::str::FromStr>::from_str(
return match <solana_pubkey::Pubkey as std::str::FromStr>::from_str(
kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID,
) {
std::result::Result::Ok(value) => return std::option::Option::Some(value),
std::result::Result::Err(_) => return std::option::Option::None,
}
std::result::Result::Ok(value) => std::option::Option::Some(value),
std::result::Result::Err(_) => std::option::Option::None,
};
}
#[derive(Clone, Copy)]

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/metadata/solana_program_metadata/instruction.rs
// version: 3
// version: 4
//! Exact contextual instruction decoding for Solana Program Metadata.
@@ -890,10 +890,6 @@ fn failed(
mod tests {
use base64::Engine; // rust-rules: trait-import
const MATRIX_JSON: &str = include_str!(
"../../../../../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_INSTRUCTION_MATRIX.json"
);
fn input(
payload: &[u8],
accounts: &[(&str, bool, bool)],
@@ -1262,7 +1258,9 @@ mod tests {
#[test]
fn instruction_matrix_is_machine_readable_and_matches_compiled_inventory() {
let matrix: serde_json::Value = match serde_json::from_str(MATRIX_JSON) {
let matrix: serde_json::Value = match serde_json::from_str(include_str!(
"../../../../../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_INSTRUCTION_MATRIX.json"
)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("instruction matrix must parse: {error}"),
};

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/metadata/solana_program_metadata/model.rs
// version: 2
// version: 3
//! Public bounded models for the Solana Program Metadata wire contract.
@@ -24,16 +24,14 @@ impl DcMetadataSpmAccountDiscriminator {
/// Resolves an exact wire discriminant.
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
match value {
0 => return std::result::Result::Ok(Self::Empty),
1 => return std::result::Result::Ok(Self::Buffer),
2 => return std::result::Result::Ok(Self::Metadata),
_ => {
return std::result::Result::Err(
crate::DcMetadataSpmModelError::UnknownAccountDiscriminator { value },
);
},
}
return match value {
0 => std::result::Result::Ok(Self::Empty),
1 => std::result::Result::Ok(Self::Buffer),
2 => std::result::Result::Ok(Self::Metadata),
_ => std::result::Result::Err(
crate::DcMetadataSpmModelError::UnknownAccountDiscriminator { value },
),
};
}
}
@@ -60,17 +58,15 @@ impl DcMetadataSpmEncoding {
/// Resolves an exact wire discriminant.
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
match value {
0 => return std::result::Result::Ok(Self::None),
1 => return std::result::Result::Ok(Self::Utf8),
2 => return std::result::Result::Ok(Self::Base58),
3 => return std::result::Result::Ok(Self::Base64),
return match value {
0 => std::result::Result::Ok(Self::None),
1 => std::result::Result::Ok(Self::Utf8),
2 => std::result::Result::Ok(Self::Base58),
3 => std::result::Result::Ok(Self::Base64),
_ => {
return std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownEncoding {
value,
});
std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownEncoding { value })
},
}
};
}
}
@@ -95,16 +91,14 @@ impl DcMetadataSpmCompression {
/// Resolves an exact wire discriminant.
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
match value {
0 => return std::result::Result::Ok(Self::None),
1 => return std::result::Result::Ok(Self::Gzip),
2 => return std::result::Result::Ok(Self::Zlib),
_ => {
return std::result::Result::Err(
crate::DcMetadataSpmModelError::UnknownCompression { value },
);
},
}
return match value {
0 => std::result::Result::Ok(Self::None),
1 => std::result::Result::Ok(Self::Gzip),
2 => std::result::Result::Ok(Self::Zlib),
_ => std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownCompression {
value,
}),
};
}
}
@@ -131,17 +125,13 @@ impl DcMetadataSpmFormat {
/// Resolves an exact wire discriminant.
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
match value {
0 => return std::result::Result::Ok(Self::None),
1 => return std::result::Result::Ok(Self::Json),
2 => return std::result::Result::Ok(Self::Yaml),
3 => return std::result::Result::Ok(Self::Toml),
_ => {
return std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownFormat {
value,
});
},
}
return match value {
0 => std::result::Result::Ok(Self::None),
1 => std::result::Result::Ok(Self::Json),
2 => std::result::Result::Ok(Self::Yaml),
3 => std::result::Result::Ok(Self::Toml),
_ => std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownFormat { value }),
};
}
}
@@ -166,16 +156,14 @@ impl DcMetadataSpmDataSource {
/// Resolves an exact wire discriminant.
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
match value {
0 => return std::result::Result::Ok(Self::Direct),
1 => return std::result::Result::Ok(Self::Url),
2 => return std::result::Result::Ok(Self::External),
_ => {
return std::result::Result::Err(
crate::DcMetadataSpmModelError::UnknownDataSource { value },
);
},
}
return match value {
0 => std::result::Result::Ok(Self::Direct),
1 => std::result::Result::Ok(Self::Url),
2 => std::result::Result::Ok(Self::External),
_ => std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownDataSource {
value,
}),
};
}
}
@@ -204,18 +192,16 @@ impl DcMetadataSpmProgramError {
/// Resolves an exact custom program error code.
pub fn from_code(value: u32) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
match value {
0 => return std::result::Result::Ok(Self::NotExecutableAccount),
1 => return std::result::Result::Ok(Self::InvalidProgramState),
2 => return std::result::Result::Ok(Self::InvalidProgramDataAccount),
3 => return std::result::Result::Ok(Self::ImmutableMetadataAccount),
4 => return std::result::Result::Ok(Self::InvalidDataLength),
_ => {
return std::result::Result::Err(
crate::DcMetadataSpmModelError::UnknownProgramError { value },
);
},
}
return match value {
0 => std::result::Result::Ok(Self::NotExecutableAccount),
1 => std::result::Result::Ok(Self::InvalidProgramState),
2 => std::result::Result::Ok(Self::InvalidProgramDataAccount),
3 => std::result::Result::Ok(Self::ImmutableMetadataAccount),
4 => std::result::Result::Ok(Self::InvalidDataLength),
_ => std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownProgramError {
value,
}),
};
}
}
@@ -272,12 +258,12 @@ impl DcMetadataSpmSeed {
while length > 0 && self.0[length - 1] == 0 {
length -= 1;
}
match std::str::from_utf8(&self.0[..length]) {
std::result::Result::Ok(value) => return std::result::Result::Ok(value),
return match std::str::from_utf8(&self.0[..length]) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(_) => {
return std::result::Result::Err(crate::DcMetadataSpmModelError::InvalidSeedUtf8);
std::result::Result::Err(crate::DcMetadataSpmModelError::InvalidSeedUtf8)
},
}
};
}
}
@@ -361,11 +347,11 @@ pub enum DcMetadataSpmData {
impl DcMetadataSpmData {
/// Returns the source discriminator associated with this content.
pub const fn source(&self) -> crate::DcMetadataSpmDataSource {
match self {
Self::Direct(_) => return crate::DcMetadataSpmDataSource::Direct,
Self::Url(_) => return crate::DcMetadataSpmDataSource::Url,
Self::External(_) => return crate::DcMetadataSpmDataSource::External,
}
return match self {
Self::Direct(_) => crate::DcMetadataSpmDataSource::Direct,
Self::Url(_) => crate::DcMetadataSpmDataSource::Url,
Self::External(_) => crate::DcMetadataSpmDataSource::External,
};
}
/// Validates source-specific length requirements and the `u32` header bound.
@@ -389,11 +375,11 @@ impl DcMetadataSpmData {
/// Returns the exact number of bytes represented on wire.
pub fn wire_byte_len(&self) -> usize {
match self {
Self::Direct(value) => return value.len(),
Self::Url(value) => return value.len(),
Self::External(_) => return crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES,
}
return match self {
Self::Direct(value) => value.len(),
Self::Url(value) => value.len(),
Self::External(_) => crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES,
};
}
/// Returns the exact little-endian header length after validation.
@@ -403,14 +389,14 @@ impl DcMetadataSpmData {
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
let actual_bytes = self.wire_byte_len();
match u32::try_from(actual_bytes) {
std::result::Result::Ok(value) => return std::result::Result::Ok(value),
return match u32::try_from(actual_bytes) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(_) => {
return std::result::Result::Err(
crate::DcMetadataSpmModelError::DataLengthExceedsU32 { actual_bytes },
);
std::result::Result::Err(crate::DcMetadataSpmModelError::DataLengthExceedsU32 {
actual_bytes,
})
},
}
};
}
/// Serializes the exact content bytes without fetching URL or external-account data.
@@ -421,15 +407,11 @@ impl DcMetadataSpmData {
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
match self {
Self::Direct(value) => return std::result::Result::Ok(value.clone()),
Self::Url(value) => {
return std::result::Result::Ok(value.as_bytes().to_vec());
},
Self::External(value) => {
return std::result::Result::Ok(value.to_wire_bytes().to_vec());
},
}
return match self {
Self::Direct(value) => std::result::Result::Ok(value.clone()),
Self::Url(value) => std::result::Result::Ok(value.as_bytes().to_vec()),
Self::External(value) => std::result::Result::Ok(value.to_wire_bytes().to_vec()),
};
}
}
@@ -503,56 +485,50 @@ pub enum DcMetadataSpmModelError {
impl std::fmt::Display for DcMetadataSpmModelError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
return match self {
Self::UnknownAccountDiscriminator { value } => {
return write!(formatter, "unknown account discriminator {value}");
write!(formatter, "unknown account discriminator {value}")
},
Self::UnknownEncoding { value } => {
return write!(formatter, "unknown encoding discriminator {value}");
write!(formatter, "unknown encoding discriminator {value}")
},
Self::UnknownCompression { value } => {
return write!(formatter, "unknown compression discriminator {value}");
write!(formatter, "unknown compression discriminator {value}")
},
Self::UnknownFormat { value } => {
return write!(formatter, "unknown format discriminator {value}");
write!(formatter, "unknown format discriminator {value}")
},
Self::UnknownDataSource { value } => {
return write!(formatter, "unknown data-source discriminator {value}");
write!(formatter, "unknown data-source discriminator {value}")
},
Self::UnknownProgramError { value } => {
return write!(formatter, "unknown program metadata error code {value}");
write!(formatter, "unknown program metadata error code {value}")
},
Self::SeedUtf8TooLong { actual_bytes, max_bytes } => {
return write!(
formatter,
"seed UTF-8 length {actual_bytes} exceeds {max_bytes} bytes",
);
write!(formatter, "seed UTF-8 length {actual_bytes} exceeds {max_bytes} bytes",)
},
Self::InvalidSeedLength { actual_bytes, expected_bytes } => {
return write!(
write!(
formatter,
"seed length {actual_bytes} does not equal {expected_bytes} bytes",
);
)
},
Self::InvalidSeedUtf8 => {
return formatter.write_str("seed contains invalid UTF-8 before terminal padding");
formatter.write_str("seed contains invalid UTF-8 before terminal padding")
},
Self::InvalidExternalDataLength { actual_bytes, expected_bytes } => {
return write!(
write!(
formatter,
"external data length {actual_bytes} does not equal {expected_bytes} bytes",
);
)
},
Self::EmptyData { source } => {
return write!(formatter, "metadata data source {source:?} cannot be empty");
write!(formatter, "metadata data source {source:?} cannot be empty")
},
Self::DataLengthExceedsU32 { actual_bytes } => {
return write!(
formatter,
"metadata data length {actual_bytes} exceeds the u32 wire bound",
);
write!(formatter, "metadata data length {actual_bytes} exceeds the u32 wire bound",)
},
}
};
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/executor.rs
// version: 14
// version: 16
//! Consolidated executor modules.
@@ -208,6 +208,30 @@ pub use self::lending::ExLendingKaminoExecutor;
pub use self::lending::ExLendingMarginfiV2Executor;
/// Exposes the reserved `lock/raydium_lp` executor.
pub use self::lock::ExLockRaydiumLpExecutor;
/// Stable Solana Program Metadata `Allocate` operation code.
pub use self::metadata::EX_METADATA_SPM_ALLOCATE_OPERATION;
/// Stable Solana Program Metadata `Close` operation code.
pub use self::metadata::EX_METADATA_SPM_CLOSE_OPERATION;
/// Solana Program Metadata operations requiring explicit destructive approval.
pub use self::metadata::EX_METADATA_SPM_DESTRUCTIVE_OPERATION_CODES;
/// Stable Solana Program Metadata `Extend` operation code.
pub use self::metadata::EX_METADATA_SPM_EXTEND_OPERATION;
/// Stable Solana Program Metadata `Initialize` operation code.
pub use self::metadata::EX_METADATA_SPM_INITIALIZE_OPERATION;
/// Conservative maximum inline Solana Program Metadata content size.
pub use self::metadata::EX_METADATA_SPM_MAX_INLINE_DATA_BYTES;
/// Stable Solana Program Metadata `SetAuthority` operation code.
pub use self::metadata::EX_METADATA_SPM_SET_AUTHORITY_OPERATION;
/// Stable Solana Program Metadata `SetData` operation code.
pub use self::metadata::EX_METADATA_SPM_SET_DATA_OPERATION;
/// Stable Solana Program Metadata `SetImmutable` operation code.
pub use self::metadata::EX_METADATA_SPM_SET_IMMUTABLE_OPERATION;
/// Exact Solana Program Metadata operation inventory.
pub use self::metadata::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES;
/// Stable Solana Program Metadata `Trim` operation code.
pub use self::metadata::EX_METADATA_SPM_TRIM_OPERATION;
/// Stable Solana Program Metadata `Write` operation code.
pub use self::metadata::EX_METADATA_SPM_WRITE_OPERATION;
/// Exposes `EX_METAPLEX_TOKEN_METADATA_APPROVE_USE_AUTHORITY_OPERATION`.
pub use self::metadata::EX_METAPLEX_TOKEN_METADATA_APPROVE_USE_AUTHORITY_OPERATION;
/// Exposes `EX_METAPLEX_TOKEN_METADATA_BURN_OPERATION`.
@@ -284,14 +308,38 @@ pub use self::metadata::EX_METAPLEX_TOKEN_METADATA_UTILIZE_OPERATION;
pub use self::metadata::EX_METAPLEX_TOKEN_METADATA_VERIFY_OPERATION;
/// Exposes the Metaplex Token Metadata executor.
pub use self::metadata::ExMetadataMetaplexTokenMetadataExecutor;
/// Reserved `kb-lib.executor.metadata.solana_program_metadata` executor.
/// Typed Solana Program Metadata executor.
pub use self::metadata::ExMetadataSolanaProgramMetadataExecutor;
/// Exposes the reserved `metadata/spl_name_service` executor.
pub use self::metadata::ExMetadataSplNameServiceExecutor;
/// Solana Program Metadata compression intent.
pub use self::metadata::ExMetadataSpmCompression;
/// Solana Program Metadata inline data intent.
pub use self::metadata::ExMetadataSpmDataInput;
/// Solana Program Metadata data-source intent.
pub use self::metadata::ExMetadataSpmDataSource;
/// Solana Program Metadata encoding intent.
pub use self::metadata::ExMetadataSpmEncoding;
/// Complete Solana Program Metadata execution intent.
pub use self::metadata::ExMetadataSpmExecutionIntent;
/// Solana Program Metadata format intent.
pub use self::metadata::ExMetadataSpmFormat;
/// One Solana Program Metadata operation.
pub use self::metadata::ExMetadataSpmOperation;
/// Optional Solana Program Metadata program context.
pub use self::metadata::ExMetadataSpmProgramContext;
/// Solana Program Metadata `SetData` source intent.
pub use self::metadata::ExMetadataSpmSetDataSource;
/// Solana Program Metadata `Write` source intent.
pub use self::metadata::ExMetadataSpmWriteSource;
/// Exposes the complete Metaplex Token Metadata execution intent.
pub use self::metadata::ExMetaplexTokenMetadataExecutionIntent;
/// Exposes one current Metaplex Token Metadata operation.
pub use self::metadata::ExMetaplexTokenMetadataOperation;
/// Derives the canonical Solana Program Metadata PDA.
pub use self::metadata::executor_metadata_solana_program_metadata_derive_canonical_pda;
/// Derives the non-canonical Solana Program Metadata PDA.
pub use self::metadata::executor_metadata_solana_program_metadata_derive_non_canonical_pda;
/// Exposes the reserved `nft/metaplex_bubblegum` executor.
pub use self::nft::ExNftMetaplexBubblegumExecutor;
/// Reserved `kb-lib.executor.nft.tensor_cnft` executor.
@@ -993,6 +1041,10 @@ pub use self::wallet::ExWalletJupiterApeproSmartWalletExecutor;
/// Exposes the reserved `weighted/swap_stabble` executor.
pub use self::weighted::ExWeightedSwapStabbleExecutor;
/// Stable Solana Program Metadata executor name.
pub(crate) use self::metadata::EX_METADATA_SPM_EXECUTOR_NAME;
/// Internal Solana Program Metadata prepared-plan builder.
pub(crate) use self::metadata::executor_metadata_solana_program_metadata_build_prepared_plan;
/// Internal Metaplex Token Metadata prepared-plan builder.
pub(crate) use self::metadata::executor_metaplex_token_metadata_build_prepared_plan;
/// Stable operation code for the nonce advance injected by durable transaction assembly.
@@ -1078,11 +1130,10 @@ pub(crate) use self::spl::executor_spl_token_build_prepared_plan;
mod reserved_executor_tests {
#[test]
fn every_reserved_executor_is_registered_and_builds_only_an_empty_plan() {
let executors: [&dyn crate::ExApiInstructionExecutor; 104] = [
let executors: [&dyn crate::ExApiInstructionExecutor; 103] = [
&crate::ExAdapterSaberDecimalWrapperExecutor,
&crate::ExAdapterSplTokenWrapExecutor,
&crate::ExGovernanceSquadsMultisigExecutor,
&crate::ExMetadataSolanaProgramMetadataExecutor,
&crate::ExNftTensorCnftExecutor,
&crate::ExPerpetualsPhoenixEternalExecutor,
&crate::ExStakingJitoTipDistributionExecutor,
@@ -1184,7 +1235,7 @@ mod reserved_executor_tests {
&crate::ExWalletJupiterApeproSmartWalletExecutor,
&crate::ExWeightedSwapStabbleExecutor,
];
assert_eq!(executors.len(), 104);
assert_eq!(executors.len(), 103);
for executor in executors {
assert!(!executor.program_ids().is_empty());
for program_id in executor.program_ids() {

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/executor/metadata.rs
// version: 8
// version: 9
//! `metadata` executor family.
@@ -87,9 +87,61 @@ pub use self::metaplex_token_metadata::ExMetadataMetaplexTokenMetadataExecutor;
pub use self::metaplex_token_metadata::ExMetaplexTokenMetadataExecutionIntent;
/// Exposes the Metaplex Token Metadata operation intent.
pub use self::metaplex_token_metadata::ExMetaplexTokenMetadataOperation;
/// Exposes the reserved `metadata/solana_program_metadata` executor.
/// Stable Solana Program Metadata `Allocate` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_ALLOCATE_OPERATION;
/// Stable Solana Program Metadata `Close` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_CLOSE_OPERATION;
/// Solana Program Metadata operations requiring explicit destructive approval.
pub use self::solana_program_metadata::EX_METADATA_SPM_DESTRUCTIVE_OPERATION_CODES;
/// Stable Solana Program Metadata `Extend` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_EXTEND_OPERATION;
/// Stable Solana Program Metadata `Initialize` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_INITIALIZE_OPERATION;
/// Conservative maximum inline Solana Program Metadata content size.
pub use self::solana_program_metadata::EX_METADATA_SPM_MAX_INLINE_DATA_BYTES;
/// Stable Solana Program Metadata `SetAuthority` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_SET_AUTHORITY_OPERATION;
/// Stable Solana Program Metadata `SetData` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_SET_DATA_OPERATION;
/// Stable Solana Program Metadata `SetImmutable` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_SET_IMMUTABLE_OPERATION;
/// Exact Solana Program Metadata operation inventory.
pub use self::solana_program_metadata::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES;
/// Stable Solana Program Metadata `Trim` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_TRIM_OPERATION;
/// Stable Solana Program Metadata `Write` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_WRITE_OPERATION;
/// Typed Solana Program Metadata executor.
pub use self::solana_program_metadata::ExMetadataSolanaProgramMetadataExecutor;
/// Solana Program Metadata compression intent.
pub use self::solana_program_metadata::ExMetadataSpmCompression;
/// Solana Program Metadata inline data intent.
pub use self::solana_program_metadata::ExMetadataSpmDataInput;
/// Solana Program Metadata data-source intent.
pub use self::solana_program_metadata::ExMetadataSpmDataSource;
/// Solana Program Metadata encoding intent.
pub use self::solana_program_metadata::ExMetadataSpmEncoding;
/// Complete Solana Program Metadata execution intent.
pub use self::solana_program_metadata::ExMetadataSpmExecutionIntent;
/// Solana Program Metadata format intent.
pub use self::solana_program_metadata::ExMetadataSpmFormat;
/// One Solana Program Metadata operation.
pub use self::solana_program_metadata::ExMetadataSpmOperation;
/// Optional Solana Program Metadata program context.
pub use self::solana_program_metadata::ExMetadataSpmProgramContext;
/// Solana Program Metadata `SetData` source intent.
pub use self::solana_program_metadata::ExMetadataSpmSetDataSource;
/// Solana Program Metadata `Write` source intent.
pub use self::solana_program_metadata::ExMetadataSpmWriteSource;
/// Derives the canonical Solana Program Metadata PDA.
pub use self::solana_program_metadata::executor_metadata_solana_program_metadata_derive_canonical_pda;
/// Derives the non-canonical Solana Program Metadata PDA.
pub use self::solana_program_metadata::executor_metadata_solana_program_metadata_derive_non_canonical_pda;
/// Exposes the reserved `metadata/spl_name_service` executor.
pub use self::spl_name_service::ExMetadataSplNameServiceExecutor;
pub(crate) use self::metaplex_token_metadata::executor_metaplex_token_metadata_build_prepared_plan;
/// Stable Solana Program Metadata executor name.
pub(crate) use self::solana_program_metadata::EX_METADATA_SPM_EXECUTOR_NAME;
/// Internal Solana Program Metadata prepared-plan builder.
pub(crate) use self::solana_program_metadata::executor_metadata_solana_program_metadata_build_prepared_plan;

View File

@@ -1,49 +1,65 @@
// file: kb-lib/src/executor/metadata/solana_program_metadata.rs
// version: 1
// version: 2
//! Reserved executor for `metadata_solana_program_metadata`.
//! Typed executor for Solana Program Metadata.
/// Reserved executor for the `metadata_solana_program_metadata` program surface.
#[derive(Clone, Debug, Default)]
pub struct ExMetadataSolanaProgramMetadataExecutor;
mod builder;
mod constants;
mod executor;
mod intent;
impl crate::ExApiInstructionExecutor for crate::ExMetadataSolanaProgramMetadataExecutor {
fn executor_name(&self) -> &'static str {
return "kb-lib.executor.metadata.solana_program_metadata";
}
/// Derives the canonical Metadata PDA for one program and seed.
pub use self::builder::executor_metadata_solana_program_metadata_derive_canonical_pda;
/// Derives the non-canonical Metadata PDA for one program, authority and seed.
pub use self::builder::executor_metadata_solana_program_metadata_derive_non_canonical_pda;
/// Stable operation code for `Allocate`.
pub use self::constants::EX_METADATA_SPM_ALLOCATE_OPERATION;
/// Stable operation code for `Close`.
pub use self::constants::EX_METADATA_SPM_CLOSE_OPERATION;
/// Operations requiring explicit destructive-operation approval.
pub use self::constants::EX_METADATA_SPM_DESTRUCTIVE_OPERATION_CODES;
/// Stable operation code for `Extend`.
pub use self::constants::EX_METADATA_SPM_EXTEND_OPERATION;
/// Stable operation code for `Initialize`.
pub use self::constants::EX_METADATA_SPM_INITIALIZE_OPERATION;
/// Conservative maximum inline content accepted by Solana Program Metadata builders.
pub use self::constants::EX_METADATA_SPM_MAX_INLINE_DATA_BYTES;
/// Stable operation code for `SetAuthority`.
pub use self::constants::EX_METADATA_SPM_SET_AUTHORITY_OPERATION;
/// Stable operation code for `SetData`.
pub use self::constants::EX_METADATA_SPM_SET_DATA_OPERATION;
/// Stable operation code for `SetImmutable`.
pub use self::constants::EX_METADATA_SPM_SET_IMMUTABLE_OPERATION;
/// Exact current operation inventory implemented by the executor.
pub use self::constants::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES;
/// Stable operation code for `Trim`.
pub use self::constants::EX_METADATA_SPM_TRIM_OPERATION;
/// Stable operation code for `Write`.
pub use self::constants::EX_METADATA_SPM_WRITE_OPERATION;
/// Typed Solana Program Metadata executor.
pub use self::executor::ExMetadataSolanaProgramMetadataExecutor;
/// Compression written by execution intents.
pub use self::intent::ExMetadataSpmCompression;
/// Exact inline content accepted by metadata instructions.
pub use self::intent::ExMetadataSpmDataInput;
/// On-chain data-source discriminator.
pub use self::intent::ExMetadataSpmDataSource;
/// Encoding written by execution intents.
pub use self::intent::ExMetadataSpmEncoding;
/// Complete Solana Program Metadata execution intent.
pub use self::intent::ExMetadataSpmExecutionIntent;
/// Structured content format written by execution intents.
pub use self::intent::ExMetadataSpmFormat;
/// One exact Solana Program Metadata operation.
pub use self::intent::ExMetadataSpmOperation;
/// Optional program-upgrade-authority validation context.
pub use self::intent::ExMetadataSpmProgramContext;
/// Source used by `SetData`.
pub use self::intent::ExMetadataSpmSetDataSource;
/// Source used by `Write`.
pub use self::intent::ExMetadataSpmWriteSource;
fn executor_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn program_ids(&self) -> &'static [&'static str] {
return &[kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID];
}
fn supports_request(
&self,
request: &crate::ExApiExecutionRequest,
) -> crate::ExApiExecutionSupport {
if crate::ExApiInstructionExecutor::handles_program_id(self, &request.program_id) {
return crate::ExApiExecutionSupport::Maybe;
}
return crate::ExApiExecutionSupport::No;
}
fn build_plan(
&self,
_request: &crate::ExApiExecutionRequest,
) -> kb_core::Result<crate::ExApiExecutionPlan> {
let payload_json = match crate::executor_api_serialize_payload_json(
&serde_json::json!({"status":"reserved_executor","surface":"metadata_solana_program_metadata"}),
) {
std::result::Result::Ok(serialized) => serialized,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(crate::ExApiExecutionPlan {
executor_name: "kb-lib.executor.metadata.solana_program_metadata".to_string(),
instruction_count: 0,
payload_json,
});
}
}
/// Crate-root access to the exact prepared-plan builder.
pub(crate) use self::builder::executor_metadata_solana_program_metadata_build_prepared_plan;
/// Stable executor name.
pub(crate) use self::constants::EX_METADATA_SPM_EXECUTOR_NAME;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,55 @@
// file: kb-lib/src/executor/metadata/solana_program_metadata/constants.rs
// version: 1
//! Stable Solana Program Metadata executor constants.
/// Stable executor name.
pub(crate) const EX_METADATA_SPM_EXECUTOR_NAME: &str =
"kb-lib.executor.metadata.solana_program_metadata";
/// Conservative maximum inline content accepted by one instruction builder.
///
/// Larger payloads must use a pre-funded Buffer account so the final transaction remains within
/// the Solana packet boundary after signatures, account keys and message framing are included.
pub const EX_METADATA_SPM_MAX_INLINE_DATA_BYTES: usize = 1_024;
/// Stable operation code for `Write`.
pub const EX_METADATA_SPM_WRITE_OPERATION: &str = "metadata.solana_program_metadata.write";
/// Stable operation code for `Initialize`.
pub const EX_METADATA_SPM_INITIALIZE_OPERATION: &str =
"metadata.solana_program_metadata.initialize";
/// Stable operation code for `SetAuthority`.
pub const EX_METADATA_SPM_SET_AUTHORITY_OPERATION: &str =
"metadata.solana_program_metadata.set_authority";
/// Stable operation code for `SetData`.
pub const EX_METADATA_SPM_SET_DATA_OPERATION: &str = "metadata.solana_program_metadata.set_data";
/// Stable operation code for `SetImmutable`.
pub const EX_METADATA_SPM_SET_IMMUTABLE_OPERATION: &str =
"metadata.solana_program_metadata.set_immutable";
/// Stable operation code for `Trim`.
pub const EX_METADATA_SPM_TRIM_OPERATION: &str = "metadata.solana_program_metadata.trim";
/// Stable operation code for `Close`.
pub const EX_METADATA_SPM_CLOSE_OPERATION: &str = "metadata.solana_program_metadata.close";
/// Stable operation code for `Allocate`.
pub const EX_METADATA_SPM_ALLOCATE_OPERATION: &str = "metadata.solana_program_metadata.allocate";
/// Stable operation code for `Extend`.
pub const EX_METADATA_SPM_EXTEND_OPERATION: &str = "metadata.solana_program_metadata.extend";
/// Exact current operation inventory implemented by the executor.
pub const EX_METADATA_SPM_SUPPORTED_OPERATION_CODES: &[&str] = &[
crate::EX_METADATA_SPM_WRITE_OPERATION,
crate::EX_METADATA_SPM_INITIALIZE_OPERATION,
crate::EX_METADATA_SPM_SET_AUTHORITY_OPERATION,
crate::EX_METADATA_SPM_SET_DATA_OPERATION,
crate::EX_METADATA_SPM_SET_IMMUTABLE_OPERATION,
crate::EX_METADATA_SPM_TRIM_OPERATION,
crate::EX_METADATA_SPM_CLOSE_OPERATION,
crate::EX_METADATA_SPM_ALLOCATE_OPERATION,
crate::EX_METADATA_SPM_EXTEND_OPERATION,
];
/// Operations that require explicit operator approval before plan construction.
pub const EX_METADATA_SPM_DESTRUCTIVE_OPERATION_CODES: &[&str] = &[
crate::EX_METADATA_SPM_WRITE_OPERATION,
crate::EX_METADATA_SPM_SET_AUTHORITY_OPERATION,
crate::EX_METADATA_SPM_SET_DATA_OPERATION,
crate::EX_METADATA_SPM_SET_IMMUTABLE_OPERATION,
crate::EX_METADATA_SPM_TRIM_OPERATION,
crate::EX_METADATA_SPM_CLOSE_OPERATION,
];

View File

@@ -0,0 +1,224 @@
// file: kb-lib/src/executor/metadata/solana_program_metadata/executor.rs
// version: 1
//! Exact capability dispatch for Solana Program Metadata.
/// Typed Solana Program Metadata executor.
#[derive(Clone, Debug, Default)]
pub struct ExMetadataSolanaProgramMetadataExecutor;
impl crate::ExMetadataSolanaProgramMetadataExecutor {
fn exact_capability(
&self,
program_id: &crate::MdProgramId,
operation_code: &str,
) -> crate::ExApiExecutionCapability {
if program_id.0 != kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID {
return crate::ExApiExecutionCapability::unsupported(
"execution_metadata_spm_program_not_owned",
format!("program {} is not Solana Program Metadata", program_id.0),
);
}
if crate::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES
.iter()
.any(|candidate| return *candidate == operation_code)
{
return crate::ExApiExecutionCapability::supported(operation_code);
}
return crate::ExApiExecutionCapability::unsupported(
"execution_metadata_spm_operation_unsupported",
format!("Solana Program Metadata operation {operation_code} is not implemented"),
);
}
}
impl crate::ExApiTypedInstructionExecutor for crate::ExMetadataSolanaProgramMetadataExecutor {
type Intent = crate::ExMetadataSpmExecutionIntent;
fn capability(
&self,
program_id: &crate::MdProgramId,
operation_code: &str,
) -> crate::ExApiExecutionCapability {
return self.exact_capability(program_id, operation_code);
}
fn build_prepared_plan(
&self,
intent: &Self::Intent,
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
let program_id = crate::MdProgramId(
kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID.to_string(),
);
return match self.exact_capability(&program_id, intent.operation.operation_code()) {
crate::ExApiExecutionCapability::Supported { operation_code: _ } => {
crate::executor_metadata_solana_program_metadata_build_prepared_plan(intent)
},
crate::ExApiExecutionCapability::Unsupported { reason_code, reason } => {
std::result::Result::Err(kb_core::Error::new(reason_code, reason))
},
};
}
}
impl crate::ExApiInstructionExecutor for crate::ExMetadataSolanaProgramMetadataExecutor {
fn executor_name(&self) -> &'static str {
return crate::EX_METADATA_SPM_EXECUTOR_NAME;
}
fn executor_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn program_ids(&self) -> &'static [&'static str] {
return &[kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID];
}
fn supports_request(
&self,
request: &crate::ExApiExecutionRequest,
) -> crate::ExApiExecutionSupport {
return if self
.exact_capability(&request.program_id, &request.operation_code)
.is_supported()
{
crate::ExApiExecutionSupport::Yes
} else {
crate::ExApiExecutionSupport::No
};
}
fn build_plan(
&self,
request: &crate::ExApiExecutionRequest,
) -> kb_core::Result<crate::ExApiExecutionPlan> {
match self.exact_capability(&request.program_id, &request.operation_code) {
crate::ExApiExecutionCapability::Supported { operation_code: _ } => {},
crate::ExApiExecutionCapability::Unsupported { reason_code, reason } => {
return std::result::Result::Err(kb_core::Error::new(reason_code, reason));
},
}
let intent = match serde_json::from_str::<crate::ExMetadataSpmExecutionIntent>(
&request.payload_json,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"execution_metadata_spm_intent_deserialize_failed",
error.to_string(),
));
},
};
if intent.operation.operation_code() != request.operation_code {
return std::result::Result::Err(kb_core::Error::new(
"execution_operation_code_mismatch",
"request and typed Solana Program Metadata intent operations differ",
));
}
let prepared =
match crate::ExApiTypedInstructionExecutor::build_prepared_plan(self, &intent) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let payload = match serde_json::to_value(&prepared) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"execution_metadata_spm_plan_serialize_failed",
error.to_string(),
));
},
};
let payload_json = match crate::executor_api_serialize_payload_json(&payload) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(crate::ExApiExecutionPlan {
executor_name: crate::EX_METADATA_SPM_EXECUTOR_NAME.to_string(),
instruction_count: prepared.instructions.len(),
payload_json,
});
}
}
#[cfg(test)]
mod tests {
#[test]
fn executor_matrix_matches_the_compiled_capability_inventory() {
let parsed = serde_json::from_str::<serde_json::Value>(include_str!(
"../../../../../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_EXECUTOR_MATRIX.json"
));
assert!(parsed.is_ok());
let matrix = match parsed {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let operations = match matrix.get("operations").and_then(serde_json::Value::as_array) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
assert_eq!(
matrix.get("maxInlineContentBytes").and_then(serde_json::Value::as_u64),
std::option::Option::Some(crate::EX_METADATA_SPM_MAX_INLINE_DATA_BYTES as u64)
);
assert_eq!(
matrix.get("canonicitySelection").and_then(serde_json::Value::as_str),
std::option::Option::Some(
"explicit_intent_flag_not_inferred_from_program_data_presence"
)
);
assert_eq!(operations.len(), crate::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES.len());
for (position, row) in operations.iter().enumerate() {
assert_eq!(
row.get("tag").and_then(serde_json::Value::as_u64),
std::option::Option::Some(position as u64)
);
assert_eq!(
row.get("operationCode").and_then(serde_json::Value::as_str),
std::option::Option::Some(
crate::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES[position]
)
);
assert_eq!(
row.get("requiresExplicitApproval").and_then(serde_json::Value::as_bool),
std::option::Option::Some(
crate::EX_METADATA_SPM_DESTRUCTIVE_OPERATION_CODES
.contains(&crate::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES[position])
)
);
assert_eq!(
row.get("deprecated").and_then(serde_json::Value::as_bool),
std::option::Option::Some(false)
);
assert_eq!(
row.get("replaced").and_then(serde_json::Value::as_bool),
std::option::Option::Some(false)
);
}
}
#[test]
fn capabilities_cover_exactly_the_nine_stable_operations() {
let executor = crate::ExMetadataSolanaProgramMetadataExecutor;
let program_id = crate::MdProgramId(
kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID.to_string(),
);
for operation in crate::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES {
let capability =
crate::ExApiTypedInstructionExecutor::capability(&executor, &program_id, operation);
assert!(capability.is_supported());
}
let unknown = crate::ExApiTypedInstructionExecutor::capability(
&executor,
&program_id,
"metadata.solana_program_metadata.unknown",
);
assert!(!unknown.is_supported());
let foreign = crate::ExApiTypedInstructionExecutor::capability(
&executor,
&crate::MdProgramId(kb_program_ids::SYSTEM_PROGRAM_ID.to_string()),
crate::EX_METADATA_SPM_WRITE_OPERATION,
);
assert!(!foreign.is_supported());
}
}

View File

@@ -0,0 +1,471 @@
// file: kb-lib/src/executor/metadata/solana_program_metadata/intent.rs
// version: 1
//! Typed intents for the Solana Program Metadata executor.
use ts_rs::TS; // rust-rules: derive-import
/// Encoding written to a Solana Program Metadata account.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[repr(u8)]
#[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmEncoding.ts"
)]
pub enum ExMetadataSpmEncoding {
/// No encoding declaration.
None = 0,
/// UTF-8 encoding.
Utf8 = 1,
/// Base58 encoding.
Base58 = 2,
/// Base64 encoding.
Base64 = 3,
}
impl crate::ExMetadataSpmEncoding {
/// Returns the exact wire value.
pub const fn wire_value(self) -> u8 {
return self as u8;
}
}
/// Compression written to a Solana Program Metadata account.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[repr(u8)]
#[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmCompression.ts"
)]
pub enum ExMetadataSpmCompression {
/// No compression declaration.
None = 0,
/// Gzip compression.
Gzip = 1,
/// Zlib compression.
Zlib = 2,
}
impl crate::ExMetadataSpmCompression {
/// Returns the exact wire value.
pub const fn wire_value(self) -> u8 {
return self as u8;
}
}
/// Structured format written to a Solana Program Metadata account.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[repr(u8)]
#[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmFormat.ts"
)]
pub enum ExMetadataSpmFormat {
/// No structured format declaration.
None = 0,
/// JSON format.
Json = 1,
/// YAML format.
Yaml = 2,
/// TOML format.
Toml = 3,
}
impl crate::ExMetadataSpmFormat {
/// Returns the exact wire value.
pub const fn wire_value(self) -> u8 {
return self as u8;
}
}
/// On-chain data source written to a Solana Program Metadata account.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[repr(u8)]
#[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmDataSource.ts"
)]
pub enum ExMetadataSpmDataSource {
/// Direct on-chain bytes.
Direct = 0,
/// UTF-8 URL stored on-chain without fetching it.
Url = 1,
/// Fixed reference to another Solana account.
External = 2,
}
impl crate::ExMetadataSpmDataSource {
/// Returns the exact wire value.
pub const fn wire_value(self) -> u8 {
return self as u8;
}
}
/// Exact inline content accepted by `Initialize` and `SetData`.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[serde(tag = "source", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmDataInput.ts"
)]
pub enum ExMetadataSpmDataInput {
/// Direct on-chain bytes.
Direct {
/// Exact bytes to store.
bytes: std::vec::Vec<u8>,
},
/// UTF-8 URL retained on-chain without remote resolution.
Url {
/// URL text to store.
url: std::string::String,
},
/// Fixed reference to another account's data.
External {
/// Referenced account.
address: crate::MdPubkey,
/// Byte offset inside the referenced account.
offset: u32,
/// Optional non-zero length; `None` means the remainder of the account.
length: std::option::Option<u32>,
},
}
impl crate::ExMetadataSpmDataInput {
/// Returns the exact data-source discriminator.
pub const fn data_source(&self) -> crate::ExMetadataSpmDataSource {
return match self {
Self::Direct { bytes: _ } => crate::ExMetadataSpmDataSource::Direct,
Self::Url { url: _ } => crate::ExMetadataSpmDataSource::Url,
Self::External { address: _, offset: _, length: _ } => {
crate::ExMetadataSpmDataSource::External
},
};
}
}
/// Source used by the `Write` instruction.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[serde(tag = "source", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmWriteSource.ts"
)]
pub enum ExMetadataSpmWriteSource {
/// Inline bytes placed after the offset argument.
Inline {
/// Exact bytes to write.
data: std::vec::Vec<u8>,
},
/// Copy all data from another Buffer account.
Buffer {
/// Source Buffer account.
source_buffer: crate::MdPubkey,
},
}
/// Source used by the `SetData` instruction.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[serde(tag = "source", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmSetDataSource.ts"
)]
pub enum ExMetadataSpmSetDataSource {
/// Runtime-supported three-byte variant that preserves current data bytes.
PreserveExisting,
/// Replace data with inline content.
Inline {
/// Exact inline content.
data: crate::ExMetadataSpmDataInput,
},
/// Copy data from one Buffer account.
Buffer {
/// Source Buffer account.
buffer: crate::MdPubkey,
/// Source discriminator that the resulting metadata account must declare.
data_source: crate::ExMetadataSpmDataSource,
},
}
/// Optional program-upgrade-authority validation context.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmProgramContext.ts"
)]
pub struct ExMetadataSpmProgramContext {
/// Executable program account.
pub program: crate::MdPubkey,
/// Optional upgradeable-loader ProgramData account used when the runtime must validate it.
pub program_data: std::option::Option<crate::MdPubkey>,
}
/// One exact Solana Program Metadata operation.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[serde(tag = "operation", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmOperation.ts"
)]
pub enum ExMetadataSpmOperation {
/// Write bytes to a Buffer account.
Write {
/// Buffer account to mutate.
buffer: crate::MdPubkey,
/// Current Buffer authority.
authority: crate::MdPubkey,
/// Byte offset inside the Buffer payload.
offset: u32,
/// Inline or Buffer source.
source: crate::ExMetadataSpmWriteSource,
},
/// Initialize one canonical or non-canonical Metadata PDA.
Initialize {
/// Metadata PDA to initialize.
metadata: crate::MdPubkey,
/// Authority signing the initialization.
authority: crate::MdPubkey,
/// Program described by this metadata account.
program: crate::MdPubkey,
/// Optional ProgramData account used for an upgradeable program.
program_data: std::option::Option<crate::MdPubkey>,
/// Whether the Metadata PDA uses the canonical `[program, seed]` derivation.
canonical: bool,
/// Exact fixed-size seed bytes.
seed: std::vec::Vec<u8>,
/// Content encoding.
encoding: crate::ExMetadataSpmEncoding,
/// Content compression.
compression: crate::ExMetadataSpmCompression,
/// Structured content format.
format: crate::ExMetadataSpmFormat,
/// Declared data source when using a preallocated Buffer.
data_source: crate::ExMetadataSpmDataSource,
/// Optional inline content; absent means the Metadata account is a preallocated Buffer.
data: std::option::Option<crate::ExMetadataSpmDataInput>,
/// Whether the instruction may invoke the System Program to allocate the PDA.
allocate_account: bool,
},
/// Change or remove one Buffer or Metadata authority.
SetAuthority {
/// Buffer or Metadata account.
account: crate::MdPubkey,
/// Current authority.
authority: crate::MdPubkey,
/// New authority, or `None` to remove a Metadata authority.
new_authority: std::option::Option<crate::MdPubkey>,
/// Optional upgrade-authority validation context.
program_context: std::option::Option<crate::ExMetadataSpmProgramContext>,
},
/// Change Metadata content descriptors and data.
SetData {
/// Metadata account.
metadata: crate::MdPubkey,
/// Current authority.
authority: crate::MdPubkey,
/// Content encoding.
encoding: crate::ExMetadataSpmEncoding,
/// Content compression.
compression: crate::ExMetadataSpmCompression,
/// Structured content format.
format: crate::ExMetadataSpmFormat,
/// Inline, Buffer or preserve-existing source.
source: crate::ExMetadataSpmSetDataSource,
/// Optional upgrade-authority validation context.
program_context: std::option::Option<crate::ExMetadataSpmProgramContext>,
},
/// Permanently make one Metadata account immutable.
SetImmutable {
/// Metadata account.
metadata: crate::MdPubkey,
/// Current authority.
authority: crate::MdPubkey,
/// Optional upgrade-authority validation context.
program_context: std::option::Option<crate::ExMetadataSpmProgramContext>,
},
/// Resize one Buffer or Metadata account to its logical minimum and refund excess lamports.
Trim {
/// Buffer or Metadata account.
account: crate::MdPubkey,
/// Current authority.
authority: crate::MdPubkey,
/// Lamport destination.
destination: crate::MdPubkey,
/// Optional upgrade-authority validation context.
program_context: std::option::Option<crate::ExMetadataSpmProgramContext>,
},
/// Close one mutable Buffer or Metadata account.
Close {
/// Buffer or Metadata account.
account: crate::MdPubkey,
/// Current authority.
authority: crate::MdPubkey,
/// Lamport destination.
destination: crate::MdPubkey,
/// Optional upgrade-authority validation context.
program_context: std::option::Option<crate::ExMetadataSpmProgramContext>,
},
/// Allocate one pre-funded Buffer account.
Allocate {
/// Buffer account.
buffer: crate::MdPubkey,
/// Authority signing the allocation.
authority: crate::MdPubkey,
/// Optional seed for a PDA Buffer; absent selects a pre-created keypair account.
seed: std::option::Option<std::vec::Vec<u8>>,
/// Optional upgrade-authority validation context.
program_context: std::option::Option<crate::ExMetadataSpmProgramContext>,
/// Whether a PDA Buffer uses the canonical `[program, seed]` derivation.
canonical: bool,
/// Whether the instruction may invoke the System Program to allocate the account.
allocate_account: bool,
},
/// Extend one pre-funded Buffer or Metadata account.
Extend {
/// Buffer or Metadata account.
account: crate::MdPubkey,
/// Current authority.
authority: crate::MdPubkey,
/// Number of bytes to append to account capacity.
length: u16,
/// Optional upgrade-authority validation context.
program_context: std::option::Option<crate::ExMetadataSpmProgramContext>,
},
}
impl crate::ExMetadataSpmOperation {
/// Returns the stable operation code.
pub const fn operation_code(&self) -> &'static str {
return match self {
Self::Write {
buffer: _,
authority: _,
offset: _,
source: _,
} => crate::EX_METADATA_SPM_WRITE_OPERATION,
Self::Initialize {
metadata: _,
authority: _,
program: _,
program_data: _,
canonical: _,
seed: _,
encoding: _,
compression: _,
format: _,
data_source: _,
data: _,
allocate_account: _,
} => crate::EX_METADATA_SPM_INITIALIZE_OPERATION,
Self::SetAuthority {
account: _,
authority: _,
new_authority: _,
program_context: _,
} => crate::EX_METADATA_SPM_SET_AUTHORITY_OPERATION,
Self::SetData {
metadata: _,
authority: _,
encoding: _,
compression: _,
format: _,
source: _,
program_context: _,
} => crate::EX_METADATA_SPM_SET_DATA_OPERATION,
Self::SetImmutable {
metadata: _,
authority: _,
program_context: _,
} => crate::EX_METADATA_SPM_SET_IMMUTABLE_OPERATION,
Self::Trim {
account: _,
authority: _,
destination: _,
program_context: _,
} => crate::EX_METADATA_SPM_TRIM_OPERATION,
Self::Close {
account: _,
authority: _,
destination: _,
program_context: _,
} => crate::EX_METADATA_SPM_CLOSE_OPERATION,
Self::Allocate {
buffer: _,
authority: _,
seed: _,
program_context: _,
canonical: _,
allocate_account: _,
} => crate::EX_METADATA_SPM_ALLOCATE_OPERATION,
Self::Extend {
account: _,
authority: _,
length: _,
program_context: _,
} => crate::EX_METADATA_SPM_EXTEND_OPERATION,
};
}
/// Returns whether the operation can overwrite, destroy or irreversibly constrain state.
pub const fn requires_explicit_approval(&self) -> bool {
return matches!(
self,
Self::Write {
buffer: _,
authority: _,
offset: _,
source: _
} | Self::SetAuthority {
account: _,
authority: _,
new_authority: _,
program_context: _,
} | Self::SetData {
metadata: _,
authority: _,
encoding: _,
compression: _,
format: _,
source: _,
program_context: _,
} | Self::SetImmutable {
metadata: _,
authority: _,
program_context: _
} | Self::Trim {
account: _,
authority: _,
destination: _,
program_context: _
} | Self::Close {
account: _,
authority: _,
destination: _,
program_context: _
}
);
}
}
/// Complete Solana Program Metadata execution intent.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmExecutionIntent.ts"
)]
pub struct ExMetadataSpmExecutionIntent {
/// Caller-provided correlation identifier.
pub intent_id: std::string::String,
/// Transaction fee payer.
pub fee_payer: crate::MdPubkey,
/// Conservative common execution policy.
pub policy: crate::ExApiExecutionPolicy,
/// Explicit approval for overwrite, destructive or irreversible operations.
pub allow_destructive_operation: bool,
/// Exact operation to build.
pub operation: crate::ExMetadataSpmOperation,
}

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/lib.rs
// version: 33
// version: 36
//! Consolidated decoder, executor, materializer and shared model library.
#![warn(missing_docs)]
@@ -401,6 +401,30 @@ pub use self::decoder::decoder_metadata_solana_program_metadata_decode_metadata_
pub use self::decoder::decoder_spl_elgamal_registry_parse_elgamal_registry_state;
/// Parses one Token-2022 Mint, Account, or Multisig state.
pub use self::decoder::decoder_spl_token_2022_parse_token_2022_state;
/// Stable Solana Program Metadata `Allocate` operation code.
pub use self::executor::EX_METADATA_SPM_ALLOCATE_OPERATION;
/// Stable Solana Program Metadata `Close` operation code.
pub use self::executor::EX_METADATA_SPM_CLOSE_OPERATION;
/// Solana Program Metadata operations requiring explicit destructive approval.
pub use self::executor::EX_METADATA_SPM_DESTRUCTIVE_OPERATION_CODES;
/// Stable Solana Program Metadata `Extend` operation code.
pub use self::executor::EX_METADATA_SPM_EXTEND_OPERATION;
/// Stable Solana Program Metadata `Initialize` operation code.
pub use self::executor::EX_METADATA_SPM_INITIALIZE_OPERATION;
/// Conservative maximum inline Solana Program Metadata content size.
pub use self::executor::EX_METADATA_SPM_MAX_INLINE_DATA_BYTES;
/// Stable Solana Program Metadata `SetAuthority` operation code.
pub use self::executor::EX_METADATA_SPM_SET_AUTHORITY_OPERATION;
/// Stable Solana Program Metadata `SetData` operation code.
pub use self::executor::EX_METADATA_SPM_SET_DATA_OPERATION;
/// Stable Solana Program Metadata `SetImmutable` operation code.
pub use self::executor::EX_METADATA_SPM_SET_IMMUTABLE_OPERATION;
/// Exact Solana Program Metadata operation inventory.
pub use self::executor::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES;
/// Stable Solana Program Metadata `Trim` operation code.
pub use self::executor::EX_METADATA_SPM_TRIM_OPERATION;
/// Stable Solana Program Metadata `Write` operation code.
pub use self::executor::EX_METADATA_SPM_WRITE_OPERATION;
/// Exposes `EX_METAPLEX_TOKEN_METADATA_APPROVE_USE_AUTHORITY_OPERATION`.
pub use self::executor::EX_METAPLEX_TOKEN_METADATA_APPROVE_USE_AUTHORITY_OPERATION;
/// Exposes `EX_METAPLEX_TOKEN_METADATA_BURN_OPERATION`.
@@ -1135,10 +1159,30 @@ pub use self::executor::ExLendingMarginfiV2Executor;
pub use self::executor::ExLockRaydiumLpExecutor;
/// Exposes the Metaplex Token Metadata executor.
pub use self::executor::ExMetadataMetaplexTokenMetadataExecutor;
/// Exposes the reserved `ExMetadataSolanaProgramMetadataExecutor` implementation.
/// Exposes the typed `ExMetadataSolanaProgramMetadataExecutor` implementation.
pub use self::executor::ExMetadataSolanaProgramMetadataExecutor;
/// Exposes the reserved `ExMetadataSplNameServiceExecutor` implementation.
pub use self::executor::ExMetadataSplNameServiceExecutor;
/// Solana Program Metadata compression intent.
pub use self::executor::ExMetadataSpmCompression;
/// Solana Program Metadata inline data intent.
pub use self::executor::ExMetadataSpmDataInput;
/// Solana Program Metadata data-source intent.
pub use self::executor::ExMetadataSpmDataSource;
/// Solana Program Metadata encoding intent.
pub use self::executor::ExMetadataSpmEncoding;
/// Complete Solana Program Metadata execution intent.
pub use self::executor::ExMetadataSpmExecutionIntent;
/// Solana Program Metadata format intent.
pub use self::executor::ExMetadataSpmFormat;
/// One Solana Program Metadata operation.
pub use self::executor::ExMetadataSpmOperation;
/// Optional Solana Program Metadata program context.
pub use self::executor::ExMetadataSpmProgramContext;
/// Solana Program Metadata `SetData` source intent.
pub use self::executor::ExMetadataSpmSetDataSource;
/// Solana Program Metadata `Write` source intent.
pub use self::executor::ExMetadataSpmWriteSource;
/// Exposes the complete Metaplex Token Metadata execution intent.
pub use self::executor::ExMetaplexTokenMetadataExecutionIntent;
/// Exposes one current Metaplex Token Metadata operation.
@@ -1349,6 +1393,10 @@ pub use self::executor::ExWeightedSwapStabbleExecutor;
pub use self::executor::executor_api_serialize_payload_json;
/// Exposes the pretty JSON payload serializer.
pub use self::executor::executor_api_serialize_payload_json_pretty;
/// Derives the canonical Solana Program Metadata PDA.
pub use self::executor::executor_metadata_solana_program_metadata_derive_canonical_pda;
/// Derives the non-canonical Solana Program Metadata PDA.
pub use self::executor::executor_metadata_solana_program_metadata_derive_non_canonical_pda;
/// Builds an unsigned durable nonce transaction from a prepared execution plan and validated nonce state.
pub use self::executor::executor_solana_build_durable_nonce_transaction;
/// Builds an unsigned legacy Solana transaction from a prepared execution plan.
@@ -1391,7 +1439,7 @@ pub use self::materializer::MtLifecycleMaterializer;
pub use self::materializer::MtLiquidityMaterializer;
/// Exposes the legacy common business materializer trait.
pub use self::materializer::MtMaterializer;
/// Token-2022, Metaplex and Solana Program Metadata materializer.
/// Compatibility wrapper for the Solana Program Metadata materializer.
pub use self::materializer::MtMetadataMaterializer;
/// Reserved NFT materializer.
pub use self::materializer::MtNftMaterializer;
@@ -1409,6 +1457,8 @@ 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.
@@ -1972,6 +2022,8 @@ pub(crate) use self::decoder::decoder_spl_token_decode;
pub(crate) use self::decoder::decoder_spl_token_entry_for_tag;
/// Returns the first byte of one retained classic SPL Token payload.
pub(crate) use self::decoder::decoder_spl_token_payload_tag;
/// Stable Solana Program Metadata executor name.
pub(crate) use self::executor::EX_METADATA_SPM_EXECUTOR_NAME;
/// Stable operation code for the nonce advance injected by durable transaction assembly.
pub(crate) use self::executor::EX_SOLANA_TRANSACTION_DURABLE_NONCE_ADVANCE_OPERATION;
/// Maximum over-the-wire size of a Solana transaction packet.
@@ -2004,6 +2056,8 @@ pub(crate) use self::executor::TRACING_TARGET_EXECUTOR_SPL_MEMO;
pub(crate) use self::executor::TRACING_TARGET_EXECUTOR_SPL_TOKEN;
/// Canonical SPL Token-2022 tracing target.
pub(crate) use self::executor::TRACING_TARGET_EXECUTOR_SPL_TOKEN_2022;
/// Internal Solana Program Metadata prepared-plan builder.
pub(crate) use self::executor::executor_metadata_solana_program_metadata_build_prepared_plan;
/// Internal Metaplex Token Metadata prepared-plan builder.
pub(crate) use self::executor::executor_metaplex_token_metadata_build_prepared_plan;
/// Crate-root access to `build_prepared_plan` from `address_lookup_table`.
@@ -2052,41 +2106,315 @@ pub(crate) use self::executor::executor_spl_memo_build_prepared_plan;
pub(crate) use self::executor::executor_spl_token_2022_build_prepared_plan;
/// Crate-root access to `executor_spl_token_build_prepared_plan`.
pub(crate) use self::executor::executor_spl_token_build_prepared_plan;
/// Canonical processor name for the native and SPL administration materializer.
/// Crate-root access to `MT_ADMIN_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_ADMIN_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES`.
pub(crate) use self::materializer::MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES;
/// Crate-root access to `MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE`.
pub(crate) use self::materializer::MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Crate-root access to `MT_ADMIN_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_ADMIN_COMPONENT_NAME;
/// Crate-root access to `MT_ADMIN_CONFIG_ENTRIES`.
pub(crate) use self::materializer::MT_ADMIN_CONFIG_ENTRIES;
/// Crate-root access to `MT_ADMIN_CONFIG_SURFACE`.
pub(crate) use self::materializer::MT_ADMIN_CONFIG_SURFACE;
/// Crate-root access to `MT_ADMIN_LOADER_V4_ENTRIES`.
pub(crate) use self::materializer::MT_ADMIN_LOADER_V4_ENTRIES;
/// Crate-root access to `MT_ADMIN_LOADER_V4_SURFACE`.
pub(crate) use self::materializer::MT_ADMIN_LOADER_V4_SURFACE;
/// Crate-root access to `MT_ADMIN_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_ADMIN_PROCESSOR_NAME;
/// Event families accepted by the Solana Program Metadata instruction materializer.
pub(crate) use self::materializer::MT_METADATA_SPM_ACCEPTED_FAMILIES;
/// Stable processor name for Solana Program Metadata projections.
pub(crate) use self::materializer::MT_METADATA_SPM_PROCESSOR_NAME;
/// Stable projection schema version for Solana Program Metadata outputs.
pub(crate) use self::materializer::MT_METADATA_SPM_PROJECTION_VERSION;
/// Stable processor name for SPL Token and ATA risk facts.
/// Crate-root access to `MT_ADMIN_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_ADMIN_PROJECTION_VERSION;
/// Crate-root access to `MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES`.
pub(crate) use self::materializer::MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES;
/// Crate-root access to `MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE`.
pub(crate) use self::materializer::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE;
/// Crate-root access to `MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES`.
pub(crate) use self::materializer::MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES;
/// Crate-root access to `MT_ADMIN_SPL_TOKEN_2022_SURFACE`.
pub(crate) use self::materializer::MT_ADMIN_SPL_TOKEN_2022_SURFACE;
/// Crate-root access to `MT_ADMIN_SPL_TOKEN_AUTHORITY_ENTRIES`.
pub(crate) use self::materializer::MT_ADMIN_SPL_TOKEN_AUTHORITY_ENTRIES;
/// Crate-root access to `MT_ADMIN_SPL_TOKEN_MULTISIG_ENTRIES`.
pub(crate) use self::materializer::MT_ADMIN_SPL_TOKEN_MULTISIG_ENTRIES;
/// Crate-root access to `MT_ADMIN_SPL_TOKEN_SURFACE`.
pub(crate) use self::materializer::MT_ADMIN_SPL_TOKEN_SURFACE;
/// Crate-root access to `MT_ADMIN_SYSTEM_ENTRIES`.
pub(crate) use self::materializer::MT_ADMIN_SYSTEM_ENTRIES;
/// Crate-root access to `MT_ADMIN_SYSTEM_SURFACE`.
pub(crate) use self::materializer::MT_ADMIN_SYSTEM_SURFACE;
/// Crate-root access to `MT_BRIDGE_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_BRIDGE_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_BRIDGE_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_BRIDGE_COMPONENT_NAME;
/// Crate-root access to `MT_BRIDGE_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_BRIDGE_PROCESSOR_NAME;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_COMPONENT_NAME;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_PROJECTION_VERSION;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_WRITE_ENTRIES`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_WRITE_ENTRIES;
/// Crate-root access to `MT_FEES_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_FEES_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_FEES_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_FEES_COMPONENT_NAME;
/// Crate-root access to `MT_FEES_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_FEES_PROCESSOR_NAME;
/// Crate-root access to `MT_FEES_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_FEES_PROJECTION_VERSION;
/// Crate-root access to `MT_FEES_SPL_TOKEN_2022_SURFACE`.
pub(crate) use self::materializer::MT_FEES_SPL_TOKEN_2022_SURFACE;
/// Crate-root access to `MT_FEES_TOKEN_2022_FEE_ENTRIES`.
pub(crate) use self::materializer::MT_FEES_TOKEN_2022_FEE_ENTRIES;
/// Crate-root access to `MT_GOVERNANCE_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_GOVERNANCE_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_GOVERNANCE_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_GOVERNANCE_COMPONENT_NAME;
/// Crate-root access to `MT_GOVERNANCE_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_GOVERNANCE_PROCESSOR_NAME;
/// Crate-root access to `MT_LENDING_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_LENDING_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_LENDING_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_LENDING_COMPONENT_NAME;
/// Crate-root access to `MT_LENDING_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_LENDING_PROCESSOR_NAME;
/// Crate-root access to `MT_LIFECYCLE_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_BPF_LOADER_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_BPF_LOADER_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_LIFECYCLE_COMPONENT_NAME;
/// Crate-root access to `MT_LIFECYCLE_FEATURE_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_FEATURE_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_FEATURE_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_FEATURE_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_IMMUTABLE_LOADER_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_IMMUTABLE_LOADER_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_LOADER_V4_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_LOADER_V4_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_LOADER_V4_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_LOADER_V4_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_LIFECYCLE_PROCESSOR_NAME;
/// Crate-root access to `MT_LIFECYCLE_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_LIFECYCLE_PROJECTION_VERSION;
/// Crate-root access to `MT_LIFECYCLE_SLASHING_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_SLASHING_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_SLASHING_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_SLASHING_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_SYSTEM_ACCOUNT_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_SYSTEM_ACCOUNT_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_SYSTEM_NONCE_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_SYSTEM_NONCE_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_SYSTEM_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_SYSTEM_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_ZK_ELGAMAL_CLOSE_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_ZK_ELGAMAL_CLOSE_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES;
/// Crate-root access to `MT_LIQUIDITY_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_LIQUIDITY_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_LIQUIDITY_COMPONENT_NAME`.
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_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_TOKEN_2022_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_METADATA_TOKEN_2022_COMPONENT_NAME;
/// 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`.
pub(crate) use self::materializer::MT_METADATA_TOKEN_2022_PROJECTION_VERSION;
/// Crate-root access to `MT_NFT_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_NFT_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_NFT_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_NFT_COMPONENT_NAME;
/// Crate-root access to `MT_NFT_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_NFT_PROCESSOR_NAME;
/// Crate-root access to `MT_ORACLE_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_ORACLE_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_ORACLE_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_ORACLE_COMPONENT_NAME;
/// Crate-root access to `MT_ORACLE_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_ORACLE_PROCESSOR_NAME;
/// Crate-root access to `MT_ORDERBOOK_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_ORDERBOOK_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_ORDERBOOK_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_ORDERBOOK_COMPONENT_NAME;
/// Crate-root access to `MT_ORDERBOOK_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_ORDERBOOK_PROCESSOR_NAME;
/// Crate-root access to `MT_PERPETUALS_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_PERPETUALS_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_PERPETUALS_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_PERPETUALS_COMPONENT_NAME;
/// Crate-root access to `MT_PERPETUALS_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_PERPETUALS_PROCESSOR_NAME;
/// Crate-root access to `MT_POOL_STATE_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_POOL_STATE_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_POOL_STATE_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_POOL_STATE_COMPONENT_NAME;
/// Crate-root access to `MT_POOL_STATE_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_POOL_STATE_PROCESSOR_NAME;
/// Crate-root access to `MT_REWARDS_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_REWARDS_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_REWARDS_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_REWARDS_COMPONENT_NAME;
/// Crate-root access to `MT_REWARDS_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_REWARDS_PROCESSOR_NAME;
/// Crate-root access to `MT_RISK_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_RISK_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_RISK_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_RISK_COMPONENT_NAME;
/// Crate-root access to `MT_RISK_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_RISK_PROCESSOR_NAME;
/// Stable projection contract version for SPL Token and ATA risk facts.
/// Crate-root access to `MT_RISK_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_RISK_PROJECTION_VERSION;
/// Stable processor name for Token, ATA and Token-2022 account projections.
/// Crate-root access to `MT_RISK_RISK_ENTRIES`.
pub(crate) use self::materializer::MT_RISK_RISK_ENTRIES;
/// Crate-root access to `MT_ROUTING_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_ROUTING_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_ROUTING_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_ROUTING_COMPONENT_NAME;
/// Crate-root access to `MT_ROUTING_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_ROUTING_PROCESSOR_NAME;
/// Crate-root access to `MT_STAKING_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_STAKING_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_STAKING_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_STAKING_COMPONENT_NAME;
/// Crate-root access to `MT_STAKING_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_STAKING_PROCESSOR_NAME;
/// Crate-root access to `MT_STAKING_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_STAKING_PROJECTION_VERSION;
/// Crate-root access to `MT_STAKING_STAKE_ACCOUNT_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_STAKE_ACCOUNT_ENTRIES;
/// Crate-root access to `MT_STAKING_STAKE_AUTHORITY_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_STAKE_AUTHORITY_ENTRIES;
/// Crate-root access to `MT_STAKING_STAKE_LOCKUP_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_STAKE_LOCKUP_ENTRIES;
/// Crate-root access to `MT_STAKING_STAKE_SURFACE`.
pub(crate) use self::materializer::MT_STAKING_STAKE_SURFACE;
/// Crate-root access to `MT_STAKING_STAKE_VALUE_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_STAKE_VALUE_ENTRIES;
/// Crate-root access to `MT_STAKING_VOTE_ACCOUNT_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_VOTE_ACCOUNT_ENTRIES;
/// Crate-root access to `MT_STAKING_VOTE_ADMIN_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_VOTE_ADMIN_ENTRIES;
/// Crate-root access to `MT_STAKING_VOTE_AUTHORITY_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_VOTE_AUTHORITY_ENTRIES;
/// Crate-root access to `MT_STAKING_VOTE_REWARD_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_VOTE_REWARD_ENTRIES;
/// Crate-root access to `MT_STAKING_VOTE_STATE_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_VOTE_STATE_ENTRIES;
/// Crate-root access to `MT_STAKING_VOTE_SURFACE`.
pub(crate) use self::materializer::MT_STAKING_VOTE_SURFACE;
/// Crate-root access to `MT_STAKING_VOTE_VALUE_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_VOTE_VALUE_ENTRIES;
/// Crate-root access to `MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES`.
pub(crate) use self::materializer::MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES;
/// Crate-root access to `MT_TOKEN_ACCOUNTS_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_TOKEN_ACCOUNTS_COMPONENT_NAME;
/// Crate-root access to `MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES`.
pub(crate) use self::materializer::MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES;
/// Crate-root access to `MT_TOKEN_ACCOUNTS_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME;
/// Stable projection contract version for Token, ATA and Token-2022 account projections.
/// Crate-root access to `MT_TOKEN_ACCOUNTS_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION;
/// Maximum Memo payload accepted by transaction annotations.
/// Crate-root access to `MT_TOKEN_METADATA_RISK_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_TOKEN_METADATA_RISK_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_TOKEN_METADATA_RISK_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_TOKEN_METADATA_RISK_COMPONENT_NAME;
/// Crate-root access to `MT_TOKEN_METADATA_RISK_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_TOKEN_METADATA_RISK_PROCESSOR_NAME;
/// Crate-root access to `MT_TRADES_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_TRADES_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_TRADES_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_TRADES_COMPONENT_NAME;
/// Crate-root access to `MT_TRADES_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_TRADES_PROCESSOR_NAME;
/// Crate-root access to `MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME;
/// Crate-root access to `MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES`.
pub(crate) use self::materializer::MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES;
/// Stable processor name for transaction annotations.
/// Crate-root access to `MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES`.
pub(crate) use self::materializer::MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES;
/// Crate-root access to `MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME;
/// Stable projection contract version for transaction annotations.
/// Crate-root access to `MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION;
/// Canonical tracing target for the native and SPL administration materializer.
/// Crate-root access to `MT_VAULT_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_VAULT_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_VAULT_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_VAULT_COMPONENT_NAME;
/// Crate-root access to `MT_VAULT_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_VAULT_PROCESSOR_NAME;
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_ADMIN`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_ADMIN;
/// Canonical tracing target for the native compliance audit materializer.
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT;
/// Canonical tracing target for the native lifecycle materializer.
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_FEES`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_FEES;
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_LIFECYCLE`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_LIFECYCLE;
/// Canonical tracing target for Solana Program Metadata materialization.
/// 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;
/// Canonical tracing target for SPL Token and ATA risk facts.
/// 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`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_RISK;
/// Canonical tracing target for the native stake and vote materializer.
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_STAKING`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_STAKING;
/// Canonical tracing target for Token, ATA and Token-2022 account projections.
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS;
/// Canonical tracing target for transaction annotations.
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS;

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer.rs
// version: 9
// version: 14
//! Consolidated materializer modules.
@@ -73,8 +73,10 @@ pub use self::lending::MtLendingMaterializer;
pub use self::lifecycle::MtLifecycleMaterializer;
/// Reserved liquidity materializer.
pub use self::liquidity::MtLiquidityMaterializer;
/// Token-2022, Metaplex and Solana Program Metadata materializer.
/// Compatibility wrapper for the Solana Program Metadata materializer.
pub use self::metadata::MtMetadataMaterializer;
/// Stable Solana Program Metadata instruction materializer.
pub use self::metadata::MtSolanaProgramMetadataMaterializer;
/// 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.
@@ -118,100 +120,533 @@ pub use self::transaction::MtTransactionAnnotationMaterializer;
/// Reserved vault materializer.
pub use self::vault::MtVaultMaterializer;
/// Canonical processor name for the native and SPL administration materializer.
/// Crate-visible `MT_ADMIN_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::admin::MT_ADMIN_ACCEPTED_FAMILIES;
/// Crate-visible `MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES` component constant.
pub(crate) use self::admin::MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES;
/// Crate-visible `MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE` component constant.
pub(crate) use self::admin::MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Crate-visible `MT_ADMIN_COMPONENT_NAME` component constant.
pub(crate) use self::admin::MT_ADMIN_COMPONENT_NAME;
/// Crate-visible `MT_ADMIN_CONFIG_ENTRIES` component constant.
pub(crate) use self::admin::MT_ADMIN_CONFIG_ENTRIES;
/// Crate-visible `MT_ADMIN_CONFIG_SURFACE` component constant.
pub(crate) use self::admin::MT_ADMIN_CONFIG_SURFACE;
/// Crate-visible `MT_ADMIN_LOADER_V4_ENTRIES` component constant.
pub(crate) use self::admin::MT_ADMIN_LOADER_V4_ENTRIES;
/// Crate-visible `MT_ADMIN_LOADER_V4_SURFACE` component constant.
pub(crate) use self::admin::MT_ADMIN_LOADER_V4_SURFACE;
/// Crate-visible `MT_ADMIN_PROCESSOR_NAME` component constant.
pub(crate) use self::admin::MT_ADMIN_PROCESSOR_NAME;
/// Canonical tracing target for the native and SPL administration materializer.
/// Crate-visible `MT_ADMIN_PROJECTION_VERSION` component constant.
pub(crate) use self::admin::MT_ADMIN_PROJECTION_VERSION;
/// Crate-visible `MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES` component constant.
pub(crate) use self::admin::MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES;
/// Crate-visible `MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE` component constant.
pub(crate) use self::admin::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES` component constant.
pub(crate) use self::admin::MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_2022_SURFACE` component constant.
pub(crate) use self::admin::MT_ADMIN_SPL_TOKEN_2022_SURFACE;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_AUTHORITY_ENTRIES` component constant.
pub(crate) use self::admin::MT_ADMIN_SPL_TOKEN_AUTHORITY_ENTRIES;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_MULTISIG_ENTRIES` component constant.
pub(crate) use self::admin::MT_ADMIN_SPL_TOKEN_MULTISIG_ENTRIES;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_SURFACE` component constant.
pub(crate) use self::admin::MT_ADMIN_SPL_TOKEN_SURFACE;
/// Crate-visible `MT_ADMIN_SYSTEM_ENTRIES` component constant.
pub(crate) use self::admin::MT_ADMIN_SYSTEM_ENTRIES;
/// Crate-visible `MT_ADMIN_SYSTEM_SURFACE` component constant.
pub(crate) use self::admin::MT_ADMIN_SYSTEM_SURFACE;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_ADMIN` component constant.
pub(crate) use self::admin::TRACING_TARGET_MATERIALIZER_ADMIN;
/// Canonical tracing target for the native compliance audit materializer.
/// Crate-visible `MT_BRIDGE_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::bridge::MT_BRIDGE_ACCEPTED_FAMILIES;
/// Crate-visible `MT_BRIDGE_COMPONENT_NAME` component constant.
pub(crate) use self::bridge::MT_BRIDGE_COMPONENT_NAME;
/// Crate-visible `MT_BRIDGE_PROCESSOR_NAME` component constant.
pub(crate) use self::bridge::MT_BRIDGE_PROCESSOR_NAME;
/// Crate-visible `MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPONENT_NAME` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_COMPONENT_NAME;
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_PROCESSOR_NAME` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME;
/// Crate-visible `MT_COMPLIANCE_AUDIT_PROJECTION_VERSION` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_PROJECTION_VERSION;
/// Crate-visible `MT_COMPLIANCE_AUDIT_WRITE_ENTRIES` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_WRITE_ENTRIES;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT` component constant.
pub(crate) use self::compliance::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT;
/// Canonical tracing target for the native lifecycle materializer.
/// Crate-visible `MT_FEES_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::fees::MT_FEES_ACCEPTED_FAMILIES;
/// Crate-visible `MT_FEES_COMPONENT_NAME` component constant.
pub(crate) use self::fees::MT_FEES_COMPONENT_NAME;
/// Crate-visible `MT_FEES_PROCESSOR_NAME` component constant.
pub(crate) use self::fees::MT_FEES_PROCESSOR_NAME;
/// Crate-visible `MT_FEES_PROJECTION_VERSION` component constant.
pub(crate) use self::fees::MT_FEES_PROJECTION_VERSION;
/// Crate-visible `MT_FEES_SPL_TOKEN_2022_SURFACE` component constant.
pub(crate) use self::fees::MT_FEES_SPL_TOKEN_2022_SURFACE;
/// Crate-visible `MT_FEES_TOKEN_2022_FEE_ENTRIES` component constant.
pub(crate) use self::fees::MT_FEES_TOKEN_2022_FEE_ENTRIES;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_FEES` component constant.
pub(crate) use self::fees::TRACING_TARGET_MATERIALIZER_FEES;
/// Crate-visible `MT_GOVERNANCE_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::governance::MT_GOVERNANCE_ACCEPTED_FAMILIES;
/// Crate-visible `MT_GOVERNANCE_COMPONENT_NAME` component constant.
pub(crate) use self::governance::MT_GOVERNANCE_COMPONENT_NAME;
/// Crate-visible `MT_GOVERNANCE_PROCESSOR_NAME` component constant.
pub(crate) use self::governance::MT_GOVERNANCE_PROCESSOR_NAME;
/// Crate-visible `MT_LENDING_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::lending::MT_LENDING_ACCEPTED_FAMILIES;
/// Crate-visible `MT_LENDING_COMPONENT_NAME` component constant.
pub(crate) use self::lending::MT_LENDING_COMPONENT_NAME;
/// Crate-visible `MT_LENDING_PROCESSOR_NAME` component constant.
pub(crate) use self::lending::MT_LENDING_PROCESSOR_NAME;
/// Crate-visible `MT_LIFECYCLE_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_ACCEPTED_FAMILIES;
/// Crate-visible `MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE;
/// Crate-visible `MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE;
/// Crate-visible `MT_LIFECYCLE_BPF_LOADER_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_BPF_LOADER_SURFACE;
/// Crate-visible `MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Crate-visible `MT_LIFECYCLE_COMPONENT_NAME` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_COMPONENT_NAME;
/// Crate-visible `MT_LIFECYCLE_FEATURE_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_FEATURE_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_FEATURE_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_FEATURE_SURFACE;
/// Crate-visible `MT_LIFECYCLE_IMMUTABLE_LOADER_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_IMMUTABLE_LOADER_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_LOADER_V4_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_LOADER_V4_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_LOADER_V4_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_LOADER_V4_SURFACE;
/// Crate-visible `MT_LIFECYCLE_PROCESSOR_NAME` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_PROCESSOR_NAME;
/// Crate-visible `MT_LIFECYCLE_PROJECTION_VERSION` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_PROJECTION_VERSION;
/// Crate-visible `MT_LIFECYCLE_SLASHING_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_SLASHING_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_SLASHING_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_SLASHING_SURFACE;
/// Crate-visible `MT_LIFECYCLE_SYSTEM_ACCOUNT_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_SYSTEM_ACCOUNT_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_SYSTEM_NONCE_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_SYSTEM_NONCE_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_SYSTEM_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_SYSTEM_SURFACE;
/// Crate-visible `MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_ZK_ELGAMAL_CLOSE_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_ZK_ELGAMAL_CLOSE_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE;
/// Crate-visible `MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_LIFECYCLE` component constant.
pub(crate) use self::lifecycle::TRACING_TARGET_MATERIALIZER_LIFECYCLE;
/// Event families accepted by the Solana Program Metadata instruction materializer.
pub(crate) use self::metadata::MT_METADATA_SPM_ACCEPTED_FAMILIES;
/// Stable processor name for Solana Program Metadata projections.
pub(crate) use self::metadata::MT_METADATA_SPM_PROCESSOR_NAME;
/// Stable projection schema version for Solana Program Metadata outputs.
pub(crate) use self::metadata::MT_METADATA_SPM_PROJECTION_VERSION;
/// Canonical tracing target for Solana Program Metadata materialization.
/// Crate-visible `MT_LIQUIDITY_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::liquidity::MT_LIQUIDITY_ACCEPTED_FAMILIES;
/// Crate-visible `MT_LIQUIDITY_COMPONENT_NAME` component constant.
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_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_TOKEN_2022_COMPONENT_NAME` component constant.
pub(crate) use self::metadata::MT_METADATA_TOKEN_2022_COMPONENT_NAME;
/// 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;
/// Stable processor name for SPL Token and ATA risk facts.
/// 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.
pub(crate) use self::nft::MT_NFT_ACCEPTED_FAMILIES;
/// Crate-visible `MT_NFT_COMPONENT_NAME` component constant.
pub(crate) use self::nft::MT_NFT_COMPONENT_NAME;
/// Crate-visible `MT_NFT_PROCESSOR_NAME` component constant.
pub(crate) use self::nft::MT_NFT_PROCESSOR_NAME;
/// Crate-visible `MT_ORACLE_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::oracle::MT_ORACLE_ACCEPTED_FAMILIES;
/// Crate-visible `MT_ORACLE_COMPONENT_NAME` component constant.
pub(crate) use self::oracle::MT_ORACLE_COMPONENT_NAME;
/// Crate-visible `MT_ORACLE_PROCESSOR_NAME` component constant.
pub(crate) use self::oracle::MT_ORACLE_PROCESSOR_NAME;
/// Crate-visible `MT_ORDERBOOK_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::orderbook::MT_ORDERBOOK_ACCEPTED_FAMILIES;
/// Crate-visible `MT_ORDERBOOK_COMPONENT_NAME` component constant.
pub(crate) use self::orderbook::MT_ORDERBOOK_COMPONENT_NAME;
/// Crate-visible `MT_ORDERBOOK_PROCESSOR_NAME` component constant.
pub(crate) use self::orderbook::MT_ORDERBOOK_PROCESSOR_NAME;
/// Crate-visible `MT_PERPETUALS_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::perpetuals::MT_PERPETUALS_ACCEPTED_FAMILIES;
/// Crate-visible `MT_PERPETUALS_COMPONENT_NAME` component constant.
pub(crate) use self::perpetuals::MT_PERPETUALS_COMPONENT_NAME;
/// Crate-visible `MT_PERPETUALS_PROCESSOR_NAME` component constant.
pub(crate) use self::perpetuals::MT_PERPETUALS_PROCESSOR_NAME;
/// Crate-visible `MT_POOL_STATE_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::pool::MT_POOL_STATE_ACCEPTED_FAMILIES;
/// Crate-visible `MT_POOL_STATE_COMPONENT_NAME` component constant.
pub(crate) use self::pool::MT_POOL_STATE_COMPONENT_NAME;
/// Crate-visible `MT_POOL_STATE_PROCESSOR_NAME` component constant.
pub(crate) use self::pool::MT_POOL_STATE_PROCESSOR_NAME;
/// Crate-visible `MT_REWARDS_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::rewards::MT_REWARDS_ACCEPTED_FAMILIES;
/// Crate-visible `MT_REWARDS_COMPONENT_NAME` component constant.
pub(crate) use self::rewards::MT_REWARDS_COMPONENT_NAME;
/// Crate-visible `MT_REWARDS_PROCESSOR_NAME` component constant.
pub(crate) use self::rewards::MT_REWARDS_PROCESSOR_NAME;
/// Crate-visible `MT_RISK_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::risk::MT_RISK_ACCEPTED_FAMILIES;
/// Crate-visible `MT_RISK_COMPONENT_NAME` component constant.
pub(crate) use self::risk::MT_RISK_COMPONENT_NAME;
/// Crate-visible `MT_RISK_PROCESSOR_NAME` component constant.
pub(crate) use self::risk::MT_RISK_PROCESSOR_NAME;
/// Stable projection contract version for SPL Token and ATA risk facts.
/// Crate-visible `MT_RISK_PROJECTION_VERSION` component constant.
pub(crate) use self::risk::MT_RISK_PROJECTION_VERSION;
/// Canonical tracing target for SPL Token and ATA risk facts.
/// Crate-visible `MT_RISK_RISK_ENTRIES` component constant.
pub(crate) use self::risk::MT_RISK_RISK_ENTRIES;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_RISK` component constant.
pub(crate) use self::risk::TRACING_TARGET_MATERIALIZER_RISK;
/// Canonical tracing target for the native stake and vote materializer.
/// Crate-visible `MT_ROUTING_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::routing::MT_ROUTING_ACCEPTED_FAMILIES;
/// Crate-visible `MT_ROUTING_COMPONENT_NAME` component constant.
pub(crate) use self::routing::MT_ROUTING_COMPONENT_NAME;
/// Crate-visible `MT_ROUTING_PROCESSOR_NAME` component constant.
pub(crate) use self::routing::MT_ROUTING_PROCESSOR_NAME;
/// Crate-visible `MT_STAKING_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::staking::MT_STAKING_ACCEPTED_FAMILIES;
/// Crate-visible `MT_STAKING_COMPONENT_NAME` component constant.
pub(crate) use self::staking::MT_STAKING_COMPONENT_NAME;
/// Crate-visible `MT_STAKING_PROCESSOR_NAME` component constant.
pub(crate) use self::staking::MT_STAKING_PROCESSOR_NAME;
/// Crate-visible `MT_STAKING_PROJECTION_VERSION` component constant.
pub(crate) use self::staking::MT_STAKING_PROJECTION_VERSION;
/// Crate-visible `MT_STAKING_STAKE_ACCOUNT_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_STAKE_ACCOUNT_ENTRIES;
/// Crate-visible `MT_STAKING_STAKE_AUTHORITY_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_STAKE_AUTHORITY_ENTRIES;
/// Crate-visible `MT_STAKING_STAKE_LOCKUP_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_STAKE_LOCKUP_ENTRIES;
/// Crate-visible `MT_STAKING_STAKE_SURFACE` component constant.
pub(crate) use self::staking::MT_STAKING_STAKE_SURFACE;
/// Crate-visible `MT_STAKING_STAKE_VALUE_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_STAKE_VALUE_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_ACCOUNT_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_VOTE_ACCOUNT_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_ADMIN_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_VOTE_ADMIN_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_AUTHORITY_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_VOTE_AUTHORITY_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_REWARD_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_VOTE_REWARD_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_STATE_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_VOTE_STATE_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_SURFACE` component constant.
pub(crate) use self::staking::MT_STAKING_VOTE_SURFACE;
/// Crate-visible `MT_STAKING_VOTE_VALUE_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_VOTE_VALUE_ENTRIES;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_STAKING` component constant.
pub(crate) use self::staking::TRACING_TARGET_MATERIALIZER_STAKING;
/// Stable processor name for Token, ATA and Token-2022 account projections.
/// Crate-visible `MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::token::MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES;
/// Crate-visible `MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES` component constant.
pub(crate) use self::token::MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES;
/// Crate-visible `MT_TOKEN_ACCOUNTS_COMPONENT_NAME` component constant.
pub(crate) use self::token::MT_TOKEN_ACCOUNTS_COMPONENT_NAME;
/// Crate-visible `MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES` component constant.
pub(crate) use self::token::MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES;
/// Crate-visible `MT_TOKEN_ACCOUNTS_PROCESSOR_NAME` component constant.
pub(crate) use self::token::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME;
/// Stable projection contract version for Token, ATA and Token-2022 account projections.
/// Crate-visible `MT_TOKEN_ACCOUNTS_PROJECTION_VERSION` component constant.
pub(crate) use self::token::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION;
/// Canonical tracing target for Token, ATA and Token-2022 account projections.
/// Crate-visible `MT_TOKEN_METADATA_RISK_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::token::MT_TOKEN_METADATA_RISK_ACCEPTED_FAMILIES;
/// Crate-visible `MT_TOKEN_METADATA_RISK_COMPONENT_NAME` component constant.
pub(crate) use self::token::MT_TOKEN_METADATA_RISK_COMPONENT_NAME;
/// Crate-visible `MT_TOKEN_METADATA_RISK_PROCESSOR_NAME` component constant.
pub(crate) use self::token::MT_TOKEN_METADATA_RISK_PROCESSOR_NAME;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS` component constant.
pub(crate) use self::token::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS;
/// Maximum Memo payload accepted by transaction annotations.
/// Crate-visible `MT_TRADES_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::trades::MT_TRADES_ACCEPTED_FAMILIES;
/// Crate-visible `MT_TRADES_COMPONENT_NAME` component constant.
pub(crate) use self::trades::MT_TRADES_COMPONENT_NAME;
/// Crate-visible `MT_TRADES_PROCESSOR_NAME` component constant.
pub(crate) use self::trades::MT_TRADES_PROCESSOR_NAME;
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::transaction::MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES;
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME` component constant.
pub(crate) use self::transaction::MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME;
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES` component constant.
pub(crate) use self::transaction::MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES;
/// Stable processor name for transaction annotations.
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES` component constant.
pub(crate) use self::transaction::MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES;
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME` component constant.
pub(crate) use self::transaction::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME;
/// Stable projection contract version for transaction annotations.
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION` component constant.
pub(crate) use self::transaction::MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION;
/// Canonical tracing target for transaction annotations.
/// Crate-visible `TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS` component constant.
pub(crate) use self::transaction::TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS;
/// Crate-visible `MT_VAULT_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::vault::MT_VAULT_ACCEPTED_FAMILIES;
/// Crate-visible `MT_VAULT_COMPONENT_NAME` component constant.
pub(crate) use self::vault::MT_VAULT_COMPONENT_NAME;
/// Crate-visible `MT_VAULT_PROCESSOR_NAME` component constant.
pub(crate) use self::vault::MT_VAULT_PROCESSOR_NAME;
#[cfg(test)]
mod tests {
#[test]
fn reserved_materializers_expose_inactive_public_contracts() {
let materializers: &[(&dyn crate::MtApiEventMaterializer, &str)] = &[
(&crate::MtBridgeMaterializer, "kb-lib.materializer.bridge"),
(&crate::MtGovernanceMaterializer, "kb-lib.materializer.governance"),
(&crate::MtLendingMaterializer, "kb-lib.materializer.lending"),
(&crate::MtLiquidityMaterializer, "kb-lib.materializer.liquidity"),
(&crate::MtNftMaterializer, "kb-lib.materializer.nft"),
(&crate::MtOracleMaterializer, "kb-lib.materializer.oracle"),
(&crate::MtOrderbookMaterializer, "kb-lib.materializer.orderbook"),
(&crate::MtPerpetualsMaterializer, "kb-lib.materializer.perpetuals"),
(&crate::MtPoolStateMaterializer, "kb-lib.materializer.pool.state"),
(&crate::MtRewardsMaterializer, "kb-lib.materializer.rewards"),
(&crate::MtRoutingMaterializer, "kb-lib.materializer.routing"),
fn materializer_contracts() -> &'static [(
&'static dyn crate::MtMaterializer,
&'static dyn crate::MtApiEventMaterializer,
&'static str,
&'static str,
)] {
return &[
(
&crate::MtAdminMaterializer,
&crate::MtAdminMaterializer,
crate::MT_ADMIN_COMPONENT_NAME,
crate::MT_ADMIN_PROCESSOR_NAME,
),
(
&crate::MtBridgeMaterializer,
&crate::MtBridgeMaterializer,
crate::MT_BRIDGE_COMPONENT_NAME,
crate::MT_BRIDGE_PROCESSOR_NAME,
),
(
&crate::MtComplianceAuditMaterializer,
&crate::MtComplianceAuditMaterializer,
crate::MT_COMPLIANCE_AUDIT_COMPONENT_NAME,
crate::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME,
),
(
&crate::MtFeesMaterializer,
&crate::MtFeesMaterializer,
crate::MT_FEES_COMPONENT_NAME,
crate::MT_FEES_PROCESSOR_NAME,
),
(
&crate::MtGovernanceMaterializer,
&crate::MtGovernanceMaterializer,
crate::MT_GOVERNANCE_COMPONENT_NAME,
crate::MT_GOVERNANCE_PROCESSOR_NAME,
),
(
&crate::MtLendingMaterializer,
&crate::MtLendingMaterializer,
crate::MT_LENDING_COMPONENT_NAME,
crate::MT_LENDING_PROCESSOR_NAME,
),
(
&crate::MtLifecycleMaterializer,
&crate::MtLifecycleMaterializer,
crate::MT_LIFECYCLE_COMPONENT_NAME,
crate::MT_LIFECYCLE_PROCESSOR_NAME,
),
(
&crate::MtLiquidityMaterializer,
&crate::MtLiquidityMaterializer,
crate::MT_LIQUIDITY_COMPONENT_NAME,
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::MtNftMaterializer,
&crate::MtNftMaterializer,
crate::MT_NFT_COMPONENT_NAME,
crate::MT_NFT_PROCESSOR_NAME,
),
(
&crate::MtOracleMaterializer,
&crate::MtOracleMaterializer,
crate::MT_ORACLE_COMPONENT_NAME,
crate::MT_ORACLE_PROCESSOR_NAME,
),
(
&crate::MtOrderbookMaterializer,
&crate::MtOrderbookMaterializer,
crate::MT_ORDERBOOK_COMPONENT_NAME,
crate::MT_ORDERBOOK_PROCESSOR_NAME,
),
(
&crate::MtPerpetualsMaterializer,
&crate::MtPerpetualsMaterializer,
crate::MT_PERPETUALS_COMPONENT_NAME,
crate::MT_PERPETUALS_PROCESSOR_NAME,
),
(
&crate::MtPoolStateMaterializer,
&crate::MtPoolStateMaterializer,
crate::MT_POOL_STATE_COMPONENT_NAME,
crate::MT_POOL_STATE_PROCESSOR_NAME,
),
(
&crate::MtRewardsMaterializer,
&crate::MtRewardsMaterializer,
crate::MT_REWARDS_COMPONENT_NAME,
crate::MT_REWARDS_PROCESSOR_NAME,
),
(
&crate::MtRiskMaterializer,
&crate::MtRiskMaterializer,
crate::MT_RISK_COMPONENT_NAME,
crate::MT_RISK_PROCESSOR_NAME,
),
(
&crate::MtRoutingMaterializer,
&crate::MtRoutingMaterializer,
crate::MT_ROUTING_COMPONENT_NAME,
crate::MT_ROUTING_PROCESSOR_NAME,
),
(
&crate::MtStakingMaterializer,
&crate::MtStakingMaterializer,
crate::MT_STAKING_COMPONENT_NAME,
crate::MT_STAKING_PROCESSOR_NAME,
),
(
&crate::MtTokenAccountsMaterializer,
&crate::MtTokenAccountsMaterializer,
crate::MT_TOKEN_ACCOUNTS_COMPONENT_NAME,
crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME,
),
(
&crate::MtTokenMetadataRiskMaterializer,
"kb-lib.materializer.token.metadata_risk",
&crate::MtTokenMetadataRiskMaterializer,
crate::MT_TOKEN_METADATA_RISK_COMPONENT_NAME,
crate::MT_TOKEN_METADATA_RISK_PROCESSOR_NAME,
),
(
&crate::MtTradesMaterializer,
&crate::MtTradesMaterializer,
crate::MT_TRADES_COMPONENT_NAME,
crate::MT_TRADES_PROCESSOR_NAME,
),
(
&crate::MtTransactionAnnotationMaterializer,
&crate::MtTransactionAnnotationMaterializer,
crate::MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME,
crate::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME,
),
(
&crate::MtVaultMaterializer,
&crate::MtVaultMaterializer,
crate::MT_VAULT_COMPONENT_NAME,
crate::MT_VAULT_PROCESSOR_NAME,
),
(&crate::MtTradesMaterializer, "kb-lib.materializer.trades"),
(&crate::MtVaultMaterializer, "kb-lib.materializer.vault"),
];
for (materializer, expected_name) in materializers {
let identity = materializer.identity();
assert_eq!(identity.name, *expected_name);
assert!(!identity.version.is_empty());
assert!(materializer.accepted_families().is_empty());
}
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() {
assert_eq!(legacy.materializer_name(), *component_name);
assert_eq!(contextual.identity().name, *processor_name);
assert!(component_name.starts_with("kb-lib.materializer."));
assert!(processor_name.starts_with("materializer."));
assert_eq!(
component_name.strip_prefix("kb-lib."),
std::option::Option::Some(*processor_name)
);
assert!(!legacy.materializer_version().is_empty());
assert!(!contextual.identity().version.is_empty());
}
}
#[test]
fn reserved_materializers_preserve_legacy_trait_contracts() {
let materializers: &[(&dyn crate::MtMaterializer, &str)] = &[
(&crate::MtBridgeMaterializer, "kb-lib.materializer.bridge"),
(&crate::MtGovernanceMaterializer, "kb-lib.materializer.governance"),
(&crate::MtLendingMaterializer, "kb-lib.materializer.lending"),
(&crate::MtLiquidityMaterializer, "kb-lib.materializer.liquidity"),
(&crate::MtNftMaterializer, "kb-lib.materializer.nft"),
(&crate::MtOracleMaterializer, "kb-lib.materializer.oracle"),
(&crate::MtOrderbookMaterializer, "kb-lib.materializer.orderbook"),
(&crate::MtPerpetualsMaterializer, "kb-lib.materializer.perpetuals"),
(&crate::MtPoolStateMaterializer, "kb-lib.materializer.pool.state"),
(&crate::MtRewardsMaterializer, "kb-lib.materializer.rewards"),
(&crate::MtRoutingMaterializer, "kb-lib.materializer.routing"),
(
&crate::MtTokenMetadataRiskMaterializer,
"kb-lib.materializer.token.metadata_risk",
),
(&crate::MtTradesMaterializer, "kb-lib.materializer.trades"),
(&crate::MtVaultMaterializer, "kb-lib.materializer.vault"),
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] = &[
&crate::MtBridgeMaterializer,
&crate::MtGovernanceMaterializer,
&crate::MtLendingMaterializer,
&crate::MtLiquidityMaterializer,
&crate::MtNftMaterializer,
&crate::MtOracleMaterializer,
&crate::MtOrderbookMaterializer,
&crate::MtPerpetualsMaterializer,
&crate::MtPoolStateMaterializer,
&crate::MtRewardsMaterializer,
&crate::MtRoutingMaterializer,
&crate::MtTokenMetadataRiskMaterializer,
&crate::MtTradesMaterializer,
&crate::MtVaultMaterializer,
];
for (materializer, expected_name) in materializers {
assert_eq!(materializer.materializer_name(), *expected_name);
assert!(!materializer.materializer_version().is_empty());
for materializer in materializers {
assert!(materializer.accepted_families().is_empty());
}
}
}

View File

@@ -1,19 +1,55 @@
// file: kb-lib/src/materializer/admin.rs
// version: 7
// version: 8
//! `admin` materializer family.
mod constants;
mod core;
mod materializer;
/// Stable native and SPL administration materializer.
pub use self::core::MtAdminMaterializer;
pub use self::materializer::MtAdminMaterializer;
/// Materializes one exact SPL ElGamal registry account snapshot.
pub use self::core::materializer_admin_materialize_elgamal_registry_state_snapshot;
pub use self::materializer::materializer_admin_materialize_elgamal_registry_state_snapshot;
/// Materializes exact Token-2022 administration account snapshots.
pub use self::core::materializer_admin_materialize_token_2022_state_snapshots;
pub use self::materializer::materializer_admin_materialize_token_2022_state_snapshots;
/// Canonical processor name for the native and SPL administration materializer.
/// Crate-visible `MT_ADMIN_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::constants::MT_ADMIN_ACCEPTED_FAMILIES;
/// Crate-visible `MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES` component constant.
pub(crate) use self::constants::MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES;
/// Crate-visible `MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE` component constant.
pub(crate) use self::constants::MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Stable runtime component name for the `admin` materializer.
pub(crate) use self::constants::MT_ADMIN_COMPONENT_NAME;
/// Crate-visible `MT_ADMIN_CONFIG_ENTRIES` component constant.
pub(crate) use self::constants::MT_ADMIN_CONFIG_ENTRIES;
/// Crate-visible `MT_ADMIN_CONFIG_SURFACE` component constant.
pub(crate) use self::constants::MT_ADMIN_CONFIG_SURFACE;
/// Crate-visible `MT_ADMIN_LOADER_V4_ENTRIES` component constant.
pub(crate) use self::constants::MT_ADMIN_LOADER_V4_ENTRIES;
/// Crate-visible `MT_ADMIN_LOADER_V4_SURFACE` component constant.
pub(crate) use self::constants::MT_ADMIN_LOADER_V4_SURFACE;
/// Stable processor name for the `admin` materializer.
pub(crate) use self::constants::MT_ADMIN_PROCESSOR_NAME;
/// Canonical tracing target for the native and SPL administration materializer.
/// Stable projection version for the `admin` materializer.
pub(crate) use self::constants::MT_ADMIN_PROJECTION_VERSION;
/// Crate-visible `MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES` component constant.
pub(crate) use self::constants::MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES;
/// Crate-visible `MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE` component constant.
pub(crate) use self::constants::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES` component constant.
pub(crate) use self::constants::MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_2022_SURFACE` component constant.
pub(crate) use self::constants::MT_ADMIN_SPL_TOKEN_2022_SURFACE;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_AUTHORITY_ENTRIES` component constant.
pub(crate) use self::constants::MT_ADMIN_SPL_TOKEN_AUTHORITY_ENTRIES;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_MULTISIG_ENTRIES` component constant.
pub(crate) use self::constants::MT_ADMIN_SPL_TOKEN_MULTISIG_ENTRIES;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_SURFACE` component constant.
pub(crate) use self::constants::MT_ADMIN_SPL_TOKEN_SURFACE;
/// Crate-visible `MT_ADMIN_SYSTEM_ENTRIES` component constant.
pub(crate) use self::constants::MT_ADMIN_SYSTEM_ENTRIES;
/// Crate-visible `MT_ADMIN_SYSTEM_SURFACE` component constant.
pub(crate) use self::constants::MT_ADMIN_SYSTEM_SURFACE;
/// Canonical tracing target for the `admin` materializer.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_ADMIN;

View File

@@ -1,9 +1,67 @@
// file: kb-lib/src/materializer/admin/constants.rs
// version: 3
// version: 5
//! Local constants for the native and SPL administration materializer.
//! Local constants for the admin materializer.
/// Canonical processor name for this component.
/// Stable runtime component name for the admin materializer.
pub(crate) const MT_ADMIN_COMPONENT_NAME: &str = "kb-lib.materializer.admin";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_ADMIN_PROCESSOR_NAME: &str = "materializer.admin";
/// Canonical tracing target for this component.
/// Stable projection contract version for the admin materializer.
pub(crate) const MT_ADMIN_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for the admin materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_ADMIN: &str = "kb-lib.materializer.admin";
/// Component contract for `accepted_families`.
pub(crate) const MT_ADMIN_ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
&[crate::MdEventFamily::Admin, crate::MdEventFamily::TokenAccount];
/// Component contract for `system_surface`.
pub(crate) const MT_ADMIN_SYSTEM_SURFACE: &str = "solana.core.system";
/// Component contract for `system_entries`.
pub(crate) const MT_ADMIN_SYSTEM_ENTRIES: &[&str] = &["assign", "assign_with_seed"];
/// Component contract for `config_surface`.
pub(crate) const MT_ADMIN_CONFIG_SURFACE: &str = "solana.core.config";
/// Component contract for `config_entries`.
pub(crate) const MT_ADMIN_CONFIG_ENTRIES: &[&str] = &["store"];
/// Component contract for `bpf_loader_upgradeable_surface`.
pub(crate) const MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE: &str =
"solana.core.bpf_loader_upgradeable";
/// Component contract for `bpf_loader_upgradeable_entries`.
pub(crate) const MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES: &[&str] =
&["set_authority", "set_authority_checked"];
/// Component contract for `loader_v4_surface`.
pub(crate) const MT_ADMIN_LOADER_V4_SURFACE: &str = "solana.core.loader_v4";
/// Component contract for `loader_v4_entries`.
pub(crate) const MT_ADMIN_LOADER_V4_ENTRIES: &[&str] = &["transfer_authority"];
/// Component contract for `spl_token_surface`.
pub(crate) const MT_ADMIN_SPL_TOKEN_SURFACE: &str = "spl.token";
/// Component contract for `spl_token_2022_surface`.
pub(crate) const MT_ADMIN_SPL_TOKEN_2022_SURFACE: &str = "spl.token_2022";
/// Component contract for `spl_elgamal_registry_surface`.
pub(crate) const MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE: &str = "spl.elgamal_registry";
/// Component contract for `spl_elgamal_registry_entries`.
pub(crate) const MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES: &[&str] =
&["create_registry", "update_registry"];
/// Component contract for `spl_token_authority_entries`.
pub(crate) const MT_ADMIN_SPL_TOKEN_AUTHORITY_ENTRIES: &[&str] = &[
"set_authority",
"initialize_permanent_delegate",
"initialize_interest_bearing_mint",
"update_interest_bearing_rate",
];
/// Component contract for `spl_token_multisig_entries`.
pub(crate) const MT_ADMIN_SPL_TOKEN_MULTISIG_ENTRIES: &[&str] =
&["initialize_multisig", "initialize_multisig2"];
/// Component contract for `spl_token_2022_extension_admin_entries`.
pub(crate) const MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES: &[&str] = &[
"initialize_transfer_hook",
"update_transfer_hook_program_id",
"initialize_metadata_pointer",
"update_metadata_pointer",
"initialize_group_pointer",
"update_group_pointer",
"initialize_group_member_pointer",
"update_group_member_pointer",
"initialize_scaled_ui_amount",
"update_scaled_ui_amount_multiplier",
];

View File

@@ -1,42 +1,8 @@
// file: kb-lib/src/materializer/admin/core.rs
// version: 20
// file: kb-lib/src/materializer/admin/materializer.rs
// version: 22
//! Stable administrative projections derived from exact committed observations.
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
&[crate::MdEventFamily::Admin, crate::MdEventFamily::TokenAccount];
const SYSTEM_SURFACE: &str = "solana.core.system";
const SYSTEM_ENTRIES: &[&str] = &["assign", "assign_with_seed"];
const CONFIG_SURFACE: &str = "solana.core.config";
const CONFIG_ENTRIES: &[&str] = &["store"];
const BPF_LOADER_UPGRADEABLE_SURFACE: &str = "solana.core.bpf_loader_upgradeable";
const BPF_LOADER_UPGRADEABLE_ENTRIES: &[&str] = &["set_authority", "set_authority_checked"];
const LOADER_V4_SURFACE: &str = "solana.core.loader_v4";
const LOADER_V4_ENTRIES: &[&str] = &["transfer_authority"];
const SPL_TOKEN_SURFACE: &str = "spl.token";
const SPL_TOKEN_2022_SURFACE: &str = "spl.token_2022";
const SPL_ELGAMAL_REGISTRY_SURFACE: &str = "spl.elgamal_registry";
const SPL_ELGAMAL_REGISTRY_ENTRIES: &[&str] = &["create_registry", "update_registry"];
const SPL_TOKEN_AUTHORITY_ENTRIES: &[&str] = &[
"set_authority",
"initialize_permanent_delegate",
"initialize_interest_bearing_mint",
"update_interest_bearing_rate",
];
const SPL_TOKEN_MULTISIG_ENTRIES: &[&str] = &["initialize_multisig", "initialize_multisig2"];
const SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES: &[&str] = &[
"initialize_transfer_hook",
"update_transfer_hook_program_id",
"initialize_metadata_pointer",
"update_metadata_pointer",
"initialize_group_pointer",
"update_group_pointer",
"initialize_group_member_pointer",
"update_group_member_pointer",
"initialize_scaled_ui_amount",
"update_scaled_ui_amount_multiplier",
];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ProjectionKind {
SystemAccountOwnership,
@@ -91,7 +57,7 @@ pub struct MtAdminMaterializer;
impl crate::MtMaterializer for crate::MtAdminMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.admin";
return crate::MT_ADMIN_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -99,7 +65,7 @@ impl crate::MtMaterializer for crate::MtAdminMaterializer {
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return ACCEPTED_FAMILIES.contains(&event.event_family)
return crate::MT_ADMIN_ACCEPTED_FAMILIES.contains(&event.event_family)
&& program_matches_surface(event)
&& projection(event.surface_code.0.as_str(), event.event_name.0.as_str()).is_some();
}
@@ -118,17 +84,17 @@ impl crate::MtMaterializer for crate::MtAdminMaterializer {
impl crate::MtApiEventMaterializer for crate::MtAdminMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "materializer.admin".to_string(),
name: crate::MT_ADMIN_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
return crate::MT_ADMIN_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return ACCEPTED_FAMILIES.contains(&observation.event.event_family)
return crate::MT_ADMIN_ACCEPTED_FAMILIES.contains(&observation.event.event_family)
&& program_matches_surface(&observation.event)
&& projection(
observation.event.surface_code.0.as_str(),
@@ -165,7 +131,7 @@ impl crate::MtApiEventMaterializer for crate::MtAdminMaterializer {
return crate::MtApiMaterializerExecutionResult::ignored();
},
};
if !ACCEPTED_FAMILIES.contains(&observation.event.event_family) {
if !crate::MT_ADMIN_ACCEPTED_FAMILIES.contains(&observation.event.event_family) {
return crate::MtApiMaterializerExecutionResult::ignored();
}
if !program_matches_surface(&observation.event) {
@@ -202,7 +168,7 @@ impl crate::MtApiEventMaterializer for crate::MtAdminMaterializer {
.output_key(observation.event.surface_code.0.as_str(), operation.as_str()),
family: crate::MdMaterializedEventFamily::Admin,
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionVersion": crate::MT_ADMIN_PROJECTION_VERSION,
"projectionSemantics": "committed_instruction_admin_event",
"domain": projection.domain(),
"loaderSurface": projection.loader_surface(),
@@ -212,6 +178,10 @@ impl crate::MtApiEventMaterializer for crate::MtAdminMaterializer {
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"transactionSucceeded": true,
"provenance": {
"processorName": crate::MT_ADMIN_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
},
"accounts": accounts,
"parameters": parameters,
"projection": projection_details(
@@ -240,41 +210,51 @@ impl crate::MtApiEventMaterializer for crate::MtAdminMaterializer {
}
fn projection(surface_code: &str, entry_code: &str) -> std::option::Option<ProjectionKind> {
if surface_code == SYSTEM_SURFACE && contains(SYSTEM_ENTRIES, entry_code) {
if surface_code == crate::MT_ADMIN_SYSTEM_SURFACE
&& contains(crate::MT_ADMIN_SYSTEM_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::SystemAccountOwnership);
}
if surface_code == CONFIG_SURFACE && contains(CONFIG_ENTRIES, entry_code) {
if surface_code == crate::MT_ADMIN_CONFIG_SURFACE
&& contains(crate::MT_ADMIN_CONFIG_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::ConfigAccountUpdate);
}
if surface_code == BPF_LOADER_UPGRADEABLE_SURFACE
&& contains(BPF_LOADER_UPGRADEABLE_ENTRIES, entry_code)
if surface_code == crate::MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE
&& contains(crate::MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::ProgramLoaderAuthority(
BPF_LOADER_UPGRADEABLE_SURFACE,
crate::MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE,
));
}
if surface_code == LOADER_V4_SURFACE && contains(LOADER_V4_ENTRIES, entry_code) {
if surface_code == crate::MT_ADMIN_LOADER_V4_SURFACE
&& contains(crate::MT_ADMIN_LOADER_V4_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::ProgramLoaderAuthority(
LOADER_V4_SURFACE,
crate::MT_ADMIN_LOADER_V4_SURFACE,
));
}
if matches!(surface_code, SPL_TOKEN_SURFACE | SPL_TOKEN_2022_SURFACE)
&& contains(SPL_TOKEN_AUTHORITY_ENTRIES, entry_code)
if matches!(
surface_code,
crate::MT_ADMIN_SPL_TOKEN_SURFACE | crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE
) && contains(crate::MT_ADMIN_SPL_TOKEN_AUTHORITY_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::SplTokenAuthority);
}
if matches!(surface_code, SPL_TOKEN_SURFACE | SPL_TOKEN_2022_SURFACE)
&& contains(SPL_TOKEN_MULTISIG_ENTRIES, entry_code)
if matches!(
surface_code,
crate::MT_ADMIN_SPL_TOKEN_SURFACE | crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE
) && contains(crate::MT_ADMIN_SPL_TOKEN_MULTISIG_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::SplTokenMultisig);
}
if surface_code == SPL_TOKEN_2022_SURFACE
&& contains(SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES, entry_code)
if surface_code == crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE
&& contains(crate::MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::SplToken2022ExtensionAdmin);
}
if surface_code == SPL_ELGAMAL_REGISTRY_SURFACE
&& contains(SPL_ELGAMAL_REGISTRY_ENTRIES, entry_code)
if surface_code == crate::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE
&& contains(crate::MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::SplElgamalRegistry);
}
@@ -282,13 +262,13 @@ fn projection(surface_code: &str, entry_code: &str) -> std::option::Option<Proje
}
fn program_matches_surface(event: &crate::MdDecodedProtocolEvent) -> bool {
if event.surface_code.0 == SPL_TOKEN_SURFACE {
if event.surface_code.0 == crate::MT_ADMIN_SPL_TOKEN_SURFACE {
return event.program_id.0 == kb_program_ids::SPL_TOKEN_PROGRAM_ID;
}
if event.surface_code.0 == SPL_TOKEN_2022_SURFACE {
if event.surface_code.0 == crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE {
return event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID;
}
if event.surface_code.0 == SPL_ELGAMAL_REGISTRY_SURFACE {
if event.surface_code.0 == crate::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE {
return event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID;
}
return true;
@@ -537,7 +517,7 @@ mod tests {
fn system_assign_creates_owner_projection() {
let materializer = crate::MtAdminMaterializer;
let observation = observation(
super::SYSTEM_SURFACE,
crate::MT_ADMIN_SYSTEM_SURFACE,
"assign",
false,
serde_json::json!([account("assigned_account", "account111",)]),
@@ -554,7 +534,7 @@ mod tests {
fn seeded_assign_preserves_base_seed_and_owner() {
let materializer = crate::MtAdminMaterializer;
let observation = observation(
super::SYSTEM_SURFACE,
crate::MT_ADMIN_SYSTEM_SURFACE,
"assign_with_seed",
false,
serde_json::json!([
@@ -573,7 +553,7 @@ mod tests {
fn config_store_preserves_bounded_opaque_audit_fields() {
let materializer = crate::MtAdminMaterializer;
let observation = observation(
super::CONFIG_SURFACE,
crate::MT_ADMIN_CONFIG_SURFACE,
"store",
false,
serde_json::json!([
@@ -600,13 +580,17 @@ mod tests {
fn loader_authority_variants_create_exact_admin_outputs() {
let materializer = crate::MtAdminMaterializer;
for (surface, entry, target_role) in [
(super::BPF_LOADER_UPGRADEABLE_SURFACE, "set_authority", "authority_target"),
(
super::BPF_LOADER_UPGRADEABLE_SURFACE,
crate::MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE,
"set_authority",
"authority_target",
),
(
crate::MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE,
"set_authority_checked",
"authority_target",
),
(super::LOADER_V4_SURFACE, "transfer_authority", "program_account"),
(crate::MT_ADMIN_LOADER_V4_SURFACE, "transfer_authority", "program_account"),
] {
let observation = observation(
surface,
@@ -629,7 +613,7 @@ mod tests {
#[test]
fn classic_token_authority_and_multisig_create_distinct_admin_outputs() {
let mut authority = observation(
super::SPL_TOKEN_SURFACE,
crate::MT_ADMIN_SPL_TOKEN_SURFACE,
"set_authority",
false,
serde_json::json!([
@@ -649,7 +633,7 @@ mod tests {
);
let mut multisig = observation(
super::SPL_TOKEN_SURFACE,
crate::MT_ADMIN_SPL_TOKEN_SURFACE,
"initialize_multisig2",
false,
serde_json::json!([
@@ -678,7 +662,7 @@ mod tests {
#[test]
fn token_2022_authority_change_uses_the_admin_owner_once() {
let mut observation = observation(
super::SPL_TOKEN_2022_SURFACE,
crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE,
"set_authority",
false,
serde_json::json!([
@@ -703,7 +687,7 @@ mod tests {
fn failed_admin_mutation_is_refused() {
let materializer = crate::MtAdminMaterializer;
let observation = observation(
super::SYSTEM_SURFACE,
crate::MT_ADMIN_SYSTEM_SURFACE,
"assign",
true,
serde_json::json!([account("assigned_account", "account111",)]),
@@ -754,7 +738,7 @@ mod tests {
fn output_serialization_is_deterministic() {
let materializer = crate::MtAdminMaterializer;
let observation = observation(
super::CONFIG_SURFACE,
crate::MT_ADMIN_CONFIG_SURFACE,
"store",
false,
serde_json::json!([account("config_account", "config111",)]),
@@ -788,7 +772,7 @@ mod tests {
("update_registry", 0_i64, 2_u64, "elgamal_registry_key_updated"),
] {
let mut observation = observation(
super::SPL_ELGAMAL_REGISTRY_SURFACE,
crate::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE,
operation,
false,
serde_json::json!([
@@ -830,7 +814,7 @@ mod tests {
fn elgamal_registry_foreign_program_and_failed_transaction_are_refused() {
let materializer = crate::MtAdminMaterializer;
let mut foreign = observation(
super::SPL_ELGAMAL_REGISTRY_SURFACE,
crate::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE,
"create_registry",
false,
serde_json::json!([]),
@@ -858,7 +842,7 @@ mod tests {
("update_group_member_pointer", "memberAddress", "member111"),
] {
let mut observation = observation(
super::SPL_TOKEN_2022_SURFACE,
crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE,
operation,
false,
serde_json::json!([{"position":0,"accountKey":"mint111"}]),
@@ -881,7 +865,7 @@ mod tests {
#[test]
fn token_2022_extension_admin_rejects_foreign_program_and_failed_transaction() {
let mut observation = observation(
super::SPL_TOKEN_2022_SURFACE,
crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE,
"initialize_metadata_pointer",
false,
serde_json::json!([]),
@@ -937,7 +921,7 @@ pub fn materializer_admin_materialize_token_2022_state_snapshots(
output_key: output_key.clone(),
family: crate::MdMaterializedEventFamily::Admin,
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionVersion": crate::MT_ADMIN_PROJECTION_VERSION,
"projectionSemantics": "authoritative_bounded_admin_state_snapshot",
"domain": "token_2022_extension_admin_state",
"idempotenceKey": output_key,
@@ -1072,7 +1056,7 @@ pub fn materializer_admin_materialize_elgamal_registry_state_snapshot(
output_key: format!("spl_elgamal_registry_state:{registry_account_key}:{slot}"),
family: crate::MdMaterializedEventFamily::Admin,
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionVersion": crate::MT_ADMIN_PROJECTION_VERSION,
"domain": "spl_elgamal_registry_account_state",
"projectionSemantics": "authoritative_exact_account_snapshot",
"idempotenceKey": format!("spl_elgamal_registry:{registry_account_key}:{slot}"),

View File

@@ -1,12 +1,12 @@
// file: kb-lib/src/materializer/api/contracts.rs
// version: 7
// version: 8
//! Backend-neutral decoded observation materialization contracts.
/// Stable identity of one decoded event materializer.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct MtApiMaterializerIdentity {
/// Stable lower snake case processor name.
/// Stable dotted lower-snake-case processor name without the runtime crate prefix.
pub name: std::string::String,
/// Semantic or deterministic implementation version.
pub version: std::string::String,
@@ -177,7 +177,7 @@ impl MtApiMaterializerExecutionResult {
/// Stable decoded observation materializer contract used by the common pipeline.
pub trait MtApiEventMaterializer: std::marker::Send + std::marker::Sync {
/// Returns the stable materializer identity.
/// Returns the stable persisted processor identity for this materializer.
fn identity(&self) -> MtApiMaterializerIdentity;
/// Returns every decoded event family explicitly accepted by this materializer.
fn accepted_families(&self) -> &'static [crate::MdEventFamily];

View File

@@ -1,11 +1,11 @@
// file: kb-lib/src/materializer/api/materializer.rs
// version: 2
// version: 3
//! Materializer API contract used by business materializer crates.
//! Legacy materializer API contract used by business materializer modules.
/// Common contract implemented by all business materializer crates.
/// Common compatibility contract implemented by all business materializer modules.
pub trait MtMaterializer {
/// Returns the stable materializer name.
/// Returns the stable runtime component name with the `kb-lib.materializer.` prefix.
fn materializer_name(&self) -> &'static str;
/// Returns the materializer version.

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/bridge.rs
// version: 3
// version: 4
//! `bridge` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved bridge materializer.
pub use self::core::MtBridgeMaterializer;
pub use self::materializer::MtBridgeMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_BRIDGE_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `bridge` materializer.
pub(crate) use self::constants::MT_BRIDGE_COMPONENT_NAME;
/// Stable processor name for the `bridge` materializer.
pub(crate) use self::constants::MT_BRIDGE_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/bridge/constants.rs
// version: 1
//! Local constants for the bridge materializer.
/// Stable runtime component name for the bridge materializer.
pub(crate) const MT_BRIDGE_COMPONENT_NAME: &str = "kb-lib.materializer.bridge";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_BRIDGE_PROCESSOR_NAME: &str = "materializer.bridge";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_BRIDGE_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/bridge/core.rs
// version: 3
// file: kb-lib/src/materializer/bridge/materializer.rs
// version: 4
//! Reserved bridge materializer.
@@ -9,7 +9,7 @@ pub struct MtBridgeMaterializer;
impl crate::MtMaterializer for crate::MtBridgeMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.bridge";
return crate::MT_BRIDGE_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtBridgeMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
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::Bridge,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtBridgeMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.bridge".to_string(),
name: crate::MT_BRIDGE_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_BRIDGE_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,13 +1,36 @@
// file: kb-lib/src/materializer/compliance.rs
// version: 4
// version: 5
//! `compliance` materializer family.
mod audit;
mod constants;
/// Stable native compliance audit materializer.
pub use self::audit::MtComplianceAuditMaterializer;
/// Crate-visible `MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Stable runtime component name for the native compliance audit materializer.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_COMPONENT_NAME;
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE;
/// Stable processor name for the native compliance audit materializer.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME;
/// Stable projection version for native compliance audit outputs.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_PROJECTION_VERSION;
/// Crate-visible `MT_COMPLIANCE_AUDIT_WRITE_ENTRIES` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_WRITE_ENTRIES;
/// Canonical tracing target for the native compliance audit materializer.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT;
pub(crate) use self::audit::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT;

View File

@@ -1,577 +1,37 @@
// file: kb-lib/src/materializer/compliance/audit.rs
// version: 10
// version: 11
//! Stable compliance-audit projections for committed native bytecode mutations and execution profiles.
//! Native compliance-audit materializer component.
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[crate::MdEventFamily::Audit];
const COMPUTE_BUDGET_SURFACE: &str = "solana.core.compute_budget";
const COMPUTE_BUDGET_ENTRIES: &[&str] = &[
"request_units_deprecated",
"request_heap_frame",
"set_compute_unit_limit",
"set_compute_unit_price",
"set_loaded_accounts_data_size_limit",
];
const BPF_LOADER_DEPRECATED_SURFACE: &str = "solana.core.bpf_loader_deprecated";
const BPF_LOADER_SURFACE: &str = "solana.core.bpf_loader";
const BPF_LOADER_UPGRADEABLE_SURFACE: &str = "solana.core.bpf_loader_upgradeable";
const LOADER_V4_SURFACE: &str = "solana.core.loader_v4";
const WRITE_ENTRIES: &[&str] = &["write"];
const LOADER_V4_COPY_ENTRIES: &[&str] = &["copy"];
mod constants;
mod materializer;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ProjectionKind {
ProgramByteWrite(&'static str),
ProgramByteCopy,
ComputeBudgetProfile,
}
/// Stable native compliance audit materializer.
pub use self::materializer::MtComplianceAuditMaterializer;
impl ProjectionKind {
fn output_key(self, surface_code: &str, operation: &str) -> std::string::String {
return match self {
Self::ProgramByteWrite(_) | Self::ProgramByteCopy => {
format!("program_loader_code:{surface_code}:{operation}:0")
},
Self::ComputeBudgetProfile => "transaction_compute_budget_profile:0".to_string(),
};
}
fn loader_surface(self) -> &'static str {
return match self {
Self::ProgramByteWrite(surface) => surface,
Self::ProgramByteCopy => LOADER_V4_SURFACE,
Self::ComputeBudgetProfile => COMPUTE_BUDGET_SURFACE,
};
}
}
/// Stable compliance-audit materializer for native bytecode mutations and execution profiles.
#[derive(Clone, Debug, Default)]
pub struct MtComplianceAuditMaterializer;
impl crate::MtMaterializer for crate::MtComplianceAuditMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.compliance.audit";
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return event.event_family == crate::MdEventFamily::Audit
&& projection(event.surface_code.0.as_str(), event.event_name.0.as_str()).is_some();
}
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
if !crate::MtMaterializer::accepts_event(self, event) {
return std::result::Result::Ok(std::vec::Vec::new());
}
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtComplianceAuditMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "materializer.compliance.audit".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return observation.event.event_family == crate::MdEventFamily::Audit
&& projection(
observation.event.surface_code.0.as_str(),
observation.event.event_name.0.as_str(),
)
.is_some();
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulOrFailedAudit;
}
fn materialize(
&self,
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
let projection = match projection(
observation.event.surface_code.0.as_str(),
observation.event.event_name.0.as_str(),
) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
action = "materialize_compliance_audit",
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
accepted = false,
"ignore observation outside supported native compliance-audit projections"
);
return crate::MtApiMaterializerExecutionResult::ignored();
},
};
if observation.event.event_family != crate::MdEventFamily::Audit {
return crate::MtApiMaterializerExecutionResult::ignored();
}
if projection != ProjectionKind::ComputeBudgetProfile
&& (observation.transaction_failed || !observation.observation_committed)
{
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
action = "materialize_compliance_audit",
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
committed = observation.observation_committed,
transaction_failed = observation.transaction_failed,
"refuse uncommitted native bytecode mutation observation"
);
return crate::MtApiMaterializerExecutionResult::refused(
"failed_transaction_compliance_audit_refused",
"failed or uncommitted native observations cannot create committed bytecode mutation outputs",
);
}
let accounts = observation
.payload_json
.get("accounts")
.cloned()
.unwrap_or(serde_json::Value::Null);
let parameters = observation
.payload_json
.get("parameters")
.cloned()
.unwrap_or(serde_json::Value::Null);
let operation = observation.event.event_name.0.clone();
if projection == ProjectionKind::ComputeBudgetProfile {
return materialize_compute_budget_profile(observation, parameters, operation);
}
let output = crate::MtApiMaterializedOutput {
output_key: projection
.output_key(observation.event.surface_code.0.as_str(), operation.as_str()),
family: crate::MdMaterializedEventFamily::ComplianceAudit,
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionSemantics": "committed_instruction_compliance_audit_event",
"domain": "program_loader_code_mutation",
"loaderSurface": projection.loader_surface(),
"operation": operation,
"programId": observation.event.program_id.0.clone(),
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"transactionSucceeded": true,
"accounts": accounts,
"parameters": parameters,
"projection": projection_details(projection, &accounts, &parameters),
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
action = "materialize_compliance_audit",
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
loader_surface = projection.loader_surface(),
output_count = 1_usize,
"materialize committed native bytecode mutation observation"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
}
fn materialize_compute_budget_profile(
observation: &crate::DcApiDecodedObservation,
parameters: serde_json::Value,
operation: std::string::String,
) -> crate::MtApiMaterializerExecutionResult {
let profile = parameters.get("transactionProfile").cloned().unwrap_or(serde_json::Value::Null);
if !compute_budget_profile_emits(&profile) {
return crate::MtApiMaterializerExecutionResult::ignored();
}
let output = crate::MtApiMaterializedOutput {
output_key: "transaction_compute_budget_profile:0".to_string(),
family: crate::MdMaterializedEventFamily::ComplianceAudit,
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionSemantics": "transaction_compute_budget_profile",
"domain": "transaction_compute_budget_profile",
"operation": operation,
"programId": observation.event.program_id.0.clone(),
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"transactionSucceeded": !observation.transaction_failed,
"profile": profile,
"profileSource": {
"surfaceCode": observation.event.surface_code.0.clone(),
"entryCode": observation.event.event_name.0.clone(),
"eventKey": observation.event_key.clone()
},
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
action = "materialize_compute_budget_profile",
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
output_count = 1_usize,
transaction_failed = observation.transaction_failed,
"materialize native Compute Budget transaction profile"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
fn compute_budget_profile_emits(profile: &serde_json::Value) -> bool {
return profile
.get("currentInstructionIsProfileEmitter")
.and_then(serde_json::Value::as_bool)
== std::option::Option::Some(true);
}
fn projection(surface_code: &str, entry_code: &str) -> std::option::Option<ProjectionKind> {
if surface_code == COMPUTE_BUDGET_SURFACE && contains(COMPUTE_BUDGET_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::ComputeBudgetProfile);
}
if contains(WRITE_ENTRIES, entry_code) {
for supported in [
BPF_LOADER_DEPRECATED_SURFACE,
BPF_LOADER_SURFACE,
BPF_LOADER_UPGRADEABLE_SURFACE,
LOADER_V4_SURFACE,
] {
if surface_code == supported {
return std::option::Option::Some(ProjectionKind::ProgramByteWrite(supported));
}
}
}
if surface_code == LOADER_V4_SURFACE && contains(LOADER_V4_COPY_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::ProgramByteCopy);
}
return std::option::Option::None;
}
fn projection_details(
projection: ProjectionKind,
accounts: &serde_json::Value,
parameters: &serde_json::Value,
) -> serde_json::Value {
return match projection {
ProjectionKind::ProgramByteWrite(_) => serde_json::json!({
"targetAccount": first_account_key_for_roles(
accounts,
&["program_account", "buffer_account"],
),
"authorityAccount": first_account_key_for_roles(
accounts,
&["authority", "buffer_authority"],
),
"offset": parameters.get("offset").cloned().unwrap_or(serde_json::Value::Null),
"byteLength": parameters.get("byteLength").cloned().unwrap_or(serde_json::Value::Null),
"bytesSha256": parameters.get("bytesSha256").cloned().unwrap_or(serde_json::Value::Null),
"bytesPrefixHex": parameters.get("bytesPrefixHex").cloned().unwrap_or(serde_json::Value::Null),
"stateTransition": "program_bytes_written",
"codeBytesMaterialized": false,
"transactionFinalAccountStateCaptured": false,
}),
ProjectionKind::ProgramByteCopy => serde_json::json!({
"targetAccount": account_key_for_role(accounts, "program_account"),
"authorityAccount": account_key_for_role(accounts, "authority"),
"sourceProgramAccount": account_key_for_role(accounts, "source_program_account"),
"destinationOffset": parameters.get("destinationOffset").cloned().unwrap_or(serde_json::Value::Null),
"sourceOffset": parameters.get("sourceOffset").cloned().unwrap_or(serde_json::Value::Null),
"byteLength": parameters.get("length").cloned().unwrap_or(serde_json::Value::Null),
"stateTransition": "program_bytes_copied",
"codeBytesMaterialized": false,
"transactionFinalAccountStateCaptured": false,
}),
ProjectionKind::ComputeBudgetProfile => serde_json::Value::Null,
};
}
fn first_account_key_for_roles(accounts: &serde_json::Value, roles: &[&str]) -> serde_json::Value {
for role in roles {
let value = account_key_for_role(accounts, role);
if !value.is_null() {
return value;
}
}
return serde_json::Value::Null;
}
fn account_key_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value {
let account_key = accounts.as_array().and_then(|values| {
return values.iter().find_map(|value| {
if value.get("role").and_then(serde_json::Value::as_str)
!= std::option::Option::Some(role)
{
return std::option::Option::None;
}
return value
.get("accountKey")
.and_then(serde_json::Value::as_str)
.map(|account_key| return account_key.to_string());
});
});
return match account_key {
std::option::Option::Some(value) => serde_json::Value::String(value),
std::option::Option::None => serde_json::Value::Null,
};
}
fn contains(entries: &[&str], entry_code: &str) -> bool {
return entries.iter().any(|entry| return *entry == entry_code);
}
#[cfg(test)]
mod tests {
fn observation(
surface_code: &str,
entry_code: &str,
transaction_failed: bool,
accounts: serde_json::Value,
parameters: serde_json::Value,
) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: format!("{entry_code}:0"),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("0".to_string()),
program_id: crate::MdProgramId("program-id".to_string()),
protocol_code: crate::MdProtocolCode("solana.core".to_string()),
surface_code: crate::MdSurfaceCode(surface_code.to_string()),
event_code: crate::MdEventCode(format!("{surface_code}.{entry_code}")),
event_name: crate::MdEventName(entry_code.to_string()),
event_family: crate::MdEventFamily::Audit,
source_kind: crate::MdEventSourceKind::Instruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({"accounts": accounts, "parameters": parameters}),
transaction_failed,
transaction_error: if transaction_failed {
std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]}))
} else {
std::option::Option::None
},
observation_committed: !transaction_failed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["fixture".to_string()],
},
};
}
fn account(role: &str, account_key: &str) -> serde_json::Value {
return serde_json::json!({"role": role, "accountKey": account_key});
}
#[test]
fn loader_write_variants_create_bounded_code_audits() {
let materializer = crate::MtComplianceAuditMaterializer;
for (surface, target_role, authority_role) in [
(super::BPF_LOADER_DEPRECATED_SURFACE, "program_account", "missing"),
(super::BPF_LOADER_SURFACE, "program_account", "missing"),
(super::BPF_LOADER_UPGRADEABLE_SURFACE, "buffer_account", "buffer_authority"),
(super::LOADER_V4_SURFACE, "program_account", "authority"),
] {
let observation = observation(
surface,
"write",
false,
serde_json::json!([
account(target_role, "target111"),
account(authority_role, "authority111"),
]),
serde_json::json!({
"offset": 7,
"byteLength": 32,
"bytesSha256": "abc",
"bytesPrefixHex": "0102",
}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs[0].family, crate::MdMaterializedEventFamily::ComplianceAudit);
assert_eq!(result.outputs[0].payload_json["projection"]["targetAccount"], "target111");
assert_eq!(result.outputs[0].payload_json["projection"]["bytesSha256"], "abc");
assert_eq!(
result.outputs[0].payload_json["projection"]["codeBytesMaterialized"],
false
);
}
}
#[test]
fn loader_v4_copy_preserves_source_and_bounded_ranges() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
super::LOADER_V4_SURFACE,
"copy",
false,
serde_json::json!([
account("program_account", "target111"),
account("authority", "authority111"),
account("source_program_account", "source111"),
]),
serde_json::json!({"destinationOffset": 8, "sourceOffset": 4, "length": 16}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(
result.outputs[0].output_key,
"program_loader_code:solana.core.loader_v4:copy:0"
);
assert_eq!(
result.outputs[0].payload_json["projection"]["sourceProgramAccount"],
"source111"
);
assert_eq!(result.outputs[0].payload_json["projection"]["byteLength"], 16);
}
#[test]
fn compute_budget_profile_is_emitted_only_by_profile_emitter() {
let materializer = crate::MtComplianceAuditMaterializer;
let emitter = observation(
super::COMPUTE_BUDGET_SURFACE,
"set_compute_unit_limit",
false,
serde_json::json!([]),
serde_json::json!({
"transactionProfile": {
"currentInstructionIsProfileEmitter": true,
"profileEmitterInstructionPath": "0",
"computeBudgetInstructionCount": 2,
"effectiveValues": {
"set_compute_unit_limit": {
"value": 200000,
"sourceInstructionPath": "0"
}
}
}
}),
);
let emitted = crate::MtApiEventMaterializer::materialize(&materializer, &emitter);
assert_eq!(emitted.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(emitted.outputs[0].output_key, "transaction_compute_budget_profile:0");
assert_eq!(emitted.outputs[0].family, crate::MdMaterializedEventFamily::ComplianceAudit);
assert_eq!(emitted.outputs[0].payload_json["profile"]["computeBudgetInstructionCount"], 2);
let non_emitter = observation(
super::COMPUTE_BUDGET_SURFACE,
"set_compute_unit_price",
false,
serde_json::json!([]),
serde_json::json!({
"transactionProfile": {
"currentInstructionIsProfileEmitter": false,
"profileEmitterInstructionPath": "0"
}
}),
);
let ignored = crate::MtApiEventMaterializer::materialize(&materializer, &non_emitter);
assert_eq!(ignored.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
#[test]
fn failed_compute_budget_profile_remains_audit_materializable() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
super::COMPUTE_BUDGET_SURFACE,
"set_compute_unit_price",
true,
serde_json::json!([]),
serde_json::json!({
"transactionProfile": {
"currentInstructionIsProfileEmitter": true,
"profileEmitterInstructionPath": "0",
"computeBudgetInstructionCount": 1
}
}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs[0].payload_json["transactionSucceeded"], false);
}
#[test]
fn failed_code_mutation_is_refused() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
super::LOADER_V4_SURFACE,
"write",
true,
serde_json::json!([account("program_account", "target111")]),
serde_json::json!({"offset": 0, "byteLength": 1, "bytesSha256": "abc"}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
assert!(result.outputs.is_empty());
}
#[test]
fn unrelated_audit_observation_is_ignored() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
"other_surface",
"write",
false,
serde_json::json!([]),
serde_json::json!({}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
#[test]
fn output_serialization_is_deterministic() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
super::BPF_LOADER_UPGRADEABLE_SURFACE,
"write",
false,
serde_json::json!([
account("buffer_account", "buffer111"),
account("buffer_authority", "authority111"),
]),
serde_json::json!({
"offset": 0,
"byteLength": 2,
"bytesSha256": "abc",
"bytesPrefixHex": "0102",
}),
);
let first = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
let second = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
let first_json = match serde_json::to_string(&first) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("first compliance serialization failed: {error}")
},
};
let second_json = match serde_json::to_string(&second) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("second compliance serialization failed: {error}")
},
};
assert_eq!(first_json, second_json);
}
}
/// Crate-visible `MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Stable runtime component name for the native compliance audit materializer.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_COMPONENT_NAME;
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE;
/// Stable processor name for the native compliance audit materializer.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME;
/// Stable projection version for native compliance audit outputs.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_PROJECTION_VERSION;
/// Crate-visible `MT_COMPLIANCE_AUDIT_WRITE_ENTRIES` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_WRITE_ENTRIES;
/// Canonical tracing target for the native compliance audit materializer.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT;

View File

@@ -0,0 +1,42 @@
// file: kb-lib/src/materializer/compliance/audit/constants.rs
// version: 4
//! Local constants for the compliance audit materializer.
/// Stable runtime component name for the compliance audit materializer.
pub(crate) const MT_COMPLIANCE_AUDIT_COMPONENT_NAME: &str = "kb-lib.materializer.compliance.audit";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_COMPLIANCE_AUDIT_PROCESSOR_NAME: &str = "materializer.compliance.audit";
/// Stable projection contract version for the compliance audit materializer.
pub(crate) const MT_COMPLIANCE_AUDIT_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for the compliance audit materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT: &str =
"kb-lib.materializer.compliance.audit";
/// Component contract for `accepted_families`.
pub(crate) const MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
&[crate::MdEventFamily::Audit];
/// Component contract for `compute_budget_surface`.
pub(crate) const MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE: &str = "solana.core.compute_budget";
/// Component contract for `compute_budget_entries`.
pub(crate) const MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES: &[&str] = &[
"request_units_deprecated",
"request_heap_frame",
"set_compute_unit_limit",
"set_compute_unit_price",
"set_loaded_accounts_data_size_limit",
];
/// Component contract for `bpf_loader_deprecated_surface`.
pub(crate) const MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE: &str =
"solana.core.bpf_loader_deprecated";
/// Component contract for `bpf_loader_surface`.
pub(crate) const MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE: &str = "solana.core.bpf_loader";
/// Component contract for `bpf_loader_upgradeable_surface`.
pub(crate) const MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE: &str =
"solana.core.bpf_loader_upgradeable";
/// Component contract for `loader_v4_surface`.
pub(crate) const MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE: &str = "solana.core.loader_v4";
/// Component contract for `write_entries`.
pub(crate) const MT_COMPLIANCE_AUDIT_WRITE_ENTRIES: &[&str] = &["write"];
/// Component contract for `loader_v4_copy_entries`.
pub(crate) const MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES: &[&str] = &["copy"];

View File

@@ -0,0 +1,581 @@
// file: kb-lib/src/materializer/compliance/audit/materializer.rs
// version: 12
//! Stable compliance-audit projections for committed native bytecode mutations and execution profiles.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ProjectionKind {
ProgramByteWrite(&'static str),
ProgramByteCopy,
ComputeBudgetProfile,
}
impl ProjectionKind {
fn output_key(self, surface_code: &str, operation: &str) -> std::string::String {
return match self {
Self::ProgramByteWrite(_) | Self::ProgramByteCopy => {
format!("program_loader_code:{surface_code}:{operation}:0")
},
Self::ComputeBudgetProfile => "transaction_compute_budget_profile:0".to_string(),
};
}
fn loader_surface(self) -> &'static str {
return match self {
Self::ProgramByteWrite(surface) => surface,
Self::ProgramByteCopy => crate::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE,
Self::ComputeBudgetProfile => crate::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE,
};
}
}
/// Stable compliance-audit materializer for native bytecode mutations and execution profiles.
#[derive(Clone, Debug, Default)]
pub struct MtComplianceAuditMaterializer;
impl crate::MtMaterializer for crate::MtComplianceAuditMaterializer {
fn materializer_name(&self) -> &'static str {
return crate::MT_COMPLIANCE_AUDIT_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::Audit
&& projection(event.surface_code.0.as_str(), event.event_name.0.as_str()).is_some();
}
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
if !crate::MtMaterializer::accepts_event(self, event) {
return std::result::Result::Ok(std::vec::Vec::new());
}
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtComplianceAuditMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return crate::MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return observation.event.event_family == crate::MdEventFamily::Audit
&& projection(
observation.event.surface_code.0.as_str(),
observation.event.event_name.0.as_str(),
)
.is_some();
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulOrFailedAudit;
}
fn materialize(
&self,
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
let projection = match projection(
observation.event.surface_code.0.as_str(),
observation.event.event_name.0.as_str(),
) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
action = "materialize_compliance_audit",
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
accepted = false,
"ignore observation outside supported native compliance-audit projections"
);
return crate::MtApiMaterializerExecutionResult::ignored();
},
};
if observation.event.event_family != crate::MdEventFamily::Audit {
return crate::MtApiMaterializerExecutionResult::ignored();
}
if projection != ProjectionKind::ComputeBudgetProfile
&& (observation.transaction_failed || !observation.observation_committed)
{
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
action = "materialize_compliance_audit",
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
committed = observation.observation_committed,
transaction_failed = observation.transaction_failed,
"refuse uncommitted native bytecode mutation observation"
);
return crate::MtApiMaterializerExecutionResult::refused(
"failed_transaction_compliance_audit_refused",
"failed or uncommitted native observations cannot create committed bytecode mutation outputs",
);
}
let accounts = observation
.payload_json
.get("accounts")
.cloned()
.unwrap_or(serde_json::Value::Null);
let parameters = observation
.payload_json
.get("parameters")
.cloned()
.unwrap_or(serde_json::Value::Null);
let operation = observation.event.event_name.0.clone();
if projection == ProjectionKind::ComputeBudgetProfile {
return materialize_compute_budget_profile(observation, parameters, operation);
}
let output = crate::MtApiMaterializedOutput {
output_key: projection
.output_key(observation.event.surface_code.0.as_str(), operation.as_str()),
family: crate::MdMaterializedEventFamily::ComplianceAudit,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_COMPLIANCE_AUDIT_PROJECTION_VERSION,
"projectionSemantics": "committed_instruction_compliance_audit_event",
"domain": "program_loader_code_mutation",
"loaderSurface": projection.loader_surface(),
"operation": operation,
"programId": observation.event.program_id.0.clone(),
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"transactionSucceeded": true,
"provenance": {
"processorName": crate::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
},
"accounts": accounts,
"parameters": parameters,
"projection": projection_details(projection, &accounts, &parameters),
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
action = "materialize_compliance_audit",
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
loader_surface = projection.loader_surface(),
output_count = 1_usize,
"materialize committed native bytecode mutation observation"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
}
fn materialize_compute_budget_profile(
observation: &crate::DcApiDecodedObservation,
parameters: serde_json::Value,
operation: std::string::String,
) -> crate::MtApiMaterializerExecutionResult {
let profile = parameters.get("transactionProfile").cloned().unwrap_or(serde_json::Value::Null);
if !compute_budget_profile_emits(&profile) {
return crate::MtApiMaterializerExecutionResult::ignored();
}
let output = crate::MtApiMaterializedOutput {
output_key: "transaction_compute_budget_profile:0".to_string(),
family: crate::MdMaterializedEventFamily::ComplianceAudit,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_COMPLIANCE_AUDIT_PROJECTION_VERSION,
"projectionSemantics": "transaction_compute_budget_profile",
"domain": "transaction_compute_budget_profile",
"operation": operation,
"programId": observation.event.program_id.0.clone(),
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"transactionSucceeded": !observation.transaction_failed,
"provenance": {
"processorName": crate::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
},
"profile": profile,
"profileSource": {
"surfaceCode": observation.event.surface_code.0.clone(),
"entryCode": observation.event.event_name.0.clone(),
"eventKey": observation.event_key.clone()
},
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
action = "materialize_compute_budget_profile",
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
output_count = 1_usize,
transaction_failed = observation.transaction_failed,
"materialize native Compute Budget transaction profile"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
fn compute_budget_profile_emits(profile: &serde_json::Value) -> bool {
return profile
.get("currentInstructionIsProfileEmitter")
.and_then(serde_json::Value::as_bool)
== std::option::Option::Some(true);
}
fn projection(surface_code: &str, entry_code: &str) -> std::option::Option<ProjectionKind> {
if surface_code == crate::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE
&& contains(crate::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::ComputeBudgetProfile);
}
if contains(crate::MT_COMPLIANCE_AUDIT_WRITE_ENTRIES, entry_code) {
for supported in [
crate::MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE,
crate::MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE,
crate::MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE,
crate::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE,
] {
if surface_code == supported {
return std::option::Option::Some(ProjectionKind::ProgramByteWrite(supported));
}
}
}
if surface_code == crate::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE
&& contains(crate::MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::ProgramByteCopy);
}
return std::option::Option::None;
}
fn projection_details(
projection: ProjectionKind,
accounts: &serde_json::Value,
parameters: &serde_json::Value,
) -> serde_json::Value {
return match projection {
ProjectionKind::ProgramByteWrite(_) => serde_json::json!({
"targetAccount": first_account_key_for_roles(
accounts,
&["program_account", "buffer_account"],
),
"authorityAccount": first_account_key_for_roles(
accounts,
&["authority", "buffer_authority"],
),
"offset": parameters.get("offset").cloned().unwrap_or(serde_json::Value::Null),
"byteLength": parameters.get("byteLength").cloned().unwrap_or(serde_json::Value::Null),
"bytesSha256": parameters.get("bytesSha256").cloned().unwrap_or(serde_json::Value::Null),
"bytesPrefixHex": parameters.get("bytesPrefixHex").cloned().unwrap_or(serde_json::Value::Null),
"stateTransition": "program_bytes_written",
"codeBytesMaterialized": false,
"transactionFinalAccountStateCaptured": false,
}),
ProjectionKind::ProgramByteCopy => serde_json::json!({
"targetAccount": account_key_for_role(accounts, "program_account"),
"authorityAccount": account_key_for_role(accounts, "authority"),
"sourceProgramAccount": account_key_for_role(accounts, "source_program_account"),
"destinationOffset": parameters.get("destinationOffset").cloned().unwrap_or(serde_json::Value::Null),
"sourceOffset": parameters.get("sourceOffset").cloned().unwrap_or(serde_json::Value::Null),
"byteLength": parameters.get("length").cloned().unwrap_or(serde_json::Value::Null),
"stateTransition": "program_bytes_copied",
"codeBytesMaterialized": false,
"transactionFinalAccountStateCaptured": false,
}),
ProjectionKind::ComputeBudgetProfile => serde_json::Value::Null,
};
}
fn first_account_key_for_roles(accounts: &serde_json::Value, roles: &[&str]) -> serde_json::Value {
for role in roles {
let value = account_key_for_role(accounts, role);
if !value.is_null() {
return value;
}
}
return serde_json::Value::Null;
}
fn account_key_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value {
let account_key = accounts.as_array().and_then(|values| {
return values.iter().find_map(|value| {
if value.get("role").and_then(serde_json::Value::as_str)
!= std::option::Option::Some(role)
{
return std::option::Option::None;
}
return value
.get("accountKey")
.and_then(serde_json::Value::as_str)
.map(|account_key| return account_key.to_string());
});
});
return match account_key {
std::option::Option::Some(value) => serde_json::Value::String(value),
std::option::Option::None => serde_json::Value::Null,
};
}
fn contains(entries: &[&str], entry_code: &str) -> bool {
return entries.iter().any(|entry| return *entry == entry_code);
}
#[cfg(test)]
mod tests {
fn observation(
surface_code: &str,
entry_code: &str,
transaction_failed: bool,
accounts: serde_json::Value,
parameters: serde_json::Value,
) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: format!("{entry_code}:0"),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("0".to_string()),
program_id: crate::MdProgramId("program-id".to_string()),
protocol_code: crate::MdProtocolCode("solana.core".to_string()),
surface_code: crate::MdSurfaceCode(surface_code.to_string()),
event_code: crate::MdEventCode(format!("{surface_code}.{entry_code}")),
event_name: crate::MdEventName(entry_code.to_string()),
event_family: crate::MdEventFamily::Audit,
source_kind: crate::MdEventSourceKind::Instruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({"accounts": accounts, "parameters": parameters}),
transaction_failed,
transaction_error: if transaction_failed {
std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]}))
} else {
std::option::Option::None
},
observation_committed: !transaction_failed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["fixture".to_string()],
},
};
}
fn account(role: &str, account_key: &str) -> serde_json::Value {
return serde_json::json!({"role": role, "accountKey": account_key});
}
#[test]
fn loader_write_variants_create_bounded_code_audits() {
let materializer = crate::MtComplianceAuditMaterializer;
for (surface, target_role, authority_role) in [
(
crate::MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE,
"program_account",
"missing",
),
(crate::MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE, "program_account", "missing"),
(
crate::MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE,
"buffer_account",
"buffer_authority",
),
(crate::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE, "program_account", "authority"),
] {
let observation = observation(
surface,
"write",
false,
serde_json::json!([
account(target_role, "target111"),
account(authority_role, "authority111"),
]),
serde_json::json!({
"offset": 7,
"byteLength": 32,
"bytesSha256": "abc",
"bytesPrefixHex": "0102",
}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs[0].family, crate::MdMaterializedEventFamily::ComplianceAudit);
assert_eq!(result.outputs[0].payload_json["projection"]["targetAccount"], "target111");
assert_eq!(result.outputs[0].payload_json["projection"]["bytesSha256"], "abc");
assert_eq!(
result.outputs[0].payload_json["projection"]["codeBytesMaterialized"],
false
);
}
}
#[test]
fn loader_v4_copy_preserves_source_and_bounded_ranges() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
crate::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE,
"copy",
false,
serde_json::json!([
account("program_account", "target111"),
account("authority", "authority111"),
account("source_program_account", "source111"),
]),
serde_json::json!({"destinationOffset": 8, "sourceOffset": 4, "length": 16}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(
result.outputs[0].output_key,
"program_loader_code:solana.core.loader_v4:copy:0"
);
assert_eq!(
result.outputs[0].payload_json["projection"]["sourceProgramAccount"],
"source111"
);
assert_eq!(result.outputs[0].payload_json["projection"]["byteLength"], 16);
}
#[test]
fn compute_budget_profile_is_emitted_only_by_profile_emitter() {
let materializer = crate::MtComplianceAuditMaterializer;
let emitter = observation(
crate::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE,
"set_compute_unit_limit",
false,
serde_json::json!([]),
serde_json::json!({
"transactionProfile": {
"currentInstructionIsProfileEmitter": true,
"profileEmitterInstructionPath": "0",
"computeBudgetInstructionCount": 2,
"effectiveValues": {
"set_compute_unit_limit": {
"value": 200000,
"sourceInstructionPath": "0"
}
}
}
}),
);
let emitted = crate::MtApiEventMaterializer::materialize(&materializer, &emitter);
assert_eq!(emitted.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(emitted.outputs[0].output_key, "transaction_compute_budget_profile:0");
assert_eq!(emitted.outputs[0].family, crate::MdMaterializedEventFamily::ComplianceAudit);
assert_eq!(emitted.outputs[0].payload_json["profile"]["computeBudgetInstructionCount"], 2);
let non_emitter = observation(
crate::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE,
"set_compute_unit_price",
false,
serde_json::json!([]),
serde_json::json!({
"transactionProfile": {
"currentInstructionIsProfileEmitter": false,
"profileEmitterInstructionPath": "0"
}
}),
);
let ignored = crate::MtApiEventMaterializer::materialize(&materializer, &non_emitter);
assert_eq!(ignored.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
#[test]
fn failed_compute_budget_profile_remains_audit_materializable() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
crate::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE,
"set_compute_unit_price",
true,
serde_json::json!([]),
serde_json::json!({
"transactionProfile": {
"currentInstructionIsProfileEmitter": true,
"profileEmitterInstructionPath": "0",
"computeBudgetInstructionCount": 1
}
}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs[0].payload_json["transactionSucceeded"], false);
}
#[test]
fn failed_code_mutation_is_refused() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
crate::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE,
"write",
true,
serde_json::json!([account("program_account", "target111")]),
serde_json::json!({"offset": 0, "byteLength": 1, "bytesSha256": "abc"}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
assert!(result.outputs.is_empty());
}
#[test]
fn unrelated_audit_observation_is_ignored() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
"other_surface",
"write",
false,
serde_json::json!([]),
serde_json::json!({}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
#[test]
fn output_serialization_is_deterministic() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
crate::MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE,
"write",
false,
serde_json::json!([
account("buffer_account", "buffer111"),
account("buffer_authority", "authority111"),
]),
serde_json::json!({
"offset": 0,
"byteLength": 2,
"bytesSha256": "abc",
"bytesPrefixHex": "0102",
}),
);
let first = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
let second = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
let first_json = match serde_json::to_string(&first) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("first compliance serialization failed: {error}")
},
};
let second_json = match serde_json::to_string(&second) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("second compliance serialization failed: {error}")
},
};
assert_eq!(first_json, second_json);
}
}

View File

@@ -1,8 +0,0 @@
// file: kb-lib/src/materializer/compliance/constants.rs
// version: 2
//! Local constants for the native compliance audit materializer.
/// Canonical tracing target for this component.
pub(crate) const TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT: &str =
"kb-lib.materializer.compliance";

View File

@@ -1,11 +1,27 @@
// file: kb-lib/src/materializer/fees.rs
// version: 4
// version: 5
//! `fees` materializer family.
mod core;
mod constants;
mod materializer;
/// Fee materializer for committed Token-2022 fee observations.
pub use self::core::MtFeesMaterializer;
pub use self::materializer::MtFeesMaterializer;
/// Materializes authoritative Token-2022 fee extension snapshots.
pub use self::core::materializer_fees_materialize_token_2022_state_snapshots;
pub use self::materializer::materializer_fees_materialize_token_2022_state_snapshots;
/// Crate-visible `MT_FEES_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::constants::MT_FEES_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `fees` materializer.
pub(crate) use self::constants::MT_FEES_COMPONENT_NAME;
/// Stable processor name for the `fees` materializer.
pub(crate) use self::constants::MT_FEES_PROCESSOR_NAME;
/// Stable projection version for the `fees` materializer.
pub(crate) use self::constants::MT_FEES_PROJECTION_VERSION;
/// Crate-visible `MT_FEES_SPL_TOKEN_2022_SURFACE` component constant.
pub(crate) use self::constants::MT_FEES_SPL_TOKEN_2022_SURFACE;
/// Crate-visible `MT_FEES_TOKEN_2022_FEE_ENTRIES` component constant.
pub(crate) use self::constants::MT_FEES_TOKEN_2022_FEE_ENTRIES;
/// Canonical tracing target for the `fees` materializer.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_FEES;

View File

@@ -0,0 +1,34 @@
// file: kb-lib/src/materializer/fees/constants.rs
// version: 2
//! Local constants for the fees materializer.
/// Stable runtime component name for the fees materializer.
pub(crate) const MT_FEES_COMPONENT_NAME: &str = "kb-lib.materializer.fees";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_FEES_PROCESSOR_NAME: &str = "materializer.fees";
/// Stable projection contract version for the fees materializer.
pub(crate) const MT_FEES_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for the fees materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_FEES: &str = "kb-lib.materializer.fees";
/// Component contract for `accepted_families`.
pub(crate) const MT_FEES_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[crate::MdEventFamily::Fee];
/// Component contract for `spl_token_2022_surface`.
pub(crate) const MT_FEES_SPL_TOKEN_2022_SURFACE: &str = "spl.token_2022";
/// Component contract for `token_2022_fee_entries`.
pub(crate) const MT_FEES_TOKEN_2022_FEE_ENTRIES: &[&str] = &[
"initialize_transfer_fee_config",
"set_transfer_fee",
"withdraw_withheld_tokens_from_mint",
"withdraw_withheld_tokens_from_accounts",
"harvest_withheld_tokens_to_mint",
"transfer_checked_with_fee",
"initialize_confidential_transfer_fee_config",
"withdraw_confidential_withheld_tokens_from_mint",
"withdraw_confidential_withheld_tokens_from_accounts",
"harvest_confidential_withheld_tokens_to_mint",
"enable_confidential_harvest_to_mint",
"disable_confidential_harvest_to_mint",
"transfer_confidential_tokens_with_fee",
];

View File

@@ -1,33 +1,15 @@
// file: kb-lib/src/materializer/fees/core.rs
// version: 5
// file: kb-lib/src/materializer/fees/materializer.rs
// version: 8
//! Stable fee projections derived from exact committed Token-2022 observations.
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[crate::MdEventFamily::Fee];
const SPL_TOKEN_2022_SURFACE: &str = "spl.token_2022";
const TOKEN_2022_FEE_ENTRIES: &[&str] = &[
"initialize_transfer_fee_config",
"set_transfer_fee",
"withdraw_withheld_tokens_from_mint",
"withdraw_withheld_tokens_from_accounts",
"harvest_withheld_tokens_to_mint",
"transfer_checked_with_fee",
"initialize_confidential_transfer_fee_config",
"withdraw_confidential_withheld_tokens_from_mint",
"withdraw_confidential_withheld_tokens_from_accounts",
"harvest_confidential_withheld_tokens_to_mint",
"enable_confidential_harvest_to_mint",
"disable_confidential_harvest_to_mint",
"transfer_confidential_tokens_with_fee",
];
/// Fee materializer for committed Token-2022 fee instructions.
#[derive(Clone, Debug, Default)]
pub struct MtFeesMaterializer;
impl crate::MtMaterializer for crate::MtFeesMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.fees";
return crate::MT_FEES_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
@@ -46,12 +28,12 @@ impl crate::MtMaterializer for crate::MtFeesMaterializer {
impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "materializer.fees".to_string(),
name: crate::MT_FEES_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
return crate::MT_FEES_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return accepts_event(&observation.event);
@@ -67,9 +49,35 @@ impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
if !accepts_event(&observation.event) {
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_FEES,
action = "materialize_fee_observation",
component_name = crate::MT_FEES_COMPONENT_NAME,
processor_name = crate::MT_FEES_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
accepted = false,
status = "ignored",
"ignore observation outside supported Token-2022 fee projections"
);
return crate::MtApiMaterializerExecutionResult::ignored();
}
if observation.transaction_failed || !observation.observation_committed {
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_FEES,
action = "materialize_fee_observation",
component_name = crate::MT_FEES_COMPONENT_NAME,
processor_name = crate::MT_FEES_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
committed = observation.observation_committed,
transaction_failed = observation.transaction_failed,
status = "refused",
error_code = "failed_transaction_fee_refused",
"refuse failed or uncommitted Token-2022 fee observation"
);
return crate::MtApiMaterializerExecutionResult::refused(
"failed_transaction_fee_refused",
"failed or uncommitted Token-2022 observations cannot create fee outputs",
@@ -90,7 +98,7 @@ impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
output_key: format!("token_2022_fee:{operation}:0"),
family: crate::MdMaterializedEventFamily::Fee,
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionVersion": crate::MT_FEES_PROJECTION_VERSION,
"projectionSemantics": "committed_instruction_fee_event",
"domain": fee_domain(observation.event.event_name.0.as_str()),
"operation": operation,
@@ -99,6 +107,10 @@ impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"transactionSucceeded": true,
"provenance": {
"processorName": crate::MT_FEES_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
},
"accounts": accounts,
"parameters": parameters,
"stateReadRequired": state_read_required(
@@ -107,6 +119,21 @@ impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
"confidentialValuesDecrypted": false,
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_FEES,
action = "materialize_fee_observation",
component_name = crate::MT_FEES_COMPONENT_NAME,
processor_name = crate::MT_FEES_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
signature = %observation.event.signature.0.as_str(),
slot = observation.event.slot.0,
instruction_path = %observation.event.instruction_path.0.as_str(),
output_count = 1_usize,
status = "inserted",
"materialize committed Token-2022 fee observation"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
@@ -117,9 +144,9 @@ impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
fn accepts_event(event: &crate::MdDecodedProtocolEvent) -> bool {
return event.event_family == crate::MdEventFamily::Fee
&& event.surface_code.0 == SPL_TOKEN_2022_SURFACE
&& event.surface_code.0 == crate::MT_FEES_SPL_TOKEN_2022_SURFACE
&& event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
&& contains(TOKEN_2022_FEE_ENTRIES, event.event_name.0.as_str());
&& contains(crate::MT_FEES_TOKEN_2022_FEE_ENTRIES, event.event_name.0.as_str());
}
fn fee_domain(operation: &str) -> &'static str {
@@ -195,7 +222,7 @@ pub fn materializer_fees_materialize_token_2022_state_snapshots(
),
family: crate::MdMaterializedEventFamily::Fee,
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionVersion": crate::MT_FEES_PROJECTION_VERSION,
"projectionSemantics": "authoritative_bounded_fee_state_snapshot",
"domain": if confidential {
"token_2022_confidential_transfer_fee_state"
@@ -218,13 +245,27 @@ pub fn materializer_fees_materialize_token_2022_state_snapshots(
"confidentialValuesDecrypted": false,
"finalAccountStateCaptured": true,
"provenance": {
"processorName": "materializer.fees",
"processorName": crate::MT_FEES_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"source": "bounded_token_2022_state_parser"
}
}),
});
}
let status = if outputs.is_empty() { "ignored" } else { "inserted" };
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_FEES,
action = "materialize_token_2022_fee_state_snapshots",
component_name = crate::MT_FEES_COMPONENT_NAME,
processor_name = crate::MT_FEES_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
account_key = %account_key,
slot,
state_kind,
output_count = outputs.len(),
status,
"materialize bounded Token-2022 fee state snapshots"
);
return std::result::Result::Ok(outputs);
}
@@ -273,7 +314,7 @@ mod tests {
#[test]
fn committed_token_2022_fee_instruction_creates_one_stable_output() {
let observation = observation(
super::SPL_TOKEN_2022_SURFACE,
crate::MT_FEES_SPL_TOKEN_2022_SURFACE,
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"set_transfer_fee",
false,
@@ -290,7 +331,7 @@ mod tests {
#[test]
fn failed_token_2022_fee_instruction_is_refused() {
let observation = observation(
super::SPL_TOKEN_2022_SURFACE,
crate::MT_FEES_SPL_TOKEN_2022_SURFACE,
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"harvest_withheld_tokens_to_mint",
true,
@@ -310,7 +351,7 @@ mod tests {
false,
);
let wrong_program = observation(
super::SPL_TOKEN_2022_SURFACE,
crate::MT_FEES_SPL_TOKEN_2022_SURFACE,
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
"set_transfer_fee",
false,
@@ -334,7 +375,7 @@ mod tests {
#[test]
fn committed_confidential_fee_instruction_is_projected_without_decryption() {
let observation = observation(
super::SPL_TOKEN_2022_SURFACE,
crate::MT_FEES_SPL_TOKEN_2022_SURFACE,
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"initialize_confidential_transfer_fee_config",
false,

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/governance.rs
// version: 3
// version: 4
//! `governance` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved governance materializer.
pub use self::core::MtGovernanceMaterializer;
pub use self::materializer::MtGovernanceMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_GOVERNANCE_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `governance` materializer.
pub(crate) use self::constants::MT_GOVERNANCE_COMPONENT_NAME;
/// Stable processor name for the `governance` materializer.
pub(crate) use self::constants::MT_GOVERNANCE_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/governance/constants.rs
// version: 1
//! Local constants for the governance materializer.
/// Stable runtime component name for the governance materializer.
pub(crate) const MT_GOVERNANCE_COMPONENT_NAME: &str = "kb-lib.materializer.governance";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_GOVERNANCE_PROCESSOR_NAME: &str = "materializer.governance";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_GOVERNANCE_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/governance/core.rs
// version: 4
// file: kb-lib/src/materializer/governance/materializer.rs
// version: 5
//! Reserved governance materializer.
@@ -9,7 +9,7 @@ pub struct MtGovernanceMaterializer;
impl crate::MtMaterializer for crate::MtGovernanceMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.governance";
return crate::MT_GOVERNANCE_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtGovernanceMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
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::Governance,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtGovernanceMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.governance".to_string(),
name: crate::MT_GOVERNANCE_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_GOVERNANCE_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/lending.rs
// version: 3
// version: 4
//! `lending` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved lending materializer.
pub use self::core::MtLendingMaterializer;
pub use self::materializer::MtLendingMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_LENDING_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `lending` materializer.
pub(crate) use self::constants::MT_LENDING_COMPONENT_NAME;
/// Stable processor name for the `lending` materializer.
pub(crate) use self::constants::MT_LENDING_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/lending/constants.rs
// version: 1
//! Local constants for the lending materializer.
/// Stable runtime component name for the lending materializer.
pub(crate) const MT_LENDING_COMPONENT_NAME: &str = "kb-lib.materializer.lending";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_LENDING_PROCESSOR_NAME: &str = "materializer.lending";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_LENDING_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/lending/core.rs
// version: 4
// file: kb-lib/src/materializer/lending/materializer.rs
// version: 5
//! Reserved lending materializer.
@@ -9,7 +9,7 @@ pub struct MtLendingMaterializer;
impl crate::MtMaterializer for crate::MtLendingMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.lending";
return crate::MT_LENDING_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtLendingMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
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::Lending,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtLendingMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.lending".to_string(),
name: crate::MT_LENDING_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_LENDING_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,13 +1,59 @@
// file: kb-lib/src/materializer/lifecycle.rs
// version: 4
// version: 5
//! `lifecycle` materializer family.
mod constants;
mod core;
mod materializer;
/// Stable native lifecycle materializer.
pub use self::core::MtLifecycleMaterializer;
pub use self::materializer::MtLifecycleMaterializer;
/// Canonical tracing target for the native lifecycle materializer.
/// Crate-visible `MT_LIFECYCLE_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_ACCEPTED_FAMILIES;
/// Crate-visible `MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE;
/// Crate-visible `MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE;
/// Crate-visible `MT_LIFECYCLE_BPF_LOADER_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_BPF_LOADER_SURFACE;
/// Crate-visible `MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Stable runtime component name for the `lifecycle` materializer.
pub(crate) use self::constants::MT_LIFECYCLE_COMPONENT_NAME;
/// Crate-visible `MT_LIFECYCLE_FEATURE_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_FEATURE_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_FEATURE_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_FEATURE_SURFACE;
/// Crate-visible `MT_LIFECYCLE_IMMUTABLE_LOADER_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_IMMUTABLE_LOADER_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_LOADER_V4_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_LOADER_V4_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_LOADER_V4_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_LOADER_V4_SURFACE;
/// Stable processor name for the `lifecycle` materializer.
pub(crate) use self::constants::MT_LIFECYCLE_PROCESSOR_NAME;
/// Stable projection version for the `lifecycle` materializer.
pub(crate) use self::constants::MT_LIFECYCLE_PROJECTION_VERSION;
/// Crate-visible `MT_LIFECYCLE_SLASHING_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_SLASHING_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_SLASHING_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_SLASHING_SURFACE;
/// Crate-visible `MT_LIFECYCLE_SYSTEM_ACCOUNT_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_SYSTEM_ACCOUNT_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_SYSTEM_NONCE_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_SYSTEM_NONCE_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_SYSTEM_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_SYSTEM_SURFACE;
/// Crate-visible `MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_ZK_ELGAMAL_CLOSE_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_ZK_ELGAMAL_CLOSE_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE;
/// Crate-visible `MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES;
/// Canonical tracing target for the `lifecycle` materializer.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_LIFECYCLE;

View File

@@ -1,7 +1,100 @@
// file: kb-lib/src/materializer/lifecycle/constants.rs
// version: 2
// version: 4
//! Local constants for the native lifecycle materializer.
//! Local constants for the lifecycle materializer.
/// Canonical tracing target for this component.
/// Stable runtime component name for the lifecycle materializer.
pub(crate) const MT_LIFECYCLE_COMPONENT_NAME: &str = "kb-lib.materializer.lifecycle";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_LIFECYCLE_PROCESSOR_NAME: &str = "materializer.lifecycle";
/// Stable projection contract version for the lifecycle materializer.
pub(crate) const MT_LIFECYCLE_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for the lifecycle materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_LIFECYCLE: &str = "kb-lib.materializer.lifecycle";
/// Component contract for `accepted_families`.
pub(crate) const MT_LIFECYCLE_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::Lifecycle,
crate::MdEventFamily::Admin,
crate::MdEventFamily::Audit,
];
/// Component contract for `address_lookup_table_surface`.
pub(crate) const MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE: &str =
"solana.core.address_lookup_table";
/// Component contract for `address_lookup_table_entries`.
pub(crate) const MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES: &[&str] = &[
"create_lookup_table",
"freeze_lookup_table",
"extend_lookup_table",
"deactivate_lookup_table",
"close_lookup_table",
];
/// Component contract for `bpf_loader_deprecated_surface`.
pub(crate) const MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE: &str =
"solana.core.bpf_loader_deprecated";
/// Component contract for `bpf_loader_surface`.
pub(crate) const MT_LIFECYCLE_BPF_LOADER_SURFACE: &str = "solana.core.bpf_loader";
/// Component contract for `bpf_loader_upgradeable_surface`.
pub(crate) const MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE: &str =
"solana.core.bpf_loader_upgradeable";
/// Component contract for `loader_v4_surface`.
pub(crate) const MT_LIFECYCLE_LOADER_V4_SURFACE: &str = "solana.core.loader_v4";
/// Component contract for `feature_surface`.
pub(crate) const MT_LIFECYCLE_FEATURE_SURFACE: &str = "solana.core.feature";
/// Component contract for `feature_entries`.
pub(crate) const MT_LIFECYCLE_FEATURE_ENTRIES: &[&str] = &["revoke_pending_activation"];
/// Component contract for `immutable_loader_entries`.
pub(crate) const MT_LIFECYCLE_IMMUTABLE_LOADER_ENTRIES: &[&str] = &["finalize"];
/// Component contract for `upgradeable_loader_entries`.
pub(crate) const MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES: &[&str] = &[
"initialize_buffer",
"deploy_with_max_data_len",
"upgrade",
"close",
"extend_program",
];
/// Component contract for `loader_v4_entries`.
pub(crate) const MT_LIFECYCLE_LOADER_V4_ENTRIES: &[&str] =
&["set_program_length", "deploy", "retract", "finalize"];
/// Component contract for `slashing_surface`.
pub(crate) const MT_LIFECYCLE_SLASHING_SURFACE: &str = "solana.core.slashing";
/// Component contract for `slashing_entries`.
pub(crate) const MT_LIFECYCLE_SLASHING_ENTRIES: &[&str] =
&["close_violation_report", "duplicate_block_proof"];
/// Component contract for `system_surface`.
pub(crate) const MT_LIFECYCLE_SYSTEM_SURFACE: &str = "solana.core.system";
/// Component contract for `system_account_entries`.
pub(crate) const MT_LIFECYCLE_SYSTEM_ACCOUNT_ENTRIES: &[&str] = &[
"create_account",
"create_account_with_seed",
"allocate",
"allocate_with_seed",
"create_account_allow_prefund",
];
/// Component contract for `system_nonce_entries`.
pub(crate) const MT_LIFECYCLE_SYSTEM_NONCE_ENTRIES: &[&str] = &[
"advance_nonce_account",
"withdraw_nonce_account",
"initialize_nonce_account",
"authorize_nonce_account",
"upgrade_nonce_account",
];
/// Component contract for `zk_elgamal_proof_surface`.
pub(crate) const MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE: &str = "solana.core.zk_elgamal_proof";
/// Component contract for `zk_elgamal_close_entries`.
pub(crate) const MT_LIFECYCLE_ZK_ELGAMAL_CLOSE_ENTRIES: &[&str] = &["close_context_state"];
/// Component contract for `zk_elgamal_verify_entries`.
pub(crate) const MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES: &[&str] = &[
"verify_zero_ciphertext",
"verify_ciphertext_ciphertext_equality",
"verify_ciphertext_commitment_equality",
"verify_pubkey_validity",
"verify_percentage_with_cap",
"verify_batched_range_proof_u64",
"verify_batched_range_proof_u128",
"verify_batched_range_proof_u256",
"verify_grouped_ciphertext_2_handles_validity",
"verify_batched_grouped_ciphertext_2_handles_validity",
"verify_grouped_ciphertext_3_handles_validity",
"verify_batched_grouped_ciphertext_3_handles_validity",
];

View File

@@ -1,70 +1,8 @@
// file: kb-lib/src/materializer/lifecycle/core.rs
// version: 16
// file: kb-lib/src/materializer/lifecycle/materializer.rs
// version: 18
//! Stable lifecycle projections derived from exact decoded observations.
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::Lifecycle,
crate::MdEventFamily::Admin,
crate::MdEventFamily::Audit,
];
const ADDRESS_LOOKUP_TABLE_SURFACE: &str = "solana.core.address_lookup_table";
const ADDRESS_LOOKUP_TABLE_ENTRIES: &[&str] = &[
"create_lookup_table",
"freeze_lookup_table",
"extend_lookup_table",
"deactivate_lookup_table",
"close_lookup_table",
];
const BPF_LOADER_DEPRECATED_SURFACE: &str = "solana.core.bpf_loader_deprecated";
const BPF_LOADER_SURFACE: &str = "solana.core.bpf_loader";
const BPF_LOADER_UPGRADEABLE_SURFACE: &str = "solana.core.bpf_loader_upgradeable";
const LOADER_V4_SURFACE: &str = "solana.core.loader_v4";
const FEATURE_SURFACE: &str = "solana.core.feature";
const FEATURE_ENTRIES: &[&str] = &["revoke_pending_activation"];
const IMMUTABLE_LOADER_ENTRIES: &[&str] = &["finalize"];
const UPGRADEABLE_LOADER_ENTRIES: &[&str] = &[
"initialize_buffer",
"deploy_with_max_data_len",
"upgrade",
"close",
"extend_program",
];
const LOADER_V4_ENTRIES: &[&str] = &["set_program_length", "deploy", "retract", "finalize"];
const SLASHING_SURFACE: &str = "solana.core.slashing";
const SLASHING_ENTRIES: &[&str] = &["close_violation_report", "duplicate_block_proof"];
const SYSTEM_SURFACE: &str = "solana.core.system";
const SYSTEM_ACCOUNT_ENTRIES: &[&str] = &[
"create_account",
"create_account_with_seed",
"allocate",
"allocate_with_seed",
"create_account_allow_prefund",
];
const SYSTEM_NONCE_ENTRIES: &[&str] = &[
"advance_nonce_account",
"withdraw_nonce_account",
"initialize_nonce_account",
"authorize_nonce_account",
"upgrade_nonce_account",
];
const ZK_ELGAMAL_PROOF_SURFACE: &str = "solana.core.zk_elgamal_proof";
const ZK_ELGAMAL_CLOSE_ENTRIES: &[&str] = &["close_context_state"];
const ZK_ELGAMAL_VERIFY_ENTRIES: &[&str] = &[
"verify_zero_ciphertext",
"verify_ciphertext_ciphertext_equality",
"verify_ciphertext_commitment_equality",
"verify_pubkey_validity",
"verify_percentage_with_cap",
"verify_batched_range_proof_u64",
"verify_batched_range_proof_u128",
"verify_batched_range_proof_u256",
"verify_grouped_ciphertext_2_handles_validity",
"verify_batched_grouped_ciphertext_2_handles_validity",
"verify_grouped_ciphertext_3_handles_validity",
"verify_batched_grouped_ciphertext_3_handles_validity",
];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ProjectionKind {
AddressLookupTable,
@@ -125,7 +63,7 @@ pub struct MtLifecycleMaterializer;
impl crate::MtMaterializer for crate::MtLifecycleMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.lifecycle";
return crate::MT_LIFECYCLE_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -152,13 +90,13 @@ impl crate::MtMaterializer for crate::MtLifecycleMaterializer {
impl crate::MtApiEventMaterializer for crate::MtLifecycleMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "materializer.lifecycle".to_string(),
name: crate::MT_LIFECYCLE_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
return crate::MT_LIFECYCLE_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
@@ -239,7 +177,7 @@ impl crate::MtApiEventMaterializer for crate::MtLifecycleMaterializer {
output_key,
family: crate::MdMaterializedEventFamily::Lifecycle,
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionVersion": crate::MT_LIFECYCLE_PROJECTION_VERSION,
"projectionSemantics": "committed_instruction_lifecycle_event",
"domain": domain,
"loaderSurface": loader_surface,
@@ -249,6 +187,10 @@ impl crate::MtApiEventMaterializer for crate::MtLifecycleMaterializer {
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"transactionSucceeded": true,
"provenance": {
"processorName": crate::MT_LIFECYCLE_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
},
"accounts": accounts,
"parameters": parameters,
"projection": projection_details,
@@ -263,7 +205,9 @@ impl crate::MtApiEventMaterializer for crate::MtLifecycleMaterializer {
}
fn family_is_accepted(family: crate::MdEventFamily) -> bool {
return ACCEPTED_FAMILIES.iter().any(|accepted| return *accepted == family);
return crate::MT_LIFECYCLE_ACCEPTED_FAMILIES
.iter()
.any(|accepted| return *accepted == family);
}
fn observation_projection(
@@ -275,8 +219,8 @@ fn observation_projection(
if static_projection.is_some() {
return static_projection;
}
if surface_code == ZK_ELGAMAL_PROOF_SURFACE
&& contains(ZK_ELGAMAL_VERIFY_ENTRIES, entry_code)
if surface_code == crate::MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE
&& contains(crate::MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES, entry_code)
&& observation
.payload_json
.get("parameters")
@@ -290,48 +234,63 @@ fn observation_projection(
}
fn static_projection(surface_code: &str, entry_code: &str) -> std::option::Option<ProjectionKind> {
if surface_code == FEATURE_SURFACE && contains(FEATURE_ENTRIES, entry_code) {
if surface_code == crate::MT_LIFECYCLE_FEATURE_SURFACE
&& contains(crate::MT_LIFECYCLE_FEATURE_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::FeatureGate);
}
if surface_code == ADDRESS_LOOKUP_TABLE_SURFACE
&& contains(ADDRESS_LOOKUP_TABLE_ENTRIES, entry_code)
if surface_code == crate::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE
&& contains(crate::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::AddressLookupTable);
}
if (surface_code == BPF_LOADER_DEPRECATED_SURFACE || surface_code == BPF_LOADER_SURFACE)
&& contains(IMMUTABLE_LOADER_ENTRIES, entry_code)
if (surface_code == crate::MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE
|| surface_code == crate::MT_LIFECYCLE_BPF_LOADER_SURFACE)
&& contains(crate::MT_LIFECYCLE_IMMUTABLE_LOADER_ENTRIES, entry_code)
{
let loader_surface = if surface_code == BPF_LOADER_DEPRECATED_SURFACE {
BPF_LOADER_DEPRECATED_SURFACE
let loader_surface = if surface_code == crate::MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE {
crate::MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE
} else {
BPF_LOADER_SURFACE
crate::MT_LIFECYCLE_BPF_LOADER_SURFACE
};
return std::option::Option::Some(ProjectionKind::ProgramLoader(loader_surface));
}
if surface_code == BPF_LOADER_UPGRADEABLE_SURFACE
&& contains(UPGRADEABLE_LOADER_ENTRIES, entry_code)
if surface_code == crate::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE
&& contains(crate::MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::ProgramLoader(
BPF_LOADER_UPGRADEABLE_SURFACE,
crate::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE,
));
}
if surface_code == LOADER_V4_SURFACE && contains(LOADER_V4_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::ProgramLoader(LOADER_V4_SURFACE));
if surface_code == crate::MT_LIFECYCLE_LOADER_V4_SURFACE
&& contains(crate::MT_LIFECYCLE_LOADER_V4_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::ProgramLoader(
crate::MT_LIFECYCLE_LOADER_V4_SURFACE,
));
}
if surface_code == SLASHING_SURFACE && contains(SLASHING_ENTRIES, entry_code) {
if surface_code == crate::MT_LIFECYCLE_SLASHING_SURFACE
&& contains(crate::MT_LIFECYCLE_SLASHING_ENTRIES, entry_code)
{
return if entry_code == "duplicate_block_proof" {
std::option::Option::Some(ProjectionKind::SlashingViolationReportInitialization)
} else {
std::option::Option::Some(ProjectionKind::SlashingViolationReportClosure)
};
}
if surface_code == SYSTEM_SURFACE && contains(SYSTEM_ACCOUNT_ENTRIES, entry_code) {
if surface_code == crate::MT_LIFECYCLE_SYSTEM_SURFACE
&& contains(crate::MT_LIFECYCLE_SYSTEM_ACCOUNT_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::SystemAccountLifecycle);
}
if surface_code == SYSTEM_SURFACE && contains(SYSTEM_NONCE_ENTRIES, entry_code) {
if surface_code == crate::MT_LIFECYCLE_SYSTEM_SURFACE
&& contains(crate::MT_LIFECYCLE_SYSTEM_NONCE_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::DurableNonceAccount);
}
if surface_code == ZK_ELGAMAL_PROOF_SURFACE && contains(ZK_ELGAMAL_CLOSE_ENTRIES, entry_code) {
if surface_code == crate::MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE
&& contains(crate::MT_LIFECYCLE_ZK_ELGAMAL_CLOSE_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::ZkProofContextClosure);
}
return std::option::Option::None;
@@ -539,7 +498,7 @@ mod tests {
transaction_failed: bool,
) -> crate::DcApiDecodedObservation {
return observation_with_payload(
super::SYSTEM_SURFACE,
crate::MT_LIFECYCLE_SYSTEM_SURFACE,
entry_code,
family,
transaction_failed,
@@ -579,7 +538,7 @@ mod tests {
)
};
return observation_with_payload(
super::SLASHING_SURFACE,
crate::MT_LIFECYCLE_SLASHING_SURFACE,
entry_code,
family,
transaction_failed,
@@ -620,7 +579,7 @@ mod tests {
crate::MdEventFamily::Audit
};
return observation_with_payload(
super::ZK_ELGAMAL_PROOF_SURFACE,
crate::MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE,
entry_code,
family,
transaction_failed,
@@ -637,7 +596,11 @@ mod tests {
let materializer = crate::MtLifecycleMaterializer;
let result = crate::MtApiEventMaterializer::materialize(
&materializer,
&observation(super::ADDRESS_LOOKUP_TABLE_SURFACE, "create_lookup_table", false),
&observation(
crate::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE,
"create_lookup_table",
false,
),
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs.len(), 1);
@@ -651,7 +614,11 @@ mod tests {
let materializer = crate::MtLifecycleMaterializer;
let result = crate::MtApiEventMaterializer::materialize(
&materializer,
&observation(super::BPF_LOADER_UPGRADEABLE_SURFACE, "deploy_with_max_data_len", false),
&observation(
crate::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE,
"deploy_with_max_data_len",
false,
),
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(
@@ -661,7 +628,7 @@ mod tests {
assert_eq!(result.outputs[0].payload_json["domain"], "program_loader");
assert_eq!(
result.outputs[0].payload_json["loaderSurface"],
super::BPF_LOADER_UPGRADEABLE_SURFACE
crate::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE
);
}
@@ -670,7 +637,7 @@ mod tests {
let materializer = crate::MtLifecycleMaterializer;
let result = crate::MtApiEventMaterializer::materialize(
&materializer,
&observation(super::FEATURE_SURFACE, "revoke_pending_activation", false),
&observation(crate::MT_LIFECYCLE_FEATURE_SURFACE, "revoke_pending_activation", false),
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs[0].output_key, "feature_gate:revoke_pending_activation:0");
@@ -696,7 +663,7 @@ mod tests {
),
] {
let observation = observation_with_payload(
super::SYSTEM_SURFACE,
crate::MT_LIFECYCLE_SYSTEM_SURFACE,
operation,
crate::MdEventFamily::Lifecycle,
false,
@@ -724,7 +691,7 @@ mod tests {
fn system_account_creation_does_not_duplicate_balance_changes() {
let materializer = crate::MtLifecycleMaterializer;
let observation = observation_with_payload(
super::SYSTEM_SURFACE,
crate::MT_LIFECYCLE_SYSTEM_SURFACE,
"create_account",
crate::MdEventFamily::Lifecycle,
false,
@@ -895,7 +862,8 @@ mod tests {
fn loader_write_and_admin_events_are_not_lifecycle_projections() {
let materializer = crate::MtLifecycleMaterializer;
for entry_code in ["write", "set_authority", "transfer_authority"] {
let observation = observation(super::BPF_LOADER_UPGRADEABLE_SURFACE, entry_code, false);
let observation =
observation(crate::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE, entry_code, false);
assert!(!crate::MtApiEventMaterializer::accepts_observation(
&materializer,
&observation,
@@ -908,7 +876,7 @@ mod tests {
let materializer = crate::MtLifecycleMaterializer;
let result = crate::MtApiEventMaterializer::materialize(
&materializer,
&observation(super::SYSTEM_SURFACE, "transfer", false),
&observation(crate::MT_LIFECYCLE_SYSTEM_SURFACE, "transfer", false),
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
@@ -934,8 +902,12 @@ mod tests {
fn failed_native_lifecycle_is_refused() {
let materializer = crate::MtLifecycleMaterializer;
let observations = [
observation(super::ADDRESS_LOOKUP_TABLE_SURFACE, "close_lookup_table", true),
observation(super::LOADER_V4_SURFACE, "deploy", true),
observation(
crate::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE,
"close_lookup_table",
true,
),
observation(crate::MT_LIFECYCLE_LOADER_V4_SURFACE, "deploy", true),
nonce_observation("authorize_nonce_account", crate::MdEventFamily::Admin, true),
zk_observation("verify_zero_ciphertext", true, true),
slashing_observation("duplicate_block_proof", true),

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/liquidity.rs
// version: 3
// version: 4
//! `liquidity` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved liquidity materializer.
pub use self::core::MtLiquidityMaterializer;
pub use self::materializer::MtLiquidityMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_LIQUIDITY_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `liquidity` materializer.
pub(crate) use self::constants::MT_LIQUIDITY_COMPONENT_NAME;
/// Stable processor name for the `liquidity` materializer.
pub(crate) use self::constants::MT_LIQUIDITY_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/liquidity/constants.rs
// version: 1
//! Local constants for the liquidity materializer.
/// Stable runtime component name for the liquidity materializer.
pub(crate) const MT_LIQUIDITY_COMPONENT_NAME: &str = "kb-lib.materializer.liquidity";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_LIQUIDITY_PROCESSOR_NAME: &str = "materializer.liquidity";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_LIQUIDITY_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/liquidity/core.rs
// version: 4
// file: kb-lib/src/materializer/liquidity/materializer.rs
// version: 5
//! Reserved liquidity materializer.
@@ -9,7 +9,7 @@ pub struct MtLiquidityMaterializer;
impl crate::MtMaterializer for crate::MtLiquidityMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.liquidity";
return crate::MT_LIQUIDITY_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtLiquidityMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
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::Liquidity,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtLiquidityMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.liquidity".to_string(),
name: crate::MT_LIQUIDITY_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_LIQUIDITY_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,31 +1,52 @@
// file: kb-lib/src/materializer/metadata.rs
// version: 5
// version: 8
//! `metadata` materializer family.
mod core;
mod metaplex_token_metadata;
mod solana_program_metadata;
mod token_2022;
/// Token-2022, Metaplex and Solana Program Metadata materializer.
pub use self::core::MtMetadataMaterializer;
/// Materializes one canonical non-Metadata Metaplex account snapshot.
pub use self::core::materializer_metadata_materialize_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::core::materializer_metadata_materialize_metaplex_metadata_snapshot;
/// Materializes Token-2022 embedded metadata and group snapshots.
pub use self::core::materializer_metadata_materialize_token_2022_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;
/// 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;
/// Materializes Token-2022 embedded metadata and group snapshots.
pub use self::token_2022::materializer_metadata_materialize_token_2022_snapshot;
/// 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.
pub(crate) use self::metaplex_token_metadata::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME;
/// Stable Metaplex Token Metadata projection version.
pub(crate) use self::metaplex_token_metadata::MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION;
/// 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_SPM_ACCEPTED_FAMILIES;
pub(crate) use self::solana_program_metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_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;
/// Stable processor name for Solana Program Metadata projections.
pub(crate) use self::solana_program_metadata::MT_METADATA_SPM_PROCESSOR_NAME;
pub(crate) use self::solana_program_metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME;
/// Stable projection schema version for Solana Program Metadata outputs.
pub(crate) use self::solana_program_metadata::MT_METADATA_SPM_PROJECTION_VERSION;
pub(crate) use self::solana_program_metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION;
/// Canonical tracing target for Solana Program Metadata materialization.
pub(crate) use self::solana_program_metadata::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;
/// Stable runtime component name for Token-2022 embedded metadata projections.
pub(crate) use self::token_2022::MT_METADATA_TOKEN_2022_COMPONENT_NAME;
/// 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.
pub(crate) use self::token_2022::MT_METADATA_TOKEN_2022_PROJECTION_VERSION;
/// Canonical tracing target for Token-2022 embedded metadata materialization.
pub(crate) use self::token_2022::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022;

View File

@@ -1,608 +0,0 @@
// file: kb-lib/src/materializer/metadata/core.rs
// version: 7
//! Stable Token-2022, Metaplex and Solana Program Metadata materializer.
/// Initial materializer implementation placeholder.
#[derive(Clone, Debug, Default)]
pub struct MtMetadataMaterializer;
impl crate::MtMaterializer for crate::MtMetadataMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.metadata";
}
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_SOLANA_PROGRAM_METADATA_PROGRAM_ID
&& event.surface_code.0 == crate::DC_METADATA_SPM_SURFACE_CODE
&& crate::DC_METADATA_SPM_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>> {
let materialized = crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::Metadata,
};
return std::result::Result::Ok(std::vec![materialized]);
}
}
/// Materialize embedded Token-2022 metadata, group, and group-member extensions.
pub fn materializer_metadata_materialize_token_2022_snapshot(
account_key: &str,
slot: u64,
state: &crate::DcToken2022State,
) -> std::result::Result<std::vec::Vec<crate::MtApiMaterializedOutput>, String> {
if account_key.is_empty() {
return std::result::Result::Err(
"Token-2022 metadata snapshot requires a non-empty account key".to_string(),
);
}
if state.kind != crate::DcToken2022StateKind::Mint {
return std::result::Result::Err(
"Token-2022 metadata snapshot requires a parsed mint state".to_string(),
);
}
let mut outputs = std::vec::Vec::new();
for entry in &state.extensions {
let projection_kind = match entry.extension_name {
"token_metadata" => std::option::Option::Some("token_metadata"),
"token_group" => std::option::Option::Some("token_group"),
"token_group_member" => std::option::Option::Some("token_group_member"),
_ => std::option::Option::None,
};
let kind = match projection_kind {
std::option::Option::Some(value) => value,
std::option::Option::None => continue,
};
outputs.push(crate::MtApiMaterializedOutput {
output_key: format!("token_2022_metadata_snapshot:{account_key}:{slot}:{kind}"),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": 1,
"domain": "spl_token_2022_embedded_metadata",
"projectionSemantics": "authoritative_bounded_mint_extension_snapshot",
"idempotenceKey": format!("token_2022_metadata:{account_key}:{slot}:{kind}"),
"accountKey": account_key,
"slot": slot,
"programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"metadataSource": "token_2022_embedded_tlv",
"projectionKind": kind,
"extensionType": entry.extension_type,
"extensionName": entry.extension_name,
"valueHex": entry.value_hex,
"valueFields": entry.value_fields,
"externalUriFetched": false,
"identityAuthority": "mint_address",
"finalAccountStateCaptured": true,
"provenance": {
"processorName": "kb-lib.materializer.metadata",
"processorVersion": env!("CARGO_PKG_VERSION"),
"source": "bounded_token_2022_state_parser"
}
}),
});
}
return std::result::Result::Ok(outputs);
}
/// Materialize one canonical Metaplex `MetadataV1` account snapshot.
///
/// Account snapshots are authoritative for current state. Instruction observations may describe
/// mutations, but must not emit a second competing metadata-state projection.
pub fn materializer_metadata_materialize_metaplex_metadata_snapshot(
snapshot: &crate::DcMetadataMtmCanonicalMetadataAccountSnapshot,
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
if !snapshot.provenance.committed {
return std::result::Result::Err(
"Metaplex metadata state materialization requires committed provenance".to_string(),
);
}
if snapshot.identity.materialization
!= crate::DcMetadataMtmCanonicalMaterializationPolicy::ActiveState
{
return std::result::Result::Err(
"Metaplex MetadataV1 materialization requires active-state policy".to_string(),
);
}
let metadata = match &snapshot.state {
crate::DcMetadataMtmCanonicalMetadataAccountState::Metadata(value) => value,
_ => {
return std::result::Result::Err(
"Metaplex metadata materializer received another account layout".to_string(),
);
},
};
let slot = snapshot.provenance.slot.map(|value| return value.to_string());
let write_version = snapshot.provenance.write_version.map(|value| return value.to_string());
let payload = serde_json::json!({
"projectionVersion": 1,
"domain": "metaplex_token_metadata",
"projectionSemantics": "authoritative_committed_account_snapshot",
"stableId": snapshot.identity.stable_id,
"account": metadata.account,
"mint": metadata.mint,
"updateAuthority": metadata.update_authority,
"name": metadata.name,
"symbol": metadata.symbol,
"uri": metadata.uri,
"sellerFeeBasisPoints": metadata.seller_fee_basis_points,
"creators": metadata.payload_json.get("creators").cloned().unwrap_or(serde_json::Value::Null),
"collection": metadata.payload_json.get("collection").cloned().unwrap_or(serde_json::Value::Null),
"uses": metadata.payload_json.get("uses").cloned().unwrap_or(serde_json::Value::Null),
"tokenStandard": metadata.payload_json.get("tokenStandard").cloned().unwrap_or(serde_json::Value::Null),
"programmableConfig": metadata.payload_json.get("programmableConfig").cloned().unwrap_or(serde_json::Value::Null),
"primarySaleHappened": metadata.primary_sale_happened,
"isMutable": metadata.is_mutable,
"layout": snapshot.identity.layout,
"layoutDiscriminant": snapshot.identity.discriminant,
"canonicalVersion": snapshot.identity.canonical_version,
"lifecycle": "active",
"materializationPolicy": "active_state",
"metadataSource": "metaplex_owned_account",
"accountState": metadata.payload_json,
"externalUriFetched": false,
"stateAuthority": "owned_account_snapshot",
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
"provenance": {
"source": format!("{:?}", snapshot.provenance.source),
"slot": slot,
"writeVersion": write_version,
"signature": snapshot.provenance.signature,
"instructionIndex": snapshot.provenance.instruction_index,
"innerInstructionIndex": snapshot.provenance.inner_instruction_index,
"committed": snapshot.provenance.committed,
"processorName": "kb-lib.materializer.metadata",
"processorVersion": env!("CARGO_PKG_VERSION")
}
});
return std::result::Result::Ok(crate::MtApiMaterializedOutput {
output_key: format!("metaplex_metadata_state:{}", metadata.account),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: payload,
});
}
#[cfg(test)]
mod tests_metaplex_metadata_state {
pub(super) fn snapshot(
committed: bool,
) -> crate::DcMetadataMtmCanonicalMetadataAccountSnapshot {
let metadata = crate::DcMetadataMtmMetadataAccountSnapshot {
account: "metadata111".to_string(),
bump: 1,
mint: "mint111".to_string(),
update_authority: "authority111".to_string(),
name: "Asset".to_string(),
symbol: "AST".to_string(),
uri: "https://example.invalid/asset.json".to_string(),
seller_fee_basis_points: 500,
primary_sale_happened: true,
is_mutable: false,
payload_json: serde_json::json!({
"creators": [{"address":"creator111","verified":true,"share":100}],
"collection": {"verified":true,"key":"collection111"},
"uses": {"useMethod":"Single","remaining":"0","total":"1"},
"tokenStandard": "ProgrammableNonFungible",
"programmableConfig": {"ruleSet":"rules111"}
}),
};
return crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_metadata(
metadata,
crate::DcMetadataMtmCanonicalAccountProvenance {
source: crate::DcMetadataMtmCanonicalObservationSource::Backfill,
slot: std::option::Option::Some(42),
write_version: std::option::Option::Some(7),
signature: std::option::Option::Some("signature111".to_string()),
instruction_index: std::option::Option::Some(3),
inner_instruction_index: std::option::Option::None,
committed,
},
);
}
#[test]
fn account_snapshot_is_one_stable_authoritative_metadata_projection() {
let first =
crate::materializer_metadata_materialize_metaplex_metadata_snapshot(&snapshot(true));
let second =
crate::materializer_metadata_materialize_metaplex_metadata_snapshot(&snapshot(true));
assert_eq!(first, second);
assert_eq!(
first.as_ref().map(|value| return value.output_key.as_str()),
std::result::Result::Ok("metaplex_metadata_state:metadata111")
);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["metadataSource"].clone()),
std::result::Result::Ok(serde_json::json!("metaplex_owned_account"))
);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["creators"].clone()),
std::result::Result::Ok(
serde_json::json!([{"address":"creator111","verified":true,"share":100}])
)
);
assert_eq!(
first
.as_ref()
.map(|value| return value.payload_json["programmableConfig"].clone()),
std::result::Result::Ok(serde_json::json!({"ruleSet":"rules111"}))
);
assert_eq!(
first
.as_ref()
.map(|value| return value.payload_json["instructionProjectionRole"].clone()),
std::result::Result::Ok(serde_json::json!(
"mutation_observation_only_no_duplicate_state"
))
);
assert_eq!(
first
.as_ref()
.map(|value| return value.payload_json["accountState"]["creators"][0]["share"]
.clone()),
std::result::Result::Ok(serde_json::json!(100))
);
}
#[test]
fn uncommitted_or_non_metadata_state_is_rejected() {
assert!(
crate::materializer_metadata_materialize_metaplex_metadata_snapshot(&snapshot(false))
.is_err()
);
let mut wrong = snapshot(true);
wrong.state = crate::DcMetadataMtmCanonicalMetadataAccountState::EditionMarker(
crate::DcMetadataMtmEditionMarkerAccountSnapshot {
account: "marker111".to_string(),
bump: 1,
mint: "mint111".to_string(),
kind: crate::DcMetadataMtmEditionMarkerAccountKind::EditionMarkerV1,
marker_group: std::option::Option::Some(0),
edition: 1,
byte_index: 0,
bit_mask: 64,
edition_taken: true,
ledger: std::vec![64],
payload_json: serde_json::json!({}),
},
);
assert!(
crate::materializer_metadata_materialize_metaplex_metadata_snapshot(&wrong).is_err()
);
}
}
/// Materialize any canonical Metaplex account state other than `MetadataV1`.
///
/// Every account address maps to one stable state key. Historical layouts remain materializable
/// with an explicit historical lifecycle, while execution policy remains independently disabled.
pub fn materializer_metadata_materialize_metaplex_account_snapshot(
snapshot: &crate::DcMetadataMtmCanonicalMetadataAccountSnapshot,
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
if !snapshot.provenance.committed {
return std::result::Result::Err(
"Metaplex account-state materialization requires committed provenance".to_string(),
);
}
let (projection_kind, fact_owner, state_json) = match &snapshot.state {
crate::DcMetadataMtmCanonicalMetadataAccountState::Metadata(_) => {
return std::result::Result::Err(
"MetadataV1 must use the dedicated metadata-state materializer".to_string(),
);
},
crate::DcMetadataMtmCanonicalMetadataAccountState::Edition(value) => {
("edition_state", "kb-lib.materializer.metadata", value.payload_json.clone())
},
crate::DcMetadataMtmCanonicalMetadataAccountState::EditionMarker(value) => (
"edition_marker_state",
"kb-lib.materializer.metadata",
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::TokenRecord(value) => (
"programmable_token_state",
"kb-lib.materializer.lifecycle",
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::DelegateRecord(value) => {
("delegate_admin_state", "kb-lib.materializer.admin", value.payload_json.clone())
},
crate::DcMetadataMtmCanonicalMetadataAccountState::AuthorityRecord(value) => {
("authority_admin_state", "kb-lib.materializer.admin", value.payload_json.clone())
},
crate::DcMetadataMtmCanonicalMetadataAccountState::TokenOwnedEscrow(value) => (
"token_owned_escrow_state",
"kb-lib.materializer.lifecycle",
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::ReservationList(value) => (
"historical_reservation_state",
"kb-lib.materializer.metadata",
value.payload_json.clone(),
),
};
let lifecycle = match snapshot.identity.lifecycle {
crate::DcMetadataMtmCanonicalAccountLifecycle::Active => "active",
crate::DcMetadataMtmCanonicalAccountLifecycle::ActiveExperimental => "active_experimental",
crate::DcMetadataMtmCanonicalAccountLifecycle::Historical => "historical",
};
let materialization_policy = match snapshot.identity.materialization {
crate::DcMetadataMtmCanonicalMaterializationPolicy::ActiveState => "active_state",
crate::DcMetadataMtmCanonicalMaterializationPolicy::HistoricalState => "historical_state",
};
let execution_policy = match snapshot.identity.execution {
crate::DcMetadataMtmCanonicalExecutionPolicy::CurrentCandidate => "current_candidate",
crate::DcMetadataMtmCanonicalExecutionPolicy::ExperimentalCandidate => {
"experimental_candidate"
},
crate::DcMetadataMtmCanonicalExecutionPolicy::NeverHistorical => "never_historical",
};
return std::result::Result::Ok(crate::MtApiMaterializedOutput {
output_key: format!("metaplex_account_state:{}", snapshot.identity.account),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": 1,
"domain": "metaplex_token_metadata",
"projectionKind": projection_kind,
"projectionSemantics": "authoritative_committed_owned_account_snapshot",
"stableId": snapshot.identity.stable_id,
"account": snapshot.identity.account,
"programId": snapshot.identity.program_id,
"layout": snapshot.identity.layout,
"layoutDiscriminant": snapshot.identity.discriminant,
"canonicalVersion": snapshot.identity.canonical_version,
"lifecycle": lifecycle,
"materializationPolicy": materialization_policy,
"executionPolicy": execution_policy,
"factOwner": fact_owner,
"metadataSource": "metaplex_owned_account",
"accountState": state_json,
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
"provenance": {
"source": format!("{:?}", snapshot.provenance.source),
"slot": snapshot.provenance.slot.map(|value: u64| return value.to_string()),
"writeVersion": snapshot.provenance.write_version.map(|value: u64| return value.to_string()),
"signature": snapshot.provenance.signature,
"instructionIndex": snapshot.provenance.instruction_index,
"innerInstructionIndex": snapshot.provenance.inner_instruction_index,
"committed": snapshot.provenance.committed,
"processorName": "kb-lib.materializer.metadata",
"processorVersion": env!("CARGO_PKG_VERSION")
}
}),
});
}
#[cfg(test)]
mod tests_metaplex_account_state {
fn provenance() -> crate::DcMetadataMtmCanonicalAccountProvenance {
return crate::DcMetadataMtmCanonicalAccountProvenance {
source: crate::DcMetadataMtmCanonicalObservationSource::Backfill,
slot: std::option::Option::Some(90),
write_version: std::option::Option::Some(2),
signature: std::option::Option::None,
instruction_index: std::option::Option::None,
inner_instruction_index: std::option::Option::None,
committed: true,
};
}
#[test]
fn historical_edition_and_reservation_are_stable_materializable_state() {
let edition = crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_edition(
crate::DcMetadataMtmEditionAccountSnapshot {
account: "master-v1".to_string(),
bump: 1,
mint: "mint-v1".to_string(),
kind: crate::DcMetadataMtmEditionAccountKind::MasterEditionV1,
supply: std::option::Option::Some(3),
max_supply: std::option::Option::Some(10),
parent: std::option::Option::None,
edition: std::option::Option::None,
printing_mint: std::option::Option::Some("printing".to_string()),
one_time_printing_authorization_mint: std::option::Option::Some(
"authorization".to_string(),
),
payload_json: serde_json::json!({"supply":"3","maxSupply":"10"}),
},
provenance(),
);
let reservation =
crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_reservation_list(
crate::DcMetadataMtmReservationListAccountSnapshot {
account: "reservation-v2".to_string(),
bump: 2,
kind: crate::DcMetadataMtmReservationListAccountKind::V2,
master_edition: "master-v1".to_string(),
resource: "resource".to_string(),
supply_snapshot: std::option::Option::Some(3),
reservations: std::vec::Vec::new(),
total_reservation_spots: 0,
current_reservation_spots: 0,
payload_json: serde_json::json!({"reservations":[]}),
},
provenance(),
);
let first = crate::materializer_metadata_materialize_metaplex_account_snapshot(&edition);
let second =
crate::materializer_metadata_materialize_metaplex_account_snapshot(&reservation);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["lifecycle"].clone()),
std::result::Result::Ok(serde_json::json!("historical"))
);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["executionPolicy"].clone()),
std::result::Result::Ok(serde_json::json!("never_historical"))
);
assert_eq!(
second.as_ref().map(|value| return value.output_key.as_str()),
std::result::Result::Ok("metaplex_account_state:reservation-v2")
);
}
#[test]
fn programmable_delegate_authority_and_escrow_keep_unique_fact_owners() {
let token = crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_token_record(
crate::DcMetadataMtmTokenRecordAccountSnapshot {
account: "token-record".to_string(),
bump: 1,
mint: "mint".to_string(),
token: "token".to_string(),
stored_bump: 1,
state: "Locked".to_string(),
rule_set_revision: std::option::Option::Some(4),
delegate: std::option::Option::Some("delegate".to_string()),
delegate_role: std::option::Option::Some("Transfer".to_string()),
locked_transfer: std::option::Option::Some("destination".to_string()),
locked: true,
payload_json: serde_json::json!({"state":"Locked"}),
},
provenance(),
);
let output = crate::materializer_metadata_materialize_metaplex_account_snapshot(&token);
assert_eq!(
output.as_ref().map(|value| return value.payload_json["factOwner"].clone()),
std::result::Result::Ok(serde_json::json!("kb-lib.materializer.lifecycle"))
);
assert_eq!(
output.as_ref().map(|value| return value.payload_json["projectionKind"].clone()),
std::result::Result::Ok(serde_json::json!("programmable_token_state"))
);
}
#[test]
fn uncommitted_and_metadata_snapshots_use_explicit_boundaries() {
let mut marker = crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_edition_marker(
crate::DcMetadataMtmEditionMarkerAccountSnapshot {
account: "marker".to_string(),
bump: 1,
mint: "mint".to_string(),
kind: crate::DcMetadataMtmEditionMarkerAccountKind::EditionMarkerV2,
marker_group: std::option::Option::None,
edition: 8,
byte_index: 1,
bit_mask: 128,
edition_taken: true,
ledger: std::vec![0, 128],
payload_json: serde_json::json!({"editionTaken":true}),
},
provenance(),
);
marker.provenance.committed = false;
assert!(
crate::materializer_metadata_materialize_metaplex_account_snapshot(&marker).is_err()
);
assert!(
crate::materializer_metadata_materialize_metaplex_account_snapshot(
&super::tests_metaplex_metadata_state::snapshot(true)
)
.is_err()
);
}
}
#[cfg(test)]
mod tests_token_2022_state {
#[test]
fn embedded_metadata_group_and_member_have_distinct_stable_outputs() {
let state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Mint,
base_fields: serde_json::json!({"supply":"1","decimals":0}),
base_hex: "00".repeat(82),
account_type: std::option::Option::Some(1),
extensions: std::vec![
crate::DcToken2022TlvEntry {
extension_type: 19,
extension_name: "token_metadata",
value_hex: "01".to_string(),
value_fields: serde_json::json!({"name":"Token","symbol":"TOK","uri":"https://example.invalid/token.json"}),
},
crate::DcToken2022TlvEntry {
extension_type: 20,
extension_name: "token_group",
value_hex: "02".to_string(),
value_fields: serde_json::json!({"mint":"mint111","size":"1","maxSize":"10"}),
},
crate::DcToken2022TlvEntry {
extension_type: 21,
extension_name: "token_group_member",
value_hex: "03".to_string(),
value_fields: serde_json::json!({"mint":"mint111","group":"group111","memberNumber":"1"}),
},
],
};
let outputs =
crate::materializer_metadata_materialize_token_2022_snapshot("mint111", 9, &state);
assert_eq!(outputs.as_ref().map(|values| return values.len()), std::result::Result::Ok(3));
assert_eq!(
outputs
.as_ref()
.map(|values| return values[0].payload_json["metadataSource"].clone()),
std::result::Result::Ok(serde_json::json!("token_2022_embedded_tlv"))
);
assert_eq!(
outputs
.as_ref()
.map(|values| return values[0].payload_json["externalUriFetched"].clone()),
std::result::Result::Ok(serde_json::json!(false))
);
assert_eq!(
outputs
.as_ref()
.map(|values| return values[1].payload_json["projectionKind"].clone()),
std::result::Result::Ok(serde_json::json!("token_group"))
);
}
#[test]
fn snapshot_is_idempotent_filters_unrelated_extensions_and_requires_mint_identity() {
let state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Mint,
base_fields: serde_json::json!({}),
base_hex: "00".repeat(82),
account_type: std::option::Option::Some(1),
extensions: std::vec![crate::DcToken2022TlvEntry {
extension_type: 1,
extension_name: "transfer_fee_config",
value_hex: "00".to_string(),
value_fields: serde_json::json!({}),
}],
};
let first =
crate::materializer_metadata_materialize_token_2022_snapshot("mint111", 9, &state);
let second =
crate::materializer_metadata_materialize_token_2022_snapshot("mint111", 9, &state);
assert_eq!(first, second);
assert_eq!(first.as_ref().map(|values| return values.len()), std::result::Result::Ok(0));
assert!(
crate::materializer_metadata_materialize_token_2022_snapshot("", 9, &state).is_err()
);
let account_state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Account,
base_fields: serde_json::json!({}),
base_hex: "00".repeat(165),
account_type: std::option::Option::None,
extensions: std::vec::Vec::new(),
};
assert!(
crate::materializer_metadata_materialize_token_2022_snapshot(
"account111",
9,
&account_state
)
.is_err()
);
}
}

View File

@@ -0,0 +1,22 @@
// file: kb-lib/src/materializer/metadata/metaplex_token_metadata.rs
// version: 2
//! Metaplex Token Metadata account-state materializer.
mod account;
mod constants;
mod state;
/// Materializes one canonical non-Metadata Metaplex account snapshot.
pub use self::account::materializer_metadata_materialize_metaplex_account_snapshot;
/// Materializes one canonical Metaplex Metadata account snapshot.
pub use self::state::materializer_metadata_materialize_metaplex_metadata_snapshot;
/// 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.
pub(crate) use self::constants::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME;
/// Stable Metaplex Token Metadata projection version.
pub(crate) use self::constants::MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION;
/// Canonical tracing target for Metaplex Token Metadata materialization.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA;

View File

@@ -0,0 +1,283 @@
// file: kb-lib/src/materializer/metadata/metaplex_token_metadata/account.rs
// version: 5
//! Authoritative non-Metadata Metaplex account-state materialization.
/// Materialize any canonical Metaplex account state other than `MetadataV1`.
///
/// Every account address maps to one stable state key. Historical layouts remain materializable
/// with an explicit historical lifecycle, while execution policy remains independently disabled.
pub fn materializer_metadata_materialize_metaplex_account_snapshot(
snapshot: &crate::DcMetadataMtmCanonicalMetadataAccountSnapshot,
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
if !snapshot.provenance.committed {
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA,
action = "materialize_metaplex_account_snapshot",
component_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
processor_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
account = %snapshot.identity.account.as_str(),
committed = false,
status = "refused",
error_code = "uncommitted_metaplex_account_snapshot",
"refuse uncommitted Metaplex account snapshot"
);
return std::result::Result::Err(
"Metaplex account-state materialization requires committed provenance".to_string(),
);
}
let (projection_kind, fact_owner, state_json) = match &snapshot.state {
crate::DcMetadataMtmCanonicalMetadataAccountState::Metadata(_) => {
return std::result::Result::Err(
"MetadataV1 must use the dedicated metadata-state materializer".to_string(),
);
},
crate::DcMetadataMtmCanonicalMetadataAccountState::Edition(value) => (
"edition_state",
crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::EditionMarker(value) => (
"edition_marker_state",
crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::TokenRecord(value) => (
"programmable_token_state",
crate::MT_LIFECYCLE_COMPONENT_NAME,
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::DelegateRecord(value) => (
"delegate_admin_state",
crate::MT_ADMIN_COMPONENT_NAME,
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::AuthorityRecord(value) => (
"authority_admin_state",
crate::MT_ADMIN_COMPONENT_NAME,
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::TokenOwnedEscrow(value) => (
"token_owned_escrow_state",
crate::MT_LIFECYCLE_COMPONENT_NAME,
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::ReservationList(value) => (
"historical_reservation_state",
crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
value.payload_json.clone(),
),
};
let lifecycle = match snapshot.identity.lifecycle {
crate::DcMetadataMtmCanonicalAccountLifecycle::Active => "active",
crate::DcMetadataMtmCanonicalAccountLifecycle::ActiveExperimental => "active_experimental",
crate::DcMetadataMtmCanonicalAccountLifecycle::Historical => "historical",
};
let materialization_policy = match snapshot.identity.materialization {
crate::DcMetadataMtmCanonicalMaterializationPolicy::ActiveState => "active_state",
crate::DcMetadataMtmCanonicalMaterializationPolicy::HistoricalState => "historical_state",
};
let execution_policy = match snapshot.identity.execution {
crate::DcMetadataMtmCanonicalExecutionPolicy::CurrentCandidate => "current_candidate",
crate::DcMetadataMtmCanonicalExecutionPolicy::ExperimentalCandidate => {
"experimental_candidate"
},
crate::DcMetadataMtmCanonicalExecutionPolicy::NeverHistorical => "never_historical",
};
let output = crate::MtApiMaterializedOutput {
output_key: format!("metaplex_account_state:{}", snapshot.identity.account),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION,
"domain": "metaplex_token_metadata",
"projectionKind": projection_kind,
"projectionSemantics": "authoritative_committed_owned_account_snapshot",
"stableId": snapshot.identity.stable_id,
"account": snapshot.identity.account,
"programId": snapshot.identity.program_id,
"layout": snapshot.identity.layout,
"layoutDiscriminant": snapshot.identity.discriminant,
"canonicalVersion": snapshot.identity.canonical_version,
"lifecycle": lifecycle,
"materializationPolicy": materialization_policy,
"executionPolicy": execution_policy,
"factOwner": fact_owner,
"metadataSource": "metaplex_owned_account",
"accountState": state_json,
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
"provenance": {
"source": format!("{:?}", snapshot.provenance.source),
"slot": snapshot.provenance.slot.map(|value: u64| return value.to_string()),
"writeVersion": snapshot.provenance.write_version.map(|value: u64| return value.to_string()),
"signature": snapshot.provenance.signature,
"instructionIndex": snapshot.provenance.instruction_index,
"innerInstructionIndex": snapshot.provenance.inner_instruction_index,
"committed": snapshot.provenance.committed,
"processorName": crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION")
}
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA,
action = "materialize_metaplex_account_snapshot",
component_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
processor_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
account = %snapshot.identity.account.as_str(),
projection_kind,
fact_owner,
slot = ?snapshot.provenance.slot,
status = "inserted",
"materialize committed Metaplex account snapshot"
);
return std::result::Result::Ok(output);
}
#[cfg(test)]
mod tests_metaplex_account_state {
fn metadata_snapshot() -> crate::DcMetadataMtmCanonicalMetadataAccountSnapshot {
return crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_metadata(
crate::DcMetadataMtmMetadataAccountSnapshot {
account: "metadata-boundary".to_string(),
bump: 1,
mint: "mint-boundary".to_string(),
update_authority: "authority-boundary".to_string(),
name: "Boundary".to_string(),
symbol: "BND".to_string(),
uri: "https://example.invalid/boundary.json".to_string(),
seller_fee_basis_points: 0,
primary_sale_happened: false,
is_mutable: true,
payload_json: serde_json::json!({}),
},
provenance(),
);
}
fn provenance() -> crate::DcMetadataMtmCanonicalAccountProvenance {
return crate::DcMetadataMtmCanonicalAccountProvenance {
source: crate::DcMetadataMtmCanonicalObservationSource::Backfill,
slot: std::option::Option::Some(90),
write_version: std::option::Option::Some(2),
signature: std::option::Option::None,
instruction_index: std::option::Option::None,
inner_instruction_index: std::option::Option::None,
committed: true,
};
}
#[test]
fn historical_edition_and_reservation_are_stable_materializable_state() {
let edition = crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_edition(
crate::DcMetadataMtmEditionAccountSnapshot {
account: "master-v1".to_string(),
bump: 1,
mint: "mint-v1".to_string(),
kind: crate::DcMetadataMtmEditionAccountKind::MasterEditionV1,
supply: std::option::Option::Some(3),
max_supply: std::option::Option::Some(10),
parent: std::option::Option::None,
edition: std::option::Option::None,
printing_mint: std::option::Option::Some("printing".to_string()),
one_time_printing_authorization_mint: std::option::Option::Some(
"authorization".to_string(),
),
payload_json: serde_json::json!({"supply":"3","maxSupply":"10"}),
},
provenance(),
);
let reservation =
crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_reservation_list(
crate::DcMetadataMtmReservationListAccountSnapshot {
account: "reservation-v2".to_string(),
bump: 2,
kind: crate::DcMetadataMtmReservationListAccountKind::V2,
master_edition: "master-v1".to_string(),
resource: "resource".to_string(),
supply_snapshot: std::option::Option::Some(3),
reservations: std::vec::Vec::new(),
total_reservation_spots: 0,
current_reservation_spots: 0,
payload_json: serde_json::json!({"reservations":[]}),
},
provenance(),
);
let first = crate::materializer_metadata_materialize_metaplex_account_snapshot(&edition);
let second =
crate::materializer_metadata_materialize_metaplex_account_snapshot(&reservation);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["lifecycle"].clone()),
std::result::Result::Ok(serde_json::json!("historical"))
);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["executionPolicy"].clone()),
std::result::Result::Ok(serde_json::json!("never_historical"))
);
assert_eq!(
second.as_ref().map(|value| return value.output_key.as_str()),
std::result::Result::Ok("metaplex_account_state:reservation-v2")
);
}
#[test]
fn programmable_delegate_authority_and_escrow_keep_unique_fact_owners() {
let token = crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_token_record(
crate::DcMetadataMtmTokenRecordAccountSnapshot {
account: "token-record".to_string(),
bump: 1,
mint: "mint".to_string(),
token: "token".to_string(),
stored_bump: 1,
state: "Locked".to_string(),
rule_set_revision: std::option::Option::Some(4),
delegate: std::option::Option::Some("delegate".to_string()),
delegate_role: std::option::Option::Some("Transfer".to_string()),
locked_transfer: std::option::Option::Some("destination".to_string()),
locked: true,
payload_json: serde_json::json!({"state":"Locked"}),
},
provenance(),
);
let output = crate::materializer_metadata_materialize_metaplex_account_snapshot(&token);
assert_eq!(
output.as_ref().map(|value| return value.payload_json["factOwner"].clone()),
std::result::Result::Ok(serde_json::json!(crate::MT_LIFECYCLE_COMPONENT_NAME))
);
assert_eq!(
output.as_ref().map(|value| return value.payload_json["projectionKind"].clone()),
std::result::Result::Ok(serde_json::json!("programmable_token_state"))
);
}
#[test]
fn uncommitted_and_metadata_snapshots_use_explicit_boundaries() {
let mut marker = crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_edition_marker(
crate::DcMetadataMtmEditionMarkerAccountSnapshot {
account: "marker".to_string(),
bump: 1,
mint: "mint".to_string(),
kind: crate::DcMetadataMtmEditionMarkerAccountKind::EditionMarkerV2,
marker_group: std::option::Option::None,
edition: 8,
byte_index: 1,
bit_mask: 128,
edition_taken: true,
ledger: std::vec![0, 128],
payload_json: serde_json::json!({"editionTaken":true}),
},
provenance(),
);
marker.provenance.committed = false;
assert!(
crate::materializer_metadata_materialize_metaplex_account_snapshot(&marker).is_err()
);
assert!(
crate::materializer_metadata_materialize_metaplex_account_snapshot(
&metadata_snapshot()
)
.is_err()
);
}
}

View File

@@ -0,0 +1,16 @@
// file: kb-lib/src/materializer/metadata/metaplex_token_metadata/constants.rs
// version: 4
//! Local constants for the metadata metaplex_token_metadata materializer.
/// Stable runtime component name for the metadata metaplex_token_metadata materializer.
pub(crate) const MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME: &str =
"kb-lib.materializer.metadata.metaplex_token_metadata";
/// Stable processor name persisted by metadata projections.
pub(crate) const MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME: &str =
"materializer.metadata.metaplex_token_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.
pub(crate) const TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA: &str =
"kb-lib.materializer.metadata.metaplex_token_metadata";

View File

@@ -0,0 +1,229 @@
// file: kb-lib/src/materializer/metadata/metaplex_token_metadata/state.rs
// version: 5
//! Authoritative Metaplex MetadataV1 state materialization.
/// Materialize one canonical Metaplex `MetadataV1` account snapshot.
///
/// Account snapshots are authoritative for current state. Instruction observations may describe
/// mutations, but must not emit a second competing metadata-state projection.
pub fn materializer_metadata_materialize_metaplex_metadata_snapshot(
snapshot: &crate::DcMetadataMtmCanonicalMetadataAccountSnapshot,
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
if !snapshot.provenance.committed {
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA,
action = "materialize_metaplex_metadata_snapshot",
component_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
processor_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
account = %snapshot.identity.account.as_str(),
committed = false,
status = "refused",
error_code = "uncommitted_metaplex_metadata_snapshot",
"refuse uncommitted Metaplex metadata snapshot"
);
return std::result::Result::Err(
"Metaplex metadata state materialization requires committed provenance".to_string(),
);
}
if snapshot.identity.materialization
!= crate::DcMetadataMtmCanonicalMaterializationPolicy::ActiveState
{
return std::result::Result::Err(
"Metaplex MetadataV1 materialization requires active-state policy".to_string(),
);
}
let metadata = match &snapshot.state {
crate::DcMetadataMtmCanonicalMetadataAccountState::Metadata(value) => value,
_ => {
return std::result::Result::Err(
"Metaplex metadata materializer received another account layout".to_string(),
);
},
};
let slot = snapshot.provenance.slot.map(|value| return value.to_string());
let write_version = snapshot.provenance.write_version.map(|value| return value.to_string());
let payload = serde_json::json!({
"projectionVersion": crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION,
"domain": "metaplex_token_metadata",
"projectionSemantics": "authoritative_committed_account_snapshot",
"stableId": snapshot.identity.stable_id,
"account": metadata.account,
"mint": metadata.mint,
"updateAuthority": metadata.update_authority,
"name": metadata.name,
"symbol": metadata.symbol,
"uri": metadata.uri,
"sellerFeeBasisPoints": metadata.seller_fee_basis_points,
"creators": json_field_or_null(&metadata.payload_json, "creators"),
"collection": json_field_or_null(&metadata.payload_json, "collection"),
"uses": json_field_or_null(&metadata.payload_json, "uses"),
"tokenStandard": json_field_or_null(&metadata.payload_json, "tokenStandard"),
"programmableConfig": json_field_or_null(&metadata.payload_json, "programmableConfig"),
"primarySaleHappened": metadata.primary_sale_happened,
"isMutable": metadata.is_mutable,
"layout": snapshot.identity.layout,
"layoutDiscriminant": snapshot.identity.discriminant,
"canonicalVersion": snapshot.identity.canonical_version,
"lifecycle": "active",
"materializationPolicy": "active_state",
"metadataSource": "metaplex_owned_account",
"accountState": metadata.payload_json,
"externalUriFetched": false,
"stateAuthority": "owned_account_snapshot",
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
"provenance": {
"source": format!("{:?}", snapshot.provenance.source),
"slot": slot,
"writeVersion": write_version,
"signature": snapshot.provenance.signature,
"instructionIndex": snapshot.provenance.instruction_index,
"innerInstructionIndex": snapshot.provenance.inner_instruction_index,
"committed": snapshot.provenance.committed,
"processorName": crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION")
}
});
let output = crate::MtApiMaterializedOutput {
output_key: format!("metaplex_metadata_state:{}", metadata.account),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: payload,
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA,
action = "materialize_metaplex_metadata_snapshot",
component_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
processor_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
account = %metadata.account.as_str(),
mint = %metadata.mint.as_str(),
slot = ?snapshot.provenance.slot,
status = "inserted",
"materialize committed Metaplex MetadataV1 snapshot"
);
return std::result::Result::Ok(output);
}
fn json_field_or_null(value: &serde_json::Value, field: &str) -> serde_json::Value {
return match value.get(field) {
std::option::Option::Some(value) => value.clone(),
std::option::Option::None => serde_json::Value::Null,
};
}
#[cfg(test)]
mod tests_metaplex_metadata_state {
fn snapshot(committed: bool) -> crate::DcMetadataMtmCanonicalMetadataAccountSnapshot {
let metadata = crate::DcMetadataMtmMetadataAccountSnapshot {
account: "metadata111".to_string(),
bump: 1,
mint: "mint111".to_string(),
update_authority: "authority111".to_string(),
name: "Asset".to_string(),
symbol: "AST".to_string(),
uri: "https://example.invalid/asset.json".to_string(),
seller_fee_basis_points: 500,
primary_sale_happened: true,
is_mutable: false,
payload_json: serde_json::json!({
"creators": [{"address":"creator111","verified":true,"share":100}],
"collection": {"verified":true,"key":"collection111"},
"uses": {"useMethod":"Single","remaining":"0","total":"1"},
"tokenStandard": "ProgrammableNonFungible",
"programmableConfig": {"ruleSet":"rules111"}
}),
};
return crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_metadata(
metadata,
crate::DcMetadataMtmCanonicalAccountProvenance {
source: crate::DcMetadataMtmCanonicalObservationSource::Backfill,
slot: std::option::Option::Some(42),
write_version: std::option::Option::Some(7),
signature: std::option::Option::Some("signature111".to_string()),
instruction_index: std::option::Option::Some(3),
inner_instruction_index: std::option::Option::None,
committed,
},
);
}
#[test]
fn account_snapshot_is_one_stable_authoritative_metadata_projection() {
let first =
crate::materializer_metadata_materialize_metaplex_metadata_snapshot(&snapshot(true));
let second =
crate::materializer_metadata_materialize_metaplex_metadata_snapshot(&snapshot(true));
assert_eq!(first, second);
assert_eq!(
first.as_ref().map(|value| return value.output_key.as_str()),
std::result::Result::Ok("metaplex_metadata_state:metadata111")
);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["metadataSource"].clone()),
std::result::Result::Ok(serde_json::json!("metaplex_owned_account"))
);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["creators"].clone()),
std::result::Result::Ok(
serde_json::json!([{"address":"creator111","verified":true,"share":100}])
)
);
assert_eq!(
first
.as_ref()
.map(|value| return value.payload_json["programmableConfig"].clone()),
std::result::Result::Ok(serde_json::json!({"ruleSet":"rules111"}))
);
assert_eq!(
first
.as_ref()
.map(|value| return value.payload_json["instructionProjectionRole"].clone()),
std::result::Result::Ok(serde_json::json!(
"mutation_observation_only_no_duplicate_state"
))
);
assert_eq!(
first.as_ref().map(|value| {
return value.payload_json["provenance"]["processorName"].clone();
}),
std::result::Result::Ok(serde_json::json!(
crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME
))
);
assert_eq!(
first
.as_ref()
.map(|value| return value.payload_json["accountState"]["creators"][0]["share"]
.clone()),
std::result::Result::Ok(serde_json::json!(100))
);
}
#[test]
fn uncommitted_or_non_metadata_state_is_rejected() {
assert!(
crate::materializer_metadata_materialize_metaplex_metadata_snapshot(&snapshot(false))
.is_err()
);
let mut wrong = snapshot(true);
wrong.state = crate::DcMetadataMtmCanonicalMetadataAccountState::EditionMarker(
crate::DcMetadataMtmEditionMarkerAccountSnapshot {
account: "marker111".to_string(),
bump: 1,
mint: "mint111".to_string(),
kind: crate::DcMetadataMtmEditionMarkerAccountKind::EditionMarkerV1,
marker_group: std::option::Option::Some(0),
edition: 1,
byte_index: 0,
bit_mask: 64,
edition_taken: true,
ledger: std::vec![64],
payload_json: serde_json::json!({}),
},
);
assert!(
crate::materializer_metadata_materialize_metaplex_metadata_snapshot(&wrong).is_err()
);
}
}

View File

@@ -1,12 +1,16 @@
// file: kb-lib/src/materializer/metadata/solana_program_metadata.rs
// version: 1
// version: 2
//! Solana Program Metadata materialization surface.
mod constants;
mod instruction;
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;
/// 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.
@@ -15,10 +19,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_SPM_ACCEPTED_FAMILIES;
pub(crate) use self::constants::MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES;
/// Stable runtime component name for Solana Program Metadata projections.
pub(crate) use self::constants::MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME;
/// Stable processor name for Solana Program Metadata projections.
pub(crate) use self::constants::MT_METADATA_SPM_PROCESSOR_NAME;
pub(crate) use self::constants::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME;
/// Stable projection schema version for Solana Program Metadata outputs.
pub(crate) use self::constants::MT_METADATA_SPM_PROJECTION_VERSION;
pub(crate) use self::constants::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION;
/// Canonical tracing target for Solana Program Metadata materialization.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;

View File

@@ -1,16 +1,18 @@
// file: kb-lib/src/materializer/metadata/solana_program_metadata/constants.rs
// version: 1
// version: 4
//! Solana Program Metadata materializer constants.
//! Local constants for the Solana Program Metadata materializer.
/// Event families accepted by the Solana Program Metadata instruction materializer.
pub(crate) const MT_METADATA_SPM_ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
&[crate::MdEventFamily::Metadata];
/// Stable processor name for Solana Program Metadata projections.
pub(crate) const MT_METADATA_SPM_PROCESSOR_NAME: &str =
/// Stable runtime component name for Solana Program Metadata materialization.
pub(crate) const MT_METADATA_SOLANA_PROGRAM_METADATA_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 =
"materializer.metadata.solana_program_metadata";
/// Stable projection schema version for Solana Program Metadata outputs.
pub(crate) const MT_METADATA_SPM_PROJECTION_VERSION: u16 = 1;
pub(crate) const MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION: u32 = 1;
pub(crate) const MT_METADATA_SOLANA_PROGRAM_METADATA_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 =
"kb-lib.materializer.metadata.solana_program_metadata";

View File

@@ -1,18 +1,128 @@
// file: kb-lib/src/materializer/metadata/solana_program_metadata/instruction.rs
// version: 1
// file: kb-lib/src/materializer/metadata/solana_program_metadata/materializer.rs
// version: 4
//! Committed Solana Program Metadata instruction projections.
/// Stable Solana Program Metadata instruction materializer.
#[derive(Clone, Debug, Default)]
pub struct MtSolanaProgramMetadataMaterializer;
/// Compatibility materializer retaining the historical generic metadata type name.
#[derive(Clone, Debug, Default)]
pub struct MtMetadataMaterializer;
impl crate::MtMaterializer for crate::MtSolanaProgramMetadataMaterializer {
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 event.event_family == crate::MdEventFamily::Metadata
&& event.program_id.0 == kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID
&& event.surface_code.0 == crate::DC_METADATA_SPM_SURFACE_CODE
&& crate::DC_METADATA_SPM_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::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 {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_METADATA_SPM_PROCESSOR_NAME.to_string(),
name: crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return crate::MT_METADATA_SPM_ACCEPTED_FAMILIES;
return crate::MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
@@ -55,7 +165,7 @@ impl crate::MtApiEventMaterializer for crate::MtMetadataMaterializer {
),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_METADATA_SPM_PROJECTION_VERSION,
"projectionVersion": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION,
"domain": "solana_program_metadata",
"projectionKind": "committed_instruction_mutation",
"projectionSemantics": "committed_instruction_fact_non_authoritative_state",
@@ -73,7 +183,7 @@ impl crate::MtApiEventMaterializer for crate::MtMetadataMaterializer {
"externalContentFetched": false,
"externalAccountRead": false,
"provenance": {
"processorName": crate::MT_METADATA_SPM_PROCESSOR_NAME,
"processorName": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"decoderProof": observation.proof.clone()
}
@@ -141,18 +251,18 @@ fn instruction_projection(operation: &str, payload: &serde_json::Value) -> serde
}
fn state_transition(operation: &str) -> &'static str {
match operation {
"write" => return "buffer_content_written",
"initialize" => return "metadata_account_initialized",
"set_authority" => return "account_authority_updated",
"set_data" => return "metadata_data_configuration_updated",
"set_immutable" => return "metadata_account_marked_immutable",
"trim" => return "account_capacity_trimmed",
"close" => return "account_closed",
"allocate" => return "buffer_account_allocated",
"extend" => return "account_capacity_extended",
_ => return "unknown",
}
return match operation {
"write" => "buffer_content_written",
"initialize" => "metadata_account_initialized",
"set_authority" => "account_authority_updated",
"set_data" => "metadata_data_configuration_updated",
"set_immutable" => "metadata_account_marked_immutable",
"trim" => "account_capacity_trimmed",
"close" => "account_closed",
"allocate" => "buffer_account_allocated",
"extend" => "account_capacity_extended",
_ => "unknown",
};
}
fn target_account(operation: &str, accounts: &serde_json::Value) -> serde_json::Value {
@@ -194,10 +304,6 @@ fn account_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Val
#[cfg(test)]
mod tests {
const MATRIX_JSON: &str = include_str!(
"../../../../../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_MATERIALIZATION_MATRIX.json"
);
fn observation(operation: &str, committed: bool) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: format!("solana_program_metadata:{operation}:0"),
@@ -249,7 +355,9 @@ mod tests {
#[test]
fn every_stable_instruction_has_one_materialized_mutation_fact() {
let materializer = crate::MtMetadataMaterializer;
let materializer = crate::MtSolanaProgramMetadataMaterializer;
let identity = crate::MtApiEventMaterializer::identity(&materializer);
assert_eq!(identity.name, crate::MT_METADATA_SOLANA_PROGRAM_METADATA_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));
@@ -259,12 +367,16 @@ mod tests {
assert_eq!(result.outputs[0].payload_json["operation"], *operation);
assert_eq!(result.outputs[0].payload_json["projection"]["deprecated"], false);
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
);
}
}
#[test]
fn failed_instruction_is_refused_and_foreign_surface_is_ignored() {
let materializer = crate::MtMetadataMaterializer;
let materializer = crate::MtSolanaProgramMetadataMaterializer;
let failed = observation("set_data", false);
let failure = crate::MtApiEventMaterializer::materialize(&materializer, &failed);
assert_eq!(failure.status, crate::MtApiMaterializerOutcomeStatus::Refused);
@@ -277,7 +389,9 @@ mod tests {
#[test]
fn materialization_matrix_matches_two_account_states_and_nine_instructions() {
let parsed = serde_json::from_str::<serde_json::Value>(MATRIX_JSON);
let parsed = serde_json::from_str::<serde_json::Value>(include_str!(
"../../../../../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_MATERIALIZATION_MATRIX.json"
));
assert!(parsed.is_ok());
let matrix = match parsed {
std::result::Result::Ok(value) => value,
@@ -310,7 +424,7 @@ mod tests {
#[test]
fn trim_keeps_prestable_alias_as_provenance_only() {
let materializer = crate::MtMetadataMaterializer;
let materializer = crate::MtSolanaProgramMetadataMaterializer;
let value = observation("trim", true);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &value);
assert_eq!(

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/metadata/solana_program_metadata/state.rs
// version: 2
// version: 4
//! Authoritative Solana Program Metadata account-state projections.
@@ -11,20 +11,18 @@ pub fn materializer_metadata_materialize_solana_program_metadata_account_snapsho
slot: u64,
snapshot: &crate::DcMetadataSpmAccountSnapshot,
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
match snapshot {
return match snapshot {
crate::DcMetadataSpmAccountSnapshot::Buffer(value) => {
return crate::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot(
slot,
value,
);
crate::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot(
slot, value,
)
},
crate::DcMetadataSpmAccountSnapshot::Metadata(value) => {
return crate::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot(
slot,
value,
);
crate::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot(
slot, value,
)
},
}
};
}
/// Materializes one bounded Solana Program Metadata Buffer account snapshot.
@@ -65,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_SPM_PROJECTION_VERSION,
"projectionVersion": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION,
"domain": "solana_program_metadata",
"projectionKind": "buffer_state",
"projectionSemantics": "authoritative_bounded_owned_account_snapshot",
@@ -85,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_SPM_PROCESSOR_NAME,
"processorName": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"source": "bounded_solana_program_metadata_account_decoder"
}
@@ -152,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_SPM_PROJECTION_VERSION,
"projectionVersion": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION,
"domain": "solana_program_metadata",
"projectionKind": "metadata_state",
"projectionSemantics": "authoritative_bounded_owned_account_snapshot",
@@ -179,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_SPM_PROCESSOR_NAME,
"processorName": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"source": "bounded_solana_program_metadata_account_decoder"
}
@@ -188,32 +186,32 @@ pub fn materializer_metadata_materialize_solana_program_metadata_metadata_snapsh
}
fn data_projection(data: &crate::DcMetadataSpmData) -> serde_json::Value {
match data {
return match data {
crate::DcMetadataSpmData::Direct(value) => {
let mut projection = bytes_projection(value.as_slice());
projection["kind"] = serde_json::json!("direct");
return projection;
projection
},
crate::DcMetadataSpmData::Url(value) => {
return serde_json::json!({
serde_json::json!({
"kind": "url",
"value": value,
"lengthBytes": value.len(),
"sha256": hash(value.as_bytes()),
"fetched": false
});
})
},
crate::DcMetadataSpmData::External(value) => {
return serde_json::json!({
serde_json::json!({
"kind": "external",
"address": value.address.to_string(),
"offset": value.offset,
"length": value.length.map(std::num::NonZeroU32::get),
"wireLengthBytes": crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES,
"accountRead": false
});
})
},
}
};
}
fn bytes_projection(bytes: &[u8]) -> serde_json::Value {

View File

@@ -0,0 +1,19 @@
// file: kb-lib/src/materializer/metadata/token_2022.rs
// version: 2
//! Embedded SPL Token-2022 metadata materialization surface.
mod constants;
mod state;
/// Materializes Token-2022 embedded metadata and group snapshots.
pub use self::state::materializer_metadata_materialize_token_2022_snapshot;
/// Stable runtime component name for Token-2022 embedded metadata projections.
pub(crate) use self::constants::MT_METADATA_TOKEN_2022_COMPONENT_NAME;
/// 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.
pub(crate) use self::constants::MT_METADATA_TOKEN_2022_PROJECTION_VERSION;
/// Canonical tracing target for Token-2022 embedded metadata materialization.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022;

View File

@@ -0,0 +1,15 @@
// file: kb-lib/src/materializer/metadata/token_2022/constants.rs
// version: 3
//! Local constants for the metadata token_2022 materializer.
/// Stable runtime component name for the metadata token_2022 materializer.
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";
/// Stable projection contract version.
pub(crate) const MT_METADATA_TOKEN_2022_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for the metadata token_2022 materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022: &str =
"kb-lib.materializer.metadata.token_2022";

View File

@@ -0,0 +1,210 @@
// file: kb-lib/src/materializer/metadata/token_2022/state.rs
// version: 4
//! Authoritative embedded SPL Token-2022 metadata state materialization.
/// Materialize embedded Token-2022 metadata, group, and group-member extensions.
pub fn materializer_metadata_materialize_token_2022_snapshot(
account_key: &str,
slot: u64,
state: &crate::DcToken2022State,
) -> std::result::Result<std::vec::Vec<crate::MtApiMaterializedOutput>, String> {
if account_key.is_empty() {
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022,
action = "materialize_token_2022_metadata_snapshot",
component_name = crate::MT_METADATA_TOKEN_2022_COMPONENT_NAME,
processor_name = crate::MT_METADATA_TOKEN_2022_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
account_key = "",
slot,
status = "refused",
error_code = "empty_account_key",
"refuse Token-2022 metadata snapshot without account identity"
);
return std::result::Result::Err(
"Token-2022 metadata snapshot requires a non-empty account key".to_string(),
);
}
if state.kind != crate::DcToken2022StateKind::Mint {
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022,
action = "materialize_token_2022_metadata_snapshot",
component_name = crate::MT_METADATA_TOKEN_2022_COMPONENT_NAME,
processor_name = crate::MT_METADATA_TOKEN_2022_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
account_key = %account_key,
slot,
state_kind = ?state.kind,
status = "refused",
error_code = "non_mint_state",
"refuse Token-2022 metadata snapshot for non-mint state"
);
return std::result::Result::Err(
"Token-2022 metadata snapshot requires a parsed mint state".to_string(),
);
}
let mut outputs = std::vec::Vec::new();
for entry in &state.extensions {
let projection_kind = match entry.extension_name {
"token_metadata" => std::option::Option::Some("token_metadata"),
"token_group" => std::option::Option::Some("token_group"),
"token_group_member" => std::option::Option::Some("token_group_member"),
_ => std::option::Option::None,
};
let kind = match projection_kind {
std::option::Option::Some(value) => value,
std::option::Option::None => continue,
};
outputs.push(crate::MtApiMaterializedOutput {
output_key: format!("token_2022_metadata_snapshot:{account_key}:{slot}:{kind}"),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_METADATA_TOKEN_2022_PROJECTION_VERSION,
"domain": "spl_token_2022_embedded_metadata",
"projectionSemantics": "authoritative_bounded_mint_extension_snapshot",
"idempotenceKey": format!("token_2022_metadata:{account_key}:{slot}:{kind}"),
"accountKey": account_key,
"slot": slot,
"programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"metadataSource": "token_2022_embedded_tlv",
"projectionKind": kind,
"extensionType": entry.extension_type,
"extensionName": entry.extension_name,
"valueHex": entry.value_hex,
"valueFields": entry.value_fields,
"externalUriFetched": false,
"identityAuthority": "mint_address",
"finalAccountStateCaptured": true,
"provenance": {
"processorName": crate::MT_METADATA_TOKEN_2022_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"source": "bounded_token_2022_state_parser"
}
}),
});
}
let status = if outputs.is_empty() { "ignored" } else { "inserted" };
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022,
action = "materialize_token_2022_metadata_snapshot",
component_name = crate::MT_METADATA_TOKEN_2022_COMPONENT_NAME,
processor_name = crate::MT_METADATA_TOKEN_2022_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
account_key = %account_key,
slot,
state_kind = "mint",
output_count = outputs.len(),
status,
"materialize embedded Token-2022 metadata state snapshots"
);
return std::result::Result::Ok(outputs);
}
#[cfg(test)]
mod tests_token_2022_state {
#[test]
fn embedded_metadata_group_and_member_have_distinct_stable_outputs() {
let state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Mint,
base_fields: serde_json::json!({"supply":"1","decimals":0}),
base_hex: "00".repeat(82),
account_type: std::option::Option::Some(1),
extensions: std::vec![
crate::DcToken2022TlvEntry {
extension_type: 19,
extension_name: "token_metadata",
value_hex: "01".to_string(),
value_fields: serde_json::json!({"name":"Token","symbol":"TOK","uri":"https://example.invalid/token.json"}),
},
crate::DcToken2022TlvEntry {
extension_type: 20,
extension_name: "token_group",
value_hex: "02".to_string(),
value_fields: serde_json::json!({"mint":"mint111","size":"1","maxSize":"10"}),
},
crate::DcToken2022TlvEntry {
extension_type: 21,
extension_name: "token_group_member",
value_hex: "03".to_string(),
value_fields: serde_json::json!({"mint":"mint111","group":"group111","memberNumber":"1"}),
},
],
};
let outputs =
crate::materializer_metadata_materialize_token_2022_snapshot("mint111", 9, &state);
assert_eq!(outputs.as_ref().map(|values| return values.len()), std::result::Result::Ok(3));
assert_eq!(
outputs
.as_ref()
.map(|values| return values[0].payload_json["metadataSource"].clone()),
std::result::Result::Ok(serde_json::json!("token_2022_embedded_tlv"))
);
assert_eq!(
outputs
.as_ref()
.map(|values| return values[0].payload_json["externalUriFetched"].clone()),
std::result::Result::Ok(serde_json::json!(false))
);
assert_eq!(
outputs
.as_ref()
.map(|values| return values[1].payload_json["projectionKind"].clone()),
std::result::Result::Ok(serde_json::json!("token_group"))
);
assert_eq!(
outputs.as_ref().map(|values| {
return values[0].payload_json["provenance"]["processorName"].clone();
}),
std::result::Result::Ok(serde_json::json!(
crate::MT_METADATA_TOKEN_2022_PROCESSOR_NAME
))
);
assert_eq!(
outputs
.as_ref()
.map(|values| return values[0].payload_json["projectionVersion"].clone()),
std::result::Result::Ok(serde_json::json!(2))
);
}
#[test]
fn snapshot_is_idempotent_filters_unrelated_extensions_and_requires_mint_identity() {
let state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Mint,
base_fields: serde_json::json!({}),
base_hex: "00".repeat(82),
account_type: std::option::Option::Some(1),
extensions: std::vec![crate::DcToken2022TlvEntry {
extension_type: 1,
extension_name: "transfer_fee_config",
value_hex: "00".to_string(),
value_fields: serde_json::json!({}),
}],
};
let first =
crate::materializer_metadata_materialize_token_2022_snapshot("mint111", 9, &state);
let second =
crate::materializer_metadata_materialize_token_2022_snapshot("mint111", 9, &state);
assert_eq!(first, second);
assert_eq!(first.as_ref().map(|values| return values.len()), std::result::Result::Ok(0));
assert!(
crate::materializer_metadata_materialize_token_2022_snapshot("", 9, &state).is_err()
);
let account_state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Account,
base_fields: serde_json::json!({}),
base_hex: "00".repeat(165),
account_type: std::option::Option::None,
extensions: std::vec::Vec::new(),
};
assert!(
crate::materializer_metadata_materialize_token_2022_snapshot(
"account111",
9,
&account_state
)
.is_err()
);
}
}

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/nft.rs
// version: 3
// version: 4
//! `nft` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved NFT materializer.
pub use self::core::MtNftMaterializer;
pub use self::materializer::MtNftMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_NFT_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `nft` materializer.
pub(crate) use self::constants::MT_NFT_COMPONENT_NAME;
/// Stable processor name for the `nft` materializer.
pub(crate) use self::constants::MT_NFT_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/nft/constants.rs
// version: 1
//! Local constants for the nft materializer.
/// Stable runtime component name for the nft materializer.
pub(crate) const MT_NFT_COMPONENT_NAME: &str = "kb-lib.materializer.nft";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_NFT_PROCESSOR_NAME: &str = "materializer.nft";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_NFT_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/nft/core.rs
// version: 4
// file: kb-lib/src/materializer/nft/materializer.rs
// version: 5
//! Reserved NFT materializer.
@@ -9,7 +9,7 @@ pub struct MtNftMaterializer;
impl crate::MtMaterializer for crate::MtNftMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.nft";
return crate::MT_NFT_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtNftMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
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::Nft,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtNftMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.nft".to_string(),
name: crate::MT_NFT_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_NFT_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/oracle.rs
// version: 3
// version: 4
//! `oracle` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved oracle materializer.
pub use self::core::MtOracleMaterializer;
pub use self::materializer::MtOracleMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_ORACLE_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `oracle` materializer.
pub(crate) use self::constants::MT_ORACLE_COMPONENT_NAME;
/// Stable processor name for the `oracle` materializer.
pub(crate) use self::constants::MT_ORACLE_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/oracle/constants.rs
// version: 1
//! Local constants for the oracle materializer.
/// Stable runtime component name for the oracle materializer.
pub(crate) const MT_ORACLE_COMPONENT_NAME: &str = "kb-lib.materializer.oracle";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_ORACLE_PROCESSOR_NAME: &str = "materializer.oracle";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_ORACLE_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/oracle/core.rs
// version: 4
// file: kb-lib/src/materializer/oracle/materializer.rs
// version: 5
//! Reserved oracle materializer.
@@ -9,7 +9,7 @@ pub struct MtOracleMaterializer;
impl crate::MtMaterializer for crate::MtOracleMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.oracle";
return crate::MT_ORACLE_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtOracleMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
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::Oracle,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtOracleMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.oracle".to_string(),
name: crate::MT_ORACLE_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_ORACLE_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/orderbook.rs
// version: 3
// version: 4
//! `orderbook` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved orderbook materializer.
pub use self::core::MtOrderbookMaterializer;
pub use self::materializer::MtOrderbookMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_ORDERBOOK_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `orderbook` materializer.
pub(crate) use self::constants::MT_ORDERBOOK_COMPONENT_NAME;
/// Stable processor name for the `orderbook` materializer.
pub(crate) use self::constants::MT_ORDERBOOK_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/orderbook/constants.rs
// version: 1
//! Local constants for the orderbook materializer.
/// Stable runtime component name for the orderbook materializer.
pub(crate) const MT_ORDERBOOK_COMPONENT_NAME: &str = "kb-lib.materializer.orderbook";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_ORDERBOOK_PROCESSOR_NAME: &str = "materializer.orderbook";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_ORDERBOOK_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/orderbook/core.rs
// version: 4
// file: kb-lib/src/materializer/orderbook/materializer.rs
// version: 5
//! Reserved orderbook materializer.
@@ -9,7 +9,7 @@ pub struct MtOrderbookMaterializer;
impl crate::MtMaterializer for crate::MtOrderbookMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.orderbook";
return crate::MT_ORDERBOOK_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtOrderbookMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
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::Orderbook,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtOrderbookMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.orderbook".to_string(),
name: crate::MT_ORDERBOOK_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_ORDERBOOK_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/perpetuals.rs
// version: 3
// version: 4
//! `perpetuals` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved perpetuals materializer.
pub use self::core::MtPerpetualsMaterializer;
pub use self::materializer::MtPerpetualsMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_PERPETUALS_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `perpetuals` materializer.
pub(crate) use self::constants::MT_PERPETUALS_COMPONENT_NAME;
/// Stable processor name for the `perpetuals` materializer.
pub(crate) use self::constants::MT_PERPETUALS_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/perpetuals/constants.rs
// version: 1
//! Local constants for the perpetuals materializer.
/// Stable runtime component name for the perpetuals materializer.
pub(crate) const MT_PERPETUALS_COMPONENT_NAME: &str = "kb-lib.materializer.perpetuals";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_PERPETUALS_PROCESSOR_NAME: &str = "materializer.perpetuals";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_PERPETUALS_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/perpetuals/core.rs
// version: 3
// file: kb-lib/src/materializer/perpetuals/materializer.rs
// version: 4
//! Reserved perpetuals materializer.
@@ -9,7 +9,7 @@ pub struct MtPerpetualsMaterializer;
impl crate::MtMaterializer for crate::MtPerpetualsMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.perpetuals";
return crate::MT_PERPETUALS_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtPerpetualsMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
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::Perpetuals,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtPerpetualsMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.perpetuals".to_string(),
name: crate::MT_PERPETUALS_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_PERPETUALS_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/pool.rs
// version: 3
// version: 4
//! `pool` materializer family.
@@ -7,3 +7,10 @@ mod state;
/// Reserved pool-state materializer.
pub use self::state::MtPoolStateMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::state::MT_POOL_STATE_ACCEPTED_FAMILIES;
/// Stable runtime component name for the reserved pool-state materializer.
pub(crate) use self::state::MT_POOL_STATE_COMPONENT_NAME;
/// Stable processor name for the reserved pool-state materializer.
pub(crate) use self::state::MT_POOL_STATE_PROCESSOR_NAME;

View File

@@ -1,61 +1,17 @@
// file: kb-lib/src/materializer/pool/state.rs
// version: 4
// version: 5
//! Reserved pool-state materializer.
//! Pool-state materializer component facade.
/// Reserved materializer for pool-state observations.
#[derive(Clone, Debug, Default)]
pub struct MtPoolStateMaterializer;
mod constants;
mod materializer;
impl crate::MtMaterializer for crate::MtPoolStateMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.pool.state";
}
/// Reserved pool-state materializer.
pub use self::materializer::MtPoolStateMaterializer;
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, _event: &crate::MdDecodedProtocolEvent) -> bool {
return false;
}
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
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::PoolState,
}]);
}
}
impl crate::MtApiEventMaterializer for crate::MtPoolStateMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.pool.state".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
_observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
return crate::MtApiMaterializerExecutionResult::ignored();
}
}
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_POOL_STATE_ACCEPTED_FAMILIES;
/// Stable runtime component name for the reserved pool-state materializer.
pub(crate) use self::constants::MT_POOL_STATE_COMPONENT_NAME;
/// Stable processor name for the reserved pool-state materializer.
pub(crate) use self::constants::MT_POOL_STATE_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/pool/state/constants.rs
// version: 1
//! Local constants for the pool state materializer.
/// Stable runtime component name for the pool state materializer.
pub(crate) const MT_POOL_STATE_COMPONENT_NAME: &str = "kb-lib.materializer.pool.state";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_POOL_STATE_PROCESSOR_NAME: &str = "materializer.pool.state";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_POOL_STATE_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -0,0 +1,56 @@
// file: kb-lib/src/materializer/pool/state/materializer.rs
// version: 5
//! Reserved pool-state materializer.
/// Reserved materializer for pool-state observations.
#[derive(Clone, Debug, Default)]
pub struct MtPoolStateMaterializer;
impl crate::MtMaterializer for crate::MtPoolStateMaterializer {
fn materializer_name(&self) -> &'static str {
return crate::MT_POOL_STATE_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, _event: &crate::MdDecodedProtocolEvent) -> bool {
return false;
}
fn materialize_event(
&self,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtPoolStateMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_POOL_STATE_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return crate::MT_POOL_STATE_ACCEPTED_FAMILIES;
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
_observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
return crate::MtApiMaterializerExecutionResult::ignored();
}
}

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/rewards.rs
// version: 3
// version: 4
//! `rewards` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved rewards materializer.
pub use self::core::MtRewardsMaterializer;
pub use self::materializer::MtRewardsMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_REWARDS_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `rewards` materializer.
pub(crate) use self::constants::MT_REWARDS_COMPONENT_NAME;
/// Stable processor name for the `rewards` materializer.
pub(crate) use self::constants::MT_REWARDS_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/rewards/constants.rs
// version: 1
//! Local constants for the rewards materializer.
/// Stable runtime component name for the rewards materializer.
pub(crate) const MT_REWARDS_COMPONENT_NAME: &str = "kb-lib.materializer.rewards";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_REWARDS_PROCESSOR_NAME: &str = "materializer.rewards";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_REWARDS_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,15 +1,15 @@
// file: kb-lib/src/materializer/rewards/core.rs
// version: 4
// file: kb-lib/src/materializer/rewards/materializer.rs
// version: 5
//! Reserved rewards materializer.
/// Reserved materializer for reward observations.
/// Reserved materializer for rewards observations.
#[derive(Clone, Debug, Default)]
pub struct MtRewardsMaterializer;
impl crate::MtMaterializer for crate::MtRewardsMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.rewards";
return crate::MT_REWARDS_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtRewardsMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
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::Reward,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtRewardsMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.rewards".to_string(),
name: crate::MT_REWARDS_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_REWARDS_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,17 +1,23 @@
// file: kb-lib/src/materializer/risk.rs
// version: 3
// version: 4
//! `risk` materializer family.
mod constants;
mod core;
mod materializer;
/// Stable SPL Token and ATA risk materializer.
pub use self::core::MtRiskMaterializer;
pub use self::materializer::MtRiskMaterializer;
/// Stable processor name for SPL Token and ATA risk facts.
/// Crate-visible `MT_RISK_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::constants::MT_RISK_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `risk` materializer.
pub(crate) use self::constants::MT_RISK_COMPONENT_NAME;
/// Stable processor name for the `risk` materializer.
pub(crate) use self::constants::MT_RISK_PROCESSOR_NAME;
/// Stable projection contract version for SPL Token and ATA risk facts.
/// Stable projection version for the `risk` materializer.
pub(crate) use self::constants::MT_RISK_PROJECTION_VERSION;
/// Canonical tracing target for SPL Token and ATA risk facts.
/// Crate-visible `MT_RISK_RISK_ENTRIES` component constant.
pub(crate) use self::constants::MT_RISK_RISK_ENTRIES;
/// Canonical tracing target for the `risk` materializer.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_RISK;

View File

@@ -1,11 +1,48 @@
// file: kb-lib/src/materializer/risk/constants.rs
// version: 3
// version: 5
//! Local constants for fact-only risk projections.
//! Local constants for the risk materializer.
/// Stable runtime component name for the risk materializer.
pub(crate) const MT_RISK_COMPONENT_NAME: &str = "kb-lib.materializer.risk";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_RISK_PROCESSOR_NAME: &str = "materializer.risk";
/// Stable projection payload contract version.
/// Stable projection contract version for the risk materializer.
pub(crate) const MT_RISK_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for this component.
/// Canonical tracing target for the risk materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_RISK: &str = "kb-lib.materializer.risk";
/// Component contract for `accepted_families`.
pub(crate) const MT_RISK_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::TokenAccount,
crate::MdEventFamily::TokenMint,
crate::MdEventFamily::Admin,
crate::MdEventFamily::Lifecycle,
crate::MdEventFamily::Audit,
];
/// Component contract for `risk_entries`.
pub(crate) const MT_RISK_RISK_ENTRIES: &[&str] = &[
"approve",
"approve_checked",
"revoke",
"freeze_account",
"thaw_account",
"set_authority",
"initialize_multisig",
"initialize_multisig2",
"initialize_default_account_state",
"update_default_account_state",
"enable_required_transfer_memos",
"disable_required_transfer_memos",
"enable_cpi_guard",
"disable_cpi_guard",
"initialize_non_transferable_mint",
"initialize_pausable_config",
"pause",
"resume",
"initialize_permissioned_burn",
"enable_confidential_credits",
"disable_confidential_credits",
"enable_non_confidential_credits",
"disable_non_confidential_credits",
];

View File

@@ -1,48 +1,15 @@
// file: kb-lib/src/materializer/risk/core.rs
// version: 6
// file: kb-lib/src/materializer/risk/materializer.rs
// version: 8
//! Stable fact-only risk projections for committed SPL Token and ATA observations.
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::TokenAccount,
crate::MdEventFamily::TokenMint,
crate::MdEventFamily::Admin,
crate::MdEventFamily::Lifecycle,
crate::MdEventFamily::Audit,
];
const RISK_ENTRIES: &[&str] = &[
"approve",
"approve_checked",
"revoke",
"freeze_account",
"thaw_account",
"set_authority",
"initialize_multisig",
"initialize_multisig2",
"initialize_default_account_state",
"update_default_account_state",
"enable_required_transfer_memos",
"disable_required_transfer_memos",
"enable_cpi_guard",
"disable_cpi_guard",
"initialize_non_transferable_mint",
"initialize_pausable_config",
"pause",
"resume",
"initialize_permissioned_burn",
"enable_confidential_credits",
"disable_confidential_credits",
"enable_non_confidential_credits",
"disable_non_confidential_credits",
];
/// Stable fact-only classic SPL Token risk materializer.
#[derive(Clone, Debug, Default)]
pub struct MtRiskMaterializer;
impl crate::MtMaterializer for crate::MtRiskMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.risk";
return crate::MT_RISK_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
@@ -66,7 +33,7 @@ impl crate::MtApiEventMaterializer for crate::MtRiskMaterializer {
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
return crate::MT_RISK_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return accepts_event(&observation.event);
@@ -171,8 +138,9 @@ fn accepts_event(event: &crate::MdDecodedProtocolEvent) -> bool {
&& event.surface_code.0 == "spl.token")
|| (event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
&& event.surface_code.0 == "spl.token_2022");
let token_risk = token_program && RISK_ENTRIES.contains(&event.event_name.0.as_str());
return ACCEPTED_FAMILIES.contains(&event.event_family)
let token_risk =
token_program && crate::MT_RISK_RISK_ENTRIES.contains(&event.event_name.0.as_str());
return crate::MT_RISK_ACCEPTED_FAMILIES.contains(&event.event_family)
&& (token_risk || is_ata_recovery(event));
}

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/routing.rs
// version: 3
// version: 4
//! `routing` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved routing materializer.
pub use self::core::MtRoutingMaterializer;
pub use self::materializer::MtRoutingMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_ROUTING_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `routing` materializer.
pub(crate) use self::constants::MT_ROUTING_COMPONENT_NAME;
/// Stable processor name for the `routing` materializer.
pub(crate) use self::constants::MT_ROUTING_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/routing/constants.rs
// version: 1
//! Local constants for the routing materializer.
/// Stable runtime component name for the routing materializer.
pub(crate) const MT_ROUTING_COMPONENT_NAME: &str = "kb-lib.materializer.routing";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_ROUTING_PROCESSOR_NAME: &str = "materializer.routing";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_ROUTING_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/routing/core.rs
// version: 4
// file: kb-lib/src/materializer/routing/materializer.rs
// version: 5
//! Reserved routing materializer.
@@ -9,7 +9,7 @@ pub struct MtRoutingMaterializer;
impl crate::MtMaterializer for crate::MtRoutingMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.routing";
return crate::MT_ROUTING_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtRoutingMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
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::Routing,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtRoutingMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.routing".to_string(),
name: crate::MT_ROUTING_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_ROUTING_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,13 +1,45 @@
// file: kb-lib/src/materializer/staking.rs
// version: 4
// version: 5
//! `staking` materializer family.
mod constants;
mod core;
mod materializer;
/// Stable native stake and vote materializer.
pub use self::core::MtStakingMaterializer;
pub use self::materializer::MtStakingMaterializer;
/// Canonical tracing target for the native stake and vote materializer.
/// Crate-visible `MT_STAKING_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::constants::MT_STAKING_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `staking` materializer.
pub(crate) use self::constants::MT_STAKING_COMPONENT_NAME;
/// Stable processor name for the `staking` materializer.
pub(crate) use self::constants::MT_STAKING_PROCESSOR_NAME;
/// Stable projection version for the `staking` materializer.
pub(crate) use self::constants::MT_STAKING_PROJECTION_VERSION;
/// Crate-visible `MT_STAKING_STAKE_ACCOUNT_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_STAKE_ACCOUNT_ENTRIES;
/// Crate-visible `MT_STAKING_STAKE_AUTHORITY_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_STAKE_AUTHORITY_ENTRIES;
/// Crate-visible `MT_STAKING_STAKE_LOCKUP_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_STAKE_LOCKUP_ENTRIES;
/// Crate-visible `MT_STAKING_STAKE_SURFACE` component constant.
pub(crate) use self::constants::MT_STAKING_STAKE_SURFACE;
/// Crate-visible `MT_STAKING_STAKE_VALUE_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_STAKE_VALUE_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_ACCOUNT_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_VOTE_ACCOUNT_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_ADMIN_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_VOTE_ADMIN_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_AUTHORITY_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_VOTE_AUTHORITY_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_REWARD_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_VOTE_REWARD_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_STATE_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_VOTE_STATE_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_SURFACE` component constant.
pub(crate) use self::constants::MT_STAKING_VOTE_SURFACE;
/// Crate-visible `MT_STAKING_VOTE_VALUE_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_VOTE_VALUE_ENTRIES;
/// Canonical tracing target for the `staking` materializer.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_STAKING;

View File

@@ -1,7 +1,77 @@
// file: kb-lib/src/materializer/staking/constants.rs
// version: 2
// version: 4
//! Local constants for the native stake and vote materializer.
//! Local constants for the staking materializer.
/// Canonical tracing target for this component.
/// Stable runtime component name for the staking materializer.
pub(crate) const MT_STAKING_COMPONENT_NAME: &str = "kb-lib.materializer.staking";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_STAKING_PROCESSOR_NAME: &str = "materializer.staking";
/// Stable projection contract version for the staking materializer.
pub(crate) const MT_STAKING_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for the staking materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_STAKING: &str = "kb-lib.materializer.staking";
/// Component contract for `accepted_families`.
pub(crate) const MT_STAKING_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::Lifecycle,
crate::MdEventFamily::Admin,
crate::MdEventFamily::Staking,
crate::MdEventFamily::Reward,
];
/// Component contract for `stake_surface`.
pub(crate) const MT_STAKING_STAKE_SURFACE: &str = "solana.core.stake";
/// Component contract for `vote_surface`.
pub(crate) const MT_STAKING_VOTE_SURFACE: &str = "solana.core.vote";
/// Component contract for `stake_account_entries`.
pub(crate) const MT_STAKING_STAKE_ACCOUNT_ENTRIES: &[&str] = &[
"initialize",
"initialize_checked",
"delegate_stake",
"deactivate",
"deactivate_delinquent",
];
/// Component contract for `stake_value_entries`.
pub(crate) const MT_STAKING_STAKE_VALUE_ENTRIES: &[&str] =
&["split", "withdraw", "merge", "move_stake", "move_lamports"];
/// Component contract for `stake_authority_entries`.
pub(crate) const MT_STAKING_STAKE_AUTHORITY_ENTRIES: &[&str] = &[
"authorize",
"authorize_with_seed",
"authorize_checked",
"authorize_checked_with_seed",
];
/// Component contract for `stake_lockup_entries`.
pub(crate) const MT_STAKING_STAKE_LOCKUP_ENTRIES: &[&str] = &["set_lockup", "set_lockup_checked"];
/// Component contract for `vote_account_entries`.
pub(crate) const MT_STAKING_VOTE_ACCOUNT_ENTRIES: &[&str] =
&["initialize_account", "initialize_account_v2"];
/// Component contract for `vote_authority_entries`.
pub(crate) const MT_STAKING_VOTE_AUTHORITY_ENTRIES: &[&str] = &[
"authorize",
"authorize_checked",
"authorize_with_seed",
"authorize_checked_with_seed",
];
/// Component contract for `vote_admin_entries`.
pub(crate) const MT_STAKING_VOTE_ADMIN_ENTRIES: &[&str] = &[
"update_validator_identity",
"update_commission",
"update_commission_collector",
"update_commission_bps",
];
/// Component contract for `vote_state_entries`.
pub(crate) const MT_STAKING_VOTE_STATE_ENTRIES: &[&str] = &[
"vote",
"vote_switch",
"update_vote_state",
"update_vote_state_switch",
"compact_update_vote_state",
"compact_update_vote_state_switch",
"tower_sync",
"tower_sync_switch",
];
/// Component contract for `vote_value_entries`.
pub(crate) const MT_STAKING_VOTE_VALUE_ENTRIES: &[&str] = &["withdraw"];
/// Component contract for `vote_reward_entries`.
pub(crate) const MT_STAKING_VOTE_REWARD_ENTRIES: &[&str] = &["deposit_delegator_rewards"];

View File

@@ -1,57 +1,8 @@
// file: kb-lib/src/materializer/staking/core.rs
// version: 10
// file: kb-lib/src/materializer/staking/materializer.rs
// version: 12
//! Instruction-level staking projections for native Stake and Vote observations.
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::Lifecycle,
crate::MdEventFamily::Admin,
crate::MdEventFamily::Staking,
crate::MdEventFamily::Reward,
];
const STAKE_SURFACE: &str = "solana.core.stake";
const VOTE_SURFACE: &str = "solana.core.vote";
const STAKE_ACCOUNT_ENTRIES: &[&str] = &[
"initialize",
"initialize_checked",
"delegate_stake",
"deactivate",
"deactivate_delinquent",
];
const STAKE_VALUE_ENTRIES: &[&str] = &["split", "withdraw", "merge", "move_stake", "move_lamports"];
const STAKE_AUTHORITY_ENTRIES: &[&str] = &[
"authorize",
"authorize_with_seed",
"authorize_checked",
"authorize_checked_with_seed",
];
const STAKE_LOCKUP_ENTRIES: &[&str] = &["set_lockup", "set_lockup_checked"];
const VOTE_ACCOUNT_ENTRIES: &[&str] = &["initialize_account", "initialize_account_v2"];
const VOTE_AUTHORITY_ENTRIES: &[&str] = &[
"authorize",
"authorize_checked",
"authorize_with_seed",
"authorize_checked_with_seed",
];
const VOTE_ADMIN_ENTRIES: &[&str] = &[
"update_validator_identity",
"update_commission",
"update_commission_collector",
"update_commission_bps",
];
const VOTE_STATE_ENTRIES: &[&str] = &[
"vote",
"vote_switch",
"update_vote_state",
"update_vote_state_switch",
"compact_update_vote_state",
"compact_update_vote_state_switch",
"tower_sync",
"tower_sync_switch",
];
const VOTE_VALUE_ENTRIES: &[&str] = &["withdraw"];
const VOTE_REWARD_ENTRIES: &[&str] = &["deposit_delegator_rewards"];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ProjectionKind {
StakeAccount,
@@ -100,7 +51,7 @@ pub struct MtStakingMaterializer;
impl crate::MtMaterializer for crate::MtStakingMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.staking";
return crate::MT_STAKING_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -126,13 +77,13 @@ impl crate::MtMaterializer for crate::MtStakingMaterializer {
impl crate::MtApiEventMaterializer for crate::MtStakingMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "materializer.staking".to_string(),
name: crate::MT_STAKING_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
return crate::MT_STAKING_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
@@ -205,7 +156,7 @@ impl crate::MtApiEventMaterializer for crate::MtStakingMaterializer {
output_key: projection.output_key(operation.as_str()),
family: projection.family(),
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionVersion": crate::MT_STAKING_PROJECTION_VERSION,
"projectionSemantics": "committed_instruction_staking_event",
"domain": projection.domain(),
"operation": operation,
@@ -214,6 +165,10 @@ impl crate::MtApiEventMaterializer for crate::MtStakingMaterializer {
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"transactionSucceeded": true,
"provenance": {
"processorName": crate::MT_STAKING_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
},
"accounts": accounts,
"parameters": parameters,
"projection": projection_details(
@@ -242,52 +197,54 @@ impl crate::MtApiEventMaterializer for crate::MtStakingMaterializer {
}
fn family_is_accepted(family: crate::MdEventFamily) -> bool {
return ACCEPTED_FAMILIES.iter().any(|accepted| return *accepted == family);
return crate::MT_STAKING_ACCEPTED_FAMILIES
.iter()
.any(|accepted| return *accepted == family);
}
fn projection(surface_code: &str, entry_code: &str) -> std::option::Option<ProjectionKind> {
if surface_code == STAKE_SURFACE {
if surface_code == crate::MT_STAKING_STAKE_SURFACE {
return stake_projection(entry_code);
}
if surface_code == VOTE_SURFACE {
if surface_code == crate::MT_STAKING_VOTE_SURFACE {
return vote_projection(entry_code);
}
return std::option::Option::None;
}
fn stake_projection(entry_code: &str) -> std::option::Option<ProjectionKind> {
if contains(STAKE_ACCOUNT_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_STAKE_ACCOUNT_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::StakeAccount);
}
if contains(STAKE_VALUE_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_STAKE_VALUE_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::StakeValue);
}
if contains(STAKE_AUTHORITY_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_STAKE_AUTHORITY_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::StakeAuthority);
}
if contains(STAKE_LOCKUP_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_STAKE_LOCKUP_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::StakeLockup);
}
return std::option::Option::None;
}
fn vote_projection(entry_code: &str) -> std::option::Option<ProjectionKind> {
if contains(VOTE_ACCOUNT_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_VOTE_ACCOUNT_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::VoteAccount);
}
if contains(VOTE_AUTHORITY_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_VOTE_AUTHORITY_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::VoteAuthority);
}
if contains(VOTE_ADMIN_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_VOTE_ADMIN_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::VoteAdmin);
}
if contains(VOTE_STATE_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_VOTE_STATE_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::VoteState);
}
if contains(VOTE_VALUE_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_VOTE_VALUE_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::VoteValue);
}
if contains(VOTE_REWARD_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_VOTE_REWARD_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::VoteReward);
}
return std::option::Option::None;
@@ -637,7 +594,7 @@ mod tests {
fn stake_initialize_creates_instructional_staking_projection() {
let materializer = crate::MtStakingMaterializer;
let observation = observation(
super::STAKE_SURFACE,
crate::MT_STAKING_STAKE_SURFACE,
"initialize",
crate::MdEventFamily::Lifecycle,
false,
@@ -659,7 +616,7 @@ mod tests {
fn stake_authority_and_lockup_events_are_projected() {
let materializer = crate::MtStakingMaterializer;
let authority = observation(
super::STAKE_SURFACE,
crate::MT_STAKING_STAKE_SURFACE,
"authorize_with_seed",
crate::MdEventFamily::Admin,
false,
@@ -672,7 +629,7 @@ mod tests {
}),
);
let lockup = observation(
super::STAKE_SURFACE,
crate::MT_STAKING_STAKE_SURFACE,
"set_lockup_checked",
crate::MdEventFamily::Admin,
false,
@@ -701,7 +658,7 @@ mod tests {
fn stake_value_projection_does_not_duplicate_lamport_changes() {
let materializer = crate::MtStakingMaterializer;
let observation = observation(
super::STAKE_SURFACE,
crate::MT_STAKING_STAKE_SURFACE,
"withdraw",
crate::MdEventFamily::Staking,
false,
@@ -724,7 +681,7 @@ mod tests {
fn vote_account_and_authority_events_are_projected() {
let materializer = crate::MtStakingMaterializer;
let initialize = observation(
super::VOTE_SURFACE,
crate::MT_STAKING_VOTE_SURFACE,
"initialize_account_v2",
crate::MdEventFamily::Lifecycle,
false,
@@ -739,7 +696,7 @@ mod tests {
}),
);
let authorize = observation(
super::VOTE_SURFACE,
crate::MT_STAKING_VOTE_SURFACE,
"authorize_checked",
crate::MdEventFamily::Admin,
false,
@@ -769,7 +726,7 @@ mod tests {
fn vote_state_and_reward_outputs_are_distinguished() {
let materializer = crate::MtStakingMaterializer;
let vote_state = observation(
super::VOTE_SURFACE,
crate::MT_STAKING_VOTE_SURFACE,
"tower_sync_switch",
crate::MdEventFamily::Staking,
false,
@@ -780,7 +737,7 @@ mod tests {
serde_json::json!({"root": 10, "hash": "hash111", "switchProofHash": "switch111"}),
);
let reward = observation(
super::VOTE_SURFACE,
crate::MT_STAKING_VOTE_SURFACE,
"deposit_delegator_rewards",
crate::MdEventFamily::Reward,
false,
@@ -805,7 +762,7 @@ mod tests {
fn failed_staking_mutation_is_refused() {
let materializer = crate::MtStakingMaterializer;
let observation = observation(
super::STAKE_SURFACE,
crate::MT_STAKING_STAKE_SURFACE,
"delegate_stake",
crate::MdEventFamily::Staking,
true,
@@ -829,7 +786,7 @@ mod tests {
serde_json::json!({}),
);
let redelegate = observation(
super::STAKE_SURFACE,
crate::MT_STAKING_STAKE_SURFACE,
"redelegate",
crate::MdEventFamily::Staking,
false,
@@ -848,7 +805,7 @@ mod tests {
fn output_serialization_is_deterministic() {
let materializer = crate::MtStakingMaterializer;
let observation = observation(
super::VOTE_SURFACE,
crate::MT_STAKING_VOTE_SURFACE,
"update_commission_bps",
crate::MdEventFamily::Admin,
false,

View File

@@ -1,10 +1,9 @@
// file: kb-lib/src/materializer/token.rs
// version: 6
// version: 7
//! `token` materializer family.
mod accounts;
mod constants;
mod metadata_risk;
/// Stable Token, ATA and Token-2022 account materializer.
@@ -14,9 +13,23 @@ pub use self::accounts::materializer_token_materialize_token_2022_state_snapshot
/// Reserved token-metadata risk materializer.
pub use self::metadata_risk::MtTokenMetadataRiskMaterializer;
/// Crate-visible `MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::accounts::MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES;
/// Crate-visible `MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES` component constant.
pub(crate) use self::accounts::MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES;
/// Stable runtime component name for Token, ATA and Token-2022 account projections.
pub(crate) use self::accounts::MT_TOKEN_ACCOUNTS_COMPONENT_NAME;
/// Crate-visible `MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES` component constant.
pub(crate) use self::accounts::MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES;
/// Stable processor name for Token, ATA and Token-2022 account projections.
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME;
pub(crate) use self::accounts::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME;
/// Stable projection contract version for Token, ATA and Token-2022 account projections.
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION;
pub(crate) use self::accounts::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION;
/// Canonical tracing target for Token, ATA and Token-2022 account projections.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS;
pub(crate) use self::accounts::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::metadata_risk::MT_TOKEN_METADATA_RISK_ACCEPTED_FAMILIES;
/// Stable runtime component name for the reserved token-metadata risk materializer.
pub(crate) use self::metadata_risk::MT_TOKEN_METADATA_RISK_COMPONENT_NAME;
/// Stable processor name for the reserved token-metadata risk materializer.
pub(crate) use self::metadata_risk::MT_TOKEN_METADATA_RISK_PROCESSOR_NAME;

View File

@@ -1,842 +1,27 @@
// file: kb-lib/src/materializer/token/accounts.rs
// version: 7
// version: 8
//! Stable committed SPL Token mutation and ATA lifecycle projections.
//! Token, ATA and Token-2022 account materializer component.
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::TokenAccount,
crate::MdEventFamily::TokenMint,
crate::MdEventFamily::TokenBurn,
crate::MdEventFamily::Lifecycle,
];
const MUTATION_ENTRIES: &[&str] = &[
"initialize_mint",
"initialize_mint2",
"initialize_account",
"initialize_account2",
"initialize_account3",
"transfer",
"transfer_checked",
"approve",
"approve_checked",
"revoke",
"mint_to",
"mint_to_checked",
"burn",
"burn_checked",
"close_account",
"freeze_account",
"thaw_account",
"sync_native",
"initialize_immutable_owner",
"withdraw_excess_lamports",
"unwrap_lamports",
"initialize_non_transferable_mint",
"reallocate",
];
const ATA_LIFECYCLE_ENTRIES: &[(&str, &str)] = &[
("create", "ata_created"),
("create_idempotent", "ata_created_or_reused_idempotently"),
("recover_nested", "nested_ata_recovered"),
];
mod constants;
mod materializer;
/// Stable committed classic SPL Token account and mint mutation materializer.
#[derive(Clone, Debug, Default)]
pub struct MtTokenAccountsMaterializer;
/// Stable Token, ATA and Token-2022 account materializer.
pub use self::materializer::MtTokenAccountsMaterializer;
/// Materializes one bounded Token-2022 account state snapshot.
pub use self::materializer::materializer_token_materialize_token_2022_state_snapshot;
impl crate::MtMaterializer for crate::MtTokenAccountsMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.token.accounts";
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return accepts_event(event);
}
fn materialize_event(
&self,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtTokenAccountsMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return accepts_event(&observation.event);
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
if !accepts_event(&observation.event) {
return crate::MtApiMaterializerExecutionResult::ignored();
}
if observation.transaction_failed || !observation.observation_committed {
return crate::MtApiMaterializerExecutionResult::refused(
"uncommitted_token_account_projection_refused",
"failed or uncommitted SPL Token or ATA observations cannot create mutable outputs",
);
}
if is_ata_event(&observation.event) {
return materialize_ata(observation);
}
let parameters = observation
.payload_json
.get("parameters")
.cloned()
.unwrap_or(serde_json::Value::Null);
let accounts = observation
.payload_json
.get("accounts")
.cloned()
.unwrap_or(serde_json::Value::Null);
let authority = observation
.payload_json
.get("authority")
.cloned()
.unwrap_or(serde_json::Value::Null);
let operation = observation.event.event_name.0.clone();
let output = crate::MtApiMaterializedOutput {
output_key: format!("token_account_mutation:{operation}:0"),
family: crate::MdMaterializedEventFamily::TokenAccount,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION,
"domain": "spl_token_account_mutation",
"projectionSemantics": "committed_instruction_mutation_not_final_account_snapshot",
"idempotenceKey": format!(
"spl_token:{}:{}:{}",
observation.event.signature.0,
observation.event.instruction_path.0,
operation
),
"operation": operation,
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"programId": observation.event.program_id.0.clone(),
"mint": account_key_for_role(&accounts, "mint"),
"amountRaw": parameters.get("amountRaw").cloned().unwrap_or(serde_json::Value::Null),
"decimals": parameters.get("decimals").cloned().unwrap_or(serde_json::Value::Null),
"parameters": parameters,
"accounts": accounts,
"authority": authority,
"committed": true,
"transactionFinalAccountStateCaptured": false,
"coreBalanceChangesDuplicated": false,
"provenance": {
"processorName": crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"sourceSurface": observation.event.surface_code.0.clone(),
"sourceEventCode": observation.event.event_code.0.clone(),
"sourceEventKey": observation.event_key.clone()
}
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS,
action = "materialize_token_account_mutation",
signature = %observation.event.signature.0,
instruction_path = %observation.event.instruction_path.0,
operation = %observation.event.event_name.0,
"materialize committed classic SPL Token mutation"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
}
fn accepts_event(event: &crate::MdDecodedProtocolEvent) -> bool {
let token_program = (event.program_id.0 == kb_program_ids::SPL_TOKEN_PROGRAM_ID
&& event.surface_code.0 == "spl.token")
|| (event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
&& event.surface_code.0 == "spl.token_2022");
let token_mutation = token_program && MUTATION_ENTRIES.contains(&event.event_name.0.as_str());
let ata = is_ata_event(event);
return ACCEPTED_FAMILIES.contains(&event.event_family) && (token_mutation || ata);
}
fn is_ata_event(event: &crate::MdDecodedProtocolEvent) -> bool {
return event.program_id.0 == kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID
&& event.surface_code.0 == "spl.associated_token_account"
&& event.event_family == crate::MdEventFamily::Lifecycle
&& ATA_LIFECYCLE_ENTRIES
.iter()
.any(|(entry, _)| return *entry == event.event_name.0);
}
fn materialize_ata(
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
let operation = observation.event.event_name.0.as_str();
let addresses = match observation.payload_json.get("addresses") {
std::option::Option::Some(value) if value.is_object() => value,
_ => {
return crate::MtApiMaterializerExecutionResult::refused(
"ata_addresses_missing",
"committed ATA lifecycle projection requires the decoded address contract",
);
},
};
let validation = validate_ata_addresses(operation, addresses);
if let std::result::Result::Err((code, message)) = validation {
return crate::MtApiMaterializerExecutionResult::refused(code, message);
}
let lifecycle_kind =
match ATA_LIFECYCLE_ENTRIES.iter().find(|(entry, _)| return *entry == operation) {
std::option::Option::Some((_, value)) => *value,
std::option::Option::None => {
return crate::MtApiMaterializerExecutionResult::ignored();
},
};
let associated_token_account = if operation == "recover_nested" {
observed_address(addresses, "nestedAssociatedTokenAccount")
} else {
observed_address(addresses, "associatedTokenAccount")
};
let token_program = addresses
.get("tokenProgram")
.and_then(|value| return value.get("programId"))
.cloned()
.unwrap_or(serde_json::Value::Null);
let wallet = addresses.get("walletOwner").cloned().unwrap_or(serde_json::Value::Null);
let mint = if operation == "recover_nested" {
addresses.get("nestedMint").cloned().unwrap_or(serde_json::Value::Null)
} else {
addresses.get("mint").cloned().unwrap_or(serde_json::Value::Null)
};
let output = crate::MtApiMaterializedOutput {
output_key: format!("ata_lifecycle:{lifecycle_kind}:0"),
family: crate::MdMaterializedEventFamily::TokenAccount,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION,
"domain": "spl_associated_token_account_lifecycle",
"projectionSemantics": "committed_associated_address_lifecycle_not_token_state_snapshot",
"idempotenceKey": format!(
"ata:{}:{}:{}:{}:{}",
observation.event.signature.0,
observation.event.instruction_path.0,
observation.event.program_id.0,
lifecycle_kind,
associated_token_account.as_str().unwrap_or("")
),
"operation": operation,
"lifecycleKind": lifecycle_kind,
"associatedTokenAccount": associated_token_account,
"walletOwner": wallet,
"mint": mint,
"tokenProgramId": token_program,
"fundingAccount": addresses.get("fundingAccount").cloned().unwrap_or(serde_json::Value::Null),
"ownerMint": addresses.get("ownerMint").cloned().unwrap_or(serde_json::Value::Null),
"nestedMint": addresses.get("nestedMint").cloned().unwrap_or(serde_json::Value::Null),
"ownerAssociatedTokenAccount": observed_address(addresses, "ownerAssociatedTokenAccount"),
"nestedAssociatedTokenAccount": observed_address(addresses, "nestedAssociatedTokenAccount"),
"walletNestedMintAssociatedTokenAccount": observed_address(addresses, "walletNestedMintAssociatedTokenAccount"),
"idempotentOutcome": if operation == "create_idempotent" {
"created_or_reused_not_distinguishable_from_parent_instruction_alone"
} else {
"not_applicable"
},
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"programId": observation.event.program_id.0.clone(),
"committed": true,
"transactionFinalAccountStateCaptured": false,
"tokenBalanceReconstructed": false,
"splTokenCpiMutationsDuplicated": false,
"ownership": {
"ataParent": "associated_address_lifecycle_only",
"splTokenCpi": "initialize_transfer_and_close_mutations"
},
"provenance": {
"processorName": crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"sourceSurface": observation.event.surface_code.0.clone(),
"sourceEventCode": observation.event.event_code.0.clone(),
"sourceEventKey": observation.event_key.clone()
}
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS,
action = "materialize_ata_lifecycle",
signature = %observation.event.signature.0,
instruction_path = %observation.event.instruction_path.0,
operation,
lifecycle_kind,
"materialize committed ATA lifecycle without duplicating SPL Token CPI mutations"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
fn validate_ata_addresses(
operation: &str,
addresses: &serde_json::Value,
) -> std::result::Result<(), (&'static str, &'static str)> {
let token_supported = addresses
.get("tokenProgram")
.and_then(|value| return value.get("supported"))
.and_then(serde_json::Value::as_bool)
== std::option::Option::Some(true);
let token_program_id = addresses
.get("tokenProgram")
.and_then(|value| return value.get("programId"))
.and_then(serde_json::Value::as_str);
let token_program_exact = matches!(
token_program_id,
std::option::Option::Some(kb_program_ids::SPL_TOKEN_PROGRAM_ID)
| std::option::Option::Some(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID)
);
if !token_supported || !token_program_exact {
return std::result::Result::Err((
"ata_token_program_not_supported",
"ATA lifecycle projection requires classic SPL Token or Token-2022",
));
}
let fields: &[&str] = if operation == "recover_nested" {
&[
"ownerAssociatedTokenAccount",
"nestedAssociatedTokenAccount",
"walletNestedMintAssociatedTokenAccount",
]
} else {
&["associatedTokenAccount"]
};
for field in fields {
let valid = addresses
.get(*field)
.and_then(|value| return value.get("valid"))
.and_then(serde_json::Value::as_bool)
== std::option::Option::Some(true);
let observed = observed_address(addresses, field);
if !valid || observed.as_str().is_none_or(|value| return value.is_empty()) {
return std::result::Result::Err((
"ata_derived_address_invalid",
"ATA lifecycle projection requires every canonical address validation to pass",
));
}
}
let identity_fields: &[&str] = if operation == "recover_nested" {
&["walletOwner", "ownerMint", "nestedMint"]
} else {
&["walletOwner", "mint"]
};
if identity_fields.iter().any(|field| {
return addresses
.get(*field)
.and_then(serde_json::Value::as_str)
.is_none_or(|value| return value.is_empty());
}) {
return std::result::Result::Err((
"ata_identity_field_missing",
"ATA lifecycle projection requires wallet and mint identities",
));
}
return std::result::Result::Ok(());
}
fn observed_address(addresses: &serde_json::Value, field: &str) -> serde_json::Value {
return addresses
.get(field)
.and_then(|value| return value.get("observed"))
.cloned()
.unwrap_or(serde_json::Value::Null);
}
fn account_key_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value {
let value = accounts.as_array().and_then(|rows| {
return rows.iter().find_map(|row| {
if row.get("role").and_then(serde_json::Value::as_str)
!= std::option::Option::Some(role)
{
return std::option::Option::None;
}
return row.get("accountKey").and_then(serde_json::Value::as_str);
});
});
return match value {
std::option::Option::Some(account_key) => {
serde_json::Value::String(account_key.to_string())
},
std::option::Option::None => serde_json::Value::Null,
};
}
/// Materialize one already parsed Token-2022 account snapshot without inferring hidden state.
pub fn materializer_token_materialize_token_2022_state_snapshot(
account_key: &str,
slot: u64,
state: &crate::DcToken2022State,
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
if account_key.is_empty() {
return std::result::Result::Err(
"Token-2022 state snapshot requires a non-empty account key".to_string(),
);
}
let kind = match state.kind {
crate::DcToken2022StateKind::Mint => "mint",
crate::DcToken2022StateKind::Account => "account",
crate::DcToken2022StateKind::Multisig => "multisig",
};
let extensions = state
.extensions
.iter()
.map(|entry| {
return serde_json::json!({
"extensionType": entry.extension_type,
"extensionName": entry.extension_name,
"valueHex": entry.value_hex,
"valueFields": entry.value_fields,
});
})
.collect::<std::vec::Vec<_>>();
let extension_names = state
.extensions
.iter()
.map(|entry| return entry.extension_name)
.collect::<std::vec::Vec<_>>();
let contains_confidential_state = state.extensions.iter().any(|entry| {
return entry.extension_name.starts_with("confidential_")
|| entry.extension_name == "confidential_mint_burn";
});
return std::result::Result::Ok(crate::MtApiMaterializedOutput {
output_key: format!("token_2022_state_snapshot:{account_key}:{slot}"),
family: crate::MdMaterializedEventFamily::TokenAccount,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION,
"domain": "spl_token_2022_account_state",
"projectionSemantics": "authoritative_bounded_account_snapshot",
"idempotenceKey": format!("token_2022_state:{account_key}:{slot}"),
"accountKey": account_key,
"slot": slot,
"programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"stateKind": kind,
"baseFields": state.base_fields,
"baseHex": state.base_hex,
"accountType": state.account_type,
"extensionCount": state.extensions.len(),
"extensionNames": extension_names,
"extensions": extensions,
"containsConfidentialState": contains_confidential_state,
"confidentialValuesDecrypted": false,
"finalAccountStateCaptured": true,
"provenance": {
"processorName": crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"source": "bounded_token_2022_state_parser"
}
}),
});
}
#[cfg(test)]
mod tests {
fn observation(
entry: &str,
family: crate::MdEventFamily,
failed: bool,
) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: format!("token:{entry}:0"),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("1/0".to_string()),
program_id: crate::MdProgramId(kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string()),
protocol_code: crate::MdProtocolCode("spl.token".to_string()),
surface_code: crate::MdSurfaceCode("spl.token".to_string()),
event_code: crate::MdEventCode(format!("spl.token.{entry}")),
event_name: crate::MdEventName(entry.to_string()),
event_family: family,
source_kind: crate::MdEventSourceKind::InnerInstruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({
"parameters": {"amountRaw":"18446744073709551615","decimals":9},
"accounts": [{"role":"mint","accountKey":"mint111"}],
"authority": {"form":"single"}
}),
transaction_failed: failed,
transaction_error: if failed {
std::option::Option::Some(serde_json::json!({"error":"failed"}))
} else {
std::option::Option::None
},
observation_committed: !failed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["fixture".to_string()],
},
};
}
fn token_2022_observation(
entry: &str,
family: crate::MdEventFamily,
failed: bool,
) -> crate::DcApiDecodedObservation {
let mut observation = observation(entry, family, failed);
observation.event.program_id =
crate::MdProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string());
observation.event.protocol_code = crate::MdProtocolCode("spl.token_2022".to_string());
observation.event.surface_code = crate::MdSurfaceCode("spl.token_2022".to_string());
observation.event.event_code = crate::MdEventCode(format!("spl.token_2022.{entry}"));
observation.event_key = format!("token_2022:{entry}:0");
return observation;
}
fn ata_observation(entry: &str, failed: bool, valid: bool) -> crate::DcApiDecodedObservation {
let addresses = if entry == "recover_nested" {
serde_json::json!({
"tokenProgram": {
"programId": kb_program_ids::SPL_TOKEN_PROGRAM_ID,
"kind": "spl_token_classic",
"supported": true
},
"ownerAssociatedTokenAccount": {"expected":"ownerAta","observed":"ownerAta","valid":valid},
"nestedAssociatedTokenAccount": {"expected":"nestedAta","observed":"nestedAta","valid":valid},
"walletNestedMintAssociatedTokenAccount": {"expected":"destinationAta","observed":"destinationAta","valid":valid},
"walletOwner": "wallet111",
"ownerMint": "ownerMint111",
"nestedMint": "nestedMint111"
})
} else {
serde_json::json!({
"tokenProgram": {
"programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"kind": "token_2022",
"supported": true
},
"associatedTokenAccount": {"expected":"ata111","observed":"ata111","valid":valid},
"fundingAccount": "payer111",
"walletOwner": "wallet111",
"mint": "mint111"
})
};
return crate::DcApiDecodedObservation {
event_key: format!("ata:{entry}:0"),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("0".to_string()),
program_id: crate::MdProgramId(
kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID.to_string(),
),
protocol_code: crate::MdProtocolCode("spl.associated_token_account".to_string()),
surface_code: crate::MdSurfaceCode("spl.associated_token_account".to_string()),
event_code: crate::MdEventCode(format!("spl.associated_token_account.{entry}")),
event_name: crate::MdEventName(entry.to_string()),
event_family: crate::MdEventFamily::Lifecycle,
source_kind: crate::MdEventSourceKind::Instruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({"addresses":addresses}),
transaction_failed: failed,
transaction_error: std::option::Option::None,
observation_committed: !failed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["fixture".to_string()],
},
};
}
#[test]
fn committed_checked_transfer_preserves_exact_amount_mint_and_path() {
let observation =
observation("transfer_checked", crate::MdEventFamily::TokenAccount, false);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs[0].payload_json["amountRaw"], "18446744073709551615");
assert_eq!(result.outputs[0].payload_json["mint"], "mint111");
assert_eq!(result.outputs[0].payload_json["instructionPath"], "1/0");
assert_eq!(result.outputs[0].payload_json["transactionFinalAccountStateCaptured"], false);
}
#[test]
fn committed_token_2022_transfer_has_the_same_single_projection_owner() {
let observation =
token_2022_observation("transfer_checked", crate::MdEventFamily::TokenAccount, false);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs.len(), 1);
assert_eq!(result.outputs[0].payload_json["operation"], "transfer_checked");
assert_eq!(
result.outputs[0].payload_json["programId"],
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
);
}
#[test]
fn failed_mutation_is_refused() {
let observation = observation("burn", crate::MdEventFamily::TokenBurn, true);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
assert!(result.outputs.is_empty());
}
#[test]
fn batch_parent_admin_and_conversion_events_are_not_duplicated() {
for (entry, family) in [
("batch", crate::MdEventFamily::Audit),
("set_authority", crate::MdEventFamily::Admin),
("amount_to_ui_amount", crate::MdEventFamily::Audit),
] {
let observation = observation(entry, family, false);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
}
#[test]
fn every_ata_variant_has_one_exact_lifecycle_projection() {
for (entry, expected_kind, expected_ata, expected_token_program) in [
("create", "ata_created", "ata111", kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID),
(
"create_idempotent",
"ata_created_or_reused_idempotently",
"ata111",
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
),
(
"recover_nested",
"nested_ata_recovered",
"nestedAta",
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
),
] {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&ata_observation(entry, false, true),
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs.len(), 1);
assert_eq!(result.outputs[0].payload_json["lifecycleKind"], expected_kind);
assert_eq!(result.outputs[0].payload_json["associatedTokenAccount"], expected_ata);
assert_eq!(result.outputs[0].payload_json["tokenProgramId"], expected_token_program);
assert_eq!(result.outputs[0].payload_json["tokenBalanceReconstructed"], false);
assert_eq!(result.outputs[0].payload_json["splTokenCpiMutationsDuplicated"], false);
}
}
#[test]
fn idempotent_parent_does_not_invent_created_or_reused_branch() {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&ata_observation("create_idempotent", false, true),
);
assert_eq!(
result.outputs[0].payload_json["idempotentOutcome"],
"created_or_reused_not_distinguishable_from_parent_instruction_alone"
);
assert!(result.outputs[0].payload_json.get("reused").is_none());
assert!(result.outputs[0].payload_json.get("created").is_none());
}
#[test]
fn uncommitted_or_invalid_ata_lifecycle_is_refused() {
for observation in [
ata_observation("create", true, true),
ata_observation("recover_nested", false, false),
] {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
assert!(result.outputs.is_empty());
}
}
#[test]
fn ata_parent_and_classic_token_cpi_children_own_distinct_facts() {
let parent = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&ata_observation("recover_nested", false, true),
);
let transfer = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation("transfer", crate::MdEventFamily::TokenAccount, false),
);
let close = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation("close_account", crate::MdEventFamily::TokenAccount, false),
);
assert_eq!(parent.outputs[0].payload_json["lifecycleKind"], "nested_ata_recovered");
assert_eq!(
parent.outputs[0].payload_json["ownership"]["ataParent"],
"associated_address_lifecycle_only"
);
assert!(parent.outputs[0].payload_json.get("amountRaw").is_none());
assert_eq!(transfer.outputs[0].payload_json["operation"], "transfer");
assert_eq!(close.outputs[0].payload_json["operation"], "close_account");
assert_eq!(transfer.outputs[0].payload_json["domain"], "spl_token_account_mutation");
assert_eq!(close.outputs[0].payload_json["domain"], "spl_token_account_mutation");
}
#[test]
fn ata_matrix_and_compiled_token_account_projection_ownership_are_equal() {
let matrix: serde_json::Value = match serde_json::from_str(include_str!(
"../../../../test-fixtures/contract-matrices/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json"
)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("ATA matrix is invalid: {error}"),
};
let instructions = match matrix.get("instructions").and_then(serde_json::Value::as_array) {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("ATA matrix instructions are absent"),
};
for (entry, fact) in super::ATA_LIFECYCLE_ENTRIES {
let row = match instructions.iter().find(|row| {
return row.get("name").and_then(serde_json::Value::as_str)
== std::option::Option::Some(*entry);
}) {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("ATA matrix entry {entry} is absent"),
};
let expected = format!("token_accounts:{fact}");
assert!(row["authorizedProjections"].as_array().is_some_and(|values| {
return values.iter().any(|value| return value == expected.as_str());
}));
}
let owner = &matrix["materializationContract"]["projectionOwners"][0];
let matrix_facts = match owner["facts"].as_array() {
std::option::Option::Some(values) => values
.iter()
.filter_map(|value| return value.as_str())
.collect::<std::vec::Vec<_>>(),
std::option::Option::None => panic!("ATA token-account owner facts are absent"),
};
let compiled_facts = super::ATA_LIFECYCLE_ENTRIES
.iter()
.map(|(_, fact)| return *fact)
.collect::<std::vec::Vec<_>>();
assert_eq!(matrix_facts, compiled_facts);
}
#[test]
fn second_ata_materialization_is_byte_for_byte_idempotent() {
let observation = ata_observation("create_idempotent", false, true);
let first = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
let second = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(first, second);
assert_eq!(first.outputs[0].output_key, second.outputs[0].output_key);
assert_eq!(
first.outputs[0].payload_json["idempotenceKey"],
second.outputs[0].payload_json["idempotenceKey"]
);
}
#[test]
fn token_2022_state_snapshot_preserves_base_and_ordered_extensions() {
let state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Mint,
base_fields: serde_json::json!({"supply":"42","decimals":9,"initialized":true}),
base_hex: "00".repeat(82),
account_type: std::option::Option::Some(1),
extensions: std::vec![
crate::DcToken2022TlvEntry {
extension_type: 1,
extension_name: "transfer_fee_config",
value_hex: "11".repeat(108),
value_fields: serde_json::json!({"withheldAmount":"7"}),
},
crate::DcToken2022TlvEntry {
extension_type: 4,
extension_name: "confidential_transfer_mint",
value_hex: "22".repeat(65),
value_fields: serde_json::json!({"autoApproveNewAccounts":true}),
},
],
};
let output = match crate::materializer_token_materialize_token_2022_state_snapshot(
"mint111", 99, &state,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("snapshot failed: {error}"),
};
assert_eq!(output.payload_json["stateKind"], "mint");
assert_eq!(output.payload_json["extensionCount"], 2);
assert_eq!(output.payload_json["extensionNames"][0], "transfer_fee_config");
assert_eq!(output.payload_json["extensionNames"][1], "confidential_transfer_mint");
assert_eq!(output.payload_json["containsConfidentialState"], true);
assert_eq!(output.payload_json["confidentialValuesDecrypted"], false);
assert_eq!(output.payload_json["baseFields"]["supply"], "42");
}
#[test]
fn token_2022_state_snapshot_is_idempotent_and_rejects_missing_identity() {
let state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Account,
base_fields: serde_json::json!({"mint":"mint111","owner":"wallet111","amount":"5"}),
base_hex: "00".repeat(165),
account_type: std::option::Option::None,
extensions: std::vec::Vec::new(),
};
let first = crate::materializer_token_materialize_token_2022_state_snapshot(
"account111",
7,
&state,
);
let second = crate::materializer_token_materialize_token_2022_state_snapshot(
"account111",
7,
&state,
);
assert_eq!(first, second);
assert!(
crate::materializer_token_materialize_token_2022_state_snapshot("", 7, &state).is_err()
);
}
}
/// Crate-visible `MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES;
/// Crate-visible `MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES` component constant.
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES;
/// Stable runtime component name for Token, ATA and Token-2022 account projections.
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_COMPONENT_NAME;
/// Crate-visible `MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES` component constant.
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES;
/// Stable processor name for Token, ATA and Token-2022 account projections.
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME;
/// Stable projection contract version for Token, ATA and Token-2022 account projections.
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION;
/// Canonical tracing target for Token, ATA and Token-2022 account projections.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS;

View File

@@ -0,0 +1,54 @@
// file: kb-lib/src/materializer/token/accounts/constants.rs
// version: 4
//! Local constants for the token accounts materializer.
/// Stable runtime component name for the token accounts materializer.
pub(crate) const MT_TOKEN_ACCOUNTS_COMPONENT_NAME: &str = "kb-lib.materializer.token.accounts";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_TOKEN_ACCOUNTS_PROCESSOR_NAME: &str = "materializer.token.accounts";
/// Stable projection contract version for the token accounts materializer.
pub(crate) const MT_TOKEN_ACCOUNTS_PROJECTION_VERSION: u32 = 3;
/// Canonical tracing target for the token accounts materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS: &str =
"kb-lib.materializer.token.accounts";
/// Component contract for `accepted_families`.
pub(crate) const MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::TokenAccount,
crate::MdEventFamily::TokenMint,
crate::MdEventFamily::TokenBurn,
crate::MdEventFamily::Lifecycle,
];
/// Component contract for `mutation_entries`.
pub(crate) const MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES: &[&str] = &[
"initialize_mint",
"initialize_mint2",
"initialize_account",
"initialize_account2",
"initialize_account3",
"transfer",
"transfer_checked",
"approve",
"approve_checked",
"revoke",
"mint_to",
"mint_to_checked",
"burn",
"burn_checked",
"close_account",
"freeze_account",
"thaw_account",
"sync_native",
"initialize_immutable_owner",
"withdraw_excess_lamports",
"unwrap_lamports",
"initialize_non_transferable_mint",
"reallocate",
];
/// Component contract for `ata_lifecycle_entries`.
pub(crate) const MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES: &[(&str, &str)] = &[
("create", "ata_created"),
("create_idempotent", "ata_created_or_reused_idempotently"),
("recover_nested", "nested_ata_recovered"),
];

View File

@@ -0,0 +1,809 @@
// file: kb-lib/src/materializer/token/accounts/materializer.rs
// version: 9
//! Stable committed SPL Token mutation and ATA lifecycle projections.
/// Stable committed classic SPL Token account and mint mutation materializer.
#[derive(Clone, Debug, Default)]
pub struct MtTokenAccountsMaterializer;
impl crate::MtMaterializer for crate::MtTokenAccountsMaterializer {
fn materializer_name(&self) -> &'static str {
return crate::MT_TOKEN_ACCOUNTS_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return accepts_event(event);
}
fn materialize_event(
&self,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtTokenAccountsMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return crate::MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return accepts_event(&observation.event);
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
if !accepts_event(&observation.event) {
return crate::MtApiMaterializerExecutionResult::ignored();
}
if observation.transaction_failed || !observation.observation_committed {
return crate::MtApiMaterializerExecutionResult::refused(
"uncommitted_token_account_projection_refused",
"failed or uncommitted SPL Token or ATA observations cannot create mutable outputs",
);
}
if is_ata_event(&observation.event) {
return materialize_ata(observation);
}
let parameters = observation
.payload_json
.get("parameters")
.cloned()
.unwrap_or(serde_json::Value::Null);
let accounts = observation
.payload_json
.get("accounts")
.cloned()
.unwrap_or(serde_json::Value::Null);
let authority = observation
.payload_json
.get("authority")
.cloned()
.unwrap_or(serde_json::Value::Null);
let operation = observation.event.event_name.0.clone();
let output = crate::MtApiMaterializedOutput {
output_key: format!("token_account_mutation:{operation}:0"),
family: crate::MdMaterializedEventFamily::TokenAccount,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION,
"domain": "spl_token_account_mutation",
"projectionSemantics": "committed_instruction_mutation_not_final_account_snapshot",
"idempotenceKey": format!(
"spl_token:{}:{}:{}",
observation.event.signature.0,
observation.event.instruction_path.0,
operation
),
"operation": operation,
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"programId": observation.event.program_id.0.clone(),
"mint": account_key_for_role(&accounts, "mint"),
"amountRaw": parameters.get("amountRaw").cloned().unwrap_or(serde_json::Value::Null),
"decimals": parameters.get("decimals").cloned().unwrap_or(serde_json::Value::Null),
"parameters": parameters,
"accounts": accounts,
"authority": authority,
"committed": true,
"transactionFinalAccountStateCaptured": false,
"coreBalanceChangesDuplicated": false,
"provenance": {
"processorName": crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"sourceSurface": observation.event.surface_code.0.clone(),
"sourceEventCode": observation.event.event_code.0.clone(),
"sourceEventKey": observation.event_key.clone()
}
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS,
action = "materialize_token_account_mutation",
signature = %observation.event.signature.0,
instruction_path = %observation.event.instruction_path.0,
operation = %observation.event.event_name.0,
"materialize committed classic SPL Token mutation"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
}
fn accepts_event(event: &crate::MdDecodedProtocolEvent) -> bool {
let token_program = (event.program_id.0 == kb_program_ids::SPL_TOKEN_PROGRAM_ID
&& event.surface_code.0 == "spl.token")
|| (event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
&& event.surface_code.0 == "spl.token_2022");
let token_mutation = token_program
&& crate::MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES.contains(&event.event_name.0.as_str());
let ata = is_ata_event(event);
return crate::MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES.contains(&event.event_family)
&& (token_mutation || ata);
}
fn is_ata_event(event: &crate::MdDecodedProtocolEvent) -> bool {
return event.program_id.0 == kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID
&& event.surface_code.0 == "spl.associated_token_account"
&& event.event_family == crate::MdEventFamily::Lifecycle
&& crate::MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES
.iter()
.any(|(entry, _)| return *entry == event.event_name.0);
}
fn materialize_ata(
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
let operation = observation.event.event_name.0.as_str();
let addresses = match observation.payload_json.get("addresses") {
std::option::Option::Some(value) if value.is_object() => value,
_ => {
return crate::MtApiMaterializerExecutionResult::refused(
"ata_addresses_missing",
"committed ATA lifecycle projection requires the decoded address contract",
);
},
};
let validation = validate_ata_addresses(operation, addresses);
if let std::result::Result::Err((code, message)) = validation {
return crate::MtApiMaterializerExecutionResult::refused(code, message);
}
let lifecycle_kind = match crate::MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES
.iter()
.find(|(entry, _)| return *entry == operation)
{
std::option::Option::Some((_, value)) => *value,
std::option::Option::None => {
return crate::MtApiMaterializerExecutionResult::ignored();
},
};
let associated_token_account = if operation == "recover_nested" {
observed_address(addresses, "nestedAssociatedTokenAccount")
} else {
observed_address(addresses, "associatedTokenAccount")
};
let token_program = addresses
.get("tokenProgram")
.and_then(|value| return value.get("programId"))
.cloned()
.unwrap_or(serde_json::Value::Null);
let wallet = addresses.get("walletOwner").cloned().unwrap_or(serde_json::Value::Null);
let mint = if operation == "recover_nested" {
addresses.get("nestedMint").cloned().unwrap_or(serde_json::Value::Null)
} else {
addresses.get("mint").cloned().unwrap_or(serde_json::Value::Null)
};
let output = crate::MtApiMaterializedOutput {
output_key: format!("ata_lifecycle:{lifecycle_kind}:0"),
family: crate::MdMaterializedEventFamily::TokenAccount,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION,
"domain": "spl_associated_token_account_lifecycle",
"projectionSemantics": "committed_associated_address_lifecycle_not_token_state_snapshot",
"idempotenceKey": format!(
"ata:{}:{}:{}:{}:{}",
observation.event.signature.0,
observation.event.instruction_path.0,
observation.event.program_id.0,
lifecycle_kind,
associated_token_account.as_str().unwrap_or("")
),
"operation": operation,
"lifecycleKind": lifecycle_kind,
"associatedTokenAccount": associated_token_account,
"walletOwner": wallet,
"mint": mint,
"tokenProgramId": token_program,
"fundingAccount": addresses.get("fundingAccount").cloned().unwrap_or(serde_json::Value::Null),
"ownerMint": addresses.get("ownerMint").cloned().unwrap_or(serde_json::Value::Null),
"nestedMint": addresses.get("nestedMint").cloned().unwrap_or(serde_json::Value::Null),
"ownerAssociatedTokenAccount": observed_address(addresses, "ownerAssociatedTokenAccount"),
"nestedAssociatedTokenAccount": observed_address(addresses, "nestedAssociatedTokenAccount"),
"walletNestedMintAssociatedTokenAccount": observed_address(addresses, "walletNestedMintAssociatedTokenAccount"),
"idempotentOutcome": if operation == "create_idempotent" {
"created_or_reused_not_distinguishable_from_parent_instruction_alone"
} else {
"not_applicable"
},
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"programId": observation.event.program_id.0.clone(),
"committed": true,
"transactionFinalAccountStateCaptured": false,
"tokenBalanceReconstructed": false,
"splTokenCpiMutationsDuplicated": false,
"ownership": {
"ataParent": "associated_address_lifecycle_only",
"splTokenCpi": "initialize_transfer_and_close_mutations"
},
"provenance": {
"processorName": crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"sourceSurface": observation.event.surface_code.0.clone(),
"sourceEventCode": observation.event.event_code.0.clone(),
"sourceEventKey": observation.event_key.clone()
}
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS,
action = "materialize_ata_lifecycle",
signature = %observation.event.signature.0,
instruction_path = %observation.event.instruction_path.0,
operation,
lifecycle_kind,
"materialize committed ATA lifecycle without duplicating SPL Token CPI mutations"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
fn validate_ata_addresses(
operation: &str,
addresses: &serde_json::Value,
) -> std::result::Result<(), (&'static str, &'static str)> {
let token_supported = addresses
.get("tokenProgram")
.and_then(|value| return value.get("supported"))
.and_then(serde_json::Value::as_bool)
== std::option::Option::Some(true);
let token_program_id = addresses
.get("tokenProgram")
.and_then(|value| return value.get("programId"))
.and_then(serde_json::Value::as_str);
let token_program_exact = matches!(
token_program_id,
std::option::Option::Some(kb_program_ids::SPL_TOKEN_PROGRAM_ID)
| std::option::Option::Some(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID)
);
if !token_supported || !token_program_exact {
return std::result::Result::Err((
"ata_token_program_not_supported",
"ATA lifecycle projection requires classic SPL Token or Token-2022",
));
}
let fields: &[&str] = if operation == "recover_nested" {
&[
"ownerAssociatedTokenAccount",
"nestedAssociatedTokenAccount",
"walletNestedMintAssociatedTokenAccount",
]
} else {
&["associatedTokenAccount"]
};
for field in fields {
let valid = addresses
.get(*field)
.and_then(|value| return value.get("valid"))
.and_then(serde_json::Value::as_bool)
== std::option::Option::Some(true);
let observed = observed_address(addresses, field);
if !valid || observed.as_str().is_none_or(|value| return value.is_empty()) {
return std::result::Result::Err((
"ata_derived_address_invalid",
"ATA lifecycle projection requires every canonical address validation to pass",
));
}
}
let identity_fields: &[&str] = if operation == "recover_nested" {
&["walletOwner", "ownerMint", "nestedMint"]
} else {
&["walletOwner", "mint"]
};
if identity_fields.iter().any(|field| {
return addresses
.get(*field)
.and_then(serde_json::Value::as_str)
.is_none_or(|value| return value.is_empty());
}) {
return std::result::Result::Err((
"ata_identity_field_missing",
"ATA lifecycle projection requires wallet and mint identities",
));
}
return std::result::Result::Ok(());
}
fn observed_address(addresses: &serde_json::Value, field: &str) -> serde_json::Value {
return addresses
.get(field)
.and_then(|value| return value.get("observed"))
.cloned()
.unwrap_or(serde_json::Value::Null);
}
fn account_key_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value {
let value = accounts.as_array().and_then(|rows| {
return rows.iter().find_map(|row| {
if row.get("role").and_then(serde_json::Value::as_str)
!= std::option::Option::Some(role)
{
return std::option::Option::None;
}
return row.get("accountKey").and_then(serde_json::Value::as_str);
});
});
return match value {
std::option::Option::Some(account_key) => {
serde_json::Value::String(account_key.to_string())
},
std::option::Option::None => serde_json::Value::Null,
};
}
/// Materialize one already parsed Token-2022 account snapshot without inferring hidden state.
pub fn materializer_token_materialize_token_2022_state_snapshot(
account_key: &str,
slot: u64,
state: &crate::DcToken2022State,
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
if account_key.is_empty() {
return std::result::Result::Err(
"Token-2022 state snapshot requires a non-empty account key".to_string(),
);
}
let kind = match state.kind {
crate::DcToken2022StateKind::Mint => "mint",
crate::DcToken2022StateKind::Account => "account",
crate::DcToken2022StateKind::Multisig => "multisig",
};
let extensions = state
.extensions
.iter()
.map(|entry| {
return serde_json::json!({
"extensionType": entry.extension_type,
"extensionName": entry.extension_name,
"valueHex": entry.value_hex,
"valueFields": entry.value_fields,
});
})
.collect::<std::vec::Vec<_>>();
let extension_names = state
.extensions
.iter()
.map(|entry| return entry.extension_name)
.collect::<std::vec::Vec<_>>();
let contains_confidential_state = state.extensions.iter().any(|entry| {
return entry.extension_name.starts_with("confidential_")
|| entry.extension_name == "confidential_mint_burn";
});
return std::result::Result::Ok(crate::MtApiMaterializedOutput {
output_key: format!("token_2022_state_snapshot:{account_key}:{slot}"),
family: crate::MdMaterializedEventFamily::TokenAccount,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION,
"domain": "spl_token_2022_account_state",
"projectionSemantics": "authoritative_bounded_account_snapshot",
"idempotenceKey": format!("token_2022_state:{account_key}:{slot}"),
"accountKey": account_key,
"slot": slot,
"programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"stateKind": kind,
"baseFields": state.base_fields,
"baseHex": state.base_hex,
"accountType": state.account_type,
"extensionCount": state.extensions.len(),
"extensionNames": extension_names,
"extensions": extensions,
"containsConfidentialState": contains_confidential_state,
"confidentialValuesDecrypted": false,
"finalAccountStateCaptured": true,
"provenance": {
"processorName": crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"source": "bounded_token_2022_state_parser"
}
}),
});
}
#[cfg(test)]
mod tests {
fn observation(
entry: &str,
family: crate::MdEventFamily,
failed: bool,
) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: format!("token:{entry}:0"),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("1/0".to_string()),
program_id: crate::MdProgramId(kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string()),
protocol_code: crate::MdProtocolCode("spl.token".to_string()),
surface_code: crate::MdSurfaceCode("spl.token".to_string()),
event_code: crate::MdEventCode(format!("spl.token.{entry}")),
event_name: crate::MdEventName(entry.to_string()),
event_family: family,
source_kind: crate::MdEventSourceKind::InnerInstruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({
"parameters": {"amountRaw":"18446744073709551615","decimals":9},
"accounts": [{"role":"mint","accountKey":"mint111"}],
"authority": {"form":"single"}
}),
transaction_failed: failed,
transaction_error: if failed {
std::option::Option::Some(serde_json::json!({"error":"failed"}))
} else {
std::option::Option::None
},
observation_committed: !failed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["fixture".to_string()],
},
};
}
fn token_2022_observation(
entry: &str,
family: crate::MdEventFamily,
failed: bool,
) -> crate::DcApiDecodedObservation {
let mut observation = observation(entry, family, failed);
observation.event.program_id =
crate::MdProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string());
observation.event.protocol_code = crate::MdProtocolCode("spl.token_2022".to_string());
observation.event.surface_code = crate::MdSurfaceCode("spl.token_2022".to_string());
observation.event.event_code = crate::MdEventCode(format!("spl.token_2022.{entry}"));
observation.event_key = format!("token_2022:{entry}:0");
return observation;
}
fn ata_observation(entry: &str, failed: bool, valid: bool) -> crate::DcApiDecodedObservation {
let addresses = if entry == "recover_nested" {
serde_json::json!({
"tokenProgram": {
"programId": kb_program_ids::SPL_TOKEN_PROGRAM_ID,
"kind": "spl_token_classic",
"supported": true
},
"ownerAssociatedTokenAccount": {"expected":"ownerAta","observed":"ownerAta","valid":valid},
"nestedAssociatedTokenAccount": {"expected":"nestedAta","observed":"nestedAta","valid":valid},
"walletNestedMintAssociatedTokenAccount": {"expected":"destinationAta","observed":"destinationAta","valid":valid},
"walletOwner": "wallet111",
"ownerMint": "ownerMint111",
"nestedMint": "nestedMint111"
})
} else {
serde_json::json!({
"tokenProgram": {
"programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"kind": "token_2022",
"supported": true
},
"associatedTokenAccount": {"expected":"ata111","observed":"ata111","valid":valid},
"fundingAccount": "payer111",
"walletOwner": "wallet111",
"mint": "mint111"
})
};
return crate::DcApiDecodedObservation {
event_key: format!("ata:{entry}:0"),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("0".to_string()),
program_id: crate::MdProgramId(
kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID.to_string(),
),
protocol_code: crate::MdProtocolCode("spl.associated_token_account".to_string()),
surface_code: crate::MdSurfaceCode("spl.associated_token_account".to_string()),
event_code: crate::MdEventCode(format!("spl.associated_token_account.{entry}")),
event_name: crate::MdEventName(entry.to_string()),
event_family: crate::MdEventFamily::Lifecycle,
source_kind: crate::MdEventSourceKind::Instruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({"addresses":addresses}),
transaction_failed: failed,
transaction_error: std::option::Option::None,
observation_committed: !failed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["fixture".to_string()],
},
};
}
#[test]
fn committed_checked_transfer_preserves_exact_amount_mint_and_path() {
let observation =
observation("transfer_checked", crate::MdEventFamily::TokenAccount, false);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs[0].payload_json["amountRaw"], "18446744073709551615");
assert_eq!(result.outputs[0].payload_json["mint"], "mint111");
assert_eq!(result.outputs[0].payload_json["instructionPath"], "1/0");
assert_eq!(result.outputs[0].payload_json["transactionFinalAccountStateCaptured"], false);
}
#[test]
fn committed_token_2022_transfer_has_the_same_single_projection_owner() {
let observation =
token_2022_observation("transfer_checked", crate::MdEventFamily::TokenAccount, false);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs.len(), 1);
assert_eq!(result.outputs[0].payload_json["operation"], "transfer_checked");
assert_eq!(
result.outputs[0].payload_json["programId"],
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
);
}
#[test]
fn failed_mutation_is_refused() {
let observation = observation("burn", crate::MdEventFamily::TokenBurn, true);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
assert!(result.outputs.is_empty());
}
#[test]
fn batch_parent_admin_and_conversion_events_are_not_duplicated() {
for (entry, family) in [
("batch", crate::MdEventFamily::Audit),
("set_authority", crate::MdEventFamily::Admin),
("amount_to_ui_amount", crate::MdEventFamily::Audit),
] {
let observation = observation(entry, family, false);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
}
#[test]
fn every_ata_variant_has_one_exact_lifecycle_projection() {
for (entry, expected_kind, expected_ata, expected_token_program) in [
("create", "ata_created", "ata111", kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID),
(
"create_idempotent",
"ata_created_or_reused_idempotently",
"ata111",
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
),
(
"recover_nested",
"nested_ata_recovered",
"nestedAta",
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
),
] {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&ata_observation(entry, false, true),
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs.len(), 1);
assert_eq!(result.outputs[0].payload_json["lifecycleKind"], expected_kind);
assert_eq!(result.outputs[0].payload_json["associatedTokenAccount"], expected_ata);
assert_eq!(result.outputs[0].payload_json["tokenProgramId"], expected_token_program);
assert_eq!(result.outputs[0].payload_json["tokenBalanceReconstructed"], false);
assert_eq!(result.outputs[0].payload_json["splTokenCpiMutationsDuplicated"], false);
}
}
#[test]
fn idempotent_parent_does_not_invent_created_or_reused_branch() {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&ata_observation("create_idempotent", false, true),
);
assert_eq!(
result.outputs[0].payload_json["idempotentOutcome"],
"created_or_reused_not_distinguishable_from_parent_instruction_alone"
);
assert!(result.outputs[0].payload_json.get("reused").is_none());
assert!(result.outputs[0].payload_json.get("created").is_none());
}
#[test]
fn uncommitted_or_invalid_ata_lifecycle_is_refused() {
for observation in [
ata_observation("create", true, true),
ata_observation("recover_nested", false, false),
] {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
assert!(result.outputs.is_empty());
}
}
#[test]
fn ata_parent_and_classic_token_cpi_children_own_distinct_facts() {
let parent = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&ata_observation("recover_nested", false, true),
);
let transfer = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation("transfer", crate::MdEventFamily::TokenAccount, false),
);
let close = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation("close_account", crate::MdEventFamily::TokenAccount, false),
);
assert_eq!(parent.outputs[0].payload_json["lifecycleKind"], "nested_ata_recovered");
assert_eq!(
parent.outputs[0].payload_json["ownership"]["ataParent"],
"associated_address_lifecycle_only"
);
assert!(parent.outputs[0].payload_json.get("amountRaw").is_none());
assert_eq!(transfer.outputs[0].payload_json["operation"], "transfer");
assert_eq!(close.outputs[0].payload_json["operation"], "close_account");
assert_eq!(transfer.outputs[0].payload_json["domain"], "spl_token_account_mutation");
assert_eq!(close.outputs[0].payload_json["domain"], "spl_token_account_mutation");
}
#[test]
fn ata_matrix_and_compiled_token_account_projection_ownership_are_equal() {
let matrix: serde_json::Value = match serde_json::from_str(include_str!(
"../../../../../test-fixtures/contract-matrices/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json"
)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("ATA matrix is invalid: {error}"),
};
let instructions = match matrix.get("instructions").and_then(serde_json::Value::as_array) {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("ATA matrix instructions are absent"),
};
for (entry, fact) in crate::MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES {
let row = match instructions.iter().find(|row| {
return row.get("name").and_then(serde_json::Value::as_str)
== std::option::Option::Some(*entry);
}) {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("ATA matrix entry {entry} is absent"),
};
let expected = format!("token_accounts:{fact}");
assert!(row["authorizedProjections"].as_array().is_some_and(|values| {
return values.iter().any(|value| return value == expected.as_str());
}));
}
let owner = &matrix["materializationContract"]["projectionOwners"][0];
let matrix_facts = match owner["facts"].as_array() {
std::option::Option::Some(values) => values
.iter()
.filter_map(|value| return value.as_str())
.collect::<std::vec::Vec<_>>(),
std::option::Option::None => panic!("ATA token-account owner facts are absent"),
};
let compiled_facts = crate::MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES
.iter()
.map(|(_, fact)| return *fact)
.collect::<std::vec::Vec<_>>();
assert_eq!(matrix_facts, compiled_facts);
}
#[test]
fn second_ata_materialization_is_byte_for_byte_idempotent() {
let observation = ata_observation("create_idempotent", false, true);
let first = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
let second = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(first, second);
assert_eq!(first.outputs[0].output_key, second.outputs[0].output_key);
assert_eq!(
first.outputs[0].payload_json["idempotenceKey"],
second.outputs[0].payload_json["idempotenceKey"]
);
}
#[test]
fn token_2022_state_snapshot_preserves_base_and_ordered_extensions() {
let state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Mint,
base_fields: serde_json::json!({"supply":"42","decimals":9,"initialized":true}),
base_hex: "00".repeat(82),
account_type: std::option::Option::Some(1),
extensions: std::vec![
crate::DcToken2022TlvEntry {
extension_type: 1,
extension_name: "transfer_fee_config",
value_hex: "11".repeat(108),
value_fields: serde_json::json!({"withheldAmount":"7"}),
},
crate::DcToken2022TlvEntry {
extension_type: 4,
extension_name: "confidential_transfer_mint",
value_hex: "22".repeat(65),
value_fields: serde_json::json!({"autoApproveNewAccounts":true}),
},
],
};
let output = match crate::materializer_token_materialize_token_2022_state_snapshot(
"mint111", 99, &state,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("snapshot failed: {error}"),
};
assert_eq!(output.payload_json["stateKind"], "mint");
assert_eq!(output.payload_json["extensionCount"], 2);
assert_eq!(output.payload_json["extensionNames"][0], "transfer_fee_config");
assert_eq!(output.payload_json["extensionNames"][1], "confidential_transfer_mint");
assert_eq!(output.payload_json["containsConfidentialState"], true);
assert_eq!(output.payload_json["confidentialValuesDecrypted"], false);
assert_eq!(output.payload_json["baseFields"]["supply"], "42");
}
#[test]
fn token_2022_state_snapshot_is_idempotent_and_rejects_missing_identity() {
let state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Account,
base_fields: serde_json::json!({"mint":"mint111","owner":"wallet111","amount":"5"}),
base_hex: "00".repeat(165),
account_type: std::option::Option::None,
extensions: std::vec::Vec::new(),
};
let first = crate::materializer_token_materialize_token_2022_state_snapshot(
"account111",
7,
&state,
);
let second = crate::materializer_token_materialize_token_2022_state_snapshot(
"account111",
7,
&state,
);
assert_eq!(first, second);
assert!(
crate::materializer_token_materialize_token_2022_state_snapshot("", 7, &state).is_err()
);
}
}

View File

@@ -1,11 +0,0 @@
// file: kb-lib/src/materializer/token/constants.rs
// version: 3
//! Local constants for classic SPL Token account projections.
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_TOKEN_ACCOUNTS_PROCESSOR_NAME: &str = "materializer.token.accounts";
/// Stable projection payload contract version.
pub(crate) const MT_TOKEN_ACCOUNTS_PROJECTION_VERSION: u32 = 3;
/// Canonical tracing target for this component.
pub(crate) const TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS: &str = "kb-lib.materializer.token";

View File

@@ -1,61 +1,17 @@
// file: kb-lib/src/materializer/token/metadata_risk.rs
// version: 4
// version: 5
//! Reserved token-metadata risk materializer.
//! Token-metadata risk materializer component facade.
/// Reserved materializer for token-metadata risk observations.
#[derive(Clone, Debug, Default)]
pub struct MtTokenMetadataRiskMaterializer;
mod constants;
mod materializer;
impl crate::MtMaterializer for crate::MtTokenMetadataRiskMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.token.metadata_risk";
}
/// Reserved token-metadata risk materializer.
pub use self::materializer::MtTokenMetadataRiskMaterializer;
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, _event: &crate::MdDecodedProtocolEvent) -> bool {
return false;
}
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
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::TokenMetadataRisk,
}]);
}
}
impl crate::MtApiEventMaterializer for crate::MtTokenMetadataRiskMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.token.metadata_risk".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
_observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
return crate::MtApiMaterializerExecutionResult::ignored();
}
}
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_TOKEN_METADATA_RISK_ACCEPTED_FAMILIES;
/// Stable runtime component name for the reserved token-metadata risk materializer.
pub(crate) use self::constants::MT_TOKEN_METADATA_RISK_COMPONENT_NAME;
/// Stable processor name for the reserved token-metadata risk materializer.
pub(crate) use self::constants::MT_TOKEN_METADATA_RISK_PROCESSOR_NAME;

View File

@@ -0,0 +1,12 @@
// file: kb-lib/src/materializer/token/metadata_risk/constants.rs
// version: 2
//! Local constants for the token metadata_risk materializer.
/// Stable runtime component name for the token metadata_risk materializer.
pub(crate) const MT_TOKEN_METADATA_RISK_COMPONENT_NAME: &str =
"kb-lib.materializer.token.metadata_risk";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_TOKEN_METADATA_RISK_PROCESSOR_NAME: &str = "materializer.token.metadata_risk";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_TOKEN_METADATA_RISK_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -0,0 +1,56 @@
// file: kb-lib/src/materializer/token/metadata_risk/materializer.rs
// version: 5
//! Reserved token-metadata risk materializer.
/// Reserved materializer for token-metadata risk observations.
#[derive(Clone, Debug, Default)]
pub struct MtTokenMetadataRiskMaterializer;
impl crate::MtMaterializer for crate::MtTokenMetadataRiskMaterializer {
fn materializer_name(&self) -> &'static str {
return crate::MT_TOKEN_METADATA_RISK_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, _event: &crate::MdDecodedProtocolEvent) -> bool {
return false;
}
fn materialize_event(
&self,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtTokenMetadataRiskMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_TOKEN_METADATA_RISK_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return crate::MT_TOKEN_METADATA_RISK_ACCEPTED_FAMILIES;
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
_observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
return crate::MtApiMaterializerExecutionResult::ignored();
}
}

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/trades.rs
// version: 3
// version: 4
//! `trades` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved trades materializer.
pub use self::core::MtTradesMaterializer;
pub use self::materializer::MtTradesMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_TRADES_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `trades` materializer.
pub(crate) use self::constants::MT_TRADES_COMPONENT_NAME;
/// Stable processor name for the `trades` materializer.
pub(crate) use self::constants::MT_TRADES_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/trades/constants.rs
// version: 1
//! Local constants for the trades materializer.
/// Stable runtime component name for the trades materializer.
pub(crate) const MT_TRADES_COMPONENT_NAME: &str = "kb-lib.materializer.trades";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_TRADES_PROCESSOR_NAME: &str = "materializer.trades";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_TRADES_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,15 +1,15 @@
// file: kb-lib/src/materializer/trades/core.rs
// version: 4
// file: kb-lib/src/materializer/trades/materializer.rs
// version: 5
//! Reserved trades materializer.
/// Reserved materializer for trade observations.
/// Reserved materializer for trades observations.
#[derive(Clone, Debug, Default)]
pub struct MtTradesMaterializer;
impl crate::MtMaterializer for crate::MtTradesMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.trades";
return crate::MT_TRADES_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtTradesMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
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::Trade,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtTradesMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.trades".to_string(),
name: crate::MT_TRADES_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_TRADES_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,19 +1,24 @@
// file: kb-lib/src/materializer/transaction.rs
// version: 3
// version: 4
//! `transaction` materializer family.
mod annotations;
mod constants;
/// Stable committed Memo transaction annotation materializer.
pub use self::annotations::MtTransactionAnnotationMaterializer;
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::annotations::MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES;
/// Stable runtime component name for transaction annotations.
pub(crate) use self::annotations::MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME;
/// Maximum Memo payload accepted by transaction annotations.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES;
pub(crate) use self::annotations::MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES;
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES` component constant.
pub(crate) use self::annotations::MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES;
/// Stable processor name for transaction annotations.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME;
pub(crate) use self::annotations::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME;
/// Stable projection contract version for transaction annotations.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION;
pub(crate) use self::annotations::MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION;
/// Canonical tracing target for transaction annotations.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS;
pub(crate) use self::annotations::TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS;

View File

@@ -1,611 +1,25 @@
// file: kb-lib/src/materializer/transaction/annotations.rs
// version: 5
// version: 6
//! Exact committed SPL Memo transaction annotation projection.
//! Committed Memo transaction annotation materializer component.
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[crate::MdEventFamily::Audit];
const MEMO_ENTRY_NAMES: &[&str] = &["add_memo", "memo_intent", "invalid_memo_attempt"];
mod constants;
mod materializer;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum MemoGeneration {
V1,
V3,
V4,
}
/// Stable committed Memo transaction annotation materializer.
pub use self::materializer::MtTransactionAnnotationMaterializer;
impl MemoGeneration {
fn from_event(event: &crate::MdDecodedProtocolEvent) -> std::option::Option<Self> {
if event.program_id.0 == kb_program_ids::SPL_MEMO_V1_PROGRAM_ID
&& event.surface_code.0 == "spl.memo.v1"
{
return std::option::Option::Some(Self::V1);
}
if event.program_id.0 == kb_program_ids::SPL_MEMO_V3_PROGRAM_ID
&& event.surface_code.0 == "spl.memo.v3"
{
return std::option::Option::Some(Self::V3);
}
if event.program_id.0 == kb_program_ids::SPL_MEMO_V4_PROGRAM_ID
&& event.surface_code.0 == "spl.memo.v4"
{
return std::option::Option::Some(Self::V4);
}
return std::option::Option::None;
}
fn code(self) -> &'static str {
return match self {
Self::V1 => "v1",
Self::V3 => "v3",
Self::V4 => "v4",
};
}
fn signer_status(self) -> &'static str {
return match self {
Self::V1 => "not_applicable",
Self::V3 | Self::V4 => "valid",
};
}
}
struct MemoProjection {
generation: MemoGeneration,
memo_text: std::string::String,
payload_length_bytes: u64,
payload_sha256: std::string::String,
required_signers: std::vec::Vec<std::string::String>,
observed_signers: std::vec::Vec<std::string::String>,
verified_signers: std::vec::Vec<std::string::String>,
signer_validation_status: std::string::String,
}
/// Stable transaction annotation materializer.
#[derive(Clone, Debug, Default)]
pub struct MtTransactionAnnotationMaterializer;
impl crate::MtMaterializer for crate::MtTransactionAnnotationMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.transaction.annotations";
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return accepts_event(event);
}
fn materialize_event(
&self,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtTransactionAnnotationMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return accepts_event(&observation.event);
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
if !accepts_event(&observation.event) {
return crate::MtApiMaterializerExecutionResult::ignored();
}
if observation.transaction_failed || !observation.observation_committed {
return crate::MtApiMaterializerExecutionResult::refused(
"uncommitted_transaction_annotation_refused",
"failed or uncommitted Memo observations cannot create transaction annotations",
);
}
if observation.event.event_name.0 != "add_memo" {
return crate::MtApiMaterializerExecutionResult::refused(
"invalid_transaction_annotation_source_refused",
"only a committed add_memo observation can create a transaction annotation",
);
}
let projection_result = parse_projection(observation);
let projection = match projection_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(message) => {
return failed("memo_annotation_payload_invalid", message);
},
};
let idempotence_key = format!(
"spl_memo:{}:{}:{}:{}",
observation.event.signature.0,
observation.event.instruction_path.0,
observation.event.program_id.0,
projection.payload_sha256
);
let output = crate::MtApiMaterializedOutput {
output_key: "transaction_annotation:spl_memo:0".to_string(),
family: crate::MdMaterializedEventFamily::TransactionAnnotation,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION,
"domain": "transaction_annotation",
"annotationKind": "spl.memo",
"idempotenceKey": idempotence_key,
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"sourceKind": match observation.event.source_kind {
crate::MdEventSourceKind::InnerInstruction => "inner_instruction",
_ => "instruction"
},
"generation": projection.generation.code(),
"programId": observation.event.program_id.0.clone(),
"text": projection.memo_text,
"payloadLengthBytes": projection.payload_length_bytes,
"payloadSha256": projection.payload_sha256,
"signers": {
"validationStatus": projection.signer_validation_status,
"required": projection.required_signers,
"observed": projection.observed_signers,
"verified": projection.verified_signers
},
"committed": true,
"provenance": {
"processorName": crate::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"sourceProtocol": observation.event.protocol_code.0.clone(),
"sourceSurface": observation.event.surface_code.0.clone(),
"sourceEventCode": observation.event.event_code.0.clone(),
"sourceEventKey": observation.event_key.clone()
}
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS,
action = "materialize_transaction_annotation",
signature = %observation.event.signature.0,
instruction_path = %observation.event.instruction_path.0,
program_id = %observation.event.program_id.0,
generation = projection.generation.code(),
payload_length_bytes = projection.payload_length_bytes,
output_count = 1_usize,
"materialize committed SPL Memo transaction annotation"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
}
fn accepts_event(event: &crate::MdDecodedProtocolEvent) -> bool {
return event.event_family == crate::MdEventFamily::Audit
&& event.protocol_code.0 == "spl.memo"
&& (event.source_kind == crate::MdEventSourceKind::Instruction
|| event.source_kind == crate::MdEventSourceKind::InnerInstruction)
&& MemoGeneration::from_event(event).is_some()
&& MEMO_ENTRY_NAMES.contains(&event.event_name.0.as_str());
}
fn parse_projection(
observation: &crate::DcApiDecodedObservation,
) -> std::result::Result<MemoProjection, std::string::String> {
let generation = match MemoGeneration::from_event(&observation.event) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(
"Memo generation and Program ID do not match".to_string(),
);
},
};
let payload = &observation.payload_json;
if payload.get("committed").and_then(serde_json::Value::as_bool)
!= std::option::Option::Some(true)
{
return std::result::Result::Err("Memo payload is not marked committed".to_string());
}
if payload.get("transactionSucceeded").and_then(serde_json::Value::as_bool)
!= std::option::Option::Some(true)
|| payload.get("payloadComplete").and_then(serde_json::Value::as_bool)
!= std::option::Option::Some(true)
|| string_field(payload, "runtimeValidation")
!= std::option::Option::Some("proven_by_successful_transaction")
|| payload
.get("utf8Validation")
.and_then(|value| return string_field(value, "status"))
!= std::option::Option::Some("valid")
{
return std::result::Result::Err(
"Memo successful runtime validation proof is incomplete".to_string(),
);
}
if string_field(payload, "generation") != std::option::Option::Some(generation.code()) {
return std::result::Result::Err(
"Memo payload generation does not match its surface".to_string(),
);
}
if string_field(payload, "programId")
!= std::option::Option::Some(observation.event.program_id.0.as_str())
{
return std::result::Result::Err(
"Memo payload Program ID does not match its event".to_string(),
);
}
if string_field(payload, "instructionPath")
!= std::option::Option::Some(observation.event.instruction_path.0.as_str())
{
return std::result::Result::Err(
"Memo payload instruction path does not match its event".to_string(),
);
}
let memo_text = match string_field(payload, "memoText") {
std::option::Option::Some(value) => value.to_string(),
std::option::Option::None => {
return std::result::Result::Err("committed Memo has no UTF-8 text".to_string());
},
};
let payload_length_bytes =
match payload.get("payloadLengthBytes").and_then(serde_json::Value::as_u64) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err("committed Memo has no byte length".to_string());
},
};
let measured_length = match u64::try_from(memo_text.len()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!(
"Memo text length conversion failed: {error}"
));
},
};
if measured_length != payload_length_bytes {
return std::result::Result::Err(
"Memo byte length does not match its UTF-8 text".to_string(),
);
}
if payload_length_bytes > crate::MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES {
return std::result::Result::Err(
"Memo byte length exceeds the projection limit".to_string(),
);
}
let payload_sha256 = match string_field(payload, "payloadSha256") {
std::option::Option::Some(value) if is_sha256(value) => value.to_string(),
_ => return std::result::Result::Err("Memo payload SHA-256 is invalid".to_string()),
};
let signer_validation = match payload.get("signerValidation") {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err("Memo signer validation is absent".to_string());
},
};
let signer_validation_status = match string_field(signer_validation, "status") {
std::option::Option::Some(value) if value == generation.signer_status() => {
value.to_string()
},
_ => {
return std::result::Result::Err(
"Memo signer validation status is inconsistent".to_string(),
);
},
};
let missing_signers = match string_array(signer_validation, "missingRequiredSigners") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(message) => return std::result::Result::Err(message),
};
if !missing_signers.is_empty() {
return std::result::Result::Err("committed Memo reports missing signers".to_string());
}
let required_signers = match string_array(signer_validation, "orderedRequiredSigners") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(message) => return std::result::Result::Err(message),
};
let observed_signers = match string_array(signer_validation, "orderedObservedSigners") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(message) => return std::result::Result::Err(message),
};
let verified_signers = if generation == MemoGeneration::V1 {
if !required_signers.is_empty() {
return std::result::Result::Err("Memo v1 cannot require signers".to_string());
}
std::vec::Vec::new()
} else {
if required_signers != observed_signers {
return std::result::Result::Err(
"committed Memo required and observed signers differ".to_string(),
);
}
observed_signers.clone()
};
return std::result::Result::Ok(MemoProjection {
generation,
memo_text,
payload_length_bytes,
payload_sha256,
required_signers,
observed_signers,
verified_signers,
signer_validation_status,
});
}
fn string_field<'a>(value: &'a serde_json::Value, key: &str) -> std::option::Option<&'a str> {
return value.get(key).and_then(serde_json::Value::as_str);
}
fn string_array(
value: &serde_json::Value,
key: &str,
) -> std::result::Result<std::vec::Vec<std::string::String>, std::string::String> {
let values = match value.get(key).and_then(serde_json::Value::as_array) {
std::option::Option::Some(values) => values,
std::option::Option::None => {
return std::result::Result::Err(format!("Memo signer field {key} is not an array"));
},
};
let mut output = std::vec::Vec::with_capacity(values.len());
for item in values {
let text = match item.as_str() {
std::option::Option::Some(text) if !text.trim().is_empty() => text,
_ => {
return std::result::Result::Err(format!(
"Memo signer field {key} contains an invalid pubkey"
));
},
};
output.push(text.to_string());
}
return std::result::Result::Ok(output);
}
fn is_sha256(value: &str) -> bool {
return value.len() == 64
&& value
.as_bytes()
.iter()
.all(|byte| return byte.is_ascii_hexdigit() && !byte.is_ascii_uppercase());
}
fn failed(code: &str, message: std::string::String) -> crate::MtApiMaterializerExecutionResult {
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Failed,
outputs: std::vec::Vec::new(),
diagnostics: std::vec![crate::MtApiMaterializerDiagnostic {
code: code.to_string(),
message,
retriable: false,
}],
};
}
#[cfg(test)]
mod tests {
fn observation(
program_id: &str,
surface_code: &str,
generation: &str,
event_name: &str,
failed: bool,
committed: bool,
required_signers: serde_json::Value,
observed_signers: serde_json::Value,
) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: "memo:0".to_string(),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("1/0".to_string()),
program_id: crate::MdProgramId(program_id.to_string()),
protocol_code: crate::MdProtocolCode("spl.memo".to_string()),
surface_code: crate::MdSurfaceCode(surface_code.to_string()),
event_code: crate::MdEventCode(format!("{surface_code}.{event_name}")),
event_name: crate::MdEventName(event_name.to_string()),
event_family: crate::MdEventFamily::Audit,
source_kind: crate::MdEventSourceKind::InnerInstruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({
"generation": generation,
"programId": program_id,
"instructionPath": "1/0",
"transactionSucceeded": !failed,
"committed": committed,
"memoText": "memo",
"payloadLengthBytes": 4,
"payloadSha256": "1111111111111111111111111111111111111111111111111111111111111111",
"payloadComplete": true,
"utf8Validation": {"status":"valid","invalidFromByte":null},
"signerValidation": {
"status": if generation == "v1" { "not_applicable" } else { "valid" },
"orderedRequiredSigners": required_signers,
"orderedObservedSigners": observed_signers,
"missingRequiredSigners": []
},
"runtimeValidation": if failed {
"not_proven_transaction_failed"
} else {
"proven_by_successful_transaction"
}
}),
transaction_failed: failed,
transaction_error: if failed {
std::option::Option::Some(serde_json::json!({"InstructionError":[0,"Custom"]}))
} else {
std::option::Option::None
},
observation_committed: committed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["memo".to_string()],
},
};
}
fn v4() -> crate::DcApiDecodedObservation {
return observation(
kb_program_ids::SPL_MEMO_V4_PROGRAM_ID,
"spl.memo.v4",
"v4",
"add_memo",
false,
true,
serde_json::json!(["signer", "signer"]),
serde_json::json!(["signer", "signer"]),
);
}
#[test]
fn committed_v4_memo_becomes_deterministic_transaction_annotation() {
let materializer = crate::MtTransactionAnnotationMaterializer;
let first = crate::MtApiEventMaterializer::materialize(&materializer, &v4());
let second = crate::MtApiEventMaterializer::materialize(&materializer, &v4());
assert_eq!(first, second);
assert_eq!(first.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(first.outputs.len(), 1);
assert_eq!(
first.outputs[0].family,
crate::MdMaterializedEventFamily::TransactionAnnotation
);
assert_eq!(
first.outputs[0].payload_json["signers"]["verified"],
serde_json::json!(["signer", "signer"])
);
assert_eq!(first.outputs[0].payload_json["committed"], true);
assert!(first.validate().is_ok());
}
#[test]
fn exact_v1_v3_and_v4_surfaces_are_accepted() {
for (program_id, surface, generation) in [
(kb_program_ids::SPL_MEMO_V1_PROGRAM_ID, "spl.memo.v1", "v1"),
(kb_program_ids::SPL_MEMO_V3_PROGRAM_ID, "spl.memo.v3", "v3"),
(kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, "spl.memo.v4", "v4"),
] {
let observation = observation(
program_id,
surface,
generation,
"add_memo",
false,
true,
serde_json::json!([]),
serde_json::json!([]),
);
assert!(crate::MtApiEventMaterializer::accepts_observation(
&crate::MtTransactionAnnotationMaterializer,
&observation,
));
}
}
#[test]
fn v1_observed_accounts_are_not_claimed_as_runtime_verified() {
let observation = observation(
kb_program_ids::SPL_MEMO_V1_PROGRAM_ID,
"spl.memo.v1",
"v1",
"add_memo",
false,
true,
serde_json::json!([]),
serde_json::json!(["observed_only"]),
);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTransactionAnnotationMaterializer,
&observation,
);
assert_eq!(result.outputs[0].payload_json["signers"]["verified"], serde_json::json!([]));
assert_eq!(
result.outputs[0].payload_json["signers"]["observed"],
serde_json::json!(["observed_only"])
);
}
#[test]
fn failed_or_uncommitted_memo_is_refused_without_output() {
let observation = observation(
kb_program_ids::SPL_MEMO_V4_PROGRAM_ID,
"spl.memo.v4",
"v4",
"memo_intent",
true,
false,
serde_json::json!([]),
serde_json::json!([]),
);
let materializer = crate::MtTransactionAnnotationMaterializer;
let policy =
crate::materializer_api_validate_materialization_policy(&materializer, &observation);
assert!(policy.is_err());
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
assert!(result.outputs.is_empty());
}
#[test]
fn malformed_committed_payload_fails_closed() {
let mut observation = v4();
observation.payload_json["payloadSha256"] = serde_json::Value::String("bad".to_string());
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTransactionAnnotationMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Failed);
assert!(result.outputs.is_empty());
assert_eq!(result.diagnostics[0].code, "memo_annotation_payload_invalid");
}
#[test]
fn foreign_audit_observation_is_not_accepted() {
let mut observation = v4();
observation.event.protocol_code = crate::MdProtocolCode("foreign".to_string());
assert!(!crate::MtApiEventMaterializer::accepts_observation(
&crate::MtTransactionAnnotationMaterializer,
&observation,
));
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTransactionAnnotationMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
#[test]
fn machine_readable_matrix_declares_transaction_annotation_policy() {
let parsed = serde_json::from_str::<serde_json::Value>(include_str!(
"../../../../test-fixtures/contract-matrices/SPL_MEMO_MATRIX.json"
));
let matrix = match parsed {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("memo matrix is invalid JSON: {error}"),
};
assert_eq!(matrix["matrixVersion"], 4);
assert_eq!(matrix["materializationContract"]["crate"], "kb-lib");
assert_eq!(
matrix["materializationContract"]["transactionPolicy"],
"SuccessfulCommittedOnly"
);
assert_eq!(matrix["materializationContract"]["failedOrUncommittedOutput"], false);
}
}
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES;
/// Stable runtime component name for transaction annotations.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME;
/// Maximum Memo payload accepted by transaction annotations.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES;
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES` component constant.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES;
/// Stable processor name for transaction annotations.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME;
/// Stable projection contract version for transaction annotations.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION;
/// Canonical tracing target for transaction annotations.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS;

View File

@@ -0,0 +1,25 @@
// file: kb-lib/src/materializer/transaction/annotations/constants.rs
// version: 5
//! Local constants for the transaction annotations materializer.
/// Stable runtime component name for the transaction annotations materializer.
pub(crate) const MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME: &str =
"kb-lib.materializer.transaction.annotations";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME: &str =
"materializer.transaction.annotations";
/// Maximum Memo payload accepted by transaction annotations.
pub(crate) const MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES: u64 = 566;
/// Stable projection contract version for the transaction annotations materializer.
pub(crate) const MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION: u32 = 1;
/// Canonical tracing target for the transaction annotations materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS: &str =
"kb-lib.materializer.transaction.annotations";
/// Component contract for `accepted_families`.
pub(crate) const MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
&[crate::MdEventFamily::Audit];
/// Component contract for `memo_entry_names`.
pub(crate) const MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES: &[&str] =
&["add_memo", "memo_intent", "invalid_memo_attempt"];

Some files were not shown because too many files have changed in this diff Show More