Puntos Clave Avanzados de Anchor
Los valores predeterminados de Anchor (cuentas Borsh, listas fijas #[derive(Accounts)], InitSpace) hacen que la mayoría de los programas se envíen. Los sistemas de producción encuentran los límites: libros grandes, rutas de salto variables, crecimiento de esquemas, llamadores externos y límites duros de CU y tamaño de despliegue.
Esta página es el mapa conceptual para Anchor Avanzado - copia cero, remaining_accounts, realloc, tipos de cuenta personalizados, feature flags y cfg, CU y tamaño binario, e interop con programas nativos o Pinocchio en Agave 4.1.1.
Resumen
- Anchor Avanzado mantiene el mismo contrato de tiempo de ejecución (cuentas declaradas, propiedad, rent, CU) pero se da de baja de las conveniencias que queman cómputo, congelan el layout, o esconden metadatos variables, reemplazándolas con cargadores, colas, redimensionamientos y puentes de CPI.
- Por Qué Importa: Un layout de copia cero incorrecto, cuentas restantes no validadas, realloc descuidado, o deriva del IDL controlada por features, convierten la velocidad del framework en corrupción, problemas de rent, o roturas del cliente bajo carga de mainnet.
- Conceptos Clave:
#[account(zero_copy)]/AccountLoader,ctx.remaining_accounts,realloc+realloc::payer/realloc::zero, traits de cuenta manuales, features de Cargo /#[cfg], unidades de cómputo y tamaño.so, CPI nativo / Pinocchio (invoke,declare_program!). - Cuándo Usar Este Modelo: Estado fijo grande, routers y rutas multi-oráculo, esquemas en crecimiento, DeFi de framework mixto, presión de CU o tamaño de despliegue, o instrucciones de desarrollo que nunca deben enviarse.
- Limitaciones / Compensaciones: Más poder significa más validación manual; la copia cero y realloc luchan contra el cambio de esquemas; las cuentas restantes dejan el IDL; cfg puede bifurcar el IDL y el layout; la interop confía en las reglas del llamador, no en las restricciones de Anchor del otro lado.
- Temas Relacionados: Cuentas de Copia Cero, realloc en Anchor, remaining_accounts, Tipos de Cuenta Personalizados, Optimización de CU y Tamaño, Feature Flags y cfg, Interacción con Programas Nativos/Pinocchio.
Fundamentos
Anchor se asienta sobre el modelo de cuentas de Solana: los programas son ejecutables sin estado; el estado duradero vive en cuentas de datos propiedad del programa; cada cuenta que una instrucción necesita debe aparecer en la transacción con las banderas correctas de firmante y escritura.
La ruta usual es: nombrar cuentas en #[derive(Accounts)] con restricciones; decodificar cuerpos con Borsh (Account<'info, T>); fijar el espacio en init con space = / InitSpace; exponer un IDL estable para clientes (@solana/kit 7.0.0, Codama, Anchor TS).
Anchor Avanzado comienza cuando esa ruta es incorrecta para la carga de trabajo:
| Presión | Predeterminado que falla | Respuesta Avanzada |
|---|---|---|
| Tablas fijas grandes (libros, ticks, bitmaps) | Deserialización/serialización Borsh completa en cada ix | Copia cero + AccountLoader |
| Número variable de cuentas (rutas, hooks, oráculos) | Solo campos nombrados fijos | ctx.remaining_accounts |
| Crecimiento de esquema o colección después de init | SPACE fijo para siempre | realloc (o migrar a una cuenta nueva) |
| Layouts extranjeros o exóticos | Account<T> + #[account] solamente | Tipos personalizados o UncheckedAccount + verificaciones manuales |
| Diagnósticos de desarrollo / ix solo para clúster | Mismo binario en todas partes | Features + cfg (o crates separados) |
| Límite de CU o tamaño de despliegue | Logs verbosos, re-encontrar PDAs, dependencias pesadas | Medir, luego cortar el costo de la ruta caliente y el peso del binario |
| Componer con programas no-Anchor | Ayudantes de CPI solo para Anchor | Interop crudo invoke / declare_program! |
Nada de esto cambia los bloqueos de Sealevel o la propiedad: todavía no puedes inventar cuentas en tiempo de ejecución, escribir datos extranjeros sin CPI en el propietario, o gastar CU ilimitadas. Solo cambias cómo Anchor mapea esas reglas en tipos y macros.
Mecánicas e Interacciones
Cuentas de Copia Cero
Para configuraciones pequeñas, Account<'info, T> está bien: Anchor verifica el propietario y el discriminador, decodifica Borsh y escribe al soltar cuando es mutable. Cuando T es grande y mayormente fijo (libros de órdenes, arrays de ticks, slabs), el parseo/serialización completo quema CU con el tamaño. Copia Cero mantiene los bytes en su lugar y muta a través de una vista tipada.
En Anchor 0.32.1 la forma usual es:
#[account(zero_copy)]
#[repr(C)]
pub struct Market {
pub head: u64,
pub bids: [u64; 1024],
}
#[derive(Accounts)]
pub struct Touch<'info> {
#[account(mut)]
pub market: AccountLoader<'info, Market>,
}
pub fn touch(ctx: Context<Touch>) -> Result<()> {
let mut market = ctx.accounts.market.load_mut()?;
market.head = market.head.saturating_add(1);
Ok(())
}Prefiere #[repr(C)], campos de tamaño fijo, y layout amigable con bytemuck/Pod (sin String/Vec ingenuos). Los discriminadores todavía se aplican. Usa AccountLoader, no Account, y no mezcles Borsh y copia cero en la misma cuenta activa. Planifica la capacidad por adelantado; marca mut para load_mut. Perfila primero: en cuentas diminutas, la copia cero a menudo cuesta más de lo que ahorra.
remaining_accounts
La estructura Accounts tipada es un prefijo de la lista de cuentas de la instrucción. Cualquier cosa después de esos campos nombrados es ctx.remaining_accounts: AccountInfo crudas sin verificaciones automáticas de propietario, firmante o discriminador.
Usos típicos: routers multi-salto, conjuntos de oráculos opcionales o hooks, y reenvío de metadatos abiertos a CPI.
pub fn route(ctx: Context<Route>, hops: u8) -> Result<()> {
require!(
ctx.remaining_accounts.len() == hops as usize * 2,
RouterError::BadMetas
);
require!(ctx.remaining_accounts.len() <= MAX_REMAINING, RouterError::TooMany);
for (i, acc) in ctx.remaining_accounts.iter().enumerate() {
if i % 2 == 0 {
require_keys_eq!(*acc.owner, token_program::ID, RouterError::BadOwner);
}
}
Ok(())
}Las cuentas restantes no se nombran en el IDL; documenta el protocolo para clientes e indexadores, o envía instrucciones tipadas por ruta común. Los clientes añaden metadatos en el orden acordado con los flags is_signer / is_writable correctos. Limita la longitud. Valida cada entrada antes de leer, escribir o hacer CPI.
realloc en Anchor
La inicialización congela data_len y el mínimo exento de rent para ese tamaño. Cuando los esquemas o colecciones crecen, realloc cambia el buffer bajo propiedad del programa dentro de una instrucción:
#[account(
mut,
realloc = 8 + Config::INIT_SPACE + extra,
realloc::payer = payer,
realloc::zero = false,
)]
pub config: Account<'info, Config>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,realloc = N:data_lenobjetivo (incluye el discriminador de 8 bytes cuando se usan patronesINIT_SPACE).realloc::payer: financia el delta de rent en el crecimiento; debe ser mutable.realloc::zero: si los nuevos bytes se ponen a cero. Cero cuando los nuevos bytes se convierten en nuevos campos; los bytes obsoletos son una clase de error si los dejas sucios.- El programa de sistema debe estar presente para las transferencias de rent.
Prefiere el crecimiento hacia adelante y migra instrucciones para rediseños grandes. No hagas realloc en cada ruta caliente: crece en bloques o preasigna cuando se conoce el límite. La copia cero más realloc frecuente es una mala combinación.
Tipos de cuenta personalizados
La mayoría de los programas deben permanecer en Account<'info, T>, InterfaceAccount / wrappers de token, AccountLoader, y UncheckedAccount / SystemAccount / Signer restringidos.
Tipos de cuenta personalizados implementan traits de Anchor a mano (AccountDeserialize, verificaciones de propiedad) cuando layouts extranjeros o validaciones especializadas no se mapean a derives. Aumentan la superficie de auditoría: lifetimes, mal uso de try_deserialize_unchecked, falta de verificaciones de propietario. Prefiere envolver los incorporados o un IDL extranjero, con fixtures de LiteSVM si debes ir por lo personalizado.
Feature flags y cfg
Las features de Cargo y #[cfg(feature = "...")] compilan código o lo excluyen:
[features]
default = []
debug-log = []
devnet-only = []#[cfg(feature = "debug-log")]
msg!("debug path");Buenos usos: diagnósticos opcionales, asserts de prueba más pesados, constantes de clúster desde scripts de compilación. Usos peligrosos: dividir #[program] para que el IDL de mainnet difiera de los clientes; ocultar layouts de campos de cuenta entre builds del mismo programa; dejar instrucciones devnet-only en el release. Prefiere valores predeterminados vacíos, CI que prohíba combinaciones peligrosas de features de mainnet, puertas de administración en tiempo de ejecución, o un crate de desarrollo separado cuando las instrucciones nunca deben existir en mainnet.
CU y tamaño binario
Las unidades de cómputo miden el trabajo dentro de un presupuesto de transacción finito (planifica alrededor de un valor predeterminado del orden de ~1,400,000 CU a menos que las instrucciones de Presupuesto de Cómputo eleven el límite dentro de los límites del protocolo). La deserialización, las restricciones, los logs, la derivación de PDAs y la CPI extraen del mismo pool.
Hábitos de alto apalancamiento: almacena bumps canónicos; usa copia cero solo donde Borsh domina en estado grande; recorta msg! y prefiere eventos; menos cuentas y CPI menos profundas; ajusta los límites de CU del cliente desde simulación con margen; los perfiles de release y la división de superficies de administración raras pueden reducir el .so. Nunca elimines la validación para ahorrar CU. Mide primero; cuando el impuesto del framework todavía domina, considera una ruta caliente Pinocchio o nativa a través de CPI.
Interop con nativo y Pinocchio
Anchor puede llamar programas no-Anchor en la misma transacción:
declare_program!cuando el llamador publica un IDL compatible (ayudantes de CPI tipados en tiempo de compilación).invoke/invoke_signedcrudos con datos deInstructionconstruidos a mano y listas deAccountMetacuando no hay IDL o el layout está empaquetado a mano.
invoke(
&Instruction {
program_id: native_program.key(),
accounts: metas,
data: ix_data, // discriminador + args por especificación del llamador
},
account_infos,
)?;Las restricciones de Anchor se aplican solo a la estructura Accounts tuya. Valida los IDs de programa, el orden de las cuentas, las banderas de escritura/firmante y el estado post-CPI. Los clientes con @solana/kit 7.0.0 deben codificar los mismos discriminadores y metadatos que espera el llamador.
Consideraciones y Aplicaciones Avanzadas
Trata estas herramientas como una pila de escapes, no como una lista de verificación para habilitar todo a la vez.
| Herramienta | Gana | Costos | Encaja |
|---|---|---|---|
| Copia cero | Baja CU en estado fijo grande | Layout rígido, migraciones más difíciles | Libros, slabs, arrays de ticks |
| remaining_accounts | Composición variable | Sin nombres IDL; validación manual completa | Routers, hooks, multi-oráculo |
| realloc | Paga rent cuando sea necesario | Payer, zeroing, CU, límites máximos | Crecimiento aditivo, listas acotadas |
| Nueva cuenta + migrar | Layout limpio V2 | Período de UX y doble lectura | Rediseños disruptivos |
| Tipos de cuenta personalizados | Ajuste de layout extranjero | Carga de auditoría y traits | Último recurso de interop |
| Features cfg | Binarios de release más pequeños o seguros | Riesgo de bifurcación IDL/layout | Logs de depuración, ix de desarrollo que nunca se envían |
| Afinación de CU/tamaño | Aterrizaje bajo presupuesto, ajuste de despliegue | Complejidad si no se mide | Rutas calientes perfiladas |
| CPI Nativo/Pinocchio | Rendimiento especializado o protocolo compartido | Riesgo de contrato a nivel de byte | Pilas DeFi mixtas |
Migración: prefiere campos Borsh de solo añadir y un byte de versión cuando sea posible; realloc para crecimiento acotado; migración dedicada cuando el layout se rompe; doble lectura durante el despliegue.
Clientes e indexadores: cada escape que deja el IDL (cuentas restantes, discriminadores manuales, instrucciones controladas por cfg) necesita un protocolo publicado: constantes, documentos de ruta, codecs de Codama/kit, y eventos de migración.
Revisión de seguridad: autenticación de cuentas restantes, griefing de rent y tamaño máximo de realloc, alineación de copia cero y confusión de tipos, deserialización personalizada no verificada, superficie de ataque de feature flags, suplantación de programa de CPI.
En una pila alineada con Agave 4.1.1 (CLI 3.0.10, Anchor 0.32.1, Rust 1.91.1, LiteSVM 0.6.x, Surfpool 0.12.0), ejercita el crecimiento, las cuentas restantes adversarias y los perfiles de CU antes de que lo haga el tráfico de mainnet.
Conceptos Erróneos Comunes
- "La copia cero es siempre más rápida, así que cada cuenta debería usarla." Las cuentas pequeñas rara vez justifican la rigidez de Pod. Perfila primero el costo de Borsh.
- "remaining_accounts ya están validadas por Anchor." Solo la estructura tipada recibe macros de restricción. La cola está cruda hasta que la verificas.
- "realloc pone a cero e inicializa nuevos campos por mí." Tú eliges la política de zeroing y debes inicializar campos semánticos.
- "Puedo encoger y crecer casualmente como un heap." El tamaño de la cuenta es una operación de rent y propietario; diseña crecimiento hacia adelante y migra para cambios grandes.
- "Los tipos de cuenta personalizados son una opción de estilo normal." Son el último recurso. Prefiere los incorporados más restricciones.
- "Las feature flags me permiten mantener una base de código con diferentes IDLs de mainnet." Los clientes y las compilaciones verificables necesitan un único producto de release.
- "Eliminar restricciones es una optimización de CU válida." Las regresiones de seguridad no son CU gratis. Optimiza el layout, logs, bumps y CPI en su lugar.
- "Si la CPI de Anchor tiene éxito, el programa nativo hizo lo que yo quería." Éxito significa que el llamador devolvió
Ok; aún así verifica montos y autoridades. - "La completitud del IDL es opcional si el programa es correcto." Las cuentas restantes y los bytes de CPI manuales son invisibles para los clientes ingenuos.
- "El tamaño del binario no importa una vez que el despliegue tiene éxito." Los programas más grandes aumentan el costo de despliegue y la fricción operativa.
Preguntas Frecuentes
¿Qué cuenta como "Anchor Avanzado" frente a Anchor cotidiano?
Anchor cotidiano: Borsh Account<T>, estructuras de cuenta fijas, inicialización con espacio conocido, ayudantes de CPI estándar. Anchor Avanzado: cargadores de copia cero, colas de cuentas restantes, crecimiento realloc, tipos de cuenta hechos a mano, divisiones de features en tiempo de compilación, trabajo agresivo de CU/tamaño, e interop nativo/Pinocchio.
¿Cuándo debo usar cuentas de copia cero?
Cuando los datos de la cuenta son grandes y mayormente fijos, y el perfilado muestra que la deserialización/serialización Borsh domina la CU. Usa #[account(zero_copy)] con #[repr(C)] y AccountLoader. Ver Cuentas de Copia Cero.
¿La copia cero todavía usa un discriminador?
Sí. Las etiquetas de tipo todavía protegen contra la lectura de un layout como otro. Las rutas de inicialización y carga deben escribir y verificar el prefijo esperado.
¿Qué es ctx.remaining_accounts?
Cuentas listadas en la instrucción después de los campos de tu estructura #[derive(Accounts)]. No se nombran en el IDL y no reciben verificaciones automáticas de restricciones. Ver remaining_accounts.
¿Cuándo necesito realloc en Anchor?
Cuando una cuenta existente propiedad del programa debe crecer para nuevos campos o más entradas (o encoger cuidadosamente). Usa realloc, realloc::payer, realloc::zero, y el programa de sistema. Ver realloc en Anchor.
¿Es realloc un sustituto de la versionado de cuentas?
No. Realloc cambia la longitud de los bytes; el versionado decide cómo parseas layouts viejos y nuevos. Combina un discriminador estable, un campo de versión, doble lectura, y migra o realloc según sea necesario.
¿Debería implementar tipos de cuenta personalizados?
Solo cuando los incorporados no pueden expresar el layout o la validación. Prefiere Account, InterfaceAccount, AccountLoader, y restricciones. Ver Tipos de Cuenta Personalizados.
¿Cómo interactúan las feature flags con el IDL?
Cualquier cosa que cambie las instrucciones o las formas de las cuentas entre compilaciones produce IDLs diferentes. Mantén un único perfil de release de mainnet. Ver Feature Flags & cfg.
¿Cómo reduzco la CU sin debilitar la seguridad?
Almacena bumps, recorta logs verbosos, prefiere copia cero solo en estado caliente grande, minimiza cuentas y profundidad de CPI, y establece límites de CU del cliente desde simulación. Nunca elimines verificaciones de propiedad o firmante por CU. Ver Optimización de CU y Tamaño.
¿Cómo mido la CU y el tamaño binario?
Usa el consumo de unidades de cómputo en pruebas de LiteSVM/Surfpool y simulación RPC en devnet; inspecciona el tamaño del .so de release después de anchor build con features de producción.
¿Puede un programa Anchor hacer CPI a un programa Pinocchio o nativo?
Sí. Prefiere declare_program! cuando existe un IDL; de lo contrario, construye datos de instrucción y metadatos e invoke / invoke_signed. Valida el ID de programa y cada cuenta. Ver Interacción con Programas Nativos/Pinocchio.
¿Qué necesita declare_program!?
Un JSON de IDL del llamador disponible en tiempo de compilación para que Anchor pueda generar ayudantes de CPI tipados. Sin un IDL, usa construcción de instrucción cruda.
¿Cómo deben tratar los clientes @solana/kit estos patrones avanzados?
Genera o escribe a mano codecs que coincidan con discriminadores, cuentas fijas, protocolos de cuentas restantes y cualquier byte de CPI no-Anchor. Apunta a @solana/kit 7.0.0 alineado con esta pila.
¿Cuándo debería dejar Anchor por Pinocchio en una ruta caliente?
Cuando la CU medida o el tamaño binario aún no cumplen los objetivos del producto después de las optimizaciones del lado de Anchor, y puedes aislar una superficie pequeña con un límite de CPI claro.
Relacionados
- Cuentas de Copia Cero -
AccountLoader, layout Pod, estado grande - realloc en Anchor - aumentar espacio, payer, política de zeroing
- remaining_accounts - colas de cuentas variables y validación
- Tipos de Cuenta Personalizados - traits de cuenta manuales y layouts extranjeros
- Optimización de CU y Tamaño - unidades de cómputo y tamaño de despliegue
- Feature Flags & cfg - compilación condicional y estabilidad del IDL
- Interacción con Programas Nativos/Pinocchio - CPI de framework mixto
- Mejores Prácticas de Anchor Avanzado - lista de verificación para los límites de producció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.