Fundamentos de Rust para Solana
10 ejemplos para empezar con Rust en cadena (On-Chain): 7 básicos y 3 intermedios.
Prerrequisitos
- Toolchain de Rust 1.91.1 con
cargoy Solana CLI 3.0.10 (cargo build-sbf). - Un crate de programa que use
solana-program(nativo) o Anchor 0.32.1. - Un validador local a través de Surfpool 0.12.0 o
solana-test-validatorpara pruebas de integración.
Ejemplos Básicos
1. Punto de entrada del programa
Cada programa en cadena expone un único entrypoint! que recibe cuentas y datos de instrucción.
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 {
Ok(())
}- El runtime llama a
process_instructionpara cada instrucción en una transacción. - Devuelve
Ok(())en caso de éxito; devuelveErr(ProgramError::...)para fallar la transacción. - No hay función
main: el cargador BPF/SBF invoca directamente tu punto de entrada.
Relacionado: El Punto de Entrada y el Despacho - enrutamiento de instrucciones
2. Entorno no_std
Los crates en cadena compilan sin la biblioteca estándar.
#![no_std]
extern crate alloc;
use alloc::vec::Vec;#![no_std]deshabilitastd; usaallocparaVec,StringyBoxcuando sea necesario.- Sin sistema de archivos, hilos o red: solo llamadas al sistema de Solana a través de
solana_program. - Mantén las dependencias mínimas para reducir el tamaño del binario y las unidades de cómputo (CUs).
Relacionado: no_std y el Entorno del Programa - APIs disponibles
3. Serializar datos de cuenta con Borsh
La mayoría de los diseños de cuentas usan Borsh para la codificación determinista de bytes.
use borsh::{BorshDeserialize, BorshSerialize};
#[derive(BorshSerialize, BorshDeserialize)]
pub struct Counter {
pub count: u64,
}
let mut data = account.try_borrow_mut_data()?;
counter.serialize(&mut &mut data[..])?;- Borsh es el formato de facto para datos de instrucción y estructuras pequeñas de cuenta.
- Serializa en el slice de bytes de la cuenta; preasigna espacio al crear la cuenta.
- Siempre valida la longitud de la cuenta antes de deserializar.
Relacionado: Serialización Borsh - guía completa
4. Leer un Pubkey a partir de bytes
Los Pubkeys son identificadores de 32 bytes para cuentas y programas.
use solana_program::pubkey::Pubkey;
let key = Pubkey::new_from_array(account.key.to_bytes());
if *account.key != expected_owner {
return Err(ProgramError::IncorrectProgramId);
}PubkeyimplementaCopyy compara en tiempo constante; úsalo para verificaciones de propietario.- Nunca confíes en los Pubkeys proporcionados por el cliente sin verificar contra semillas o estado almacenado.
- Los IDs de programa son Pubkeys derivados del par de claves del programa en el momento del despliegue.
5. Aritmética verificada
Los desbordamientos (overflow) provocan el pánico y abortan la transacción; usa matemáticas verificadas.
let new_balance = old_balance
.checked_add(amount)
.ok_or(ProgramError::InvalidArgument)?;- Las compilaciones de lanzamiento en SBF no atrapan el desbordamiento de la misma manera que el Rust del host; aún así usa
checked_*. saturating_*es apropiado cuando limitar los valores es aceptable.- La lógica financiera nunca debe usar
+,-o*sin verificar.
Relacionado: Aritmética Segura - patrones de desbordamiento
6. Devolver un error de programa
Falla rápidamente con errores tipados en lugar de entrar en pánico.
use solana_program::program_error::ProgramError;
if amount == 0 {
return Err(ProgramError::InvalidArgument);
}- Los códigos de
ProgramErrorson compactos y baratos en comparación con las cadenas personalizadas. - Los enums de error personalizados (a través de
thiserroro manualmente) se mapean a códigosu32para los clientes. - Nunca uses
unwrap()oexpect()en rutas de programas de producción.
Relacionado: Manejo de Errores en Cadena - errores personalizados
7. Tomar prestados datos de cuenta
Los datos de cuenta se acceden a través de préstamos al estilo RefCell.
let data = account.try_borrow_data()?;
if data.len() < Counter::LEN {
return Err(ProgramError::InvalidAccountData);
}try_borrow_datafalla si la cuenta ya está prestada de forma mutable.- Libera los préstamos antes de las CPI (Cross-Program Invocations) que toquen la misma cuenta.
- Valida que
data.len()coincida con el tamaño de tu struct antes de parsear.
Ejemplos Intermedios
8. Cabecera de cuenta sin copia (zero-copy)
Las cuentas grandes pueden usar bytemuck para reinterpretar bytes sin copiar.
use bytemuck::{Pod, Zeroable};
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
pub struct Header {
pub discriminator: u64,
pub count: u64,
}
let header = bytemuck::from_bytes::<Header>(&data[..8]);Pod+Zeroablerequieren#[repr(C)]y sin sorpresas de relleno.- Zero-copy evita la asignación en el heap y ahorra CUs en rutas críticas.
- Combínalo con una cola Borsh para campos de longitud variable cuando sea necesario.
Relacionado: Zero-Copy con bytemuck - reglas de Pod
9. Registrar sin agotar el presupuesto de CU
Usa msg! con moderación; los registros consumen unidades de cómputo.
solana_program::msg!("deposit: {} lamports", amount);- Los registros aparecen en los metadatos de la transacción y en la salida de simulación.
- Las cadenas de formato y muchos registros pueden dominar el uso de CU en bucles ajustados.
- Prefiere errores personalizados estructurados para fallos dirigidos al cliente.
Relacionado: Registro con msg! - registro consciente de CU
10. Patrón de bucle consciente de CU
Limita las iteraciones y evita el crecimiento del heap en rutas críticas.
const MAX_ITEMS: usize = 32;
for (i, item) in items.iter().take(MAX_ITEMS).enumerate() {
process_item(i, item)?;
}- Los bucles sin límites son un hallazgo común en auditorías y un vector de agotamiento de CU.
- Pre-dimensiona los vectores al inicializar la cuenta en lugar de hacer
pushen cada instrucción. - Perfila con
solana program show --programsy simulación de transacciones.
Relacionado: Disciplina de Tamaño de Programa y CU - optimización
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.