Rust para Programas de Solana
El código en cadena de Solana es Rust, pero no es Rust de portátil con un main diferente. Compilas a sBPF, ejecutas dentro del SVM, y solo tocas el estado de la cadena a través de cuentas y llamadas al sistema.
Ese subconjunto - no_std, asignación restringida, un punto de entrada fijo, diseños Borsh (o zero-copy), matemáticas verificadas, errores tipados y presupuestos de cómputo/tamaño - es el lenguaje de cada programa nativo, Anchor y ligero en Agave 4.1.1.
Resumen
- Los programas de Solana son ejecutables sBPF escritos en un subconjunto de Rust compatible con
no_std: un punto de entrada, datos de instrucción y cuenta a nivel de byte (generalmente Borsh), aritmética segura, errores de fallo rápido y límites estrictos en unidades de cómputo y tamaño binario. - Por Qué Importa: Los hábitos de escritorio (
unwrap, crecimiento ilimitado deVec, dependenciasstd, desbordamiento silencioso, registro excesivo) o no compilan paracargo build-sbfo pasan las pruebas y luego fallan bajo presión de CU, alquiler y auditoría en mainnet-beta. - Conceptos Clave: sBPF / SVM, no_std / alloc, entrypoint, AccountInfo, Borsh, checked arithmetic, ProgramError, unidades de cómputo (CU), tamaño del programa.
- Cuándo Usar Este Modelo: Escribir o revisar cualquier crate en cadena (nativo, Anchor 0.32.1, o Pinocchio); incorporar ingenieros de Rust que no han enviado código BPF/SBF; decidir qué aprender a continuación en esta sección.
- Limitaciones / Compensaciones: Cambias la ergonomía del host por determinismo, programación paralela y ejecución medible. Características como tiempos de ejecución asíncronos, hilos y patrones de heap sin restricciones no aplican.
- Temas Relacionados: Fundamentos de Rust para Solana, no_std y el Entorno del Programa, Serialización Borsh, Aritmética Segura, Manejo de Errores en Cadena, Tamaño del Programa y Disciplina de CU.
Fundamentos
El código fuente de los programas de Solana es Rust 1.91.1 (el pin de este sitio), pero el target no es un triple de host.
cargo build-sbf (a través de las herramientas de la plataforma Agave / compilación de Anchor) compila cruzado a sBPF (Solana Bytecode Format). Los validadores cargan el .so y el SVM lo ejecuta bajo los bloqueos de Sealevel y la medición de cómputo.
No hay un main de proceso. El cargador invoca un único punto de entrada que el crate registra (nativo entrypoint!, o el despachador generado por Anchor). Esa función recibe program_id, un slice de AccountInfo, y bytes de instruction_data. Las variantes de instrucción, los diseños de cuenta, los PDA y las CPI se basan en ese triple.
El entorno es no_std: sin sistema de archivos, sockets, hilos o biblioteca estándar completa. alloc opcional proporciona Vec / String / Box, pero el uso del heap cuesta cómputo. El código de producción prefiere cuentas de tamaño fijo, análisis de pila y evitar format! en rutas críticas.
El estado duradero no vive en el binario del programa. Los registros de la aplicación residen en cuentas que el programa posee (o que otros programas poseen, mutadas solo a través de CPI). Las estructuras de Rust en cadena son esquemas sobre bytes de cuenta.
Borsh es la codificación de facto para instrucciones y la mayoría de las estructuras de cuenta (predeterminado de Anchor 0.32.1). Los diseños grandes y fijos a menudo usan bytemuck zero-copy en su lugar. El dinero y los contadores usan u64 (o enteros fijos más anchos) con operaciones verificadas. Los fallos devuelven ProgramError (o códigos personalizados mapeados); prefiera ? sobre unwrap. La ejecución se mide en unidades de cómputo, y el tamaño binario afecta el costo de despliegue. "Compila" no significa "encaja en los presupuestos de CU y tamaño".
Mecánicas e Interacciones
De código fuente Rust a sBPF
Los crates nativos dependen de solana-program (o Pinocchio). Los proyectos de Anchor usan anchor-lang = "0.32.1" para la generación del punto de entrada y la validación de cuentas.
Flujo de compilación: compilar a sBPF ELF con herramientas de plataforma, desplegar con Solana CLI 3.0.10 / Anchor, almacenar el bytecode en ProgramData bajo el cargador actualizable, y luego en cada instrucción el SVM mapea cuentas, llama al punto de entrada, mide CU y confirma o revierte.
Las pruebas unitarias del host (#[cfg(test)]) se ejecutan en la estación de trabajo; las pruebas de integración usan LiteSVM 0.6.x, Surfpool 0.12.0, o un validador local.
Punto de entrada y despacho
Estilo nativo:
use solana_program::{
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
pubkey::Pubkey,
};
entrypoint!(process_instruction);
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
// decodificar instruction_data, validar cuentas, mutar estado propio
Ok(())
}Dentro del manejador generalmente:
- Decodificar un enum de instrucción Borsh (o etiqueta cruda + payload)
- Recorrer cuentas con
next_account_infoo verificaciones de índice - Verificar propietarios, firmantes, PDA y longitudes de datos
- Aplicar actualizaciones de estado verificadas
- Devolver
Ok(())oErr(...)
Anchor envuelve esto en módulos #[program] y discriminadores de 8 bytes. El modelo de tiempo de ejecución no cambia: un punto de entrada, cuentas declaradas, datos de bytes.
no_std y qué puedes importar
En cadena obtienes core, crates no_std seleccionados, llamadas al sistema solana_program (log, CPI, sysvars), alloc opcional y serializadores como Borsh / bytemuck que compilan para sBPF.
No obtienes std completo, tiempos de ejecución asíncronos, clientes HTTP o acceso al sistema de archivos. Si cargo build-sbf falla en una dependencia, audita cargo tree para dependencias std transitivas y cambia a crates seguros para programas.
Borsh como formato de cableado
Los datos de instrucción y los cuerpos de cuenta son bytes. Borsh es determinista y little-endian: enteros fijos, bool de 1 byte, Pubkey de 32 bytes, Vec/String prefijados con u32, y discriminadores de enum.
Tamaño de las cuentas en la creación para que el serializador no pueda escribir más allá de account.data.len(). Los cambios de esquema requieren versionado o migraciones; reordenar variantes de enum cambia silenciosamente las etiquetas. Zero-copy (Pod + Zeroable + #[repr(C)]) reinterpreta cuentas grandes y fijas sin una copia completa cuando CU es importante.
Aritmética segura
Las cantidades de tokens, las tarifas en puntos básicos, las matemáticas de acciones y los índices de recompensas deben usar:
let next = balance
.checked_add(amount)
.ok_or(ProgramError::InvalidArgument)?;Prefiera checked_* cuando el desbordamiento sea un error, saturating_* cuando un límite sea intencional, y verificaciones de límites explícitas para reglas de negocio (suministro máximo, bps máximos). No confíe en los pánicos de desbordamiento solo de depuración; trate el desbordamiento como un fallo de diseño.
Errores que los clientes pueden decodificar
ProgramResult es Result<(), ProgramError>. Mapee fallos de dominio a códigos personalizados (implementaciones From nativas, #[error_code] de Anchor, o similar) para que los clientes TypeScript con @solana/kit 7.0.0 puedan ramificarse en números de error en lugar de solo raspar registros.
Use msg! con moderación para diagnósticos. Los registros cuestan CU y no son un sustituto de los errores estructurados en rutas de fallo esperadas (no autorizado, deslizamiento, caducado).
CU y tamaño del programa
CU se extrae de un presupuesto a nivel de transacción (en Solana moderno, incluido Agave 4.1.1, planifique alrededor de un valor predeterminado cercano a 1,400,000 CU a menos que las instrucciones de Compute Budget cambien el límite dentro de los máximos del protocolo).
Sumideros comunes de CU:
- Deserializar cuentas enormes en cada instrucción
- Bucles ilimitados sobre listas proporcionadas por el cliente
msg!verboso en bucles ajustados- Pilas de CPI profundas sin simulación
Sumideros de tamaño binario:
- Grafos de dependencias pesados
- Programas monolíticos "un crate hace todo"
- Código solo de depuración dejado en compilaciones de lanzamiento
La disciplina significa retornos tempranos, iteración acotada, dependencias ligeras y perfilado con simulación antes del tráfico de mainnet-beta.
Consideraciones Avanzadas y Aplicaciones
Elección de una superficie Rust
| Enfoque | Experiencia Rust | CU / tamaño | Cuándo encaja |
|---|---|---|---|
| Anchor 0.32.1 | Macros, cuentas, IDL | Línea de base más alta | Velocidad de producto, clientes tipados |
solana-program nativo | Punto de entrada y verificaciones explícitas | Techo más bajo si se tiene cuidado | Aprendizaje, cargadores de control personalizados |
| Pinocchio / pilas delgadas | Orientado a sistemas | A menudo el más ligero | Rutas críticas, programas de infraestructura |
| Híbrido | Capa de aplicación Anchor + núcleo delgado | Dividido por CPI | Sistemas maduros con puntos críticos medidos |
Los tres todavía obedecen las restricciones de no_std, los diseños Borsh o bytes, la aritmética verificada y la medición de CU. La elección del framework cambia las macros y la ergonomía de validación, no las reglas de Sealevel.
Mapeo de la intuición de Rust del host al trabajo en cadena
| Hábito de Rust del host | Sustituto en cadena |
|---|---|
main + argumentos CLI | Punto de entrada + bytes de instrucción + lista de cuentas |
| Archivos / filas de DB | Cuentas propiedad del programa con diseños fijos o versionados |
anyhow + rastreos de pila | Códigos ProgramError compactos + registros selectivos |
Crecimiento ilimitado de Vec | Capacidad fija, almacenamiento respaldado por cuenta, recuentos máximos |
| Hilos / async | Instrucción de un solo hilo; la paralelización es Sealevel entre transacciones |
println! | msg! (medido) |
| Dinero flotante | Lamports enteros / unidades base de token + operaciones verificadas |
Patrones y pruebas
Use encabezados de cuenta versionados, discriminadores (etiquetas manuales o de 8 bytes de Anchor), semillas de PDA como parte de la API, y ayudantes de CPI que aún requieren cuentas declaradas. Mantenga las pruebas solo del host detrás de cfg para que no inflen el binario sBPF.
Pruebe unitariamente matemáticas puras y empaquetado en el host. Ejecute instrucciones completas bajo LiteSVM 0.6.x o Surfpool 0.12.0. Mida el tamaño binario después de la compilación y computeUnitsConsumed de la simulación (los metadatos de Kit RPC son suficientes para verificaciones del lado del cliente).
Conceptos erróneos comunes
- "Si conozco Rust, los programas de Solana son solo otro crate." El target, el punto de entrada, el modelo de cuenta y la medición cambian qué crates y patrones son válidos.
- "Puedo usar la biblioteca estándar como un servicio backend." En cadena es
no_stdconallocopcional; la mayoría de las pilasstdy asíncronas no compilarán para sBPF. - "Anchor elimina la necesidad de entender Borsh y las cuentas." Anchor genera código sobre los mismos bytes, propietarios y discriminadores; los diseños incorrectos aún arruinan las migraciones.
- "Las compilaciones de lanzamiento hacen que el desbordamiento de enteros sea seguro automáticamente." La lógica financiera debe usar APIs verificadas o saturantes deliberadamente.
- "
unwrapestá bien si los clientes son honestos." Los clientes son adversarios; los pánicos desperdician CU y producen una experiencia de usuario del cliente peor que los errores tipados. - "Más registros siempre ayudan en producción." El exceso de
msg!puede hacer que las rutas superen el presupuesto de CU; prefiera errores y indexación fuera de cadena para eventos de alto volumen. - "Las dependencias más grandes solo afectan el tiempo de compilación." Pueden inflar el tamaño de despliegue y atraer más código a las rutas críticas; audite las dependencias como riesgo de producción.
- "Zero-copy es siempre mejor que Borsh." Zero-copy requiere reglas estrictas de
repr(C)/Pod; los campos de longitud variable a menudo favorecen Borsh. - "Las actualizaciones de programas reescriben los datos de la cuenta de forma gratuita." Las actualizaciones intercambian bytecode; los bytes de la cuenta permanecen hasta que los migre.
Preguntas Frecuentes
¿A qué compila Rust para los programas de Solana?
sBPF bytecode (ELF .so) ejecutado por el SVM en los validadores de Agave. Compilas con cargo build-sbf o la compilación de Anchor, no solo con un target de host normal.
¿Por qué no hay una función `main`?
El cargador BPF/SBF llama al punto de entrada registrado para cada instrucción. Los frameworks generan o envuelven ese punto de entrada por usted.
¿Cuál es la diferencia práctica entre no_std y "alloc limitado"?
no_std elimina la biblioteca estándar. alloc restaura opcionalmente tipos de heap (Vec, String) a través del asignador del programa, todavía sin E/S del sistema operativo o hilos. Prefiera diseños fijos cuando sea posible.
¿Sigue siendo Anchor "real" Rust en cadena?
Sí. Anchor 0.32.1 emite Rust que apunta al mismo tiempo de ejecución. Las macros reducen el código repetitivo para cuentas, errores e IDL; no cambian las reglas de Sealevel.
¿Por qué Borsh es tan común?
Es determinista, compacto y compartido entre programas nativos, predeterminados de Anchor y muchos codificadores de cliente. La estabilidad entre lenguajes importa más que usar cualquier formato serde que le guste en el host.
¿Cuándo debería cambiar de Borsh a bytemuck?
Cuando las cuentas son grandes, de tamaño fijo y lo suficientemente críticas como para que la deserialización/serialización completa domine la CU. Mantenga las reglas de alineación y relleno bajo revisión.
¿Necesito aritmética verificada si solo uso cantidades de tokens u64?
Sí. La suma, la multiplicación de tarifas y las matemáticas de acciones aún pueden desbordar u64. Las operaciones verificadas convierten eso en un fallo de instrucción limpio.
¿Cómo deberían los clientes leer los fallos del programa?
Mapee códigos de error personalizados en el programa y decodifíquelos fuera de cadena (tablas de IDL/errores con Anchor, o códigos documentados para nativos). Use metadatos de transacción de @solana/kit 7.0.0 para registros y campos de error.
¿Cuál es una buena primera pasada de optimización para CU?
Elimine registros de rutas críticas, limite bucles, evite clonar búferes de bytes grandes, deserialice solo los campos necesarios y simule con tamaños de cuenta realistas.
¿Cómo sé si un crate es seguro para depender de él?
Debe compilar bajo cargo build-sbf, preferiblemente como no_std. Verifique las dependencias transitivas para características solo de std y prefiera crates mantenidos para programas de Solana.
¿Puedo usar punto flotante en cadena?
Prefiera punto fijo entero para saldos, tarifas y acciones. Evite los flotantes para cualquier cosa financiera.
¿Cómo se relaciona esta sección con la documentación del tiempo de ejecución de Sealevel?
Las páginas de Sealevel cubren bloqueos, paralelismo y medición. Esta sección cubre el subconjunto de Rust que escribe dentro de ese tiempo de ejecución.
¿Qué versiones debería fijar mi equipo?
Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, LiteSVM 0.6.x.
¿Por dónde empiezo si soy nuevo en Rust en cadena?
Esta descripción general, luego Fundamentos de Rust para Solana, luego las páginas de no_std, Borsh, aritmética, errores y CU a medida que construye.
¿Cambia Pinocchio el modelo?
Adelgaza las dependencias y el estilo del punto de entrada. Todavía razonas sobre cuentas, bytes, errores y CU de la misma manera.
Relacionados
- Fundamentos de Rust para Solana - ejemplos cortos de punto de entrada, no_std, Borsh y operaciones verificadas
- no_std y el Entorno del Programa - qué permite y prohíbe el sandbox
- Serialización Borsh - codificación de instrucciones y cuentas en profundidad
- Aritmética Segura - patrones verificados y de saturación para saldos y tarifas
- Manejo de Errores en Cadena - ProgramError, códigos personalizados y
? - Tamaño del Programa y Disciplina de CU - tamaño binario y presupuestos de cómputo
Versiones de la pila: Esta página fue escrita para Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, y LiteSVM 0.6.x.