Ruka kwenda kwenye maudhui makuu

Vipengele vya seva na mawakala kwa ajili ya programu za web3

agent
server
offchain
Beginner
Ori Pomerantz
15 Julai 2024
8 minute read

Utangulizi

Katika hali nyingi, programu isiyo ya kati hutumia seva kusambaza programu, lakini mwingiliano wote halisi hutokea kati ya mteja (kawaida, kivinjari cha wavuti) na mnyororo wa bloku.

Mwingiliano wa kawaida kati ya seva ya wavuti, mteja, na mnyororo wa bloku

Hata hivyo, kuna baadhi ya matukio ambapo programu ingenufaika kutokana na kuwa na kipengele cha seva kinachofanya kazi kivyake. Seva kama hiyo itaweza kujibu matukio, na maombi yanayotoka kwenye vyanzo vingine, kama vile API, kwa kutoa miamala.

Mwingiliano na nyongeza ya seva

Kuna kazi kadhaa zinazowezekana ambazo seva kama hiyo inaweza kutimiza.

  • Mmiliki wa hali ya siri. Katika michezo ya kubahatisha mara nyingi ni muhimu kutokuwa na taarifa zote ambazo mchezo unajua zinazopatikana kwa wachezaji. Hata hivyo, hakuna siri kwenye mnyororo wa bloku, taarifa yoyote iliyo kwenye mnyororo wa bloku ni rahisi kwa yeyote kuigundua. Kwa hiyo, kama sehemu ya hali ya mchezo inapaswa kuwekwa siri, ni lazima ihifadhiwe mahali pengine (na ikiwezekana athari za hali hiyo zithibitishwe kwa kutumia uthibitisho wa zero-knowledge).

  • Oracle ya kati. Ikiwa hisa ni za chini vya kutosha, seva ya nje inayosoma baadhi ya taarifa mtandaoni na kisha kuichapisha kwenye chaini inaweza kuwa nzuri ya kutosha kutumia kama oracle.

  • Wakala. Hakuna kinachotokea kwenye mnyororo wa bloku bila muamala wa kuiwasha. Seva inaweza kutenda kwa niaba ya mtumiaji kutekeleza vitendo kama vile arbitrage wakati fursa inapojitokeza.

Programu ya mfano

Unaweza kuona seva ya mfano kwenye githubopens in a new tab. Seva hii inasikiliza matukio yanayotoka kwenye mkataba huuopens in a new tab, toleo lililobadilishwa la Greeter ya Hardhat. Wakati salamu inabadilishwa, inairudisha kama ilivyokuwa.

Ili kuiendesha:

  1. Kloni hifadhi.

    1git clone https://github.com/qbzzt/20240715-server-component.git
    2cd 20240715-server-component
  2. Sakinisha vifurushi vinavyohitajika. Kama huna tayari, sakinisha Node kwanzaopens in a new tab.

    1npm install
  3. Hariri .env ili kubainisha ufunguo binafsi wa akaunti iliyo na ETH kwenye testnet ya Holesky. Ikiwa huna ETH kwenye Holesky, unaweza kutumia bomba hiliopens in a new tab.

    1PRIVATE_KEY=0x <ufunguo binafsi unaenda hapa>
  4. Anzisha seva.

    1npm start
  5. Nenda kwenye kichunguzi cha blokuopens in a new tab, na ukitumia anwani tofauti na ile iliyo na ufunguo binafsi rekebisha salamu. Tazama kwamba salamu inarekebishwa kiotomatiki.

Inafanyaje kazi?

Njia rahisi zaidi ya kuelewa jinsi ya kuandika kipengele cha seva ni kupitia sampuli mstari kwa mstari.

src/app.ts

Idadi kubwa ya programu imo ndani ya src/app.tsopens in a new tab.

Kuunda vitu vinavyohitajika
1import {
2 createPublicClient,
3 createWalletClient,
4 getContract,
5 http,
6 Address,
7} from "viem"

Hizi ni huluki za Viemopens in a new tab tunazohitaji, kazi na aina ya Anwaniopens in a new tab. Seva hii imeandikwa kwa TypeScriptopens in a new tab, ambayo ni nyongeza kwa JavaScript inayoifanya iwe na aina madhubutiopens in a new tab.

1import { privateKeyToAccount } from "viem/accounts"

Kitendakazi hikiopens in a new tab kinaturuhusu kutoa maelezo ya mkoba, ikiwa ni pamoja na anwani, inayolingana na ufunguo binafsi.

1import { holesky } from "viem/chains"

Ili kutumia mnyororo wa bloku katika Viem unahitaji kuingiza ufafanuzi wake. Katika kesi hii, tunataka kuunganisha kwenye mnyororo wa bloku wa majaribio wa Holeskyopens in a new tab.

1// Hivi ndivyo tunavyoongeza ufafanuzi katika .env kwa process.env.
2import * as dotenv from "dotenv"
3dotenv.config()

Hivi ndivyo tunavyosoma .env katika mazingira. Tunaihitaji kwa ufunguo binafsi (tazama baadaye).

1const greeterAddress : Address = "0xB8f6460Dc30c44401Be26B0d6eD250873d8a50A6"
2const greeterABI = [
3 {
4 "inputs": [
5 {
6 "internalType": "string",
7 "name": "_greeting",
8 "type": "string"
9 }
10 ],
11 "stateMutability": "nonpayable",
12 "type": "constructor"
13 },
14 .
15 .
16 .
17 {
18 "inputs": [
19 {
20 "internalType": "string",
21 "name": "_greeting",
22 "type": "string"
23 }
24 ],
25 "name": "setGreeting",
26 "outputs": [],
27 "stateMutability": "nonpayable",
28 "type": "function"
29 }
30] as const
Onyesha yote

Ili kutumia mkataba tunahitaji anwani yake na yake. Tunatoa zote mbili hapa.

Katika JavaScript (na kwa hiyo TypeScript) huwezi kugawa thamani mpya kwa kistari, lakini unaweza kurekebisha kitu kilichohifadhiwa ndani yake. Kwa kutumia kiambishi tamati as const tunaiambia TypeScript kwamba orodha yenyewe ni ya kudumu na haiwezi kubadilishwa.

1const publicClient = createPublicClient({
2 chain: holesky,
3 transport: http(),
4})

Unda mteja wa ummaopens in a new tab wa Viem. Wateja wa umma hawana ufunguo binafsi ulioambatanishwa, na kwa hivyo hawawezi kutuma miamala. Wanaweza kuita kazi za viewopens in a new tab, kusoma salio za akaunti, n.k.

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

Vigezo vya mazingira vinapatikana katika process.envopens in a new tab. Hata hivyo, TypeScript ina aina madhubuti. Kigezo cha mazingira kinaweza kuwa mfuatano wowote, au tupu, kwa hivyo aina ya kigezo cha mazingira ni string | undefined. Hata hivyo, ufunguo umefafanuliwa katika Viem kama 0x${string} (0x ikifuatiwa na mfuatano). Hapa tunaiambia TypeScript kwamba kigezo cha mazingira cha PRIVATE_KEY kitakuwa cha aina hiyo. Ikiwa sivyo, tutapata hitilafu ya muda wa utekelezaji.

Kitendakazi cha privateKeyToAccountopens in a new tab kisha hutumia ufunguo huu binafsi kuunda kitu kamili cha akaunti.

1const walletClient = createWalletClient({
2 account,
3 chain: holesky,
4 transport: http(),
5})

Ifuatayo, tunatumia kitu cha akaunti kuunda mteja wa mkobaopens in a new tab. Mteja huyu ana ufunguo binafsi na anwani, kwa hivyo inaweza kutumika kutuma miamala.

1const greeter = getContract({
2 address: greeterAddress,
3 abi: greeterABI,
4 client: { public: publicClient, wallet: walletClient },
5})

Sasa kwa kuwa tuna mahitaji yote, hatimaye tunaweza kuunda mfano wa mkatabaopens in a new tab. Tutatumia mfano huu wa mkataba kuwasiliana na mkataba wa onchain.

Kusoma kutoka kwenye mnyororo wa bloku
1console.log(`Current greeting:`, await greeter.read.greet())

Kazi za mkataba ambazo zinasomwa tu (viewopens in a new tab na pureopens in a new tab) zinapatikana chini ya read. Katika kisa hiki, tunaitumia kufikia kitendakazi cha greetopens in a new tab, ambacho hurudisha salamu.

JavaScript ina uzi mmoja, kwa hivyo tunapoanzisha mchakato mrefu tunahitaji kubainisha kuwa tunafanya hivyo kwa njia isiyosawazishwaopens in a new tab. Kuita mnyororo wa bloku, hata kwa operesheni ya kusoma tu, inahitaji safari ya kwenda na kurudi kati ya kompyuta na nodi ya mnyororo wa bloku. Hiyo ndiyo sababu tunabainisha hapa msimbo unahitaji kusubiri matokeo.

Ikiwa una nia ya jinsi hii inavyofanya kazi unaweza kusoma kuihusu hapaopens in a new tab, lakini kwa vitendo unachohitaji kujua ni kwamba unasubiri matokeo ukianza operesheni inayochukua muda mrefu, na kwamba kitendakazi chochote kinachofanya hivi lazima kitangazwe kama async.

Kutoa miamala
1const setGreeting = async (greeting: string): Promise<any> => {

Hiki ndicho kitendakazi unachoita ili kutoa muamala unaobadilisha salamu. Kwa kuwa hii ni operesheni ndefu, kitendakazi kinatangazwa kama async. Kwa sababu ya utekelezaji wa ndani, kitendakazi chochote cha async kinahitaji kurudisha kitu cha Promise. Katika kesi hii, Promise<any> inamaanisha kuwa hatubainishi nini hasa kitarudishwa katika Promise.

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

Sehemu ya write ya mfano wa mkataba ina kazi zote zinazoandika kwenye hali ya mnyororo wa bloku (zile zinazohitaji kutuma muamala), kama vile setGreetingopens in a new tab. Vigezo, ikiwa vipo, vinatolewa kama orodha, na kitendakazi hurudisha hashi ya muamala.

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

Ripoti hashi ya muamala (kama sehemu ya URL kwa kichunguzi cha bloku ili kuitazama) na uirudishe.

Kujibu matukio
1greeter.watchEvent.SetGreeting({

Kitendakazi cha watchEventopens in a new tab hukuruhusu kubainisha kuwa kitendakazi kitaendeshwa tukio linapotolewa. Ikiwa unajali tu aina moja ya tukio (katika kesi hii, SetGreeting), unaweza kutumia sintaksia hii kujizuia kwa aina hiyo ya tukio.

1 onLogs: logs => {

Kitendakazi cha onLogs huitwa kunapokuwa na maingizo ya kumbukumbu. Katika Ethereum "log" na "tukio" kawaida hubadilishana.

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

Kunaweza kuwa na matukio mengi, lakini kwa urahisi tunajali tu lile la kwanza. logs[0].args ni hoja za tukio, katika kesi hii sender na greeting.

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

Ikiwa mtumaji sio seva hii, tumia setGreeting kubadilisha salamu.

package.json

Faili hiiopens in a new tab inadhibiti usanidi wa Node.jsopens in a new tab. Makala hii inaelezea tu ufafanuzi muhimu.

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

Ufafanuzi huu unabainisha faili gani ya JavaScript itaendeshwa.

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

Hati ni vitendo mbalimbali vya programu. Katika kesi hii, pekee tuliyo nayo ni start, ambayo inakusanya na kisha kuendesha seva. Amri ya tsc ni sehemu ya kifurushi cha typescript na inakusanya TypeScript kuwa JavaScript. Ikiwa unataka kuiendesha mwenyewe, iko katika node_modules/.bin. Amri ya pili inaendesha seva.

1 "type": "module",

Kuna aina nyingi za programu za Node za JavaScript. Aina ya module inaturuhusu kuwa na await katika msimbo wa kiwango cha juu, ambayo ni muhimu unapofanya operesheni za polepole (na kwa hivyo zisizosawazishwa).

1 "devDependencies": {
2 "@types/node": "^20.14.2",
3 "typescript": "^5.4.5"
4 },

Hivi ni vifurushi vinavyohitajika tu kwa ajili ya maendeleo. Hapa tunahitaji typescript na kwa sababu tunaitumia na Node.js, tunapata pia aina za vigezo na vitu vya Node, kama vile process. Nukuu ya ^<toleo>opens in a new tab inamaanisha toleo hilo au toleo la juu zaidi ambalo halina mabadiliko ya kuvunja. Tazama hapaopens in a new tab kwa habari zaidi kuhusu maana ya nambari za toleo.

1 "dependencies": {
2 "dotenv": "^16.4.5",
3 "viem": "2.14.1"
4 }
5}

Hivi ni vifurushi vinavyohitajika wakati wa utekelezaji, wakati wa kuendesha dist/app.js.

Hitimisho

Seva ya kati tuliyoiunda hapa inafanya kazi yake, ambayo ni kutenda kama wakala kwa mtumiaji. Mtu mwingine yeyote anayetaka mfumo mtawanyo wa kimamlaka uendelee kufanya kazi na yuko tayari kutumia gesi anaweza kuendesha mfano mpya wa seva na anwani yake mwenyewe.

Hata hivyo, hii inafanya kazi tu wakati vitendo vya seva ya kati vinaweza kuthibitishwa kwa urahisi. Ikiwa seva ya kati ina habari yoyote ya hali ya siri, au inafanya hesabu ngumu, ni chombo cha kati ambacho unahitaji kuamini ili kutumia programu, ambacho ndicho hasa minyororo ya bloku inajaribu kuepuka. Katika makala ya baadaye napanga kuonyesha jinsi ya kutumia uthibitisho wa zero-knowledge ili kutatua tatizo hili.

Tazama hapa kwa kazi zangu zaidiopens in a new tab.

Ukurasa ulihaririwa mwisho: 9 Septemba 2025

Umesaidika na mafunzo haya?