Solana के Anchor फ्रेमवर्क में, close असल में init (initializing an account in Anchor) का विपरीत है — यह lamport बैलेंस को घटाकर शून्य कर देता है, lamports को एक टारगेट एड्रेस पर भेज देता है, और अकाउंट के ओनर को बदलकर system program कर देता है।
Rust में close इंस्ट्रक्शन के इस्तेमाल का एक उदाहरण यहाँ दिया गया है:
use anchor_lang::prelude::*;
use std::mem::size_of;
declare_id!("8gaSDFr5cVy2BkLrWfSX9MCtPX9N4gmXDvTVm7RS6DYK");
#[program]
pub mod close_program {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
Ok(())
}
pub fn delete(ctx: Context<Delete>) -> Result<()> {
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = signer, space = size_of::<ThePda>() + 8, seeds = [], bump)]
pub the_pda: Account<'info, ThePda>,
#[account(mut)]
pub signer: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Delete<'info> {
#[account(mut, close = signer, )]
pub the_pda: Account<'info, ThePda>,
#[account(mut)]
pub signer: Signer<'info>,
}
#[account]
pub struct ThePda {
pub x: u32,
}
Solana अकाउंट्स को close करने पर रेंट (rent) वापस करता है
close = signer मैक्रो यह निर्दिष्ट करता है कि ट्रांजैक्शन में signer को वह रेंट वापस मिलेगा जो स्टोरेज के भुगतान के लिए अलग रखा गया था (हालाँकि निश्चित रूप से कोई दूसरा एड्रेस भी निर्दिष्ट किया जा सकता है)। यह काफी हद तक Ethereum (Dencun अपग्रेड से पहले) में selfdestruct के काम करने के तरीके के समान है, जो स्पेस क्लियर करने के लिए यूज़र्स को रिफंड देता था। किसी अकाउंट को close करने से जो SOL प्राप्त किया जा सकता है, उसकी मात्रा इस बात पर निर्भर करती है कि वह अकाउंट कितना बड़ा था।
यहाँ initialize और उसके बाद delete को कॉल करने के लिए Typescript दी गई है:
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { CloseProgram } from "../target/types/close_program";
import { assert } from "chai";
describe("close_program", () => {
// Configure the client to use the local cluster.
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.CloseProgram as Program<CloseProgram>;
it("Is initialized!", async () => {
let [thePda, _bump] = anchor.web3.PublicKey.findProgramAddressSync([], program.programId);
await program.methods.initialize().accounts({thePda: thePda}).rpc();
await program.methods.delete().accounts({thePda: thePda}).rpc();
let account = await program.account.thePda.fetchNullable(thePda);
console.log(account)
});
});
close = signer इंस्ट्रक्शन रेंट lamports को signer को भेजने के लिए कहता है, लेकिन आप अपनी पसंद का कोई भी एड्रेस निर्दिष्ट कर सकते हैं।
उपरोक्त संरचना किसी को भी अकाउंट close करने की अनुमति देती है, एक वास्तविक एप्लीकेशन में आप संभवतः किसी प्रकार का एक्सेस कंट्रोल (access control) जोड़ना चाहेंगे!
Accounts को close होने के बाद initialize किया जा सकता है
यदि आप किसी अकाउंट को close करने के बाद initialize को कॉल करते हैं, तो यह फिर से initialize हो जाएगा। बेशक, जो रेंट पहले रिडीम (redeem) किया गया था, उसका दोबारा भुगतान करना होगा।
Exercise: यह देखने के लिए कि यह पास होता है, यूनिट टेस्ट में initialize के लिए एक और कॉल जोड़ें। ध्यान दें कि टेस्ट के अंत में अकाउंट अब null नहीं रहता है।
close बैकग्राउंड (under the hood) में क्या करता है?
यदि हम Anchor में source code for the close command को देखें, तो हम इसे वे ऑपरेशन करते हुए देख सकते हैं जिनका हमने ऊपर वर्णन किया है:

कई Anchorlang उदाहरण पुराने (outdated) हैं
Anchor के वर्ज़न 0.25 में, close सीक्वेंस अलग था।
वर्तमान इम्प्लीमेंटेशन के समान, यह सबसे पहले सभी lamports को डेस्टिनेशन एड्रेस पर भेजता था।
हालाँकि, डेटा को मिटाने और उसे system program में ट्रांसफर करने के बजाय, close एक विशेष 8 बाइट सीक्वेंस लिखता था जिसे CLOSE_ACCOUNT_DISCRIMINATOR कहा जाता है। (original code):
/// The discriminator anchor uses to mark an account as closed.
pub const CLOSED_ACCOUNT_DISCRIMINATOR: [u8; 8] = [255, 255, 255, 255, 255, 255, 255, 255];
अंततः, रनटाइम अकाउंट को मिटा देता था क्योंकि इसमें शून्य lamports होते थे।
Anchor में account discriminator क्या है?
जब Anchor किसी अकाउंट को initialize करता है, तो यह discriminator की गणना करता है और उसे अकाउंट के पहले 8 बाइट्स में स्टोर करता है। account discriminator, struct के Rust आइडेंटिफायर के SHA256 के पहले 8 बाइट्स होते हैं।
जब कोई यूज़र प्रोग्राम को pub the_pda: Account<'info, ThePda> के माध्यम से अकाउंट लोड करने के लिए कहता है, तो प्रोग्राम ThePda आइडेंटिफायर के SHA256 के पहले 8 बाइट्स की गणना करेगा। फिर यह ThePda डेटा को लोड करेगा और वहां स्टोर किए गए discriminator की तुलना उस discriminator से करेगा जिसकी उसने गणना की है। यदि वे मेल नहीं खाते हैं, तो Anchor अकाउंट को deserialize नहीं करेगा।
यहाँ इसका उद्देश्य किसी हमलावर (attacker) को एक दुर्भावनापूर्ण (malicious) अकाउंट बनाने से रोकना है जो “गलत struct के माध्यम से” पार्स किए जाने पर अप्रत्याशित परिणामों में deserialize हो जाएगा।
Anchor पहले account discriminator को [255, ..., 255] पर क्यों सेट करता था
account discriminator को सभी वन्स (ones) पर सेट करने से, Anchor हमेशा अकाउंट को deserialize करने से मना कर देगा क्योंकि यह किसी भी account discriminators से मेल नहीं खाएगा।
account discriminator को सभी वन्स (ones) के रूप में लिखने का कारण किसी हमलावर को रनटाइम द्वारा मिटाए जाने से पहले अकाउंट में सीधे SOL भेजने से रोकना था। इस परिस्थिति में, प्रोग्राम “सोचता” था कि उसने प्रोग्राम को close कर दिया है, लेकिन हमलावर ने इसे “पुनर्जीवित (revived)” कर दिया। यदि पुराना account discriminator अभी भी वहीं है, तो वह डेटा जिसे डिलीट मान लिया गया था, वापस पढ़ (read back) लिया जाएगा।
account discriminator को [255, …, 255] पर सेट करने की अब आवश्यकता क्यों नहीं है
इसके बजाय ओनरशिप को system program में बदलने से, अकाउंट को पुनर्जीवित करने पर ऐसा नहीं होगा कि प्रोग्राम अचानक से फिर से अकाउंट का “मालिक (owning)” बन जाए; system program पुनर्जीवित अकाउंट का मालिक होता है और हमलावर का SOL बर्बाद हो जाता है।
ओनरशिप को वापस प्रोग्राम में बदलने के लिए, इसे फिर से स्पष्ट रूप से initialize करने की आवश्यकता होती है, इसे रनटाइम को मिटाने से रोकने के लिए SOL भेजने जैसे साइड-चैनल के माध्यम से पुनर्जीवित नहीं किया जा सकता है।
CLI के माध्यम से किसी प्रोग्राम को close करना
इसके स्वामित्व वाले (owned) अकाउंट के विपरीत, किसी प्रोग्राम को close करने के लिए, हम कमांड-लाइन का उपयोग कर सकते हैं:
solana program close <address> --bypass warning
चेतावनी (warning) यह है कि एक बार प्रोग्राम close हो जाने के बाद, उसी एड्रेस वाला प्रोग्राम फिर से नहीं बनाया जा सकता है। यहाँ एक अकाउंट को close करने का उदाहरण देने वाले शेल कमांड्स का एक सीक्वेंस दिया गया है:

ऊपर दिए गए स्क्रीनशॉट में कमांड्स का सीक्वेंस यहाँ दिया गया है:
- सबसे पहले हम प्रोग्राम को deploy करते हैं
- हम
--bypass-warningफ्लैग के बिना प्रोग्राम को close करते हैं और टूल हमें चेतावनी देता है कि प्रोग्राम को फिर से deploy नहीं किया जा सकता है - हम फ्लैग के साथ प्रोग्राम को close करते हैं, प्रोग्राम close हो जाता है, और हमें अकाउंट close करने के लिए रिफंड के रूप में 2.918 SOL प्राप्त होते हैं
- हम फिर से deploy करने का प्रयास करते हैं और विफल हो जाते हैं क्योंकि close किए गए प्रोग्राम को फिर से deploy (redeploy) नहीं किया जा सकता है
RareSkills के साथ और जानें
Solana डेवलपमेंट सीखना जारी रखने के लिए, कृपया हमारा Solana course देखें। अन्य ब्लॉकचेन विषयों के लिए, हमारा blockchain bootcamp देखें।
मूल रूप से 12 मार्च, 2024 को प्रकाशित