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,5 +1,5 @@
# file: Cargo.toml
# version: 28
# version: 29
[workspace]
resolver = "3"
@@ -18,7 +18,7 @@ members = [
]
[workspace.package]
version = "0.4.8-pre.6"
version = "0.4.8-pre.7"
edition = "2024"
license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3"

View File

@@ -1,5 +1,5 @@
<!-- file: ROADMAP.md -->
<!-- version: 29 -->
<!-- version: 30 -->
# ROADMAP — khadhroony-bot3
@@ -70,7 +70,8 @@ La première prerelease produit le plan vivant et linventaire fermé avant to
- compléter multi-wallets, import/export, chiffrement, verrouillage, sauvegarde et restauration dans `kb-wallet` ;
- auditer pool, résilience, administration et besoins historiques de `kb-store` ;
- maintenir les scénarios UI Devnet/Testnet dans le desktop et leurs équivalents automatisés dans `kb-pipeline-demo-scenarios` ;
- compléter les validations Devnet spécialisées Metaplex Token Metadata reportées de `0.4.7` lorsque leur utilité le justifie.
- compléter les validations Devnet spécialisées Metaplex Token Metadata reportées de `0.4.7` lorsque leur utilité le justifie ;
- exécuter laudit transversal de complétude des décodeurs, matérialisateurs et exécuteurs déjà livrés : couverture officielle, propriété unique des faits, opérations dangereuses gouvernées par `ExSafetyChecker`, opérations obsolètes constructibles marquées `#[deprecated]` et versions remplacées maintenues en decode-only.
## 0.6.x — Anchor générique, programmes SPL et Metaplex complémentaires

View File

@@ -1,5 +1,5 @@
<!-- file: config/README.md -->
<!-- version: 15 -->
<!-- version: 16 -->
# Configuration locale
@@ -68,9 +68,12 @@ Les matérialisateurs natifs consolidés rejoignent la console et disposent de l
```text
logs/<profil>/kb-lib/materializer/admin/
logs/<profil>/kb-lib/materializer/compliance/
logs/<profil>/kb-lib/materializer/compliance/audit/
logs/<profil>/kb-lib/materializer/lifecycle/
logs/<profil>/kb-lib/materializer/risk/
logs/<profil>/kb-lib/materializer/staking/
logs/<profil>/kb-lib/materializer/token/accounts/
logs/<profil>/kb-lib/materializer/transaction/annotations/
```
Le test de configuration découvre dynamiquement toutes les crates déclarant `tracing.workspace = true` et vérifie leurs trois routes dans chaque profil. Les logs ne doivent contenir ni secret, ni DSN non masqué, ni payload Config ou bytecode complet.

View File

@@ -584,42 +584,42 @@
]
},
{
"name": "file_kb-lib_materializer_compliance_debug",
"name": "file_kb-lib_materializer_compliance_audit_debug",
"enabled": true,
"sink": "file",
"level": "debug",
"path": "logs/devnet/kb-lib/materializer/compliance/debug.log",
"path": "logs/devnet/kb-lib/materializer/compliance/audit/debug.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.compliance"
"kb-lib.materializer.compliance.audit"
]
},
{
"name": "file_kb-lib_materializer_compliance_info",
"name": "file_kb-lib_materializer_compliance_audit_info",
"enabled": true,
"sink": "file",
"level": "info",
"path": "logs/devnet/kb-lib/materializer/compliance/info.log",
"path": "logs/devnet/kb-lib/materializer/compliance/audit/info.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.compliance"
"kb-lib.materializer.compliance.audit"
]
},
{
"name": "file_kb-lib_materializer_compliance_error",
"name": "file_kb-lib_materializer_compliance_audit_error",
"enabled": true,
"sink": "file",
"level": "error",
"path": "logs/devnet/kb-lib/materializer/compliance/error.jsonl",
"path": "logs/devnet/kb-lib/materializer/compliance/audit/error.jsonl",
"rotation": "daily",
"format": "json",
"ansi": false,
"targets": [
"kb-lib.materializer.compliance"
"kb-lib.materializer.compliance.audit"
]
},
{
@@ -662,42 +662,42 @@
]
},
{
"name": "file_kb-lib_materializer_transaction_debug",
"name": "file_kb-lib_materializer_transaction_annotations_debug",
"enabled": true,
"sink": "file",
"level": "debug",
"path": "logs/devnet/kb-lib/materializer/transaction/debug.log",
"path": "logs/devnet/kb-lib/materializer/transaction/annotations/debug.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.transaction"
"kb-lib.materializer.transaction.annotations"
]
},
{
"name": "file_kb-lib_materializer_transaction_info",
"name": "file_kb-lib_materializer_transaction_annotations_info",
"enabled": true,
"sink": "file",
"level": "info",
"path": "logs/devnet/kb-lib/materializer/transaction/info.log",
"path": "logs/devnet/kb-lib/materializer/transaction/annotations/info.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.transaction"
"kb-lib.materializer.transaction.annotations"
]
},
{
"name": "file_kb-lib_materializer_transaction_error",
"name": "file_kb-lib_materializer_transaction_annotations_error",
"enabled": true,
"sink": "file",
"level": "error",
"path": "logs/devnet/kb-lib/materializer/transaction/error.jsonl",
"path": "logs/devnet/kb-lib/materializer/transaction/annotations/error.jsonl",
"rotation": "daily",
"format": "json",
"ansi": false,
"targets": [
"kb-lib.materializer.transaction"
"kb-lib.materializer.transaction.annotations"
]
},
{
@@ -899,42 +899,42 @@
]
},
{
"name": "file_kb-lib_materializer_token_debug",
"name": "file_kb-lib_materializer_token_accounts_debug",
"enabled": true,
"sink": "file",
"level": "debug",
"path": "logs/devnet/kb-lib/materializer/token/debug.log",
"path": "logs/devnet/kb-lib/materializer/token/accounts/debug.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.token"
"kb-lib.materializer.token.accounts"
]
},
{
"name": "file_kb-lib_materializer_token_info",
"name": "file_kb-lib_materializer_token_accounts_info",
"enabled": true,
"sink": "file",
"level": "info",
"path": "logs/devnet/kb-lib/materializer/token/info.log",
"path": "logs/devnet/kb-lib/materializer/token/accounts/info.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.token"
"kb-lib.materializer.token.accounts"
]
},
{
"name": "file_kb-lib_materializer_token_error",
"name": "file_kb-lib_materializer_token_accounts_error",
"enabled": true,
"sink": "file",
"level": "error",
"path": "logs/devnet/kb-lib/materializer/token/error.jsonl",
"path": "logs/devnet/kb-lib/materializer/token/accounts/error.jsonl",
"rotation": "daily",
"format": "json",
"ansi": false,
"targets": [
"kb-lib.materializer.token"
"kb-lib.materializer.token.accounts"
]
},
{
@@ -2002,42 +2002,42 @@
]
},
{
"name": "file_kb-lib_materializer_compliance_debug",
"name": "file_kb-lib_materializer_compliance_audit_debug",
"enabled": true,
"sink": "file",
"level": "debug",
"path": "logs/mainnet_research/kb-lib/materializer/compliance/debug.log",
"path": "logs/mainnet_research/kb-lib/materializer/compliance/audit/debug.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.compliance"
"kb-lib.materializer.compliance.audit"
]
},
{
"name": "file_kb-lib_materializer_compliance_info",
"name": "file_kb-lib_materializer_compliance_audit_info",
"enabled": true,
"sink": "file",
"level": "info",
"path": "logs/mainnet_research/kb-lib/materializer/compliance/info.log",
"path": "logs/mainnet_research/kb-lib/materializer/compliance/audit/info.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.compliance"
"kb-lib.materializer.compliance.audit"
]
},
{
"name": "file_kb-lib_materializer_compliance_error",
"name": "file_kb-lib_materializer_compliance_audit_error",
"enabled": true,
"sink": "file",
"level": "error",
"path": "logs/mainnet_research/kb-lib/materializer/compliance/error.jsonl",
"path": "logs/mainnet_research/kb-lib/materializer/compliance/audit/error.jsonl",
"rotation": "daily",
"format": "json",
"ansi": false,
"targets": [
"kb-lib.materializer.compliance"
"kb-lib.materializer.compliance.audit"
]
},
{
@@ -2080,42 +2080,42 @@
]
},
{
"name": "file_kb-lib_materializer_transaction_debug",
"name": "file_kb-lib_materializer_transaction_annotations_debug",
"enabled": true,
"sink": "file",
"level": "debug",
"path": "logs/mainnet_research/kb-lib/materializer/transaction/debug.log",
"path": "logs/mainnet_research/kb-lib/materializer/transaction/annotations/debug.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.transaction"
"kb-lib.materializer.transaction.annotations"
]
},
{
"name": "file_kb-lib_materializer_transaction_info",
"name": "file_kb-lib_materializer_transaction_annotations_info",
"enabled": true,
"sink": "file",
"level": "info",
"path": "logs/mainnet_research/kb-lib/materializer/transaction/info.log",
"path": "logs/mainnet_research/kb-lib/materializer/transaction/annotations/info.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.transaction"
"kb-lib.materializer.transaction.annotations"
]
},
{
"name": "file_kb-lib_materializer_transaction_error",
"name": "file_kb-lib_materializer_transaction_annotations_error",
"enabled": true,
"sink": "file",
"level": "error",
"path": "logs/mainnet_research/kb-lib/materializer/transaction/error.jsonl",
"path": "logs/mainnet_research/kb-lib/materializer/transaction/annotations/error.jsonl",
"rotation": "daily",
"format": "json",
"ansi": false,
"targets": [
"kb-lib.materializer.transaction"
"kb-lib.materializer.transaction.annotations"
]
},
{
@@ -2317,42 +2317,42 @@
]
},
{
"name": "file_kb-lib_materializer_token_debug",
"name": "file_kb-lib_materializer_token_accounts_debug",
"enabled": true,
"sink": "file",
"level": "debug",
"path": "logs/mainnet_research/kb-lib/materializer/token/debug.log",
"path": "logs/mainnet_research/kb-lib/materializer/token/accounts/debug.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.token"
"kb-lib.materializer.token.accounts"
]
},
{
"name": "file_kb-lib_materializer_token_info",
"name": "file_kb-lib_materializer_token_accounts_info",
"enabled": true,
"sink": "file",
"level": "info",
"path": "logs/mainnet_research/kb-lib/materializer/token/info.log",
"path": "logs/mainnet_research/kb-lib/materializer/token/accounts/info.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.token"
"kb-lib.materializer.token.accounts"
]
},
{
"name": "file_kb-lib_materializer_token_error",
"name": "file_kb-lib_materializer_token_accounts_error",
"enabled": true,
"sink": "file",
"level": "error",
"path": "logs/mainnet_research/kb-lib/materializer/token/error.jsonl",
"path": "logs/mainnet_research/kb-lib/materializer/token/accounts/error.jsonl",
"rotation": "daily",
"format": "json",
"ansi": false,
"targets": [
"kb-lib.materializer.token"
"kb-lib.materializer.token.accounts"
]
},
{
@@ -3620,42 +3620,42 @@
]
},
{
"name": "file_kb-lib_materializer_compliance_debug",
"name": "file_kb-lib_materializer_compliance_audit_debug",
"enabled": true,
"sink": "file",
"level": "debug",
"path": "logs/mainnet/kb-lib/materializer/compliance/debug.log",
"path": "logs/mainnet/kb-lib/materializer/compliance/audit/debug.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.compliance"
"kb-lib.materializer.compliance.audit"
]
},
{
"name": "file_kb-lib_materializer_compliance_info",
"name": "file_kb-lib_materializer_compliance_audit_info",
"enabled": true,
"sink": "file",
"level": "info",
"path": "logs/mainnet/kb-lib/materializer/compliance/info.log",
"path": "logs/mainnet/kb-lib/materializer/compliance/audit/info.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.compliance"
"kb-lib.materializer.compliance.audit"
]
},
{
"name": "file_kb-lib_materializer_compliance_error",
"name": "file_kb-lib_materializer_compliance_audit_error",
"enabled": true,
"sink": "file",
"level": "error",
"path": "logs/mainnet/kb-lib/materializer/compliance/error.jsonl",
"path": "logs/mainnet/kb-lib/materializer/compliance/audit/error.jsonl",
"rotation": "daily",
"format": "json",
"ansi": false,
"targets": [
"kb-lib.materializer.compliance"
"kb-lib.materializer.compliance.audit"
]
},
{
@@ -3698,42 +3698,42 @@
]
},
{
"name": "file_kb-lib_materializer_transaction_debug",
"name": "file_kb-lib_materializer_transaction_annotations_debug",
"enabled": true,
"sink": "file",
"level": "debug",
"path": "logs/mainnet/kb-lib/materializer/transaction/debug.log",
"path": "logs/mainnet/kb-lib/materializer/transaction/annotations/debug.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.transaction"
"kb-lib.materializer.transaction.annotations"
]
},
{
"name": "file_kb-lib_materializer_transaction_info",
"name": "file_kb-lib_materializer_transaction_annotations_info",
"enabled": true,
"sink": "file",
"level": "info",
"path": "logs/mainnet/kb-lib/materializer/transaction/info.log",
"path": "logs/mainnet/kb-lib/materializer/transaction/annotations/info.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.transaction"
"kb-lib.materializer.transaction.annotations"
]
},
{
"name": "file_kb-lib_materializer_transaction_error",
"name": "file_kb-lib_materializer_transaction_annotations_error",
"enabled": true,
"sink": "file",
"level": "error",
"path": "logs/mainnet/kb-lib/materializer/transaction/error.jsonl",
"path": "logs/mainnet/kb-lib/materializer/transaction/annotations/error.jsonl",
"rotation": "daily",
"format": "json",
"ansi": false,
"targets": [
"kb-lib.materializer.transaction"
"kb-lib.materializer.transaction.annotations"
]
},
{
@@ -3935,42 +3935,42 @@
]
},
{
"name": "file_kb-lib_materializer_token_debug",
"name": "file_kb-lib_materializer_token_accounts_debug",
"enabled": true,
"sink": "file",
"level": "debug",
"path": "logs/mainnet/kb-lib/materializer/token/debug.log",
"path": "logs/mainnet/kb-lib/materializer/token/accounts/debug.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.token"
"kb-lib.materializer.token.accounts"
]
},
{
"name": "file_kb-lib_materializer_token_info",
"name": "file_kb-lib_materializer_token_accounts_info",
"enabled": true,
"sink": "file",
"level": "info",
"path": "logs/mainnet/kb-lib/materializer/token/info.log",
"path": "logs/mainnet/kb-lib/materializer/token/accounts/info.log",
"rotation": "daily",
"format": "human",
"ansi": false,
"targets": [
"kb-lib.materializer.token"
"kb-lib.materializer.token.accounts"
]
},
{
"name": "file_kb-lib_materializer_token_error",
"name": "file_kb-lib_materializer_token_accounts_error",
"enabled": true,
"sink": "file",
"level": "error",
"path": "logs/mainnet/kb-lib/materializer/token/error.jsonl",
"path": "logs/mainnet/kb-lib/materializer/token/accounts/error.jsonl",
"rotation": "daily",
"format": "json",
"ansi": false,
"targets": [
"kb-lib.materializer.token"
"kb-lib.materializer.token.accounts"
]
},
{

View File

@@ -1,5 +1,5 @@
<!-- file: docs/OPERATION_NAMING_CONVENTION.md -->
<!-- version: 2 -->
<!-- version: 3 -->
# Convention canonique des identités runtime et des codes dopération
@@ -82,6 +82,15 @@ kb-lib.materializer.transaction.annotations
Cette identité désigne le composant logiciel, pas une opération on-chain.
Pour un matérialisateur, le tracing target reprend exactement cette identité runtime. La paire avec le `processor_name` est déterministe :
```text
component_name = kb-lib.<processor_name>
processor_name = component_name sans le préfixe kb-lib.
```
Les domaines spécialisés ne doivent pas être raccourcis : utiliser `compliance.audit`, `transaction.annotations` et `token.accounts`, et non les parents génériques `compliance`, `transaction` ou `token`.
### 3.2 `processor_name`
Format :
@@ -265,4 +274,3 @@ Avant de fusionner une nouvelle opération :
6. ajouter ou mettre à jour la matrice normative ;
7. ajouter les tests de cohérence entre constantes et matrice ;
8. vérifier limpact PostgreSQL avant tout backfill ou replay durable.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/README.md -->
<!-- version: 16 -->
<!-- version: 18 -->
# Documentation active de Khadhroony Bot3
@@ -40,6 +40,8 @@ Modèles documentaires non génératifs :
- [`audits/V0_4_7_PRE_016_DOCUMENTATION_AND_HEADERS_AUDIT.md`](audits/V0_4_7_PRE_016_DOCUMENTATION_AND_HEADERS_AUDIT.md) ;
- [`audits/V0_4_8_PRE_002_METADATA_CONTRACT_AUDIT.md`](audits/V0_4_8_PRE_002_METADATA_CONTRACT_AUDIT.md) ;
- [`audits/V0_4_8_PRE_007_METAPLEX_MATERIALIZER_STRUCTURE_AUDIT.md`](audits/V0_4_8_PRE_007_METAPLEX_MATERIALIZER_STRUCTURE_AUDIT.md) ;
- [`audits/V0_4_8_PRE_007_MATERIALIZER_CONVENTION_AUDIT.md`](audits/V0_4_8_PRE_007_MATERIALIZER_CONVENTION_AUDIT.md) ;
- [`decisions/DOCUMENT_ARCHIVE_SELECTION_POLICY.md`](decisions/DOCUMENT_ARCHIVE_SELECTION_POLICY.md) ;
- [`decisions/WINCODE_COMPATIBILITY_POLICY.md`](decisions/WINCODE_COMPATIBILITY_POLICY.md).
@@ -97,7 +99,9 @@ Les onze crates possèdent désormais `README.md`, `TODO.md`, `USAGE.md` et `CHA
## Audits actifs
- [`Audit contractuel 0.4.8-pre.002 — metadata`](audits/V0_4_8_PRE_002_METADATA_CONTRACT_AUDIT.md) ;
- [`Audit 0.4.8-pre.005 — instructions et historique Solana Program Metadata`](audits/V0_4_8_PRE_005_SOLANA_PROGRAM_METADATA_INSTRUCTION_HISTORY_AUDIT.md).
- [`Audit 0.4.8-pre.005 — instructions et historique Solana Program Metadata`](audits/V0_4_8_PRE_005_SOLANA_PROGRAM_METADATA_INSTRUCTION_HISTORY_AUDIT.md) ;
- [`Audit 0.4.8-pre.007 — structure du matérialiseur Metaplex Token Metadata`](audits/V0_4_8_PRE_007_METAPLEX_MATERIALIZER_STRUCTURE_AUDIT.md) ;
- [`Audit 0.4.8-pre.007 — conventions et structure de tous les matérialisateurs`](audits/V0_4_8_PRE_007_MATERIALIZER_CONVENTION_AUDIT.md).
## Plans de version actifs

View File

@@ -1,5 +1,5 @@
<!-- file: docs/audits/V0_4_8_PRE_002_METADATA_CONTRACT_AUDIT.md -->
<!-- version: 1 -->
<!-- version: 2 -->
# Audit contractuel `0.4.8-pre.002` — Solana Program Metadata et Token-2022
@@ -48,11 +48,11 @@ La structure, le Program ID, la version déclarée, les PDA, les comptes et les
### 4.1 PDA
| PDA | Seeds |
|---|---|
| canonical | `[program, seed]` |
| non-canonical | `[program, authority, seed]` |
| metadata | helper conditionnel sélectionnant lune des deux dérivations selon la présence dune autorité tierce |
| PDA | Seeds |
|---------------|------------------------------------------------------------------------------------------------------|
| canonical | `[program, seed]` |
| non-canonical | `[program, authority, seed]` |
| metadata | helper conditionnel sélectionnant lune des deux dérivations selon la présence dune autorité tierce |
`seed` est une chaîne UTF-8 de taille fixe 16 octets dans lIDL. Le code doit distinguer longueur en octets et nombre de caractères Unicode.
@@ -76,27 +76,27 @@ Ces valeurs décrivent uniquement les données on-chain. `Url` ne déclenche auc
### 4.4 Instructions
| Discriminant | Instruction | Comptes | Arguments |
|---:|---|---|---|
| `0` | `write` | `buffer` (writable)<br>`authority` (signer)<br>`sourceBuffer` (optional) | `offset: u32`<br>`data: Option<bytes>` |
| `1` | `initialize` | `metadata` (writable)<br>`authority` (signer)<br>`program`<br>`programData` (optional)<br>`system` (optional) | `seed: Seed`<br>`encoding: Encoding`<br>`compression: Compression`<br>`format: Format`<br>`dataSource: DataSource`<br>`data: Option<bytes>` |
| `2` | `setAuthority` | `account` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional) | `newAuthority: Option<pubkey>` |
| `3` | `setData` | `metadata` (writable)<br>`authority` (signer)<br>`buffer` (writable, optional)<br>`program` (optional)<br>`programData` (optional) | `encoding: Encoding`<br>`compression: Compression`<br>`format: Format`<br>`dataSource: DataSource`<br>`data: Option<bytes>` |
| `4` | `setImmutable` | `metadata` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional) | — |
| `5` | `trim` | `account` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional)<br>`destination` (writable)<br>`rent` | — |
| `6` | `close` | `account` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional)<br>`destination` (writable) | — |
| `7` | `allocate` | `buffer` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional)<br>`system` (optional) | `seed: Option<Seed>` |
| `8` | `extend` | `account` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional) | `length: u16` |
| Discriminant | Instruction | Comptes | Arguments |
|-------------:|----------------|----------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
| `0` | `write` | `buffer` (writable)<br>`authority` (signer)<br>`sourceBuffer` (optional) | `offset: u32`<br>`data: Option<bytes>` |
| `1` | `initialize` | `metadata` (writable)<br>`authority` (signer)<br>`program`<br>`programData` (optional)<br>`system` (optional) | `seed: Seed`<br>`encoding: Encoding`<br>`compression: Compression`<br>`format: Format`<br>`dataSource: DataSource`<br>`data: Option<bytes>` |
| `2` | `setAuthority` | `account` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional) | `newAuthority: Option<pubkey>` |
| `3` | `setData` | `metadata` (writable)<br>`authority` (signer)<br>`buffer` (writable, optional)<br>`program` (optional)<br>`programData` (optional) | `encoding: Encoding`<br>`compression: Compression`<br>`format: Format`<br>`dataSource: DataSource`<br>`data: Option<bytes>` |
| `4` | `setImmutable` | `metadata` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional) | — |
| `5` | `trim` | `account` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional)<br>`destination` (writable)<br>`rent` | — |
| `6` | `close` | `account` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional)<br>`destination` (writable) | — |
| `7` | `allocate` | `buffer` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional)<br>`system` (optional) | `seed: Option<Seed>` |
| `8` | `extend` | `account` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional) | `length: u16` |
### 4.5 Erreurs officielles
| Code | Erreur |
|---:|---|
| 0 | `NotExecutableAccount` |
| 1 | `InvalidProgramState` |
| 2 | `InvalidProgramDataAccount` |
| 3 | `ImmutableMetadataAccount` |
| 4 | `InvalidDataLength` |
| Code | Erreur |
|-----:|-----------------------------|
| 0 | `NotExecutableAccount` |
| 1 | `InvalidProgramState` |
| 2 | `InvalidProgramDataAccount` |
| 3 | `ImmutableMetadataAccount` |
| 4 | `InvalidDataLength` |
## 5. Décisions de modèles et de validation `ProgM6…`
@@ -113,13 +113,13 @@ Ces valeurs décrivent uniquement les données on-chain. `Url` ne déclenche auc
## 6. Matrice de couverture Token-2022 réelle
| Capacité | Décodage wire | Intent public | Builder | Exécution | Lecture stateful | Matérialisation | Scénario dédié | Desktop | Devnet confirmé |
|---|---|---|---|---|---|---|---|---|---|
| `Initialize` | oui | oui | oui | oui, via builder générique | état extension lisible | extension observée | non dédié | non dédié | non établi |
| `UpdateField` | oui | oui | oui | oui, via builder générique | état extension lisible | extension observée | non dédié | non dédié | non établi |
| `RemoveKey` | oui | oui | oui | oui, via builder générique | état extension lisible | extension observée | non dédié | non dédié | non établi |
| `UpdateAuthority` | oui | non | non | non | état extension lisible | extension observée | non | non | non |
| `Emit` | oui | non | non | non | résultat de retour non orchestré | non dédiée | non | non | non |
| Capacité | Décodage wire | Intent public | Builder | Exécution | Lecture stateful | Matérialisation | Scénario dédié | Desktop | Devnet confirmé |
|-------------------|---------------|---------------|---------|----------------------------|----------------------------------|--------------------|----------------|-----------|-----------------|
| `Initialize` | oui | oui | oui | oui, via builder générique | état extension lisible | extension observée | non dédié | non dédié | non établi |
| `UpdateField` | oui | oui | oui | oui, via builder générique | état extension lisible | extension observée | non dédié | non dédié | non établi |
| `RemoveKey` | oui | oui | oui | oui, via builder générique | état extension lisible | extension observée | non dédié | non dédié | non établi |
| `UpdateAuthority` | oui | non | non | non | état extension lisible | extension observée | non | non | non |
| `Emit` | oui | non | non | non | résultat de retour non orchestré | non dédiée | non | non | non |
Constats complémentaires :

View File

@@ -0,0 +1,189 @@
<!-- file: docs/audits/V0_4_8_PRE_007_MATERIALIZER_CONVENTION_AUDIT.md -->
<!-- version: 3 -->
# Audit des conventions et de la structure des matérialisateurs — `0.4.8-pre.007-fix-004`
## 1. Objet
Cet audit couvre lintégralité de `kb-lib/src/materializer` après lactivation du matérialisateur Solana Program Metadata et lextraction des projections Metaplex Token Metadata et Token-2022.
Les contrôles portent sur :
- les identités runtime et les `processorName` persistés ;
- les tracing targets ;
- la structure des façades et sous-modules ;
- lemplacement, le nommage et la réexportation des constantes ;
- les contrats communs `MtMaterializer` et `MtApiEventMaterializer` ;
- la neutralité effective des matérialisateurs encore réservés ;
- la provenance et les versions de projection des sorties actives.
## 2. Convention retenue
Chaque composant possède une paire didentités issue de son `constants.rs` :
```text
runtime component : kb-lib.materializer.<domain>[.<subsystem>]
processorName : materializer.<domain>[.<subsystem>]
```
Le `processorName` est exactement lidentité runtime privée du préfixe `kb-lib.`. Le tracing target dun composant actif est identique à son identité runtime.
Exemples :
```text
kb-lib.materializer.compliance.audit
materializer.compliance.audit
kb-lib.materializer.transaction.annotations
materializer.transaction.annotations
kb-lib.materializer.metadata.solana_program_metadata
materializer.metadata.solana_program_metadata
```
Les identités runtime servent au code et au routage des logs. Les `processorName` servent aux sorties persistées, au replay et à la provenance. Ils ne doivent pas être interchangeables.
## 3. Inventaire audité
| Composant | Type principal ou API | Statut | Identité runtime | `processorName` |
|-------------------------|---------------------------------------|------------|--------------------------------------------------------|-------------------------------------------------|
| Administration | `MtAdminMaterializer` | actif | `kb-lib.materializer.admin` | `materializer.admin` |
| Bridge | `MtBridgeMaterializer` | réservé | `kb-lib.materializer.bridge` | `materializer.bridge` |
| Compliance audit | `MtComplianceAuditMaterializer` | actif | `kb-lib.materializer.compliance.audit` | `materializer.compliance.audit` |
| Fees | `MtFeesMaterializer` | actif | `kb-lib.materializer.fees` | `materializer.fees` |
| Governance | `MtGovernanceMaterializer` | réservé | `kb-lib.materializer.governance` | `materializer.governance` |
| Lending | `MtLendingMaterializer` | réservé | `kb-lib.materializer.lending` | `materializer.lending` |
| Lifecycle | `MtLifecycleMaterializer` | actif | `kb-lib.materializer.lifecycle` | `materializer.lifecycle` |
| Liquidity | `MtLiquidityMaterializer` | réservé | `kb-lib.materializer.liquidity` | `materializer.liquidity` |
| Metaplex Token Metadata | APIs de snapshots | state-only | `kb-lib.materializer.metadata.metaplex_token_metadata` | `materializer.metadata.metaplex_token_metadata` |
| Solana Program Metadata | `MtSolanaProgramMetadataMaterializer` | actif | `kb-lib.materializer.metadata.solana_program_metadata` | `materializer.metadata.solana_program_metadata` |
| Token-2022 metadata | API de snapshots | state-only | `kb-lib.materializer.metadata.token_2022` | `materializer.metadata.token_2022` |
| NFT | `MtNftMaterializer` | réservé | `kb-lib.materializer.nft` | `materializer.nft` |
| Oracle | `MtOracleMaterializer` | réservé | `kb-lib.materializer.oracle` | `materializer.oracle` |
| Orderbook | `MtOrderbookMaterializer` | réservé | `kb-lib.materializer.orderbook` | `materializer.orderbook` |
| Perpetuals | `MtPerpetualsMaterializer` | réservé | `kb-lib.materializer.perpetuals` | `materializer.perpetuals` |
| Pool state | `MtPoolStateMaterializer` | réservé | `kb-lib.materializer.pool.state` | `materializer.pool.state` |
| Rewards | `MtRewardsMaterializer` | réservé | `kb-lib.materializer.rewards` | `materializer.rewards` |
| Risk | `MtRiskMaterializer` | actif | `kb-lib.materializer.risk` | `materializer.risk` |
| Routing | `MtRoutingMaterializer` | réservé | `kb-lib.materializer.routing` | `materializer.routing` |
| Staking | `MtStakingMaterializer` | actif | `kb-lib.materializer.staking` | `materializer.staking` |
| Token accounts | `MtTokenAccountsMaterializer` | actif | `kb-lib.materializer.token.accounts` | `materializer.token.accounts` |
| Token metadata risk | `MtTokenMetadataRiskMaterializer` | réservé | `kb-lib.materializer.token.metadata_risk` | `materializer.token.metadata_risk` |
| Trades | `MtTradesMaterializer` | réservé | `kb-lib.materializer.trades` | `materializer.trades` |
| Transaction annotations | `MtTransactionAnnotationMaterializer` | actif | `kb-lib.materializer.transaction.annotations` | `materializer.transaction.annotations` |
| Vault | `MtVaultMaterializer` | réservé | `kb-lib.materializer.vault` | `materializer.vault` |
Bilan : **25 composants nommés**, dont **9 matérialisateurs actifs**, **14 coquilles réservées** et **2 composants state-only**.
`MtMetadataMaterializer` reste un wrapper public de compatibilité vers `MtSolanaProgramMetadataMaterializer`. Il ne possède pas une identité distincte et ne crée pas un second propriétaire de projection.
## 4. Structure normalisée
Un matérialisateur simple suit désormais la structure :
```text
<component>.rs
<component>/constants.rs
<component>/materializer.rs
```
La façade `<component>.rs` ne contient que les déclarations de sous-modules et leurs réexports. Les composants complexes peuvent ajouter des fichiers spécialisés, par exemple `state.rs` ou `account.rs`, sans placer dimplémentation dans la façade.
Les domaines imbriqués suivent la même règle :
```text
compliance/audit.rs
compliance/audit/constants.rs
compliance/audit/materializer.rs
transaction/annotations.rs
transaction/annotations/constants.rs
transaction/annotations/materializer.rs
```
Les anciens noms génériques `core.rs` ont été remplacés par `materializer.rs`. Les tables de surfaces, familles acceptées, identités, versions de projection et tracing targets résident dans les `constants.rs`, sont réexportées jusquà `kb-lib/src/lib.rs` et sont consommées via `crate::`.
## 5. Coquilles réservées
Une coquille réservée doit :
- conserver son type public `Mt*Materializer` ;
- implémenter les deux traits communs ;
- exposer une constante `MT_*_ACCEPTED_FAMILIES` vide ;
- retourner `false` depuis lancien `accepts_event` ;
- retourner une liste vide depuis lancien `materialize_event` ;
- retourner `Ignored` depuis le contrat contextuel ;
- ne produire aucun événement, aucune sortie et aucune provenance fictive.
Le comportement antérieur qui pouvait retourner un événement legacy malgré une frontière inactive a été supprimé.
## 6. Provenance et versionnement
Les sorties actives utilisent uniquement leur constante `MT_*_PROCESSOR_NAME`. Aucun `processorName` littéral ne reste dans les implémentations.
Les changements de provenance Metaplex et Token-2022 livrés dans `fix-002` restent associés à `projectionVersion = 2`. Solana Program Metadata conserve `projectionVersion = 1`, car sa projection est introduite dans `0.4.8`.
Lajout dune provenance explicite aux sorties qui ne possédaient encore quune version numérique modifie leur contrat persisté. Les matérialisateurs `admin`, `compliance.audit`, `fees`, `lifecycle` et `staking` passent donc de `projectionVersion = 1` à `projectionVersion = 2`. Les matérialisateurs `risk`, `token.accounts` et `transaction.annotations` conservaient déjà une provenance spécialisée et gardent leurs versions existantes.
Les `output_key` et les familles métier existantes ne sont pas renommées par cet audit.
## 7. Configuration de logging
Les routes de `config/example.config.json` sont alignées sur les tracing targets spécialisés :
```text
kb-lib.materializer.compliance.audit
kb-lib.materializer.transaction.annotations
kb-lib.materializer.token.accounts
```
Les noms de sinks et leurs chemins sont harmonisés avec les mêmes sous-domaines.
## 8. Garde-fous ajoutés
Laudit mécanique du workspace vérifie désormais :
- la paire obligatoire `MT_*_COMPONENT_NAME` / `MT_*_PROCESSOR_NAME` ;
- la relation exacte obtenue par suppression du préfixe `kb-lib.` ;
- labsence de constantes hors `constants.rs` ;
- la présence dun `constants.rs` à côté de chaque implémentation ;
- labsence didentités littérales dans les implémentations ;
- la présence conjointe de `projectionVersion` et `processorName` dans chaque payload versionné ;
- lutilisation obligatoire des constantes `MT_*_PROJECTION_VERSION` et `MT_*_PROCESSOR_NAME` dans ces payloads ;
- la pureté des façades ;
- le résultat vide des deux contrats dune coquille réservée.
Les tests Rust conservent en plus linventaire typé des matérialisateurs et contrôlent la convention de nommage.
## 9. Fichiers obsolètes à supprimer
Les 22 chemins retirés sont fournis dans `delete-files.txt` à la racine du delta. Leur suppression est obligatoire avant la validation, car une extraction ZIP ne peut pas retirer les anciens fichiers.
## 10. Conclusion
Les matérialisateurs utilisent désormais une convention unique pour les identités runtime, les processeurs persistés, les tracing targets, les constantes, les façades et les coquilles réservées. Les différences restantes sont fonctionnelles et explicites : un composant peut être actif, réservé ou state-only, mais il ne change pas de convention structurelle ou de provenance selon son statut.
## 11. Correction des identités opérationnelles et du tracing
La compilation du workspace corrigé a révélé trois tracing targets déclarés mais sans événement réel :
- `kb-lib.materializer.fees` ;
- `kb-lib.materializer.metadata.metaplex_token_metadata` ;
- `kb-lib.materializer.metadata.token_2022`.
Le composant state-only Token-2022 réexportait également `MT_METADATA_TOKEN_2022_COMPONENT_NAME` sans le consommer dans son implémentation. Cette situation ne signifiait pas que les snapshots Token-2022 nétaient pas matérialisés : la fonction `materializer_metadata_materialize_token_2022_snapshot` produisait déjà les projections `TokenMetadata`, `TokenGroup` et `TokenGroupMember`. Elle révélait en revanche que lidentité runtime du composant nétait pas attachée à une opération observable.
Le correctif ajoute des événements structurés réels pour les chemins suivants :
- observation et snapshots de frais Token-2022 ;
- snapshots de comptes Metaplex et `MetadataV1` ;
- snapshots metadata, group et group-member Token-2022.
Les événements portent le tracing target du composant, son `component_name`, son `processor_name`, sa version, le statut et les identités disponibles. Aucun faux `let _target` nest utilisé et aucun champ persistant nest ajouté uniquement pour supprimer un warning.
Laudit mécanique refuse désormais :
- un `TRACING_TARGET_*` de `kb-lib` déclaré sans macro `tracing` réelle ;
- un `MT_*_COMPONENT_NAME` qui nest consommé par aucun fichier dimplémentation du composant.
Les composants state-only restent distincts des matérialiseurs instructionnels : ils nimplémentent pas artificiellement `MtApiEventMaterializer`, mais leur identité runtime est désormais effectivement utilisée par leurs opérations de snapshot.

View File

@@ -0,0 +1,109 @@
<!-- file: docs/audits/V0_4_8_PRE_007_METAPLEX_MATERIALIZER_STRUCTURE_AUDIT.md -->
<!-- version: 4 -->
# Audit `0.4.8-pre.007` — structure et identités des matérialiseurs metadata
## 1. Questions vérifiées
Deux ambiguïtés structurelles ont été vérifiées :
- labsence initiale dun sous-module dédié à Metaplex Token Metadata ;
- lemplacement et la couverture de la matérialisation Token-2022 Token Metadata.
Laudit a également séparé lidentité runtime dun composant de son `processorName` persisté.
## 2. Metaplex Token Metadata
Il nexistait pas doubli fonctionnel en `0.4.7`. Les APIs publiques suivantes étaient déjà implémentées :
- `materializer_metadata_materialize_metaplex_metadata_snapshot` pour létat autoritatif `MetadataV1` ;
- `materializer_metadata_materialize_metaplex_account_snapshot` pour les autres comptes Metaplex canoniques matérialisables.
Le défaut était structurel : ces fonctions partageaient auparavant `metadata/core.rs` avec dautres domaines. Elles sont désormais isolées dans :
```text
kb-lib/src/materializer/metadata/metaplex_token_metadata.rs
kb-lib/src/materializer/metadata/metaplex_token_metadata/constants.rs
kb-lib/src/materializer/metadata/metaplex_token_metadata/state.rs
kb-lib/src/materializer/metadata/metaplex_token_metadata/account.rs
```
Les clés de sortie et la sémantique métier restent inchangées. La provenance utilise désormais le `processorName` dédié :
```text
materializer.metadata.metaplex_token_metadata
```
Comme cette valeur persistée change par rapport au contrat antérieur, `projectionVersion` passe de `1` à `2`.
## 3. Token-2022 Token Metadata
Les états Token-2022 étaient déjà matérialisés par :
```text
materializer_metadata_materialize_token_2022_snapshot
```
Cette fonction projette les extensions de mint suivantes :
- `TokenMetadata` ;
- `TokenGroup` ;
- `TokenGroupMember`.
Elle était cependant placée dans `metadata/core.rs`. Le correctif lextrait vers :
```text
kb-lib/src/materializer/metadata/token_2022.rs
kb-lib/src/materializer/metadata/token_2022/constants.rs
kb-lib/src/materializer/metadata/token_2022/state.rs
```
La fonction publique et les `output_key` restent inchangées. Sa provenance utilise :
```text
materializer.metadata.token_2022
```
Le schéma de projection passe à `2` pour refléter cette correction didentité.
## 4. Écart fonctionnel Token-2022 restant
La matérialisation détat ne couvre pas à elle seule toutes les observations décodées.
Les faits `InitializeMetadataPointer` et `UpdateMetadataPointer` sont déjà possédés par le matérialiseur dadministration. En revanche, les cinq observations de `spl-token-metadata-interface` ne disposent pas encore dune matérialisation dinstruction dédiée :
- `Initialize` ;
- `UpdateField` ;
- `RemoveKey` ;
- `UpdateAuthority` ;
- `Emit`.
Cet écart doit être fermé dans `0.4.8-pre.010`, en même temps que la complétude des intents, builders, lectures avant/après et postconditions Token-2022.
## 5. Solana Program Metadata
Lidentité runtime du composant reste :
```text
kb-lib.materializer.metadata.solana_program_metadata
```
Le `processorName` persistant du composant Solana Program Metadata est corrigé en :
```text
materializer.metadata.solana_program_metadata
```
Le tracing target reste distinct et conserve son préfixe runtime :
```text
kb-lib.materializer.metadata.solana_program_metadata
```
## 6. Conséquence de migration
Les projections dérivées Metaplex et Token-2022 produites avec les anciennes provenances doivent être reconstruites depuis les raws ou migrées si elles sont conservées. Les clés de sortie ne changent pas.
## 7. Conclusion
Metaplex et les états metadata Token-2022 étaient déjà matérialisés. `pre.007-fix-002` corrige leurs identités et leur organisation modulaire. `pre.007-fix-003` étend ensuite la même convention à lintégralité des matérialisateurs ; voir `V0_4_8_PRE_007_MATERIALIZER_CONVENTION_AUDIT.md`. La seule lacune fonctionnelle confirmée concerne les cinq faits dinstructions `spl-token-metadata-interface`, explicitement reportés à `pre.010`.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/guides/LOGGING.md -->
<!-- version: 1 -->
<!-- version: 2 -->
# Guide de logging et tracing
@@ -50,8 +50,13 @@ kb-pipeline.backfill
kb-pipeline.decode-replay
kb-onchain-transport.http
kb-lib.executor.spl.token-2022
kb-lib.materializer.compliance.audit
kb-lib.materializer.token.accounts
kb-lib.materializer.transaction.annotations
```
Pour un matérialisateur, la target de tracing est lidentité runtime `kb-lib.materializer.<domain>[.<subsystem>]`. Elle reste distincte du `processorName` persisté `materializer.<domain>[.<subsystem>]`, qui ne doit pas être utilisé comme target de logs.
Une nouvelle target doit être ajoutée selon `docs/OPERATION_NAMING_CONVENTION.md` et les règles Khadhroony.
## Frontend desktop

View File

@@ -1,11 +1,11 @@
<!-- file: docs/plans/V0_4_8_SOLANA_PROGRAM_METADATA_AND_TOKEN_2022_COMPLETENESS_PLAN.md -->
<!-- version: 7 -->
<!-- version: 10 -->
# Plan `0.4.8` — Solana Program Metadata et complétude Token-2022
## 1. Statut et rôle
Ce document est le livrable principal de `0.4.8-pre.001`, réconcilié après laudit contractuel de `0.4.8-pre.002`, les modèles de `pre.003`, les décodeurs de comptes de `pre.004`, le décodeur dinstructions de `pre.005` et la matérialisation de `pre.006`. Il constitue le plan vivant de la version et reste modifiable lorsque laudit du code, des interfaces officielles, de lIDL ou des validations Devnet impose un ajustement.
Ce document est le livrable principal de `0.4.8-pre.001`, réconcilié après laudit contractuel de `0.4.8-pre.002`, les modèles de `pre.003`, les décodeurs de comptes de `pre.004`, le décodeur dinstructions de `pre.005`, la matérialisation de `pre.006` et lexécuteur de `pre.007`. Il constitue le plan vivant de la version et reste modifiable lorsque laudit du code, des interfaces officielles, de lIDL ou des validations Devnet impose un ajustement.
Il doit être maintenu pendant chaque prerelease, puis archivé pendant la dernière prerelease après transfert des décisions durables vers le ROADMAP, les matrices, les guides, les rapports, les TODO et les changelogs concernés.
@@ -15,13 +15,13 @@ Il doit être maintenu pendant chaque prerelease, puis archivé pendant la derni
### 2.1 Surfaces strictement distinctes
| Surface | Program ID ou frontière | Rôle dans `0.4.8` |
|---|---|---|
| Metaplex Token Metadata | `metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s` | surface clôturée en `0.4.7`, hors développement principal |
| Token-2022 Token Metadata | programme Token-2022 `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` | compléter le contrat déjà partiellement pris en charge |
| `spl-token-metadata-interface` | interface sans Program ID autonome imposé | source contractuelle des instructions metadata implémentées par Token-2022 |
| Solana Program Metadata | `ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S` | nouvelle surface on-chain à implémenter sous `metadata/solana_program_metadata` |
| contenu distant HTTP/IPFS/Arweave | transport off-chain indépendant | hors `0.4.8`, reporté à lhorizon `0.15+` |
| Surface | Program ID ou frontière | Rôle dans `0.4.8` |
|-----------------------------------|--------------------------------------------------------------------|---------------------------------------------------------------------------------|
| Metaplex Token Metadata | `metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s` | surface clôturée en `0.4.7`, hors développement principal |
| Token-2022 Token Metadata | programme Token-2022 `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` | compléter le contrat déjà partiellement pris en charge |
| `spl-token-metadata-interface` | interface sans Program ID autonome imposé | source contractuelle des instructions metadata implémentées par Token-2022 |
| Solana Program Metadata | `ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S` | nouvelle surface on-chain à implémenter sous `metadata/solana_program_metadata` |
| contenu distant HTTP/IPFS/Arweave | transport off-chain indépendant | hors `0.4.8`, reporté à lhorizon `0.15+` |
Aucun décodeur Token-2022 existant nest un décodeur partiel de `ProgM6…`. Les deux contrats doivent conserver des namespaces, modèles, matrices, tests et scénarios distincts.
@@ -259,16 +259,22 @@ Résultat durable : les deux comptes décodés et les neuf instructions stables
- matrice de onze projections et test de façade externe ;
- aucune résolution de contenu distant ni lecture implicite dun compte externe.
Critère atteint côté delta : tout ce qui est décodé dans la surface `ProgM6…` est matérialisable. Compilation, Clippy et tests Rust restent à exécuter dans lenvironnement du projet avant validation définitive.
Validation définitive reçue le 5 août 2026 : `cargo fmt --all`, `cargo check --workspace`, `cargo clippy --all-targets` et laudit workspace sont propres ; `cargo test -p kb-lib` réussit avec 667 tests unitaires, les tests dAPI externes et les doc-tests.
### `0.4.8-pre.007` — builders et exécuteur Solana Program Metadata
### `0.4.8-pre.007` — builders et exécuteur Solana Program Metadata — terminé côté delta
- PDA, calculs de taille et rent ;
- builders contractuellement équivalents pour toutes les opérations courantes, y compris les mutations destructrices, irréversibles ou écrasantes ;
- politiques de signataires, autorités, coûts, confirmations, postconditions et capacités ;
- intégration explicite au cadre commun `crate::ExSafetyChecker` : le builder expose le plan complet, tandis que la politique de sécurité décide si ce plan peut progresser vers la simulation, la signature et lenvoi ;
- classification opérationnelle conforme aux règles : opérations courantes exécutables, opérations officiellement obsolètes annotées `#[deprecated]` avec approbation explicite, versions officiellement remplacées conservées en decode-only ;
- état audit actuel : les neuf instructions stables `0..8` sont courantes, sans instruction remplacée ou obsolète, donc toutes les neuf doivent être ciblées par lexécuteur sauf découverte officielle contraire documentée avant code.
Résultat durable : les neuf instructions stables possèdent une opération typée, un builder wire exact et une capacité dexécution. Les mutations dangereuses restent constructibles mais exigent une approbation explicite ; tous les plans traversent `ExSafetyChecker` avant dêtre remis à lorchestration.
- helpers de PDA canonical et non-canonical avec seed exact de 16 octets ;
- sélection de canonicité explicite dans lintent, indépendante de la seule présence de `ProgramData` ;
- builders des neuf opérations, comptes positionnels, signataires et variantes inline/Buffer ;
- simulation et quatre postconditions obligatoires ;
- approbation explicite pour `Write`, `SetAuthority`, `SetData`, `SetImmutable`, `Trim` et `Close` ;
- matrice dexécution et test dAPI externe ;
- aucune opération stable classée deprecated ou replaced faute de preuve officielle ;
- financement de rent volontairement laissé à la fixture et à lorchestration RPC : le programme exige que les comptes à allouer ou étendre soient préfinancés.
Travaux parallèles intégrés : les fonctions de matérialisation Metaplex existaient déjà dans `materializer/metadata/core.rs`; il ne sagissait donc pas dun oubli fonctionnel de `0.4.6/0.4.7`. Elles sont déplacées dans un sous-module dédié sans changement de clé métier. Token-2022 reçoit également son composant state-only dédié. Laudit est ensuite étendu à lensemble de `src/materializer` : 25 identités sont harmonisées, les façades deviennent pures, les implémentations utilisent `materializer.rs`, les constantes sont centralisées et les coquilles réservées ne produisent plus de faux événements legacy. Les constats sont consignés dans `docs/audits/V0_4_8_PRE_007_METAPLEX_MATERIALIZER_STRUCTURE_AUDIT.md` et `docs/audits/V0_4_8_PRE_007_MATERIALIZER_CONVENTION_AUDIT.md`. Un audit transversal de toutes les surfaces déjà livrées est planifié en `0.5.x` pour vérifier les règles de décodage exhaustif, propriété de matérialisation, exécution des opérations dangereuses via `ExSafetyChecker`, dépréciation officielle et versions remplacées decode-only.
### `0.4.8-pre.008` — pipeline stateful Solana Program Metadata
@@ -283,6 +289,9 @@ Critère atteint côté delta : tout ce qui est décodé dans la surface `ProgM6
### `0.4.8-pre.010` — complétude Token-2022 Token Metadata
- implémentation des écarts confirmés de `Initialize`, `UpdateField`, `RemoveKey`, `UpdateAuthority` et `Emit` ;
- conservation du matérialiseur détat dédié `metadata/token_2022/` pour les extensions `TokenMetadata`, `TokenGroup` et `TokenGroupMember` ;
- ajout dune matérialisation explicite des cinq observations dinstructions `spl-token-metadata-interface`, actuellement absente ;
- maintien de `InitializeMetadataPointer` et `UpdateMetadataPointer` dans le matérialiseur dadministration qui en possède déjà les faits ;
- tests synthétiques et stateful.
### `0.4.8-pre.011` — campagne Devnet Token-2022 Token Metadata

View File

@@ -1,5 +1,5 @@
<!-- file: docs/rules/RULES_SPECIFIC_KHADHROONY.md -->
<!-- version: 8 -->
<!-- version: 10 -->
# Règles spécifiques à `khadhroony-bot3`
@@ -53,6 +53,13 @@ Toute divergence avec une règle générale doit être explicitement documentée
- Une même réalité métier doit avoir une seule projection canonique. Les snapshots de comptes sont la source autoritative de l'état final lorsqu'ils sont disponibles ; les instructions et événements corrélés restent des observations de mutation et ne doivent pas produire un doublon concurrent du même état.
- Les matérialisateurs doivent refuser les transactions non commitées pour les mutations d'état, tout en pouvant conserver séparément une intention non commitée lorsque le modèle métier le prévoit explicitement.
- Toute absence volontaire de projection doit être documentée avec une justification technique précise. Un matérialisateur ne doit inventer ni état final, ni montant, ni autorité, ni frais, ni agrégation que l'observation ne prouve pas.
- Chaque matérialisateur concret possède un composant structuré avec une façade limitée aux déclarations de sous-modules et réexports, un `constants.rs` et un `materializer.rs`. Les fichiers spécialisés `state.rs`, `account.rs` ou équivalents sont ajoutés seulement lorsque leur responsabilité est distincte.
- Lidentité runtime suit `kb-lib.materializer.<domain>[.<subsystem>]`. Le `processorName` persisté suit `materializer.<domain>[.<subsystem>]` et doit être exactement lidentité runtime sans le préfixe `kb-lib.`. Le tracing target dun composant actif est identique à son identité runtime.
- Les constantes dun composant utilisent le préfixe `MT_<COMPONENT>_`, résident dans son `constants.rs`, sont réexportées par chaque façade jusquà `kb-lib/src/lib.rs` et sont consommées par `crate::`, y compris dans les tests lorsquelles sont `pub` ou `pub(crate)`.
- Toute sortie persistée contient un `processorName` issu de la constante du composant et une `projectionVersion` explicite. Une correction de provenance persistée impose une nouvelle version de projection.
- Une coquille réservée implémente `MtMaterializer` et `MtApiEventMaterializer`, expose une constante `MT_*_ACCEPTED_FAMILIES` vide, refuse tous les événements, retourne une liste legacy vide et un résultat contextuel `Ignored`. Elle ne doit jamais créer un faux événement de transition.
- Un composant state-only peut exposer uniquement des APIs de snapshots sans implémenter `MtApiEventMaterializer`; ce statut doit être explicite et ne crée pas artificiellement un matérialisateur instructionnel.
- Un wrapper public de compatibilité délègue vers le composant canonique et conserve exactement la même identité. Il ne constitue pas un second propriétaire de faits.
### Couverture des exécuteurs
@@ -178,7 +185,7 @@ Aucun renommage massif de modules n'est autorisé sans étape de contrôle dédi
- Les targets historiques `khbot.*` et les targets de fenêtre sont interdits dans les nouvelles modifications.
- Les macros `tracing` doivent utiliser `target: crate::TRACING_TARGET` et des champs structurés stables. La granularité interne passe par `action`, `stage`, `window`, `campaign_id`, `signature`, `instruction_path`, `program_id`, `processor_name`, `processor_version`, `status` et `error_code`.
- Les crates passives de types, contrats, DTO, API sans exécution, registres ou constantes restent sans dépendance `tracing`. `kb-config` reste une exception de bootstrap tant que sa validation précède linstallation du subscriber.
- Il est interdit dajouter `tracing` sans événement réel ou de conserver un faux target uniquement consommé par `let _target`.
- Il est interdit dajouter `tracing` sans événement réel, de conserver un tracing target déclaré mais inutilisé ou de consommer artificiellement un target avec `let _target`.
- Une décision interne doit être journalisée par la crate responsable ; `kb-app-demo-desktop` ne journalise que les frontières Tauri/UI, les actions utilisateur et les résumés dorchestration.
- Tout input sélectionné sans décodeur compatible, tout résultat de décodage `failed` ou `unsupported`, tout résultat de matérialisation `failed`, toute validation de résultat invalide et toute erreur de persistance doivent émettre un événement `error` avant le retour ou la persistance terminale.
- Une transaction Solana échouée mais correctement décodée nest pas une erreur du logiciel. Une décision `ignored`, un refus de matérialisation conforme à la politique ou une annulation coopérative ne doivent pas être promus artificiellement au niveau `error`.
@@ -187,7 +194,7 @@ Aucun renommage massif de modules n'est autorisé sans étape de contrôle dédi
- Chaque profil doit router les événements vers les sorties globales `debug.log`, `info.log`, `error.jsonl` et `app.log`, puis vers `debug.log`, `info.log` et `error.jsonl` dans un répertoire propre à chaque crate utilisant `tracing`.
- Lajout ou la suppression de `tracing.workspace = true` dans une crate impose la mise à jour simultanée de la matrice de routes, de ses tests et de `docs/TRACING_CONTRACT.md`.
- Le contrat détaillé est défini dans `docs/TRACING_CONTRACT.md`.
- Laudit mécanique spécifique au projet est exécuté par `python3 scripts/audit_khadhroony_workspace_rules.py`. Il couvre notamment `TRACING_TARGET_DECODER_METADATA_METAPLEX_TOKEN_METADATA` et lusage obligatoire de `solana_pubkey::Pubkey` à la place de `solana_address::Address`.
- Laudit mécanique spécifique au projet est exécuté par `python3 scripts/audit_khadhroony_workspace_rules.py`. Il couvre notamment les conventions de composants et `processorName` des matérialisateurs, la pureté de leurs façades, la neutralité des coquilles réservées, `TRACING_TARGET_DECODER_METADATA_METAPLEX_TOKEN_METADATA` et lusage obligatoire de `solana_pubkey::Pubkey` à la place de `solana_address::Address`.
## Règles de réutilisation des interfaces Solana et SPL

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;

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