Invocación entre programas (CPI)
Una invocación entre programas (cross program invocation o CPI) se refiere a cuando un programa invoca las instrucciones de otro programa. Este mecanismo permite la composición de programas en Solana.
Puede pensar en instrucciones como endpoints API que un programa expone a la red y una CPI como si una API internamente invoca otra API.
Cross Program Invocation
Cuando un programa inicia una invocación a otro programa (CPI):
- Dada una transacción al programa A que invoca al programa B, las firmas aplicadas a la transacción se extienden a la invocación del programa B
- El programa invocado (B) puede hacer CPI a otros programas, hasta una profundidad máxima de 4 (ej.: B->C, C->D)
- Los programas pueden "firmar" en nombre de las PDAs derivadas de su identificador
El tiempo de ejecución del programa Solana define una constante llamada
max_invoke_stack_height
,
que se establece en el
valor de 5.
Esto representa la altura máxima de la pila de la invocación de instrucciones
del programa. La altura de la pila comienza en 1 para las instrucciones de una
transacción, incrementa en 1 cada vez que un programa invoca otra instrucción.
Este ajuste efectivamente limita la profundidad de invocación de una CPI a 4.
Puntos clave #
-
Las CPIs habilitan a programas de Solana para invocar directamente las instrucciones de otro programa.
-
Los derechos de los que firman en el programa invocador se extienden al programa invocado.
-
Al crear una CPI, los programas pueden "firmar" en nombre de PDAs derivadas de su propio identificador.
-
El programa invocado puede hacer CPI adicionales para otros programas, hasta una profundidad máxima de 4.
Cómo escribir una CPI #
Escribir una instrucción para un CPI sigue el mismo patrón que construir una instrucción para añadir a una transacción. Bajo el capó, cada instrucción CPI debe especificar la siguiente información:
- Program address: Especifica el programa que se está invocando
- Accounts: Lista todas las cuentas a las que leen o escriban las instrucciones, incluyendo otros programas
- Instruction Data: Especifica qué instrucción se debe invocar en el programa, además de cualquier información adicional requerida por la instrucción (argumentos de la función)
Dependiendo del programa al que esté haciendo la invocación, puede haber
crates
disponibles con funciones que te ayuden a construir la instrucción. A
continuación, los programas ejecutan las CPI utilizando una de las siguientes
funciones del crate
solana_program
:
invoke
- usado cuando no hay firmas PDAinvoke_signed
- usado cuando el programa invocador necesita firmar con una PDA derivada de su identificador
CPI básica #
La función
invoke
se utiliza cuando se crea una CPI que no requiere firmas de PDA. Al crear CPIs,
las cuentas que proporcionan firmas al programa invocador extienden sus
privilegios al programa invocado.
pub fn invoke(
instruction: &Instruction,
account_infos: &[AccountInfo<'_>]
) -> Result<(), ProgramError>
Aquí hay un programa de ejemplo en
Solana Playground
que hace una CPI usando la función invoke
para llamar a la instrucción de
transferencia en el programa del sistema. También puedes hacer referencia a la
Guía básica de CPI para más
detalles.
CPI con firmas de PDAs #
La función
invoke_signed
se utiliza cuando se crea una CPI que requiere firmas de PDA. Las seeds usadas
para derivar las PDAs que firman se pasan a la función invoke_signed
como
signer_seeds
.
Puedes hacer referencia a la página Dirección Derivada de un Programa para ver detalles sobre cómo se derivan las PDA.
pub fn invoke_signed(
instruction: &Instruction,
account_infos: &[AccountInfo<'_>],
signers_seeds: &[&[&[u8]]]
) -> Result<(), ProgramError>
El tiempo de ejecución utiliza los derechos otorgados al programa invocador para determinar qué derechos pueden extenderse al programa invocado. En este contexto, los privilegios se refieren a los firmantes y a las cuentas con permisos de escritura. Por ejemplo, si la instrucción que el invocador está procesando contiene un firmante o una cuenta con permisos de escritura, entonces el invocador puede invocar una instrucción que también contenga ese firmante y/o cuenta con permisos de escritura.
Aunque las PDA no tienen llaves privadas,
pueden actuar como firmantes en una instrucción a través de una CPI. Para
verificar que una PDA es derivada del programa invocador, las seeds utilizadas
para generar la PDA deben ser incluidas como signers_seeds
.
Cuando la CPI es procesada, el tiempo de ejecución de Solana
internamente llama a create_program_address
usando el signers_seeds
y el program_id
del programa invocador. Si se
encuentra una PDA válida, la dirección se
añade como firmante válido.
Aquí hay un programa de ejemplo en
Solana Playground
que hace una CPI usando la función invoke_signed
para llamar a la instrucción
de transferencia en el programa del sistema con un firmante PDA. Puede consultar
la guía
CPI con firmante PDA
para más detalles.