نموذج حساب سولانا

في سولانا، يتم تخزين جميع البيانات فيما يسمى "الحسابات". يمكنك التفكير في البيانات على سولانا كقاعدة بيانات عامة تحتوي على جدول واحد "الحسابات"، حيث كل إدخال في هذا الجدول هو "حساب". كل حساب في سولانا يشترك في نفس نوع الحساب الأساسي.

الحساباتالحسابات

النقاط الرئيسية

  • يمكن للحسابات تخزين ما يصل إلى 10MiB من البيانات، والتي تحتوي إما على رمز البرنامج القابل للتنفيذ أو حالة البرنامج.
  • تتطلب الحسابات إيداع إيجار (rent) بوحدات lamport (SOL) يتناسب مع كمية البيانات المخزنة، ويمكنك استرداده بالكامل عند إغلاق الحساب.
  • كل حساب له مالك برنامج. فقط البرنامج الذي يملك الحساب يمكنه تغيير بياناته أو خصم رصيد الـ lamport الخاص به. ولكن يمكن لأي شخص زيادة الرصيد.
  • حسابات Sysvar هي حسابات خاصة تخزن حالة مجموعة الشبكة.
  • حسابات البرامج (program account) تخزن الرمز القابل للتنفيذ للعقود الذكية.
  • حسابات البيانات يتم إنشاؤها بواسطة البرامج لتخزين وإدارة حالة البرنامج.

الحساب

كل حساب على سولانا له عنوان فريد مكون من 32 بايت، غالبًا ما يظهر كسلسلة مشفرة بنظام base58 (مثل 14grJpemFaf88c8tiVb77W7TYg2W3ir6pfkKz3YjhhZ5).

تعمل العلاقة بين الحساب وعنوانه مثل زوج المفتاح والقيمة، حيث يكون العنوان هو المفتاح لتحديد موقع البيانات المقابلة على السلسلة للحساب. يعمل عنوان الحساب كـ "معرف فريد" لكل إدخال في جدول "الحسابات".

عنوان الحسابعنوان الحساب

تستخدم معظم حسابات سولانا مفتاح Ed25519 العام كعنوان لها.

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

بينما يتم استخدام المفاتيح العامة بشكل شائع كعناوين للحسابات، تدعم سولانا أيضًا ميزة تسمى العناوين المشتقة من البرنامج (PDAs). العناوين المشتقة من البرنامج هي عناوين خاصة يمكنك اشتقاقها بشكل حتمي من معرف البرنامج ومدخلات اختيارية (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}`);
Console
Click to execute the code.

نوع الحساب

الحسابات لها حجم أقصى يبلغ 10MiB وكل حساب على سولانا يشترك في نفس نوع الحساب الأساسي.

نوع الحسابنوع الحساب

كل حساب على سولانا يحتوي على الحقول التالية.

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

حقل لامبورت

رصيد الحساب بوحدة لامبورت، وهي أصغر وحدة من SOL (1 SOL = 1 مليار لامبورت). رصيد SOL للحساب هو المبلغ الموجود في حقل lamports محولًا إلى SOL.

يجب أن تحتوي حسابات سولانا على رصيد لامبورت أدنى يتناسب مع كمية البيانات المخزنة في الحساب (بالبايت). يسمى هذا الرصيد الأدنى "rent".

يمكن استرداد رصيد اللامبورت المخزن في الحساب بالكامل عند إغلاق الحساب.

حقل البيانات

مصفوفة بايت تخزن بيانات عشوائية للحساب. يُطلق على حقل البيانات عادةً "بيانات الحساب".

  • بالنسبة لحسابات البرامج (العقود الذكية)، يحتوي هذا الحقل إما على رمز البرنامج القابل للتنفيذ نفسه أو عنوان حساب آخر يخزن رمز البرنامج القابل للتنفيذ.
  • بالنسبة للحسابات غير القابلة للتنفيذ، فإنها تخزن عمومًا الحالة المقصود قراءتها.

تتضمن قراءة البيانات من حساب سولانا خطوتين:

  1. جلب الحساب باستخدام عنوانه (المفتاح العام)
  2. فك تشفير حقل بيانات الحساب من البايتات الخام إلى بنية البيانات المناسبة، والتي يحددها البرنامج الذي يملك الحساب

حقل المالك

معرف البرنامج (المفتاح العام) للبرنامج الذي يملك هذا الحساب.

كل حساب في سولانا له برنامج معين كمالك له. فقط البرنامج الذي يملك الحساب يمكنه تغيير بيانات الحساب أو خصم رصيد اللامبورت الخاص به.

تحدد التعليمات المعرفة في البرنامج كيفية تغيير بيانات الحساب ورصيد lamport.

حقل قابلية التنفيذ

يشير هذا الحقل إلى ما إذا كان الحساب برنامجًا قابلاً للتنفيذ.

  • إذا كان true، فإن الحساب هو برنامج سولانا قابل للتنفيذ.
  • إذا كان false، فإن الحساب هو حساب بيانات يخزن الحالة.

بالنسبة للحسابات القابلة للتنفيذ، يحتوي حقل owner على معرف برنامج المحمّل. برامج المحمّل هي برامج مدمجة مسؤولة عن تحميل وإدارة حسابات البرامج القابلة للتنفيذ.

حقل فترة الإيجار

حقل rent_epoch هو حقل قديم لم يعد مستخدمًا.

في الأصل، كان هذا الحقل يتتبع متى سيحتاج الحساب إلى دفع الإيجار (بوحدات lamport) للحفاظ على بياناته على الشبكة. ومع ذلك، تم إلغاء آلية تحصيل الإيجار هذه منذ ذلك الحين.

حقل لامبورت

رصيد الحساب بوحدة لامبورت، وهي أصغر وحدة من SOL (1 SOL = 1 مليار لامبورت). رصيد SOL للحساب هو المبلغ الموجود في حقل lamports محولًا إلى SOL.

يجب أن تحتوي حسابات سولانا على رصيد لامبورت أدنى يتناسب مع كمية البيانات المخزنة في الحساب (بالبايت). يسمى هذا الرصيد الأدنى "rent".

يمكن استرداد رصيد اللامبورت المخزن في الحساب بالكامل عند إغلاق الحساب.

حقل البيانات

مصفوفة بايت تخزن بيانات عشوائية للحساب. يُطلق على حقل البيانات عادةً "بيانات الحساب".

  • بالنسبة لحسابات البرامج (العقود الذكية)، يحتوي هذا الحقل إما على رمز البرنامج القابل للتنفيذ نفسه أو عنوان حساب آخر يخزن رمز البرنامج القابل للتنفيذ.
  • بالنسبة للحسابات غير القابلة للتنفيذ، فإنها تخزن عمومًا الحالة المقصود قراءتها.

تتضمن قراءة البيانات من حساب سولانا خطوتين:

  1. جلب الحساب باستخدام عنوانه (المفتاح العام)
  2. فك تشفير حقل بيانات الحساب من البايتات الخام إلى بنية البيانات المناسبة، والتي يحددها البرنامج الذي يملك الحساب

حقل المالك

معرف البرنامج (المفتاح العام) للبرنامج الذي يملك هذا الحساب.

كل حساب في سولانا له برنامج معين كمالك له. فقط البرنامج الذي يملك الحساب يمكنه تغيير بيانات الحساب أو خصم رصيد اللامبورت الخاص به.

تحدد التعليمات المعرفة في البرنامج كيفية تغيير بيانات الحساب ورصيد lamport.

حقل قابلية التنفيذ

يشير هذا الحقل إلى ما إذا كان الحساب برنامجًا قابلاً للتنفيذ.

  • إذا كان true، فإن الحساب هو برنامج سولانا قابل للتنفيذ.
  • إذا كان false، فإن الحساب هو حساب بيانات يخزن الحالة.

بالنسبة للحسابات القابلة للتنفيذ، يحتوي حقل owner على معرف برنامج المحمّل. برامج المحمّل هي برامج مدمجة مسؤولة عن تحميل وإدارة حسابات البرامج القابلة للتنفيذ.

حقل فترة الإيجار

حقل rent_epoch هو حقل قديم لم يعد مستخدمًا.

في الأصل، كان هذا الحقل يتتبع متى سيحتاج الحساب إلى دفع الإيجار (بوحدات lamport) للحفاظ على بياناته على الشبكة. ومع ذلك، تم إلغاء آلية تحصيل الإيجار هذه منذ ذلك الحين.

Base Account Type
pub struct Account {
/// lamports in the account
pub lamports: u64,
}
// Example Token Mint Account
Account {
lamports: 1461600,
}
// Example Token Program Account
Account {
lamports: 4513200894,
}

الإيجار

لتخزين البيانات على السلسلة، يجب أن تحتفظ الحسابات أيضًا برصيد lamport (SOL) يتناسب مع كمية البيانات المخزنة في الحساب (بالبايت). يُسمى هذا الرصيد "الإيجار"، لكنه يعمل أكثر كوديعة لأنه يمكنك استرداد المبلغ بالكامل عند إغلاق الحساب. يمكنك العثور على طريقة الحساب هنا باستخدام هذه الثوابت.

يأتي مصطلح "الإيجار" من آلية قديمة كانت تخصم بانتظام وحدات lamport من الحسابات التي تقل عن حد الإيجار. هذه الآلية لم تعد نشطة.

مالك البرنامج

في سولانا، تُسمى "العقود الذكية" برامج. ملكية البرنامج هي جزء أساسي من نموذج حساب سولانا. كل حساب له برنامج معين كمالك له. فقط البرنامج المالك يمكنه:

  • تغيير حقل data للحساب
  • خصم lamports من رصيد الحساب

يحدد كل برنامج بنية البيانات المخزنة في حقل data للحساب. تحدد تعليمات البرنامج كيفية تغيير هذه البيانات ورصيد lamports للحساب.

System Program

بشكل افتراضي، تكون جميع الحسابات الجديدة مملوكة لـ System Program. يقوم System Program بأداء الوظائف الرئيسية التالية:

الوظيفةالوصف
إنشاء حساب جديدفقط System Program يمكنه إنشاء حسابات جديدة.
تخصيص المساحةيحدد سعة البايت لحقل البيانات لكل حساب.
تعيين ملكية البرنامجبمجرد أن ينشئ System Program حسابًا، يمكنه إعادة تعيين مالك البرنامج المحدد إلى حساب برنامج مختلف. هكذا تأخذ البرامج المخصصة ملكية الحسابات الجديدة التي تم إنشاؤها بواسطة System Program.
تحويل SOLتحويل lamports (SOL) من حسابات النظام إلى حسابات أخرى.

لاحظ أن جميع حسابات "المحفظة" على سولانا هي "حسابات نظام" مملوكة لـ System Program. يظهر رصيد lamport في هذه الحسابات مقدار SOL المملوك للمحفظة. فقط حسابات النظام يمكنها دفع رسوم المعاملات.

حساب النظامحساب النظام

عندما يتم إرسال SOL إلى عنوان جديد لأول مرة، يتم إنشاء حساب تلقائيًا في ذلك العنوان مملوك لـ System Program.

في المثال أدناه، يتم إنشاء زوج مفاتيح جديد وتمويله بـ SOL. قم بتشغيل الكود لرؤية النتيجة. لاحظ أن حقل owner للحساب هو System Program بالعنوان 11111111111111111111111111111111.

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

حسابات Sysvar

حسابات Sysvar هي حسابات خاصة بعناوين محددة مسبقًا توفر وصولاً إلى بيانات حالة المجموعة. تتحدث هذه الحسابات ديناميكيًا مع بيانات حول مجموعة الشبكة. يمكنك العثور على القائمة الكاملة لحسابات Sysvar هنا.

يوضح المثال التالي كيفية جلب وفك تشفير البيانات من حساب 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 data
const clock = await fetchSysvarClock(rpc);
console.log(clock);
Console
Click to execute the code.

program account

نشر برنامج سولانا ينشئ program account قابل للتنفيذ. يخزن program account الكود القابل للتنفيذ للبرنامج. تكون program accounts مملوكة من قبل برنامج التحميل.

Program AccountProgram Account

للتبسيط، يمكنك التعامل مع program account كما لو كان البرنامج نفسه. عندما تستدعي تعليمات البرنامج، فإنك تحدد عنوان program account (يُطلق عليه عادةً "Program ID").

يقوم المثال التالي بجلب Token Program لإظهار أن program accounts لها نفس النوع الأساسي Account، باستثناء أن الحقل executable يتم تعيينه إلى true. نظرًا لأن program accounts تحتوي على كود قابل للتنفيذ في حقل البيانات الخاص بها، فإننا لا نقوم بفك تشفير البيانات.

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

عند نشر برنامج سولانا، يتم تخزينه في program account. تكون program accounts مملوكة من قبل برنامج التحميل. هناك عدة إصدارات من برنامج التحميل، لكن جميعها باستثناء loader-v3 تخزن الكود القابل للتنفيذ مباشرة في program account. يقوم loader-v3 بتخزين الكود القابل للتنفيذ في "حساب بيانات البرنامج" منفصل ويشير program account فقط إليه. عند نشر برنامج جديد، تستخدم واجهة سطر أوامر سولانا أحدث إصدار من برنامج التحميل افتراضيًا.

حساب المخزن المؤقت

يحتوي Loader-v3 على نوع حساب خاص لتجهيز تحميل البرنامج مؤقتًا أثناء النشر أو الترقيات. في loader-v4، لا تزال هناك مخازن مؤقتة، لكنها مجرد حسابات برنامج عادية.

حساب بيانات البرنامج

يعمل Loader-v3 بشكل مختلف عن جميع برامج BPF Loader الأخرى. يحتوي حساب البرنامج فقط على عنوان حساب بيانات البرنامج، الذي يخزن الكود القابل للتنفيذ الفعلي:

حساب بيانات البرنامجحساب بيانات البرنامج

لا تخلط بين حسابات بيانات البرنامج هذه وحسابات البيانات الخاصة بالبرامج (انظر أدناه).

حساب البيانات

في سولانا، يتم تخزين الكود القابل للتنفيذ للبرنامج في حساب مختلف عن حالة البرنامج. هذا يشبه كيفية احتفاظ أنظمة التشغيل عادةً بملفات منفصلة للبرامج وبياناتها.

للحفاظ على الحالة، تحدد البرامج تعليمات لإنشاء حسابات منفصلة تمتلكها. يحتوي كل من هذه الحسابات على عنوانه الفريد الخاص ويمكنه تخزين أي بيانات عشوائية يحددها البرنامج.

حساب البياناتحساب البيانات

لاحظ أن System Program فقط يمكنه إنشاء حسابات جديدة. بمجرد إنشاء System Program لحساب، يمكنه بعد ذلك تعيين ملكية الحساب الجديد لبرنامج آخر.

بعبارة أخرى، يتطلب إنشاء حساب بيانات لبرنامج مخصص خطوتين:

  1. استدعاء System Program لإنشاء حساب، ثم نقل الملكية إلى البرنامج المخصص
  2. استدعاء البرنامج المخصص، الذي يمتلك الحساب الآن، لتهيئة بيانات الحساب كما هو محدد في تعليمات البرنامج

غالبًا ما يتم تبسيط عملية إنشاء الحساب هذه كخطوة واحدة، ولكن من المفيد فهم العملية الأساسية.

يوضح المثال التالي كيفية إنشاء واسترجاع حساب mint account مملوك من قبل برنامج 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 example
const rpc = createSolanaRpc("http://localhost: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);
const mintAccount = await fetchMint(rpc, mint.address);
console.log(mintAccount);
Console
Click to execute the code.

Is this page helpful?

جدول المحتويات

تعديل الصفحة