Model Akun Solana
Di Solana, semua data disimpan dalam apa yang disebut "akun." Anda dapat menganggap data di Solana sebagai database publik dengan satu tabel "Akun", di mana setiap entri dalam tabel ini adalah "akun." Setiap akun Solana berbagi tipe Akun dasar yang sama.
Akun
Poin Penting
- Akun dapat menyimpan hingga 10MiB data, yang berisi kode program yang dapat dieksekusi atau status program.
- Akun memerlukan deposit rent dalam lamport (SOL) yang sebanding dengan jumlah data yang disimpan, dan Anda dapat sepenuhnya memulihkannya saat menutup akun.
- Setiap akun memiliki pemilik program. Hanya program yang memiliki akun yang dapat mengubah datanya atau mengurangi saldo lamport-nya. Tetapi siapa pun dapat menambah saldo.
- Akun sysvar adalah akun khusus yang menyimpan status cluster jaringan.
- Akun program menyimpan kode yang dapat dieksekusi dari kontrak pintar.
- Akun data dibuat oleh program untuk menyimpan dan mengelola status program.
Akun
Setiap akun di Solana memiliki alamat unik 32 byte, sering ditampilkan sebagai
string yang dikodekan base58 (misalnya
14grJpemFaf88c8tiVb77W7TYg2W3ir6pfkKz3YjhhZ5
).
Hubungan antara akun dan alamatnya bekerja seperti pasangan key-value, di mana alamat adalah kunci untuk menemukan data on-chain yang sesuai dari akun tersebut. Alamat akun bertindak sebagai "ID unik" untuk setiap entri dalam tabel "Akun".
Alamat Akun
Sebagian besar akun Solana menggunakan kunci publik Ed25519 sebagai alamat mereka.
import { generateKeyPairSigner } from "@solana/kit";// Kit does not enable extractable private keysconst keypairSigner = await generateKeyPairSigner();console.log(keypairSigner);
Meskipun kunci publik umumnya digunakan sebagai alamat akun, Solana juga mendukung fitur yang disebut Program Derived Addresses (PDA). PDA adalah alamat khusus yang dapat Anda turunkan secara deterministik dari ID program dan input opsional (seed).
import { Address, getProgramDerivedAddress } from "@solana/kit";const programAddress = "11111111111111111111111111111111" as Address;const seeds = ["helloWorld"];const [pda, bump] = await getProgramDerivedAddress({programAddress,seeds});console.log(`PDA: ${pda}`);console.log(`Bump: ${bump}`);
Tipe Akun
Akun memiliki ukuran maksimal 10MiB dan setiap akun di Solana berbagi tipe dasar Account yang sama.
Tipe Akun
Setiap Akun di Solana memiliki bidang-bidang berikut.
pub struct Account {/// lamports in the accountpub lamports: u64,/// data held in this account#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]pub data: Vec<u8>,/// the program that owns this account. If executable, the program that loads this account.pub owner: Pubkey,/// this account's data contains a loaded program (and is now read-only)pub executable: bool,/// the epoch at which this account will next owe rentpub rent_epoch: Epoch,}
Bidang Lamports
Saldo akun dalam lamport, unit terkecil dari SOL (1 SOL = 1 miliar lamport).
Saldo SOL sebuah akun adalah jumlah dalam bidang lamports
yang dikonversi ke
SOL.
Akun Solana harus memiliki saldo lamport minimum yang sebanding dengan jumlah data yang disimpan pada akun (dalam byte). Saldo minimum ini disebut "rent."
Saldo lamport yang disimpan dalam akun dapat sepenuhnya dipulihkan ketika akun ditutup.
Bidang Data
Array byte yang menyimpan data arbitrer untuk sebuah akun. Bidang data ini umumnya disebut "data akun."
- Untuk akun program (kontrak pintar), bidang ini berisi kode program yang dapat dieksekusi atau alamat akun lain yang menyimpan kode program yang dapat dieksekusi.
- Untuk akun yang tidak dapat dieksekusi, ini umumnya menyimpan status yang dimaksudkan untuk dibaca.
Membaca data dari akun Solana melibatkan dua langkah:
- Ambil akun menggunakan alamatnya (kunci publik)
- Deserialisasi bidang data akun dari byte mentah ke dalam struktur data yang sesuai, yang ditentukan oleh program yang memiliki akun tersebut
Bidang Pemilik
ID program (kunci publik) dari program yang memiliki akun ini.
Setiap akun Solana memiliki program yang ditunjuk sebagai pemiliknya. Hanya program yang memiliki akun yang dapat mengubah data akun atau mengurangi saldo lamport-nya.
Instruksi yang didefinisikan dalam program menentukan bagaimana data akun dan saldo lamport dapat diubah.
Kolom Executable
Kolom ini menunjukkan apakah sebuah akun adalah program yang dapat dieksekusi.
- Jika
true
, akun tersebut adalah program Solana yang dapat dieksekusi. - Jika
false
, akun tersebut adalah akun data yang menyimpan state.
Untuk akun yang dapat dieksekusi, kolom owner
berisi ID program dari program
loader. Program loader adalah program bawaan yang bertanggung jawab untuk memuat
dan mengelola akun program yang dapat dieksekusi.
Kolom Rent Epoch
Kolom rent_epoch
adalah kolom lama yang tidak lagi digunakan.
Awalnya, kolom ini melacak kapan sebuah akun perlu membayar rent (dalam lamport) untuk mempertahankan datanya di jaringan. Namun, mekanisme pengumpulan rent ini sejak itu telah tidak digunakan lagi.
Rent
Untuk menyimpan data on-chain, akun juga harus mempertahankan saldo lamport (SOL) yang sebanding dengan jumlah data yang disimpan pada akun (dalam byte). Saldo ini disebut "rent", tetapi lebih berfungsi seperti deposit karena Anda dapat mendapatkan kembali jumlah penuh saat Anda menutup akun. Anda dapat menemukan perhitungannya di sini menggunakan konstanta ini.
Istilah "rent" berasal dari mekanisme lama yang secara teratur mengurangi lamport dari akun yang berada di bawah ambang batas rent. Mekanisme ini tidak lagi aktif.
Pemilik Program
Di Solana, "smart contract" disebut program. Kepemilikan program adalah bagian penting dari Model Akun Solana. Setiap akun memiliki program yang ditunjuk sebagai pemiliknya. Hanya program pemilik yang dapat:
- Mengubah bidang
data
akun - Mengurangi lamport dari saldo akun
Setiap program mendefinisikan struktur data yang disimpan dalam bidang data
akun. Instruksi program menentukan bagaimana data ini dan saldo lamports
akun
dapat diubah.
System Program
Secara default, semua akun baru dimiliki oleh System Program. System Program melakukan fungsi-fungsi utama berikut:
Fungsi | Deskripsi |
---|---|
Pembuatan Akun Baru | Hanya System Program yang dapat membuat akun baru. |
Alokasi Ruang | Mengatur kapasitas byte untuk bidang data setiap akun. |
Menetapkan Kepemilikan Program | Setelah System Program membuat akun, ia dapat menetapkan kembali pemilik program yang ditunjuk ke program account yang berbeda. Begitulah cara program kustom mengambil kepemilikan akun baru yang dibuat oleh System Program. |
Transfer SOL | Mentransfer lamport (SOL) dari Akun System ke akun lainnya. |
Perlu diketahui bahwa semua akun "dompet" di Solana adalah "Akun System" yang dimiliki oleh System Program. Saldo lamport dalam akun ini menunjukkan jumlah SOL yang dimiliki oleh dompet. Hanya Akun System yang dapat membayar biaya transaksi.
Akun System
Ketika SOL dikirim ke alamat baru untuk pertama kalinya, sebuah akun secara otomatis dibuat di alamat tersebut yang dimiliki oleh System Program.
Dalam contoh di bawah ini, sebuah keypair baru dibuat dan didanai dengan SOL.
Jalankan kode untuk melihat hasilnya. Perhatikan bahwa bidang owner
dari akun
tersebut adalah System Program dengan alamat 11111111111111111111111111111111
.
import {airdropFactory,createSolanaRpc,createSolanaRpcSubscriptions,generateKeyPairSigner,lamports} from "@solana/kit";// Create a connection to Solana clusterconst rpc = createSolanaRpc("http://localhost:8899");const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");// Generate a new keypairconst keypair = await generateKeyPairSigner();console.log(`Public Key: ${keypair.address}`);// Funding an address with SOL automatically creates an accountconst signature = await airdropFactory({ rpc, rpcSubscriptions })({recipientAddress: keypair.address,lamports: lamports(1_000_000_000n),commitment: "confirmed"});const accountInfo = await rpc.getAccountInfo(keypair.address).send();console.log(accountInfo);
Akun Sysvar
Akun Sysvar adalah akun khusus pada alamat yang telah ditentukan yang menyediakan akses ke data status klaster. Akun-akun ini diperbarui secara dinamis dengan data tentang klaster jaringan. Anda dapat menemukan daftar lengkap Akun Sysvar di sini.
Contoh berikut menunjukkan cara mengambil dan mendeserialkan data dari akun Sysvar Clock.
import { createSolanaRpc } from "@solana/kit";import { fetchSysvarClock, SYSVAR_CLOCK_ADDRESS } from "@solana/sysvars";const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");const accountInfo = await rpc.getAccountInfo(SYSVAR_CLOCK_ADDRESS, { encoding: "base64" }).send();console.log(accountInfo);// Automatically fetch and deserialize the account dataconst clock = await fetchSysvarClock(rpc);console.log(clock);
Program Account
Menerapkan program Solana menciptakan program account yang dapat dieksekusi. Program account menyimpan kode yang dapat dieksekusi dari program tersebut. Program account dimiliki oleh Loader Program.
Program Account
Untuk menyederhanakan, Anda dapat memperlakukan program account sebagai program itu sendiri. Ketika Anda memanggil instruksi program, Anda menentukan alamat program account (umumnya disebut "Program ID").
Contoh berikut mengambil Token Program account untuk menunjukkan bahwa program
account memiliki tipe dasar Account
yang sama, kecuali bidang executable
diatur ke true
. Karena program account berisi kode yang dapat dieksekusi di
bidang data mereka, kita tidak mendeserialkan datanya.
import { Address, createSolanaRpc } from "@solana/kit";const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");const programId = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address;const accountInfo = await rpc.getAccountInfo(programId, { encoding: "base64" }).send();console.log(accountInfo);
Ketika Anda menerapkan program Solana, program tersebut disimpan dalam program account. Program account dimiliki oleh Loader Program. Ada beberapa versi loader, tetapi semua kecuali loader-v3 menyimpan kode yang dapat dieksekusi langsung di program account. Loader-v3 menyimpan kode yang dapat dieksekusi dalam "program data account" terpisah dan program account hanya menunjuk ke akun tersebut. Ketika Anda menerapkan program baru, Solana CLI menggunakan versi loader terbaru secara default.
Akun Buffer
Loader-v3 memiliki tipe akun khusus untuk sementara menyiapkan unggahan program selama deployment atau upgrade. Di loader-v4, masih ada buffer, tetapi mereka hanyalah program account biasa.
Akun Data Program
Loader-v3 bekerja berbeda dari semua program BPF Loader lainnya. Program account hanya berisi alamat dari program data account, yang menyimpan kode yang dapat dieksekusi:
Akun Data Program
Jangan bingungkan program data account ini dengan data account dari program (lihat di bawah).
Akun Data
Di Solana, kode yang dapat dieksekusi dari program disimpan di akun yang berbeda dari state program. Ini seperti bagaimana sistem operasi biasanya memiliki file terpisah untuk program dan data mereka.
Untuk mempertahankan state, program mendefinisikan instruksi untuk membuat akun terpisah yang mereka miliki. Setiap akun ini memiliki alamat uniknya sendiri dan dapat menyimpan data arbitrer apa pun yang didefinisikan oleh program.
Akun Data
Perhatikan bahwa hanya System Program yang dapat membuat akun baru. Setelah System Program membuat akun, kemudian dapat menetapkan kepemilikan akun baru ke program lain.
Dengan kata lain, membuat data account untuk program kustom memerlukan dua langkah:
- Memanggil System Program untuk membuat akun, kemudian mentransfer kepemilikan ke program kustom
- Memanggil program kustom, yang sekarang memiliki akun tersebut, untuk menginisialisasi data akun seperti yang didefinisikan oleh instruksi program
Proses pembuatan akun ini sering diabstraksikan sebagai satu langkah, tetapi penting untuk memahami proses yang mendasarinya.
Contoh berikut menunjukkan cara membuat dan mengambil akun Token Mint yang dimiliki oleh program Token 2022.
import {airdropFactory,appendTransactionMessageInstructions,createSolanaRpc,createSolanaRpcSubscriptions,createTransactionMessage,generateKeyPairSigner,getSignatureFromTransaction,lamports,pipe,sendAndConfirmTransactionFactory,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,signTransactionMessageWithSigners} from "@solana/kit";import { getCreateAccountInstruction } from "@solana-program/system";import {getInitializeMintInstruction,getMintSize,TOKEN_2022_PROGRAM_ADDRESS,fetchMint} from "@solana-program/token-2022";// Create Connection, local validator in this exampleconst rpc = createSolanaRpc("http://localhost:8899");const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");// Generate keypairs for fee payerconst feePayer = await generateKeyPairSigner();// Fund fee payerawait airdropFactory({ rpc, rpcSubscriptions })({recipientAddress: feePayer.address,lamports: lamports(1_000_000_000n),commitment: "confirmed"});// Generate keypair to use as address of mintconst mint = await generateKeyPairSigner();// Get default mint account size (in bytes), no extensions enabledconst space = BigInt(getMintSize());// Get minimum balance for rent exemptionconst rent = await rpc.getMinimumBalanceForRentExemption(space).send();// Instruction to create new account for mint (token 2022 program)// Invokes the system programconst createAccountInstruction = getCreateAccountInstruction({payer: feePayer,newAccount: mint,lamports: rent,space,programAddress: TOKEN_2022_PROGRAM_ADDRESS});// Instruction to initialize mint account data// Invokes the token 2022 programconst initializeMintInstruction = getInitializeMintInstruction({mint: mint.address,decimals: 9,mintAuthority: feePayer.address});const instructions = [createAccountInstruction, initializeMintInstruction];// Get latest blockhash to include in transactionconst { value: latestBlockhash } = await rpc.getLatestBlockhash().send();// Create transaction messageconst transactionMessage = pipe(createTransactionMessage({ version: 0 }), // Create transaction message(tx) => setTransactionMessageFeePayerSigner(feePayer, tx), // Set fee payer(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), // Set transaction blockhash(tx) => appendTransactionMessageInstructions(instructions, tx) // Append instructions);// Sign transaction message with required signers (fee payer and mint keypair)const signedTransaction =await signTransactionMessageWithSigners(transactionMessage);// Send and confirm transactionawait sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction,{ commitment: "confirmed" });// Get transaction signatureconst transactionSignature = getSignatureFromTransaction(signedTransaction);console.log("Mint Address:", mint.address);console.log("Transaction Signature:", transactionSignature);const accountInfo = await rpc.getAccountInfo(mint.address).send();console.log(accountInfo);const mintAccount = await fetchMint(rpc, mint.address);console.log(mintAccount);
Is this page helpful?