Patrones de Inicialización de Cuentas
La inicialización es el momento de mayor riesgo: los ataques de reinicialización, la sustitución de cuentas y la omisión del discriminador comprometen permanentemente la custodia. Separe la creación de la inicialización, escriba los discriminadores una vez y rechace las cuentas ya inicializadas.
Receta
if account.lamports() > 0 && is_initialized(&account)? { return Err(...); }Cuándo usar esto:
- Primera instrucción de incorporación de usuario.
- Elección entre
initeinit_if_neededde Anchor. - Auditoría de creación de bóvedas.
Ejemplo de Trabajo
pub fn initialize(state: &AccountInfo, payer: &AccountInfo, system: &AccountInfo, program_id: &Pubkey, bump: u8) -> ProgramResult {
if state.lamports() > 0 {
let data = state.try_borrow_data()?;
if data.len() >= 8 && data[..8] == STATE_DISC { return Err(ProgramError::AccountAlreadyInitialized); }
}
// create_account CPI si está vacío, luego escribe disc + State::default()
Ok(())
}Lo que esto demuestra:
- Detectar el discriminador existente.
- Devolver el equivalente a
AccountAlreadyInitialized. - Crear solo cuando está vacío.
Análisis Profundo
Patrones
| Patrón | Riesgo |
|---|---|
| init | Bajo si se verifica el discriminador |
| init_if_needed | Reinit si la guarda es débil |
| creación por cliente + inicialización por programa | Confianza dividida |
Anchor 0.32.1
Restricciones de init, init_if_needed, realloc.
Notas de Rust
// Siempre establezca el discriminador antes de aceptar depósitos.Trampas
- init_if_needed sin verificación del discriminador - Drenaje por reinit. Solución: Guarda de inicialización fuerte.
- Inicialización después de transferencia de tokens - Cualquiera puede adelantarse a la forma. Solución: Inicializar antes del valor.
- Inicialización de tipo de cuenta incorrecto - Confusión de tipos. Solución: Discriminador único por tipo.
- Pagador no firmante - La creación falla o el pagador es incorrecto. Solución: Validar firmante.
- Omitir verificación de alquiler - Cuenta desasignada. Solución: Financiar el saldo mínimo.
Alternativas
| Alternativa | Usar cuándo | No usar cuándo |
|---|---|---|
| Transacciones de creación/inicialización en dos pasos | Costo de UX más seguro | Conveniencia de un solo paso |
| Creación solo por cliente | Programa más simple | Flujos de PDA más difíciles |
Macro init de Anchor | Más rápido | Debe entender la expansión |
Preguntas Frecuentes
AccountAlreadyInitialized?
Datos personalizados o inválidos.
Pagador de init de Anchor?
Restricción de firmante.
Inicialización de PDA?
invoke_signed create.
Discriminador primero?
Antes de aceptar fondos.
Realloc en init?
Si el tamaño se desconoce de antemano - raro.
Cerrar y luego reinicializar?
Permitido si está completamente a cero.
Inicialización de cuenta de token?
Flujo SPL separado.
Pruebas?
Intentar doble inicialización en LiteSVM.
Adelantarse a la inicialización?
Usar creación idempotente o solo pagador firmado por el usuario.
Programa inmutable?
Lógica de inicialización congelada - probar exhaustivamente.
Surfpool?
Simular creación+inicialización.
Auditoría #1?
Clase de reinicialización.
Relacionado
- Ataques de Reinicialización - Exploits
- Creación de Cuentas a través de CPI - Creación CPI
- Discriminadores de Cuentas - Etiquetas
Versiones de Stack: 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.