Account Runtime

Summary

Before execution, the runtime loads accounts, validates the fee payer, checks rent-exemption, and serializes account data into a memory layout that programs can access.

This page covers runtime internals. Most developers don't need this information for building programs. See Account Structure for the developer-facing view.

Account loading

Before a transaction executes, the runtime loads all referenced accounts via load_transaction_accounts(). This process performs several validations:

  1. Fee payer validation: The fee payer (first account) must exist, be a system account or nonce account, and have enough lamports to cover fees (validate_fee_payer()). After paying fees, the account must either remain rent-exempt or go to exactly 0 lamports. It cannot end up between 0 and the rent-exempt minimum. Nonce accounts must always retain enough lamports to remain rent-exempt. If the payer is neither a system account nor a nonce account, the transaction fails with TransactionError::InvalidAccountForFee.

  2. Loaded data size limit: The total size of all loaded accounts (including a TRANSACTION_ACCOUNT_BASE_SIZE of 64 bytes per account) must not exceed MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES (64 MiB). Exceeding this limit produces TransactionError::MaxLoadedAccountsDataSizeExceeded.

  3. Program account validation: Every program invoked by an instruction must exist and be owned by a valid loader: one of the PROGRAM_OWNERS (BPF Loader Upgradeable, BPF Loader, BPF Loader Deprecated, Loader V4) or the native loader. If the program account does not exist, the transaction fails with TransactionError::ProgramAccountNotFound. If it exists but has an invalid owner, the transaction fails with TransactionError::InvalidProgramForExecution.

  4. Non-existent accounts: Accounts that do not exist on-chain are loaded as default accounts (0 lamports, empty data, owned by system program) with rent_epoch set to u64::MAX.

BPF serialization format

When a program is invoked, the runtime serializes accounts into a contiguous memory buffer and passes it to the BPF VM. The serialization format (for the standard aligned format used by all loaders except the deprecated loader-v1) is defined in serialize_parameters_aligned().

The buffer begins with a u64 (8 bytes, little-endian) containing the number of accounts. Then, for each account in the instruction, the buffer contains:

OffsetSizeFieldType
01duplicate markeru8 (0xFF = unique, index = duplicate of that account)
11is_signeru8 (0 or 1)
21is_writableu8 (0 or 1)
31executableu8 (0 or 1)
44original_data_len (reserved, always 0)[0u8; 4]
832keyPubkey
4032ownerPubkey
728lamportsu64 (little-endian)
808data_lenu64 (little-endian)
88data_lendata[u8]
88 + data_len10240 + paddingrealloc space + alignmentZero-filled to MAX_PERMITTED_DATA_INCREASE (10 KiB) + padding to align to BPF_ALIGN_OF_U128 (8 bytes)
...8rent_epochu64 (little-endian)

After all accounts, the buffer appends:

SizeField
8instruction_data_len (u64, little-endian)
instruction_data_leninstruction_data
32program_id (Pubkey)

For duplicate accounts, only 1 byte (the duplicate marker with the index of the original) plus 7 bytes of padding are written.

Account deduplication

When the same account public key appears multiple times in an instruction's accounts array, the runtime deduplicates them. Each entry in the instruction's account list gets its own InstructionAccount struct, but entries that refer to the same transaction-level account point to the same underlying data.

The is_instruction_account_duplicate method determines whether a given instruction account index is the first occurrence or a duplicate by looking up the transaction-level account index and finding the first instruction-level index that maps to it:

  • If the current instruction account index equals the first mapped index, it is not a duplicate (returns None).
  • Otherwise, it returns Some(first_index), where first_index is the index of the first occurrence.

Because all references to the same account share the same underlying AccountSharedData, modifications through any reference are immediately visible through all other references. However, only one mutable borrow can be held at a time. Attempting to borrow the same account mutably through two different instruction account indices simultaneously returns InstructionError::AccountBorrowFailed. Programs must drop one borrow before acquiring another on the same underlying account.

After the program executes, the runtime deserializes the buffer back (deserialize_parameters_aligned()) and applies any changes to lamports, data (including length changes up to MAX_PERMITTED_DATA_INCREASE), and owner.

Is this page helpful?

सामग्री तालिका

पृष्ठ संपादित करें

द्वारा प्रबंधित

© 2026 सोलाना फाउंडेशन। सर्वाधिकार सुरक्षित।
जुड़े रहें