Transakcje
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
A
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ą całkowity limit rozmiaru wynoszący
1232
bajtów. Limit ten obejmuje zarówno tablicę signatures, jak i
strukturę message.
Ten limit wynika z maksymalnej jednostki transmisji (MTU) IPv6 wynoszącej 1280 bajtów, pomniejszonej o 48 bajtów na nagłówki sieciowe (40 bajtów IPv6 + 8 bajtów nagłówka).
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 za pomocą
prywatnego klucza konta. Podpis musi być dostarczony dla każdego
konta podpisującego uwzględnionego w dowolnej z 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ści headeraccount_keys: Tablica adresów kont wymaganych przez instrukcje transakcjirecent_blockhash: blockhash, który działa jako znacznik czasu dla transakcjiinstructions: Tablica instrukcji
Aby zaoszczędzić miejsce, transakcja nie przechowuje uprawnień dla każdego
konta indywidualnie. Zamiast tego uprawnienia kont są określane za pomocą
header i 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 zawierająca 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 w tablicy to klucz
publiczny, wskazujący na konto używane przez jego 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 na połączenie tablicy account_keys z
informacjami w header wiadomości w celu określenia uprawnień dla
każdego konta.
Diagram przedstawiający kolejność tablicy adresów kont
Ostatni blockhash
recent_blockhash wiadomości to wartość hash, która działa jako znacznik czasu
transakcji i zapobiega duplikatom 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
uzyskać aktualny blockhash oraz ostatnią wysokość bloku, przy której blockhash
będzie ważny.
Instrukcje
instructions wiadomości to tablica wszystkich instrukcji do przetworzenia,
wysyłana w
kompaktowym formacie tablicy.
Prefiks tablicy wskazuje jej długość. Każdy element tablicy to struktura
CompiledInstruction
i zawiera następujące informacje:
program_id_index: Indeks wskazujący adres w tablicyaccount_keys. Wartość ta wskazuje adres programu, który przetwarza instrukcję.accounts: Tablica indeksów wskazujących adresy w tablicyaccount_keys. Każdy indeks wskazuje adres konta wymaganego dla tej instrukcji.data: Tablica bajtów określająca, którą instrukcję wywołać w programie. Zawiera również wszelkie dodatkowe dane wymagane przez instrukcję (na przykład 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?