हमारे पिछले ट्यूटोरियल में, हमने इस बात पर चर्चा की थी कि एक अकाउंट को कैसे initialize किया जाए ताकि हम डेटा को स्टोरेज में persist कर सकें। यह ट्यूटोरियल दिखाता है कि हमारे द्वारा पहले से initialize किए गए अकाउंट में कैसे लिखा (write किया) जाए।
नीचे Solana अकाउंट्स को initialize करने के पिछले ट्यूटोरियल का कोड दिया गया है। हमने MyStorage और संबंधित Set struct में एक नंबर स्टोर करने के लिए एक set() फंक्शन जोड़ा है।
बाकी का कोड अपरिवर्तित (unchanged) है:
use anchor_lang::prelude::*;
use std::mem::size_of;
declare_id!("GLKUcCtHx6nkuDLTz5TNFrR4tt4wDNuk24Aid2GrDLC6");
#[program]
pub mod basic_storage {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
Ok(())
}
// ****************************
// *** THIS FUNCTION IS NEW ***
// ****************************
pub fn set(ctx: Context<Set>, new_x: u64) -> Result<()> {
ctx.accounts.my_storage.x = new_x;
Ok(())
}
}
// **************************
// *** THIS STRUCT IS NEW ***
// **************************
#[derive(Accounts)]
pub struct Set<'info> {
#[account(mut, seeds = [], bump)]
pub my_storage: Account<'info, MyStorage>,
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init,
payer = signer,
space=size_of::<MyStorage>() + 8,
seeds = [],
bump)]
pub my_storage: Account<'info, MyStorage>,
#[account(mut)]
pub signer: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[account]
pub struct MyStorage {
x: u64,
}
Exercise: आर्ग्यूमेंट 170 के साथ set() को कॉल करने के लिए टेस्ट को मॉडिफाई करें। यह MyStorage में x की वह वैल्यू है जिसे हम persist करने का प्रयास कर रहे हैं। आपको initialize() के बाद set() को कॉल करना होगा। 170 को bignumber बनाना न भूलें।
set() फंक्शन की व्याख्या
नीचे, हमने set() फंक्शन, Set struct, और MyStorage struct को एक साथ दिखाने के लिए कोड को थोड़ा पुनर्व्यवस्थित (reorder) किया है:

अब हम समझाते हैं कि ctx.accounts.my_storage.x = new_x कैसे काम करता है:
ctxमें accounts फील्ड (ऊपर का blue box) हमेंSetstruct की सभी keys का एक्सेस देता है। Rust में किसी struct की keys को इस तरह से लिस्ट नहीं किया जाता है। accounts कीSetstruct में keys को रेफर करने की क्षमता#[derive(Accounts)]मैक्रो (नीचे का blue box) के कारण जादुई रूप से जुड़ (insert हो) जाती है।- अकाउंट
my_storage(orange box) को mut या mutable (green box) सेट किया गया है क्योंकि हम इसमें मौजूद एक वैल्यू,x(red box) को बदलने का इरादा रखते हैं। AccountमेंMyStorageको एक generics पैरामीटर के रूप में पास करने से keymy_storage(orange box) हमेंMyStorageअकाउंट (yellow box) का रेफरेंस देती है। यह तथ्य कि हमने key के रूप मेंmy_storageऔर स्टोरेज struct के रूप मेंMyStorageका उपयोग किया है, केवल पढ़ने में आसानी (readability) के लिए है, उन्हें एक-दूसरे का camel-cased वेरिएशन होने की आवश्यकता नहीं है। जो चीज उन्हें “एक साथ बांधती है (ties them together)” उसे yellow boxes और yellow arrow के साथ दर्शाया गया है।
मूल रूप से, जब set() को कॉल किया जाता है, तो कॉलर (Typescript क्लाइंट) myStorage अकाउंट को set() में पास करता है। इस अकाउंट के अंदर स्टोरेज का एड्रेस होता है। पर्दे के पीछे (Behind the scenes), set स्टोरेज को लोड करेगा, x की नई वैल्यू लिखेगा, struct को serialize करेगा, और फिर इसे वापस स्टोर कर देगा।
Context struct Set
set() के लिए Context struct, initialize की तुलना में काफी सरल है क्योंकि इसे केवल एक रिसोर्स की आवश्यकता होती है: MyStorage अकाउंट का एक mutable रेफरेंस।
#[derive(Accounts)]
pub struct Set<'info> {
#[account(mut, seeds = [], bump)]
pub my_storage: Account<'info, MyStorage>,
}
याद रखें, एक Solana ट्रांजेक्शन को पहले से ही निर्दिष्ट (specify) करना होता है कि वह किन अकाउंट्स को एक्सेस करेगा। set() फंक्शन के लिए struct यह निर्दिष्ट करता है कि यह my_storage अकाउंट को mutably (mut) एक्सेस करेगा।
seeds = [] और bump का उपयोग उस अकाउंट का एड्रेस प्राप्त (derive) करने के लिए किया जाता है जिसे हम मॉडिफाई करेंगे। हालाँकि यूजर हमारे लिए अकाउंट पास कर रहा है, Anchor यह वैलिडेट करता है कि यूजर वही अकाउंट पास कर रहा है जो वास्तव में इस प्रोग्राम के स्वामित्व (owns) में है। यह एड्रेस को फिर से derive करके और यूजर द्वारा प्रदान किए गए एड्रेस से उसकी तुलना करके ऐसा करता है।
bump शब्द को अभी के लिए boilerplate माना जा सकता है। लेकिन जिज्ञासुओं (curious) के लिए, इसका उपयोग यह सुनिश्चित करने के लिए किया जाता है कि अकाउंट एक cryptographically वैलिड पब्लिक की (public key) नहीं है। इसी से रनटाइम को पता चलता है कि इसका उपयोग प्रोग्राम्स के लिए डेटा स्टोरेज के रूप में किया जाएगा।
भले ही हमारा Solana प्रोग्राम स्टोरेज अकाउंट का एड्रेस अपने आप derive कर सकता है, फिर भी यूजर को अकाउंट myStorage प्रदान करने की आवश्यकता होती है। यह Solana रनटाइम द्वारा उन कारणों से आवश्यक है जिन पर हम आगामी ट्यूटोरियल में चर्चा करेंगे।
set फंक्शन को लिखने का एक वैकल्पिक तरीका
यदि हम अकाउंट में कई वेरिएबल्स लिख रहे होते, तो बार-बार ctx.accounts.my_storage लिखना कुछ इस तरह से काफी अटपटा (clumsy) होता:
ctx.accounts.my_storage.x = new_x;
ctx.accounts.my_storage.y = new_y;
ctx.accounts.my_storage.z = new_z;
इसके बजाय, हम Rust से एक “mutable reference” (&mut) का उपयोग कर सकते हैं जो हमें वैल्यू में हेरफेर (manipulate) करने के लिए एक “handle” देता है। हमारे set() फंक्शन के निम्नलिखित पुनर्लेखन (rewrite) पर विचार करें:
pub fn set(ctx: Context<Set>, new_x: u64) -> Result<()> {
let my_storage = &mut ctx.accounts.my_storage;
my_storage.x = new_x;
Ok(())
}
Exercise: नए set फंक्शन के साथ टेस्ट्स को फिर से रन करें (Rerun)। यदि आप एक लोकल टेस्टनेट का उपयोग कर रहे हैं तो वैलिडेटर को रीसेट (reset) करना न भूलें।
अपने स्टोरेज अकाउंट को देखना
यदि आप टेस्ट्स के लिए एक लोकल वैलिडेटर रन कर रहे हैं, तो आप निम्नलिखित Solana कमांड लाइन इंस्ट्रक्शन के साथ अकाउंट डेटा देख सकते हैं:
# replace the address with the one in your test
solana account 9opwLZhoPdEh12DYpksnSmKQ4HTPSAmMVnRZKymMfGvn
यूनिट टेस्ट्स से कंसोल में लॉग किए गए एड्रेस के साथ इस एड्रेस को रिप्लेस (Replace) करें।
आउटपुट इस प्रकार है:

पहले 8 बाइट्स (green box) discriminator हैं। हमारे टेस्ट ने struct में नंबर 170 स्टोर किया था, इसका एक हेक्स रिप्रजेंटेशन (hex representation) aa है जिसे red box में दिखाया गया है।
बेशक, कमांड लाइन वह मैकेनिज्म नहीं है जिसका उपयोग हम फ्रंटएंड पर अकाउंट डेटा देखने के लिए करना चाहते हैं, या यदि हम चाहते हैं कि हमारा प्रोग्राम किसी अन्य प्रोग्राम के अकाउंट को देखे। इस पर अगले ट्यूटोरियल में चर्चा की जाएगी।
Rust प्रोग्राम के अंदर से अपने स्टोरेज अकाउंट को देखना
हालाँकि, Rust प्रोग्राम के अंदर अपनी खुद की स्टोरेज वैल्यू को पढ़ना सीधा और सरल (straightforward) है।
हम pub mod basic_storage में निम्नलिखित फंक्शन जोड़ते हैं:
pub fn print_x(ctx: Context<PrintX>) -> Result<()> {
let x = ctx.accounts.my_storage.x;
msg!("The value of x is {}", x);
Ok(())
}
और फिर हम PrintX के लिए निम्नलिखित struct जोड़ते हैं:
#[derive(Accounts)]
pub struct PrintX<'info> {
pub my_storage: Account<'info, MyStorage>,
}
ध्यान दें कि my_storage में #[account(mut)] मैक्रो नहीं है क्योंकि हमें इसे mutable बनाने की आवश्यकता नहीं है, हम इसे केवल पढ़ रहे हैं।
फिर हम अपने टेस्ट में निम्नलिखित लाइन जोड़ते हैं:
await program.methods.printX().accounts({myStorage: myStorage}).rpc();
यदि आप बैकग्राउंड में solana logs रन कर रहे हैं, तो आपको नंबर प्रिंट होता हुआ दिखाई देना चाहिए।
Exercise: एक increment फंक्शन लिखें जो x को पढ़ता है और x + 1 को वापस x में स्टोर करता है।
मूल रूप से 25 फरवरी, 2024 को प्रकाशित