# kb_execution_solana `kb_execution_solana` transforme un `PreparedExecutionPlan` commun en message et transaction Solana, puis résout les signataires via l’interface Solana `Signer` et signe uniquement après une simulation autorisée par `kb_execution_safety`. La crate est indépendante des exécuteurs concrets et des transports RPC. Elle assemble aussi bien les plans à recent blockhash que les transactions consommant réellement un durable nonce. ## API publique | Export | Usage | |----------------------------------------|-------------------------------------------------------------------------------------------------------| | `build_legacy_transaction(...)` | Compile un plan `Latest` avec un recent blockhash explicite. | | `build_durable_nonce_transaction(...)` | Compile un plan `DurableNonce` avec un état nonce validé et injecte l’avance en première instruction. | | `durable_nonce_account_data_length()` | Fournit la longueur exacte à demander au RPC pour un compte nonce. | | `parse_durable_nonce_account(...)` | Valide et décode un snapshot complet de compte nonce System. | | `DurableNonceAccountState` | État validé exposant compte, autorité, nonce/blockhash et coût par signature. | | `UnsignedSolanaTransaction` | Message compilé non signé, destiné aux frais, à la simulation exacte et à la signature contrôlée. | | `SolanaSimulationEvidence` | Simulation liée au hash et au blockhash/nonce exacts du message. | | `SignedSolanaTransaction` | Transaction signée, vérifiable localement et sérialisable pour `sendTransaction`. | ### Méthodes des types | Type | Méthodes publiques | |-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `DurableNonceAccountState` | `account()`, `authority()`, `blockhash()`, `lamports_per_signature()`. | | `UnsignedSolanaTransaction` | `plan()`, `message_hash()`, `blockhash()`, `required_signer_pubkeys()`, `message_base64()`, `transaction_base64()`, `bind_simulation(...)`, `sign_after_simulation(...)`. | | `SolanaSimulationEvidence` | `message_hash()`, `blockhash()`, `simulation()`. | | `SignedSolanaTransaction` | `message_hash()`, `primary_signature()`, `signer_pubkeys()`, `wire_length()`, `transaction_base64()`, `verify_signatures()`. | ## Exemple de flux ```rust let unsigned = kb_execution_solana::build_legacy_transaction(&plan, recent_blockhash)?; let simulation = rpc_simulation.to_execution_result( unsigned.message_hash(), unsigned.blockhash(), observed_cluster, )?; let evidence = unsigned.bind_simulation(simulation)?; let signed = unsigned.sign_after_simulation(&evidence, signers.as_slice())?; signed.verify_signatures()?; ``` Le transport RPC reste extérieur : cette crate ne récupère pas le blockhash, ne charge pas le compte nonce et n’envoie pas le payload signé. ## Assemblage à recent blockhash ### `build_legacy_transaction(plan, recent_blockhash)` Construit une transaction legacy non signée à partir d’un blockhash base58 obtenu par `getLatestBlockhash`. La fonction vérifie : - la politique `ExecutionBlockhashKind::Latest` ; - les public keys et le blockhash ; - la présence d’au moins une instruction ; - l’égalité exacte entre signataires déclarés et signataires compilés ; - la limite wire Solana de 1 232 octets. Un plan `DurableNonce` est refusé et doit utiliser le chemin dédié. ## Lecture et validation d’un compte nonce ### `durable_nonce_account_data_length()` Retourne la taille wire officielle d’un compte nonce, actuellement 80 octets. Cette valeur doit être utilisée comme limite exacte d’un appel `getAccountInfo` avec données complètes. ### `parse_durable_nonce_account(account, owner, executable, space, data)` Décode l’état officiel `solana_nonce::versions::Versions` avec `wincode::deserialize_exact` et produit un `DurableNonceAccountState` uniquement lorsque toutes les conditions suivantes sont satisfaites : - adresse du compte valide ; - owner égal au System Program ; - compte non exécutable ; - `space` et longueur décodée égaux à la taille officielle ; - version `Current` ; - état `Initialized` ; - autorité et valeur nonce extraites sans approximation. Les états `Legacy`, `Uninitialized`, tronqués, suffixés ou possédés par un autre programme sont refusés. `DurableNonceAccountState` expose uniquement l’adresse, l’autorité, la valeur nonce utilisée comme blockhash et le coût par signature stocké dans l’état. Le chargement réseau reste extérieur à cette crate. Le contrat RPC prévu est : ```rust let config = kb_rpc::GetAccountInfoConfig::confirmed_with_data( kb_execution_solana::durable_nonce_account_data_length(), )?; ``` ## Assemblage durable nonce ### `build_durable_nonce_transaction(plan, nonce_state)` Construit une transaction non signée qui consomme l’état nonce validé. La fonction exige : - une politique `ExecutionBlockhashKind::DurableNonce` sans âge de blockhash récent ; - le même compte nonce dans la politique et dans l’état validé ; - la même autorité dans la politique et dans l’état validé ; - l’autorité nonce dans `required_signers` ; - au moins une instruction métier à exécuter après l’avance nonce. L’assemblage utilise `Message::new_with_nonce`, place l’instruction officielle `AdvanceNonceAccount` en position zéro, puis remplace le champ `recent_blockhash` par la valeur nonce extraite de l’état. Le plan d’origine n’est pas muté : `UnsignedSolanaTransaction::plan()` expose un plan effectif cloné contenant l’instruction injectée. Après compilation, la crate vérifie : - la reconnaissance de la transaction par `solana_transaction::uses_durable_nonce` ; - l’égalité exacte entre signataires déclarés et signataires compilés ; - la taille wire ; - le hash du message et la valeur nonce effectivement compilée. Les opérations de gestion du compte nonce elles-mêmes, telles que `initialize`, `authorize`, `withdraw` ou `upgrade`, restent des transactions ordinaires à recent blockhash. Elles ne passent pas par ce chemin. ## Simulation et signature ### `UnsignedSolanaTransaction` Expose : - le plan effectif ; - le hash exact du message ; - le recent blockhash ou la valeur nonce compilée ; - l’ordre exact des signataires ; - le message base64 pour `getFeeForMessage` ; - la transaction non signée base64 pour `simulateTransaction` avec `sigVerify = false` et `replaceRecentBlockhash = false`. ### `bind_simulation(result)` Lie le résultat typé de simulation au hash du message **et** au blockhash/nonce compilé. Une simulation utilisant un autre message, une autre source de blockhash ou un blockhash de remplacement ne peut pas autoriser la signature. ### `sign_after_simulation(evidence, signers)` Réévalue `kb_execution_safety`, exige une décision `Allow`, résout exactement tous les signataires via `&dyn solana_signer::Signer`, signe puis vérifie que la transaction est complète. Les signataires manquants, supplémentaires ou dupliqués sont refusés. La fonction ne contacte aucun endpoint RPC et n’envoie rien. ### `SignedSolanaTransaction` Expose uniquement des données non secrètes : - hash du message ; - signature primaire ; - public keys des signataires ; - taille wire ; - payload base64 signé ; - vérification locale des signatures. Le payload signé reste côté backend. Il ne doit pas être transféré automatiquement à Tauri. `kb_rpc::HttpClient::send_transaction` et sa variante pool imposent le preflight et refusent une signature RPC différente de la signature primaire locale.