Le modèle de comptes#
Le modèle de comptes est le concept le plus fondamental de Solana. Toute intéraction avec le réseau – transfert de SOL, exécution d’un programme, création d’un token – se ramène à la lecture ou à l’écriture de comptes. Comprendre leur structure interne, leurs rêgles de propriété et leur coût de stockage est un préalable indispensable à tout developpement sur Solana.
Contrairement à Bitcoin, qui raisonne en termes de sorties de transactions non dépensées (UTXO), Solana adopte un modèle de comptes analogue à celui d’Ethereum : l’état du réseau est un ensemble global de comptes, chacun identifié par une clé publique et porteur d’un solde et de données arbitraires. Ce choix architectural a des conséquences profondes sur la parallélisation des transactions, la programmation des contrats intelligents et la gestion de la mémoire.
Dans ce chapitre, nous comparons d’abord les deux grands modèles d’état (comptes vs UTXO), puis nous détaillons l’anatomie d’un compte Solana, les différents types de comptes, le mécanisme de loyer (rent) et les programmes système natifs. L’objectif est de fournir une compréhension solide de la couche de données sur laquelle repose tout le reste de l’écosystème.
Modèle de comptes vs UTXO#
Deux modèles fondamentaux coexistent dans l’univers des blockchains pour représenter l’état du réseau. Chacun implique des compromis différents en matière de parallélisme, de contrats intelligents et de confidentialité.
Définition 42 (Modèle de comptes)
Le modèle de comptes (account model), utilisé par Solana et Ethereum, représente l’état du réseau comme une collection globale de comptes. Chaque compte possède une adresse unique, un solde et un espace de données. Une transaction modifie directement l’état des comptes concernés. L’état global est donc un instantané de tous les comptes à un instant donné.
Définition 43 (Modèle UTXO)
Le modèle UTXO (Unspent Transaction Output), utilisé par Bitcoin et Cardano, représente l’état du réseau comme l’ensemble des sorties de transactions non encore dépensées. Il n’existe pas de notion de « compte » avec un solde modifiable : le solde d’un utilisateur est la somme des UTXO qu’il contrôle. Chaque transaction consomme des UTXO existants et en crée de nouveaux.
Le tableau suivant résume les différences principales entre les deux modèles.
Critère |
Modèle de comptes |
Modèle UTXO |
|---|---|---|
Représentation de l’état |
Etat global (mapping adresse \(\to\) compte) |
Ensemble de sorties non dépensées |
Solde |
Champ mutable dans le compte |
Somme des UTXO controlés |
Parallélisme |
Possible si les comptes accédés sont disjoints |
Naturel (chaque UTXO est indépendant) |
Contrats intelligents |
Naturels (les programmes lisent/écrivent les comptes) |
Limités (scripts de validation) |
Confidentialité |
Faible (les soldes sont publics) |
Meilleure (nouvelles adresses à chaque transaction) |
Complexité de validation |
Vérification de l’état global |
Vérification locale (chaque UTXO est autonome) |
Remarque 31
Solana a choisi le modèle de comptes pour une raison précise : il permet au runtime Sealevel d’exécuter des transactions en parallèle. Avant l’exécution, chaque transaction déclare explicitement les comptes qu’elle lira ou écrira. Le scheduler peut alors exécuter simultanément les transactions qui touchent des ensembles de comptes disjoints, exploitant ainsi pleinement les architectures multi-coeurs. Ce parallélisme est au coeur de la capacité de Solana à traiter des milliers de transactions par seconde.
Anatomie d’un compte Solana#
Chaque compte sur Solana est une structure de données stockée dans la mémoire globale du cluster. Sa taille et sa composition sont determinées par six champs.
Définition 44 (Compte Solana)
Un compte Solana (Solana Account) est un enregistrement dans l’état global du réseau, défini par les champs suivants :
key(Pubkey, 32 octets) : l’adresse unique du compte, dérivée d’une clé publique Ed25519 ou d’un calcul déterministe (PDA).lamports(u64, 8 octets) : le solde du compte exprimé en lamports, la plus petite unité de SOL. On a \(1 \text{ SOL} = 10^9 \text{ lamports}\).owner(Pubkey, 32 octets) : l’adresse du programme propriétaire du compte. Seul le programme propriétaire peut modifier le champdata.data(Vec<u8>, taille variable) : un tampon d’octets contenant des données arbitraires (état d’un programme, bytecode, métadonnées, etc.).executable(bool, 1 octet) : indicateur qui vauttruesi le compte contient un programme exécutable (bytecode BPF/SBF).rent_epoch(u64, 8 octets) : l’époque à laquelle le loyer a été collecté pour la dernière fois.
La structure Rust correspondante dans le runtime Solana est la suivante :
pub struct Account {
pub lamports: u64,
pub data: Vec<u8>,
pub owner: Pubkey,
pub executable: bool,
pub rent_epoch: Epoch,
}
Remarque 32
La rêgle de propriété est fondamentale : seul le programme propriétaire (identifié par le champ owner) peut modifier le champ data d’un compte. En revanche, n’importe qui peut créditer des lamports à un compte (augmenter son solde). Seul le propriétaire peut débiter des lamports. Cette asymétrie est une mesure de sécurité : elle empêche un programme malveillant de modifier les données d’un compte qu’il ne possède pas, tout en permettant les transferts entrants sans restriction.
La visualisation suivante montre la structure d’un compte Solana sous forme de rectangles empilés, avec les noms de champs, les types et les tailles.
Types de comptes#
Tous les comptes partagent la même structure interne, mais leur rôle dans le réseau varie selon leur propriétaire, leur contenu et la valeur du champ executable. On distingue quatre types principaux.
Comptes système (portefeuilles)#
Définition 45 (Compte système)
Un compte système (system account) est un compte dont le propriétaire (owner) est le System Program (11111111111111111111111111111111). Son champ data est vide (0 octet) et son champ executable est false. Ce type de compte représente un portefeuille : il ne stocke qu’un solde en SOL. C’est le type de compte créé par défaut lorsqu’un utilisateur reçoit du SOL pour la première fois.
Comptes de programme#
Définition 46 (Compte de programme)
Un compte de programme (program account) est un compte dont le champ executable vaut true. Son champ data contient le bytecode BPF/SBF compilé du programme. Le propriétaire d’un compte de programme est généralement le BPF Loader. Un programme est immuable une fois deployé (sauf s’il a été déployé comme upgradeable). Les programmes sont sans état : ils ne stockent pas de données dans leur propre compte, mais lisent et écrivent dans des comptes de données séparés.
Comptes de données#
Définition 47 (Compte de données)
Un compte de données (data account) est un compte dont le propriétaire est un programme spécifique (autre que le System Program). Son champ data contient l’état sérialisé du programme : paramètres de configuration, soldes de tokens, votes, etc. Le champ executable vaut false. Seul le programme propriétaire peut modifier le contenu de data.
Exemple 15
Un compte de token SPL est un compte de données dont le propriétaire est le SPL Token Program (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA). Son champ data contient, entre autres, l’adresse du propriétaire du token (authority), l’adresse du mint, et le solde en tokens. La structure sérialisée occupe exactement 165 octets.
Program Derived Addresses (PDA)#
Définition 48 (Program Derived Address (PDA))
Une Program Derived Address (PDA) est une adresse de compte dérivée de manière déterministe à partir d’un ensemble de seeds (graines) et de l’identifiant du programme (program_id). La dérivation utilise la fonction :
La propriété essentielle d’une PDA est qu’elle ne se trouve pas sur la courbe Ed25519 : il n’existe aucune clé privée correspondante. Le programme qui a généré la PDA peut « signer » des instructions pour ce compte via le mécanisme de cross-program invocation (CPI), sans avoir besoin d’une clé privée.
Le code Rust suivant illustre la dérivation d’une PDA :
// Dérivation d'une PDA à partir de seeds
let (pda, bump) = Pubkey::find_program_address(
&[b"user-stats", user_pubkey.as_ref()],
&program_id,
);
// pda : l'adresse dérivée
// bump : le bump seed utilisé pour garantir que l'adresse
// est hors de la courbe Ed25519
Remarque 33
La fonction find_program_address essaie des valeurs de bump seed décroissantes (de 255 a 0) jusqu’à trouver une adresse qui n’est pas sur la courbe Ed25519. Le premier bump qui produit une adresse valide est appelé le bump canonique. Il est recommandé de toujours stocker et réutiliser ce bump pour éviter des collisions et pour des raisons de sécurité. En Anchor, le bump est accessible via :
#[account(
seeds = [b"user-stats", user.key().as_ref()],
bump,
)]
pub user_stats: Account<'info, UserStats>,
La visualisation suivante montre les relations de propriété entre les différents types de comptes sous forme de graphe orienté.
Le concept de rent (loyer)#
Le stockage sur une blockchain est une ressource rare : chaque octet de données doit être maintenu en mémoire par l’ensemble des validateurs. Solana utilise un mécanisme de loyer (rent) pour inciter les utilisateurs à ne pas gaspiller cet espace.
Définition 49 (Rent (loyer))
Le rent (loyer) est le coût de stockage qu’un compte Solana doit acquitter pour occuper de l’espace dans l’état global du réseau. Le loyer est proportionnel à la taille du champ data du compte. Un compte qui ne peut plus payer son loyer est supprimé par le runtime et ses lamports sont récupérés.
Définition 50 (Exemption de loyer)
Un compte est exempté de loyer (rent-exempt) si son solde en lamports est supérieur ou égal au seuil d’exemption, qui correspond à deux années de loyer. Un compte exempté de loyer est permanent : il ne sera jamais supprimé tant que son solde reste au-dessus du seuil. Depuis 2023, Solana exige que tous les comptes soient rent-exempt à leur création.
Le seuil d’exemption de loyer suit une formule linéaire approchée :
ou data_size est la taille du champ data en octets. Le terme constant (\(890\,880\) lamports) correspond au coût fixe pour les métadonnées du compte (128 octets d’en-tête).
Exemple 16
Calculons le seuil d’exemption pour quelques tailles de comptes courantes :
Portefeuille (0 octet de données) : \(19.055 \times 0 + 890\,880 = 890\,880\) lamports \(\approx 0.00089\) SOL.
Compte de token SPL (165 octets) : \(19.055 \times 165 + 890\,880 = 894\,024\) lamports \(\approx 0.00204\) SOL.
Compte de métadonnées NFT (\(\sim\!680\) octets) : \(19.055 \times 680 + 890\,880 = 903\,837\) lamports \(\approx 0.0136\) SOL.
Compte de 10 ko (\(10\,240\) octets) : \(19.055 \times 10\,240 + 890\,880 = 1\,086\,003\) lamports \(\approx 0.086\) SOL.
La commande CLI suivante permet de vérifier le seuil d’exemption pour une taille donnée :
# Seuil d'exemption pour un compte de 165 octets
solana rent 165
# Rent-exempt minimum: 0.00203928 SOL
# Seuil d'exemption pour un compte de 10240 octets
solana rent 10240
# Rent-exempt minimum: 0.07385280 SOL
Remarque 34
Historiquement, le loyer était collecté à chaque époque (~2 jours) lors de l’accès au compte par une transaction. Un compte non exempté voyait son solde diminuer progressivement. Depuis la mise à jour de 2023, cette collecte périodique a été supprimée : tous les comptes doivent être rent-exempt à la création. Le champ rent_epoch subsiste dans la structure du compte mais n’est plus utilisé activement. On peut le considérer comme un vestige historique. En pratique, la commande suivante permet de vérifier si un compte est exempté :
solana account <PUBKEY>
# Le champ "Rent Epoch" est affiché dans la sortie
Programmes système natifs#
Solana est livré avec un ensemble de programmes natifs (native programs) prédéployés à des adresses fixes. Ces programmes fournissent les fonctionnalités de base du réseau. Contrairement aux programmes déployés par les utilisateurs, ils font partie du runtime et sont mis à jour avec le logiciel du validateur.
Définition 51 (System Program)
Le System Program (adresse 11111111111111111111111111111111) est le programme le plus fondamental de Solana. Il fournit les instructions suivantes :
CreateAccount: crée un nouveau compte avec une taille de données et un propriétaire spécifiés.Transfer: transfère des lamports d’un compte à un autre.Allocate: alloue de l’espace (champdata) à un compte existant.Assign: change le propriétaire (owner) d’un compte.
Tout compte nouvellement créé est initialement possédé par le System Program, jusqu’à ce qu’une instruction Assign ou CreateAccount (avec un owner spécifié) le transfère à un autre programme.
Définition 52 (BPF Loader)
Le BPF Loader (adresse BPFLoaderUpgradeab1e11111111111111111111111) est le programme responsable du déploiement et de la mise à jour des programmes utilisateurs. Il charge le bytecode BPF/SBF dans un compte de programme et marque ce compte comme executable = true. La version upgradeable permet de mettre à jour le bytecode d’un programme après son déploiement initial, sous réserve que l’autorité de mise à jour (upgrade authority) signe la transaction.
Définition 53 (SPL Token Program)
Le SPL Token Program (adresse TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA) est le programme standard pour la création et la gestion de tokens sur Solana. Il gère aussi bien les tokens fongibles (équivalents de ERC-20 sur Ethereum) que les tokens non fongibles (NFT, équivalents de ERC-721). Ses instructions principales sont :
InitializeMint: crée un nouveau type de token (mint).InitializeAccount: crée un compte pour detenir des tokens d’un mint donné.MintTo: émettre de nouveaux tokens.Transfer: transférer des tokens entre comptes.Burn: détruire des tokens.
Définition 54 (Associated Token Account Program)
L”Associated Token Account Program (adresse ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL) fournit un mécanisme pour dériver une adresse de compte de token déterministe à partir de l’adresse du portefeuille et de l’adresse du mint. L’adresse est calculée comme une PDA :
Cela signifie que pour toute paire (portefeuille, mint), il existe une et une seule adresse de compte de token associé. Ce déterminisme simplifie les transferts : l’expéditeur n’a pas besoin de demander au destinataire l’adresse de son compte de token.
Exemple 17
Pour créer un nouveau compte de données de 256 octets possédé par un programme personnalisé, on utilise l’instruction CreateAccount du System Program. En Rust (client-side) :
let create_ix = system_instruction::create_account(
&payer.pubkey(), // financeur
&new_account.pubkey(), // nouveau compte
rent_exempt_lamports, // solde minimum (rent-exempt)
256, // taille des données en octets
&my_program_id, // propriétaire du nouveau compte
);
Cette instruction atomique effectue trois operations : elle alloue l’espace, transfère les lamports pour l’exemption de loyer et assigne le propriétaire.
Les commandes CLI suivantes permettent d’inspecter les comptes sur le réseau :
# Afficher les détails d'un compte (owner, lamports, data, executable)
solana account <PUBKEY>
# Afficher le solde en SOL d'un compte
solana balance <PUBKEY>
# Afficher le solde en SOL du portefeuille par défaut
solana balance
# Lister les comptes de tokens possédés par un portefeuille
spl-token accounts
# Afficher les détails d'un mint
spl-token display <MINT_ADDRESS>
La sortie de solana account ressemble à ceci :
$ solana account TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
# Public Key: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
# Balance: 1.14157584 SOL
# Owner: BPFLoaderUpgradeab1e11111111111111111111111
# Executable: true
# Rent Epoch: 361
Remarque 35
La séparation du code et de l’état est un principe architectural central de Solana. Contrairement à Ethereum, ou le bytecode et le stockage vivent dans le même contrat, Solana impose que les programmes soient sans état : leur compte ne contient que du bytecode, et tout état mutable est stocké dans des comptes de données séparés. Cette séparation a trois avantages majeurs :
Un même programme peut gérer un nombre arbitraire de comptes de données, sans limite de stockage intrinsèque.
Le programme peut être mis à jour indépendamment des données qu’il gère (avec le BPF Loader upgradeable).
Le parallélisme est facilité : le runtime peut exécuter le même programme sur des comptes de données différents simultanément, puisque le code est en lecture seule.
// Illustration : un programme counter avec un compte de données séparé
#[account]
pub struct Counter {
pub count: u64, // 8 octets
pub authority: Pubkey, // 32 octets
}
// Le programme (code) et le Counter (données) vivent dans des comptes distincts.
Résumé#
Le tableau suivant récapitule les concepts clés de ce chapitre.
Concept |
Description |
|---|---|
Modèle de comptes |
L’état global est une collection de comptes (adresse \(\to\) données) |
Compte Solana |
Structure à 6 champs : |
Compte système |
Portefeuille simple, |
Compte de programme |
|
Compte de données |
|
PDA |
Adresse déterministe hors courbe Ed25519, dérivée de seeds + |
Rent (loyer) |
Coût de stockage ; exemption si solde \(\geq\) 2 ans de loyer |
Règle de propriété |
Seul le programme |
System Program |
Crée les comptes, transfère du SOL, alloue de l’espace |
BPF Loader |
Déploie et met à jour les programmes |
SPL Token Program |
Gère les tokens fongibles et non fongibles |
ATA Program |
Dérive des adresses de comptes de token déterministes |
Séparation code/état |
Les programmes sont sans état ; les données vivent dans des comptes séparés |