Derivación de PDAs
Los PDAs se derivan de semillas y un ID de programa. find_program_address devuelve la dirección canónica fuera de la curva y la semilla de "bump" necesaria para invoke_signed.
Receta
let (pda, bump) = Pubkey::find_program_address(
&[b"metadata", mint.key.as_ref()],
program_id,
);Cuándo usar esto:
- Calcular direcciones en clientes antes de enviar transacciones.
- Verificar PDAs proporcionados por el cliente en la cadena.
- Diseñar esquemas de semillas multi-inquilino.
Ejemplo de Trabajo
pub fn verify_config(config: &AccountInfo, authority: &Pubkey, program_id: &Pubkey, stored_bump: u8) -> ProgramResult {
let (expected, bump) = Pubkey::find_program_address(&[b"config", authority.as_ref()], program_id);
if expected != *config.key || bump != stored_bump {
return Err(ProgramError::InvalidSeeds);
}
Ok(())
}Lo que esto demuestra:
- Incluye la clave pública de autoridad en las semillas para configuraciones por usuario.
- Compara la clave derivada y el "bump" con los valores almacenados.
- Devuelve
InvalidSeedsen caso de discrepancia.
Análisis Profundo
Algoritmo
- Hashea las semillas + ID de programa + "bump" hasta encontrar un punto fuera de la curva.
- El "bump" canónico es el más alto válido (de 255 hacia abajo).
Límites de Semillas
| Restricción | Valor |
|---|---|
| Conteo de semillas | <= 16 |
| Longitud de semilla | <= 32 bytes cada una |
Notas de Rust
let bump_seed = [bump];
let signer_seeds: &[&[u8]] = &[b"vault", owner.as_ref(), &bump_seed];Errores Comunes
- ID de programa incorrecto en el cliente - Deriva una dirección diferente. Solución: Pasar el ID del programa de la clave desplegada.
- "Bump" faltante en las semillas -
invoke_signedfalla. Solución: Añadir el slice&[bump]. - Semillas de cadena UTF-8 - Usar
.as_bytes()consistentemente. Solución: Documentar la codificación. - u64 en semillas - Usar bytes con orden de bytes fijo:
id.to_le_bytes(). Solución: Evitar empaquetado ambiguo. - Rederivar cada vez - Desperdicia CU en la cadena. Solución: Almacenar el "bump" canónico.
Alternativas
| Alternativa | Usar cuándo | No usar cuándo |
|---|---|---|
create_program_address | "Bump" conocido | "Bump" desconocido |
Macro seeds de Anchor | Ergonómico | Explícito nativo |
| Derivación solo fuera de la cadena | Ahorrar CU | Debe verificarse en la cadena |
Preguntas Frecuentes
¿`find` vs `create`?
find busca el "bump"; create requiere el "bump" exacto.
¿Mismas semillas, diferentes programas?
PDAs diferentes - por diseño.
¿Se permiten semillas vacías?
Desaconsejado - usar prefijo constante.
¿Orden de las semillas de Pubkey?
Documentar; cambiar el orden cambia la dirección.
¿Siempre "bump" 255?
No garantizado - almacenar el "bump" real.
¿Derivación TS en cliente?
@solana/kit getProgramDerivedAddress.
¿Vectores de prueba?
Almacenar "snapshot" de PDA+"bump" en pruebas.
¿Colisión de semillas?
Las diferentes semánticas necesitan prefijos diferentes.
¿Actualización de programa cambia PDA?
No si el ID del programa no cambia.
¿Múltiples PDAs por usuario?
Añadir bytes de semilla "disambiguador".
¿Máximo de semillas?
16 semillas por derivación.
¿Costo de búsqueda en cadena?
Evitar - usar el "bump" almacenado.
Relacionado
- Conceptos básicos de PDA - Introducción
- Bumps Canónicos - Almacenar el "bump"
- Firmar con PDAs -
invoke_signed
Versiones de Stack: Esta página se escribió 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.