Um mit dem Solana-Netzwerk zu interagieren, müssen Sie eine Transaktion senden. Sie können sich eine Transaktion wie einen Umschlag vorstellen, der mehrere Formulare enthält. Jedes Formular ist eine Anweisung, die dem Netzwerk mitteilt, was zu tun ist. Das Senden der Transaktion ist wie das Versenden des Umschlags, damit die Formulare verarbeitet werden können.
Das folgende Beispiel zeigt eine vereinfachte Version von zwei Transaktionen. Wenn die erste Transaktion verarbeitet wird, führt sie eine einzelne Anweisung aus. Wenn die zweite Transaktion verarbeitet wird, führt sie drei Anweisungen in sequentieller Reihenfolge aus: zuerst Anweisung 1, gefolgt von Anweisung 2, gefolgt von Anweisung 3.
Transaktionen sind atomar: Wenn eine einzelne Anweisung fehlschlägt, wird die gesamte Transaktion fehlschlagen und es werden keine Änderungen vorgenommen.
Ein vereinfachtes Diagramm, das zwei Transaktionen zeigt
Eine
Transaction
besteht aus folgenden Informationen:
signatures: Ein Array von Signaturenmessage: Transaktionsinformationen, einschließlich der Liste der zu verarbeitenden Anweisungen
pub struct Transaction {#[wasm_bindgen(skip)]#[serde(with = "short_vec")]pub signatures: Vec<Signature>,#[wasm_bindgen(skip)]pub message: Message,}
Diagramm, das die zwei Teile einer Transaktion zeigt
Transaktionen haben eine Gesamtgrößenbeschränkung von
1232
Bytes. Diese Beschränkung umfasst sowohl das signatures-Array
als auch die message-Struktur.
Dieses Limit wurde entwickelt, um Paketfragmentierung in typischer Internet-Infrastruktur zu vermeiden. Obwohl IPv6 MTUs größer als 9000 Bytes unterstützt, verwenden die meisten Internet-Router eine Standard-MTU von 1500 Bytes (Standard-Ethernet). Um sicherzustellen, dass Transaktionen ohne Fragmentierung in ein einzelnes Paket passen, verwendet Solana 1280 Bytes (die für IPv6 erforderliche Mindest-MTU) minus 48 Bytes für Netzwerk-Header (40 Bytes IPv6 + 8 Bytes Fragment/UDP-Header), was zu dem Transaktionsgrößenlimit von 1232 Bytes führt.
Diagramm, das das Transaktionsformat und Größenbegrenzungen zeigt
Signaturen
Das signatures-Array der Transaktion enthält Signature-Strukturen. Jede
Signature
ist 64 Bytes groß und wird durch Signieren der Message der Transaktion mit dem
privaten Schlüssel des Kontos erstellt. Für jedes
Signer-Konto, das in einer der Anweisungen der Transaktion
enthalten ist, muss eine Signatur bereitgestellt werden.
Die erste Signatur gehört zu dem Konto, das die Basisgebühr der Transaktion bezahlt und ist die Transaktionssignatur. Die Transaktionssignatur kann verwendet werden, um die Details der Transaktion im Netzwerk nachzuschlagen.
Nachricht
Die message der Transaktion ist eine
Message-Struktur,
die folgende Informationen enthält:
header: Der Nachrichten-Headeraccount_keys: Ein Array von Kontoadressen, die für die Anweisungen der Transaktion erforderlich sindrecent_blockhash: Ein Blockhash, der als Zeitstempel für die Transaktion dientinstructions: Ein Array von Anweisungen
Um Platz zu sparen, speichert die Transaktion nicht die Berechtigungen für
jedes Konto einzeln. Stattdessen werden die Kontoberechtigungen mithilfe des
header und der account_keys bestimmt.
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
Der header der Nachricht ist eine
MessageHeader-Struktur.
Er enthält folgende Informationen:
num_required_signatures: Die Gesamtzahl der für die Transaktion erforderlichen Signaturennum_readonly_signed_accounts: Die Gesamtzahl der schreibgeschützten Konten, die Signaturen erfordernnum_readonly_unsigned_accounts: Die Gesamtzahl der schreibgeschützten Konten, die keine Signaturen erfordern
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,}
Diagramm, das die drei Teile des Nachrichtenheaders zeigt
Kontoadressen
Das
account_keys
der Nachricht ist ein Array von Kontoadressen, das im
Compact-Array-Format
gesendet wird. Das Präfix des Arrays gibt dessen Länge an. Jedes Element im
Array ist ein öffentlicher Schlüssel, der auf ein Konto verweist, das von seinen
Anweisungen verwendet wird. Das accounts_keys-Array muss vollständig und
streng geordnet sein, wie folgt:
- Signer + Beschreibbar
- Signer + Schreibgeschützt
- Kein Signer + Beschreibbar
- Kein Signer + Schreibgeschützt
Die strikte Reihenfolge ermöglicht es, das account_keys-Array mit den
Informationen im header der Nachricht zu kombinieren, um die
Berechtigungen für jedes Konto zu bestimmen.
Diagramm, das die Reihenfolge des Kontenadressenfeldes zeigt
Aktueller Blockhash
Der recent_blockhash der Nachricht ist ein Hash-Wert, der als
Transaktions-Zeitstempel fungiert und doppelte Transaktionen verhindert. Ein
Blockhash läuft nach
150 Blöcken
ab. (Entspricht einer Minute – unter der Annahme, dass jeder Block 400 ms
dauert.) Nach Ablauf des Blocks ist die Transaktion abgelaufen und kann nicht
mehr verarbeitet werden.
Die RPC-Methode getLatestBlockhash
ermöglicht es Ihnen, den aktuellen Blockhash und die letzte Blockhöhe
abzurufen, bei der der Blockhash gültig sein wird.
Anweisungen
Die
instructions
der Nachricht ist ein Array aller zu verarbeitenden Anweisungen, das im
Compact-Array-Format
gesendet wird. Das Präfix des Arrays gibt dessen Länge an. Jedes Element im
Array ist eine
CompiledInstruction
Struktur und enthält folgende Informationen:
program_id_index: Ein Index, der auf eine Adresse imaccount_keys-Array verweist. Dieser Wert gibt die Adresse des Programms an, das die Anweisung verarbeitet.accounts: Ein Array von Indizes, die auf Adressen imaccount_keys-Array verweisen. Jeder Index verweist auf die Adresse eines Kontos, das für diese Anweisung erforderlich ist.data: Ein Byte-Array, das angibt, welche Anweisung auf dem Programm aufgerufen werden soll. Es enthält auch alle zusätzlichen Daten, die von der Anweisung benötigt werden. (Zum Beispiel Funktionsargumente)
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>,}
Kompaktes Array von Anweisungen
Beispiel einer Transaktionsstruktur
Das folgende Beispiel zeigt die Struktur einer Transaktion, die eine einzelne SOL-Überweisungsanweisung enthält.
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));
Der folgende Code zeigt die Ausgabe der vorherigen Codeausschnitte. Das Format unterscheidet sich zwischen den SDKs, aber beachte, dass jede Anweisung die gleichen erforderlichen Informationen enthält.
{"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}}]}
Nachdem eine Transaktion übermittelt wurde, können Sie ihre Details mithilfe der Transaktionssignatur und der getTransaction RPC-Methode abrufen. Die Antwort wird eine Struktur ähnlich dem folgenden Ausschnitt haben.
Sie können die Transaktion auch über den Solana Explorer finden.
{"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?