交易
要与 Solana 网络交互,您必须发送一笔交易。您可以将交易视为一个装有多种表单的信封。每个表单都是一条指令,告诉网络该做什么。发送交易就像邮寄信封,以便处理这些表单。
下面的示例展示了两个交易的简化版本。当第一个交易被处理时,它将执行一条指令。当第二个交易被处理时,它将按顺序执行三条指令:首先是指令 1,然后是指令 2,最后是指令 3。
交易是原子性的:如果单条指令失败,整个交易将失败,并且不会发生任何更改。
一个展示两个交易的简化图示
一个
Transaction
包含以下信息:
signatures:一个签名数组message:交易信息,包括要处理的指令列表
pub struct Transaction {#[wasm_bindgen(skip)]#[serde(with = "short_vec")]pub signatures: Vec<Signature>,#[wasm_bindgen(skip)]pub message: Message,}
展示交易两部分的图示
交易的总大小限制为
1232
字节。此限制包括 signatures 数组和 message
结构体。
此限制来源于 IPv6 最大传输单元 (MTU) 的大小为 1280 字节,减去 48 字节的网络头(40 字节 IPv6 + 8 字节头)。
展示交易格式和大小限制的图示
签名
交易的 signatures 数组包含 Signature 结构体。每个
Signature
为 64 字节,通过使用账户的私钥对交易的 Message 进行签名创建。每个
签名账户 的指令都必须提供一个签名。
第一个签名属于支付交易基础费用的账户,并且是交易签名。交易签名可用于在网络上查找交易的详细信息。
消息
交易的 message 是一个
Message
结构体,包含以下信息:
为了节省空间,交易不会单独存储每个账户的权限。相反,账户权限是通过 header 和
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>,}
头部
消息的 header 是一个
MessageHeader
结构体,包含以下信息:
num_required_signatures:交易所需的总签名数num_readonly_signed_accounts:需要签名的只读账户总数num_readonly_unsigned_accounts:不需要签名的只读账户总数
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,}
显示消息头部三个部分的图示
账户地址
消息的
account_keys
是一个账户地址数组,以
紧凑数组格式发送。数组的前缀表示其长度。数组中的每一项是一个公钥,指向其指令使用的账户。accounts_keys
数组必须完整,并严格按以下顺序排列:
- 签名者 + 可写
- 签名者 + 只读
- 非签名者 + 可写
- 非签名者 + 只读
严格的排序允许 account_keys 数组与消息的 header
中的信息结合,以确定每个账户的权限。
显示账户地址数组顺序的图示
最近的区块哈希
消息的 recent_blockhash
是一个哈希值,作为交易的时间戳并防止重复交易。区块哈希在
150 个区块后过期。(相当于一分钟——假设每个区块为 400 毫秒。)区块过期后,交易也会过期,无法被处理。
getLatestBlockhash RPC 方法
允许您获取当前的区块哈希以及区块哈希有效的最后区块高度。
指令
消息的
instructions
是一个包含所有待处理指令的数组,采用
紧凑数组格式。数组的前缀表示其长度。数组中的每一项是一个
CompiledInstruction
结构体,包含以下信息:
program_id_index:一个索引,指向account_keys数组中的地址。此值表示处理该指令的程序的地址。accounts:一个索引数组,指向account_keys数组中的地址。每个索引指向该指令所需账户的地址。data:一个字节数组,指定要在程序上调用的指令。它还包括指令所需的任何附加数据。(例如,函数参数)
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>,}
指令的紧凑数组
示例交易结构
以下示例展示了包含单个 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));
以下代码展示了前面代码片段的输出。 格式因 SDK 而异, 但请注意,每个指令都包含相同的必要信息。
{"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}}]}
提交交易后,您可以使用交易签名和 getTransaction RPC 方法检索其详细信息。响应的结构将类似于以下代码片段。
您还可以使用 Solana Explorer 查找交易。
{"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?