Ejecución de programas

Resumen

Los programas se compilan a sBPF mediante LLVM y se ejecutan en una VM aislada con un presupuesto de 1.4M CU por transacción. El runtime almacena en caché hasta 512 programas compilados, proporciona syscalls para logging, CPI, criptografía y memoria, y retrasa los nuevos despliegues en 1 slot.

Compilación

Solana utiliza LLVM para compilar programas en binarios ELF que contienen Solana Bytecode Format (sBPF). El binario ELF se almacena on-chain en una cuenta ejecutable.

sBPF es la variante personalizada de Solana del bytecode eBPF, adaptada para el runtime de Solana. No es eBPF estándar y tiene modificaciones específicas de Solana.

Escribir programas

Los programas de Solana se escriben principalmente en Rust utilizando uno de dos enfoques:

Modelo de ejecución de programas

Cuando se procesa una transacción, el runtime ejecuta cada instrucción secuencialmente a través de process_message(). Para cada instrucción, el runtime:

  1. Prepara el contexto de la instrucción. Llama a prepare_next_top_level_instruction() para mapear los índices de cuenta de la instrucción, establecer los flags de firmante y escritura, y configurar el TransactionContext.

  2. Verifica precompilados. Si el programa es un precompilado, el runtime llama a process_precompile(), que aún empuja y extrae un stack frame (mediante push() y pop()) pero omite la VM sBPF y la búsqueda en la caché de programas, ejecutando código nativo directamente.

  3. Empuja un marco de pila. (Los pasos 3-6 ocurren dentro de InvokeContext::process_instruction() y process_executable_chain(), llamados desde process_message().) Llama a push() en InvokeContext, que incrementa la altura de la pila de instrucciones y aplica la regla de reentrada: un programa solo puede volver a entrar en sí mismo si el llamador inmediato (el programa en la parte superior actual de la pila de instrucciones) es el mismo programa. Se permite la auto-recursión profunda (A -> A -> A), sujeta a límites de profundidad de pila. Otros patrones de reentrada (por ejemplo, A llama a B llama a A) devuelven InstructionError::ReentrancyNotAllowed.

  4. Resuelve el programa. El runtime llama a process_executable_chain() que determina el cargador. Si el propietario de la cuenta del programa es el cargador nativo, el programa es un builtin y su función de punto de entrada se busca directamente desde el ProgramCacheForTxBatch. Si el propietario es uno de los cargadores BPF (bpf_loader_deprecated, bpf_loader, bpf_loader_upgradeable, o loader_v4), se invoca en su lugar el punto de entrada builtin del propio cargador.

  5. Ejecuta el programa BPF. Para programas BPF, el punto de entrada del cargador busca el ejecutable compilado desde la caché de programas. La función execute() entonces:

    • Serializa los datos de la cuenta en un búfer de parámetros plano
    • Crea la VM sBPF con regiones de pila, heap y memoria
    • Ejecuta el código compilado, consumiendo unidades de cómputo durante la ejecución. Devuelve ComputationalBudgetExceeded si se excede el presupuesto.
    • Deserializa los datos de la cuenta desde el búfer de vuelta al estado de la cuenta
  6. Extrae el marco de pila. Llama a pop() que verifica que la instrucción no violó las reglas de contabilidad del runtime (los balances de lamports están equilibrados, las cuentas de solo lectura no fueron modificadas, los tamaños de datos de cuenta están dentro de los límites).

  7. Acumula unidades de cómputo. Las unidades de cómputo consumidas por la instrucción se agregan al total de la transacción mediante saturating_add.

Caché de programas

El runtime mantiene una ProgramCache global que almacena programas verificados y compilados. Es consciente del grafo de bifurcaciones y maneja las reglas de visibilidad de despliegue, desalojo y recompilación en límites de epoch.

Tipos de entrada de caché

Cada programa en caché tiene un ProgramCacheEntryType que determina su comportamiento en tiempo de ejecución:

TipoDescripción
LoadedPrograma verificado y compilado, listo para ejecución.
BuiltinPrograma nativo compilado en el binario del validador (System, Stake, Vote, etc.). No se almacena en cadena.
UnloadedPrograma previamente verificado cuyo ejecutable compilado fue desalojado de la memoria para liberar espacio. Aún rastrea estadísticas de uso. Puede recargarse sin re-verificación.
FailedVerificationMarcador para programas que no pasaron el verificador sBPF bajo el conjunto de características actual. Puede convertirse en Loaded si las activaciones de características cambian las reglas de verificación.
ClosedMarcador para programas que fueron explícitamente cerrados o nunca desplegados. También se usa para cuentas (como cuentas de búfer) que pertenecen a un cargador pero no contienen código ejecutable.
DelayVisibilityMarcador sintético devuelto por ProgramCacheForTxBatch::find() cuando existe una entrada Loaded pero aún no es efectiva (su effective_slot está en el futuro). Nunca se almacena directamente en la caché.

Retraso de visibilidad

Los programas recién desplegados o actualizados no son efectivos inmediatamente. La constante DELAY_VISIBILITY_SLOT_OFFSET es 1, lo que significa que un programa desplegado en el slot N se vuelve efectivo en el slot N+1. Durante el slot de despliegue, cualquier intento de invocar la nueva versión devuelve DelayVisibility, causando que el runtime reporte "El programa no está desplegado".

Política de desalojo

La caché almacena hasta MAX_LOADED_ENTRY_COUNT (512) entradas de programas compilados. Cuando se alcanza el límite, los programas menos utilizados se desalojan al estado Unloaded. El uso se rastrea mediante tx_usage_counter (incrementado cada vez que una transacción hace referencia al programa) y latest_access_slot.

Recompilación en límite de epoch

Si una activación de característica cambia el ProgramRuntimeEnvironments en un límite de epoch, todos los programas en caché se recompilan contra el nuevo entorno.

Datos de retorno

Los programas pueden establecer datos de retorno mediante la syscall sol_set_return_data. Los datos se almacenan en una estructura TransactionReturnData a nivel de transacción que contiene los bytes de datos y el program_id del programa cuya instrucción llamó a la syscall. El tamaño máximo es de 1.024 bytes (MAX_RETURN_DATA).

Is this page helpful?

Tabla de Contenidos

Editar Página

Gestionado por

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