Résumé
Avant l'exécution, le runtime charge les comptes, valide le payeur de frais, vérifie l'exemption de loyer et sérialise les données de compte dans une disposition mémoire accessible aux programmes.
Cette page couvre les mécanismes internes du runtime. La plupart des développeurs n'ont pas besoin de ces informations pour créer des programmes. Consultez Structure des comptes pour la vue destinée aux développeurs.
Chargement des comptes
Avant l'exécution d'une transaction, le runtime charge tous les comptes
référencés via
load_transaction_accounts().
Ce processus effectue plusieurs validations :
-
Validation du payeur de frais : le payeur de frais (premier compte) doit exister, être un compte système ou un compte nonce, et disposer de suffisamment de lamports pour couvrir les frais (
validate_fee_payer()). Après paiement des frais, le compte doit soit rester exempté de loyer, soit atteindre exactement 0 lamport. Il ne peut pas se retrouver entre 0 et le minimum d'exemption de loyer. Les comptes nonce doivent toujours conserver suffisamment de lamports pour rester exemptés de loyer. Si le payeur n'est ni un compte système ni un compte nonce, la transaction échoue avecTransactionError::InvalidAccountForFee. -
Limite de taille des données chargées : la taille totale de tous les comptes chargés (incluant un
TRANSACTION_ACCOUNT_BASE_SIZEde 64 octets par compte) ne doit pas dépasserMAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES(64 Mio). Le dépassement de cette limite produitTransactionError::MaxLoadedAccountsDataSizeExceeded. -
Validation du compte de programme : chaque programme invoqué par une instruction doit exister et appartenir à un chargeur valide : l'un des
PROGRAM_OWNERS(BPF Loader Upgradeable, BPF Loader, BPF Loader Deprecated, Loader V4) ou le chargeur natif. Si le compte de programme n'existe pas, la transaction échoue avecTransactionError::ProgramAccountNotFound. S'il existe mais possède un propriétaire invalide, la transaction échoue avecTransactionError::InvalidProgramForExecution. -
Comptes non existants : les comptes qui n'existent pas on-chain sont chargés en tant que comptes par défaut (0 lamport, données vides, détenus par le programme système) avec
rent_epochdéfini suru64::MAX.
Format de sérialisation BPF
Lorsqu'un programme est invoqué, le runtime sérialise les comptes dans un tampon
mémoire contigu et le transmet à la VM BPF. Le format de sérialisation (pour le
format aligné standard utilisé par tous les chargeurs sauf le loader-v1
obsolète) est défini dans
serialize_parameters_aligned().
Le tampon commence par un u64 (8 octets, little-endian) contenant le nombre de
comptes. Ensuite, pour chaque compte dans l'instruction, le tampon contient :
| Décalage | Taille | Champ | Type |
|---|---|---|---|
| 0 | 1 | marqueur de duplication | u8 (0xFF = unique, index = duplication de ce compte) |
| 1 | 1 | is_signer | u8 (0 ou 1) |
| 2 | 1 | is_writable | u8 (0 ou 1) |
| 3 | 1 | executable | u8 (0 ou 1) |
| 4 | 4 | original_data_len (réservé, toujours 0) | [0u8; 4] |
| 8 | 32 | key | Pubkey |
| 40 | 32 | owner | Pubkey |
| 72 | 8 | lamports | u64 (little-endian) |
| 80 | 8 | data_len | u64 (little-endian) |
| 88 | data_len | data | [u8] |
| 88 + data_len | 10240 + padding | espace de réallocation + alignement | Rempli de zéros jusqu'à MAX_PERMITTED_DATA_INCREASE (10 Kio) + padding pour aligner sur BPF_ALIGN_OF_U128 (8 octets) |
| ... | 8 | rent_epoch | u64 (little-endian) |
Après tous les comptes, le buffer ajoute :
| Taille | Champ |
|---|---|
| 8 | instruction_data_len (u64, little-endian) |
| instruction_data_len | instruction_data |
| 32 | program_id (Pubkey) |
Pour les comptes dupliqués, seul 1 octet (le marqueur de duplication avec l'index de l'original) plus 7 octets de remplissage sont écrits.
Déduplication des comptes
Lorsque la même clé publique de compte apparaît plusieurs fois dans le tableau
accounts d'une instruction, le runtime les
déduplique.
Chaque entrée dans la liste de comptes de l'instruction obtient sa propre
structure
InstructionAccount,
mais les entrées qui font référence au même compte au niveau de la transaction
pointent vers les mêmes données sous-jacentes.
La méthode
is_instruction_account_duplicate
détermine si un index de compte d'instruction donné est la première occurrence
ou un duplicata en recherchant l'index de compte au niveau de la transaction et
en trouvant le premier index au niveau de l'instruction qui y correspond :
- Si l'index de compte d'instruction actuel est égal au premier index mappé, ce
n'est pas un duplicata (retourne
None). - Sinon, il retourne
Some(first_index), oùfirst_indexest l'index de la première occurrence.
Étant donné que toutes les références au même compte partagent le même
AccountSharedData sous-jacent, les modifications effectuées via n'importe
quelle référence sont immédiatement visibles via toutes les autres références.
Cependant, un seul emprunt mutable peut être détenu à la fois. Tenter
d'emprunter le même compte de manière mutable via deux index de compte
d'instruction différents simultanément retourne
InstructionError::AccountBorrowFailed. Les programmes doivent libérer un
emprunt avant d'en acquérir un autre sur le même compte sous-jacent.
Après l'exécution du programme, le runtime désérialise le buffer
(deserialize_parameters_aligned())
et applique tous les changements à lamports, data (y compris les changements
de longueur jusqu'à MAX_PERMITTED_DATA_INCREASE), et owner.
Is this page helpful?