Transacciones
Para interactuar con la red de Solana, debes enviar una transacción. Puedes pensar en una transacción como un sobre que contiene varios formularios. Cada formulario es una instrucción que le dice a la red qué hacer. Enviar la transacción es como enviar el sobre por correo para que los formularios puedan ser procesados.
El ejemplo a continuación muestra una versión simplificada de dos transacciones. Cuando la primera transacción es procesada, ejecutará una sola instrucción. Cuando la segunda transacción es procesada, ejecutará tres instrucciones en orden secuencial: primero la instrucción 1, seguida por la instrucción 2, seguida por la instrucción 3.
Las transacciones son atómicas: si una sola instrucción falla, toda la transacción fallará y no ocurrirá ningún cambio.
Un diagrama simplificado que muestra dos transacciones
Una
Transaction
consiste en la siguiente información:
signatures: Un array de firmasmessage: Información de la transacción, incluyendo la lista de instrucciones a procesar
pub struct Transaction {#[wasm_bindgen(skip)]#[serde(with = "short_vec")]pub signatures: Vec<Signature>,#[wasm_bindgen(skip)]pub message: Message,}
Diagrama que muestra las dos partes de una transacción
Las transacciones tienen un límite de tamaño total de
1232
bytes. Este límite incluye tanto el array de signatures como la
estructura message.
Este límite proviene del tamaño de la Unidad Máxima de Transmisión (MTU) de IPv6 de 1280 bytes, menos 48 bytes para encabezados de red (40 bytes IPv6 + 8 bytes de encabezado).
Diagrama que muestra el formato de transacción y los límites de tamaño
Firmas
El array signatures de la transacción contiene estructuras Signature. Cada
Signature
tiene 64 bytes y se crea firmando el Message de la transacción con la clave
privada de la cuenta. Se debe proporcionar una firma para cada
cuenta firmante incluida en cualquiera de las
instrucciones de la transacción.
La primera firma pertenece a la cuenta que pagará la tarifa base de la transacción y es la firma de la transacción. La firma de la transacción puede utilizarse para buscar los detalles de la transacción en la red.
Mensaje
El message de la transacción es una estructura
Message
que contiene la siguiente información:
header: El encabezado del mensajeaccount_keys: Un array de direcciones de cuentas requeridas por las instrucciones de la transacciónrecent_blockhash: Un blockhash que actúa como marca de tiempo para la transaccióninstructions: Un array de instrucciones
Para ahorrar espacio, la transacción no almacena permisos para cada cuenta
individualmente. En su lugar, los permisos de las cuentas se determinan
utilizando el header y el account_keys.
pub struct Message {/// The message header, identifying signed and read-only `account_keys`.pub header: MessageHeader,/// All the account keys used by this transaction.#[serde(with = "short_vec")]pub account_keys: Vec<Pubkey>,/// The id of a recent ledger entry.pub recent_blockhash: Hash,/// Programs that will be executed in sequence and committed in/// one atomic transaction if all succeed.#[serde(with = "short_vec")]pub instructions: Vec<CompiledInstruction>,}
Encabezado
El header del mensaje es una estructura
MessageHeader.
Contiene la siguiente información:
num_required_signatures: El número total de firmas requeridas por la transacciónnum_readonly_signed_accounts: El número total de cuentas de solo lectura que requieren firmasnum_readonly_unsigned_accounts: El número total de cuentas de solo lectura que no requieren firmas
pub struct MessageHeader {/// The number of signatures required for this message to be considered/// valid. The signers of those signatures must match the first/// `num_required_signatures` of [`Message::account_keys`].pub num_required_signatures: u8,/// The last `num_readonly_signed_accounts` of the signed keys are read-only/// accounts.pub num_readonly_signed_accounts: u8,/// The last `num_readonly_unsigned_accounts` of the unsigned keys are/// read-only accounts.pub num_readonly_unsigned_accounts: u8,}
Diagrama que muestra las tres partes del encabezado del mensaje
Direcciones de cuentas
El
account_keys
del mensaje es un array de direcciones de cuentas, enviado en
formato de array compacto.
El prefijo del array indica su longitud. Cada elemento en el array es una clave
pública, que apunta a una cuenta utilizada por sus instrucciones. El array
accounts_keys debe estar completo y estrictamente ordenado de la siguiente
manera:
- Firmante + Escribible
- Firmante + Solo lectura
- No firmante + Escribible
- No firmante + Solo lectura
El orden estricto permite que el array account_keys se combine con la
información en el header del mensaje para determinar los permisos
de cada cuenta.
Diagrama que muestra el orden del array de direcciones de cuentas
Blockhash reciente
El recent_blockhash del mensaje es un valor hash que actúa como una marca de
tiempo de la transacción y evita transacciones duplicadas. Un blockhash expira
después de
150 bloques.
(Equivalente a un minuto, asumiendo que cada bloque dura 400ms). Después de que
el bloque expira, la transacción caduca y no puede ser procesada.
El método RPC getLatestBlockhash te
permite obtener el blockhash actual y la altura del último bloque en el que el
blockhash será válido.
Instrucciones
El
instructions
del mensaje es un array de todas las instrucciones a procesar, enviado en
formato de array compacto.
El prefijo del array indica su longitud. Cada elemento en el array es una
estructura
CompiledInstruction
e incluye la siguiente información:
program_id_index: Un índice que apunta a una dirección en el arrayaccount_keys. Este valor indica la dirección del programa que procesa la instrucción.accounts: Un array de índices que apuntan a direcciones en el arrayaccount_keys. Cada índice apunta a la dirección de una cuenta requerida para esta instrucción.data: Un array de bytes que especifica qué instrucción invocar en el programa. También incluye cualquier dato adicional requerido por la instrucción. (Por ejemplo, argumentos de función)
pub struct CompiledInstruction {/// Index into the transaction keys array indicating the program account that executes this instruction.pub program_id_index: u8,/// Ordered indices into the transaction keys array indicating which accounts to pass to the program.#[serde(with = "short_vec")]pub accounts: Vec<u8>,/// The program input data.#[serde(with = "short_vec")]pub data: Vec<u8>,}
Array compacto de instrucciones
Estructura de transacción de ejemplo
El siguiente ejemplo muestra la estructura de una transacción que contiene una única instrucción de transferencia de SOL.
import {createSolanaRpc,generateKeyPairSigner,lamports,createTransactionMessage,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,appendTransactionMessageInstructions,pipe,signTransactionMessageWithSigners,getCompiledTransactionMessageDecoder} from "@solana/kit";import { getTransferSolInstruction } from "@solana-program/system";const rpc = createSolanaRpc("http://localhost:8899");const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();// Generate sender and recipient keypairsconst sender = await generateKeyPairSigner();const recipient = await generateKeyPairSigner();// Define the amount to transferconst LAMPORTS_PER_SOL = 1_000_000_000n;const transferAmount = lamports(LAMPORTS_PER_SOL / 100n); // 0.01 SOL// Create a transfer instruction for transferring SOL from sender to recipientconst transferInstruction = getTransferSolInstruction({source: sender,destination: recipient.address,amount: transferAmount});// Create transaction messageconst transactionMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(sender, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) => appendTransactionMessageInstructions([transferInstruction], tx));const signedTransaction =await signTransactionMessageWithSigners(transactionMessage);// Decode the messageBytesconst compiledTransactionMessage =getCompiledTransactionMessageDecoder().decode(signedTransaction.messageBytes);console.log(JSON.stringify(compiledTransactionMessage, null, 2));
El código a continuación muestra la salida de los fragmentos de código anteriores. El formato difiere entre SDKs, pero observa que cada instrucción contiene la misma información requerida.
{"version": 0,"header": {"numSignerAccounts": 1,"numReadonlySignerAccounts": 0,"numReadonlyNonSignerAccounts": 1},"staticAccounts": ["HoCy8p5xxDDYTYWEbQZasEjVNM5rxvidx8AfyqA4ywBa","5T388jBjovy7d8mQ3emHxMDTbUF8b7nWvAnSiP3EAdFL","11111111111111111111111111111111"],"lifetimeToken": "EGCWPUEXhqHJWYBfDirq3mHZb4qDpATmYqBZMBy9TBC1","instructions": [{"programAddressIndex": 2,"accountIndices": [0, 1],"data": {"0": 2,"1": 0,"2": 0,"3": 0,"4": 128,"5": 150,"6": 152,"7": 0,"8": 0,"9": 0,"10": 0,"11": 0}}]}
Después de enviar una transacción, puedes recuperar sus detalles utilizando la firma de la transacción y el método RPC getTransaction. La respuesta tendrá una estructura similar al siguiente fragmento.
También puedes encontrar la transacción usando el Explorador de Solana.
{"blockTime": 1745196488,"meta": {"computeUnitsConsumed": 150,"err": null,"fee": 5000,"innerInstructions": [],"loadedAddresses": {"readonly": [],"writable": []},"logMessages": ["Program 11111111111111111111111111111111 invoke [1]","Program 11111111111111111111111111111111 success"],"postBalances": [989995000, 10000000, 1],"postTokenBalances": [],"preBalances": [1000000000, 0, 1],"preTokenBalances": [],"rewards": [],"status": {"Ok": null}},"slot": 13049,"transaction": {"message": {"header": {"numReadonlySignedAccounts": 0,"numReadonlyUnsignedAccounts": 1,"numRequiredSignatures": 1},"accountKeys": ["8PLdpLxkuv9Nt8w3XcGXvNa663LXDjSrSNon4EK7QSjQ","7GLg7bqgLBv1HVWXKgWAm6YoPf1LoWnyWGABbgk487Ma","11111111111111111111111111111111"],"recentBlockhash": "7ZCxc2SDhzV2bYgEQqdxTpweYJkpwshVSDtXuY7uPtjf","instructions": [{"accounts": [0, 1],"data": "3Bxs4NN8M2Yn4TLb","programIdIndex": 2,"stackHeight": null}],"indexToProgramIds": {}},"signatures": ["3jUKrQp1UGq5ih6FTDUUt2kkqUfoG2o4kY5T1DoVHK2tXXDLdxJSXzuJGY4JPoRivgbi45U2bc7LZfMa6C4R3szX"]},"version": "legacy"}
Is this page helpful?