Aby wchodzić w interakcje z siecią Solana, musisz wysłać transakcję. Możesz myśleć o transakcji jak o kopercie, która zawiera kilka formularzy. Każdy formularz to instrukcja, która mówi sieci, co ma zrobić. Wysłanie transakcji jest jak wysłanie koperty, aby formularze mogły zostać przetworzone.
Poniższy przykład pokazuje uproszczoną wersję dwóch transakcji. Gdy pierwsza transakcja zostanie przetworzona, wykona pojedynczą instrukcję. Gdy druga transakcja zostanie przetworzona, wykona trzy instrukcje w kolejności sekwencyjnej: najpierw instrukcję 1, następnie instrukcję 2, a na końcu instrukcję 3.
Transakcje są atomowe: jeśli pojedyncza instrukcja się nie powiedzie, cała transakcja zakończy się niepowodzeniem i żadne zmiany nie zostaną wprowadzone.
Uproszczony diagram przedstawiający dwie transakcje
Transaction
składa się z następujących informacji:
signatures: Tablica podpisówmessage: Informacje o transakcji, w tym lista instrukcji do przetworzenia
pub struct Transaction {#[wasm_bindgen(skip)]#[serde(with = "short_vec")]pub signatures: Vec<Signature>,#[wasm_bindgen(skip)]pub message: Message,}
Diagram przedstawiający dwie części transakcji
Transakcje mają łączny limit rozmiaru wynoszący
1232
bajtów. Limit ten obejmuje zarówno tablicę signatures, jak i
strukturę message.
Ten limit został zaprojektowany, aby uniknąć fragmentacji pakietów w typowej infrastrukturze internetowej. Chociaż IPv6 obsługuje MTU większe niż 9000 bajtów, większość routerów internetowych używa domyślnego MTU wynoszącego 1500 bajtów (standard Ethernet). Aby zapewnić, że transakcje mieszczą się w jednym pakiecie bez fragmentacji, Solana używa 1280 bajtów (minimalne MTU wymagane dla IPv6) minus 48 bajtów na nagłówki sieciowe (40 bajtów IPv6 + 8 bajtów nagłówka fragmentu/UDP), co daje limit rozmiaru transakcji wynoszący 1232 bajty.
Diagram przedstawiający format transakcji i limity rozmiaru
Podpisy
Tablica signatures transakcji zawiera struktury Signature. Każda
Signature
ma 64 bajty i jest tworzona przez podpisanie Message transakcji kluczem
prywatnym konta. Podpis musi być dostarczony dla każdego
konta podpisującego uwzględnionego w dowolnej instrukcji
transakcji.
Pierwszy podpis należy do konta, które opłaci opłatę bazową transakcji i jest podpisem transakcji. Podpis transakcji może być użyty do wyszukiwania szczegółów transakcji w sieci.
Wiadomość
message transakcji to
Message
struktura zawierająca następujące informacje:
header: Nagłówek wiadomościaccount_keys: Tablica adresów kont wymaganych przez instrukcje transakcjirecent_blockhash: Blockhash, który pełni rolę znacznika czasu dla transakcjiinstructions: Tablica instrukcji
Aby zaoszczędzić miejsce, transakcja nie przechowuje uprawnień dla każdego
konta osobno. Zamiast tego uprawnienia kont są określane na podstawie header
oraz 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>,}
Nagłówek
header wiadomości to
MessageHeader
struktura. Zawiera następujące informacje:
num_required_signatures: Całkowita liczba podpisów wymaganych przez transakcjęnum_readonly_signed_accounts: Całkowita liczba kont tylko do odczytu, które wymagają podpisównum_readonly_unsigned_accounts: Całkowita liczba kont tylko do odczytu, które nie wymagają podpisów
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,}
Diagram przedstawiający trzy części nagłówka wiadomości
Adresy kont
account_keys
wiadomości to tablica adresów kont, przesyłana w
kompaktowym formacie tablicy.
Prefiks tablicy wskazuje jej długość. Każdy element tablicy to klucz publiczny,
wskazujący na konto używane przez instrukcje. Tablica accounts_keys musi być
kompletna i ściśle uporządkowana w następujący sposób:
- Podpisujący + Zapis
- Podpisujący + Tylko do odczytu
- Nie podpisujący + Zapis
- Nie podpisujący + Tylko do odczytu
Ścisłe uporządkowanie pozwala połączyć tablicę account_keys z informacjami z
header wiadomości, aby określić uprawnienia dla każdego konta.
Diagram przedstawiający kolejność tablicy adresów kont
Ostatni blockhash
[recent_blockhash] wiadomości to wartość skrótu (hash), która pełni rolę
znacznika czasu transakcji i zapobiega duplikacji transakcji. Blockhash wygasa
po
150 blokach.
(Odpowiada to jednej minucie — zakładając, że każdy blok trwa 400 ms). Po
wygaśnięciu bloku transakcja wygasa i nie może zostać przetworzona.
Metoda RPC getLatestBlockhash pozwala
pobrać aktualny blockhash oraz ostatnią wysokość bloku, przy której blockhash
będzie ważny.
Instrukcje
instructions
wiadomości to tablica wszystkich instrukcji do przetworzenia, przesyłana w
kompaktowym formacie tablicy.
Prefiks tablicy wskazuje jej długość. Każdy element tablicy to
CompiledInstruction
i zawiera następujące informacje:
program_id_index: Indeks wskazujący adres w tablicyaccount_keys. Ta wartość wskazuje adres programu, który przetwarza instrukcję.accounts: Tablica indeksów wskazujących adresy w tablicyaccount_keys. Każdy indeks wskazuje adres konta wymaganego do tej instrukcji.data: Tablica bajtów określająca, którą instrukcję wywołać w programie. Zawiera także wszelkie dodatkowe dane wymagane przez instrukcję (np. argumenty funkcji).
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>,}
Kompaktowa tablica instrukcji
Przykładowa struktura transakcji
Poniższy przykład pokazuje strukturę transakcji zawierającej pojedynczą instrukcję transferu 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));
Kod poniżej pokazuje wynik z poprzednich fragmentów kodu. Format różni się między SDK, ale zauważ, że każda instrukcja zawiera te same wymagane informacje.
{"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}}]}
Po przesłaniu transakcji możesz pobrać jej szczegóły, używając sygnatury transakcji oraz metody RPC getTransaction. Odpowiedź będzie miała strukturę podobną do poniższego fragmentu.
Możesz również znaleźć transakcję, korzystając z 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?