Explicación de las Restricciones de Anclaje
Las restricciones de Anclaje son las reglas declarativas que convierten una lista de cuentas de transacción en un contexto tipado y de cierre por fallo para su instrucción. En Solana, cada cuenta es solo una pubkey que el cliente eligió pasar. Sin verificaciones, un atacante puede sustituir un mint diferente, un PDA de bóveda diferente o una cuenta escribible que controle. Las estructuras #[derive(Accounts)] de Anclaje, los tipos envoltorio y los atributos #[account(...)] existen para que esos errores fallen en el momento de la validación en lugar de en el movimiento de fondos en producción.
Esta página es el paraguas para Cuentas, Restricciones y Validación. Las páginas hermanas profundizan en los tipos de cuenta, el orden de validación, las semillas y el bump, init / init_if_needed, el espacio y el alquiler, las invariantes personalizadas y los ayudantes de tokens. Aquí obtendrá un modelo coherente para que esas páginas encajen como zooms, no como historias separadas.
Resumen
- Anclaje valida cada campo en su struct de cuentas antes de que se ejecute el manejador: verificaciones del tipo envoltorio (propietario, firmante, ID del programa, deserialización), luego restricciones de atributos (
mut,seeds,init,has_one,constraint, ayudantes de tokens). - Por Qué Importa: La seguridad de Solana es principalmente validación de cuentas. Las restricciones son la forma de codificar "esta debe ser nuestra PDA", "este pagador financia la creación", "esta ATA pertenece a ese mint y autoridad", y "esta relación de campo se mantiene" sin tener que implementar cada verificación manualmente.
- Conceptos Clave: Envoltorios de cuenta, atributos
#[account], orden de validación, semillas / bump,init/init_if_needed, espacio / exención de alquiler,constraintpersonalizado, restricciones de token y ATA, discriminadores. - Cuándo Usar: Cada instrucción de Anclaje. Prefiera el tipo más estricto más los atributos mínimos que demuestren propiedad, identidad y mutabilidad para ese flujo.
- Limitaciones / Compensaciones: Las restricciones no pueden ejecutar CPIs ni bucles complejos; evalúan expresiones sobre cuentas y argumentos de instrucciones. El uso excesivo de
init_if_neededoUncheckedAccountsin salvaguardias reintroduce errores clásicos de reinicialización y sustitución. Unspaceincorrecto desperdicia alquiler o falla al serializar. - Temas Relacionados: Tipos de Cuenta, Orden de Validación de Cuentas, Restricciones de Semillas y Bump,
inityinit_if_needed, Espacio y Alquiler, Restricciones Personalizadas.
Fundamentos
Los programas de Solana son sin estado. Todo el estado duradero vive en las cuentas. El tiempo de ejecución solo impone reglas generales: banderas de firmante, banderas de escritura y escrituras del propietario (solo el programa propietario puede mutar los datos de una cuenta). Todo lo demás (mint correcto, PDA correcto, relación de administrador correcta) es trabajo de su programa.
Los programas nativos de Rust verifican estas reglas con if explícitos y retornos tempranos. Anclaje 0.32.1 genera la misma clase de verificaciones a partir de macros:
Cuentas de transacción del cliente
|
v
#[derive(Accounts)] -- tipos envoltorio (Signer, Account<T>, Program, ...)
|
v
#[account(...)] -- mut, seeds, bump, init, has_one, constraint, ayudantes de token
|
v
Context<T> listo -- el manejador se ejecuta con cuentas tipadas y validadasDos capas se apilan:
- Capa de tipo. Elegir
Signer<'info>,Account<'info, Vault>,Program<'info, System>, oUncheckedAccount<'info>decide qué verificaciones automáticas se ejecutan (firma, propietario + discriminador + carga Borsh, ID del programa, o ninguna). - Capa de atributo.
#[account(mut, seeds = ..., bump, has_one = authority, constraint = ...)]compone reglas adicionales. Todos los atributos de un campo deben pasar.
Los clientes construidos con @solana/kit 7.0.0 (o el cliente TS de Anclaje) aún ensamblan la lista completa de cuentas. Las restricciones no obtienen claves faltantes; solo aceptan o rechazan lo que la transacción ya declaró. Su IDL y el código del cliente deben pasar las mismas pubkeys, semillas y programas que esperan las macros.
Mecánica e Interacciones
Tipos de cuenta como primer filtro
Elija el envoltorio más estricto que coincida con el rol:
| Tipo | Verificaciones automáticas |
|---|---|
Signer<'info> | is_signer |
Account<'info, T> | Propietario es este programa, discriminador de 8 bytes, deserializar T |
InterfaceAccount<'info, T> | Propietarios compatibles con Token-2022 / interfaz |
Program<'info, T> | Coincide con el ID del programa para T |
SystemAccount<'info> | Propiedad del Programa del Sistema |
UncheckedAccount<'info> | Ninguna (debe documentar y aplicar verificaciones) |
AccountLoader es para diseños de cero copia; Interface / TokenInterface son importantes cuando se admite tanto SPL Token como Token-2022. Usar UncheckedAccount para rutas de fondos o autoridad es una vía de escape deliberada, no un valor predeterminado. Detalles: Tipos de Cuenta.
Orden de validación
Anclaje no "adivina" basándose solo en el orden de los campos de la estructura. Aplica un pipeline determinista aproximadamente: verificaciones de tipo y propietario, banderas de firmante y mutabilidad, restricciones de dirección y programa, init / init_if_needed (crear cuando sea necesario), semillas y otros atributos, expresiones de constraint personalizadas, y luego carga completa de datos para cuentas tipadas.
Consecuencias prácticas:
- El pagador y el programa del sistema deben ser válidos antes de que
initpueda asignar. - Las cuentas referenciadas en
seedsdeben estar presentes y con las claves correctas para la derivación del PDA. - No puede hacer CPI desde una expresión de restricción; los efectos secundarios pertenecen al manejador después de la validación.
- Depurar un error significa leer el error de restricción de Anclaje (y los registros), no reorganizar el orden de los campos y esperar.
Consulte Orden de Validación de Cuentas al diseñar flujos de creación y uso o al buscar "¿por qué falló init antes de mi manejador?".
Semillas y bump: prueba de PDAs
Las Direcciones Derivadas del Programa (PDAs) son direcciones controladas por su programa a través de semillas. Las restricciones hacen que esa prueba sea declarativa:
#[account(
seeds = [b"vault", authority.key().as_ref()],
bump = vault.bump,
)]
pub vault: Account<'info, Vault>,seeds = [...]debe coincidir con la derivación del lado del cliente (orden de bytes, endianness para enteros, orden de claves de cuenta).bumpeninitencuentra el bump canónico (el más alto); almacénelo en los datos de la cuenta.bump = account.bumpen instrucciones posteriores reutiliza el valor almacenado para evitar la búsqueda de bump cada vez.
Semillas incorrectas significan una dirección diferente, no una discrepancia leve. Los PDAs entre programas necesitan una orientación explícita del programa; la derivación predeterminada es bajo el ID del programa actual. Patrones completos: Restricciones de Semillas y Bump.
init y init_if_needed
init crea una nueva cuenta en la misma instrucción: asignación/asignación de CPI del sistema (y financiación), establece el propietario en su programa, tamaño a partir de space. Falla si la cuenta ya existe. Compañeros típicos: payer = ..., space = 8 + T::INIT_SPACE, seeds + bump opcionales y system_program.
init_if_needed crea solo cuando la cuenta falta. Eso es conveniente para la incorporación idempotente y peligroso cuando se acepta una cuenta existente sin probar que es su estado inicializado correctamente. Prefiera init simple para bóvedas de alto valor; si necesita idempotencia, agregue restricciones estrictas en el discriminador, la autoridad y las semillas cuando la cuenta ya exista.
#[account(
init,
payer = user,
space = 8 + Profile::INIT_SPACE,
seeds = [b"profile", user.key().as_ref()],
bump,
)]
pub profile: Account<'info, Profile>,Requisitos: pagador firmante mutable con suficiente SOL, programa del sistema presente, espacio suficiente para serializar. Inmersión profunda: init y init_if_needed.
Espacio y alquiler
Las cuentas de Solana que contienen datos deben permanecer exentas de alquiler. El space de Anclaje es la longitud de bytes asignada. Para las cuentas normales de Anclaje, eso es 8 (discriminador) + carga útil. Use #[derive(InitSpace)] y T::INIT_SPACE, con #[max_len(n)] en campos String / Vec para que el tamaño se conozca en tiempo de compilación.
Subestimar el tamaño y la serialización fallan o corrompen el estado. Sobreestimar el tamaño y los usuarios pagan demasiado alquiler. Crecer más tarde necesita realloc (y migración cuidadosa). Cerrar cuentas debe devolver lamports a un destinatario a través de close = ... para que el alquiler no quede varado. Detalles: Espacio y Alquiler.
Restricciones y relaciones personalizadas
Los elementos integrados cubren mucho (mut, has_one, address, owner, semillas, init). Las reglas de dominio usan constraint = <expresión booleana>, opcionalmente mapeadas con @ MiError::Variante para errores claros del cliente:
#[account(
mut,
has_one = admin,
constraint = config.fee_bps <= 10_000 @ ConfigError::FeeTooHigh,
)]
pub config: Account<'info, Config>,- Múltiples restricciones se combinan con AND.
#[instruction(amount: u64)]trae los argumentos de la instrucción a las expresiones.- Prefiera las verificaciones declarativas para relaciones estáticas; use
require!en el manejador para lógica dependiente de oráculos, bucles o cuentas restantes.
Los módulos y ayudantes de restricciones reutilizables mantienen los programas grandes consistentes. Vea Restricciones Personalizadas y la cobertura hermana de los atributos #[account] comunes.
Restricciones de Token y ATA (concepto)
Los flujos de tokens fallan en producción cuando el mint, el propietario o el ID del programa de token no coinciden. anchor-spl agrega ayudantes para que no tenga que verificar manualmente cada campo:
token::mint/token::authorityen cuentas de tokenassociated_token::mint/associated_token::authoritypara ATAsinit/init_if_neededen ATAs con token asociado + programas del sistema + token en la lista de cuentas
Para Token-2022, prefiera los tipos de interfaz (InterfaceAccount, TokenInterface) para que las verificaciones de propietario acepten tanto Token heredado como Token-2022. Siempre marque las cuentas de token de origen y destino como mut cuando los saldos cambien. Conceptualmente: fije la identidad del mint, la autoridad y el ID del programa de la misma manera que fija las PDAs de bóveda con semillas.
Consideraciones Avanzadas y Aplicaciones
Las restricciones son una herramienta de arquitectura de seguridad, no azúcar sintáctico. En las revisiones de diseño, mapee cada cuenta sensible a (1) un tipo, (2) prueba de identidad (semillas, dirección, derivación de ATA), (3) prueba de relación (has_one, coincidencia de mint) y (4) ciclo de vida (init, cierre, realloc).
| Patrón | Forma de restricción | Tenga en cuenta |
|---|---|---|
| PDA de perfil de usuario | init + seeds + bump + espacio | Orden de semillas del cliente; almacenar bump |
| Singleton de configuración | semillas [b"config"], has_one = admin | Autoridad de actualización frente a rol de administrador |
| Retiro de bóveda | semillas, constraint en saldos, coincidencia de mint de token | CEI: validar luego mutar luego CPI |
| Garantía de ATA idempotente | init_if_needed + associated_token::* | La reinicialización solo es segura con verificaciones existentes sólidas |
| Ruta Token-2022 | InterfaceAccount + TokenInterface | ID de programa incorrecto falla la transferencia |
remaining_accounts se encuentran fuera de la estructura tipada: valide cada clave y propietario en el manejador antes de usar. Cero copia y realloc cambian el tamaño y los tipos de cargador pero no la necesidad de restricciones de semillas y autoridad.
En Agave 4.1.1 con Solana CLI 3.0.10, las pruebas locales (anchor test, Surfpool, LiteSVM) deben afirmar tanto las rutas de éxito como los fallos deliberados de restricciones (mint incorrecto, PDA incorrecto, falta de mut). Los fallos en la validación son más baratos y seguros que la ejecución parcial del manejador.
Anclaje 0.32.1 continúa generando metadatos de cuenta IDL a partir de estas estructuras: los clientes ven qué cuentas son escribibles y cuáles son requeridas. Mantenga las restricciones y los constructores de clientes sincronizados después de cambios en las semillas o el espacio.
Conceptos erróneos comunes
- "Si compila, las cuentas son seguras." Los tipos ayudan; los ataques de sustitución necesitan semillas, mint y restricciones de autoridad también.
- "El orden de los campos en la estructura es el orden de validación." Los tipos de atributos y las clases de tipos impulsan el pipeline; no confíe en reordenar campos para solucionar errores de
init. - "
init_if_neededes siempre una UX más segura." A menudo es una seguridad más débil. Prefierainita menos que las restricciones de estado existente sean herméticas. - "El espacio es solo el sizeof de Rust de la estructura." Agregue el discriminador de 8 bytes; limite los campos dinámicos; el alquiler depende del tamaño asignado.
- "El bump solo sin semillas es suficiente." Las semillas definen la dirección; el bump selecciona entre puntos válidos fuera de la curva para esas semillas.
- "UncheckedAccount está bien si lo verifico más tarde en el manejador." Está bien solo cuando realmente verifica cada ruta; omitir una ruta es una clase común de explotación.
- "Las cuentas de token solo necesitan ser mutables." La mutabilidad sin restricciones de mint/autoridad/ATA aún permite drenajes de cuentas incorrectas o CPIs fallidos.
- "Las restricciones personalizadas reemplazan la necesidad de cuentas tipadas." Use ambos: tipos para propietario y diseño, restricciones para invariantes de dominio.
Preguntas Frecuentes
¿Qué problema resuelven las restricciones de Anclaje?
Convierten las listas de cuentas proporcionadas por el cliente en contextos tipados y validados para que los propietarios incorrectos, las PDAs incorrectas, los firmantes faltantes y las relaciones rotas fallen antes de que se ejecute su lógica de negocio.
¿Cuál es la diferencia entre los tipos de cuenta y los atributos #[account]?
Los tipos aplican verificaciones de línea base (firmante, propietario, discriminador, ID del programa). Los atributos agregan reglas de ciclo de vida y relación (init, semillas, mut, has_one, expresiones personalizadas, ayudantes de tokens).
¿Cuándo se ejecuta la validación en relación con mi manejador?
Toda la validación de cuentas para el Context se completa primero. El cuerpo de su función de instrucción se ejecuta solo después de que la estructura de cuentas se haya construido completamente y las restricciones se hayan superado.
¿Por qué las semillas y el bump pertenecen a las restricciones?
Demuestran que la cuenta pasada es la PDA que su programa espera para las semillas dadas. Declararlas una vez mantiene los manejadores libres de derivaciones repetitivas y falla de forma cerrada ante discrepancias.
¿Debo usar init o init_if_needed?
Use init cuando la cuenta deba crearse exactamente una vez. Use init_if_needed solo para flujos idempotentes donde las cuentas existentes aún se validan completamente (tipo, semillas, autoridad, restricciones de dominio).
¿Cómo dimensiono el espacio correctamente?
Use space = 8 + T::INIT_SPACE con InitSpace (y max_len en campos dinámicos). Nunca omita el discriminador. Financie lamports exentos de alquiler a través de un pagador mutable.
¿Qué hace una expresión de restricción personalizada?
Evalúa un booleano sobre cuentas y argumentos de instrucción. Adjunte @ Error::Variant para que los clientes y los registros obtengan un código de fallo específico en lugar de un error de restricción genérico.
¿Cómo encajan las restricciones de token y ATA en este modelo?
Son verificaciones de identidad especializadas: la cuenta de token debe coincidir con el mint y la autoridad (y generalmente la derivación de la dirección asociada). Se sientan junto a las semillas y mut de la misma manera que la identidad de la bóveda.
¿Pueden las restricciones realizar CPIs?
No. La creación a través de init es administrada por el framework. Las CPIs arbitrarias y la lógica de varios pasos pertenecen al manejador después de que la validación tenga éxito.
¿Todavía necesito require! en los manejadores?
Sí, para reglas dinámicas o de varios pasos (oráculos, cuentas restantes, bucles). Prefiera las restricciones declarativas para relaciones estáticas que se conocen en el momento de la validación.
¿Qué versiones de stack apunta esta sección?
Los ejemplos se alinean con Agave 4.1.1, Solana CLI 3.0.10, Anclaje 0.32.1, Rust 1.91.1 y clientes como @solana/kit 7.0.0.
¿Adónde debo ir después de esta página?
Lea Tipos de Cuenta y Orden de Validación de Cuentas, luego Restricciones de Semillas y Bump, init y init_if_needed, Espacio y Alquiler, y Restricciones Personalizadas a medida que implementa instrucciones reales.
Relacionado
- Tipos de Cuenta - Signer, Account, Program, SystemAccount, UncheckedAccount
- Orden de Validación de Cuentas - cuándo se ejecutan los tipos, init y las restricciones
- Restricciones de Semillas y Bump - identidad PDA con semillas y bump almacenado
inityinit_if_needed- ciclo de vida de creación y riesgo de reinicialización- Espacio y Alquiler - dimensionamiento, discriminador, financiación exenta de alquiler
- Restricciones Personalizadas - expresiones de restricción, has_one, errores tipados
Versiones de Stack: Esta página fue escrita para Agave 4.1.1, Solana CLI 3.0.10, Anclaje 0.32.1, Rust 1.91.1 y @solana/kit 7.0.0.