Solana accountmodel
Op Solana wordt alle data opgeslagen in wat "accounts" worden genoemd. Je kunt data op Solana zien als een openbare database met een enkele "Accounts" tabel, waarbij elke invoer in deze tabel een "account" is. Elk Solana-account deelt hetzelfde basis Account type.
Accounts
Belangrijke punten
- Accounts kunnen tot 10MiB aan data opslaan, die ofwel uitvoerbare programmacode of programmastatus bevat.
- Accounts vereisen een rent deposit in lamports (SOL) dat evenredig is aan de hoeveelheid opgeslagen data, en je kunt het volledig terugkrijgen wanneer je het account sluit.
- Elk account heeft een programma owner. Alleen het programma dat een account bezit, kan de data ervan wijzigen of het lamport-saldo verminderen. Maar iedereen kan het saldo verhogen.
- Sysvar accounts zijn speciale accounts die de netwerkclusterstatus opslaan.
- Programma-accounts slaan de uitvoerbare code van smart contracts op.
- Data-accounts worden door programma's gemaakt om programmastatus op te slaan en te beheren.
Account
Elk account op Solana heeft een uniek 32-byte adres, vaak weergegeven als een
base58 gecodeerde string (bijv. vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg
).
De relatie tussen het account en zijn adres werkt als een sleutel-waarde paar, waarbij het adres de sleutel is om de bijbehorende on-chain data van het account te lokaliseren. Het accountadres fungeert als de "unieke ID" voor elke invoer in de "Accounts" tabel.
Account Adres
De meeste Solana-accounts gebruiken een Ed25519 publieke sleutel als hun adres.
import { generateKeyPairSigner } from "@solana/kit";// Kit does not enable extractable private keysconst keypairSigner = await generateKeyPairSigner();console.log(keypairSigner);
Hoewel publieke sleutels vaak worden gebruikt als account-adressen, ondersteunt Solana ook een functie genaamd Program Derived Addresses (PDA's). PDA's zijn speciale adressen die je deterministisch kunt afleiden van een programma-ID en optionele inputs (seeds).
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}`);
Account type
Accounts hebben een maximale grootte van 10MiB en elk account op Solana deelt hetzelfde basis Account type.
Account Type
Elk account op Solana heeft de volgende velden:
data
: Een byte-array die willekeurige gegevens voor een account opslaat. Voor niet-uitvoerbare accounts wordt hier vaak de status opgeslagen die bedoeld is om uit te lezen. Voor programma-accounts (smart contracts) bevat dit de uitvoerbare programmacode. Het dataveld wordt vaak "account data" genoemd.executable
: Deze vlag geeft aan of een account een programma is.lamports
: Het saldo van het account in lamports, de kleinste eenheid van SOL (1 SOL = 1 miljard lamports).owner
: De programma-ID (publieke sleutel) van het programma dat eigenaar is van dit account. Alleen het eigenaar-programma kan de gegevens van het account wijzigen of het lamports-saldo verminderen.rent_epoch
: Een verouderd veld uit de tijd dat Solana een mechanisme had dat periodiek lamports van accounts aftrok. Hoewel dit veld nog steeds bestaat in het Account-type, wordt het niet meer gebruikt sinds rent-inning is afgeschaft.
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,}
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);
Rent
Om gegevens on-chain op te slaan, moeten accounts ook een lamport (SOL) saldo behouden dat evenredig is aan de hoeveelheid gegevens die op het account is opgeslagen (in bytes). Dit saldo wordt "rent" genoemd, maar het werkt meer als een borg omdat je het volledige bedrag kunt terugkrijgen wanneer je een account sluit. Je kunt de berekening hier vinden met deze constanten.
De term "rent" komt van een verouderd mechanisme dat regelmatig lamports aftrok van accounts die onder de rent-drempel vielen. Dit mechanisme is niet meer actief.
Programma-eigenaar
Op Solana worden "smart contracts" programma's genoemd. Programma- eigenaarschap is een essentieel onderdeel van het Solana Account Model. Elk account heeft een aangewezen programma als eigenaar. Alleen het eigenaarsprogramma kan:
- Het
data
veld van het account wijzigen - Lamports aftrekken van het saldo van het account
System Program
Standaard zijn alle nieuwe accounts eigendom van het System Program. Het System Program doet een aantal belangrijke dingen:
- Aanmaken van nieuwe accounts: Alleen het System Program kan nieuwe accounts aanmaken.
- Ruimtetoewijzing: Stelt de bytecapaciteit in voor het gegevensveld van elk account.
- Overdracht / Toewijzing van programma-eigenaarschap: Zodra het System Program een account heeft aangemaakt, kan het de aangewezen programma-eigenaar opnieuw toewijzen aan een ander programma-account. Zo nemen aangepaste programma's eigendom over van nieuwe accounts die door het System Program zijn aangemaakt.
Alle "wallet"-accounts op Solana zijn gewoon accounts die eigendom zijn van het System Program. Het lamport-saldo in deze accounts toont de hoeveelheid SOL die de wallet bezit. Alleen accounts die eigendom zijn van het System Program kunnen transactiekosten betalen.
Systeemaccount
Sysvar-accounts
Sysvar-accounts zijn speciale accounts op vooraf gedefinieerde adressen die toegang bieden tot clusterstatusgegevens. Deze accounts worden dynamisch bijgewerkt met gegevens over het netwerkcluster. Je kunt de volledige lijst van Sysvar-accounts hier vinden.
import { Address, createSolanaRpc } from "@solana/kit";const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");const SYSVAR_CLOCK_ADDRESS ="SysvarC1ock11111111111111111111111111111111" as Address;const accountInfo = await rpc.getAccountInfo(SYSVAR_CLOCK_ADDRESS, { encoding: "base64" }).send();console.log(accountInfo);
Programma-account
Het implementeren van een Solana-programma creëert een uitvoerbaar programma-account. Het programma-account slaat de uitvoerbare code van het programma op.
Programma-accounts zijn eigendom van een Loader-programma.
Programma-account
Voor de eenvoud kun je het programma-account behandelen als het programma zelf. Wanneer je de instructies van een programma aanroept, specificeer je het adres van het programma-account (vaak de "Program ID" genoemd).
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);
Wanneer je een Solana-programma implementeert, wordt het opgeslagen in een programma-account. Programma-accounts zijn eigendom van een Loader-programma. Er zijn verschillende versies van de loader, maar alle behalve loader-v3 slaan de uitvoerbare code direct op in het programma-account. Loader-v3 slaat de uitvoerbare code op in een apart "programma-data-account" en het programma-account verwijst er alleen naar. Wanneer je een nieuw programma implementeert, gebruikt de Solana CLI standaard de nieuwste loader-versie.
Buffer-account
Loader-v3 heeft een speciaal accounttype voor het tijdelijk opslaan van een programma tijdens implementatie of herimplementatie/upgrades. In loader-v4 zijn er nog steeds buffers, maar dit zijn gewoon normale programma-accounts.
Programma-data-account
Loader-v3 werkt anders dan alle andere BPF Loader-programma's. Het programma-account bevat alleen het adres van een programma-data-account, dat de feitelijke uitvoerbare code opslaat:
Program Data Account
Verwar deze programma-dataaccounts niet met de dataaccounts van programma's (zie hieronder).
Data Account
Op Solana wordt de uitvoerbare code van een programma opgeslagen in een ander account dan de status van het programma. Dit is vergelijkbaar met hoe besturingssystemen doorgaans aparte bestanden hebben voor programma's en hun gegevens.
Om de status bij te houden, definiëren programma's instructies om aparte accounts te maken die ze bezitten. Elk van deze accounts heeft zijn eigen unieke adres en kan willekeurige gegevens opslaan die door het programma zijn gedefinieerd.
Data Account
Merk op dat alleen het System Program nieuwe accounts kan aanmaken. Zodra het System Program een account heeft aangemaakt, kan het vervolgens het eigendom van het nieuwe account overdragen of toewijzen aan een ander programma.
Met andere woorden, het maken van een data account voor een aangepast programma bestaat uit twee stappen:
- Roep het System Program aan om een account aan te maken en draag vervolgens het eigendom over aan het aangepaste programma
- Roep het aangepaste programma aan, dat nu eigenaar is van het account, om de accountgegevens te initialiseren zoals gedefinieerd door de instructie van het programma
Dit proces van accountcreatie wordt vaak geabstraheerd als één enkele stap, maar het is nuttig om het onderliggende proces te begrijpen.
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} from "@solana-program/token-2022";// Create Connection, local validator in this exampleconst rpc = createSolanaRpc("http://127.0.0.1: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);
Is this page helpful?