मुख्य सामग्री पर जाएं

वेब3 ऐप्स के लिए सर्वर कंपोनेंट्स और एजेंट

एजेंट
सर्वर
ऑफ-चेन
शुरआती
ओरी पोमेरेंट्ज़
15 जुलाई 2024
10 मिनट का पठन

परिचय

अधिकांश मामलों में, एक विकेंद्रीकृत ऐप सॉफ़्टवेयर को वितरित करने के लिए एक सर्वर का उपयोग करता है, लेकिन सभी वास्तविक इंटरैक्शन क्लाइंट (आमतौर पर, वेब ब्राउज़र) और ब्लॉकचेन के बीच होती है।

वेब सर्वर, क्लाइंट और ब्लॉकचेन के बीच सामान्य इंटरैक्शन

हालांकि, कुछ ऐसे मामले हैं जहां एक एप्लिकेशन को स्वतंत्र रूप से चलने वाले सर्वर कंपोनेंट से लाभ होगा। ऐसा सर्वर ट्रांज़ैक्शन जारी करके इवेंट्स और अन्य स्रोतों, जैसे कि API, से आने वाले अनुरोधों का जवाब देने में सक्षम होगा।

सर्वर के जुड़ने के साथ इंटरैक्शन

ऐसे सर्वर द्वारा पूरे किए जा सकने वाले कई संभावित कार्य हैं।

  • गुप्त स्टेट का धारक। गेमिंग में यह अक्सर उपयोगी होता है कि गेम को ज्ञात सभी जानकारी खिलाड़ियों के लिए उपलब्ध न हो। हालांकि, ब्लॉकचेन पर कोई रहस्य नहीं होते हैं, ब्लॉकचेन में मौजूद कोई भी जानकारी किसी के लिए भी पता लगाना आसान है। इसलिए, यदि गेम स्टेट के किसी हिस्से को गुप्त रखा जाना है, तो इसे कहीं और संग्रहीत किया जाना चाहिए (और संभवतः उस स्टेट के प्रभावों को ज़ीरो-नॉलेज प्रमाण का उपयोग करके सत्यापित किया जाना चाहिए)।

  • केंद्रीकृत ओरेकल। यदि स्टेक पर्याप्त रूप से कम हैं, तो एक बाहरी सर्वर जो ऑनलाइन कुछ जानकारी पढ़ता है और फिर उसे चेन पर पोस्ट करता है, ओरेकल के रूप में उपयोग करने के लिए पर्याप्त हो सकता है।

  • एजेंट। ब्लॉकचेन पर इसे सक्रिय करने के लिए ट्रांज़ैक्शन के बिना कुछ भी नहीं होता है। एक सर्वर किसी यूज़र की ओर से आर्बिट्रेज जैसे कार्य करने के लिए कार्य कर सकता है जब अवसर स्वयं प्रस्तुत होता है।

नमूना प्रोग्राम

आप github (opens in a new tab) पर एक नमूना सर्वर देख सकते हैं। यह सर्वर हार्डहैट के Greeter के एक संशोधित संस्करण, इस कॉन्ट्रैक्ट (opens in a new tab) से आने वाले इवेंट्स को सुनता है। जब ग्रीटिंग बदल दी जाती है, तो यह उसे वापस बदल देता है।

इसे चलाने के लिए:

  1. रिपॉजिटरी को क्लोन करें।

    git clone https://github.com/qbzzt/20240715-server-component.git
    cd 20240715-server-component
    
  2. आवश्यक पैकेज इंस्टॉल करें। यदि आपके पास यह पहले से नहीं है, तो पहले नोड इंस्टॉल करें (opens in a new tab)

    npm install
    
  3. होलस्की टेस्टनेट पर ETH वाले खाते की निजी चाबी को निर्दिष्ट करने के लिए .env को संपादित करें। यदि आपके पास होलस्की पर ETH नहीं है, तो आप इस फोसेट का उपयोग (opens in a new tab) कर सकते हैं।

    PRIVATE_KEY=0x <private key goes here>
    
  4. सर्वर शुरू करें।

    npm start
    
  5. एक ब्लॉक खोजकर्ता (opens in a new tab) पर जाएं, और निजी चाबी वाले पते से भिन्न पते का उपयोग करके ग्रीटिंग को संशोधित करें। देखें कि ग्रीटिंग स्वचालित रूप से वापस संशोधित हो जाती है।

यह कैसे काम करता है?

सर्वर कंपोनेंट कैसे लिखना है, यह समझने का सबसे आसान तरीका नमूने को एक-एक लाइन करके देखना है।

src/app.ts

प्रोग्राम का अधिकांश हिस्सा src/app.ts (opens in a new tab) में निहित है।

आवश्यक ऑब्जेक्ट बनाना
import {
  createPublicClient,
  createWalletClient,
  getContract,
  http,
  Address,
} from "viem"

ये वे वीएम (opens in a new tab) एंटिटीज़ हैं जिनकी हमें आवश्यकता है, फ़ंक्शन और Address टाइप (opens in a new tab)। यह सर्वर टाइपस्क्रिप्ट (opens in a new tab) में लिखा गया है, जो जावास्क्रिप्ट का एक एक्सटेंशन है जो इसे स्ट्रॉन्गली टाइप्ड (opens in a new tab) बनाता है।

import { privateKeyToAccount } from "viem/accounts"

यह फ़ंक्शन (opens in a new tab) हमें एक निजी चाबी के अनुरूप वॉलेट जानकारी, जिसमें पता भी शामिल है, उत्पन्न करने देता है।

import { holesky } from "viem/chains"

वीएम में ब्लॉकचेन का उपयोग करने के लिए आपको इसकी परिभाषा आयात करनी होगी। इस मामले में, हम होलस्की (opens in a new tab) टेस्ट ब्लॉकचेन से कनेक्ट करना चाहते हैं।

// इस तरह हम .env में परिभाषाओं को process.env में जोड़ते हैं।
import * as dotenv from "dotenv"
dotenv.config()

इस तरह हम .env को एनवायरनमेंट में पढ़ते हैं। हमें इसकी निजी चाबी के लिए आवश्यकता है (बाद में देखें)।

किसी कॉन्ट्रैक्ट का उपयोग करने के लिए हमें उसके पते और उसके लिए की आवश्यकता होती है। हम यहां दोनों प्रदान करते हैं।

जावास्क्रिप्ट (और इसलिए टाइपस्क्रिप्ट) में आप किसी कॉन्सटेंट को एक नया मान असाइन नहीं कर सकते, लेकिन आप उसमें संग्रहीत ऑब्जेक्ट को संशोधित कर सकते हैं। as const प्रत्यय का उपयोग करके हम टाइपस्क्रिप्ट को बता रहे हैं कि सूची स्वयं कॉन्सटेंट है और इसे बदला नहीं जा सकता है।

const publicClient = createPublicClient({
  chain: holesky,
  transport: http(),
})

एक वीएम पब्लिक क्लाइंट (opens in a new tab) बनाएँ। पब्लिक क्लाइंट के पास संलग्न निजी चाबी नहीं होती है, और इसलिए वे ट्रांज़ैक्शन नहीं भेज सकते। वे view फ़ंक्शन (opens in a new tab) को कॉल कर सकते हैं, खाते की शेष राशि पढ़ सकते हैं, आदि।

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)

एनवायरनमेंट वैरिएबल process.env (opens in a new tab) में उपलब्ध हैं। हालांकि, टाइपस्क्रिप्ट स्ट्रॉन्गली टाइप्ड है। एक एनवायरनमेंट वैरिएबल कोई भी स्ट्रिंग, या खाली हो सकता है, इसलिए एक एनवायरनमेंट वैरिएबल का टाइप string | undefined होता है। हालांकि, वीएम में एक की को 0x${string} (0x के बाद एक स्ट्रिंग) के रूप में परिभाषित किया गया है। यहां हम टाइपस्क्रिप्ट को बताते हैं कि PRIVATE_KEY एनवायरनमेंट वैरिएबल उस टाइप का होगा। यदि ऐसा नहीं है, तो हमें एक रनटाइम एरर मिलेगा।

privateKeyToAccount (opens in a new tab) फ़ंक्शन तब इस निजी चाबी का उपयोग करके एक पूर्ण खाता ऑब्जेक्ट बनाता है।

const walletClient = createWalletClient({
  account,
  chain: holesky,
  transport: http(),
})

इसके बाद, हम एक वॉलेट क्लाइंट (opens in a new tab) बनाने के लिए खाता ऑब्जेक्ट का उपयोग करते हैं। इस क्लाइंट के पास एक निजी चाबी और एक पता होता है, इसलिए इसका उपयोग ट्रांज़ैक्शन भेजने के लिए किया जा सकता है।

const greeter = getContract({
  address: greeterAddress,
  abi: greeterABI,
  client: { public: publicClient, wallet: walletClient },
})

अब जब हमारे पास सभी पूर्वापेक्षाएँ हैं, तो हम अंततः एक कॉन्ट्रैक्ट इंस्टेंस (opens in a new tab) बना सकते हैं। हम ऑन-चेन कॉन्ट्रैक्ट के साथ संवाद करने के लिए इस कॉन्ट्रैक्ट इंस्टेंस का उपयोग करेंगे।

ब्लॉकचेन से पढ़ना
console.log(`Current greeting:`, await greeter.read.greet())

जो कॉन्ट्रैक्ट फ़ंक्शन केवल पढ़ने के लिए हैं (view (opens in a new tab) और pure (opens in a new tab)) वे read के अंतर्गत उपलब्ध हैं। इस मामले में, हम इसका उपयोग greet (opens in a new tab) फ़ंक्शन तक पहुँचने के लिए करते हैं, जो ग्रीटिंग लौटाता है।

जावास्क्रिप्ट सिंगल-थ्रेडेड है, इसलिए जब हम एक लंबे समय तक चलने वाली प्रक्रिया शुरू करते हैं तो हमें यह निर्दिष्ट करने की आवश्यकता होती है कि हम इसे एसिंक्रोनस रूप से करते हैं (opens in a new tab)। ब्लॉकचेन को कॉल करने के लिए, यहां तक कि केवल पढ़ने के ऑपरेशन के लिए भी, कंप्यूटर और ब्लॉकचेन नोड के बीच एक राउंड-ट्रिप की आवश्यकता होती है। यही कारण है कि हम यहां निर्दिष्ट करते हैं कि कोड को परिणाम के लिए await करने की आवश्यकता है।

यदि आप इस काम के तरीके में रुचि रखते हैं तो आप इसके बारे में यहां पढ़ सकते हैं (opens in a new tab), लेकिन व्यावहारिक रूप से आपको बस यह जानना है कि यदि आप एक ऐसा ऑपरेशन शुरू करते हैं जिसमें लंबा समय लगता है, तो आप परिणामों का await करते हैं, और ऐसा करने वाले किसी भी फ़ंक्शन को async के रूप में घोषित किया जाना चाहिए।

ट्रांज़ैक्शन जारी करना
const setGreeting = async (greeting: string): Promise<any> => {

यह वह फ़ंक्शन है जिसे आप ग्रीटिंग बदलने वाले ट्रांज़ैक्शन को जारी करने के लिए कॉल करते हैं। चूंकि यह एक लंबा ऑपरेशन है, फ़ंक्शन को async के रूप में घोषित किया गया है। आंतरिक कार्यान्वयन के कारण, किसी भी async फ़ंक्शन को एक Promise ऑब्जेक्ट लौटाना होता है। इस मामले में, Promise<any> का मतलब है कि हम यह निर्दिष्ट नहीं करते हैं कि Promise में वास्तव में क्या लौटाया जाएगा।

const txHash = await greeter.write.setGreeting([greeting])

कॉन्ट्रैक्ट इंस्टेंस के write फ़ील्ड में वे सभी फ़ंक्शन होते हैं जो ब्लॉकचेन स्टेट में लिखते हैं (वे जिन्हें ट्रांज़ैक्शन भेजने की आवश्यकता होती है), जैसे कि setGreeting (opens in a new tab)। पैरामीटर, यदि कोई हों, एक सूची के रूप में प्रदान किए जाते हैं, और फ़ंक्शन ट्रांज़ैक्शन का हैश लौटाता है।

    console.log(`Working on a fix, see https://eth-holesky.blockscout.com/tx/${txHash}`)

    return txHash
}

ट्रांज़ैक्शन के हैश की रिपोर्ट करें (इसे देखने के लिए ब्लॉक खोजकर्ता के URL के हिस्से के रूप में) और इसे लौटाएँ।

इवेंट्स का जवाब देना
greeter.watchEvent.SetGreeting({

watchEvent फ़ंक्शन (opens in a new tab) आपको यह निर्दिष्ट करने देता है कि जब कोई इवेंट उत्सर्जित होता है तो एक फ़ंक्शन चलाना है। यदि आप केवल एक प्रकार के इवेंट (इस मामले में, SetGreeting) की परवाह करते हैं, तो आप खुद को उस इवेंट प्रकार तक सीमित करने के लिए इस सिंटैक्स का उपयोग कर सकते हैं।

    onLogs: logs => {

onLogs फ़ंक्शन को तब कॉल किया जाता है जब लॉग एंट्री होती हैं। एथेरियम में "लॉग" और "इवेंट" आमतौर पर विनिमेय होते हैं।

console.log(
  `Address ${logs[0].args.sender} changed the greeting to ${logs[0].args.greeting}`
)

कई इवेंट्स हो सकते हैं, लेकिन सरलता के लिए हम केवल पहले वाले की परवाह करते हैं। logs[0].args इवेंट के तर्क हैं, इस मामले में sender और greeting

        if (logs[0].args.sender != account.address)
            setGreeting(`${account.address} insists on it being Hello!`)
    }
})

यदि प्रेषक यह सर्वर नहीं है, तो ग्रीटिंग बदलने के लिए setGreeting का उपयोग करें।

package.json

यह फ़ाइल (opens in a new tab) नोड.जेएस (opens in a new tab) कॉन्फ़िगरेशन को नियंत्रित करती है। यह लेख केवल महत्वपूर्ण परिभाषाओं की व्याख्या करता है।

{
  "main": "dist/index.js",

यह परिभाषा निर्दिष्ट करती है कि कौन सी जावास्क्रिप्ट फ़ाइल चलानी है।

  "scripts": {
    "start": "tsc && node dist/app.js",
  },

स्क्रिप्ट विभिन्न एप्लिकेशन एक्शन हैं। इस मामले में, हमारे पास केवल start है, जो सर्वर को कंपाइल करता है और फिर चलाता है। tsc कमांड typescript पैकेज का हिस्सा है और टाइपस्क्रिप्ट को जावास्क्रिप्ट में कंपाइल करता है। यदि आप इसे मैन्युअल रूप से चलाना चाहते हैं, तो यह node_modules/.bin में स्थित है। दूसरा कमांड सर्वर चलाता है।

  "type": "module",

जावास्क्रिप्ट नोड एप्लिकेशन कई प्रकार के होते हैं। module प्रकार हमें शीर्ष स्तर के कोड में await रखने की अनुमति देता है, जो तब महत्वपूर्ण होता है जब आप धीमे (और इसलिए एसिंक्रोनस) ऑपरेशन करते हैं।

  "devDependencies": {
    "@types/node": "^20.14.2",
    "typescript": "^5.4.5"
  },

ये वे पैकेज हैं जो केवल डेवलपमेंट के लिए आवश्यक हैं। यहां हमें typescript की आवश्यकता है और क्योंकि हम इसे नोड.जेएस के साथ उपयोग कर रहे हैं, हम नोड वैरिएबल और ऑब्जेक्ट, जैसे process, के लिए भी टाइप प्राप्त कर रहे हैं। ^<version> नोटेशन (opens in a new tab) का अर्थ है वह संस्करण या एक उच्च संस्करण जिसमें ब्रेकिंग परिवर्तन नहीं हैं। संस्करण संख्याओं के अर्थ के बारे में अधिक जानकारी के लिए यहां (opens in a new tab) देखें।

  "dependencies": {
    "dotenv": "^16.4.5",
    "viem": "2.14.1"
  }
}

ये वे पैकेज हैं जो रनटाइम पर, dist/app.js चलाते समय आवश्यक होते हैं।

निष्कर्ष

हमारे द्वारा यहां बनाया गया केंद्रीकृत सर्वर अपना काम करता है, जो एक यूज़र के लिए एजेंट के रूप में कार्य करना है। कोई भी और जो चाहता है कि डैप काम करता रहे और गैस खर्च करने को तैयार हो, वह अपने पते के साथ सर्वर का एक नया इंस्टेंस चला सकता है।

हालांकि, यह केवल तभी काम करता है जब केंद्रीकृत सर्वर के कार्यों को आसानी से सत्यापित किया जा सके। यदि केंद्रीकृत सर्वर के पास कोई गुप्त स्टेट जानकारी है, या कठिन गणनाएँ चलाता है, तो यह एक केंद्रीकृत इकाई है जिस पर आपको एप्लिकेशन का उपयोग करने के लिए भरोसा करने की आवश्यकता है, जो कि ठीक वही है जिससे ब्लॉकचेन बचने की कोशिश करते हैं। भविष्य के एक लेख में मैं यह दिखाने की योजना बना रहा हूं कि इस समस्या से निपटने के लिए ज़ीरो-नॉलेज प्रमाण का उपयोग कैसे करें।

मेरे और काम के लिए यहाँ देखें (opens in a new tab)

पेज का अंतिम अपडेट: 3 मार्च 2026

क्या यह ट्यूटोरियल उपयोगी था?