Solana Hesap Modeli
Solana'da, tüm veriler "hesaplar" olarak adlandırılan yapılarda saklanır. Solana'daki verileri, her girişin bir "hesap" olduğu tek bir "Hesaplar" tablosuna sahip genel bir veritabanı olarak düşünebilirsiniz. Her Solana hesabı aynı temel Hesap türünü paylaşır.
Hesaplar
Önemli Noktalar
- Hesaplar, yürütülebilir program kodunu veya program durumunu içeren en fazla 10MiB veri depolayabilir.
- Hesaplar, depolanan veri miktarıyla orantılı olarak lamport (SOL) cinsinden bir rent depozitosu gerektirir ve hesabı kapattığınızda bu depozitoyu tamamen geri alabilirsiniz.
- Her hesabın bir program sahibi vardır. Bir hesabın verilerini değiştirebilen veya lamport bakiyesini düşürebilen yalnızca hesabın sahibi olan programdır. Ancak herkes bakiyeyi artırabilir.
- Sysvar hesapları, ağ kümesi durumunu depolayan özel hesaplardır.
- Program hesapları, akıllı sözleşmelerin yürütülebilir kodunu depolar.
- Veri hesapları, program durumunu depolamak ve yönetmek için programlar tarafından oluşturulur.
Hesap
Solana'daki her hesabın, genellikle base58 kodlu bir dize olarak gösterilen
benzersiz 32 baytlık bir adresi vardır (örn.
vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg
).
Hesap ile adresi arasındaki ilişki, adresin hesabın zincir üzerindeki ilgili verilerini bulmak için anahtar olduğu bir anahtar-değer çifti gibi çalışır. Hesap adresi, "Hesaplar" tablosundaki her giriş için "benzersiz kimlik" görevi görür.
Hesap Adresi
Çoğu Solana hesabı, adres olarak bir Ed25519 açık anahtarı kullanır.
import { generateKeyPairSigner } from "@solana/kit";// Kit does not enable extractable private keysconst keypairSigner = await generateKeyPairSigner();console.log(keypairSigner);
Açık anahtarlar yaygın olarak hesap adresleri olarak kullanılırken, Solana aynı zamanda Program Derived Addresses (PDA'lar) adı verilen bir özelliği de destekler. PDA'lar, bir program kimliğinden ve isteğe bağlı girdilerden (seed) deterministik olarak türetebileceğiniz özel adreslerdir.
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}`);
Hesap Türü
Hesaplar maksimum 10MiB boyutunda olabilir ve Solana'daki her hesap aynı temel Account türünü paylaşır.
Hesap Türü
Solana'daki her Hesap aşağıdaki alanlara sahiptir.
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,}
Lamports Alanı
Hesabın lamport cinsinden bakiyesi, SOL'un en küçük birimi (1 SOL = 1 milyar
lamport). Bir hesabın SOL bakiyesi, lamports
alanındaki miktarın SOL'a
dönüştürülmüş halidir.
Solana hesaplarının, hesapta depolanan veri miktarıyla (bayt cinsinden) orantılı olan minimum bir lamport bakiyesine sahip olması gerekir. Bu minimum bakiye "rent" olarak adlandırılır.
Hesapta depolanan lamport bakiyesi, hesap kapatıldığında tamamen geri alınabilir.
Veri Alanı
Bir hesap için rastgele verileri depolayan bir bayt dizisi. Veri alanı genellikle "hesap verisi" olarak adlandırılır.
- Program hesapları (akıllı sözleşmeler) için, bu alan ya çalıştırılabilir program kodunun kendisini ya da çalıştırılabilir program kodunu depolayan başka bir hesabın adresini içerir.
- Çalıştırılamayan hesaplar için, bu genellikle okunması amaçlanan durumu depolar.
Solana hesabından veri okumak iki adımı içerir:
- Hesabı adresi (açık anahtar) kullanarak getirin
- Hesabın veri alanını ham baytlardan, hesabın sahibi olan program tarafından tanımlanan uygun veri yapısına dönüştürün
Sahip Alanı
Bu hesabın sahibi olan programın program kimliği (açık anahtar).
Her Solana hesabının sahibi olarak belirlenmiş bir programı vardır. Bir hesabın verilerini değiştirebilen veya lamport bakiyesini düşürebilen yalnızca hesabın sahibi olan programdır.
Bir programda tanımlanan talimatlar, hesabın verilerinin ve lamport bakiyesinin nasıl değiştirilebileceğini belirler.
Çalıştırılabilir Alanı
Bu alan, bir hesabın çalıştırılabilir bir program olup olmadığını gösterir.
- Eğer
true
ise, hesap çalıştırılabilir bir Solana programıdır. - Eğer
false
ise, hesap durumu depolayan bir veri hesabıdır.
Çalıştırılabilir hesaplar için, owner
alanı bir yükleyici programın program
kimliğini içerir. Yükleyici programlar, çalıştırılabilir program hesaplarını
yüklemek ve yönetmekten sorumlu yerleşik programlardır.
Rent Epoch Alanı
rent_epoch
alanı artık kullanılmayan eski bir alandır.
Başlangıçta, bu alan bir hesabın ağdaki verilerini korumak için ne zaman rent (lamport cinsinden) ödemesi gerektiğini takip ediyordu. Ancak, bu rent toplama mekanizması o zamandan beri kullanımdan kaldırılmıştır.
Rent
Zincir üzerinde veri depolamak için, hesapların ayrıca hesapta depolanan veri miktarıyla (bayt cinsinden) orantılı bir lamport (SOL) bakiyesi tutması gerekir. Bu bakiye "rent" olarak adlandırılır, ancak bir hesabı kapattığınızda tam tutarı geri alabileceğiniz için daha çok bir depozito gibi çalışır. Hesaplamayı burada bu sabitler kullanılarak bulabilirsiniz.
"Rent" terimi, rent eşiğinin altına düşen hesaplardan düzenli olarak lamport düşen eski bir mekanizmadan gelmektedir. Bu mekanizma artık aktif değildir.
Program Sahibi
Solana'da, "akıllı sözleşmeler" programlar olarak adlandırılır. Program sahipliği, Solana Hesap Modelinin önemli bir parçasıdır. Her hesabın sahibi olarak belirlenmiş bir programı vardır. Yalnızca sahibi olan program:
- Hesabın
data
alanını değiştirebilir - Hesabın bakiyesinden lamport düşebilir
Her program, bir hesabın data
alanında depolanan verinin yapısını tanımlar.
Programın talimatları, bu verinin ve hesabın lamports
bakiyesinin nasıl
değiştirilebileceğini belirler.
System Program
Varsayılan olarak, tüm yeni hesaplar System Program tarafından sahiplenilir. System Program aşağıdaki temel işlevleri yerine getirir:
İşlev | Açıklama |
---|---|
Yeni Hesap Oluşturma | Yalnızca System Program yeni hesaplar oluşturabilir. |
Alan Tahsisi | Her hesabın veri alanı için bayt kapasitesini ayarlar. |
Program Sahipliği Atama | System Program bir hesap oluşturduktan sonra, belirlenen program sahibini farklı bir program hesabına yeniden atayabilir. Özel programların System Program tarafından oluşturulan yeni hesapların sahipliğini alması bu şekilde gerçekleşir. |
SOL Transfer | System Hesaplarından diğer hesaplara lamport (SOL) transferi yapar. |
Solana'daki tüm "cüzdan" hesaplarının System Program tarafından sahiplenilen "System Accounts" olduğunu unutmayın. Bu hesaplardaki lamport bakiyesi, cüzdanın sahip olduğu SOL miktarını gösterir. Yalnızca System Accounts işlem ücretlerini ödeyebilir.
System Account
SOL ilk kez yeni bir adrese gönderildiğinde, o adreste System Program tarafından sahiplenilen bir hesap otomatik olarak oluşturulur.
Aşağıdaki örnekte, yeni bir keypair oluşturulur ve SOL ile fonlanır. Çıktıyı
görmek için kodu çalıştırın. Hesabın owner
alanının,
11111111111111111111111111111111
adresine sahip System Program olduğuna dikkat
edin.
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);
Sysvar Accounts
Sysvar accounts, küme durumu verilerine erişim sağlayan, önceden tanımlanmış adreslerdeki özel hesaplardır. Bu hesaplar, ağ kümesi hakkındaki verilerle dinamik olarak güncellenir. Sysvar Accounts'ın tam listesini burada bulabilirsiniz.
Aşağıdaki örnek, Sysvar Clock hesabından veri çekmeyi ve bu veriyi çözümlemeyi göstermektedir.
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
Bir Solana programı dağıtmak, çalıştırılabilir bir program account oluşturur. Program account, programın çalıştırılabilir kodunu saklar. Program account'lar bir Loader Program tarafından sahiplenilir.
Program Account
Basitlik açısından, program account'ı programın kendisi olarak düşünebilirsiniz. Bir programın talimatlarını çağırdığınızda, program account'ın adresini (yaygın olarak "Program ID" olarak adlandırılır) belirtirsiniz.
Aşağıdaki örnek, program account'ların aynı temel Account
tipine sahip
olduğunu göstermek için Token Program hesabını çeker, ancak executable
alanı
true
olarak ayarlanmıştır. Program account'lar veri alanlarında
çalıştırılabilir kod içerdiğinden, verileri çözümlemiyoruz.
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);
Bir Solana programını dağıttığınızda, program account içinde saklanır. Program account'ları bir Yükleyici Program tarafından sahiplenilir. Yükleyicinin birkaç versiyonu vardır, ancak loader-v3 hariç hepsi çalıştırılabilir kodu doğrudan program account içinde saklar. Loader-v3, çalıştırılabilir kodu ayrı bir "program data account" içinde saklar ve program account sadece buna işaret eder. Yeni bir program dağıttığınızda, Solana CLI varsayılan olarak en son yükleyici sürümünü kullanır.
Buffer Account
Loader-v3, dağıtım veya yükseltmeler sırasında bir programın yüklenmesini geçici olarak depolamak için özel bir hesap türüne sahiptir. Loader-v4'te hala buffer'lar vardır, ancak bunlar sadece normal program account'larıdır.
Program Data Account
Loader-v3, diğer tüm BPF Yükleyici programlarından farklı çalışır. Program account sadece gerçek çalıştırılabilir kodu depolayan bir program data account'ın adresini içerir:
Program Data Account
Bu program data account'ları, programların veri hesaplarıyla (aşağıya bakın) karıştırmayın.
Data Account
Solana'da, bir programın çalıştırılabilir kodu, programın durumundan farklı bir hesapta saklanır. Bu, işletim sistemlerinin genellikle programlar ve verileri için ayrı dosyalara sahip olması gibidir.
Durumu korumak için, programlar sahip oldukları ayrı hesaplar oluşturmak için talimatlar tanımlar. Bu hesapların her biri kendi benzersiz adresine sahiptir ve program tarafından tanımlanan herhangi bir keyfi veriyi depolayabilir.
Data Account
Yalnızca System Program'ın yeni hesaplar oluşturabileceğini unutmayın. System Program bir hesap oluşturduktan sonra, yeni hesabın sahipliğini başka bir programa devredebilir.
Başka bir deyişle, özel bir program için veri hesabı oluşturmak iki adımdan oluşur:
- Hesap oluşturmak için System Program'ı çağırın, ardından sahipliği özel programa aktarın
- Artık hesabın sahibi olan özel programı çağırarak, programın talimatında tanımlandığı şekilde hesap verilerini başlatın
Bu hesap oluşturma süreci genellikle tek bir adım olarak soyutlanır, ancak altta yatan süreci anlamak faydalıdır.
Aşağıdaki örnek, Token 2022 programının sahibi olduğu bir Token Mint hesabının nasıl oluşturulacağını ve alınacağını göstermektedir.
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?