Transaktiot ja ohjeet

Solanassa käyttäjät lähettävät transaktioita vuorovaikuttaakseen verkon kanssa. Transaktiot sisältävät yhden tai useamman ohjeen, jotka määrittelevät suoritettavat toiminnot. Ohjeiden suorituslogiikka on tallennettu Solana-verkkoon käyttöönotettuihin ohjelmiin, joissa kukin ohjelma määrittelee omat ohjeensa.

Alla on keskeisiä tietoja Solanan transaktioiden käsittelystä:

  • Jos transaktio sisältää useita ohjeita, ohjeet suoritetaan siinä järjestyksessä, jossa ne on lisätty transaktioon.
  • Transaktiot ovat "atomisia" - kaikkien ohjeiden on käsiteltävä onnistuneesti, tai koko transaktio epäonnistuu eikä muutoksia tapahdu.

Transaktio on olennaisesti pyyntö käsitellä yksi tai useampi ohje.

Transaktio yksinkertaistettunaTransaktio yksinkertaistettuna

Transaktio on kuin kirjekuori, joka sisältää lomakkeita. Jokainen lomake on ohje, joka kertoo verkolle, mitä tehdä. Transaktion lähettäminen on kuin kirjekuoren postittaminen lomakkeiden käsittelyä varten.

Keskeiset kohdat

  • Solana-transaktiot sisältävät ohjeita, jotka kutsuvat ohjelmia verkossa.
  • Transaktiot ovat atomisia - jos jokin ohje epäonnistuu, koko transaktio epäonnistuu eikä muutoksia tapahdu.
  • Transaktion ohjeet suoritetaan peräkkäisessä järjestyksessä.
  • Transaktion kokorajoitus on 1232 tavua.
  • Jokainen ohje vaatii kolme tietoelementtiä:
    1. Kutsuttavan ohjelman osoite
    2. Tilit, joista ohje lukee tai joihin se kirjoittaa
    3. Ohjeen mahdollisesti vaatimat lisätiedot (esim. funktioargumentit)

SOL-siirtoesimerkki

Alla oleva kaavio esittää transaktiota, jossa on yksi ohje SOL:n siirtämiseksi lähettäjältä vastaanottajalle.

Solanassa "lompakot" ovat tilejä, jotka omistaa System Program. Vain ohjelman omistaja voi muuttaa tilin tietoja, joten SOL:n siirtäminen vaatii transaktion lähettämistä System Programin kutsumiseksi.

SOL-siirtoSOL-siirto

Lähettäjätilin on allekirjoitettava (is_signer) transaktio, jotta System Program voi vähentää sen lamport-saldoa. Lähettäjä- ja vastaanottajatilien on oltava kirjoitettavia (is_writable), koska niiden lamport-saldot muuttuvat.

Transaktion lähettämisen jälkeen System Program käsittelee siirto- ohjeen. System Program päivittää sitten sekä lähettäjä- että vastaanottajatilien lamport-saldot.

SOL-siirtoprosessiSOL-siirtoprosessi

Alla olevat esimerkit näyttävät, miten lähetetään transaktio, joka siirtää SOL:ia yhdeltä tililtä toiselle.

import {
airdropFactory,
appendTransactionMessageInstructions,
createSolanaRpc,
createSolanaRpcSubscriptions,
createTransactionMessage,
generateKeyPairSigner,
getSignatureFromTransaction,
lamports,
pipe,
sendAndConfirmTransactionFactory,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
signTransactionMessageWithSigners
} from "@solana/kit";
import { getTransferSolInstruction } from "@solana-program/system";
// Create a connection to cluster
const rpc = createSolanaRpc("http://localhost:8899");
const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");
// Generate sender and recipient keypairs
const sender = await generateKeyPairSigner();
const recipient = await generateKeyPairSigner();
const LAMPORTS_PER_SOL = 1_000_000_000n;
const transferAmount = lamports(LAMPORTS_PER_SOL / 100n); // 0.01 SOL
// Fund sender with airdrop
await airdropFactory({ rpc, rpcSubscriptions })({
recipientAddress: sender.address,
lamports: lamports(LAMPORTS_PER_SOL), // 1 SOL
commitment: "confirmed"
});
// Check balance before transfer
const { value: preBalance1 } = await rpc.getBalance(sender.address).send();
const { value: preBalance2 } = await rpc.getBalance(recipient.address).send();
// Create a transfer instruction for transferring SOL from sender to recipient
const transferInstruction = getTransferSolInstruction({
source: sender,
destination: recipient.address,
amount: transferAmount // 0.01 SOL in lamports
});
// Add the transfer instruction to a new transaction
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(sender, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
(tx) => appendTransactionMessageInstructions([transferInstruction], tx)
);
// Send the transaction to the network
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(
signedTransaction,
{ commitment: "confirmed" }
);
const transactionSignature = getSignatureFromTransaction(signedTransaction);
// Check balance after transfer
const { value: postBalance1 } = await rpc.getBalance(sender.address).send();
const { value: postBalance2 } = await rpc.getBalance(recipient.address).send();
console.log(
"Sender prebalance:",
Number(preBalance1) / Number(LAMPORTS_PER_SOL)
);
console.log(
"Recipient prebalance:",
Number(preBalance2) / Number(LAMPORTS_PER_SOL)
);
console.log(
"Sender postbalance:",
Number(postBalance1) / Number(LAMPORTS_PER_SOL)
);
console.log(
"Recipient postbalance:",
Number(postBalance2) / Number(LAMPORTS_PER_SOL)
);
console.log("Transaction Signature:", transactionSignature);
Click to execute the code.

Asiakaskirjastot usein abstrahoivat ohjelmaohjeiden rakentamisen yksityiskohdat. Jos kirjastoa ei ole saatavilla, voit rakentaa ohjeen manuaalisesti. Tämä edellyttää, että tunnet ohjeen toteutuksen yksityiskohdat.

Alla olevat esimerkit näyttävät, miten siirto-ohje rakennetaan manuaalisesti. Expanded Instruction välilehti on toiminnallisesti vastaava kuin Instruction välilehti.

  • Kit
const transferAmount = 0.01; // 0.01 SOL
const transferInstruction = getTransferSolInstruction({
source: sender,
destination: recipient.address,
amount: transferAmount * LAMPORTS_PER_SOL
});
  • Legacy
const transferAmount = 0.01; // 0.01 SOL
const transferInstruction = SystemProgram.transfer({
fromPubkey: sender.publicKey,
toPubkey: receiver.publicKey,
lamports: transferAmount * LAMPORTS_PER_SOL
});
  • Rust
let transfer_amount = LAMPORTS_PER_SOL / 100; // 0.01 SOL
let transfer_instruction =
system_instruction::transfer(&sender.pubkey(), &recipient.pubkey(), transfer_amount);

Seuraavissa osioissa käymme läpi transaktioiden ja ohjeiden yksityiskohdat.

Instructions

Solanan ohjelmaan kohdistuvaa instructionia voidaan ajatella julkisena funktiona, jota kuka tahansa Solana-verkon käyttäjä voi kutsua.

Ohjelman instruction-kutsun suorittaminen vaatii kolme keskeistä tietoa:

  • Program ID: Ohjelma, joka sisältää instruction-kutsun suorituslogiikan
  • Accounts: Lista tileistä, joita instruction tarvitsee
  • Instruction Data: Tavutaulukko, joka määrittää ohjelmassa suoritettavan instruction-kutsun ja kaikki sen vaatimat argumentit
Instruction
pub struct Instruction {
/// Pubkey of the program that executes this instruction.
pub program_id: Pubkey,
/// Metadata describing accounts that should be passed to the program.
pub accounts: Vec<AccountMeta>,
/// Opaque data passed to the program for its own interpretation.
pub data: Vec<u8>,
}

Transaction InstructionTransaction Instruction

AccountMeta

Jokainen instruction-kutsun vaatima tili on annettava AccountMeta -muodossa, joka sisältää:

  • pubkey: Tilin osoite
  • is_signer: Tarvitseeko tilin allekirjoittaa transaktio
  • is_writable: Muokkaako instruction tilin dataa
AccountMeta
pub struct AccountMeta {
/// An account's public key.
pub pubkey: Pubkey,
/// True if an `Instruction` requires a `Transaction` signature matching `pubkey`.
pub is_signer: bool,
/// True if the account data or metadata may be mutated during program execution.
pub is_writable: bool,
}

AccountMetaAccountMeta

Määrittelemällä etukäteen, mitä tilejä instruction lukee tai kirjoittaa, transaktiot, jotka eivät muokkaa samoja tilejä, voidaan suorittaa rinnakkain.

Esimerkki instruction-rakenteesta

Suorita alla olevat esimerkit nähdäksesi SOL-siirto-instructionin rakenteen.

import { generateKeyPairSigner, lamports } from "@solana/kit";
import { getTransferSolInstruction } from "@solana-program/system";
// Generate sender and recipient keypairs
const sender = await generateKeyPairSigner();
const recipient = await generateKeyPairSigner();
// Define the amount to transfer
const 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 recipient
const transferInstruction = getTransferSolInstruction({
source: sender,
destination: recipient.address,
amount: transferAmount
});
console.log(JSON.stringify(transferInstruction, null, 2));
Click to execute the code.

Seuraavat esimerkit näyttävät edellisten koodiesimerkkien tulosteen. Tarkka muoto vaihtelee SDK:n mukaan, mutta jokainen Solana instruction vaatii seuraavat tiedot:

  • Program ID: Ohjelman osoite, joka suorittaa käskyn.
  • Tilit: Luettelo käskyn vaatimista tileistä. Jokaiselle tilille käskyn on määritettävä sen osoite, onko sen allekirjoitettava transaktio ja kirjoitetaanko siihen.
  • Data: Tavupuskuri, joka kertoo ohjelmalle, mikä käsky suoritetaan ja sisältää käskyn vaatimat argumentit.
{
"accounts": [
{
"address": "Hu28vRMGWpQXN56eaE7jRiDDRRz3vCXEs7EKHRfL6bC",
"role": 3,
"signer": {
"address": "Hu28vRMGWpQXN56eaE7jRiDDRRz3vCXEs7EKHRfL6bC",
"keyPair": {
"privateKey": {},
"publicKey": {}
}
}
},
{
"address": "2mBY6CTgeyJNJDzo6d2Umipw2aGUquUA7hLdFttNEj7p",
"role": 1
}
],
"programAddress": "11111111111111111111111111111111",
"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
}
}

Transaktiot

Solana transaktio koostuu seuraavista:

  1. Allekirjoitukset: Transaktioon sisältyvien allekirjoitusten taulukko.
  2. Viesti: Luettelo käskyistä, jotka käsitellään atomisesti.
Transaction
pub struct Transaction {
#[wasm_bindgen(skip)]
#[serde(with = "short_vec")]
pub signatures: Vec<Signature>,
#[wasm_bindgen(skip)]
pub message: Message,
}

Transaktion muotoTransaktion muoto

Transaktion viestin rakenne koostuu seuraavista:

Message
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>,
}

Transaktion viestiTransaktion viesti

Transaktion koko

Solana-transaktioilla on kokorajoitus, joka on 1232 tavua. Tämä rajoitus tulee IPv6 Maximum Transmission Unit (MTU) -koosta, joka on 1280 tavua, miinus 48 tavua verkko-otsikoille (40 tavua IPv6 + 8 tavua fragmenttiotsikko).

Transaktion kokonaiskoon (allekirjoitukset ja viesti) on pysyttävä tämän rajan alla ja se sisältää:

  • Allekirjoitukset: 64 tavua kukin
  • Viesti: Otsikko (3 tavua), tilien avaimet (32 tavua kukin), viimeisin lohkohash (32 tavua) ja käskyt

Transaktion muotoTransaktion muoto

Viestin otsikko

Viestin otsikko käyttää kolmea tavua määrittämään tilien käyttöoikeudet.

  1. Vaaditut allekirjoitukset
  2. Vain luku -tilassa olevien allekirjoitettujen tilien määrä
  3. Vain luku -tilassa olevien allekirjoittamattomien tilien määrä
MessageHeader
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,
}

Viestin otsikkoViestin otsikko

Compact-Array-muoto

Compact array (tiivistetty taulukko) transaktion viestissä on taulukko, joka on sarjallistettu seuraavassa muodossa:

  1. Taulukon pituus (koodattu compact-u16 -muodossa)
  2. Taulukon kohteet listattuna peräkkäin

Compact array -muotoCompact array -muoto

Tätä muotoa käytetään koodaamaan tiliosoitteiden ja instruction-komentojen taulukoiden pituudet transaktion viesteissä.

Tiliosoitteiden taulukko

Transaktion viesti sisältää taulukon tiliosoitteita, joita sen instruction-komennot tarvitsevat. Taulukko alkaa compact-u16 -numerolla, joka ilmaisee kuinka monta osoitetta se sisältää. Osoitteet on järjestetty niiden käyttöoikeuksien mukaan, jotka määräytyvät viestin otsikon perusteella.

  • Tilit, jotka ovat kirjoitettavia ja allekirjoittajia
  • Tilit, jotka ovat vain luettavia ja allekirjoittajia
  • Tilit, jotka ovat kirjoitettavia mutta eivät allekirjoittajia
  • Tilit, jotka ovat vain luettavia eivätkä allekirjoittajia

Tiliosoitteiden tiivistetty taulukkoTiliosoitteiden tiivistetty taulukko

Recent Blockhash

Jokainen transaktio vaatii recent blockhash -arvon, jolla on kaksi tarkoitusta:

  1. Toimii aikaleimana
  2. Estää transaktioiden kopiot

Blockhash vanhenee 150 lohkon jälkeen (noin 1 minuutti olettaen 400ms lohkoajat), minkä jälkeen transaktiota ei voida käsitellä.

Voit käyttää getLatestBlockhash RPC-metodia saadaksesi nykyisen blockhash-arvon ja viimeisen lohkon korkeuden, jossa blockhash on vielä voimassa. Tässä on esimerkki Solana Playground -alustalla.

Instruction-komentojen taulukko

Transaktion viesti sisältää taulukon instruction-komentoja CompiledInstruction -tyypissä. Instruction-komennot muunnetaan tähän tyyppiin, kun ne lisätään transaktioon.

Kuten tiliosoitteiden taulukko viestissä, se alkaa compact-u16 -pituudella, jota seuraa instruction-komentojen data. Jokainen instruction-komento sisältää:

  1. Ohjelmatunnuksen indeksi: u8-indeksi, joka osoittaa ohjelman osoitteeseen tilien osoitteiden taulukossa. Tämä määrittää ohjelman, joka käsittelee ohjeen.
  2. Tilien indeksit: Taulukko u8-indeksejä, jotka osoittavat tämän ohjeen vaatimiin tilien osoitteisiin.
  3. Instruction Data: Tavutaulukko, joka määrittää, mikä ohje ohjelmassa suoritetaan ja mitä lisätietoja ohje tarvitsee (esim. funktion argumentit).
CompiledInstruction
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>,
}

Kompakti taulukko ohjeitaKompakti taulukko ohjeita

Esimerkki transaktion rakenteesta

Suorita alla olevat esimerkit nähdäksesi transaktion rakenteen, jossa on yksi SOL- siirto-ohje.

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 keypairs
const sender = await generateKeyPairSigner();
const recipient = await generateKeyPairSigner();
// Define the amount to transfer
const 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 recipient
const transferInstruction = getTransferSolInstruction({
source: sender,
destination: recipient.address,
amount: transferAmount
});
// Create transaction message
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(sender, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
(tx) => appendTransactionMessageInstructions([transferInstruction], tx)
);
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);
// Decode the messageBytes
const compiledTransactionMessage =
getCompiledTransactionMessageDecoder().decode(signedTransaction.messageBytes);
console.log(JSON.stringify(compiledTransactionMessage, null, 2));
Click to execute the code.

Seuraavat esimerkit näyttävät transaktion viestin tulosteen edellisistä koodiesimerkeistä. Tarkka muoto vaihtelee SDK:n mukaan, mutta sisältää samat tiedot.

{
"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
}
}
]
}

Kun haet transaktion sen allekirjoituksen avulla lähetettyäsi sen verkkoon, saat vastauksen seuraavalla rakenteella.

message -kenttä sisältää seuraavat kentät:

  • header: Määrittää luku-/kirjoitusoikeudet ja allekirjoittajan oikeudet accountKeys -taulukon osoitteille

  • accountKeys: Taulukko kaikista tilien osoitteista, joita käytetään transaktion ohjeissa

  • recentBlockhash: Lohkohash, jota käytetään transaktion aikaleimana

  • instructions: Taulukko suoritettavista ohjeista. Jokainen account ja programIdIndex ohjeessa viittaa accountKeys -taulukkoon indeksin avulla.

  • signatures: Taulukko, joka sisältää allekirjoitukset kaikille tileille, joita transaktion ohjeet vaativat allekirjoittajiksi. Allekirjoitus luodaan allekirjoittamalla transaktion viesti käyttäen vastaavaa yksityistä avainta tilille.

Transaction Data
{
"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?

Sisällysluettelo

Muokkaa sivua