v0.4.8-pre.007
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 28
|
# version: 29
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
@@ -18,7 +18,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.4.8-pre.6"
|
version = "0.4.8-pre.7"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3"
|
repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: ROADMAP.md -->
|
<!-- file: ROADMAP.md -->
|
||||||
<!-- version: 29 -->
|
<!-- version: 30 -->
|
||||||
|
|
||||||
# ROADMAP — khadhroony-bot3
|
# ROADMAP — khadhroony-bot3
|
||||||
|
|
||||||
@@ -70,7 +70,8 @@ La première prerelease produit le plan vivant et l’inventaire fermé avant to
|
|||||||
- compléter multi-wallets, import/export, chiffrement, verrouillage, sauvegarde et restauration dans `kb-wallet` ;
|
- 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` ;
|
- 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` ;
|
- 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 l’audit 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
|
## 0.6.x — Anchor générique, programmes SPL et Metaplex complémentaires
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: config/README.md -->
|
<!-- file: config/README.md -->
|
||||||
<!-- version: 15 -->
|
<!-- version: 16 -->
|
||||||
|
|
||||||
# Configuration locale
|
# Configuration locale
|
||||||
|
|
||||||
@@ -68,9 +68,12 @@ Les matérialisateurs natifs consolidés rejoignent la console et disposent de l
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
logs/<profil>/kb-lib/materializer/admin/
|
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/lifecycle/
|
||||||
|
logs/<profil>/kb-lib/materializer/risk/
|
||||||
logs/<profil>/kb-lib/materializer/staking/
|
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.
|
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.
|
||||||
|
|||||||
@@ -584,42 +584,42 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "file_kb-lib_materializer_compliance_debug",
|
"name": "file_kb-lib_materializer_compliance_audit_debug",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "debug",
|
"level": "debug",
|
||||||
"path": "logs/devnet/kb-lib/materializer/compliance/debug.log",
|
"path": "logs/devnet/kb-lib/materializer/compliance/audit/debug.log",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "info",
|
"level": "info",
|
||||||
"path": "logs/devnet/kb-lib/materializer/compliance/info.log",
|
"path": "logs/devnet/kb-lib/materializer/compliance/audit/info.log",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "error",
|
"level": "error",
|
||||||
"path": "logs/devnet/kb-lib/materializer/compliance/error.jsonl",
|
"path": "logs/devnet/kb-lib/materializer/compliance/audit/error.jsonl",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "json",
|
"format": "json",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "debug",
|
"level": "debug",
|
||||||
"path": "logs/devnet/kb-lib/materializer/transaction/debug.log",
|
"path": "logs/devnet/kb-lib/materializer/transaction/annotations/debug.log",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "info",
|
"level": "info",
|
||||||
"path": "logs/devnet/kb-lib/materializer/transaction/info.log",
|
"path": "logs/devnet/kb-lib/materializer/transaction/annotations/info.log",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "error",
|
"level": "error",
|
||||||
"path": "logs/devnet/kb-lib/materializer/transaction/error.jsonl",
|
"path": "logs/devnet/kb-lib/materializer/transaction/annotations/error.jsonl",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "json",
|
"format": "json",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "debug",
|
"level": "debug",
|
||||||
"path": "logs/devnet/kb-lib/materializer/token/debug.log",
|
"path": "logs/devnet/kb-lib/materializer/token/accounts/debug.log",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "info",
|
"level": "info",
|
||||||
"path": "logs/devnet/kb-lib/materializer/token/info.log",
|
"path": "logs/devnet/kb-lib/materializer/token/accounts/info.log",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "error",
|
"level": "error",
|
||||||
"path": "logs/devnet/kb-lib/materializer/token/error.jsonl",
|
"path": "logs/devnet/kb-lib/materializer/token/accounts/error.jsonl",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "json",
|
"format": "json",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "debug",
|
"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",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "info",
|
"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",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "error",
|
"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",
|
"rotation": "daily",
|
||||||
"format": "json",
|
"format": "json",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "debug",
|
"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",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "info",
|
"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",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "error",
|
"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",
|
"rotation": "daily",
|
||||||
"format": "json",
|
"format": "json",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "debug",
|
"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",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "info",
|
"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",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "error",
|
"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",
|
"rotation": "daily",
|
||||||
"format": "json",
|
"format": "json",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "debug",
|
"level": "debug",
|
||||||
"path": "logs/mainnet/kb-lib/materializer/compliance/debug.log",
|
"path": "logs/mainnet/kb-lib/materializer/compliance/audit/debug.log",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "info",
|
"level": "info",
|
||||||
"path": "logs/mainnet/kb-lib/materializer/compliance/info.log",
|
"path": "logs/mainnet/kb-lib/materializer/compliance/audit/info.log",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "error",
|
"level": "error",
|
||||||
"path": "logs/mainnet/kb-lib/materializer/compliance/error.jsonl",
|
"path": "logs/mainnet/kb-lib/materializer/compliance/audit/error.jsonl",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "json",
|
"format": "json",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "debug",
|
"level": "debug",
|
||||||
"path": "logs/mainnet/kb-lib/materializer/transaction/debug.log",
|
"path": "logs/mainnet/kb-lib/materializer/transaction/annotations/debug.log",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "info",
|
"level": "info",
|
||||||
"path": "logs/mainnet/kb-lib/materializer/transaction/info.log",
|
"path": "logs/mainnet/kb-lib/materializer/transaction/annotations/info.log",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "error",
|
"level": "error",
|
||||||
"path": "logs/mainnet/kb-lib/materializer/transaction/error.jsonl",
|
"path": "logs/mainnet/kb-lib/materializer/transaction/annotations/error.jsonl",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "json",
|
"format": "json",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "debug",
|
"level": "debug",
|
||||||
"path": "logs/mainnet/kb-lib/materializer/token/debug.log",
|
"path": "logs/mainnet/kb-lib/materializer/token/accounts/debug.log",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "info",
|
"level": "info",
|
||||||
"path": "logs/mainnet/kb-lib/materializer/token/info.log",
|
"path": "logs/mainnet/kb-lib/materializer/token/accounts/info.log",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"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,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "error",
|
"level": "error",
|
||||||
"path": "logs/mainnet/kb-lib/materializer/token/error.jsonl",
|
"path": "logs/mainnet/kb-lib/materializer/token/accounts/error.jsonl",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "json",
|
"format": "json",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
"targets": [
|
"targets": [
|
||||||
"kb-lib.materializer.token"
|
"kb-lib.materializer.token.accounts"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/OPERATION_NAMING_CONVENTION.md -->
|
<!-- file: docs/OPERATION_NAMING_CONVENTION.md -->
|
||||||
<!-- version: 2 -->
|
<!-- version: 3 -->
|
||||||
|
|
||||||
# Convention canonique des identités runtime et des codes d’opération
|
# Convention canonique des identités runtime et des codes d’opération
|
||||||
|
|
||||||
@@ -82,6 +82,15 @@ kb-lib.materializer.transaction.annotations
|
|||||||
|
|
||||||
Cette identité désigne le composant logiciel, pas une opération on-chain.
|
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`
|
### 3.2 `processor_name`
|
||||||
|
|
||||||
Format :
|
Format :
|
||||||
@@ -265,4 +274,3 @@ Avant de fusionner une nouvelle opération :
|
|||||||
6. ajouter ou mettre à jour la matrice normative ;
|
6. ajouter ou mettre à jour la matrice normative ;
|
||||||
7. ajouter les tests de cohérence entre constantes et matrice ;
|
7. ajouter les tests de cohérence entre constantes et matrice ;
|
||||||
8. vérifier l’impact PostgreSQL avant tout backfill ou replay durable.
|
8. vérifier l’impact PostgreSQL avant tout backfill ou replay durable.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/README.md -->
|
<!-- file: docs/README.md -->
|
||||||
<!-- version: 16 -->
|
<!-- version: 18 -->
|
||||||
|
|
||||||
# Documentation active de Khadhroony Bot3
|
# 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_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_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/DOCUMENT_ARCHIVE_SELECTION_POLICY.md`](decisions/DOCUMENT_ARCHIVE_SELECTION_POLICY.md) ;
|
||||||
- [`decisions/WINCODE_COMPATIBILITY_POLICY.md`](decisions/WINCODE_COMPATIBILITY_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
|
## Audits actifs
|
||||||
|
|
||||||
- [`Audit contractuel 0.4.8-pre.002 — metadata`](audits/V0_4_8_PRE_002_METADATA_CONTRACT_AUDIT.md) ;
|
- [`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
|
## Plans de version actifs
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/audits/V0_4_8_PRE_002_METADATA_CONTRACT_AUDIT.md -->
|
<!-- 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
|
# Audit contractuel `0.4.8-pre.002` — Solana Program Metadata et Token-2022
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ La structure, le Program ID, la version déclarée, les PDA, les comptes et les
|
|||||||
### 4.1 PDA
|
### 4.1 PDA
|
||||||
|
|
||||||
| PDA | Seeds |
|
| PDA | Seeds |
|
||||||
|---|---|
|
|---------------|------------------------------------------------------------------------------------------------------|
|
||||||
| canonical | `[program, seed]` |
|
| canonical | `[program, seed]` |
|
||||||
| non-canonical | `[program, authority, seed]` |
|
| non-canonical | `[program, authority, seed]` |
|
||||||
| metadata | helper conditionnel sélectionnant l’une des deux dérivations selon la présence d’une autorité tierce |
|
| metadata | helper conditionnel sélectionnant l’une des deux dérivations selon la présence d’une autorité tierce |
|
||||||
@@ -77,7 +77,7 @@ Ces valeurs décrivent uniquement les données on-chain. `Url` ne déclenche auc
|
|||||||
### 4.4 Instructions
|
### 4.4 Instructions
|
||||||
|
|
||||||
| Discriminant | Instruction | Comptes | Arguments |
|
| Discriminant | Instruction | Comptes | Arguments |
|
||||||
|---:|---|---|---|
|
|-------------:|----------------|----------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `0` | `write` | `buffer` (writable)<br>`authority` (signer)<br>`sourceBuffer` (optional) | `offset: u32`<br>`data: Option<bytes>` |
|
| `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>` |
|
| `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>` |
|
| `2` | `setAuthority` | `account` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional) | `newAuthority: Option<pubkey>` |
|
||||||
@@ -91,7 +91,7 @@ Ces valeurs décrivent uniquement les données on-chain. `Url` ne déclenche auc
|
|||||||
### 4.5 Erreurs officielles
|
### 4.5 Erreurs officielles
|
||||||
|
|
||||||
| Code | Erreur |
|
| Code | Erreur |
|
||||||
|---:|---|
|
|-----:|-----------------------------|
|
||||||
| 0 | `NotExecutableAccount` |
|
| 0 | `NotExecutableAccount` |
|
||||||
| 1 | `InvalidProgramState` |
|
| 1 | `InvalidProgramState` |
|
||||||
| 2 | `InvalidProgramDataAccount` |
|
| 2 | `InvalidProgramDataAccount` |
|
||||||
@@ -114,7 +114,7 @@ Ces valeurs décrivent uniquement les données on-chain. `Url` ne déclenche auc
|
|||||||
## 6. Matrice de couverture Token-2022 réelle
|
## 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é |
|
| 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 |
|
| `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 |
|
| `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 |
|
| `RemoveKey` | oui | oui | oui | oui, via builder générique | état extension lisible | extension observée | non dédié | non dédié | non établi |
|
||||||
|
|||||||
189
docs/audits/V0_4_8_PRE_007_MATERIALIZER_CONVENTION_AUDIT.md
Normal file
189
docs/audits/V0_4_8_PRE_007_MATERIALIZER_CONVENTION_AUDIT.md
Normal 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 l’intégralité de `kb-lib/src/materializer` après l’activation du matérialisateur Solana Program Metadata et l’extraction 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 ;
|
||||||
|
- l’emplacement, 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 d’identités issue de son `constants.rs` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
runtime component : kb-lib.materializer.<domain>[.<subsystem>]
|
||||||
|
processorName : materializer.<domain>[.<subsystem>]
|
||||||
|
```
|
||||||
|
|
||||||
|
Le `processorName` est exactement l’identité runtime privée du préfixe `kb-lib.`. Le tracing target d’un 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 d’implé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 l’ancien `accepts_event` ;
|
||||||
|
- retourner une liste vide depuis l’ancien `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`.
|
||||||
|
|
||||||
|
L’ajout d’une provenance explicite aux sorties qui ne possédaient encore qu’une 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
|
||||||
|
|
||||||
|
L’audit 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.` ;
|
||||||
|
- l’absence de constantes hors `constants.rs` ;
|
||||||
|
- la présence d’un `constants.rs` à côté de chaque implémentation ;
|
||||||
|
- l’absence d’identités littérales dans les implémentations ;
|
||||||
|
- la présence conjointe de `projectionVersion` et `processorName` dans chaque payload versionné ;
|
||||||
|
- l’utilisation 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 d’une coquille réservée.
|
||||||
|
|
||||||
|
Les tests Rust conservent en plus l’inventaire 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 l’identité 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` n’est utilisé et aucun champ persistant n’est ajouté uniquement pour supprimer un warning.
|
||||||
|
|
||||||
|
L’audit mécanique refuse désormais :
|
||||||
|
|
||||||
|
- un `TRACING_TARGET_*` de `kb-lib` déclaré sans macro `tracing` réelle ;
|
||||||
|
- un `MT_*_COMPONENT_NAME` qui n’est consommé par aucun fichier d’implémentation du composant.
|
||||||
|
|
||||||
|
Les composants state-only restent distincts des matérialiseurs instructionnels : ils n’implémentent pas artificiellement `MtApiEventMaterializer`, mais leur identité runtime est désormais effectivement utilisée par leurs opérations de snapshot.
|
||||||
@@ -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 :
|
||||||
|
|
||||||
|
- l’absence initiale d’un sous-module dédié à Metaplex Token Metadata ;
|
||||||
|
- l’emplacement et la couverture de la matérialisation Token-2022 Token Metadata.
|
||||||
|
|
||||||
|
L’audit a également séparé l’identité runtime d’un composant de son `processorName` persisté.
|
||||||
|
|
||||||
|
## 2. Metaplex Token Metadata
|
||||||
|
|
||||||
|
Il n’existait pas d’oubli 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 d’autres 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 l’extrait 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 d’identité.
|
||||||
|
|
||||||
|
## 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 d’administration. En revanche, les cinq observations de `spl-token-metadata-interface` ne disposent pas encore d’une matérialisation d’instruction 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
|
||||||
|
|
||||||
|
L’identité 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 à l’intégralité des matérialisateurs ; voir `V0_4_8_PRE_007_MATERIALIZER_CONVENTION_AUDIT.md`. La seule lacune fonctionnelle confirmée concerne les cinq faits d’instructions `spl-token-metadata-interface`, explicitement reportés à `pre.010`.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/guides/LOGGING.md -->
|
<!-- file: docs/guides/LOGGING.md -->
|
||||||
<!-- version: 1 -->
|
<!-- version: 2 -->
|
||||||
|
|
||||||
# Guide de logging et tracing
|
# Guide de logging et tracing
|
||||||
|
|
||||||
@@ -50,8 +50,13 @@ kb-pipeline.backfill
|
|||||||
kb-pipeline.decode-replay
|
kb-pipeline.decode-replay
|
||||||
kb-onchain-transport.http
|
kb-onchain-transport.http
|
||||||
kb-lib.executor.spl.token-2022
|
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 l’identité 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.
|
Une nouvelle target doit être ajoutée selon `docs/OPERATION_NAMING_CONVENTION.md` et les règles Khadhroony.
|
||||||
|
|
||||||
## Frontend desktop
|
## Frontend desktop
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<!-- file: docs/plans/V0_4_8_SOLANA_PROGRAM_METADATA_AND_TOKEN_2022_COMPLETENESS_PLAN.md -->
|
<!-- 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
|
# Plan `0.4.8` — Solana Program Metadata et complétude Token-2022
|
||||||
|
|
||||||
## 1. Statut et rôle
|
## 1. Statut et rôle
|
||||||
|
|
||||||
Ce document est le livrable principal de `0.4.8-pre.001`, réconcilié après l’audit contractuel de `0.4.8-pre.002`, les modèles de `pre.003`, les décodeurs de comptes de `pre.004`, le décodeur d’instructions de `pre.005` et la matérialisation de `pre.006`. Il constitue le plan vivant de la version et reste modifiable lorsque l’audit du code, des interfaces officielles, de l’IDL ou des validations Devnet impose un ajustement.
|
Ce document est le livrable principal de `0.4.8-pre.001`, réconcilié après l’audit contractuel de `0.4.8-pre.002`, les modèles de `pre.003`, les décodeurs de comptes de `pre.004`, le décodeur d’instructions de `pre.005`, la matérialisation de `pre.006` et l’exécuteur de `pre.007`. Il constitue le plan vivant de la version et reste modifiable lorsque l’audit du code, des interfaces officielles, de l’IDL 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.
|
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.
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ Il doit être maintenu pendant chaque prerelease, puis archivé pendant la derni
|
|||||||
### 2.1 Surfaces strictement distinctes
|
### 2.1 Surfaces strictement distinctes
|
||||||
|
|
||||||
| Surface | Program ID ou frontière | Rôle dans `0.4.8` |
|
| 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 |
|
| 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 |
|
| 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 |
|
| `spl-token-metadata-interface` | interface sans Program ID autonome imposé | source contractuelle des instructions metadata implémentées par Token-2022 |
|
||||||
@@ -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 ;
|
- matrice de onze projections et test de façade externe ;
|
||||||
- aucune résolution de contenu distant ni lecture implicite d’un compte externe.
|
- aucune résolution de contenu distant ni lecture implicite d’un 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 l’environnement 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 l’audit workspace sont propres ; `cargo test -p kb-lib` réussit avec 667 tests unitaires, les tests d’API 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 ;
|
Résultat durable : les neuf instructions stables possèdent une opération typée, un builder wire exact et une capacité d’exécution. Les mutations dangereuses restent constructibles mais exigent une approbation explicite ; tous les plans traversent `ExSafetyChecker` avant d’être remis à l’orchestration.
|
||||||
- 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 ;
|
- helpers de PDA canonical et non-canonical avec seed exact de 16 octets ;
|
||||||
- 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 l’envoi ;
|
- sélection de canonicité explicite dans l’intent, indépendante de la seule présence de `ProgramData` ;
|
||||||
- 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 ;
|
- builders des neuf opérations, comptes positionnels, signataires et variantes inline/Buffer ;
|
||||||
- é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 l’exécuteur sauf découverte officielle contraire documentée avant code.
|
- simulation et quatre postconditions obligatoires ;
|
||||||
|
- approbation explicite pour `Write`, `SetAuthority`, `SetData`, `SetImmutable`, `Trim` et `Close` ;
|
||||||
|
- matrice d’exécution et test d’API externe ;
|
||||||
|
- aucune opération stable classée deprecated ou replaced faute de preuve officielle ;
|
||||||
|
- financement de rent volontairement laissé à la fixture et à l’orchestration 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 s’agissait donc pas d’un 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é. L’audit est ensuite étendu à l’ensemble 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
|
### `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
|
### `0.4.8-pre.010` — complétude Token-2022 Token Metadata
|
||||||
|
|
||||||
- implémentation des écarts confirmés de `Initialize`, `UpdateField`, `RemoveKey`, `UpdateAuthority` et `Emit` ;
|
- 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 d’une matérialisation explicite des cinq observations d’instructions `spl-token-metadata-interface`, actuellement absente ;
|
||||||
|
- maintien de `InitializeMetadataPointer` et `UpdateMetadataPointer` dans le matérialiseur d’administration qui en possède déjà les faits ;
|
||||||
- tests synthétiques et stateful.
|
- tests synthétiques et stateful.
|
||||||
|
|
||||||
### `0.4.8-pre.011` — campagne Devnet Token-2022 Token Metadata
|
### `0.4.8-pre.011` — campagne Devnet Token-2022 Token Metadata
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/rules/RULES_SPECIFIC_KHADHROONY.md -->
|
<!-- file: docs/rules/RULES_SPECIFIC_KHADHROONY.md -->
|
||||||
<!-- version: 8 -->
|
<!-- version: 10 -->
|
||||||
|
|
||||||
# Règles spécifiques à `khadhroony-bot3`
|
# 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.
|
- 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.
|
- 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.
|
- 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.
|
||||||
|
- L’identité runtime suit `kb-lib.materializer.<domain>[.<subsystem>]`. Le `processorName` persisté suit `materializer.<domain>[.<subsystem>]` et doit être exactement l’identité runtime sans le préfixe `kb-lib.`. Le tracing target d’un composant actif est identique à son identité runtime.
|
||||||
|
- Les constantes d’un 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 lorsqu’elles 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
|
### 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 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 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 l’installation du subscriber.
|
- 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 l’installation du subscriber.
|
||||||
- Il est interdit d’ajouter `tracing` sans événement réel ou de conserver un faux target uniquement consommé par `let _target`.
|
- Il est interdit d’ajouter `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 d’orchestration.
|
- 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 d’orchestration.
|
||||||
- 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.
|
- 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 n’est 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`.
|
- Une transaction Solana échouée mais correctement décodée n’est 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`.
|
- 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`.
|
||||||
- L’ajout 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`.
|
- L’ajout 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`.
|
- Le contrat détaillé est défini dans `docs/TRACING_CONTRACT.md`.
|
||||||
- L’audit 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 l’usage obligatoire de `solana_pubkey::Pubkey` à la place de `solana_address::Address`.
|
- L’audit 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 l’usage obligatoire de `solana_pubkey::Pubkey` à la place de `solana_address::Address`.
|
||||||
|
|
||||||
## Règles de réutilisation des interfaces Solana et SPL
|
## Règles de réutilisation des interfaces Solana et SPL
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,33 @@
|
|||||||
<!-- file: kb-lib/CHANGELOG.md -->
|
<!-- file: kb-lib/CHANGELOG.md -->
|
||||||
<!-- version: 23 -->
|
<!-- version: 28 -->
|
||||||
|
|
||||||
# CHANGELOG — kb-lib
|
# 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 l’infé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 qu’une 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 d’exécution, le test de parité des capacités et le test d’API externe depuis la façade de crate ;
|
||||||
|
- retire l’exécuteur Solana Program Metadata de l’inventaire réservé ;
|
||||||
|
- corrige le total attendu du test d’inventaire 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 qu’aucune annotation `#[deprecated]` n’est 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 d’administration et que les cinq faits d’instructions `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 d’exemple sur `compliance.audit`, `transaction.annotations` et `token.accounts`, et ajoute des audits mécaniques empêchant la réintroduction de noms, constantes ou façades non conformes ;
|
||||||
|
- conserve `MtMetadataMaterializer` comme wrapper de compatibilité du composant canonique `MtSolanaProgramMetadataMaterializer`, sans second `processorName` ni second propriétaire de faits.
|
||||||
|
- corrige les warnings de contrats opérationnels en ajoutant des événements `tracing` réels aux matérialisations `fees`, Metaplex Token Metadata et metadata Token-2022, avec `component_name`, `processor_name`, statut, identité de compte ou d’instruction et nombre de sorties ;
|
||||||
|
- consomme désormais l’identité runtime `MT_METADATA_TOKEN_2022_COMPONENT_NAME` dans l’implémentation state-only au lieu de la limiter aux réexports, et étend les audits pour refuser tout tracing target opérationnel déclaré mais inutilisé ainsi que toute identité de composant absente de son implémentation.
|
||||||
|
|
||||||
## 0.4.8-pre.006 — matérialisation Solana Program Metadata
|
## 0.4.8-pre.006 — matérialisation Solana Program Metadata
|
||||||
|
|
||||||
- active `MtMetadataMaterializer` comme matérialiseur contextualisé de la surface exacte `metadata.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 d’API externe correspondant.
|
- ajoute les helpers publics de matérialisation d’états et le test d’API externe correspondant.
|
||||||
- corrige le helper de test de snapshot afin d’utiliser les variantes wire existantes `Encoding::Utf8` et `Format::Json`.
|
- corrige le helper de test de snapshot afin d’utiliser 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 l’exécuteur.
|
- 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 l’exé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 d’API externes et les doc-tests.
|
||||||
|
|
||||||
## 0.4.8-pre.005 — instructions Solana Program Metadata
|
## 0.4.8-pre.005 — instructions Solana Program Metadata
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: kb-lib/README.md -->
|
<!-- file: kb-lib/README.md -->
|
||||||
<!-- version: 23 -->
|
<!-- version: 25 -->
|
||||||
|
|
||||||
# kb-lib
|
# 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 ;
|
- 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 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` ;
|
- 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ù l’ancien nom pré-stable `WithdrawExcessLamports` du tag `5` reste une provenance de `Trim`, sans dixième entrée de couverture.
|
- une frontière historique où l’ancien 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 ;
|
- deux projections d’état autoritatives pour `Buffer` et `Metadata`, avec conservation bornée du contenu on-chain ;
|
||||||
- neuf faits de mutation d’instructions committées, non autoritatifs vis-à-vis de l’état final et réconciliables avec les snapshots post-exécution ;
|
- neuf faits de mutation d’instructions 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 :
|
Pour Metaplex Token Metadata, elle fournit :
|
||||||
|
|
||||||
@@ -32,7 +34,21 @@ Pour Metaplex Token Metadata, elle fournit :
|
|||||||
- 20 opérations courantes exécutables ;
|
- 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 ;
|
- 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 ;
|
- 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
|
||||||
|
|
||||||
|
L’arbre `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 l’exige ;
|
||||||
|
- une identité runtime `kb-lib.materializer.*` distincte du `processorName` persisté `materializer.*` ;
|
||||||
|
- des coquilles réservées réellement inactives sur les deux contrats legacy et contextuel.
|
||||||
|
|
||||||
|
L’inventaire audité comprend 25 composants nommés : neuf matérialisateurs actifs, quatorze coquilles réservées et deux composants metadata state-only. Le détail est consigné dans [l’audit de convention des matérialisateurs](../docs/audits/V0_4_8_PRE_007_MATERIALIZER_CONVENTION_AUDIT.md).
|
||||||
|
|
||||||
## Responsabilités
|
## Responsabilités
|
||||||
|
|
||||||
@@ -57,6 +73,7 @@ Pour Metaplex Token Metadata, elle fournit :
|
|||||||
- contrats `ExApi*`, politiques de sécurité et exécuteurs `Ex*Executor` ;
|
- contrats `ExApi*`, politiques de sécurité et exécuteurs `Ex*Executor` ;
|
||||||
- `ExMetadataMetaplexTokenMetadataExecutor`, `ExMetaplexTokenMetadataExecutionIntent` et `ExMetaplexTokenMetadataOperation` ;
|
- `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` ;
|
- `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.
|
- modèles canoniques `Md*` réexportés par la façade.
|
||||||
|
|
||||||
Les exemples d’appel et invariants sont documentés dans [USAGE.md](USAGE.md).
|
Les exemples d’appel et invariants sont documentés dans [USAGE.md](USAGE.md).
|
||||||
@@ -71,7 +88,7 @@ Les exemples d’appel 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 d’un manque de builder dans `kb-lib`.
|
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 d’un 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 d’autorité ; `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 jusqu’aux lectures stateful prévues, et aucune forme pré-stable n’est 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 l’exé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 d’autorité ; `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 jusqu’aux lectures stateful prévues, et aucune forme pré-stable n’est déclarée compatible réseau sans preuve de cluster.
|
||||||
|
|
||||||
Le registre ElGamal n’est pas déclaré validé réellement sur réseau.
|
Le registre ElGamal n’est pas déclaré validé réellement sur réseau.
|
||||||
|
|
||||||
@@ -84,4 +101,5 @@ Le registre ElGamal n’est 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 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 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 de matérialisation Solana Program Metadata](../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_MATERIALIZATION_MATRIX.json)
|
||||||
|
- [Matrice d’exécution Solana Program Metadata](../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_EXECUTOR_MATRIX.json)
|
||||||
- [Audit de l’inventaire et de l’historique des instructions](../docs/audits/V0_4_8_PRE_005_SOLANA_PROGRAM_METADATA_INSTRUCTION_HISTORY_AUDIT.md)
|
- [Audit de l’inventaire et de l’historique des instructions](../docs/audits/V0_4_8_PRE_005_SOLANA_PROGRAM_METADATA_INSTRUCTION_HISTORY_AUDIT.md)
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
<!-- file: kb-lib/TODO.md -->
|
<!-- file: kb-lib/TODO.md -->
|
||||||
<!-- version: 13 -->
|
<!-- version: 14 -->
|
||||||
|
|
||||||
# TODO — kb-lib
|
# 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.
|
- [ ] Inventaire - recenser chaque surface courante, historique, remplacée et officiellement obsolète de tous les décodeurs déjà livrés.
|
||||||
- [ ] 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.
|
- [ ] Décodage - vérifier que chaque décodeur couvre exhaustivement son contrat officiel, y compris les formes historiques qui restent observables.
|
||||||
- [ ] SPL Memo - réexaminer Memo v1 et Memo v3 et documenter leur statut exact de remplacement ou d’obsolescence constructible.
|
- [ ] 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.
|
||||||
- [ ] SPL Token, ATA, Token-2022 et registre ElGamal - appliquer la même classification opération par opération.
|
- [ ] Exécution - vérifier que chaque opération courante, destructive, irréversible ou écrasante officiellement constructible possède un builder et une capacité d’exécution.
|
||||||
- [ ] Matrices - aligner statuts, annotations `#[deprecated]`, approbations opérateur et tests de parité des surfaces réauditées.
|
- [ ] 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 qu’elle 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
|
## Report conditionnel — registre SPL ElGamal
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: kb-lib/USAGE.md -->
|
<!-- file: kb-lib/USAGE.md -->
|
||||||
<!-- version: 12 -->
|
<!-- version: 13 -->
|
||||||
|
|
||||||
# Utilisation de kb-lib
|
# Utilisation de kb-lib
|
||||||
|
|
||||||
@@ -226,7 +226,8 @@ Les signatures exactes doivent être vérifiées lors de l’utilisation : certa
|
|||||||
- `ExSplTokenExecutor` ;
|
- `ExSplTokenExecutor` ;
|
||||||
- `ExSplAssociatedTokenAccountExecutor` ;
|
- `ExSplAssociatedTokenAccountExecutor` ;
|
||||||
- `ExSplToken2022Executor` ;
|
- `ExSplToken2022Executor` ;
|
||||||
- `ExSplElgamalRegistryExecutor`.
|
- `ExSplElgamalRegistryExecutor` ;
|
||||||
|
- `ExMetadataSolanaProgramMetadataExecutor`.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use kb_lib::{ExApiTypedInstructionExecutor, ExSplMemoExecutor};
|
use kb_lib::{ExApiTypedInstructionExecutor, ExSplMemoExecutor};
|
||||||
@@ -235,7 +236,6 @@ let executor = ExSplMemoExecutor;
|
|||||||
let plan = ExApiTypedInstructionExecutor::build_prepared_plan(
|
let plan = ExApiTypedInstructionExecutor::build_prepared_plan(
|
||||||
&executor,
|
&executor,
|
||||||
&intent,
|
&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.
|
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 ; l’appelant 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 l’API. 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` n’inventent pas le financement préalable nécessaire à l’exemption de rent. La préparation de fixture ou l’orchestration 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
|
## Exécution Metaplex Token Metadata
|
||||||
|
|
||||||
La surface publique repose sur :
|
La surface publique repose sur :
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/decoder/metadata/metaplex_token_metadata/decoder.rs
|
// file: kb-lib/src/decoder/metadata/metaplex_token_metadata/decoder.rs
|
||||||
// version: 31
|
// version: 32
|
||||||
|
|
||||||
//! Exact contextual dispatch for Metaplex Token Metadata.
|
//! Exact contextual dispatch for Metaplex Token Metadata.
|
||||||
|
|
||||||
@@ -144,9 +144,6 @@ impl crate::DcApiInstructionDecoder for crate::DcMetadataMetaplexTokenMetadataDe
|
|||||||
mod tests {
|
mod tests {
|
||||||
use base64::Engine; // rust-rules: trait-import
|
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 MAX_SDK_INSTRUCTION_MODULES: usize = 128;
|
||||||
const MIN_SDK_INSTRUCTION_MODULES: usize = 90;
|
const MIN_SDK_INSTRUCTION_MODULES: usize = 90;
|
||||||
const MIN_PDA_FAMILIES: usize = 8;
|
const MIN_PDA_FAMILIES: usize = 8;
|
||||||
@@ -2023,7 +2020,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn audit_matrix_remains_machine_readable_and_bounded() {
|
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());
|
assert!(parsed.is_ok());
|
||||||
let matrix = match parsed {
|
let matrix = match parsed {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
@@ -3053,7 +3052,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pre_002_contract_closes_all_instruction_statuses_and_idl_accounts() {
|
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::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => panic!("matrix parse failed: {error}"),
|
std::result::Result::Err(error) => panic!("matrix parse failed: {error}"),
|
||||||
};
|
};
|
||||||
@@ -3093,7 +3094,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pre_002_fact_ownership_is_unique_and_keeps_metadata_domains_separate() {
|
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::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => panic!("matrix parse failed: {error}"),
|
std::result::Result::Err(error) => panic!("matrix parse failed: {error}"),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/decoder/metadata/solana_program_metadata/account.rs
|
// file: kb-lib/src/decoder/metadata/solana_program_metadata/account.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
//! Bounded account decoding for the Solana Program Metadata program.
|
//! Bounded account decoding for the Solana Program Metadata program.
|
||||||
|
|
||||||
@@ -61,66 +61,61 @@ pub enum DcMetadataSpmAccountDecodeError {
|
|||||||
|
|
||||||
impl std::fmt::Display for DcMetadataSpmAccountDecodeError {
|
impl std::fmt::Display for DcMetadataSpmAccountDecodeError {
|
||||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
return match self {
|
||||||
Self::InvalidAccountKey => {
|
Self::InvalidAccountKey => {
|
||||||
return formatter.write_str("program metadata account key is invalid");
|
formatter.write_str("program metadata account key is invalid")
|
||||||
},
|
|
||||||
Self::InvalidOwner => {
|
|
||||||
return formatter.write_str("program metadata account owner is invalid");
|
|
||||||
},
|
},
|
||||||
|
Self::InvalidOwner => formatter.write_str("program metadata account owner is invalid"),
|
||||||
Self::ForeignOwner => {
|
Self::ForeignOwner => {
|
||||||
return formatter.write_str("program metadata account is owned by another program");
|
formatter.write_str("program metadata account is owned by another program")
|
||||||
},
|
|
||||||
Self::EmptyAccountData => {
|
|
||||||
return formatter.write_str("program metadata account data is empty");
|
|
||||||
},
|
},
|
||||||
|
Self::EmptyAccountData => formatter.write_str("program metadata account data is empty"),
|
||||||
Self::UninitializedAccount => {
|
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 } => {
|
Self::AccountDataTooLarge { actual_bytes, max_bytes } => {
|
||||||
return write!(
|
write!(
|
||||||
formatter,
|
formatter,
|
||||||
"program metadata account data length {actual_bytes} exceeds {max_bytes} bytes",
|
"program metadata account data length {actual_bytes} exceeds {max_bytes} bytes",
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
Self::TruncatedHeader { actual_bytes, minimum_bytes } => {
|
Self::TruncatedHeader { actual_bytes, minimum_bytes } => {
|
||||||
return write!(
|
write!(
|
||||||
formatter,
|
formatter,
|
||||||
"program metadata account header length {actual_bytes} is below {minimum_bytes} bytes",
|
"program metadata account header length {actual_bytes} is below {minimum_bytes} bytes",
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
Self::UnexpectedAccountDiscriminator { expected, actual } => {
|
Self::UnexpectedAccountDiscriminator { expected, actual } => {
|
||||||
return write!(
|
write!(
|
||||||
formatter,
|
formatter,
|
||||||
"program metadata account discriminator {actual:?} does not match {expected:?}",
|
"program metadata account discriminator {actual:?} does not match {expected:?}",
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
Self::LogicalDataOutOfBounds { declared_bytes, available_bytes } => {
|
Self::LogicalDataOutOfBounds { declared_bytes, available_bytes } => {
|
||||||
return write!(
|
write!(
|
||||||
formatter,
|
formatter,
|
||||||
"program metadata logical data length {declared_bytes} exceeds {available_bytes} allocated bytes",
|
"program metadata logical data length {declared_bytes} exceeds {available_bytes} allocated bytes",
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
Self::LogicalDataLengthUnsupported { declared_bytes } => {
|
Self::LogicalDataLengthUnsupported { declared_bytes } => {
|
||||||
return write!(
|
write!(
|
||||||
formatter,
|
formatter,
|
||||||
"program metadata logical data length {declared_bytes} cannot be represented on this architecture",
|
"program metadata logical data length {declared_bytes} cannot be represented on this architecture",
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
Self::MissingNonCanonicalAuthority => {
|
Self::MissingNonCanonicalAuthority => {
|
||||||
return formatter
|
formatter.write_str("non-canonical program metadata account has no authority")
|
||||||
.write_str("non-canonical program metadata account has no authority");
|
|
||||||
},
|
},
|
||||||
Self::InvalidMetadataPda => {
|
Self::InvalidMetadataPda => {
|
||||||
return formatter.write_str("program metadata account PDA is invalid");
|
formatter.write_str("program metadata account PDA is invalid")
|
||||||
},
|
},
|
||||||
Self::InvalidUrlUtf8 => {
|
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) => {
|
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));
|
return std::result::Result::Err(crate::DcMetadataSpmAccountDecodeError::Model(error));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
match discriminator {
|
return match discriminator {
|
||||||
crate::DcMetadataSpmAccountDiscriminator::Buffer => {
|
crate::DcMetadataSpmAccountDiscriminator::Buffer => {
|
||||||
let snapshot =
|
let snapshot =
|
||||||
match crate::decoder_metadata_solana_program_metadata_decode_buffer_account(
|
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::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
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 => {
|
crate::DcMetadataSpmAccountDiscriminator::Metadata => {
|
||||||
let snapshot =
|
let snapshot =
|
||||||
@@ -226,16 +221,12 @@ pub fn decoder_metadata_solana_program_metadata_decode_account(
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
return std::result::Result::Ok(crate::DcMetadataSpmAccountSnapshot::Metadata(
|
std::result::Result::Ok(crate::DcMetadataSpmAccountSnapshot::Metadata(snapshot))
|
||||||
snapshot,
|
|
||||||
));
|
|
||||||
},
|
},
|
||||||
crate::DcMetadataSpmAccountDiscriminator::Empty => {
|
crate::DcMetadataSpmAccountDiscriminator::Empty => {
|
||||||
return std::result::Result::Err(
|
std::result::Result::Err(crate::DcMetadataSpmAccountDecodeError::UninitializedAccount)
|
||||||
crate::DcMetadataSpmAccountDecodeError::UninitializedAccount,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decodes one Solana Program Metadata Buffer account without claiming a stable PDA after authority changes.
|
/// 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)
|
crate::DcMetadataSpmData::External(external)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
match model.validate() {
|
return match model.validate() {
|
||||||
std::result::Result::Ok(()) => return std::result::Result::Ok(model),
|
std::result::Result::Ok(()) => std::result::Result::Ok(model),
|
||||||
std::result::Result::Err(error) => {
|
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(
|
fn decoder_metadata_solana_program_metadata_validate_metadata_pda(
|
||||||
@@ -576,12 +567,12 @@ fn decoder_metadata_solana_program_metadata_validate_metadata_pda(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
fn metadata_program_id() -> std::option::Option<solana_pubkey::Pubkey> {
|
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,
|
kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID,
|
||||||
) {
|
) {
|
||||||
std::result::Result::Ok(value) => return std::option::Option::Some(value),
|
std::result::Result::Ok(value) => std::option::Option::Some(value),
|
||||||
std::result::Result::Err(_) => return std::option::Option::None,
|
std::result::Result::Err(_) => std::option::Option::None,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/decoder/metadata/solana_program_metadata/instruction.rs
|
// file: kb-lib/src/decoder/metadata/solana_program_metadata/instruction.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! Exact contextual instruction decoding for Solana Program Metadata.
|
//! Exact contextual instruction decoding for Solana Program Metadata.
|
||||||
|
|
||||||
@@ -890,10 +890,6 @@ fn failed(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use base64::Engine; // rust-rules: trait-import
|
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(
|
fn input(
|
||||||
payload: &[u8],
|
payload: &[u8],
|
||||||
accounts: &[(&str, bool, bool)],
|
accounts: &[(&str, bool, bool)],
|
||||||
@@ -1262,7 +1258,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn instruction_matrix_is_machine_readable_and_matches_compiled_inventory() {
|
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::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => panic!("instruction matrix must parse: {error}"),
|
std::result::Result::Err(error) => panic!("instruction matrix must parse: {error}"),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/decoder/metadata/solana_program_metadata/model.rs
|
// 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.
|
//! Public bounded models for the Solana Program Metadata wire contract.
|
||||||
|
|
||||||
@@ -24,16 +24,14 @@ impl DcMetadataSpmAccountDiscriminator {
|
|||||||
|
|
||||||
/// Resolves an exact wire discriminant.
|
/// Resolves an exact wire discriminant.
|
||||||
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
||||||
match value {
|
return match value {
|
||||||
0 => return std::result::Result::Ok(Self::Empty),
|
0 => std::result::Result::Ok(Self::Empty),
|
||||||
1 => return std::result::Result::Ok(Self::Buffer),
|
1 => std::result::Result::Ok(Self::Buffer),
|
||||||
2 => return std::result::Result::Ok(Self::Metadata),
|
2 => std::result::Result::Ok(Self::Metadata),
|
||||||
_ => {
|
_ => std::result::Result::Err(
|
||||||
return std::result::Result::Err(
|
|
||||||
crate::DcMetadataSpmModelError::UnknownAccountDiscriminator { value },
|
crate::DcMetadataSpmModelError::UnknownAccountDiscriminator { value },
|
||||||
);
|
),
|
||||||
},
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,17 +58,15 @@ impl DcMetadataSpmEncoding {
|
|||||||
|
|
||||||
/// Resolves an exact wire discriminant.
|
/// Resolves an exact wire discriminant.
|
||||||
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
||||||
match value {
|
return match value {
|
||||||
0 => return std::result::Result::Ok(Self::None),
|
0 => std::result::Result::Ok(Self::None),
|
||||||
1 => return std::result::Result::Ok(Self::Utf8),
|
1 => std::result::Result::Ok(Self::Utf8),
|
||||||
2 => return std::result::Result::Ok(Self::Base58),
|
2 => std::result::Result::Ok(Self::Base58),
|
||||||
3 => return std::result::Result::Ok(Self::Base64),
|
3 => std::result::Result::Ok(Self::Base64),
|
||||||
_ => {
|
_ => {
|
||||||
return std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownEncoding {
|
std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownEncoding { value })
|
||||||
value,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,16 +91,14 @@ impl DcMetadataSpmCompression {
|
|||||||
|
|
||||||
/// Resolves an exact wire discriminant.
|
/// Resolves an exact wire discriminant.
|
||||||
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
||||||
match value {
|
return match value {
|
||||||
0 => return std::result::Result::Ok(Self::None),
|
0 => std::result::Result::Ok(Self::None),
|
||||||
1 => return std::result::Result::Ok(Self::Gzip),
|
1 => std::result::Result::Ok(Self::Gzip),
|
||||||
2 => return std::result::Result::Ok(Self::Zlib),
|
2 => std::result::Result::Ok(Self::Zlib),
|
||||||
_ => {
|
_ => std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownCompression {
|
||||||
return std::result::Result::Err(
|
value,
|
||||||
crate::DcMetadataSpmModelError::UnknownCompression { value },
|
}),
|
||||||
);
|
};
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,17 +125,13 @@ impl DcMetadataSpmFormat {
|
|||||||
|
|
||||||
/// Resolves an exact wire discriminant.
|
/// Resolves an exact wire discriminant.
|
||||||
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
||||||
match value {
|
return match value {
|
||||||
0 => return std::result::Result::Ok(Self::None),
|
0 => std::result::Result::Ok(Self::None),
|
||||||
1 => return std::result::Result::Ok(Self::Json),
|
1 => std::result::Result::Ok(Self::Json),
|
||||||
2 => return std::result::Result::Ok(Self::Yaml),
|
2 => std::result::Result::Ok(Self::Yaml),
|
||||||
3 => return std::result::Result::Ok(Self::Toml),
|
3 => std::result::Result::Ok(Self::Toml),
|
||||||
_ => {
|
_ => std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownFormat { value }),
|
||||||
return std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownFormat {
|
};
|
||||||
value,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,16 +156,14 @@ impl DcMetadataSpmDataSource {
|
|||||||
|
|
||||||
/// Resolves an exact wire discriminant.
|
/// Resolves an exact wire discriminant.
|
||||||
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
||||||
match value {
|
return match value {
|
||||||
0 => return std::result::Result::Ok(Self::Direct),
|
0 => std::result::Result::Ok(Self::Direct),
|
||||||
1 => return std::result::Result::Ok(Self::Url),
|
1 => std::result::Result::Ok(Self::Url),
|
||||||
2 => return std::result::Result::Ok(Self::External),
|
2 => std::result::Result::Ok(Self::External),
|
||||||
_ => {
|
_ => std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownDataSource {
|
||||||
return std::result::Result::Err(
|
value,
|
||||||
crate::DcMetadataSpmModelError::UnknownDataSource { value },
|
}),
|
||||||
);
|
};
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,18 +192,16 @@ impl DcMetadataSpmProgramError {
|
|||||||
|
|
||||||
/// Resolves an exact custom program error code.
|
/// Resolves an exact custom program error code.
|
||||||
pub fn from_code(value: u32) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
pub fn from_code(value: u32) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
||||||
match value {
|
return match value {
|
||||||
0 => return std::result::Result::Ok(Self::NotExecutableAccount),
|
0 => std::result::Result::Ok(Self::NotExecutableAccount),
|
||||||
1 => return std::result::Result::Ok(Self::InvalidProgramState),
|
1 => std::result::Result::Ok(Self::InvalidProgramState),
|
||||||
2 => return std::result::Result::Ok(Self::InvalidProgramDataAccount),
|
2 => std::result::Result::Ok(Self::InvalidProgramDataAccount),
|
||||||
3 => return std::result::Result::Ok(Self::ImmutableMetadataAccount),
|
3 => std::result::Result::Ok(Self::ImmutableMetadataAccount),
|
||||||
4 => return std::result::Result::Ok(Self::InvalidDataLength),
|
4 => std::result::Result::Ok(Self::InvalidDataLength),
|
||||||
_ => {
|
_ => std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownProgramError {
|
||||||
return std::result::Result::Err(
|
value,
|
||||||
crate::DcMetadataSpmModelError::UnknownProgramError { value },
|
}),
|
||||||
);
|
};
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,12 +258,12 @@ impl DcMetadataSpmSeed {
|
|||||||
while length > 0 && self.0[length - 1] == 0 {
|
while length > 0 && self.0[length - 1] == 0 {
|
||||||
length -= 1;
|
length -= 1;
|
||||||
}
|
}
|
||||||
match std::str::from_utf8(&self.0[..length]) {
|
return match std::str::from_utf8(&self.0[..length]) {
|
||||||
std::result::Result::Ok(value) => return std::result::Result::Ok(value),
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||||
std::result::Result::Err(_) => {
|
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 {
|
impl DcMetadataSpmData {
|
||||||
/// Returns the source discriminator associated with this content.
|
/// Returns the source discriminator associated with this content.
|
||||||
pub const fn source(&self) -> crate::DcMetadataSpmDataSource {
|
pub const fn source(&self) -> crate::DcMetadataSpmDataSource {
|
||||||
match self {
|
return match self {
|
||||||
Self::Direct(_) => return crate::DcMetadataSpmDataSource::Direct,
|
Self::Direct(_) => crate::DcMetadataSpmDataSource::Direct,
|
||||||
Self::Url(_) => return crate::DcMetadataSpmDataSource::Url,
|
Self::Url(_) => crate::DcMetadataSpmDataSource::Url,
|
||||||
Self::External(_) => return crate::DcMetadataSpmDataSource::External,
|
Self::External(_) => crate::DcMetadataSpmDataSource::External,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validates source-specific length requirements and the `u32` header bound.
|
/// 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.
|
/// Returns the exact number of bytes represented on wire.
|
||||||
pub fn wire_byte_len(&self) -> usize {
|
pub fn wire_byte_len(&self) -> usize {
|
||||||
match self {
|
return match self {
|
||||||
Self::Direct(value) => return value.len(),
|
Self::Direct(value) => value.len(),
|
||||||
Self::Url(value) => return value.len(),
|
Self::Url(value) => value.len(),
|
||||||
Self::External(_) => return crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES,
|
Self::External(_) => crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the exact little-endian header length after validation.
|
/// 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),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
}
|
}
|
||||||
let actual_bytes = self.wire_byte_len();
|
let actual_bytes = self.wire_byte_len();
|
||||||
match u32::try_from(actual_bytes) {
|
return match u32::try_from(actual_bytes) {
|
||||||
std::result::Result::Ok(value) => return std::result::Result::Ok(value),
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||||
std::result::Result::Err(_) => {
|
std::result::Result::Err(_) => {
|
||||||
return std::result::Result::Err(
|
std::result::Result::Err(crate::DcMetadataSpmModelError::DataLengthExceedsU32 {
|
||||||
crate::DcMetadataSpmModelError::DataLengthExceedsU32 { actual_bytes },
|
actual_bytes,
|
||||||
);
|
})
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serializes the exact content bytes without fetching URL or external-account data.
|
/// 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::Ok(()) => {},
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
}
|
}
|
||||||
match self {
|
return match self {
|
||||||
Self::Direct(value) => return std::result::Result::Ok(value.clone()),
|
Self::Direct(value) => std::result::Result::Ok(value.clone()),
|
||||||
Self::Url(value) => {
|
Self::Url(value) => std::result::Result::Ok(value.as_bytes().to_vec()),
|
||||||
return std::result::Result::Ok(value.as_bytes().to_vec());
|
Self::External(value) => std::result::Result::Ok(value.to_wire_bytes().to_vec()),
|
||||||
},
|
};
|
||||||
Self::External(value) => {
|
|
||||||
return std::result::Result::Ok(value.to_wire_bytes().to_vec());
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,56 +485,50 @@ pub enum DcMetadataSpmModelError {
|
|||||||
|
|
||||||
impl std::fmt::Display for DcMetadataSpmModelError {
|
impl std::fmt::Display for DcMetadataSpmModelError {
|
||||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
return match self {
|
||||||
Self::UnknownAccountDiscriminator { value } => {
|
Self::UnknownAccountDiscriminator { value } => {
|
||||||
return write!(formatter, "unknown account discriminator {value}");
|
write!(formatter, "unknown account discriminator {value}")
|
||||||
},
|
},
|
||||||
Self::UnknownEncoding { value } => {
|
Self::UnknownEncoding { value } => {
|
||||||
return write!(formatter, "unknown encoding discriminator {value}");
|
write!(formatter, "unknown encoding discriminator {value}")
|
||||||
},
|
},
|
||||||
Self::UnknownCompression { value } => {
|
Self::UnknownCompression { value } => {
|
||||||
return write!(formatter, "unknown compression discriminator {value}");
|
write!(formatter, "unknown compression discriminator {value}")
|
||||||
},
|
},
|
||||||
Self::UnknownFormat { value } => {
|
Self::UnknownFormat { value } => {
|
||||||
return write!(formatter, "unknown format discriminator {value}");
|
write!(formatter, "unknown format discriminator {value}")
|
||||||
},
|
},
|
||||||
Self::UnknownDataSource { value } => {
|
Self::UnknownDataSource { value } => {
|
||||||
return write!(formatter, "unknown data-source discriminator {value}");
|
write!(formatter, "unknown data-source discriminator {value}")
|
||||||
},
|
},
|
||||||
Self::UnknownProgramError { 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 } => {
|
Self::SeedUtf8TooLong { actual_bytes, max_bytes } => {
|
||||||
return write!(
|
write!(formatter, "seed UTF-8 length {actual_bytes} exceeds {max_bytes} bytes",)
|
||||||
formatter,
|
|
||||||
"seed UTF-8 length {actual_bytes} exceeds {max_bytes} bytes",
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
Self::InvalidSeedLength { actual_bytes, expected_bytes } => {
|
Self::InvalidSeedLength { actual_bytes, expected_bytes } => {
|
||||||
return write!(
|
write!(
|
||||||
formatter,
|
formatter,
|
||||||
"seed length {actual_bytes} does not equal {expected_bytes} bytes",
|
"seed length {actual_bytes} does not equal {expected_bytes} bytes",
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
Self::InvalidSeedUtf8 => {
|
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 } => {
|
Self::InvalidExternalDataLength { actual_bytes, expected_bytes } => {
|
||||||
return write!(
|
write!(
|
||||||
formatter,
|
formatter,
|
||||||
"external data length {actual_bytes} does not equal {expected_bytes} bytes",
|
"external data length {actual_bytes} does not equal {expected_bytes} bytes",
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
Self::EmptyData { source } => {
|
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 } => {
|
Self::DataLengthExceedsU32 { actual_bytes } => {
|
||||||
return write!(
|
write!(formatter, "metadata data length {actual_bytes} exceeds the u32 wire bound",)
|
||||||
formatter,
|
|
||||||
"metadata data length {actual_bytes} exceeds the u32 wire bound",
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/executor.rs
|
// file: kb-lib/src/executor.rs
|
||||||
// version: 14
|
// version: 16
|
||||||
|
|
||||||
//! Consolidated executor modules.
|
//! Consolidated executor modules.
|
||||||
|
|
||||||
@@ -208,6 +208,30 @@ pub use self::lending::ExLendingKaminoExecutor;
|
|||||||
pub use self::lending::ExLendingMarginfiV2Executor;
|
pub use self::lending::ExLendingMarginfiV2Executor;
|
||||||
/// Exposes the reserved `lock/raydium_lp` executor.
|
/// Exposes the reserved `lock/raydium_lp` executor.
|
||||||
pub use self::lock::ExLockRaydiumLpExecutor;
|
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`.
|
/// Exposes `EX_METAPLEX_TOKEN_METADATA_APPROVE_USE_AUTHORITY_OPERATION`.
|
||||||
pub use self::metadata::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`.
|
/// 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;
|
pub use self::metadata::EX_METAPLEX_TOKEN_METADATA_VERIFY_OPERATION;
|
||||||
/// Exposes the Metaplex Token Metadata executor.
|
/// Exposes the Metaplex Token Metadata executor.
|
||||||
pub use self::metadata::ExMetadataMetaplexTokenMetadataExecutor;
|
pub use self::metadata::ExMetadataMetaplexTokenMetadataExecutor;
|
||||||
/// Reserved `kb-lib.executor.metadata.solana_program_metadata` executor.
|
/// Typed Solana Program Metadata executor.
|
||||||
pub use self::metadata::ExMetadataSolanaProgramMetadataExecutor;
|
pub use self::metadata::ExMetadataSolanaProgramMetadataExecutor;
|
||||||
/// Exposes the reserved `metadata/spl_name_service` executor.
|
/// Exposes the reserved `metadata/spl_name_service` executor.
|
||||||
pub use self::metadata::ExMetadataSplNameServiceExecutor;
|
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.
|
/// Exposes the complete Metaplex Token Metadata execution intent.
|
||||||
pub use self::metadata::ExMetaplexTokenMetadataExecutionIntent;
|
pub use self::metadata::ExMetaplexTokenMetadataExecutionIntent;
|
||||||
/// Exposes one current Metaplex Token Metadata operation.
|
/// Exposes one current Metaplex Token Metadata operation.
|
||||||
pub use self::metadata::ExMetaplexTokenMetadataOperation;
|
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.
|
/// Exposes the reserved `nft/metaplex_bubblegum` executor.
|
||||||
pub use self::nft::ExNftMetaplexBubblegumExecutor;
|
pub use self::nft::ExNftMetaplexBubblegumExecutor;
|
||||||
/// Reserved `kb-lib.executor.nft.tensor_cnft` executor.
|
/// Reserved `kb-lib.executor.nft.tensor_cnft` executor.
|
||||||
@@ -993,6 +1041,10 @@ pub use self::wallet::ExWalletJupiterApeproSmartWalletExecutor;
|
|||||||
/// Exposes the reserved `weighted/swap_stabble` executor.
|
/// Exposes the reserved `weighted/swap_stabble` executor.
|
||||||
pub use self::weighted::ExWeightedSwapStabbleExecutor;
|
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.
|
/// Internal Metaplex Token Metadata prepared-plan builder.
|
||||||
pub(crate) use self::metadata::executor_metaplex_token_metadata_build_prepared_plan;
|
pub(crate) use self::metadata::executor_metaplex_token_metadata_build_prepared_plan;
|
||||||
/// Stable operation code for the nonce advance injected by durable transaction assembly.
|
/// 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 {
|
mod reserved_executor_tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn every_reserved_executor_is_registered_and_builds_only_an_empty_plan() {
|
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::ExAdapterSaberDecimalWrapperExecutor,
|
||||||
&crate::ExAdapterSplTokenWrapExecutor,
|
&crate::ExAdapterSplTokenWrapExecutor,
|
||||||
&crate::ExGovernanceSquadsMultisigExecutor,
|
&crate::ExGovernanceSquadsMultisigExecutor,
|
||||||
&crate::ExMetadataSolanaProgramMetadataExecutor,
|
|
||||||
&crate::ExNftTensorCnftExecutor,
|
&crate::ExNftTensorCnftExecutor,
|
||||||
&crate::ExPerpetualsPhoenixEternalExecutor,
|
&crate::ExPerpetualsPhoenixEternalExecutor,
|
||||||
&crate::ExStakingJitoTipDistributionExecutor,
|
&crate::ExStakingJitoTipDistributionExecutor,
|
||||||
@@ -1184,7 +1235,7 @@ mod reserved_executor_tests {
|
|||||||
&crate::ExWalletJupiterApeproSmartWalletExecutor,
|
&crate::ExWalletJupiterApeproSmartWalletExecutor,
|
||||||
&crate::ExWeightedSwapStabbleExecutor,
|
&crate::ExWeightedSwapStabbleExecutor,
|
||||||
];
|
];
|
||||||
assert_eq!(executors.len(), 104);
|
assert_eq!(executors.len(), 103);
|
||||||
for executor in executors {
|
for executor in executors {
|
||||||
assert!(!executor.program_ids().is_empty());
|
assert!(!executor.program_ids().is_empty());
|
||||||
for program_id in executor.program_ids() {
|
for program_id in executor.program_ids() {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/executor/metadata.rs
|
// file: kb-lib/src/executor/metadata.rs
|
||||||
// version: 8
|
// version: 9
|
||||||
|
|
||||||
//! `metadata` executor family.
|
//! `metadata` executor family.
|
||||||
|
|
||||||
@@ -87,9 +87,61 @@ pub use self::metaplex_token_metadata::ExMetadataMetaplexTokenMetadataExecutor;
|
|||||||
pub use self::metaplex_token_metadata::ExMetaplexTokenMetadataExecutionIntent;
|
pub use self::metaplex_token_metadata::ExMetaplexTokenMetadataExecutionIntent;
|
||||||
/// Exposes the Metaplex Token Metadata operation intent.
|
/// Exposes the Metaplex Token Metadata operation intent.
|
||||||
pub use self::metaplex_token_metadata::ExMetaplexTokenMetadataOperation;
|
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;
|
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.
|
/// Exposes the reserved `metadata/spl_name_service` executor.
|
||||||
pub use self::spl_name_service::ExMetadataSplNameServiceExecutor;
|
pub use self::spl_name_service::ExMetadataSplNameServiceExecutor;
|
||||||
|
|
||||||
pub(crate) use self::metaplex_token_metadata::executor_metaplex_token_metadata_build_prepared_plan;
|
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;
|
||||||
|
|||||||
@@ -1,49 +1,65 @@
|
|||||||
// file: kb-lib/src/executor/metadata/solana_program_metadata.rs
|
// 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.
|
mod builder;
|
||||||
#[derive(Clone, Debug, Default)]
|
mod constants;
|
||||||
pub struct ExMetadataSolanaProgramMetadataExecutor;
|
mod executor;
|
||||||
|
mod intent;
|
||||||
|
|
||||||
impl crate::ExApiInstructionExecutor for crate::ExMetadataSolanaProgramMetadataExecutor {
|
/// Derives the canonical Metadata PDA for one program and seed.
|
||||||
fn executor_name(&self) -> &'static str {
|
pub use self::builder::executor_metadata_solana_program_metadata_derive_canonical_pda;
|
||||||
return "kb-lib.executor.metadata.solana_program_metadata";
|
/// 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 {
|
/// Crate-root access to the exact prepared-plan builder.
|
||||||
return env!("CARGO_PKG_VERSION");
|
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;
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
1270
kb-lib/src/executor/metadata/solana_program_metadata/builder.rs
Normal file
1270
kb-lib/src/executor/metadata/solana_program_metadata/builder.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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,
|
||||||
|
];
|
||||||
224
kb-lib/src/executor/metadata/solana_program_metadata/executor.rs
Normal file
224
kb-lib/src/executor/metadata/solana_program_metadata/executor.rs
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
471
kb-lib/src/executor/metadata/solana_program_metadata/intent.rs
Normal file
471
kb-lib/src/executor/metadata/solana_program_metadata/intent.rs
Normal 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,
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/lib.rs
|
// file: kb-lib/src/lib.rs
|
||||||
// version: 33
|
// version: 36
|
||||||
|
|
||||||
//! Consolidated decoder, executor, materializer and shared model library.
|
//! Consolidated decoder, executor, materializer and shared model library.
|
||||||
#![warn(missing_docs)]
|
#![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;
|
pub use self::decoder::decoder_spl_elgamal_registry_parse_elgamal_registry_state;
|
||||||
/// Parses one Token-2022 Mint, Account, or Multisig state.
|
/// Parses one Token-2022 Mint, Account, or Multisig state.
|
||||||
pub use self::decoder::decoder_spl_token_2022_parse_token_2022_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`.
|
/// Exposes `EX_METAPLEX_TOKEN_METADATA_APPROVE_USE_AUTHORITY_OPERATION`.
|
||||||
pub use self::executor::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`.
|
/// Exposes `EX_METAPLEX_TOKEN_METADATA_BURN_OPERATION`.
|
||||||
@@ -1135,10 +1159,30 @@ pub use self::executor::ExLendingMarginfiV2Executor;
|
|||||||
pub use self::executor::ExLockRaydiumLpExecutor;
|
pub use self::executor::ExLockRaydiumLpExecutor;
|
||||||
/// Exposes the Metaplex Token Metadata executor.
|
/// Exposes the Metaplex Token Metadata executor.
|
||||||
pub use self::executor::ExMetadataMetaplexTokenMetadataExecutor;
|
pub use self::executor::ExMetadataMetaplexTokenMetadataExecutor;
|
||||||
/// Exposes the reserved `ExMetadataSolanaProgramMetadataExecutor` implementation.
|
/// Exposes the typed `ExMetadataSolanaProgramMetadataExecutor` implementation.
|
||||||
pub use self::executor::ExMetadataSolanaProgramMetadataExecutor;
|
pub use self::executor::ExMetadataSolanaProgramMetadataExecutor;
|
||||||
/// Exposes the reserved `ExMetadataSplNameServiceExecutor` implementation.
|
/// Exposes the reserved `ExMetadataSplNameServiceExecutor` implementation.
|
||||||
pub use self::executor::ExMetadataSplNameServiceExecutor;
|
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.
|
/// Exposes the complete Metaplex Token Metadata execution intent.
|
||||||
pub use self::executor::ExMetaplexTokenMetadataExecutionIntent;
|
pub use self::executor::ExMetaplexTokenMetadataExecutionIntent;
|
||||||
/// Exposes one current Metaplex Token Metadata operation.
|
/// 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;
|
pub use self::executor::executor_api_serialize_payload_json;
|
||||||
/// Exposes the pretty JSON payload serializer.
|
/// Exposes the pretty JSON payload serializer.
|
||||||
pub use self::executor::executor_api_serialize_payload_json_pretty;
|
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.
|
/// 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;
|
pub use self::executor::executor_solana_build_durable_nonce_transaction;
|
||||||
/// Builds an unsigned legacy Solana transaction from a prepared execution plan.
|
/// 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;
|
pub use self::materializer::MtLiquidityMaterializer;
|
||||||
/// Exposes the legacy common business materializer trait.
|
/// Exposes the legacy common business materializer trait.
|
||||||
pub use self::materializer::MtMaterializer;
|
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;
|
pub use self::materializer::MtMetadataMaterializer;
|
||||||
/// Reserved NFT materializer.
|
/// Reserved NFT materializer.
|
||||||
pub use self::materializer::MtNftMaterializer;
|
pub use self::materializer::MtNftMaterializer;
|
||||||
@@ -1409,6 +1457,8 @@ pub use self::materializer::MtRewardsMaterializer;
|
|||||||
pub use self::materializer::MtRiskMaterializer;
|
pub use self::materializer::MtRiskMaterializer;
|
||||||
/// Reserved routing materializer.
|
/// Reserved routing materializer.
|
||||||
pub use self::materializer::MtRoutingMaterializer;
|
pub use self::materializer::MtRoutingMaterializer;
|
||||||
|
/// Stable Solana Program Metadata instruction materializer.
|
||||||
|
pub use self::materializer::MtSolanaProgramMetadataMaterializer;
|
||||||
/// Stable native stake and vote materializer.
|
/// Stable native stake and vote materializer.
|
||||||
pub use self::materializer::MtStakingMaterializer;
|
pub use self::materializer::MtStakingMaterializer;
|
||||||
/// Stable Token, ATA and Token-2022 account materializer.
|
/// 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;
|
pub(crate) use self::decoder::decoder_spl_token_entry_for_tag;
|
||||||
/// Returns the first byte of one retained classic SPL Token payload.
|
/// Returns the first byte of one retained classic SPL Token payload.
|
||||||
pub(crate) use self::decoder::decoder_spl_token_payload_tag;
|
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.
|
/// Stable operation code for the nonce advance injected by durable transaction assembly.
|
||||||
pub(crate) use self::executor::EX_SOLANA_TRANSACTION_DURABLE_NONCE_ADVANCE_OPERATION;
|
pub(crate) use self::executor::EX_SOLANA_TRANSACTION_DURABLE_NONCE_ADVANCE_OPERATION;
|
||||||
/// Maximum over-the-wire size of a Solana transaction packet.
|
/// 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;
|
pub(crate) use self::executor::TRACING_TARGET_EXECUTOR_SPL_TOKEN;
|
||||||
/// Canonical SPL Token-2022 tracing target.
|
/// Canonical SPL Token-2022 tracing target.
|
||||||
pub(crate) use self::executor::TRACING_TARGET_EXECUTOR_SPL_TOKEN_2022;
|
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.
|
/// Internal Metaplex Token Metadata prepared-plan builder.
|
||||||
pub(crate) use self::executor::executor_metaplex_token_metadata_build_prepared_plan;
|
pub(crate) use self::executor::executor_metaplex_token_metadata_build_prepared_plan;
|
||||||
/// Crate-root access to `build_prepared_plan` from `address_lookup_table`.
|
/// 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;
|
pub(crate) use self::executor::executor_spl_token_2022_build_prepared_plan;
|
||||||
/// Crate-root access to `executor_spl_token_build_prepared_plan`.
|
/// Crate-root access to `executor_spl_token_build_prepared_plan`.
|
||||||
pub(crate) use self::executor::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;
|
pub(crate) use self::materializer::MT_ADMIN_PROCESSOR_NAME;
|
||||||
/// Event families accepted by the Solana Program Metadata instruction materializer.
|
/// Crate-root access to `MT_ADMIN_PROJECTION_VERSION`.
|
||||||
pub(crate) use self::materializer::MT_METADATA_SPM_ACCEPTED_FAMILIES;
|
pub(crate) use self::materializer::MT_ADMIN_PROJECTION_VERSION;
|
||||||
/// Stable processor name for Solana Program Metadata projections.
|
/// Crate-root access to `MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES`.
|
||||||
pub(crate) use self::materializer::MT_METADATA_SPM_PROCESSOR_NAME;
|
pub(crate) use self::materializer::MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES;
|
||||||
/// Stable projection schema version for Solana Program Metadata outputs.
|
/// Crate-root access to `MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE`.
|
||||||
pub(crate) use self::materializer::MT_METADATA_SPM_PROJECTION_VERSION;
|
pub(crate) use self::materializer::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE;
|
||||||
/// Stable processor name for SPL Token and ATA risk facts.
|
/// 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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/materializer.rs
|
// file: kb-lib/src/materializer.rs
|
||||||
// version: 9
|
// version: 14
|
||||||
|
|
||||||
//! Consolidated materializer modules.
|
//! Consolidated materializer modules.
|
||||||
|
|
||||||
@@ -73,8 +73,10 @@ pub use self::lending::MtLendingMaterializer;
|
|||||||
pub use self::lifecycle::MtLifecycleMaterializer;
|
pub use self::lifecycle::MtLifecycleMaterializer;
|
||||||
/// Reserved liquidity materializer.
|
/// Reserved liquidity materializer.
|
||||||
pub use self::liquidity::MtLiquidityMaterializer;
|
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;
|
pub use self::metadata::MtMetadataMaterializer;
|
||||||
|
/// Stable Solana Program Metadata instruction materializer.
|
||||||
|
pub use self::metadata::MtSolanaProgramMetadataMaterializer;
|
||||||
/// Materializes one canonical non-Metadata Metaplex account snapshot.
|
/// Materializes one canonical non-Metadata Metaplex account snapshot.
|
||||||
pub use self::metadata::materializer_metadata_materialize_metaplex_account_snapshot;
|
pub use self::metadata::materializer_metadata_materialize_metaplex_account_snapshot;
|
||||||
/// Materializes one canonical Metaplex Metadata account snapshot.
|
/// Materializes one canonical Metaplex Metadata account snapshot.
|
||||||
@@ -118,100 +120,533 @@ pub use self::transaction::MtTransactionAnnotationMaterializer;
|
|||||||
/// Reserved vault materializer.
|
/// Reserved vault materializer.
|
||||||
pub use self::vault::MtVaultMaterializer;
|
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;
|
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;
|
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;
|
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;
|
pub(crate) use self::lifecycle::TRACING_TARGET_MATERIALIZER_LIFECYCLE;
|
||||||
/// Event families accepted by the Solana Program Metadata instruction materializer.
|
/// Crate-visible `MT_LIQUIDITY_ACCEPTED_FAMILIES` component constant.
|
||||||
pub(crate) use self::metadata::MT_METADATA_SPM_ACCEPTED_FAMILIES;
|
pub(crate) use self::liquidity::MT_LIQUIDITY_ACCEPTED_FAMILIES;
|
||||||
/// Stable processor name for Solana Program Metadata projections.
|
/// Crate-visible `MT_LIQUIDITY_COMPONENT_NAME` component constant.
|
||||||
pub(crate) use self::metadata::MT_METADATA_SPM_PROCESSOR_NAME;
|
pub(crate) use self::liquidity::MT_LIQUIDITY_COMPONENT_NAME;
|
||||||
/// Stable projection schema version for Solana Program Metadata outputs.
|
/// Crate-visible `MT_LIQUIDITY_PROCESSOR_NAME` component constant.
|
||||||
pub(crate) use self::metadata::MT_METADATA_SPM_PROJECTION_VERSION;
|
pub(crate) use self::liquidity::MT_LIQUIDITY_PROCESSOR_NAME;
|
||||||
/// Canonical tracing target for Solana Program Metadata materialization.
|
/// 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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[test]
|
fn materializer_contracts() -> &'static [(
|
||||||
fn reserved_materializers_expose_inactive_public_contracts() {
|
&'static dyn crate::MtMaterializer,
|
||||||
let materializers: &[(&dyn crate::MtApiEventMaterializer, &str)] = &[
|
&'static dyn crate::MtApiEventMaterializer,
|
||||||
(&crate::MtBridgeMaterializer, "kb-lib.materializer.bridge"),
|
&'static str,
|
||||||
(&crate::MtGovernanceMaterializer, "kb-lib.materializer.governance"),
|
&'static str,
|
||||||
(&crate::MtLendingMaterializer, "kb-lib.materializer.lending"),
|
)] {
|
||||||
(&crate::MtLiquidityMaterializer, "kb-lib.materializer.liquidity"),
|
return &[
|
||||||
(&crate::MtNftMaterializer, "kb-lib.materializer.nft"),
|
(
|
||||||
(&crate::MtOracleMaterializer, "kb-lib.materializer.oracle"),
|
&crate::MtAdminMaterializer,
|
||||||
(&crate::MtOrderbookMaterializer, "kb-lib.materializer.orderbook"),
|
&crate::MtAdminMaterializer,
|
||||||
(&crate::MtPerpetualsMaterializer, "kb-lib.materializer.perpetuals"),
|
crate::MT_ADMIN_COMPONENT_NAME,
|
||||||
(&crate::MtPoolStateMaterializer, "kb-lib.materializer.pool.state"),
|
crate::MT_ADMIN_PROCESSOR_NAME,
|
||||||
(&crate::MtRewardsMaterializer, "kb-lib.materializer.rewards"),
|
),
|
||||||
(&crate::MtRoutingMaterializer, "kb-lib.materializer.routing"),
|
(
|
||||||
|
&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,
|
&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);
|
fn state_only_materializer_contracts() -> &'static [(&'static str, &'static str, &'static str)]
|
||||||
assert!(!identity.version.is_empty());
|
{
|
||||||
assert!(materializer.accepted_families().is_empty());
|
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]
|
#[test]
|
||||||
fn reserved_materializers_preserve_legacy_trait_contracts() {
|
fn state_only_materializers_keep_runtime_processor_and_tracing_identity_aligned() {
|
||||||
let materializers: &[(&dyn crate::MtMaterializer, &str)] = &[
|
for (component_name, processor_name, tracing_target) in state_only_materializer_contracts()
|
||||||
(&crate::MtBridgeMaterializer, "kb-lib.materializer.bridge"),
|
{
|
||||||
(&crate::MtGovernanceMaterializer, "kb-lib.materializer.governance"),
|
assert_eq!(*tracing_target, *component_name);
|
||||||
(&crate::MtLendingMaterializer, "kb-lib.materializer.lending"),
|
assert_eq!(
|
||||||
(&crate::MtLiquidityMaterializer, "kb-lib.materializer.liquidity"),
|
component_name.strip_prefix("kb-lib."),
|
||||||
(&crate::MtNftMaterializer, "kb-lib.materializer.nft"),
|
std::option::Option::Some(*processor_name)
|
||||||
(&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"),
|
#[test]
|
||||||
(&crate::MtRoutingMaterializer, "kb-lib.materializer.routing"),
|
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::MtTokenMetadataRiskMaterializer,
|
||||||
"kb-lib.materializer.token.metadata_risk",
|
&crate::MtTradesMaterializer,
|
||||||
),
|
&crate::MtVaultMaterializer,
|
||||||
(&crate::MtTradesMaterializer, "kb-lib.materializer.trades"),
|
|
||||||
(&crate::MtVaultMaterializer, "kb-lib.materializer.vault"),
|
|
||||||
];
|
];
|
||||||
for (materializer, expected_name) in materializers {
|
for materializer in materializers {
|
||||||
assert_eq!(materializer.materializer_name(), *expected_name);
|
assert!(materializer.accepted_families().is_empty());
|
||||||
assert!(!materializer.materializer_version().is_empty());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,55 @@
|
|||||||
// file: kb-lib/src/materializer/admin.rs
|
// file: kb-lib/src/materializer/admin.rs
|
||||||
// version: 7
|
// version: 8
|
||||||
|
|
||||||
//! `admin` materializer family.
|
//! `admin` materializer family.
|
||||||
|
|
||||||
mod constants;
|
mod constants;
|
||||||
mod core;
|
mod materializer;
|
||||||
|
|
||||||
/// Stable native and SPL administration 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.
|
/// 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.
|
/// 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;
|
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;
|
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_ADMIN;
|
||||||
|
|||||||
@@ -1,9 +1,67 @@
|
|||||||
// file: kb-lib/src/materializer/admin/constants.rs
|
// 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";
|
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";
|
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",
|
||||||
|
];
|
||||||
|
|||||||
@@ -1,42 +1,8 @@
|
|||||||
// file: kb-lib/src/materializer/admin/core.rs
|
// file: kb-lib/src/materializer/admin/materializer.rs
|
||||||
// version: 20
|
// version: 22
|
||||||
|
|
||||||
//! Stable administrative projections derived from exact committed observations.
|
//! 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)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
enum ProjectionKind {
|
enum ProjectionKind {
|
||||||
SystemAccountOwnership,
|
SystemAccountOwnership,
|
||||||
@@ -91,7 +57,7 @@ pub struct MtAdminMaterializer;
|
|||||||
|
|
||||||
impl crate::MtMaterializer for crate::MtAdminMaterializer {
|
impl crate::MtMaterializer for crate::MtAdminMaterializer {
|
||||||
fn materializer_name(&self) -> &'static str {
|
fn materializer_name(&self) -> &'static str {
|
||||||
return "kb-lib.materializer.admin";
|
return crate::MT_ADMIN_COMPONENT_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn materializer_version(&self) -> &'static str {
|
fn materializer_version(&self) -> &'static str {
|
||||||
@@ -99,7 +65,7 @@ impl crate::MtMaterializer for crate::MtAdminMaterializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
|
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)
|
&& program_matches_surface(event)
|
||||||
&& projection(event.surface_code.0.as_str(), event.event_name.0.as_str()).is_some();
|
&& 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 {
|
impl crate::MtApiEventMaterializer for crate::MtAdminMaterializer {
|
||||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||||
return 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(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||||
return ACCEPTED_FAMILIES;
|
return crate::MT_ADMIN_ACCEPTED_FAMILIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
|
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)
|
&& program_matches_surface(&observation.event)
|
||||||
&& projection(
|
&& projection(
|
||||||
observation.event.surface_code.0.as_str(),
|
observation.event.surface_code.0.as_str(),
|
||||||
@@ -165,7 +131,7 @@ impl crate::MtApiEventMaterializer for crate::MtAdminMaterializer {
|
|||||||
return crate::MtApiMaterializerExecutionResult::ignored();
|
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();
|
return crate::MtApiMaterializerExecutionResult::ignored();
|
||||||
}
|
}
|
||||||
if !program_matches_surface(&observation.event) {
|
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()),
|
.output_key(observation.event.surface_code.0.as_str(), operation.as_str()),
|
||||||
family: crate::MdMaterializedEventFamily::Admin,
|
family: crate::MdMaterializedEventFamily::Admin,
|
||||||
payload_json: serde_json::json!({
|
payload_json: serde_json::json!({
|
||||||
"projectionVersion": 1,
|
"projectionVersion": crate::MT_ADMIN_PROJECTION_VERSION,
|
||||||
"projectionSemantics": "committed_instruction_admin_event",
|
"projectionSemantics": "committed_instruction_admin_event",
|
||||||
"domain": projection.domain(),
|
"domain": projection.domain(),
|
||||||
"loaderSurface": projection.loader_surface(),
|
"loaderSurface": projection.loader_surface(),
|
||||||
@@ -212,6 +178,10 @@ impl crate::MtApiEventMaterializer for crate::MtAdminMaterializer {
|
|||||||
"slot": observation.event.slot.0,
|
"slot": observation.event.slot.0,
|
||||||
"instructionPath": observation.event.instruction_path.0.clone(),
|
"instructionPath": observation.event.instruction_path.0.clone(),
|
||||||
"transactionSucceeded": true,
|
"transactionSucceeded": true,
|
||||||
|
"provenance": {
|
||||||
|
"processorName": crate::MT_ADMIN_PROCESSOR_NAME,
|
||||||
|
"processorVersion": env!("CARGO_PKG_VERSION"),
|
||||||
|
},
|
||||||
"accounts": accounts,
|
"accounts": accounts,
|
||||||
"parameters": parameters,
|
"parameters": parameters,
|
||||||
"projection": projection_details(
|
"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> {
|
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);
|
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);
|
return std::option::Option::Some(ProjectionKind::ConfigAccountUpdate);
|
||||||
}
|
}
|
||||||
if surface_code == BPF_LOADER_UPGRADEABLE_SURFACE
|
if surface_code == crate::MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE
|
||||||
&& contains(BPF_LOADER_UPGRADEABLE_ENTRIES, entry_code)
|
&& contains(crate::MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES, entry_code)
|
||||||
{
|
{
|
||||||
return std::option::Option::Some(ProjectionKind::ProgramLoaderAuthority(
|
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(
|
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)
|
if matches!(
|
||||||
&& contains(SPL_TOKEN_AUTHORITY_ENTRIES, entry_code)
|
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);
|
return std::option::Option::Some(ProjectionKind::SplTokenAuthority);
|
||||||
}
|
}
|
||||||
if matches!(surface_code, SPL_TOKEN_SURFACE | SPL_TOKEN_2022_SURFACE)
|
if matches!(
|
||||||
&& contains(SPL_TOKEN_MULTISIG_ENTRIES, entry_code)
|
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);
|
return std::option::Option::Some(ProjectionKind::SplTokenMultisig);
|
||||||
}
|
}
|
||||||
if surface_code == SPL_TOKEN_2022_SURFACE
|
if surface_code == crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE
|
||||||
&& contains(SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES, entry_code)
|
&& contains(crate::MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES, entry_code)
|
||||||
{
|
{
|
||||||
return std::option::Option::Some(ProjectionKind::SplToken2022ExtensionAdmin);
|
return std::option::Option::Some(ProjectionKind::SplToken2022ExtensionAdmin);
|
||||||
}
|
}
|
||||||
if surface_code == SPL_ELGAMAL_REGISTRY_SURFACE
|
if surface_code == crate::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE
|
||||||
&& contains(SPL_ELGAMAL_REGISTRY_ENTRIES, entry_code)
|
&& contains(crate::MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES, entry_code)
|
||||||
{
|
{
|
||||||
return std::option::Option::Some(ProjectionKind::SplElgamalRegistry);
|
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 {
|
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;
|
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;
|
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 event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -537,7 +517,7 @@ mod tests {
|
|||||||
fn system_assign_creates_owner_projection() {
|
fn system_assign_creates_owner_projection() {
|
||||||
let materializer = crate::MtAdminMaterializer;
|
let materializer = crate::MtAdminMaterializer;
|
||||||
let observation = observation(
|
let observation = observation(
|
||||||
super::SYSTEM_SURFACE,
|
crate::MT_ADMIN_SYSTEM_SURFACE,
|
||||||
"assign",
|
"assign",
|
||||||
false,
|
false,
|
||||||
serde_json::json!([account("assigned_account", "account111",)]),
|
serde_json::json!([account("assigned_account", "account111",)]),
|
||||||
@@ -554,7 +534,7 @@ mod tests {
|
|||||||
fn seeded_assign_preserves_base_seed_and_owner() {
|
fn seeded_assign_preserves_base_seed_and_owner() {
|
||||||
let materializer = crate::MtAdminMaterializer;
|
let materializer = crate::MtAdminMaterializer;
|
||||||
let observation = observation(
|
let observation = observation(
|
||||||
super::SYSTEM_SURFACE,
|
crate::MT_ADMIN_SYSTEM_SURFACE,
|
||||||
"assign_with_seed",
|
"assign_with_seed",
|
||||||
false,
|
false,
|
||||||
serde_json::json!([
|
serde_json::json!([
|
||||||
@@ -573,7 +553,7 @@ mod tests {
|
|||||||
fn config_store_preserves_bounded_opaque_audit_fields() {
|
fn config_store_preserves_bounded_opaque_audit_fields() {
|
||||||
let materializer = crate::MtAdminMaterializer;
|
let materializer = crate::MtAdminMaterializer;
|
||||||
let observation = observation(
|
let observation = observation(
|
||||||
super::CONFIG_SURFACE,
|
crate::MT_ADMIN_CONFIG_SURFACE,
|
||||||
"store",
|
"store",
|
||||||
false,
|
false,
|
||||||
serde_json::json!([
|
serde_json::json!([
|
||||||
@@ -600,13 +580,17 @@ mod tests {
|
|||||||
fn loader_authority_variants_create_exact_admin_outputs() {
|
fn loader_authority_variants_create_exact_admin_outputs() {
|
||||||
let materializer = crate::MtAdminMaterializer;
|
let materializer = crate::MtAdminMaterializer;
|
||||||
for (surface, entry, target_role) in [
|
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",
|
"set_authority_checked",
|
||||||
"authority_target",
|
"authority_target",
|
||||||
),
|
),
|
||||||
(super::LOADER_V4_SURFACE, "transfer_authority", "program_account"),
|
(crate::MT_ADMIN_LOADER_V4_SURFACE, "transfer_authority", "program_account"),
|
||||||
] {
|
] {
|
||||||
let observation = observation(
|
let observation = observation(
|
||||||
surface,
|
surface,
|
||||||
@@ -629,7 +613,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn classic_token_authority_and_multisig_create_distinct_admin_outputs() {
|
fn classic_token_authority_and_multisig_create_distinct_admin_outputs() {
|
||||||
let mut authority = observation(
|
let mut authority = observation(
|
||||||
super::SPL_TOKEN_SURFACE,
|
crate::MT_ADMIN_SPL_TOKEN_SURFACE,
|
||||||
"set_authority",
|
"set_authority",
|
||||||
false,
|
false,
|
||||||
serde_json::json!([
|
serde_json::json!([
|
||||||
@@ -649,7 +633,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let mut multisig = observation(
|
let mut multisig = observation(
|
||||||
super::SPL_TOKEN_SURFACE,
|
crate::MT_ADMIN_SPL_TOKEN_SURFACE,
|
||||||
"initialize_multisig2",
|
"initialize_multisig2",
|
||||||
false,
|
false,
|
||||||
serde_json::json!([
|
serde_json::json!([
|
||||||
@@ -678,7 +662,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn token_2022_authority_change_uses_the_admin_owner_once() {
|
fn token_2022_authority_change_uses_the_admin_owner_once() {
|
||||||
let mut observation = observation(
|
let mut observation = observation(
|
||||||
super::SPL_TOKEN_2022_SURFACE,
|
crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE,
|
||||||
"set_authority",
|
"set_authority",
|
||||||
false,
|
false,
|
||||||
serde_json::json!([
|
serde_json::json!([
|
||||||
@@ -703,7 +687,7 @@ mod tests {
|
|||||||
fn failed_admin_mutation_is_refused() {
|
fn failed_admin_mutation_is_refused() {
|
||||||
let materializer = crate::MtAdminMaterializer;
|
let materializer = crate::MtAdminMaterializer;
|
||||||
let observation = observation(
|
let observation = observation(
|
||||||
super::SYSTEM_SURFACE,
|
crate::MT_ADMIN_SYSTEM_SURFACE,
|
||||||
"assign",
|
"assign",
|
||||||
true,
|
true,
|
||||||
serde_json::json!([account("assigned_account", "account111",)]),
|
serde_json::json!([account("assigned_account", "account111",)]),
|
||||||
@@ -754,7 +738,7 @@ mod tests {
|
|||||||
fn output_serialization_is_deterministic() {
|
fn output_serialization_is_deterministic() {
|
||||||
let materializer = crate::MtAdminMaterializer;
|
let materializer = crate::MtAdminMaterializer;
|
||||||
let observation = observation(
|
let observation = observation(
|
||||||
super::CONFIG_SURFACE,
|
crate::MT_ADMIN_CONFIG_SURFACE,
|
||||||
"store",
|
"store",
|
||||||
false,
|
false,
|
||||||
serde_json::json!([account("config_account", "config111",)]),
|
serde_json::json!([account("config_account", "config111",)]),
|
||||||
@@ -788,7 +772,7 @@ mod tests {
|
|||||||
("update_registry", 0_i64, 2_u64, "elgamal_registry_key_updated"),
|
("update_registry", 0_i64, 2_u64, "elgamal_registry_key_updated"),
|
||||||
] {
|
] {
|
||||||
let mut observation = observation(
|
let mut observation = observation(
|
||||||
super::SPL_ELGAMAL_REGISTRY_SURFACE,
|
crate::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE,
|
||||||
operation,
|
operation,
|
||||||
false,
|
false,
|
||||||
serde_json::json!([
|
serde_json::json!([
|
||||||
@@ -830,7 +814,7 @@ mod tests {
|
|||||||
fn elgamal_registry_foreign_program_and_failed_transaction_are_refused() {
|
fn elgamal_registry_foreign_program_and_failed_transaction_are_refused() {
|
||||||
let materializer = crate::MtAdminMaterializer;
|
let materializer = crate::MtAdminMaterializer;
|
||||||
let mut foreign = observation(
|
let mut foreign = observation(
|
||||||
super::SPL_ELGAMAL_REGISTRY_SURFACE,
|
crate::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE,
|
||||||
"create_registry",
|
"create_registry",
|
||||||
false,
|
false,
|
||||||
serde_json::json!([]),
|
serde_json::json!([]),
|
||||||
@@ -858,7 +842,7 @@ mod tests {
|
|||||||
("update_group_member_pointer", "memberAddress", "member111"),
|
("update_group_member_pointer", "memberAddress", "member111"),
|
||||||
] {
|
] {
|
||||||
let mut observation = observation(
|
let mut observation = observation(
|
||||||
super::SPL_TOKEN_2022_SURFACE,
|
crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE,
|
||||||
operation,
|
operation,
|
||||||
false,
|
false,
|
||||||
serde_json::json!([{"position":0,"accountKey":"mint111"}]),
|
serde_json::json!([{"position":0,"accountKey":"mint111"}]),
|
||||||
@@ -881,7 +865,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn token_2022_extension_admin_rejects_foreign_program_and_failed_transaction() {
|
fn token_2022_extension_admin_rejects_foreign_program_and_failed_transaction() {
|
||||||
let mut observation = observation(
|
let mut observation = observation(
|
||||||
super::SPL_TOKEN_2022_SURFACE,
|
crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE,
|
||||||
"initialize_metadata_pointer",
|
"initialize_metadata_pointer",
|
||||||
false,
|
false,
|
||||||
serde_json::json!([]),
|
serde_json::json!([]),
|
||||||
@@ -937,7 +921,7 @@ pub fn materializer_admin_materialize_token_2022_state_snapshots(
|
|||||||
output_key: output_key.clone(),
|
output_key: output_key.clone(),
|
||||||
family: crate::MdMaterializedEventFamily::Admin,
|
family: crate::MdMaterializedEventFamily::Admin,
|
||||||
payload_json: serde_json::json!({
|
payload_json: serde_json::json!({
|
||||||
"projectionVersion": 1,
|
"projectionVersion": crate::MT_ADMIN_PROJECTION_VERSION,
|
||||||
"projectionSemantics": "authoritative_bounded_admin_state_snapshot",
|
"projectionSemantics": "authoritative_bounded_admin_state_snapshot",
|
||||||
"domain": "token_2022_extension_admin_state",
|
"domain": "token_2022_extension_admin_state",
|
||||||
"idempotenceKey": output_key,
|
"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}"),
|
output_key: format!("spl_elgamal_registry_state:{registry_account_key}:{slot}"),
|
||||||
family: crate::MdMaterializedEventFamily::Admin,
|
family: crate::MdMaterializedEventFamily::Admin,
|
||||||
payload_json: serde_json::json!({
|
payload_json: serde_json::json!({
|
||||||
"projectionVersion": 1,
|
"projectionVersion": crate::MT_ADMIN_PROJECTION_VERSION,
|
||||||
"domain": "spl_elgamal_registry_account_state",
|
"domain": "spl_elgamal_registry_account_state",
|
||||||
"projectionSemantics": "authoritative_exact_account_snapshot",
|
"projectionSemantics": "authoritative_exact_account_snapshot",
|
||||||
"idempotenceKey": format!("spl_elgamal_registry:{registry_account_key}:{slot}"),
|
"idempotenceKey": format!("spl_elgamal_registry:{registry_account_key}:{slot}"),
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
// file: kb-lib/src/materializer/api/contracts.rs
|
// file: kb-lib/src/materializer/api/contracts.rs
|
||||||
// version: 7
|
// version: 8
|
||||||
|
|
||||||
//! Backend-neutral decoded observation materialization contracts.
|
//! Backend-neutral decoded observation materialization contracts.
|
||||||
|
|
||||||
/// Stable identity of one decoded event materializer.
|
/// Stable identity of one decoded event materializer.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct MtApiMaterializerIdentity {
|
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,
|
pub name: std::string::String,
|
||||||
/// Semantic or deterministic implementation version.
|
/// Semantic or deterministic implementation version.
|
||||||
pub version: std::string::String,
|
pub version: std::string::String,
|
||||||
@@ -177,7 +177,7 @@ impl MtApiMaterializerExecutionResult {
|
|||||||
|
|
||||||
/// Stable decoded observation materializer contract used by the common pipeline.
|
/// Stable decoded observation materializer contract used by the common pipeline.
|
||||||
pub trait MtApiEventMaterializer: std::marker::Send + std::marker::Sync {
|
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;
|
fn identity(&self) -> MtApiMaterializerIdentity;
|
||||||
/// Returns every decoded event family explicitly accepted by this materializer.
|
/// Returns every decoded event family explicitly accepted by this materializer.
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily];
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily];
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// file: kb-lib/src/materializer/api/materializer.rs
|
// 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 {
|
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;
|
fn materializer_name(&self) -> &'static str;
|
||||||
|
|
||||||
/// Returns the materializer version.
|
/// Returns the materializer version.
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
// file: kb-lib/src/materializer/bridge.rs
|
// file: kb-lib/src/materializer/bridge.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! `bridge` materializer family.
|
//! `bridge` materializer family.
|
||||||
|
|
||||||
mod core;
|
mod constants;
|
||||||
|
mod materializer;
|
||||||
|
|
||||||
/// Reserved bridge 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;
|
||||||
|
|||||||
11
kb-lib/src/materializer/bridge/constants.rs
Normal file
11
kb-lib/src/materializer/bridge/constants.rs
Normal 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] = &[];
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/materializer/bridge/core.rs
|
// file: kb-lib/src/materializer/bridge/materializer.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! Reserved bridge materializer.
|
//! Reserved bridge materializer.
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ pub struct MtBridgeMaterializer;
|
|||||||
|
|
||||||
impl crate::MtMaterializer for crate::MtBridgeMaterializer {
|
impl crate::MtMaterializer for crate::MtBridgeMaterializer {
|
||||||
fn materializer_name(&self) -> &'static str {
|
fn materializer_name(&self) -> &'static str {
|
||||||
return "kb-lib.materializer.bridge";
|
return crate::MT_BRIDGE_COMPONENT_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn materializer_version(&self) -> &'static str {
|
fn materializer_version(&self) -> &'static str {
|
||||||
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtBridgeMaterializer {
|
|||||||
|
|
||||||
fn materialize_event(
|
fn materialize_event(
|
||||||
&self,
|
&self,
|
||||||
event: &crate::MdDecodedProtocolEvent,
|
_event: &crate::MdDecodedProtocolEvent,
|
||||||
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
||||||
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
|
return std::result::Result::Ok(std::vec::Vec::new());
|
||||||
signature: event.signature.clone(),
|
|
||||||
slot: event.slot,
|
|
||||||
decoded_family: event.event_family,
|
|
||||||
materialized_family: crate::MdMaterializedEventFamily::Bridge,
|
|
||||||
}]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::MtApiEventMaterializer for crate::MtBridgeMaterializer {
|
impl crate::MtApiEventMaterializer for crate::MtBridgeMaterializer {
|
||||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||||
return 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(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||||
return &[];
|
return crate::MT_BRIDGE_ACCEPTED_FAMILIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_policy(
|
fn transaction_policy(
|
||||||
@@ -1,13 +1,36 @@
|
|||||||
// file: kb-lib/src/materializer/compliance.rs
|
// file: kb-lib/src/materializer/compliance.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
//! `compliance` materializer family.
|
//! `compliance` materializer family.
|
||||||
|
|
||||||
mod audit;
|
mod audit;
|
||||||
mod constants;
|
|
||||||
|
|
||||||
/// Stable native compliance audit materializer.
|
/// Stable native compliance audit materializer.
|
||||||
pub use self::audit::MtComplianceAuditMaterializer;
|
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.
|
/// 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;
|
||||||
|
|||||||
@@ -1,577 +1,37 @@
|
|||||||
// file: kb-lib/src/materializer/compliance/audit.rs
|
// 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];
|
mod constants;
|
||||||
const COMPUTE_BUDGET_SURFACE: &str = "solana.core.compute_budget";
|
mod materializer;
|
||||||
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"];
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
/// Stable native compliance audit materializer.
|
||||||
enum ProjectionKind {
|
pub use self::materializer::MtComplianceAuditMaterializer;
|
||||||
ProgramByteWrite(&'static str),
|
|
||||||
ProgramByteCopy,
|
|
||||||
ComputeBudgetProfile,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProjectionKind {
|
/// Crate-visible `MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES` component constant.
|
||||||
fn output_key(self, surface_code: &str, operation: &str) -> std::string::String {
|
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES;
|
||||||
return match self {
|
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE` component constant.
|
||||||
Self::ProgramByteWrite(_) | Self::ProgramByteCopy => {
|
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE;
|
||||||
format!("program_loader_code:{surface_code}:{operation}:0")
|
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE` component constant.
|
||||||
},
|
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE;
|
||||||
Self::ComputeBudgetProfile => "transaction_compute_budget_profile:0".to_string(),
|
/// 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;
|
||||||
fn loader_surface(self) -> &'static str {
|
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES` component constant.
|
||||||
return match self {
|
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES;
|
||||||
Self::ProgramByteWrite(surface) => surface,
|
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE` component constant.
|
||||||
Self::ProgramByteCopy => LOADER_V4_SURFACE,
|
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE;
|
||||||
Self::ComputeBudgetProfile => 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.
|
||||||
/// Stable compliance-audit materializer for native bytecode mutations and execution profiles.
|
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME;
|
||||||
#[derive(Clone, Debug, Default)]
|
/// Stable projection version for native compliance audit outputs.
|
||||||
pub struct MtComplianceAuditMaterializer;
|
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_PROJECTION_VERSION;
|
||||||
|
/// Crate-visible `MT_COMPLIANCE_AUDIT_WRITE_ENTRIES` component constant.
|
||||||
impl crate::MtMaterializer for crate::MtComplianceAuditMaterializer {
|
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_WRITE_ENTRIES;
|
||||||
fn materializer_name(&self) -> &'static str {
|
/// Canonical tracing target for the native compliance audit materializer.
|
||||||
return "kb-lib.materializer.compliance.audit";
|
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT;
|
||||||
}
|
|
||||||
|
|
||||||
fn materializer_version(&self) -> &'static str {
|
|
||||||
return env!("CARGO_PKG_VERSION");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
|
|
||||||
return event.event_family == crate::MdEventFamily::Audit
|
|
||||||
&& projection(event.surface_code.0.as_str(), event.event_name.0.as_str()).is_some();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn materialize_event(
|
|
||||||
&self,
|
|
||||||
event: &crate::MdDecodedProtocolEvent,
|
|
||||||
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
|
||||||
if !crate::MtMaterializer::accepts_event(self, event) {
|
|
||||||
return std::result::Result::Ok(std::vec::Vec::new());
|
|
||||||
}
|
|
||||||
return std::result::Result::Ok(std::vec::Vec::new());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl crate::MtApiEventMaterializer for crate::MtComplianceAuditMaterializer {
|
|
||||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
|
||||||
return crate::MtApiMaterializerIdentity {
|
|
||||||
name: "materializer.compliance.audit".to_string(),
|
|
||||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
|
||||||
return ACCEPTED_FAMILIES;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
|
|
||||||
return observation.event.event_family == crate::MdEventFamily::Audit
|
|
||||||
&& projection(
|
|
||||||
observation.event.surface_code.0.as_str(),
|
|
||||||
observation.event.event_name.0.as_str(),
|
|
||||||
)
|
|
||||||
.is_some();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transaction_policy(
|
|
||||||
&self,
|
|
||||||
_family: crate::MdEventFamily,
|
|
||||||
) -> crate::MtApiMaterializationTransactionPolicy {
|
|
||||||
return crate::MtApiMaterializationTransactionPolicy::SuccessfulOrFailedAudit;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn materialize(
|
|
||||||
&self,
|
|
||||||
observation: &crate::DcApiDecodedObservation,
|
|
||||||
) -> crate::MtApiMaterializerExecutionResult {
|
|
||||||
let projection = match projection(
|
|
||||||
observation.event.surface_code.0.as_str(),
|
|
||||||
observation.event.event_name.0.as_str(),
|
|
||||||
) {
|
|
||||||
std::option::Option::Some(value) => value,
|
|
||||||
std::option::Option::None => {
|
|
||||||
tracing::debug!(
|
|
||||||
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
|
|
||||||
action = "materialize_compliance_audit",
|
|
||||||
surface_code = %observation.event.surface_code.0.as_str(),
|
|
||||||
entry_code = %observation.event.event_name.0.as_str(),
|
|
||||||
accepted = false,
|
|
||||||
"ignore observation outside supported native compliance-audit projections"
|
|
||||||
);
|
|
||||||
return crate::MtApiMaterializerExecutionResult::ignored();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
if observation.event.event_family != crate::MdEventFamily::Audit {
|
|
||||||
return crate::MtApiMaterializerExecutionResult::ignored();
|
|
||||||
}
|
|
||||||
if projection != ProjectionKind::ComputeBudgetProfile
|
|
||||||
&& (observation.transaction_failed || !observation.observation_committed)
|
|
||||||
{
|
|
||||||
tracing::debug!(
|
|
||||||
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
|
|
||||||
action = "materialize_compliance_audit",
|
|
||||||
surface_code = %observation.event.surface_code.0.as_str(),
|
|
||||||
entry_code = %observation.event.event_name.0.as_str(),
|
|
||||||
committed = observation.observation_committed,
|
|
||||||
transaction_failed = observation.transaction_failed,
|
|
||||||
"refuse uncommitted native bytecode mutation observation"
|
|
||||||
);
|
|
||||||
return crate::MtApiMaterializerExecutionResult::refused(
|
|
||||||
"failed_transaction_compliance_audit_refused",
|
|
||||||
"failed or uncommitted native observations cannot create committed bytecode mutation outputs",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let accounts = observation
|
|
||||||
.payload_json
|
|
||||||
.get("accounts")
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or(serde_json::Value::Null);
|
|
||||||
let parameters = observation
|
|
||||||
.payload_json
|
|
||||||
.get("parameters")
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or(serde_json::Value::Null);
|
|
||||||
let operation = observation.event.event_name.0.clone();
|
|
||||||
if projection == ProjectionKind::ComputeBudgetProfile {
|
|
||||||
return materialize_compute_budget_profile(observation, parameters, operation);
|
|
||||||
}
|
|
||||||
let output = crate::MtApiMaterializedOutput {
|
|
||||||
output_key: projection
|
|
||||||
.output_key(observation.event.surface_code.0.as_str(), operation.as_str()),
|
|
||||||
family: crate::MdMaterializedEventFamily::ComplianceAudit,
|
|
||||||
payload_json: serde_json::json!({
|
|
||||||
"projectionVersion": 1,
|
|
||||||
"projectionSemantics": "committed_instruction_compliance_audit_event",
|
|
||||||
"domain": "program_loader_code_mutation",
|
|
||||||
"loaderSurface": projection.loader_surface(),
|
|
||||||
"operation": operation,
|
|
||||||
"programId": observation.event.program_id.0.clone(),
|
|
||||||
"signature": observation.event.signature.0.clone(),
|
|
||||||
"slot": observation.event.slot.0,
|
|
||||||
"instructionPath": observation.event.instruction_path.0.clone(),
|
|
||||||
"transactionSucceeded": true,
|
|
||||||
"accounts": accounts,
|
|
||||||
"parameters": parameters,
|
|
||||||
"projection": projection_details(projection, &accounts, ¶meters),
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
tracing::debug!(
|
|
||||||
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
|
|
||||||
action = "materialize_compliance_audit",
|
|
||||||
surface_code = %observation.event.surface_code.0.as_str(),
|
|
||||||
entry_code = %observation.event.event_name.0.as_str(),
|
|
||||||
loader_surface = projection.loader_surface(),
|
|
||||||
output_count = 1_usize,
|
|
||||||
"materialize committed native bytecode mutation observation"
|
|
||||||
);
|
|
||||||
return crate::MtApiMaterializerExecutionResult {
|
|
||||||
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
|
|
||||||
outputs: std::vec![output],
|
|
||||||
diagnostics: std::vec::Vec::new(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn materialize_compute_budget_profile(
|
|
||||||
observation: &crate::DcApiDecodedObservation,
|
|
||||||
parameters: serde_json::Value,
|
|
||||||
operation: std::string::String,
|
|
||||||
) -> crate::MtApiMaterializerExecutionResult {
|
|
||||||
let profile = parameters.get("transactionProfile").cloned().unwrap_or(serde_json::Value::Null);
|
|
||||||
if !compute_budget_profile_emits(&profile) {
|
|
||||||
return crate::MtApiMaterializerExecutionResult::ignored();
|
|
||||||
}
|
|
||||||
let output = crate::MtApiMaterializedOutput {
|
|
||||||
output_key: "transaction_compute_budget_profile:0".to_string(),
|
|
||||||
family: crate::MdMaterializedEventFamily::ComplianceAudit,
|
|
||||||
payload_json: serde_json::json!({
|
|
||||||
"projectionVersion": 1,
|
|
||||||
"projectionSemantics": "transaction_compute_budget_profile",
|
|
||||||
"domain": "transaction_compute_budget_profile",
|
|
||||||
"operation": operation,
|
|
||||||
"programId": observation.event.program_id.0.clone(),
|
|
||||||
"signature": observation.event.signature.0.clone(),
|
|
||||||
"slot": observation.event.slot.0,
|
|
||||||
"instructionPath": observation.event.instruction_path.0.clone(),
|
|
||||||
"transactionSucceeded": !observation.transaction_failed,
|
|
||||||
"profile": profile,
|
|
||||||
"profileSource": {
|
|
||||||
"surfaceCode": observation.event.surface_code.0.clone(),
|
|
||||||
"entryCode": observation.event.event_name.0.clone(),
|
|
||||||
"eventKey": observation.event_key.clone()
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
tracing::debug!(
|
|
||||||
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
|
|
||||||
action = "materialize_compute_budget_profile",
|
|
||||||
surface_code = %observation.event.surface_code.0.as_str(),
|
|
||||||
entry_code = %observation.event.event_name.0.as_str(),
|
|
||||||
output_count = 1_usize,
|
|
||||||
transaction_failed = observation.transaction_failed,
|
|
||||||
"materialize native Compute Budget transaction profile"
|
|
||||||
);
|
|
||||||
return crate::MtApiMaterializerExecutionResult {
|
|
||||||
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
|
|
||||||
outputs: std::vec![output],
|
|
||||||
diagnostics: std::vec::Vec::new(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_budget_profile_emits(profile: &serde_json::Value) -> bool {
|
|
||||||
return profile
|
|
||||||
.get("currentInstructionIsProfileEmitter")
|
|
||||||
.and_then(serde_json::Value::as_bool)
|
|
||||||
== std::option::Option::Some(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn projection(surface_code: &str, entry_code: &str) -> std::option::Option<ProjectionKind> {
|
|
||||||
if surface_code == COMPUTE_BUDGET_SURFACE && contains(COMPUTE_BUDGET_ENTRIES, entry_code) {
|
|
||||||
return std::option::Option::Some(ProjectionKind::ComputeBudgetProfile);
|
|
||||||
}
|
|
||||||
if contains(WRITE_ENTRIES, entry_code) {
|
|
||||||
for supported in [
|
|
||||||
BPF_LOADER_DEPRECATED_SURFACE,
|
|
||||||
BPF_LOADER_SURFACE,
|
|
||||||
BPF_LOADER_UPGRADEABLE_SURFACE,
|
|
||||||
LOADER_V4_SURFACE,
|
|
||||||
] {
|
|
||||||
if surface_code == supported {
|
|
||||||
return std::option::Option::Some(ProjectionKind::ProgramByteWrite(supported));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if surface_code == LOADER_V4_SURFACE && contains(LOADER_V4_COPY_ENTRIES, entry_code) {
|
|
||||||
return std::option::Option::Some(ProjectionKind::ProgramByteCopy);
|
|
||||||
}
|
|
||||||
return std::option::Option::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn projection_details(
|
|
||||||
projection: ProjectionKind,
|
|
||||||
accounts: &serde_json::Value,
|
|
||||||
parameters: &serde_json::Value,
|
|
||||||
) -> serde_json::Value {
|
|
||||||
return match projection {
|
|
||||||
ProjectionKind::ProgramByteWrite(_) => serde_json::json!({
|
|
||||||
"targetAccount": first_account_key_for_roles(
|
|
||||||
accounts,
|
|
||||||
&["program_account", "buffer_account"],
|
|
||||||
),
|
|
||||||
"authorityAccount": first_account_key_for_roles(
|
|
||||||
accounts,
|
|
||||||
&["authority", "buffer_authority"],
|
|
||||||
),
|
|
||||||
"offset": parameters.get("offset").cloned().unwrap_or(serde_json::Value::Null),
|
|
||||||
"byteLength": parameters.get("byteLength").cloned().unwrap_or(serde_json::Value::Null),
|
|
||||||
"bytesSha256": parameters.get("bytesSha256").cloned().unwrap_or(serde_json::Value::Null),
|
|
||||||
"bytesPrefixHex": parameters.get("bytesPrefixHex").cloned().unwrap_or(serde_json::Value::Null),
|
|
||||||
"stateTransition": "program_bytes_written",
|
|
||||||
"codeBytesMaterialized": false,
|
|
||||||
"transactionFinalAccountStateCaptured": false,
|
|
||||||
}),
|
|
||||||
ProjectionKind::ProgramByteCopy => serde_json::json!({
|
|
||||||
"targetAccount": account_key_for_role(accounts, "program_account"),
|
|
||||||
"authorityAccount": account_key_for_role(accounts, "authority"),
|
|
||||||
"sourceProgramAccount": account_key_for_role(accounts, "source_program_account"),
|
|
||||||
"destinationOffset": parameters.get("destinationOffset").cloned().unwrap_or(serde_json::Value::Null),
|
|
||||||
"sourceOffset": parameters.get("sourceOffset").cloned().unwrap_or(serde_json::Value::Null),
|
|
||||||
"byteLength": parameters.get("length").cloned().unwrap_or(serde_json::Value::Null),
|
|
||||||
"stateTransition": "program_bytes_copied",
|
|
||||||
"codeBytesMaterialized": false,
|
|
||||||
"transactionFinalAccountStateCaptured": false,
|
|
||||||
}),
|
|
||||||
ProjectionKind::ComputeBudgetProfile => serde_json::Value::Null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn first_account_key_for_roles(accounts: &serde_json::Value, roles: &[&str]) -> serde_json::Value {
|
|
||||||
for role in roles {
|
|
||||||
let value = account_key_for_role(accounts, role);
|
|
||||||
if !value.is_null() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return serde_json::Value::Null;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn account_key_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value {
|
|
||||||
let account_key = accounts.as_array().and_then(|values| {
|
|
||||||
return values.iter().find_map(|value| {
|
|
||||||
if value.get("role").and_then(serde_json::Value::as_str)
|
|
||||||
!= std::option::Option::Some(role)
|
|
||||||
{
|
|
||||||
return std::option::Option::None;
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
.get("accountKey")
|
|
||||||
.and_then(serde_json::Value::as_str)
|
|
||||||
.map(|account_key| return account_key.to_string());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return match account_key {
|
|
||||||
std::option::Option::Some(value) => serde_json::Value::String(value),
|
|
||||||
std::option::Option::None => serde_json::Value::Null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn contains(entries: &[&str], entry_code: &str) -> bool {
|
|
||||||
return entries.iter().any(|entry| return *entry == entry_code);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
fn observation(
|
|
||||||
surface_code: &str,
|
|
||||||
entry_code: &str,
|
|
||||||
transaction_failed: bool,
|
|
||||||
accounts: serde_json::Value,
|
|
||||||
parameters: serde_json::Value,
|
|
||||||
) -> crate::DcApiDecodedObservation {
|
|
||||||
return crate::DcApiDecodedObservation {
|
|
||||||
event_key: format!("{entry_code}:0"),
|
|
||||||
event: crate::MdDecodedProtocolEvent {
|
|
||||||
signature: crate::MdSignature("signature".to_string()),
|
|
||||||
slot: crate::MdSlot(42),
|
|
||||||
instruction_path: crate::MdInstructionPath("0".to_string()),
|
|
||||||
program_id: crate::MdProgramId("program-id".to_string()),
|
|
||||||
protocol_code: crate::MdProtocolCode("solana.core".to_string()),
|
|
||||||
surface_code: crate::MdSurfaceCode(surface_code.to_string()),
|
|
||||||
event_code: crate::MdEventCode(format!("{surface_code}.{entry_code}")),
|
|
||||||
event_name: crate::MdEventName(entry_code.to_string()),
|
|
||||||
event_family: crate::MdEventFamily::Audit,
|
|
||||||
source_kind: crate::MdEventSourceKind::Instruction,
|
|
||||||
confidence: crate::MdDecoderConfidence::ManualExact,
|
|
||||||
},
|
|
||||||
payload_json: serde_json::json!({"accounts": accounts, "parameters": parameters}),
|
|
||||||
transaction_failed,
|
|
||||||
transaction_error: if transaction_failed {
|
|
||||||
std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]}))
|
|
||||||
} else {
|
|
||||||
std::option::Option::None
|
|
||||||
},
|
|
||||||
observation_committed: !transaction_failed,
|
|
||||||
proof: crate::DcApiDecoderProof {
|
|
||||||
kind: crate::DcApiDecoderProofKind::Manual,
|
|
||||||
confidence: crate::MdDecoderConfidence::ManualExact,
|
|
||||||
evidence: std::vec!["fixture".to_string()],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn account(role: &str, account_key: &str) -> serde_json::Value {
|
|
||||||
return serde_json::json!({"role": role, "accountKey": account_key});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn loader_write_variants_create_bounded_code_audits() {
|
|
||||||
let materializer = crate::MtComplianceAuditMaterializer;
|
|
||||||
for (surface, target_role, authority_role) in [
|
|
||||||
(super::BPF_LOADER_DEPRECATED_SURFACE, "program_account", "missing"),
|
|
||||||
(super::BPF_LOADER_SURFACE, "program_account", "missing"),
|
|
||||||
(super::BPF_LOADER_UPGRADEABLE_SURFACE, "buffer_account", "buffer_authority"),
|
|
||||||
(super::LOADER_V4_SURFACE, "program_account", "authority"),
|
|
||||||
] {
|
|
||||||
let observation = observation(
|
|
||||||
surface,
|
|
||||||
"write",
|
|
||||||
false,
|
|
||||||
serde_json::json!([
|
|
||||||
account(target_role, "target111"),
|
|
||||||
account(authority_role, "authority111"),
|
|
||||||
]),
|
|
||||||
serde_json::json!({
|
|
||||||
"offset": 7,
|
|
||||||
"byteLength": 32,
|
|
||||||
"bytesSha256": "abc",
|
|
||||||
"bytesPrefixHex": "0102",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
|
|
||||||
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
|
|
||||||
assert_eq!(result.outputs[0].family, crate::MdMaterializedEventFamily::ComplianceAudit);
|
|
||||||
assert_eq!(result.outputs[0].payload_json["projection"]["targetAccount"], "target111");
|
|
||||||
assert_eq!(result.outputs[0].payload_json["projection"]["bytesSha256"], "abc");
|
|
||||||
assert_eq!(
|
|
||||||
result.outputs[0].payload_json["projection"]["codeBytesMaterialized"],
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn loader_v4_copy_preserves_source_and_bounded_ranges() {
|
|
||||||
let materializer = crate::MtComplianceAuditMaterializer;
|
|
||||||
let observation = observation(
|
|
||||||
super::LOADER_V4_SURFACE,
|
|
||||||
"copy",
|
|
||||||
false,
|
|
||||||
serde_json::json!([
|
|
||||||
account("program_account", "target111"),
|
|
||||||
account("authority", "authority111"),
|
|
||||||
account("source_program_account", "source111"),
|
|
||||||
]),
|
|
||||||
serde_json::json!({"destinationOffset": 8, "sourceOffset": 4, "length": 16}),
|
|
||||||
);
|
|
||||||
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
|
|
||||||
assert_eq!(
|
|
||||||
result.outputs[0].output_key,
|
|
||||||
"program_loader_code:solana.core.loader_v4:copy:0"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
result.outputs[0].payload_json["projection"]["sourceProgramAccount"],
|
|
||||||
"source111"
|
|
||||||
);
|
|
||||||
assert_eq!(result.outputs[0].payload_json["projection"]["byteLength"], 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn compute_budget_profile_is_emitted_only_by_profile_emitter() {
|
|
||||||
let materializer = crate::MtComplianceAuditMaterializer;
|
|
||||||
let emitter = observation(
|
|
||||||
super::COMPUTE_BUDGET_SURFACE,
|
|
||||||
"set_compute_unit_limit",
|
|
||||||
false,
|
|
||||||
serde_json::json!([]),
|
|
||||||
serde_json::json!({
|
|
||||||
"transactionProfile": {
|
|
||||||
"currentInstructionIsProfileEmitter": true,
|
|
||||||
"profileEmitterInstructionPath": "0",
|
|
||||||
"computeBudgetInstructionCount": 2,
|
|
||||||
"effectiveValues": {
|
|
||||||
"set_compute_unit_limit": {
|
|
||||||
"value": 200000,
|
|
||||||
"sourceInstructionPath": "0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
let emitted = crate::MtApiEventMaterializer::materialize(&materializer, &emitter);
|
|
||||||
assert_eq!(emitted.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
|
|
||||||
assert_eq!(emitted.outputs[0].output_key, "transaction_compute_budget_profile:0");
|
|
||||||
assert_eq!(emitted.outputs[0].family, crate::MdMaterializedEventFamily::ComplianceAudit);
|
|
||||||
assert_eq!(emitted.outputs[0].payload_json["profile"]["computeBudgetInstructionCount"], 2);
|
|
||||||
let non_emitter = observation(
|
|
||||||
super::COMPUTE_BUDGET_SURFACE,
|
|
||||||
"set_compute_unit_price",
|
|
||||||
false,
|
|
||||||
serde_json::json!([]),
|
|
||||||
serde_json::json!({
|
|
||||||
"transactionProfile": {
|
|
||||||
"currentInstructionIsProfileEmitter": false,
|
|
||||||
"profileEmitterInstructionPath": "0"
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
let ignored = crate::MtApiEventMaterializer::materialize(&materializer, &non_emitter);
|
|
||||||
assert_eq!(ignored.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn failed_compute_budget_profile_remains_audit_materializable() {
|
|
||||||
let materializer = crate::MtComplianceAuditMaterializer;
|
|
||||||
let observation = observation(
|
|
||||||
super::COMPUTE_BUDGET_SURFACE,
|
|
||||||
"set_compute_unit_price",
|
|
||||||
true,
|
|
||||||
serde_json::json!([]),
|
|
||||||
serde_json::json!({
|
|
||||||
"transactionProfile": {
|
|
||||||
"currentInstructionIsProfileEmitter": true,
|
|
||||||
"profileEmitterInstructionPath": "0",
|
|
||||||
"computeBudgetInstructionCount": 1
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
|
|
||||||
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
|
|
||||||
assert_eq!(result.outputs[0].payload_json["transactionSucceeded"], false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn failed_code_mutation_is_refused() {
|
|
||||||
let materializer = crate::MtComplianceAuditMaterializer;
|
|
||||||
let observation = observation(
|
|
||||||
super::LOADER_V4_SURFACE,
|
|
||||||
"write",
|
|
||||||
true,
|
|
||||||
serde_json::json!([account("program_account", "target111")]),
|
|
||||||
serde_json::json!({"offset": 0, "byteLength": 1, "bytesSha256": "abc"}),
|
|
||||||
);
|
|
||||||
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
|
|
||||||
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
|
|
||||||
assert!(result.outputs.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unrelated_audit_observation_is_ignored() {
|
|
||||||
let materializer = crate::MtComplianceAuditMaterializer;
|
|
||||||
let observation = observation(
|
|
||||||
"other_surface",
|
|
||||||
"write",
|
|
||||||
false,
|
|
||||||
serde_json::json!([]),
|
|
||||||
serde_json::json!({}),
|
|
||||||
);
|
|
||||||
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
|
|
||||||
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn output_serialization_is_deterministic() {
|
|
||||||
let materializer = crate::MtComplianceAuditMaterializer;
|
|
||||||
let observation = observation(
|
|
||||||
super::BPF_LOADER_UPGRADEABLE_SURFACE,
|
|
||||||
"write",
|
|
||||||
false,
|
|
||||||
serde_json::json!([
|
|
||||||
account("buffer_account", "buffer111"),
|
|
||||||
account("buffer_authority", "authority111"),
|
|
||||||
]),
|
|
||||||
serde_json::json!({
|
|
||||||
"offset": 0,
|
|
||||||
"byteLength": 2,
|
|
||||||
"bytesSha256": "abc",
|
|
||||||
"bytesPrefixHex": "0102",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
let first = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
|
|
||||||
let second = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
|
|
||||||
let first_json = match serde_json::to_string(&first) {
|
|
||||||
std::result::Result::Ok(value) => value,
|
|
||||||
std::result::Result::Err(error) => {
|
|
||||||
panic!("first compliance serialization failed: {error}")
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let second_json = match serde_json::to_string(&second) {
|
|
||||||
std::result::Result::Ok(value) => value,
|
|
||||||
std::result::Result::Err(error) => {
|
|
||||||
panic!("second compliance serialization failed: {error}")
|
|
||||||
},
|
|
||||||
};
|
|
||||||
assert_eq!(first_json, second_json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
42
kb-lib/src/materializer/compliance/audit/constants.rs
Normal file
42
kb-lib/src/materializer/compliance/audit/constants.rs
Normal 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"];
|
||||||
581
kb-lib/src/materializer/compliance/audit/materializer.rs
Normal file
581
kb-lib/src/materializer/compliance/audit/materializer.rs
Normal 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, ¶meters),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
tracing::debug!(
|
||||||
|
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
|
||||||
|
action = "materialize_compliance_audit",
|
||||||
|
surface_code = %observation.event.surface_code.0.as_str(),
|
||||||
|
entry_code = %observation.event.event_name.0.as_str(),
|
||||||
|
loader_surface = projection.loader_surface(),
|
||||||
|
output_count = 1_usize,
|
||||||
|
"materialize committed native bytecode mutation observation"
|
||||||
|
);
|
||||||
|
return crate::MtApiMaterializerExecutionResult {
|
||||||
|
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
|
||||||
|
outputs: std::vec![output],
|
||||||
|
diagnostics: std::vec::Vec::new(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn materialize_compute_budget_profile(
|
||||||
|
observation: &crate::DcApiDecodedObservation,
|
||||||
|
parameters: serde_json::Value,
|
||||||
|
operation: std::string::String,
|
||||||
|
) -> crate::MtApiMaterializerExecutionResult {
|
||||||
|
let profile = parameters.get("transactionProfile").cloned().unwrap_or(serde_json::Value::Null);
|
||||||
|
if !compute_budget_profile_emits(&profile) {
|
||||||
|
return crate::MtApiMaterializerExecutionResult::ignored();
|
||||||
|
}
|
||||||
|
let output = crate::MtApiMaterializedOutput {
|
||||||
|
output_key: "transaction_compute_budget_profile:0".to_string(),
|
||||||
|
family: crate::MdMaterializedEventFamily::ComplianceAudit,
|
||||||
|
payload_json: serde_json::json!({
|
||||||
|
"projectionVersion": 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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";
|
|
||||||
@@ -1,11 +1,27 @@
|
|||||||
// file: kb-lib/src/materializer/fees.rs
|
// file: kb-lib/src/materializer/fees.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
//! `fees` materializer family.
|
//! `fees` materializer family.
|
||||||
|
|
||||||
mod core;
|
mod constants;
|
||||||
|
mod materializer;
|
||||||
|
|
||||||
/// Fee materializer for committed Token-2022 fee observations.
|
/// 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.
|
/// 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;
|
||||||
|
|||||||
34
kb-lib/src/materializer/fees/constants.rs
Normal file
34
kb-lib/src/materializer/fees/constants.rs
Normal 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",
|
||||||
|
];
|
||||||
@@ -1,33 +1,15 @@
|
|||||||
// file: kb-lib/src/materializer/fees/core.rs
|
// file: kb-lib/src/materializer/fees/materializer.rs
|
||||||
// version: 5
|
// version: 8
|
||||||
|
|
||||||
//! Stable fee projections derived from exact committed Token-2022 observations.
|
//! 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.
|
/// Fee materializer for committed Token-2022 fee instructions.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct MtFeesMaterializer;
|
pub struct MtFeesMaterializer;
|
||||||
|
|
||||||
impl crate::MtMaterializer for crate::MtFeesMaterializer {
|
impl crate::MtMaterializer for crate::MtFeesMaterializer {
|
||||||
fn materializer_name(&self) -> &'static str {
|
fn materializer_name(&self) -> &'static str {
|
||||||
return "kb-lib.materializer.fees";
|
return crate::MT_FEES_COMPONENT_NAME;
|
||||||
}
|
}
|
||||||
fn materializer_version(&self) -> &'static str {
|
fn materializer_version(&self) -> &'static str {
|
||||||
return env!("CARGO_PKG_VERSION");
|
return env!("CARGO_PKG_VERSION");
|
||||||
@@ -46,12 +28,12 @@ impl crate::MtMaterializer for crate::MtFeesMaterializer {
|
|||||||
impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
|
impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
|
||||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||||
return 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(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||||
return ACCEPTED_FAMILIES;
|
return crate::MT_FEES_ACCEPTED_FAMILIES;
|
||||||
}
|
}
|
||||||
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
|
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
|
||||||
return accepts_event(&observation.event);
|
return accepts_event(&observation.event);
|
||||||
@@ -67,9 +49,35 @@ impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
|
|||||||
observation: &crate::DcApiDecodedObservation,
|
observation: &crate::DcApiDecodedObservation,
|
||||||
) -> crate::MtApiMaterializerExecutionResult {
|
) -> crate::MtApiMaterializerExecutionResult {
|
||||||
if !accepts_event(&observation.event) {
|
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();
|
return crate::MtApiMaterializerExecutionResult::ignored();
|
||||||
}
|
}
|
||||||
if observation.transaction_failed || !observation.observation_committed {
|
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(
|
return crate::MtApiMaterializerExecutionResult::refused(
|
||||||
"failed_transaction_fee_refused",
|
"failed_transaction_fee_refused",
|
||||||
"failed or uncommitted Token-2022 observations cannot create fee outputs",
|
"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"),
|
output_key: format!("token_2022_fee:{operation}:0"),
|
||||||
family: crate::MdMaterializedEventFamily::Fee,
|
family: crate::MdMaterializedEventFamily::Fee,
|
||||||
payload_json: serde_json::json!({
|
payload_json: serde_json::json!({
|
||||||
"projectionVersion": 1,
|
"projectionVersion": crate::MT_FEES_PROJECTION_VERSION,
|
||||||
"projectionSemantics": "committed_instruction_fee_event",
|
"projectionSemantics": "committed_instruction_fee_event",
|
||||||
"domain": fee_domain(observation.event.event_name.0.as_str()),
|
"domain": fee_domain(observation.event.event_name.0.as_str()),
|
||||||
"operation": operation,
|
"operation": operation,
|
||||||
@@ -99,6 +107,10 @@ impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
|
|||||||
"slot": observation.event.slot.0,
|
"slot": observation.event.slot.0,
|
||||||
"instructionPath": observation.event.instruction_path.0.clone(),
|
"instructionPath": observation.event.instruction_path.0.clone(),
|
||||||
"transactionSucceeded": true,
|
"transactionSucceeded": true,
|
||||||
|
"provenance": {
|
||||||
|
"processorName": crate::MT_FEES_PROCESSOR_NAME,
|
||||||
|
"processorVersion": env!("CARGO_PKG_VERSION"),
|
||||||
|
},
|
||||||
"accounts": accounts,
|
"accounts": accounts,
|
||||||
"parameters": parameters,
|
"parameters": parameters,
|
||||||
"stateReadRequired": state_read_required(
|
"stateReadRequired": state_read_required(
|
||||||
@@ -107,6 +119,21 @@ impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
|
|||||||
"confidentialValuesDecrypted": false,
|
"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 {
|
return crate::MtApiMaterializerExecutionResult {
|
||||||
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
|
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
|
||||||
outputs: std::vec![output],
|
outputs: std::vec![output],
|
||||||
@@ -117,9 +144,9 @@ impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
|
|||||||
|
|
||||||
fn accepts_event(event: &crate::MdDecodedProtocolEvent) -> bool {
|
fn accepts_event(event: &crate::MdDecodedProtocolEvent) -> bool {
|
||||||
return event.event_family == crate::MdEventFamily::Fee
|
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
|
&& 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 {
|
fn fee_domain(operation: &str) -> &'static str {
|
||||||
@@ -195,7 +222,7 @@ pub fn materializer_fees_materialize_token_2022_state_snapshots(
|
|||||||
),
|
),
|
||||||
family: crate::MdMaterializedEventFamily::Fee,
|
family: crate::MdMaterializedEventFamily::Fee,
|
||||||
payload_json: serde_json::json!({
|
payload_json: serde_json::json!({
|
||||||
"projectionVersion": 1,
|
"projectionVersion": crate::MT_FEES_PROJECTION_VERSION,
|
||||||
"projectionSemantics": "authoritative_bounded_fee_state_snapshot",
|
"projectionSemantics": "authoritative_bounded_fee_state_snapshot",
|
||||||
"domain": if confidential {
|
"domain": if confidential {
|
||||||
"token_2022_confidential_transfer_fee_state"
|
"token_2022_confidential_transfer_fee_state"
|
||||||
@@ -218,13 +245,27 @@ pub fn materializer_fees_materialize_token_2022_state_snapshots(
|
|||||||
"confidentialValuesDecrypted": false,
|
"confidentialValuesDecrypted": false,
|
||||||
"finalAccountStateCaptured": true,
|
"finalAccountStateCaptured": true,
|
||||||
"provenance": {
|
"provenance": {
|
||||||
"processorName": "materializer.fees",
|
"processorName": crate::MT_FEES_PROCESSOR_NAME,
|
||||||
"processorVersion": env!("CARGO_PKG_VERSION"),
|
"processorVersion": env!("CARGO_PKG_VERSION"),
|
||||||
"source": "bounded_token_2022_state_parser"
|
"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);
|
return std::result::Result::Ok(outputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +314,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn committed_token_2022_fee_instruction_creates_one_stable_output() {
|
fn committed_token_2022_fee_instruction_creates_one_stable_output() {
|
||||||
let observation = observation(
|
let observation = observation(
|
||||||
super::SPL_TOKEN_2022_SURFACE,
|
crate::MT_FEES_SPL_TOKEN_2022_SURFACE,
|
||||||
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
|
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
|
||||||
"set_transfer_fee",
|
"set_transfer_fee",
|
||||||
false,
|
false,
|
||||||
@@ -290,7 +331,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn failed_token_2022_fee_instruction_is_refused() {
|
fn failed_token_2022_fee_instruction_is_refused() {
|
||||||
let observation = observation(
|
let observation = observation(
|
||||||
super::SPL_TOKEN_2022_SURFACE,
|
crate::MT_FEES_SPL_TOKEN_2022_SURFACE,
|
||||||
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
|
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
|
||||||
"harvest_withheld_tokens_to_mint",
|
"harvest_withheld_tokens_to_mint",
|
||||||
true,
|
true,
|
||||||
@@ -310,7 +351,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
let wrong_program = observation(
|
let wrong_program = observation(
|
||||||
super::SPL_TOKEN_2022_SURFACE,
|
crate::MT_FEES_SPL_TOKEN_2022_SURFACE,
|
||||||
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
|
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
|
||||||
"set_transfer_fee",
|
"set_transfer_fee",
|
||||||
false,
|
false,
|
||||||
@@ -334,7 +375,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn committed_confidential_fee_instruction_is_projected_without_decryption() {
|
fn committed_confidential_fee_instruction_is_projected_without_decryption() {
|
||||||
let observation = observation(
|
let observation = observation(
|
||||||
super::SPL_TOKEN_2022_SURFACE,
|
crate::MT_FEES_SPL_TOKEN_2022_SURFACE,
|
||||||
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
|
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
|
||||||
"initialize_confidential_transfer_fee_config",
|
"initialize_confidential_transfer_fee_config",
|
||||||
false,
|
false,
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
// file: kb-lib/src/materializer/governance.rs
|
// file: kb-lib/src/materializer/governance.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! `governance` materializer family.
|
//! `governance` materializer family.
|
||||||
|
|
||||||
mod core;
|
mod constants;
|
||||||
|
mod materializer;
|
||||||
|
|
||||||
/// Reserved governance 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;
|
||||||
|
|||||||
11
kb-lib/src/materializer/governance/constants.rs
Normal file
11
kb-lib/src/materializer/governance/constants.rs
Normal 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] = &[];
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/materializer/governance/core.rs
|
// file: kb-lib/src/materializer/governance/materializer.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
//! Reserved governance materializer.
|
//! Reserved governance materializer.
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ pub struct MtGovernanceMaterializer;
|
|||||||
|
|
||||||
impl crate::MtMaterializer for crate::MtGovernanceMaterializer {
|
impl crate::MtMaterializer for crate::MtGovernanceMaterializer {
|
||||||
fn materializer_name(&self) -> &'static str {
|
fn materializer_name(&self) -> &'static str {
|
||||||
return "kb-lib.materializer.governance";
|
return crate::MT_GOVERNANCE_COMPONENT_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn materializer_version(&self) -> &'static str {
|
fn materializer_version(&self) -> &'static str {
|
||||||
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtGovernanceMaterializer {
|
|||||||
|
|
||||||
fn materialize_event(
|
fn materialize_event(
|
||||||
&self,
|
&self,
|
||||||
event: &crate::MdDecodedProtocolEvent,
|
_event: &crate::MdDecodedProtocolEvent,
|
||||||
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
||||||
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
|
return std::result::Result::Ok(std::vec::Vec::new());
|
||||||
signature: event.signature.clone(),
|
|
||||||
slot: event.slot,
|
|
||||||
decoded_family: event.event_family,
|
|
||||||
materialized_family: crate::MdMaterializedEventFamily::Governance,
|
|
||||||
}]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::MtApiEventMaterializer for crate::MtGovernanceMaterializer {
|
impl crate::MtApiEventMaterializer for crate::MtGovernanceMaterializer {
|
||||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||||
return 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(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||||
return &[];
|
return crate::MT_GOVERNANCE_ACCEPTED_FAMILIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_policy(
|
fn transaction_policy(
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
// file: kb-lib/src/materializer/lending.rs
|
// file: kb-lib/src/materializer/lending.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! `lending` materializer family.
|
//! `lending` materializer family.
|
||||||
|
|
||||||
mod core;
|
mod constants;
|
||||||
|
mod materializer;
|
||||||
|
|
||||||
/// Reserved lending 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;
|
||||||
|
|||||||
11
kb-lib/src/materializer/lending/constants.rs
Normal file
11
kb-lib/src/materializer/lending/constants.rs
Normal 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] = &[];
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/materializer/lending/core.rs
|
// file: kb-lib/src/materializer/lending/materializer.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
//! Reserved lending materializer.
|
//! Reserved lending materializer.
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ pub struct MtLendingMaterializer;
|
|||||||
|
|
||||||
impl crate::MtMaterializer for crate::MtLendingMaterializer {
|
impl crate::MtMaterializer for crate::MtLendingMaterializer {
|
||||||
fn materializer_name(&self) -> &'static str {
|
fn materializer_name(&self) -> &'static str {
|
||||||
return "kb-lib.materializer.lending";
|
return crate::MT_LENDING_COMPONENT_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn materializer_version(&self) -> &'static str {
|
fn materializer_version(&self) -> &'static str {
|
||||||
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtLendingMaterializer {
|
|||||||
|
|
||||||
fn materialize_event(
|
fn materialize_event(
|
||||||
&self,
|
&self,
|
||||||
event: &crate::MdDecodedProtocolEvent,
|
_event: &crate::MdDecodedProtocolEvent,
|
||||||
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
||||||
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
|
return std::result::Result::Ok(std::vec::Vec::new());
|
||||||
signature: event.signature.clone(),
|
|
||||||
slot: event.slot,
|
|
||||||
decoded_family: event.event_family,
|
|
||||||
materialized_family: crate::MdMaterializedEventFamily::Lending,
|
|
||||||
}]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::MtApiEventMaterializer for crate::MtLendingMaterializer {
|
impl crate::MtApiEventMaterializer for crate::MtLendingMaterializer {
|
||||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||||
return 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(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||||
return &[];
|
return crate::MT_LENDING_ACCEPTED_FAMILIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_policy(
|
fn transaction_policy(
|
||||||
@@ -1,13 +1,59 @@
|
|||||||
// file: kb-lib/src/materializer/lifecycle.rs
|
// file: kb-lib/src/materializer/lifecycle.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
//! `lifecycle` materializer family.
|
//! `lifecycle` materializer family.
|
||||||
|
|
||||||
mod constants;
|
mod constants;
|
||||||
mod core;
|
mod materializer;
|
||||||
|
|
||||||
/// Stable native lifecycle 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;
|
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_LIFECYCLE;
|
||||||
|
|||||||
@@ -1,7 +1,100 @@
|
|||||||
// file: kb-lib/src/materializer/lifecycle/constants.rs
|
// 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";
|
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",
|
||||||
|
];
|
||||||
|
|||||||
@@ -1,70 +1,8 @@
|
|||||||
// file: kb-lib/src/materializer/lifecycle/core.rs
|
// file: kb-lib/src/materializer/lifecycle/materializer.rs
|
||||||
// version: 16
|
// version: 18
|
||||||
|
|
||||||
//! Stable lifecycle projections derived from exact decoded observations.
|
//! 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)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
enum ProjectionKind {
|
enum ProjectionKind {
|
||||||
AddressLookupTable,
|
AddressLookupTable,
|
||||||
@@ -125,7 +63,7 @@ pub struct MtLifecycleMaterializer;
|
|||||||
|
|
||||||
impl crate::MtMaterializer for crate::MtLifecycleMaterializer {
|
impl crate::MtMaterializer for crate::MtLifecycleMaterializer {
|
||||||
fn materializer_name(&self) -> &'static str {
|
fn materializer_name(&self) -> &'static str {
|
||||||
return "kb-lib.materializer.lifecycle";
|
return crate::MT_LIFECYCLE_COMPONENT_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn materializer_version(&self) -> &'static str {
|
fn materializer_version(&self) -> &'static str {
|
||||||
@@ -152,13 +90,13 @@ impl crate::MtMaterializer for crate::MtLifecycleMaterializer {
|
|||||||
impl crate::MtApiEventMaterializer for crate::MtLifecycleMaterializer {
|
impl crate::MtApiEventMaterializer for crate::MtLifecycleMaterializer {
|
||||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||||
return 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(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||||
return ACCEPTED_FAMILIES;
|
return crate::MT_LIFECYCLE_ACCEPTED_FAMILIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
|
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
|
||||||
@@ -239,7 +177,7 @@ impl crate::MtApiEventMaterializer for crate::MtLifecycleMaterializer {
|
|||||||
output_key,
|
output_key,
|
||||||
family: crate::MdMaterializedEventFamily::Lifecycle,
|
family: crate::MdMaterializedEventFamily::Lifecycle,
|
||||||
payload_json: serde_json::json!({
|
payload_json: serde_json::json!({
|
||||||
"projectionVersion": 1,
|
"projectionVersion": crate::MT_LIFECYCLE_PROJECTION_VERSION,
|
||||||
"projectionSemantics": "committed_instruction_lifecycle_event",
|
"projectionSemantics": "committed_instruction_lifecycle_event",
|
||||||
"domain": domain,
|
"domain": domain,
|
||||||
"loaderSurface": loader_surface,
|
"loaderSurface": loader_surface,
|
||||||
@@ -249,6 +187,10 @@ impl crate::MtApiEventMaterializer for crate::MtLifecycleMaterializer {
|
|||||||
"slot": observation.event.slot.0,
|
"slot": observation.event.slot.0,
|
||||||
"instructionPath": observation.event.instruction_path.0.clone(),
|
"instructionPath": observation.event.instruction_path.0.clone(),
|
||||||
"transactionSucceeded": true,
|
"transactionSucceeded": true,
|
||||||
|
"provenance": {
|
||||||
|
"processorName": crate::MT_LIFECYCLE_PROCESSOR_NAME,
|
||||||
|
"processorVersion": env!("CARGO_PKG_VERSION"),
|
||||||
|
},
|
||||||
"accounts": accounts,
|
"accounts": accounts,
|
||||||
"parameters": parameters,
|
"parameters": parameters,
|
||||||
"projection": projection_details,
|
"projection": projection_details,
|
||||||
@@ -263,7 +205,9 @@ impl crate::MtApiEventMaterializer for crate::MtLifecycleMaterializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn family_is_accepted(family: crate::MdEventFamily) -> bool {
|
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(
|
fn observation_projection(
|
||||||
@@ -275,8 +219,8 @@ fn observation_projection(
|
|||||||
if static_projection.is_some() {
|
if static_projection.is_some() {
|
||||||
return static_projection;
|
return static_projection;
|
||||||
}
|
}
|
||||||
if surface_code == ZK_ELGAMAL_PROOF_SURFACE
|
if surface_code == crate::MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE
|
||||||
&& contains(ZK_ELGAMAL_VERIFY_ENTRIES, entry_code)
|
&& contains(crate::MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES, entry_code)
|
||||||
&& observation
|
&& observation
|
||||||
.payload_json
|
.payload_json
|
||||||
.get("parameters")
|
.get("parameters")
|
||||||
@@ -290,48 +234,63 @@ fn observation_projection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn static_projection(surface_code: &str, entry_code: &str) -> std::option::Option<ProjectionKind> {
|
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);
|
return std::option::Option::Some(ProjectionKind::FeatureGate);
|
||||||
}
|
}
|
||||||
if surface_code == ADDRESS_LOOKUP_TABLE_SURFACE
|
if surface_code == crate::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE
|
||||||
&& contains(ADDRESS_LOOKUP_TABLE_ENTRIES, entry_code)
|
&& contains(crate::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES, entry_code)
|
||||||
{
|
{
|
||||||
return std::option::Option::Some(ProjectionKind::AddressLookupTable);
|
return std::option::Option::Some(ProjectionKind::AddressLookupTable);
|
||||||
}
|
}
|
||||||
if (surface_code == BPF_LOADER_DEPRECATED_SURFACE || surface_code == BPF_LOADER_SURFACE)
|
if (surface_code == crate::MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE
|
||||||
&& contains(IMMUTABLE_LOADER_ENTRIES, entry_code)
|
|| 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 {
|
let loader_surface = if surface_code == crate::MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE {
|
||||||
BPF_LOADER_DEPRECATED_SURFACE
|
crate::MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE
|
||||||
} else {
|
} else {
|
||||||
BPF_LOADER_SURFACE
|
crate::MT_LIFECYCLE_BPF_LOADER_SURFACE
|
||||||
};
|
};
|
||||||
return std::option::Option::Some(ProjectionKind::ProgramLoader(loader_surface));
|
return std::option::Option::Some(ProjectionKind::ProgramLoader(loader_surface));
|
||||||
}
|
}
|
||||||
if surface_code == BPF_LOADER_UPGRADEABLE_SURFACE
|
if surface_code == crate::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE
|
||||||
&& contains(UPGRADEABLE_LOADER_ENTRIES, entry_code)
|
&& contains(crate::MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES, entry_code)
|
||||||
{
|
{
|
||||||
return std::option::Option::Some(ProjectionKind::ProgramLoader(
|
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) {
|
if surface_code == crate::MT_LIFECYCLE_LOADER_V4_SURFACE
|
||||||
return std::option::Option::Some(ProjectionKind::ProgramLoader(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" {
|
return if entry_code == "duplicate_block_proof" {
|
||||||
std::option::Option::Some(ProjectionKind::SlashingViolationReportInitialization)
|
std::option::Option::Some(ProjectionKind::SlashingViolationReportInitialization)
|
||||||
} else {
|
} else {
|
||||||
std::option::Option::Some(ProjectionKind::SlashingViolationReportClosure)
|
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);
|
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);
|
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::Some(ProjectionKind::ZkProofContextClosure);
|
||||||
}
|
}
|
||||||
return std::option::Option::None;
|
return std::option::Option::None;
|
||||||
@@ -539,7 +498,7 @@ mod tests {
|
|||||||
transaction_failed: bool,
|
transaction_failed: bool,
|
||||||
) -> crate::DcApiDecodedObservation {
|
) -> crate::DcApiDecodedObservation {
|
||||||
return observation_with_payload(
|
return observation_with_payload(
|
||||||
super::SYSTEM_SURFACE,
|
crate::MT_LIFECYCLE_SYSTEM_SURFACE,
|
||||||
entry_code,
|
entry_code,
|
||||||
family,
|
family,
|
||||||
transaction_failed,
|
transaction_failed,
|
||||||
@@ -579,7 +538,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
return observation_with_payload(
|
return observation_with_payload(
|
||||||
super::SLASHING_SURFACE,
|
crate::MT_LIFECYCLE_SLASHING_SURFACE,
|
||||||
entry_code,
|
entry_code,
|
||||||
family,
|
family,
|
||||||
transaction_failed,
|
transaction_failed,
|
||||||
@@ -620,7 +579,7 @@ mod tests {
|
|||||||
crate::MdEventFamily::Audit
|
crate::MdEventFamily::Audit
|
||||||
};
|
};
|
||||||
return observation_with_payload(
|
return observation_with_payload(
|
||||||
super::ZK_ELGAMAL_PROOF_SURFACE,
|
crate::MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE,
|
||||||
entry_code,
|
entry_code,
|
||||||
family,
|
family,
|
||||||
transaction_failed,
|
transaction_failed,
|
||||||
@@ -637,7 +596,11 @@ mod tests {
|
|||||||
let materializer = crate::MtLifecycleMaterializer;
|
let materializer = crate::MtLifecycleMaterializer;
|
||||||
let result = crate::MtApiEventMaterializer::materialize(
|
let result = crate::MtApiEventMaterializer::materialize(
|
||||||
&materializer,
|
&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.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
|
||||||
assert_eq!(result.outputs.len(), 1);
|
assert_eq!(result.outputs.len(), 1);
|
||||||
@@ -651,7 +614,11 @@ mod tests {
|
|||||||
let materializer = crate::MtLifecycleMaterializer;
|
let materializer = crate::MtLifecycleMaterializer;
|
||||||
let result = crate::MtApiEventMaterializer::materialize(
|
let result = crate::MtApiEventMaterializer::materialize(
|
||||||
&materializer,
|
&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!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
|
||||||
assert_eq!(
|
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["domain"], "program_loader");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.outputs[0].payload_json["loaderSurface"],
|
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 materializer = crate::MtLifecycleMaterializer;
|
||||||
let result = crate::MtApiEventMaterializer::materialize(
|
let result = crate::MtApiEventMaterializer::materialize(
|
||||||
&materializer,
|
&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.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
|
||||||
assert_eq!(result.outputs[0].output_key, "feature_gate:revoke_pending_activation:0");
|
assert_eq!(result.outputs[0].output_key, "feature_gate:revoke_pending_activation:0");
|
||||||
@@ -696,7 +663,7 @@ mod tests {
|
|||||||
),
|
),
|
||||||
] {
|
] {
|
||||||
let observation = observation_with_payload(
|
let observation = observation_with_payload(
|
||||||
super::SYSTEM_SURFACE,
|
crate::MT_LIFECYCLE_SYSTEM_SURFACE,
|
||||||
operation,
|
operation,
|
||||||
crate::MdEventFamily::Lifecycle,
|
crate::MdEventFamily::Lifecycle,
|
||||||
false,
|
false,
|
||||||
@@ -724,7 +691,7 @@ mod tests {
|
|||||||
fn system_account_creation_does_not_duplicate_balance_changes() {
|
fn system_account_creation_does_not_duplicate_balance_changes() {
|
||||||
let materializer = crate::MtLifecycleMaterializer;
|
let materializer = crate::MtLifecycleMaterializer;
|
||||||
let observation = observation_with_payload(
|
let observation = observation_with_payload(
|
||||||
super::SYSTEM_SURFACE,
|
crate::MT_LIFECYCLE_SYSTEM_SURFACE,
|
||||||
"create_account",
|
"create_account",
|
||||||
crate::MdEventFamily::Lifecycle,
|
crate::MdEventFamily::Lifecycle,
|
||||||
false,
|
false,
|
||||||
@@ -895,7 +862,8 @@ mod tests {
|
|||||||
fn loader_write_and_admin_events_are_not_lifecycle_projections() {
|
fn loader_write_and_admin_events_are_not_lifecycle_projections() {
|
||||||
let materializer = crate::MtLifecycleMaterializer;
|
let materializer = crate::MtLifecycleMaterializer;
|
||||||
for entry_code in ["write", "set_authority", "transfer_authority"] {
|
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(
|
assert!(!crate::MtApiEventMaterializer::accepts_observation(
|
||||||
&materializer,
|
&materializer,
|
||||||
&observation,
|
&observation,
|
||||||
@@ -908,7 +876,7 @@ mod tests {
|
|||||||
let materializer = crate::MtLifecycleMaterializer;
|
let materializer = crate::MtLifecycleMaterializer;
|
||||||
let result = crate::MtApiEventMaterializer::materialize(
|
let result = crate::MtApiEventMaterializer::materialize(
|
||||||
&materializer,
|
&materializer,
|
||||||
&observation(super::SYSTEM_SURFACE, "transfer", false),
|
&observation(crate::MT_LIFECYCLE_SYSTEM_SURFACE, "transfer", false),
|
||||||
);
|
);
|
||||||
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
|
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
|
||||||
}
|
}
|
||||||
@@ -934,8 +902,12 @@ mod tests {
|
|||||||
fn failed_native_lifecycle_is_refused() {
|
fn failed_native_lifecycle_is_refused() {
|
||||||
let materializer = crate::MtLifecycleMaterializer;
|
let materializer = crate::MtLifecycleMaterializer;
|
||||||
let observations = [
|
let observations = [
|
||||||
observation(super::ADDRESS_LOOKUP_TABLE_SURFACE, "close_lookup_table", true),
|
observation(
|
||||||
observation(super::LOADER_V4_SURFACE, "deploy", true),
|
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),
|
nonce_observation("authorize_nonce_account", crate::MdEventFamily::Admin, true),
|
||||||
zk_observation("verify_zero_ciphertext", true, true),
|
zk_observation("verify_zero_ciphertext", true, true),
|
||||||
slashing_observation("duplicate_block_proof", true),
|
slashing_observation("duplicate_block_proof", true),
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
// file: kb-lib/src/materializer/liquidity.rs
|
// file: kb-lib/src/materializer/liquidity.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! `liquidity` materializer family.
|
//! `liquidity` materializer family.
|
||||||
|
|
||||||
mod core;
|
mod constants;
|
||||||
|
mod materializer;
|
||||||
|
|
||||||
/// Reserved liquidity 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;
|
||||||
|
|||||||
11
kb-lib/src/materializer/liquidity/constants.rs
Normal file
11
kb-lib/src/materializer/liquidity/constants.rs
Normal 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] = &[];
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/materializer/liquidity/core.rs
|
// file: kb-lib/src/materializer/liquidity/materializer.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
//! Reserved liquidity materializer.
|
//! Reserved liquidity materializer.
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ pub struct MtLiquidityMaterializer;
|
|||||||
|
|
||||||
impl crate::MtMaterializer for crate::MtLiquidityMaterializer {
|
impl crate::MtMaterializer for crate::MtLiquidityMaterializer {
|
||||||
fn materializer_name(&self) -> &'static str {
|
fn materializer_name(&self) -> &'static str {
|
||||||
return "kb-lib.materializer.liquidity";
|
return crate::MT_LIQUIDITY_COMPONENT_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn materializer_version(&self) -> &'static str {
|
fn materializer_version(&self) -> &'static str {
|
||||||
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtLiquidityMaterializer {
|
|||||||
|
|
||||||
fn materialize_event(
|
fn materialize_event(
|
||||||
&self,
|
&self,
|
||||||
event: &crate::MdDecodedProtocolEvent,
|
_event: &crate::MdDecodedProtocolEvent,
|
||||||
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
||||||
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
|
return std::result::Result::Ok(std::vec::Vec::new());
|
||||||
signature: event.signature.clone(),
|
|
||||||
slot: event.slot,
|
|
||||||
decoded_family: event.event_family,
|
|
||||||
materialized_family: crate::MdMaterializedEventFamily::Liquidity,
|
|
||||||
}]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::MtApiEventMaterializer for crate::MtLiquidityMaterializer {
|
impl crate::MtApiEventMaterializer for crate::MtLiquidityMaterializer {
|
||||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||||
return 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(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||||
return &[];
|
return crate::MT_LIQUIDITY_ACCEPTED_FAMILIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_policy(
|
fn transaction_policy(
|
||||||
@@ -1,31 +1,52 @@
|
|||||||
// file: kb-lib/src/materializer/metadata.rs
|
// file: kb-lib/src/materializer/metadata.rs
|
||||||
// version: 5
|
// version: 8
|
||||||
|
|
||||||
//! `metadata` materializer family.
|
//! `metadata` materializer family.
|
||||||
|
|
||||||
mod core;
|
mod metaplex_token_metadata;
|
||||||
mod solana_program_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.
|
/// 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.
|
/// Materializes one canonical Metaplex Metadata account snapshot.
|
||||||
pub use self::core::materializer_metadata_materialize_metaplex_metadata_snapshot;
|
pub use self::metaplex_token_metadata::materializer_metadata_materialize_metaplex_metadata_snapshot;
|
||||||
/// Materializes Token-2022 embedded metadata and group snapshots.
|
/// Compatibility materializer retaining the historical generic metadata type name.
|
||||||
pub use self::core::materializer_metadata_materialize_token_2022_snapshot;
|
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.
|
/// Materializes one decoded Solana Program Metadata account snapshot.
|
||||||
pub use self::solana_program_metadata::materializer_metadata_materialize_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.
|
/// Materializes one bounded Solana Program Metadata Buffer account snapshot.
|
||||||
pub use self::solana_program_metadata::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot;
|
pub use self::solana_program_metadata::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot;
|
||||||
/// Materializes one bounded Solana Program Metadata Metadata account snapshot.
|
/// Materializes one bounded Solana Program Metadata Metadata account snapshot.
|
||||||
pub use self::solana_program_metadata::materializer_metadata_materialize_solana_program_metadata_metadata_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.
|
/// 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.
|
/// 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.
|
/// 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.
|
/// Canonical tracing target for Solana Program Metadata materialization.
|
||||||
pub(crate) use self::solana_program_metadata::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;
|
pub(crate) use self::solana_program_metadata::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_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;
|
||||||
|
|||||||
@@ -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()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
22
kb-lib/src/materializer/metadata/metaplex_token_metadata.rs
Normal file
22
kb-lib/src/materializer/metadata/metaplex_token_metadata.rs
Normal 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;
|
||||||
@@ -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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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";
|
||||||
@@ -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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,16 @@
|
|||||||
// file: kb-lib/src/materializer/metadata/solana_program_metadata.rs
|
// file: kb-lib/src/materializer/metadata/solana_program_metadata.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
//! Solana Program Metadata materialization surface.
|
//! Solana Program Metadata materialization surface.
|
||||||
|
|
||||||
mod constants;
|
mod constants;
|
||||||
mod instruction;
|
mod materializer;
|
||||||
mod state;
|
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.
|
/// Materializes one decoded Solana Program Metadata account snapshot.
|
||||||
pub use self::state::materializer_metadata_materialize_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.
|
/// 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;
|
pub use self::state::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot;
|
||||||
|
|
||||||
/// Event families accepted by the Solana Program Metadata instruction materializer.
|
/// 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.
|
/// 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.
|
/// 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.
|
/// Canonical tracing target for Solana Program Metadata materialization.
|
||||||
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;
|
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
// file: kb-lib/src/materializer/metadata/solana_program_metadata/constants.rs
|
// 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.
|
/// Stable runtime component name for Solana Program Metadata materialization.
|
||||||
pub(crate) const MT_METADATA_SPM_ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
|
pub(crate) const MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME: &str =
|
||||||
&[crate::MdEventFamily::Metadata];
|
|
||||||
/// Stable processor name for Solana Program Metadata projections.
|
|
||||||
pub(crate) const MT_METADATA_SPM_PROCESSOR_NAME: &str =
|
|
||||||
"kb-lib.materializer.metadata.solana_program_metadata";
|
"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.
|
/// 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.
|
/// Canonical tracing target for Solana Program Metadata materialization.
|
||||||
pub(crate) const TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA: &str =
|
pub(crate) const TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA: &str =
|
||||||
"kb-lib.materializer.metadata.solana_program_metadata";
|
"kb-lib.materializer.metadata.solana_program_metadata";
|
||||||
|
|||||||
@@ -1,18 +1,128 @@
|
|||||||
// file: kb-lib/src/materializer/metadata/solana_program_metadata/instruction.rs
|
// file: kb-lib/src/materializer/metadata/solana_program_metadata/materializer.rs
|
||||||
// version: 1
|
// version: 4
|
||||||
|
|
||||||
//! Committed Solana Program Metadata instruction projections.
|
//! 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 {
|
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 {
|
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||||
return 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(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
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 {
|
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
|
||||||
@@ -55,7 +165,7 @@ impl crate::MtApiEventMaterializer for crate::MtMetadataMaterializer {
|
|||||||
),
|
),
|
||||||
family: crate::MdMaterializedEventFamily::Metadata,
|
family: crate::MdMaterializedEventFamily::Metadata,
|
||||||
payload_json: serde_json::json!({
|
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",
|
"domain": "solana_program_metadata",
|
||||||
"projectionKind": "committed_instruction_mutation",
|
"projectionKind": "committed_instruction_mutation",
|
||||||
"projectionSemantics": "committed_instruction_fact_non_authoritative_state",
|
"projectionSemantics": "committed_instruction_fact_non_authoritative_state",
|
||||||
@@ -73,7 +183,7 @@ impl crate::MtApiEventMaterializer for crate::MtMetadataMaterializer {
|
|||||||
"externalContentFetched": false,
|
"externalContentFetched": false,
|
||||||
"externalAccountRead": false,
|
"externalAccountRead": false,
|
||||||
"provenance": {
|
"provenance": {
|
||||||
"processorName": crate::MT_METADATA_SPM_PROCESSOR_NAME,
|
"processorName": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME,
|
||||||
"processorVersion": env!("CARGO_PKG_VERSION"),
|
"processorVersion": env!("CARGO_PKG_VERSION"),
|
||||||
"decoderProof": observation.proof.clone()
|
"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 {
|
fn state_transition(operation: &str) -> &'static str {
|
||||||
match operation {
|
return match operation {
|
||||||
"write" => return "buffer_content_written",
|
"write" => "buffer_content_written",
|
||||||
"initialize" => return "metadata_account_initialized",
|
"initialize" => "metadata_account_initialized",
|
||||||
"set_authority" => return "account_authority_updated",
|
"set_authority" => "account_authority_updated",
|
||||||
"set_data" => return "metadata_data_configuration_updated",
|
"set_data" => "metadata_data_configuration_updated",
|
||||||
"set_immutable" => return "metadata_account_marked_immutable",
|
"set_immutable" => "metadata_account_marked_immutable",
|
||||||
"trim" => return "account_capacity_trimmed",
|
"trim" => "account_capacity_trimmed",
|
||||||
"close" => return "account_closed",
|
"close" => "account_closed",
|
||||||
"allocate" => return "buffer_account_allocated",
|
"allocate" => "buffer_account_allocated",
|
||||||
"extend" => return "account_capacity_extended",
|
"extend" => "account_capacity_extended",
|
||||||
_ => return "unknown",
|
_ => "unknown",
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn target_account(operation: &str, accounts: &serde_json::Value) -> serde_json::Value {
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
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 {
|
fn observation(operation: &str, committed: bool) -> crate::DcApiDecodedObservation {
|
||||||
return crate::DcApiDecodedObservation {
|
return crate::DcApiDecodedObservation {
|
||||||
event_key: format!("solana_program_metadata:{operation}:0"),
|
event_key: format!("solana_program_metadata:{operation}:0"),
|
||||||
@@ -249,7 +355,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn every_stable_instruction_has_one_materialized_mutation_fact() {
|
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() {
|
for (_, operation) in crate::DC_METADATA_SPM_INSTRUCTION_ENTRIES.iter().copied() {
|
||||||
let value = observation(operation, true);
|
let value = observation(operation, true);
|
||||||
assert!(crate::MtApiEventMaterializer::accepts_observation(&materializer, &value));
|
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["operation"], *operation);
|
||||||
assert_eq!(result.outputs[0].payload_json["projection"]["deprecated"], false);
|
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["projection"]["replaced"], false);
|
||||||
|
assert_eq!(
|
||||||
|
result.outputs[0].payload_json["provenance"]["processorName"],
|
||||||
|
crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn failed_instruction_is_refused_and_foreign_surface_is_ignored() {
|
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 failed = observation("set_data", false);
|
||||||
let failure = crate::MtApiEventMaterializer::materialize(&materializer, &failed);
|
let failure = crate::MtApiEventMaterializer::materialize(&materializer, &failed);
|
||||||
assert_eq!(failure.status, crate::MtApiMaterializerOutcomeStatus::Refused);
|
assert_eq!(failure.status, crate::MtApiMaterializerOutcomeStatus::Refused);
|
||||||
@@ -277,7 +389,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn materialization_matrix_matches_two_account_states_and_nine_instructions() {
|
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());
|
assert!(parsed.is_ok());
|
||||||
let matrix = match parsed {
|
let matrix = match parsed {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
@@ -310,7 +424,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trim_keeps_prestable_alias_as_provenance_only() {
|
fn trim_keeps_prestable_alias_as_provenance_only() {
|
||||||
let materializer = crate::MtMetadataMaterializer;
|
let materializer = crate::MtSolanaProgramMetadataMaterializer;
|
||||||
let value = observation("trim", true);
|
let value = observation("trim", true);
|
||||||
let result = crate::MtApiEventMaterializer::materialize(&materializer, &value);
|
let result = crate::MtApiEventMaterializer::materialize(&materializer, &value);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/materializer/metadata/solana_program_metadata/state.rs
|
// file: kb-lib/src/materializer/metadata/solana_program_metadata/state.rs
|
||||||
// version: 2
|
// version: 4
|
||||||
|
|
||||||
//! Authoritative Solana Program Metadata account-state projections.
|
//! Authoritative Solana Program Metadata account-state projections.
|
||||||
|
|
||||||
@@ -11,20 +11,18 @@ pub fn materializer_metadata_materialize_solana_program_metadata_account_snapsho
|
|||||||
slot: u64,
|
slot: u64,
|
||||||
snapshot: &crate::DcMetadataSpmAccountSnapshot,
|
snapshot: &crate::DcMetadataSpmAccountSnapshot,
|
||||||
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
|
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
|
||||||
match snapshot {
|
return match snapshot {
|
||||||
crate::DcMetadataSpmAccountSnapshot::Buffer(value) => {
|
crate::DcMetadataSpmAccountSnapshot::Buffer(value) => {
|
||||||
return crate::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot(
|
crate::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot(
|
||||||
slot,
|
slot, value,
|
||||||
value,
|
)
|
||||||
);
|
|
||||||
},
|
},
|
||||||
crate::DcMetadataSpmAccountSnapshot::Metadata(value) => {
|
crate::DcMetadataSpmAccountSnapshot::Metadata(value) => {
|
||||||
return crate::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot(
|
crate::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot(
|
||||||
slot,
|
slot, value,
|
||||||
value,
|
)
|
||||||
);
|
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Materializes one bounded Solana Program Metadata Buffer account snapshot.
|
/// 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),
|
output_key: format!("solana_program_metadata_account_state:{}", snapshot.account),
|
||||||
family: crate::MdMaterializedEventFamily::Metadata,
|
family: crate::MdMaterializedEventFamily::Metadata,
|
||||||
payload_json: serde_json::json!({
|
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",
|
"domain": "solana_program_metadata",
|
||||||
"projectionKind": "buffer_state",
|
"projectionKind": "buffer_state",
|
||||||
"projectionSemantics": "authoritative_bounded_owned_account_snapshot",
|
"projectionSemantics": "authoritative_bounded_owned_account_snapshot",
|
||||||
@@ -85,7 +83,7 @@ pub fn materializer_metadata_materialize_solana_program_metadata_buffer_snapshot
|
|||||||
"externalAccountRead": false,
|
"externalAccountRead": false,
|
||||||
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
|
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
|
||||||
"provenance": {
|
"provenance": {
|
||||||
"processorName": crate::MT_METADATA_SPM_PROCESSOR_NAME,
|
"processorName": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME,
|
||||||
"processorVersion": env!("CARGO_PKG_VERSION"),
|
"processorVersion": env!("CARGO_PKG_VERSION"),
|
||||||
"source": "bounded_solana_program_metadata_account_decoder"
|
"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),
|
output_key: format!("solana_program_metadata_account_state:{}", snapshot.account),
|
||||||
family: crate::MdMaterializedEventFamily::Metadata,
|
family: crate::MdMaterializedEventFamily::Metadata,
|
||||||
payload_json: serde_json::json!({
|
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",
|
"domain": "solana_program_metadata",
|
||||||
"projectionKind": "metadata_state",
|
"projectionKind": "metadata_state",
|
||||||
"projectionSemantics": "authoritative_bounded_owned_account_snapshot",
|
"projectionSemantics": "authoritative_bounded_owned_account_snapshot",
|
||||||
@@ -179,7 +177,7 @@ pub fn materializer_metadata_materialize_solana_program_metadata_metadata_snapsh
|
|||||||
"externalAccountRead": false,
|
"externalAccountRead": false,
|
||||||
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
|
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
|
||||||
"provenance": {
|
"provenance": {
|
||||||
"processorName": crate::MT_METADATA_SPM_PROCESSOR_NAME,
|
"processorName": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME,
|
||||||
"processorVersion": env!("CARGO_PKG_VERSION"),
|
"processorVersion": env!("CARGO_PKG_VERSION"),
|
||||||
"source": "bounded_solana_program_metadata_account_decoder"
|
"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 {
|
fn data_projection(data: &crate::DcMetadataSpmData) -> serde_json::Value {
|
||||||
match data {
|
return match data {
|
||||||
crate::DcMetadataSpmData::Direct(value) => {
|
crate::DcMetadataSpmData::Direct(value) => {
|
||||||
let mut projection = bytes_projection(value.as_slice());
|
let mut projection = bytes_projection(value.as_slice());
|
||||||
projection["kind"] = serde_json::json!("direct");
|
projection["kind"] = serde_json::json!("direct");
|
||||||
return projection;
|
projection
|
||||||
},
|
},
|
||||||
crate::DcMetadataSpmData::Url(value) => {
|
crate::DcMetadataSpmData::Url(value) => {
|
||||||
return serde_json::json!({
|
serde_json::json!({
|
||||||
"kind": "url",
|
"kind": "url",
|
||||||
"value": value,
|
"value": value,
|
||||||
"lengthBytes": value.len(),
|
"lengthBytes": value.len(),
|
||||||
"sha256": hash(value.as_bytes()),
|
"sha256": hash(value.as_bytes()),
|
||||||
"fetched": false
|
"fetched": false
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
crate::DcMetadataSpmData::External(value) => {
|
crate::DcMetadataSpmData::External(value) => {
|
||||||
return serde_json::json!({
|
serde_json::json!({
|
||||||
"kind": "external",
|
"kind": "external",
|
||||||
"address": value.address.to_string(),
|
"address": value.address.to_string(),
|
||||||
"offset": value.offset,
|
"offset": value.offset,
|
||||||
"length": value.length.map(std::num::NonZeroU32::get),
|
"length": value.length.map(std::num::NonZeroU32::get),
|
||||||
"wireLengthBytes": crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES,
|
"wireLengthBytes": crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES,
|
||||||
"accountRead": false
|
"accountRead": false
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bytes_projection(bytes: &[u8]) -> serde_json::Value {
|
fn bytes_projection(bytes: &[u8]) -> serde_json::Value {
|
||||||
|
|||||||
19
kb-lib/src/materializer/metadata/token_2022.rs
Normal file
19
kb-lib/src/materializer/metadata/token_2022.rs
Normal 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;
|
||||||
15
kb-lib/src/materializer/metadata/token_2022/constants.rs
Normal file
15
kb-lib/src/materializer/metadata/token_2022/constants.rs
Normal 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";
|
||||||
210
kb-lib/src/materializer/metadata/token_2022/state.rs
Normal file
210
kb-lib/src/materializer/metadata/token_2022/state.rs
Normal 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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
// file: kb-lib/src/materializer/nft.rs
|
// file: kb-lib/src/materializer/nft.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! `nft` materializer family.
|
//! `nft` materializer family.
|
||||||
|
|
||||||
mod core;
|
mod constants;
|
||||||
|
mod materializer;
|
||||||
|
|
||||||
/// Reserved NFT 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;
|
||||||
|
|||||||
11
kb-lib/src/materializer/nft/constants.rs
Normal file
11
kb-lib/src/materializer/nft/constants.rs
Normal 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] = &[];
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/materializer/nft/core.rs
|
// file: kb-lib/src/materializer/nft/materializer.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
//! Reserved NFT materializer.
|
//! Reserved NFT materializer.
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ pub struct MtNftMaterializer;
|
|||||||
|
|
||||||
impl crate::MtMaterializer for crate::MtNftMaterializer {
|
impl crate::MtMaterializer for crate::MtNftMaterializer {
|
||||||
fn materializer_name(&self) -> &'static str {
|
fn materializer_name(&self) -> &'static str {
|
||||||
return "kb-lib.materializer.nft";
|
return crate::MT_NFT_COMPONENT_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn materializer_version(&self) -> &'static str {
|
fn materializer_version(&self) -> &'static str {
|
||||||
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtNftMaterializer {
|
|||||||
|
|
||||||
fn materialize_event(
|
fn materialize_event(
|
||||||
&self,
|
&self,
|
||||||
event: &crate::MdDecodedProtocolEvent,
|
_event: &crate::MdDecodedProtocolEvent,
|
||||||
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
||||||
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
|
return std::result::Result::Ok(std::vec::Vec::new());
|
||||||
signature: event.signature.clone(),
|
|
||||||
slot: event.slot,
|
|
||||||
decoded_family: event.event_family,
|
|
||||||
materialized_family: crate::MdMaterializedEventFamily::Nft,
|
|
||||||
}]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::MtApiEventMaterializer for crate::MtNftMaterializer {
|
impl crate::MtApiEventMaterializer for crate::MtNftMaterializer {
|
||||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||||
return 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(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||||
return &[];
|
return crate::MT_NFT_ACCEPTED_FAMILIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_policy(
|
fn transaction_policy(
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
// file: kb-lib/src/materializer/oracle.rs
|
// file: kb-lib/src/materializer/oracle.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! `oracle` materializer family.
|
//! `oracle` materializer family.
|
||||||
|
|
||||||
mod core;
|
mod constants;
|
||||||
|
mod materializer;
|
||||||
|
|
||||||
/// Reserved oracle 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;
|
||||||
|
|||||||
11
kb-lib/src/materializer/oracle/constants.rs
Normal file
11
kb-lib/src/materializer/oracle/constants.rs
Normal 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] = &[];
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/materializer/oracle/core.rs
|
// file: kb-lib/src/materializer/oracle/materializer.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
//! Reserved oracle materializer.
|
//! Reserved oracle materializer.
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ pub struct MtOracleMaterializer;
|
|||||||
|
|
||||||
impl crate::MtMaterializer for crate::MtOracleMaterializer {
|
impl crate::MtMaterializer for crate::MtOracleMaterializer {
|
||||||
fn materializer_name(&self) -> &'static str {
|
fn materializer_name(&self) -> &'static str {
|
||||||
return "kb-lib.materializer.oracle";
|
return crate::MT_ORACLE_COMPONENT_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn materializer_version(&self) -> &'static str {
|
fn materializer_version(&self) -> &'static str {
|
||||||
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtOracleMaterializer {
|
|||||||
|
|
||||||
fn materialize_event(
|
fn materialize_event(
|
||||||
&self,
|
&self,
|
||||||
event: &crate::MdDecodedProtocolEvent,
|
_event: &crate::MdDecodedProtocolEvent,
|
||||||
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
||||||
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
|
return std::result::Result::Ok(std::vec::Vec::new());
|
||||||
signature: event.signature.clone(),
|
|
||||||
slot: event.slot,
|
|
||||||
decoded_family: event.event_family,
|
|
||||||
materialized_family: crate::MdMaterializedEventFamily::Oracle,
|
|
||||||
}]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::MtApiEventMaterializer for crate::MtOracleMaterializer {
|
impl crate::MtApiEventMaterializer for crate::MtOracleMaterializer {
|
||||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||||
return 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(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||||
return &[];
|
return crate::MT_ORACLE_ACCEPTED_FAMILIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_policy(
|
fn transaction_policy(
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
// file: kb-lib/src/materializer/orderbook.rs
|
// file: kb-lib/src/materializer/orderbook.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! `orderbook` materializer family.
|
//! `orderbook` materializer family.
|
||||||
|
|
||||||
mod core;
|
mod constants;
|
||||||
|
mod materializer;
|
||||||
|
|
||||||
/// Reserved orderbook 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;
|
||||||
|
|||||||
11
kb-lib/src/materializer/orderbook/constants.rs
Normal file
11
kb-lib/src/materializer/orderbook/constants.rs
Normal 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] = &[];
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/materializer/orderbook/core.rs
|
// file: kb-lib/src/materializer/orderbook/materializer.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
//! Reserved orderbook materializer.
|
//! Reserved orderbook materializer.
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ pub struct MtOrderbookMaterializer;
|
|||||||
|
|
||||||
impl crate::MtMaterializer for crate::MtOrderbookMaterializer {
|
impl crate::MtMaterializer for crate::MtOrderbookMaterializer {
|
||||||
fn materializer_name(&self) -> &'static str {
|
fn materializer_name(&self) -> &'static str {
|
||||||
return "kb-lib.materializer.orderbook";
|
return crate::MT_ORDERBOOK_COMPONENT_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn materializer_version(&self) -> &'static str {
|
fn materializer_version(&self) -> &'static str {
|
||||||
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtOrderbookMaterializer {
|
|||||||
|
|
||||||
fn materialize_event(
|
fn materialize_event(
|
||||||
&self,
|
&self,
|
||||||
event: &crate::MdDecodedProtocolEvent,
|
_event: &crate::MdDecodedProtocolEvent,
|
||||||
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
||||||
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
|
return std::result::Result::Ok(std::vec::Vec::new());
|
||||||
signature: event.signature.clone(),
|
|
||||||
slot: event.slot,
|
|
||||||
decoded_family: event.event_family,
|
|
||||||
materialized_family: crate::MdMaterializedEventFamily::Orderbook,
|
|
||||||
}]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::MtApiEventMaterializer for crate::MtOrderbookMaterializer {
|
impl crate::MtApiEventMaterializer for crate::MtOrderbookMaterializer {
|
||||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||||
return 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(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||||
return &[];
|
return crate::MT_ORDERBOOK_ACCEPTED_FAMILIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_policy(
|
fn transaction_policy(
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
// file: kb-lib/src/materializer/perpetuals.rs
|
// file: kb-lib/src/materializer/perpetuals.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! `perpetuals` materializer family.
|
//! `perpetuals` materializer family.
|
||||||
|
|
||||||
mod core;
|
mod constants;
|
||||||
|
mod materializer;
|
||||||
|
|
||||||
/// Reserved perpetuals 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;
|
||||||
|
|||||||
11
kb-lib/src/materializer/perpetuals/constants.rs
Normal file
11
kb-lib/src/materializer/perpetuals/constants.rs
Normal 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] = &[];
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/materializer/perpetuals/core.rs
|
// file: kb-lib/src/materializer/perpetuals/materializer.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! Reserved perpetuals materializer.
|
//! Reserved perpetuals materializer.
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ pub struct MtPerpetualsMaterializer;
|
|||||||
|
|
||||||
impl crate::MtMaterializer for crate::MtPerpetualsMaterializer {
|
impl crate::MtMaterializer for crate::MtPerpetualsMaterializer {
|
||||||
fn materializer_name(&self) -> &'static str {
|
fn materializer_name(&self) -> &'static str {
|
||||||
return "kb-lib.materializer.perpetuals";
|
return crate::MT_PERPETUALS_COMPONENT_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn materializer_version(&self) -> &'static str {
|
fn materializer_version(&self) -> &'static str {
|
||||||
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtPerpetualsMaterializer {
|
|||||||
|
|
||||||
fn materialize_event(
|
fn materialize_event(
|
||||||
&self,
|
&self,
|
||||||
event: &crate::MdDecodedProtocolEvent,
|
_event: &crate::MdDecodedProtocolEvent,
|
||||||
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
||||||
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
|
return std::result::Result::Ok(std::vec::Vec::new());
|
||||||
signature: event.signature.clone(),
|
|
||||||
slot: event.slot,
|
|
||||||
decoded_family: event.event_family,
|
|
||||||
materialized_family: crate::MdMaterializedEventFamily::Perpetuals,
|
|
||||||
}]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::MtApiEventMaterializer for crate::MtPerpetualsMaterializer {
|
impl crate::MtApiEventMaterializer for crate::MtPerpetualsMaterializer {
|
||||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||||
return 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(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||||
return &[];
|
return crate::MT_PERPETUALS_ACCEPTED_FAMILIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_policy(
|
fn transaction_policy(
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/materializer/pool.rs
|
// file: kb-lib/src/materializer/pool.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! `pool` materializer family.
|
//! `pool` materializer family.
|
||||||
|
|
||||||
@@ -7,3 +7,10 @@ mod state;
|
|||||||
|
|
||||||
/// Reserved pool-state materializer.
|
/// Reserved pool-state materializer.
|
||||||
pub use self::state::MtPoolStateMaterializer;
|
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;
|
||||||
|
|||||||
@@ -1,61 +1,17 @@
|
|||||||
// file: kb-lib/src/materializer/pool/state.rs
|
// 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.
|
mod constants;
|
||||||
#[derive(Clone, Debug, Default)]
|
mod materializer;
|
||||||
pub struct MtPoolStateMaterializer;
|
|
||||||
|
|
||||||
impl crate::MtMaterializer for crate::MtPoolStateMaterializer {
|
/// Reserved pool-state materializer.
|
||||||
fn materializer_name(&self) -> &'static str {
|
pub use self::materializer::MtPoolStateMaterializer;
|
||||||
return "kb-lib.materializer.pool.state";
|
|
||||||
}
|
|
||||||
|
|
||||||
fn materializer_version(&self) -> &'static str {
|
/// Empty accepted-family boundary for the reserved component.
|
||||||
return env!("CARGO_PKG_VERSION");
|
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;
|
||||||
fn accepts_event(&self, _event: &crate::MdDecodedProtocolEvent) -> bool {
|
/// Stable processor name for the reserved pool-state materializer.
|
||||||
return false;
|
pub(crate) use self::constants::MT_POOL_STATE_PROCESSOR_NAME;
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
11
kb-lib/src/materializer/pool/state/constants.rs
Normal file
11
kb-lib/src/materializer/pool/state/constants.rs
Normal 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] = &[];
|
||||||
56
kb-lib/src/materializer/pool/state/materializer.rs
Normal file
56
kb-lib/src/materializer/pool/state/materializer.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
// file: kb-lib/src/materializer/rewards.rs
|
// file: kb-lib/src/materializer/rewards.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! `rewards` materializer family.
|
//! `rewards` materializer family.
|
||||||
|
|
||||||
mod core;
|
mod constants;
|
||||||
|
mod materializer;
|
||||||
|
|
||||||
/// Reserved rewards 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;
|
||||||
|
|||||||
11
kb-lib/src/materializer/rewards/constants.rs
Normal file
11
kb-lib/src/materializer/rewards/constants.rs
Normal 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] = &[];
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
// file: kb-lib/src/materializer/rewards/core.rs
|
// file: kb-lib/src/materializer/rewards/materializer.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
//! Reserved rewards materializer.
|
//! Reserved rewards materializer.
|
||||||
|
|
||||||
/// Reserved materializer for reward observations.
|
/// Reserved materializer for rewards observations.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct MtRewardsMaterializer;
|
pub struct MtRewardsMaterializer;
|
||||||
|
|
||||||
impl crate::MtMaterializer for crate::MtRewardsMaterializer {
|
impl crate::MtMaterializer for crate::MtRewardsMaterializer {
|
||||||
fn materializer_name(&self) -> &'static str {
|
fn materializer_name(&self) -> &'static str {
|
||||||
return "kb-lib.materializer.rewards";
|
return crate::MT_REWARDS_COMPONENT_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn materializer_version(&self) -> &'static str {
|
fn materializer_version(&self) -> &'static str {
|
||||||
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtRewardsMaterializer {
|
|||||||
|
|
||||||
fn materialize_event(
|
fn materialize_event(
|
||||||
&self,
|
&self,
|
||||||
event: &crate::MdDecodedProtocolEvent,
|
_event: &crate::MdDecodedProtocolEvent,
|
||||||
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
||||||
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
|
return std::result::Result::Ok(std::vec::Vec::new());
|
||||||
signature: event.signature.clone(),
|
|
||||||
slot: event.slot,
|
|
||||||
decoded_family: event.event_family,
|
|
||||||
materialized_family: crate::MdMaterializedEventFamily::Reward,
|
|
||||||
}]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::MtApiEventMaterializer for crate::MtRewardsMaterializer {
|
impl crate::MtApiEventMaterializer for crate::MtRewardsMaterializer {
|
||||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||||
return 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(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||||
return &[];
|
return crate::MT_REWARDS_ACCEPTED_FAMILIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_policy(
|
fn transaction_policy(
|
||||||
@@ -1,17 +1,23 @@
|
|||||||
// file: kb-lib/src/materializer/risk.rs
|
// file: kb-lib/src/materializer/risk.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! `risk` materializer family.
|
//! `risk` materializer family.
|
||||||
|
|
||||||
mod constants;
|
mod constants;
|
||||||
mod core;
|
mod materializer;
|
||||||
|
|
||||||
/// Stable SPL Token and ATA risk 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;
|
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;
|
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;
|
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_RISK;
|
||||||
|
|||||||
@@ -1,11 +1,48 @@
|
|||||||
// file: kb-lib/src/materializer/risk/constants.rs
|
// 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.
|
/// Stable processor name persisted by the replay ledger.
|
||||||
pub(crate) const MT_RISK_PROCESSOR_NAME: &str = "materializer.risk";
|
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;
|
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";
|
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",
|
||||||
|
];
|
||||||
|
|||||||
@@ -1,48 +1,15 @@
|
|||||||
// file: kb-lib/src/materializer/risk/core.rs
|
// file: kb-lib/src/materializer/risk/materializer.rs
|
||||||
// version: 6
|
// version: 8
|
||||||
|
|
||||||
//! Stable fact-only risk projections for committed SPL Token and ATA observations.
|
//! 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.
|
/// Stable fact-only classic SPL Token risk materializer.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct MtRiskMaterializer;
|
pub struct MtRiskMaterializer;
|
||||||
|
|
||||||
impl crate::MtMaterializer for crate::MtRiskMaterializer {
|
impl crate::MtMaterializer for crate::MtRiskMaterializer {
|
||||||
fn materializer_name(&self) -> &'static str {
|
fn materializer_name(&self) -> &'static str {
|
||||||
return "kb-lib.materializer.risk";
|
return crate::MT_RISK_COMPONENT_NAME;
|
||||||
}
|
}
|
||||||
fn materializer_version(&self) -> &'static str {
|
fn materializer_version(&self) -> &'static str {
|
||||||
return env!("CARGO_PKG_VERSION");
|
return env!("CARGO_PKG_VERSION");
|
||||||
@@ -66,7 +33,7 @@ impl crate::MtApiEventMaterializer for crate::MtRiskMaterializer {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||||
return ACCEPTED_FAMILIES;
|
return crate::MT_RISK_ACCEPTED_FAMILIES;
|
||||||
}
|
}
|
||||||
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
|
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
|
||||||
return accepts_event(&observation.event);
|
return accepts_event(&observation.event);
|
||||||
@@ -171,8 +138,9 @@ fn accepts_event(event: &crate::MdDecodedProtocolEvent) -> bool {
|
|||||||
&& event.surface_code.0 == "spl.token")
|
&& event.surface_code.0 == "spl.token")
|
||||||
|| (event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
|
|| (event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
|
||||||
&& event.surface_code.0 == "spl.token_2022");
|
&& event.surface_code.0 == "spl.token_2022");
|
||||||
let token_risk = token_program && RISK_ENTRIES.contains(&event.event_name.0.as_str());
|
let token_risk =
|
||||||
return ACCEPTED_FAMILIES.contains(&event.event_family)
|
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));
|
&& (token_risk || is_ata_recovery(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
// file: kb-lib/src/materializer/routing.rs
|
// file: kb-lib/src/materializer/routing.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! `routing` materializer family.
|
//! `routing` materializer family.
|
||||||
|
|
||||||
mod core;
|
mod constants;
|
||||||
|
mod materializer;
|
||||||
|
|
||||||
/// Reserved routing 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;
|
||||||
|
|||||||
11
kb-lib/src/materializer/routing/constants.rs
Normal file
11
kb-lib/src/materializer/routing/constants.rs
Normal 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] = &[];
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/materializer/routing/core.rs
|
// file: kb-lib/src/materializer/routing/materializer.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
//! Reserved routing materializer.
|
//! Reserved routing materializer.
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ pub struct MtRoutingMaterializer;
|
|||||||
|
|
||||||
impl crate::MtMaterializer for crate::MtRoutingMaterializer {
|
impl crate::MtMaterializer for crate::MtRoutingMaterializer {
|
||||||
fn materializer_name(&self) -> &'static str {
|
fn materializer_name(&self) -> &'static str {
|
||||||
return "kb-lib.materializer.routing";
|
return crate::MT_ROUTING_COMPONENT_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn materializer_version(&self) -> &'static str {
|
fn materializer_version(&self) -> &'static str {
|
||||||
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtRoutingMaterializer {
|
|||||||
|
|
||||||
fn materialize_event(
|
fn materialize_event(
|
||||||
&self,
|
&self,
|
||||||
event: &crate::MdDecodedProtocolEvent,
|
_event: &crate::MdDecodedProtocolEvent,
|
||||||
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
||||||
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
|
return std::result::Result::Ok(std::vec::Vec::new());
|
||||||
signature: event.signature.clone(),
|
|
||||||
slot: event.slot,
|
|
||||||
decoded_family: event.event_family,
|
|
||||||
materialized_family: crate::MdMaterializedEventFamily::Routing,
|
|
||||||
}]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::MtApiEventMaterializer for crate::MtRoutingMaterializer {
|
impl crate::MtApiEventMaterializer for crate::MtRoutingMaterializer {
|
||||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||||
return 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(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||||
return &[];
|
return crate::MT_ROUTING_ACCEPTED_FAMILIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_policy(
|
fn transaction_policy(
|
||||||
@@ -1,13 +1,45 @@
|
|||||||
// file: kb-lib/src/materializer/staking.rs
|
// file: kb-lib/src/materializer/staking.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
//! `staking` materializer family.
|
//! `staking` materializer family.
|
||||||
|
|
||||||
mod constants;
|
mod constants;
|
||||||
mod core;
|
mod materializer;
|
||||||
|
|
||||||
/// Stable native stake and vote 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;
|
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_STAKING;
|
||||||
|
|||||||
@@ -1,7 +1,77 @@
|
|||||||
// file: kb-lib/src/materializer/staking/constants.rs
|
// 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";
|
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"];
|
||||||
|
|||||||
@@ -1,57 +1,8 @@
|
|||||||
// file: kb-lib/src/materializer/staking/core.rs
|
// file: kb-lib/src/materializer/staking/materializer.rs
|
||||||
// version: 10
|
// version: 12
|
||||||
|
|
||||||
//! Instruction-level staking projections for native Stake and Vote observations.
|
//! 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)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
enum ProjectionKind {
|
enum ProjectionKind {
|
||||||
StakeAccount,
|
StakeAccount,
|
||||||
@@ -100,7 +51,7 @@ pub struct MtStakingMaterializer;
|
|||||||
|
|
||||||
impl crate::MtMaterializer for crate::MtStakingMaterializer {
|
impl crate::MtMaterializer for crate::MtStakingMaterializer {
|
||||||
fn materializer_name(&self) -> &'static str {
|
fn materializer_name(&self) -> &'static str {
|
||||||
return "kb-lib.materializer.staking";
|
return crate::MT_STAKING_COMPONENT_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn materializer_version(&self) -> &'static str {
|
fn materializer_version(&self) -> &'static str {
|
||||||
@@ -126,13 +77,13 @@ impl crate::MtMaterializer for crate::MtStakingMaterializer {
|
|||||||
impl crate::MtApiEventMaterializer for crate::MtStakingMaterializer {
|
impl crate::MtApiEventMaterializer for crate::MtStakingMaterializer {
|
||||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||||
return 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(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||||
return ACCEPTED_FAMILIES;
|
return crate::MT_STAKING_ACCEPTED_FAMILIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
|
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()),
|
output_key: projection.output_key(operation.as_str()),
|
||||||
family: projection.family(),
|
family: projection.family(),
|
||||||
payload_json: serde_json::json!({
|
payload_json: serde_json::json!({
|
||||||
"projectionVersion": 1,
|
"projectionVersion": crate::MT_STAKING_PROJECTION_VERSION,
|
||||||
"projectionSemantics": "committed_instruction_staking_event",
|
"projectionSemantics": "committed_instruction_staking_event",
|
||||||
"domain": projection.domain(),
|
"domain": projection.domain(),
|
||||||
"operation": operation,
|
"operation": operation,
|
||||||
@@ -214,6 +165,10 @@ impl crate::MtApiEventMaterializer for crate::MtStakingMaterializer {
|
|||||||
"slot": observation.event.slot.0,
|
"slot": observation.event.slot.0,
|
||||||
"instructionPath": observation.event.instruction_path.0.clone(),
|
"instructionPath": observation.event.instruction_path.0.clone(),
|
||||||
"transactionSucceeded": true,
|
"transactionSucceeded": true,
|
||||||
|
"provenance": {
|
||||||
|
"processorName": crate::MT_STAKING_PROCESSOR_NAME,
|
||||||
|
"processorVersion": env!("CARGO_PKG_VERSION"),
|
||||||
|
},
|
||||||
"accounts": accounts,
|
"accounts": accounts,
|
||||||
"parameters": parameters,
|
"parameters": parameters,
|
||||||
"projection": projection_details(
|
"projection": projection_details(
|
||||||
@@ -242,52 +197,54 @@ impl crate::MtApiEventMaterializer for crate::MtStakingMaterializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn family_is_accepted(family: crate::MdEventFamily) -> bool {
|
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> {
|
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);
|
return stake_projection(entry_code);
|
||||||
}
|
}
|
||||||
if surface_code == VOTE_SURFACE {
|
if surface_code == crate::MT_STAKING_VOTE_SURFACE {
|
||||||
return vote_projection(entry_code);
|
return vote_projection(entry_code);
|
||||||
}
|
}
|
||||||
return std::option::Option::None;
|
return std::option::Option::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stake_projection(entry_code: &str) -> std::option::Option<ProjectionKind> {
|
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);
|
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);
|
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);
|
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::Some(ProjectionKind::StakeLockup);
|
||||||
}
|
}
|
||||||
return std::option::Option::None;
|
return std::option::Option::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vote_projection(entry_code: &str) -> std::option::Option<ProjectionKind> {
|
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);
|
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);
|
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);
|
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);
|
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);
|
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::Some(ProjectionKind::VoteReward);
|
||||||
}
|
}
|
||||||
return std::option::Option::None;
|
return std::option::Option::None;
|
||||||
@@ -637,7 +594,7 @@ mod tests {
|
|||||||
fn stake_initialize_creates_instructional_staking_projection() {
|
fn stake_initialize_creates_instructional_staking_projection() {
|
||||||
let materializer = crate::MtStakingMaterializer;
|
let materializer = crate::MtStakingMaterializer;
|
||||||
let observation = observation(
|
let observation = observation(
|
||||||
super::STAKE_SURFACE,
|
crate::MT_STAKING_STAKE_SURFACE,
|
||||||
"initialize",
|
"initialize",
|
||||||
crate::MdEventFamily::Lifecycle,
|
crate::MdEventFamily::Lifecycle,
|
||||||
false,
|
false,
|
||||||
@@ -659,7 +616,7 @@ mod tests {
|
|||||||
fn stake_authority_and_lockup_events_are_projected() {
|
fn stake_authority_and_lockup_events_are_projected() {
|
||||||
let materializer = crate::MtStakingMaterializer;
|
let materializer = crate::MtStakingMaterializer;
|
||||||
let authority = observation(
|
let authority = observation(
|
||||||
super::STAKE_SURFACE,
|
crate::MT_STAKING_STAKE_SURFACE,
|
||||||
"authorize_with_seed",
|
"authorize_with_seed",
|
||||||
crate::MdEventFamily::Admin,
|
crate::MdEventFamily::Admin,
|
||||||
false,
|
false,
|
||||||
@@ -672,7 +629,7 @@ mod tests {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
let lockup = observation(
|
let lockup = observation(
|
||||||
super::STAKE_SURFACE,
|
crate::MT_STAKING_STAKE_SURFACE,
|
||||||
"set_lockup_checked",
|
"set_lockup_checked",
|
||||||
crate::MdEventFamily::Admin,
|
crate::MdEventFamily::Admin,
|
||||||
false,
|
false,
|
||||||
@@ -701,7 +658,7 @@ mod tests {
|
|||||||
fn stake_value_projection_does_not_duplicate_lamport_changes() {
|
fn stake_value_projection_does_not_duplicate_lamport_changes() {
|
||||||
let materializer = crate::MtStakingMaterializer;
|
let materializer = crate::MtStakingMaterializer;
|
||||||
let observation = observation(
|
let observation = observation(
|
||||||
super::STAKE_SURFACE,
|
crate::MT_STAKING_STAKE_SURFACE,
|
||||||
"withdraw",
|
"withdraw",
|
||||||
crate::MdEventFamily::Staking,
|
crate::MdEventFamily::Staking,
|
||||||
false,
|
false,
|
||||||
@@ -724,7 +681,7 @@ mod tests {
|
|||||||
fn vote_account_and_authority_events_are_projected() {
|
fn vote_account_and_authority_events_are_projected() {
|
||||||
let materializer = crate::MtStakingMaterializer;
|
let materializer = crate::MtStakingMaterializer;
|
||||||
let initialize = observation(
|
let initialize = observation(
|
||||||
super::VOTE_SURFACE,
|
crate::MT_STAKING_VOTE_SURFACE,
|
||||||
"initialize_account_v2",
|
"initialize_account_v2",
|
||||||
crate::MdEventFamily::Lifecycle,
|
crate::MdEventFamily::Lifecycle,
|
||||||
false,
|
false,
|
||||||
@@ -739,7 +696,7 @@ mod tests {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
let authorize = observation(
|
let authorize = observation(
|
||||||
super::VOTE_SURFACE,
|
crate::MT_STAKING_VOTE_SURFACE,
|
||||||
"authorize_checked",
|
"authorize_checked",
|
||||||
crate::MdEventFamily::Admin,
|
crate::MdEventFamily::Admin,
|
||||||
false,
|
false,
|
||||||
@@ -769,7 +726,7 @@ mod tests {
|
|||||||
fn vote_state_and_reward_outputs_are_distinguished() {
|
fn vote_state_and_reward_outputs_are_distinguished() {
|
||||||
let materializer = crate::MtStakingMaterializer;
|
let materializer = crate::MtStakingMaterializer;
|
||||||
let vote_state = observation(
|
let vote_state = observation(
|
||||||
super::VOTE_SURFACE,
|
crate::MT_STAKING_VOTE_SURFACE,
|
||||||
"tower_sync_switch",
|
"tower_sync_switch",
|
||||||
crate::MdEventFamily::Staking,
|
crate::MdEventFamily::Staking,
|
||||||
false,
|
false,
|
||||||
@@ -780,7 +737,7 @@ mod tests {
|
|||||||
serde_json::json!({"root": 10, "hash": "hash111", "switchProofHash": "switch111"}),
|
serde_json::json!({"root": 10, "hash": "hash111", "switchProofHash": "switch111"}),
|
||||||
);
|
);
|
||||||
let reward = observation(
|
let reward = observation(
|
||||||
super::VOTE_SURFACE,
|
crate::MT_STAKING_VOTE_SURFACE,
|
||||||
"deposit_delegator_rewards",
|
"deposit_delegator_rewards",
|
||||||
crate::MdEventFamily::Reward,
|
crate::MdEventFamily::Reward,
|
||||||
false,
|
false,
|
||||||
@@ -805,7 +762,7 @@ mod tests {
|
|||||||
fn failed_staking_mutation_is_refused() {
|
fn failed_staking_mutation_is_refused() {
|
||||||
let materializer = crate::MtStakingMaterializer;
|
let materializer = crate::MtStakingMaterializer;
|
||||||
let observation = observation(
|
let observation = observation(
|
||||||
super::STAKE_SURFACE,
|
crate::MT_STAKING_STAKE_SURFACE,
|
||||||
"delegate_stake",
|
"delegate_stake",
|
||||||
crate::MdEventFamily::Staking,
|
crate::MdEventFamily::Staking,
|
||||||
true,
|
true,
|
||||||
@@ -829,7 +786,7 @@ mod tests {
|
|||||||
serde_json::json!({}),
|
serde_json::json!({}),
|
||||||
);
|
);
|
||||||
let redelegate = observation(
|
let redelegate = observation(
|
||||||
super::STAKE_SURFACE,
|
crate::MT_STAKING_STAKE_SURFACE,
|
||||||
"redelegate",
|
"redelegate",
|
||||||
crate::MdEventFamily::Staking,
|
crate::MdEventFamily::Staking,
|
||||||
false,
|
false,
|
||||||
@@ -848,7 +805,7 @@ mod tests {
|
|||||||
fn output_serialization_is_deterministic() {
|
fn output_serialization_is_deterministic() {
|
||||||
let materializer = crate::MtStakingMaterializer;
|
let materializer = crate::MtStakingMaterializer;
|
||||||
let observation = observation(
|
let observation = observation(
|
||||||
super::VOTE_SURFACE,
|
crate::MT_STAKING_VOTE_SURFACE,
|
||||||
"update_commission_bps",
|
"update_commission_bps",
|
||||||
crate::MdEventFamily::Admin,
|
crate::MdEventFamily::Admin,
|
||||||
false,
|
false,
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
// file: kb-lib/src/materializer/token.rs
|
// file: kb-lib/src/materializer/token.rs
|
||||||
// version: 6
|
// version: 7
|
||||||
|
|
||||||
//! `token` materializer family.
|
//! `token` materializer family.
|
||||||
|
|
||||||
mod accounts;
|
mod accounts;
|
||||||
mod constants;
|
|
||||||
mod metadata_risk;
|
mod metadata_risk;
|
||||||
|
|
||||||
/// Stable Token, ATA and Token-2022 account materializer.
|
/// 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.
|
/// Reserved token-metadata risk materializer.
|
||||||
pub use self::metadata_risk::MtTokenMetadataRiskMaterializer;
|
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.
|
/// 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.
|
/// 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.
|
/// 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;
|
||||||
|
|||||||
@@ -1,842 +1,27 @@
|
|||||||
// file: kb-lib/src/materializer/token/accounts.rs
|
// 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] = &[
|
mod constants;
|
||||||
crate::MdEventFamily::TokenAccount,
|
mod materializer;
|
||||||
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"),
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Stable committed classic SPL Token account and mint mutation materializer.
|
/// Stable Token, ATA and Token-2022 account materializer.
|
||||||
#[derive(Clone, Debug, Default)]
|
pub use self::materializer::MtTokenAccountsMaterializer;
|
||||||
pub struct 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 {
|
/// Crate-visible `MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES` component constant.
|
||||||
fn materializer_name(&self) -> &'static str {
|
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES;
|
||||||
return "kb-lib.materializer.token.accounts";
|
/// 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.
|
||||||
fn materializer_version(&self) -> &'static str {
|
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_COMPONENT_NAME;
|
||||||
return env!("CARGO_PKG_VERSION");
|
/// 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.
|
||||||
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
|
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME;
|
||||||
return accepts_event(event);
|
/// 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.
|
||||||
fn materialize_event(
|
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS;
|
||||||
&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()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user