Runtime de cuentas

Resumen

Antes de la ejecución, el runtime carga las cuentas, valida el pagador de comisiones, verifica la exención de renta y serializa los datos de las cuentas en un diseño de memoria al que los programas pueden acceder.

Esta página cubre aspectos internos del runtime. La mayoría de los desarrolladores no necesitan esta información para construir programas. Consulta Estructura de cuentas para la vista orientada al desarrollador.

Carga de cuentas

Antes de que se ejecute una transacción, el runtime carga todas las cuentas referenciadas mediante load_transaction_accounts(). Este proceso realiza varias validaciones:

  1. Validación del pagador de comisiones: el pagador de comisiones (primera cuenta) debe existir, ser una cuenta del sistema o una cuenta nonce, y tener suficientes lamports para cubrir las comisiones (validate_fee_payer()). Después de pagar las comisiones, la cuenta debe permanecer exenta de renta o llegar exactamente a 0 lamports. No puede terminar entre 0 y el mínimo de exención de renta. Las cuentas nonce siempre deben conservar suficientes lamports para permanecer exentas de renta. Si el pagador no es una cuenta del sistema ni una cuenta nonce, la transacción falla con TransactionError::InvalidAccountForFee.

  2. Límite de tamaño de datos cargados: el tamaño total de todas las cuentas cargadas (incluyendo un TRANSACTION_ACCOUNT_BASE_SIZE de 64 bytes por cuenta) no debe exceder MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES (64 MiB). Exceder este límite produce TransactionError::MaxLoadedAccountsDataSizeExceeded.

  3. Validación de cuenta de programa: cada programa invocado por una instrucción debe existir y ser propiedad de un cargador válido: uno de los PROGRAM_OWNERS (BPF Loader Upgradeable, BPF Loader, BPF Loader Deprecated, Loader V4) o el cargador nativo. Si la cuenta del programa no existe, la transacción falla con TransactionError::ProgramAccountNotFound. Si existe pero tiene un propietario inválido, la transacción falla con TransactionError::InvalidProgramForExecution.

  4. Cuentas inexistentes: Las cuentas que no existen en la cadena se cargan como cuentas predeterminadas (0 lamports, datos vacíos, propiedad del System Program) con rent_epoch establecido en u64::MAX.

Formato de serialización BPF

Cuando se invoca un programa, el runtime serializa las cuentas en un búfer de memoria contiguo y lo pasa a la VM BPF. El formato de serialización (para el formato alineado estándar utilizado por todos los cargadores excepto el loader-v1 obsoleto) está definido en serialize_parameters_aligned().

El búfer comienza con un u64 (8 bytes, little-endian) que contiene el número de cuentas. Luego, para cada cuenta en la instrucción, el búfer contiene:

DesplazamientoTamañoCampoTipo
01marcador de duplicadou8 (0xFF = único, índice = duplicado de esa cuenta)
11is_signeru8 (0 o 1)
21is_writableu8 (0 o 1)
31executableu8 (0 o 1)
44original_data_len (reservado, siempre 0)[0u8; 4]
832keyPubkey
4032ownerPubkey
728lamportsu64 (little-endian)
808data_lenu64 (little-endian)
88data_lendata[u8]
88 + data_len10240 + paddingespacio de realloc + alineaciónRellenado con ceros hasta MAX_PERMITTED_DATA_INCREASE (10 KiB) + padding para alinear a BPF_ALIGN_OF_U128 (8 bytes)
...8rent_epochu64 (little-endian)

Después de todas las cuentas, el búfer añade:

TamañoCampo
8instruction_data_len (u64, little-endian)
instruction_data_leninstruction_data
32program_id (Pubkey)

Para cuentas duplicadas, solo se escriben 1 byte (el marcador de duplicado con el índice del original) más 7 bytes de relleno.

Deduplicación de cuentas

Cuando la misma clave pública de cuenta aparece múltiples veces en el array accounts de una instrucción, el runtime deduplica las entradas. Cada entrada en la lista de cuentas de la instrucción obtiene su propia estructura InstructionAccount, pero las entradas que se refieren a la misma cuenta a nivel de transacción apuntan a los mismos datos subyacentes.

El método is_instruction_account_duplicate determina si un índice de cuenta de instrucción dado es la primera ocurrencia o un duplicado buscando el índice de cuenta a nivel de transacción y encontrando el primer índice a nivel de instrucción que mapea a él:

  • Si el índice de cuenta de instrucción actual es igual al primer índice mapeado, no es un duplicado (devuelve None).
  • De lo contrario, devuelve Some(first_index), donde first_index es el índice de la primera ocurrencia.

Debido a que todas las referencias a la misma cuenta comparten el mismo AccountSharedData subyacente, las modificaciones a través de cualquier referencia son inmediatamente visibles a través de todas las demás referencias. Sin embargo, solo se puede mantener un préstamo mutable a la vez. Intentar tomar prestada la misma cuenta de forma mutable a través de dos índices de cuenta de instrucción diferentes simultáneamente devuelve InstructionError::AccountBorrowFailed. Los programas deben liberar un préstamo antes de adquirir otro sobre la misma cuenta subyacente.

Después de que el programa se ejecuta, el runtime deserializa el búfer de vuelta (deserialize_parameters_aligned()) y aplica cualquier cambio a lamports, data (incluyendo cambios de longitud hasta MAX_PERMITTED_DATA_INCREASE), y owner.

Is this page helpful?

Tabla de Contenidos

Editar Página

Gestionado por

© 2026 Fundación Solana.
Todos los derechos reservados.
Conéctate