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.

AccountsAccounts

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 AdresAccount Adres

De meeste Solana-accounts gebruiken een Ed25519 publieke sleutel als hun adres.

import { generateKeyPairSigner } from "@solana/kit";
// Kit does not enable extractable private keys
const keypairSigner = await generateKeyPairSigner();
console.log(keypairSigner);
Click to execute the code.

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}`);
Click to execute the code.

Account type

Accounts hebben een maximale grootte van 10MiB en elk account op Solana deelt hetzelfde basis Account type.

Account TypeAccount 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.
Base Account Type
pub struct Account {
/// lamports in the account
pub 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 rent
pub rent_epoch: Epoch,
}
import {
airdropFactory,
createSolanaRpc,
createSolanaRpcSubscriptions,
generateKeyPairSigner,
lamports
} from "@solana/kit";
// Create a connection to Solana cluster
const rpc = createSolanaRpc("http://localhost:8899");
const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");
// Generate a new keypair
const keypair = await generateKeyPairSigner();
console.log(`Public Key: ${keypair.address}`);
// Funding an address with SOL automatically creates an account
const 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);
Click to execute the code.

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.

SysteemaccountSysteemaccount

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);
Click to execute the code.

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-accountProgramma-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);
Click to execute the code.

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 AccountProgram 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 AccountData 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:

  1. Roep het System Program aan om een account aan te maken en draag vervolgens het eigendom over aan het aangepaste programma
  2. 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 example
const rpc = createSolanaRpc("http://127.0.0.1:8899");
const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");
// Generate keypairs for fee payer
const feePayer = await generateKeyPairSigner();
// Fund fee payer
await airdropFactory({ rpc, rpcSubscriptions })({
recipientAddress: feePayer.address,
lamports: lamports(1_000_000_000n),
commitment: "confirmed"
});
// Generate keypair to use as address of mint
const mint = await generateKeyPairSigner();
// Get default mint account size (in bytes), no extensions enabled
const space = BigInt(getMintSize());
// Get minimum balance for rent exemption
const rent = await rpc.getMinimumBalanceForRentExemption(space).send();
// Instruction to create new account for mint (token 2022 program)
// Invokes the system program
const createAccountInstruction = getCreateAccountInstruction({
payer: feePayer,
newAccount: mint,
lamports: rent,
space,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
});
// Instruction to initialize mint account data
// Invokes the token 2022 program
const initializeMintInstruction = getInitializeMintInstruction({
mint: mint.address,
decimals: 9,
mintAuthority: feePayer.address
});
const instructions = [createAccountInstruction, initializeMintInstruction];
// Get latest blockhash to include in transaction
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
// Create transaction message
const 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 transaction
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(
signedTransaction,
{ commitment: "confirmed" }
);
// Get transaction signature
const 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);
Click to execute the code.

Is this page helpful?

Inhoudsopgave

Pagina Bewerken