Telegram Open Network (TON) kontratu adimendun bat nola idatzi eta argitaratzeari buruz

TON-en kontratu adimendun bat nola idatzi eta argitaratzeari buruz

Zeri buruz da artikulu hau?

Artikuluan Telegram blockchain lehiaketan lehenengo (bietatik) parte hartu nuen, saririk hartu ez nuen eta nire esperientzia artikulu batean grabatzea erabaki nuen, ahanzturan murgil ez zedin eta, beharbada, laguntzeari buruz hitz egingo dut. norbait.

Kode abstraktua idatzi nahi ez nuenez, funtzionatzen duen zerbait egin nahi nuenez, artikulurako kontratu adimenduna idatzi nuen berehalako loteria baterako eta TONetik zuzenean kontratu adimendunen datuak erakusten dituen webgune bat, tarteko biltegiratzea erabili gabe.

Artikulua erabilgarria izango da TON-en lehen kontratu adimenduna egin nahi dutenentzat, baina nondik hasi ez dakitenentzat.

Loteria adibide gisa hartuta, ingurunea instalatzetik kontratu adimendun bat argitaratzera pasako naiz, harekin elkarreragin eta datuak jaso eta argitaratzeko webgune bat idaztera.

Lehiaketan parte hartzeari buruz

Joan den urrian, Telegramek hizkuntza berriekin blockchain lehiaketa bat iragarri zuen Fift ΠΈ FunC. Beharrezkoa zen proposatutako bost kontratu adimendunetatik edozein idazteko aukeratzea. Pentsatu nuen polita izango zela zerbait ezberdina egitea, hizkuntza bat ikastea eta zerbait egitea, nahiz eta etorkizunean beste ezer idatzi behar ez izan. Gainera, gaia etengabe ezpainetan dago.

Esan beharra dago ez nuela esperientziarik kontratu adimentsuak garatzen.

Ahal nuen arte amaierara arte parte hartzeko asmoa nuen eta gero berrikuspen-artikulu bat idazteko, baina lehenengoan huts egin nuen berehala. I zorro bat idatzi zuen sinadura anitzeko aktibatuta FunC eta orokorrean funtzionatu zuen. Oinarritzat hartu nuen Solidity-ri buruzko kontratu adimenduna.

Garai hartan, uste nuen hori nahikoa zela, behintzat, sari-posturen bat hartzeko. Ondorioz, 40 parte-hartzaileetatik 60 inguru izan ziren saridun eta ni ez nintzen haien artean. Orokorrean, ez dago ezer gaizki, baina gauza batek molestatzen ninduen. Emaitzak iragartzeko momentuan, nire kontratuaren azterketaren berrikuspena egin gabe zegoen, txatean parte hartzaileei galdetu nien ea ez zegoen beste inor, ez zegoen.

Itxuraz nire mezuei erreparatuz, bi egun geroago epaileek iruzkin bat argitaratu zuten eta oraindik ez dut ulertzen ustekabean nire kontratu adimenduna galdu ote zuten epaiketa garaian edo, besterik gabe, uste zuten hain txarra zela, ez zuela komentariorik behar. Galdera bat egin nuen orrialdean, baina ez nuen erantzunik jaso. Nork epaitu zuen sekretua ez den arren, mezu pertsonalak idaztea alferrikakotzat jo nuen.

Ulermenean denbora asko eman zen, beraz, artikulu bat idaztea erabaki zen. Oraindik informazio asko ez dagoenez, artikulu honek denbora aurrezten lagunduko die interesa duten guztiei.

Kontratu adimendunen kontzeptua TONen

Ezer idatzi aurretik, gauza honi zein aldetatik heldu behar zaion asmatu behar duzu. Hori dela eta, orain esango dizut zer atalez osatuta dagoen sistema. Zehatzago esanda, zein zati jakin behar dituzun gutxienez lan-kontraturen bat idazteko.

Kontratu adimendun bat idaztera eta harekin lan egitean zentratuko gara TON Virtual Machine (TVM), Fift ΠΈ FunC, beraz, artikulua ohiko programa baten garapenaren deskribapena bezalakoa da. Ez gara hemen plataformak berak nola funtzionatzen duen azalduko.

Oro har, nola funtzionatzen duen TVM eta hizkuntza Fift dokumentazio ofizial ona dago. Lehiaketan parte hartzen nuen bitartean eta orain egungo kontratua idazten ari nintzenean, askotan jotzen nuen harengana.

Kontratu adimendunak idazten diren hizkuntza nagusia da FunC. Momentuz ez dago horri buruzko dokumentaziorik, beraz, zerbait idazteko, biltegi ofizialeko kontratu adimendunen adibideak aztertu behar dituzu eta hizkuntzaren beraren ezarpena bertan, gainera, azken bietako kontratu adimendunen adibideak ere ikus ditzakezu. lehiaketak. Artikuluaren amaieran dauden estekak.

Demagun dagoeneko kontratu adimendun bat idatzi dugula FunC, horren ondoren kodea Fift muntatzailean konpilatzen dugu.

Konpilatutako kontratu adimenduna argitaratzeko dago. Horretarako, funtzio bat idatzi behar duzu Fift, kontratu adimendunaren kodea eta beste parametro batzuk sarrera gisa hartuko dituena, eta irteera luzapena duen fitxategi bat izango da .boc (Β«zelula poltsaΒ» esan nahi du), eta, nola idazten dugunaren arabera, gako eta helbide pribatu bat, smart contract kodean oinarrituta sortzen dena. Gramoak bidal ditzakezu oraindik argitaratu ez den kontratu adimendun baten helbidera.

Jasotako TON-en kontratu adimenduna argitaratzeko .boc fitxategia blockchain-era bidali beharko da bezero arin bat erabiliz (behean gehiago). Baina argitaratu aurretik, gramoak transferitu behar dituzu sortutako helbidera, bestela kontratu adimenduna ez da argitaratuko. Argitaratu ondoren, kontratu adimendunarekin elkarreragin dezakezu kanpotik mezuak bidaliz (adibidez, bezero argi bat erabiliz) edo barrutik (adibidez, kontratu adimendun batek beste bati mezu bat bidaltzen dio TON barruan).

Kodea nola argitaratzen den ulertzen dugunean, errazagoa da. Gutxi gorabehera badakigu zer idatzi nahi dugun eta gure programa nola funtzionatuko duen. Eta idazten ari zaren bitartean, hori lehendik dauden kontratu adimendunetan nola inplementatzen den bilatzen dugu edo inplementazio kodea aztertzen dugu Fift ΠΈ FunC biltegi ofizialean, edo begiratu dokumentazio ofizialean.

Askotan, lehiaketako parte-hartzaile guztiak eta Telegrameko langileak biltzen ziren Telegrameko txatean gako-hitzak bilatzen nituen, eta gertatu zen lehiaketan denak bertan bildu zirela eta Fift eta FunC eztabaidatzen hasi zirela. Artikuluaren amaieran dagoen esteka.

Teoriatik praktikara pasatzeko garaia da.

TONekin lan egiteko ingurunea prestatzea

MacOS-en artikuluan deskribatuko den guztia egin nuen eta Docker-en Ubuntu 18.04 LTS garbian egiaztatu nuen.

Egin behar duzun lehenengo gauza deskargatu eta instalatu da lite-client horrekin eskaerak bidal ditzakezu TONera.

Webgune ofizialeko argibideek instalazio-prozesua oso zehatz eta argi deskribatzen dute eta xehetasun batzuk baztertzen dituzte. Hemen argibideak jarraitzen ditugu, bidean falta diren mendekotasunak instalatuz. Nik ez nuen proiektu bakoitza konpilatu eta Ubunturen biltegi ofizialetik instalatu (erabili nuen MacOS-en brew).

apt -y install git 
apt -y install wget 
apt -y install cmake 
apt -y install g++ 
apt -y install zlib1g-dev 
apt -y install libssl-dev 

Mendekotasun guztiak instalatu ondoren instalatu dezakezu lite-client, Fift, FunC.

Lehenik eta behin, TON biltegia klonatuko dugu bere menpekotasunekin batera. Erosotasunerako, dena karpeta batean egingo dugu ~/TON.

cd ~/TON
git clone https://github.com/ton-blockchain/ton.git
cd ./ton
git submodule update --init --recursive

Biltegiak inplementazioak ere gordetzen ditu Fift ΠΈ FunC.

Orain prest gaude proiektua muntatzeko. Biltegiko kodea karpeta batean klonatzen da ~/TON/ton. Urtean ~/TON karpeta bat sortu build eta proiektua bertan jaso.

mkdir ~/TON/build 
cd ~/TON/build
cmake ../ton

Kontratu adimendun bat idatziko dugunez, ez dugu bakarrik behar lite-clientBaina Fift с FunC, beraz, konpila dezagun dena. Ez da prozesu azkarra, beraz, zain gaude.

cmake --build . --target lite-client
cmake --build . --target fift
cmake --build . --target func

Ondoren, deskargatu nodoari buruzko datuak dituen konfigurazio fitxategia lite-client konektatuko da.

wget https://test.ton.org/ton-lite-client-test1.config.json

TON-i lehen eskaerak egitea

Orain abiarazi dezagun lite-client.

cd ~/TON/build
./lite-client/lite-client -C ton-lite-client-test1.config.json

Eraikuntza arrakastatsua izan bada, abiarazi ondoren argi bezeroak nodoarekin duen konexioaren erregistroa ikusiko duzu.

[ 1][t 2][1582054822.963129282][lite-client.h:201][!testnode]   conn ready
[ 2][t 2][1582054823.085654020][lite-client.cpp:277][!testnode] server version is 1.1, capabilities 7
[ 3][t 2][1582054823.085725069][lite-client.cpp:286][!testnode] server time is 1582054823 (delta 0)
...

Komandoa exekutatu dezakezu help eta ikusi zer komando dauden eskuragarri.

help

Zerrenda ditzagun artikulu honetan erabiliko ditugun komandoak.

list of available commands:
last    Get last block and state info from server
sendfile <filename> Load a serialized message from <filename> and send it to server
getaccount <addr> [<block-id-ext>]  Loads the most recent state of specified account; <addr> is in [<workchain>:]<hex-or-base64-addr> format
runmethod <addr> [<block-id-ext>] <method-id> <params>...   Runs GET method <method-id> of account <addr> with specified parameters

last ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅Ρ‚ послСдний созданный Π±Π»ΠΎΠΊ с сСрвСра. 

sendfile <filename> отправляСт Π² TON Ρ„Π°ΠΉΠ» с сообщСниСм, ΠΈΠΌΠ΅Π½Π½ΠΎ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ этой ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ публикуСтся смарт-ΠΊΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚ ΠΈ запрсосы ΠΊ Π½Π΅ΠΌΡƒ. 

getaccount <addr> Π·Π°Π³Ρ€ΡƒΠΆΠ°Π΅Ρ‚ Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π΅ состояниС смарт-ΠΊΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚Π° с ΡƒΠΊΠ°Π·Π°Π½Π½Ρ‹ΠΌ адрСсом. 

runmethod <addr> [<block-id-ext>] <method-id> <params>  запускаСт get-ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ смартконтракта. 

Orain prest gaude kontratua bera idazteko.

Inplementazioa

Idea

Goian idatzi dudan bezala, idazten ari garen kontratu adimenduna loteria bat da.

Gainera, hau ez da txartela erosi eta ordubete, egun edo hilabete itxaron behar duzun loteria bat, erabiltzaileak kontratuaren helbidera transferitzen duen berehalakoa baizik. N gramo, eta berehala berreskuratzen du 2 * N gramo edo galtzen. Irabazteko probabilitatea %40 inguru egingo dugu. Ordaintzeko gramo nahikorik ez badago, transakzioa karga gisa hartuko dugu.

Gainera, garrantzitsua da apustuak denbora errealean eta modu erosoan ikustea, erabiltzaileak irabazi duen ala galdu duen berehala uler dezan. Hori dela eta, TONetik zuzenean apustuak eta emaitzak erakutsiko dituen webgune bat egin behar duzu.

Kontratu adimenduna idaztea

Erosotasunerako, FunC-ren kodea nabarmendu dut; plugina Visual Studio Code bilaketan aurkitu eta instalatu daiteke; bat-batean zerbait gehitu nahi baduzu, plugina publikoki eskuragarri jarri dut. Gainera, norbaitek Fift-ekin lan egiteko plugin bat egin zuen, instalatu eta VSC-en aurki dezakezu.

Sor dezagun berehala biltegi bat non tarteko emaitzak konprometituko ditugun.

Gure bizitza errazteko, smart contract bat idatziko dugu eta lokalean probatuko dugu prest egon arte. Horren ostean bakarrik argitaratuko dugu TONen.

Kontratu adimendunak kanpoko bi metodo ditu atzitu daitezkeen. Lehenik eta behin, recv_external() funtzio hori kontratuaren eskaera kanpotik datorrenean exekutatzen da, hau da, ez TONetik, adibidez, guk geuk mezu bat sortu eta lite-client bidez bidaltzen dugunean. Bigarrena, recv_internal() hau da, TON beraren baitan, edozein kontratu gureari egiten dio erreferentzia. Bi kasuetan, parametroak pasa ditzakezu funtzioari.

Has gaitezen argitaratzen bada funtzionatuko duen adibide sinple batekin, baina ez dago karga funtzionalik.

() recv_internal(slice in_msg) impure {
    ;; TODO: implementation 
}

() recv_external(slice in_msg) impure {
    ;; TODO: implementation  
}

Hemen azaldu behar dugu zer den slice. TON Blockchain-en gordetako datu guztiak bilduma bat dira TVM cell edo, besterik gabe, cell, halako gelaxka batean 1023 bit datu eta gehienez 4 esteka beste gelaxketarako gorde ditzakezu.

TVM cell slice edo slice hau lehendik dagoenaren parte da cell analizatzeko erabiltzen da, gero argi geratuko da. Guretzat gauza nagusia transferitzea da slice eta mezu motaren arabera, datuak prozesatu recv_external() edo recv_internal().

impure β€” Funtzioak kontratu adimendunen datuak aldatzen dituela adierazten duen gako-hitz bat.

Gorde dezagun kontratuaren kodea lottery-code.fc eta konpilatu.

~/TON/build/crypto/func -APSR -o lottery-compiled.fif ~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fc 

Banderen esanahia ikus daiteke komandoa erabiliz

~/TON/build/crypto/func -help

Fift muntatzaileen kodea bildu dugu lottery-compiled.fif:

// lottery-compiled.fif

"Asm.fif" include
// automatically generated from `/Users/rajymbekkapisev/TON/ton/crypto/smartcont/stdlib.fc` `./lottery-code.fc` 
PROGRAM{
  DECLPROC recv_internal
  DECLPROC recv_external
  recv_internal PROC:<{
    //  in_msg
    DROP    // 
  }>
  recv_external PROC:<{
    //  in_msg
    DROP    // 
  }>
}END>c

Lokalean jarri daiteke martxan, horretarako ingurumena prestatuko dugu.

Kontuan izan lehen lerroa lotzen dela Asm.fif, Fift-en idatzitako kodea da Fift-en muntatzailearentzat.

Kontratu adimenduna lokalean exekutatu eta probatu nahi dugunez, fitxategi bat sortuko dugu lottery-test-suite.fif eta kopiatu konpilatutako kodea bertan, azken lerroa ordezkatuz, kontratu adimendunaren kodea konstante batean idazten duena codegero makina birtualera transferitzeko:

"TonUtil.fif" include
"Asm.fif" include

PROGRAM{
  DECLPROC recv_internal
  DECLPROC recv_external
  recv_internal PROC:<{
    //  in_msg
    DROP    // 
  }>
  recv_external PROC:<{
    //  in_msg
    DROP    // 
  }>
}END>s constant code

Orain arte argia dirudi, orain gehi diezaiogun fitxategi berean TVM abiarazteko erabiliko dugun kodea.

0 tuple 0x076ef1ea , // magic
0 , 0 , // actions msg_sents
1570998536 , // unix_time
1 , 1 , 3 , // block_lt, trans_lt, rand_seed
0 tuple 100000000000000 , dictnew , , // remaining balance
0 , dictnew , // contract_address, global_config
1 tuple // wrap to another tuple
constant c7

0 constant recv_internal // to run recv_internal() 
-1 constant recv_external // to invoke recv_external()

Π’ c7 testuingurua erregistratzen dugu, hau da, TVM (edo sarearen egoera) abiaraziko den datuekin. Lehiaketan ere, garatzaileetako batek nola sortu erakutsi zuen c7 eta kopiatu nuen. Artikulu honetan agian aldatu beharko dugu rand_seed ausazko zenbaki baten sorrera horren araberakoa baita eta aldatzen ez bada, zenbaki bera itzuliko da bakoitzean.

recv_internal ΠΈ recv_external 0 eta -1 balioak dituzten konstanteak kontratu adimendunean dagozkien funtzioak deitzeaz arduratuko dira.

Orain prest gaude gure kontratu adimendun hutsaren lehen proba sortzeko. Argitasuna lortzeko, oraingoz proba guztiak fitxategi berean gehituko ditugu lottery-test-suite.fif.

Sor dezagun aldagai bat storage eta idatzi bertan huts bat cell, hau izango da kontratu adimendunaren biltegia.

message Hau da kanpotik kontaktu adimendunari transmitituko diogun mezua. Oraingoz ere hutsik egingo dugu.

variable storage 
<b b> storage ! 

variable message 
<b b> message ! 

Konstanteak eta aldagaiak prestatu ondoren, TVM abiarazten dugu komandoa erabiliz runvmctx eta sortutako parametroak sarrerara pasatu.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

Azkenean lortuko dugu horrela tarteko kodea Fift.

Orain ondoriozko kodea exekutatu dezakegu.

export FIFTPATH=~/TON/ton/crypto/fift/lib // выполняСм ΠΎΠ΄ΠΈΠ½ Ρ€Π°Π· для удобства 
~/TON/build/crypto/fift -s lottery-test-suite.fif 

Programak errorerik gabe exekutatu beharko luke eta irteeran exekuzio erregistroa ikusiko dugu:

execute SETCP 0
execute DICTPUSHCONST 19 (xC_,1)
execute DICTIGETJMPZ
execute DROP
execute implicit RET
[ 3][t 0][1582281699.325381279][vm.cpp:479]     steps: 5 gas: used=304, max=9223372036854775807, limit=9223372036854775807, credit=0

Bikaina, kontratu adimendunaren lehen lan-bertsioa idatzi dugu.

Orain funtzionaltasuna gehitu behar dugu. Lehenik eta behin kanpotik datozen mezuak jorratu ditzagun recv_external()

Garatzaileak berak aukeratzen du kontratuak onar dezakeen mezu formatua.

Baina normalean

  • lehenik eta behin, gure kontratua kanpotik babestu nahi dugu eta kontratuaren jabeak bakarrik bidali diezazkion kanpoko mezuak.
  • bigarrenik, baliozko mezu bat TON-i bidaltzen diogunean, hau zehatz-mehatz behin gertatzea nahi dugu eta mezu bera berriro bidaltzen dugunean, kontratu adimendunak baztertzen du.

Beraz, ia kontratu guztiek konpontzen dituzte bi arazo hauek, gure kontratuak kanpoko mezuak onartzen dituenez, hori ere zaindu behar dugu.

Alderantzizko ordenan egingo dugu. Lehenik eta behin, konpon dezagun arazoa errepikapenarekin; kontratuak dagoeneko halako mezu bat jaso eta prozesatu badu, ez du bigarren aldiz exekutatuko. Eta orduan arazoa konponduko dugu, pertsona zirkulu jakin batek soilik bidal ditzan kontratu adimendunari mezuak.

Mezu bikoiztuekin arazoa konpontzeko modu desberdinak daude. Hona hemen nola egingo dugun. Kontratu adimendunean, jasotako mezuen kontagailua hasierako 0 balioarekin hasieratzen dugu. Kontratu adimendunaren mezu bakoitzean, uneko kontagailuaren balioa gehituko dugu. Mezuko kontagailuaren balioa ez badator bat kontratu adimendunaren balioarekin, orduan ez dugu prozesatzen; hala bada, prozesatu eta kontratu adimendunean kontagailua 1 handituko dugu.

Itzuli gaitezen lottery-test-suite.fif eta gehitu bigarren proba bat. Zenbaki oker bat bidaltzen badugu, kodeak salbuespen bat bota beharko luke. Adibidez, utzi kontratuaren datuek 166 gorde, eta 165 bidaliko dugu.

<b 166 32 u, b> storage !
<b 165 32 u, b> message !

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx

drop 
exit_code ! 
."Exit code " exit_code @ . cr 
exit_code @ 33 - abort"Test #2 Not passed"

Abiarazi gaitezen.

 ~/TON/build/crypto/fift -s lottery-test-suite.fif 

Eta proba errore batekin exekutatzen dela ikusiko dugu.

[ 1][t 0][1582283084.210902214][words.cpp:3046] lottery-test-suite.fif:67: abort": Test #2 Not passed
[ 1][t 0][1582283084.210941076][fift-main.cpp:196]      Error interpreting file `lottery-test-suite.fif`: error interpreting included file `lottery-test-suite.fif` : lottery-test-suite.fif:67: abort": Test #2 Not passed

Etapa honetan lottery-test-suite.fif itxura izan beharko luke ΠΏΠΎ ссылкС.

Orain gehi diezaiogun kontagailuaren logika kontratu adimendunari lottery-code.fc.

() recv_internal(slice in_msg) impure {
    ;; TODO: implementation 
}

() recv_external(slice in_msg) impure {
    if (slice_empty?(in_msg)) {
        return (); 
    }
    int msg_seqno = in_msg~load_uint(32);
    var ds = begin_parse(get_data());
    int stored_seqno = ds~load_uint(32);
    throw_unless(33, msg_seqno == stored_seqno);
}

Π’ slice in_msg bidaltzen dugun mezua datza.

Egiten dugun lehenengo gauza mezuak datuak dituen egiaztatzea da, ez bada, irteten gara besterik gabe.

Ondoren, mezua analizatzen dugu. in_msg~load_uint(32) 165 zenbakia kargatzen du, 32 biteko unsigned int igorritako mezutik.

Ondoren, 32 bit kargatzen ditugu kontratu adimendunaren biltegitik. Kargatutako zenbakia gainditutakoarekin bat datorrela egiaztatzen dugu; hala ez bada, salbuespen bat botako dugu. Gure kasuan, partida ez den bat pasatzen ari garenez, salbuespen bat bota behar da.

Orain konpilatu dezagun.

~/TON/build/crypto/func -APSR -o lottery-compiled.fif ~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fc 

Kopiatu ondoriozko kodea hemen lottery-test-suite.fif, azken lerroa ordezkatzeaz ahaztu gabe.

Proba gainditzen dela egiaztatzen dugu:

~/TON/build/crypto/fift -s lottery-test-suite.fif

Hementxe Dagokion konpromisoa ikus dezakezu uneko emaitzekin.

Kontuan izan deserosoa dela kontratu adimendun baten kodea konpilatua etengabe probak dituen fitxategi batean kopiatzea, beraz, kodea konstante batean idatziko duen script bat idatziko dugu, eta konpilatutako kodea gure probetara konektatuko dugu. "include".

Sortu fitxategi bat proiektuaren karpetan build.sh ondoko edukiarekin.

#!/bin/bash

~/TON/build/crypto/func -SPA -R -o lottery-compiled.fif ~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fc

Egin dezagun exekutagarria.

chmod +x ./build.sh

Orain, exekutatu gure script-a kontratua osatzeko. Baina honetaz gain, konstante batean idatzi behar dugu code. Beraz, fitxategi berri bat sortuko dugu lotter-compiled-for-test.fif, fitxategian sartuko duguna lottery-test-suite.fif.

Gehi dezagun skirpt kodea sh-ra, eta horrek konpilatutako fitxategia bikoiztuko du lotter-compiled-for-test.fif eta aldatu azken lerroa.

# copy and change for test 
cp lottery-compiled.fif lottery-compiled-for-test.fif
sed '$d' lottery-compiled-for-test.fif > test.fif
rm lottery-compiled-for-test.fif
mv test.fif lottery-compiled-for-test.fif
echo -n "}END>s constant code" >> lottery-compiled-for-test.fif

Orain, egiaztatzeko, exekutatu dezagun ondoriozko scripta eta fitxategi bat sortuko da lottery-compiled-for-test.fif, gurean sartuko duguna lottery-test-suite.fif

Π’ lottery-test-suite.fif ezabatu kontratuaren kodea eta gehitu lerroa "lottery-compiled-for-test.fif" include.

Probak egiten ditugu gainditzen direla egiaztatzeko.

~/TON/build/crypto/fift -s lottery-test-suite.fif

Bikaina, orain probak abiarazteko automatizatzeko, sor dezagun fitxategi bat test.sh, lehenengo exekutatu egingo dena build.sh, eta ondoren exekutatu probak.

touch test.sh
chmod +x test.sh

Barruan idazten dugu

./build.sh 

echo "nCompilation completedn"

export FIFTPATH=~/TON/ton/crypto/fift/lib
~/TON/build/crypto/fift -s lottery-test-suite.fif

Egin dezagun test.sh eta exekutatu probak funtzionatzen duela ziurtatzeko.

chmod +x ./test.sh
./test.sh

Kontratua osatzen dela eta probak egiten direla egiaztatzen dugu.

Bikaina, orain martxan jartzen test.sh Probak bildu eta berehala egingo dira martxan. Hona hemen esteka konprometitu.

Ados, jarraitu baino lehen, egin dezagun gauza bat erosotasunerako.

Sor dezagun karpeta bat build bertan kopiatutako kontratua eta bere klona konstante batean idatzita gordeko ditugu lottery-compiled.fif, lottery-compiled-for-test.fif. Sor dezagun karpeta bat ere test non gordeko da proba fitxategia? lottery-test-suite.fif eta balizko beste fitxategi osagarri batzuk. Lotura dagozkion aldaketetarako.

Jarrai dezagun kontratu adimenduna garatzen.

Ondoren, mezua jaso dela egiaztatzen duen proba bat egin behar da eta kontagailua dendan eguneratzen dela zenbaki zuzena bidaltzen dugunean. Baina hori geroago egingo dugu.

Orain pentsa dezagun zer datu-egitura eta zer datu gorde behar diren kontratu adimendunean.

Gordetzen dugun guztia deskribatuko dut.

`seqno` 32-Ρ… Π±ΠΈΡ‚Π½ΠΎΠ΅ Ρ†Π΅Π»ΠΎΠ΅ ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ число счСтчик. 

`pubkey` 256-Ρ‚ΠΈ Π±ΠΈΡ‚Π½ΠΎΠ΅ Ρ†Π΅Π»ΠΎΠ΅ ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ число ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡, с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ, ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡ‚ΡŒ подпись ΠΎΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½Π½ΠΎΠ³ΠΎ ΠΈΠ·Π²Π½Π΅ сообщСния, ΠΎ Ρ‡Π΅ΠΌ Π½ΠΈΠΆΠ΅. 

`order_seqno` 32-Ρ… Π±ΠΈΡ‚Π½ΠΎΠ΅ Ρ†Π΅Π»ΠΎΠ΅ ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ число Ρ…Ρ€Π°Π½ΠΈΡ‚ счСтчик количСства ставок. 

`number_of_wins` 32-Ρ… Π±ΠΈΡ‚Π½ΠΎΠ΅ Ρ†Π΅Π»ΠΎΠ΅ ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ число Ρ…Ρ€Π°Π½ΠΈΡ‚  количСство ΠΏΠΎΠ±Π΅Π΄. 

`incoming_amount` Ρ‚ΠΈΠΏ Π΄Π°Π½Π½Ρ‹Ρ… Gram (ΠΏΠ΅Ρ€Π²Ρ‹Π΅ 4 Π±ΠΈΡ‚Π° ΠΎΡ‚Π²Π΅Ρ‡Π°Π΅Ρ‚ Π·Π° Π΄Π»ΠΈΠ½Ρƒ), Ρ…Ρ€Π°Π½ΠΈΡ‚ ΠΎΠ±Ρ‰Π΅Π΅ количСство Π³Ρ€Π°ΠΌΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π±Ρ‹Π»ΠΈ ΠΎΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½Ρ‹ Π½Π° ΠΊΠΎΠ½Ρ‚Ρ€Ρ‚Π°ΠΊΡ‚. 

`outgoing_amount` ΠΎΠ±Ρ‰Π΅Π΅ количСство Π³Ρ€Π°ΠΌΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±Ρ‹Π»ΠΎ ΠΎΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½ΠΎ побСдитСлям. 

`owner_wc` Π½ΠΎΠΌΠ΅Ρ€ Π²ΠΎΡ€ΠΊΡ‡Π΅ΠΉΠ½Π°, 32-Ρ… Π±ΠΈΡ‚Π½ΠΎΠ΅ (Π² Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… мСстах написано, Ρ‡Ρ‚ΠΎ 8-ΠΌΠΈ Π±ΠΈΡ‚Π½ΠΎΠ΅) Ρ†Π΅Π»ΠΎΠ΅ число. Π’ Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ всСго Π΄Π²Π° -1 ΠΈ 0. 

`owner_account_id` 256-Ρ‚ΠΈ Π±ΠΈΡ‚Π½ΠΎΠ΅ Ρ†Π΅Π»ΠΎΠ΅ ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ число, адрСс ΠΊΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚Π° Π² Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΌ Π²ΠΎΡ€ΠΊΡ‡Π΅ΠΉΠ½Π΅. 

`orders` пСрСмСнная Ρ‚ΠΈΠΏΠ° ΡΠ»ΠΎΠ²Π°Ρ€ΡŒ, Ρ…Ρ€Π°Π½ΠΈΡ‚ послСдниС Π΄Π²Π°Π΄Ρ†Π°Ρ‚ΡŒ ставок. 

Ondoren, bi funtzio idatzi behar dituzu. Dei diezaiogun lehenengoari pack_state(), datuak kontratu adimendunen biltegian gero gordetzeko bilduko dituena. Dei diezaiogun bigarrenari unpack_state() biltegitik datuak irakurri eta itzuliko ditu.

_ pack_state(int seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) inline_ref {
    return begin_cell()
            .store_uint(seqno, 32)
            .store_uint(pubkey, 256)
            .store_uint(order_seqno, 32)
            .store_uint(number_of_wins, 32)
            .store_grams(incoming_amount)
            .store_grams(outgoing_amount)
            .store_int(owner_wc, 32)
            .store_uint(owner_account_id, 256)
            .store_dict(orders)
            .end_cell();
}

_ unpack_state() inline_ref {
    var ds = begin_parse(get_data());
    var unpacked = (ds~load_uint(32), ds~load_uint(256), ds~load_uint(32), ds~load_uint(32), ds~load_grams(), ds~load_grams(), ds~load_int(32), ds~load_uint(256), ds~load_dict());
    ds.end_parse();
    return unpacked;
}

Bi funtzio hauek kontratu adimendunaren hasieran gehitzen ditugu. Landuko da horrela tarteko emaitza.

Datuak gordetzeko integratutako funtziora deitu beharko duzu set_data() eta datuak idatziko ditu pack_state() smart contract biltegian.

cell packed_state = pack_state(arg_1, .., arg_n); 
set_data(packed_state);

Orain datuak idazteko eta irakurtzeko funtzio erosoak ditugula, aurrera egin dezakegu.

Kanpotik jasotzen den mezua kontratuaren jabeak (edo gako pribaturako sarbidea duen beste erabiltzaile batek) sinatzen duela egiaztatu behar dugu.

Kontratu adimendun bat argitaratzen dugunean, biltegian behar ditugun datuekin abiarazi dezakegu, etorkizunean erabiltzeko gordeko direnak. Bertan gako publikoa grabatuko dugu, sarrerako mezua dagokion gako pribatuarekin sinatu dela egiaztatu ahal izateko.

Jarraitu aurretik, sor dezagun gako pribatu bat eta idatz dezagun test/keys/owner.pk. Horretarako, abiarazi dezagun Fift modu interaktiboan eta exekuta ditzagun lau komando.

`newkeypair` гСнСрация ΠΏΡƒΠ±Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΈ ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½ΠΎΠ³ΠΎ ΠΊΠ»ΡŽΡ‡Π° ΠΈ запись ΠΈΡ… Π² стСк. 

`drop` удалСния ΠΈΠ· стСка Π²Π΅Ρ€Ρ…Π½Π΅Π³ΠΎ элСмСнта (Π² Π΄Π°Π½Π½ΠΎΠΌ случаС ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡)  

`.s` просто ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Ρ‡Ρ‚ΠΎ Π»Π΅ΠΆΠΈΡ‚ Π² стСкС Π² Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ 

`"owner.pk" B>file` запись ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½ΠΎΠ³ΠΎ ΠΊΠ»ΡŽΡ‡Π° Π² Ρ„Π°ΠΉΠ» с ΠΈΠΌΠ΅Π½Π΅ΠΌ `owner.pk`. 

`bye` Π·Π°Π²Π΅Ρ€ΡˆΠ°Π΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Ρƒ с Fift. 

Sor dezagun karpeta bat keys karpeta barruan test eta idatzi bertan gako pribatua.

mkdir test/keys
cd test/keys
~/TON/build/crypto/fift -i 
newkeypair
 ok
.s 
BYTES:128DB222CEB6CF5722021C3F21D4DF391CE6D5F70C874097E28D06FCE9FD6917 BYTES:DD0A81AAF5C07AAAA0C7772BB274E494E93BB0123AA1B29ECE7D42AE45184128 
drop 
 ok
"owner.pk" B>file
 ok
bye

Uneko karpetan fitxategi bat ikusten dugu owner.pk.

Gako publikoa pilatik kentzen dugu eta behar denean pribatutik lor dezakegu.

Orain sinadura egiaztapena idatzi behar dugu. Has gaitezen probarekin. Lehenik eta behin, fitxategiko gako pribatua irakurriko dugu funtzioa erabiliz file>B eta idatzi aldagai batean owner_private_key, gero funtzioa erabiliz priv>pub bihurtu gako pribatua gako publiko batean eta idatzi emaitza owner_public_key.

variable owner_private_key
variable owner_public_key 

"./keys/owner.pk" file>B owner_private_key !
owner_private_key @ priv>pub owner_public_key !

Bi giltzak beharko ditugu.

Kontratu adimendunen biltegiratzea datu arbitrarioekin hasieratzen dugu funtzioaren sekuentzia berean pack_state()eta idatzi aldagai batean storage.

variable owner_private_key
variable owner_public_key 
variable orders
variable owner_wc
variable owner_account_id

"./keys/owner.pk" file>B owner_private_key !
owner_private_key @ priv>pub owner_public_key !
dictnew orders !
0 owner_wc !
0 owner_account_id !

<b 0 32 u, owner_public_key @ B, 0 32 u, 0 32 u, 0 Gram, 0 Gram, owner_wc @ 32 i, owner_account_id @ 256 u,  orders @ dict, b> storage !

Jarraian, sinatutako mezu bat idatziko dugu, sinadura eta kontagailuaren balioa soilik izango ditu.

Lehenik eta behin, transmititu nahi ditugun datuak sortzen ditugu, ondoren gako pribatu batekin sinatzen ditugu eta azkenik sinatutako mezu bat sortzen dugu.

variable message_to_sign
variable message_to_send
variable signature
<b 0 32 u, b> message_to_sign !
message_to_sign @ hashu owner_private_key @ ed25519_sign_uint signature !
<b signature @ B, 0 32 u, b> <s  message_to_send !  

Ondorioz, smart contract-era bidaliko dugun mezua aldagai batean erregistratzen da message_to_send, funtzioei buruz hashu, ed25519_sign_uint irakur dezakezu Bosteko dokumentazioan.

Eta proba egiteko berriro deitzen dugu.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

Horrela Probak dituen fitxategiak honelakoa izan beharko luke fase honetan.

Egin dezagun proba eta huts egingo du, beraz, kontratu adimenduna aldatuko dugu, formatu honetako mezuak jaso eta sinadura egiazta ditzan.

Lehenik eta behin, mezutik sinaduraren 512 bit zenbatu eta aldagai batean idazten dugu, gero kontagailuaren aldagaiaren 32 bit zenbatuko ditugu.

Smart Contract biltegitik datuak irakurtzeko funtzio bat dugunez, erabiliko dugu.

Hurrengoa biltegiratzearekin transferitutako kontagailua egiaztatzea eta sinadura egiaztatzea da. Zerbait bat ez badator, orduan salbuespen bat botako dugu dagokion kodearekin.

var signature = in_msg~load_bits(512);
var message = in_msg;
int msg_seqno = message~load_uint(32);
(int stored_seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) = unpack_state();
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, check_signature(slice_hash(in_msg), signature, pubkey));

Konpromiso garrantzitsua hemen.

Egin ditzagun probak eta ikus ditzagun bigarren probak huts egiten duela. Bi arrazoirengatik, mezuan ez dago bit nahikorik eta biltegian ez dago nahikoa bit, beraz, kodea huts egiten du analizatzean. Bidaltzen ari garen mezuari sinadura bat gehitu eta azken probako biltegiratzea kopiatu behar dugu.

Bigarren proban, mezu sinadura bat gehituko dugu eta kontratu adimendunen biltegiratzea aldatuko dugu. Horrela probak dituen fitxategia une honetan itxura du.

Idatz dezagun laugarren proba, eta bertan beste norbaiten gako pribatuarekin sinatutako mezu bat bidaliko dugu. Sor dezagun beste gako pribatu bat eta gorde dezagun fitxategi batean not-owner.pk. Gako pribatu honekin sinatuko dugu mezua. Egin ditzagun probak eta ziurtatu proba guztiak gainditzen direla. Konprometitu une honetan.

Orain, azkenean, kontratu adimendunaren logika ezartzera pasa gaitezke.
Π’ recv_external() bi mezu mota onartuko ditugu.

Gure kontratuak jokalarien galerak pilatuko dituenez, diru hori loteriaren sortzaileari transferitu behar zaio. Loteriaren sortzailearen zorroaren helbidea biltegian erregistratzen da kontratua sortzen denean.

Badaezpada, galtzaileen gramoak bidaltzen ditugun helbidea aldatzeko gaitasuna behar dugu. Loteriatik gramoak jabearen helbidera bidaltzeko aukera ere izan beharko genuke.

Has gaitezen lehenengotik. Idatz dezagun lehenik mezua bidali ondoren, kontratu adimendunak helbide berria biltegian gorde duela egiaztatuko duen proba. Kontuan izan mezuan, kontagailuaz eta helbide berriaz gain, transmititzen dugula ere action 7 biteko zenbaki oso ez negatiboa, horren arabera, mezua nola prozesatu aukeratuko dugu kontratu adimendunean.

<b 0 32 u, 1 @ 7 u, new_owner_wc @  32 i, new_owner_account_id @ 256 u, b> message_to_sign !

Probak smartcontract biltegiratzea nola deserializatuta dagoen ikus dezakezu storage Bostean. Aldagaien deserializazioa Fift-en dokumentazioan deskribatzen da.

Konprometitu esteka ore erantsiarekin.

Egin dezagun proba eta ziurtatu huts egiten duela. Orain gehi dezagun logika loteriaren jabearen helbidea aldatzeko.

Kontratu adimendunean analizatzen jarraitzen dugu message, irakurri action. Gogora dezagun bi izango ditugula action: helbidea aldatu eta gramoak bidali.

Ondoren, kontratuaren jabearen helbide berria irakurri eta biltegian gordetzen dugu.
Probak egiten ditugu eta hirugarren probak huts egiten duela ikusten dugu. Huts egiten du kontratuak orain mezutik 7 bit analizatzen dituelako, proban falta direnak. Gehitu existitzen ez den bat mezuari action. Egin ditzagun probak eta ea dena pasatzen dela. Hemen aldaketak egiteko konpromisoa. Bikaina.

Orain idatz dezagun zehaztutako gramo kopurua aurrez gordetako helbidera bidaltzeko logika.

Lehenik eta behin, idatzi dezagun proba. Bi proba idatziko ditugu, bata oreka nahikorik ez dagoenean, bigarrena dena ongi gainditu behar denean. Probak ikus daitezke konpromiso honetan.

Orain gehi dezagun kodea. Lehenik eta behin, idatzi ditzagun bi metodo laguntzaile. Lehenengo lortzeko metodoa kontratu adimendun baten egungo saldoa jakitea da.

int balance() inline_ref method_id {
    return get_balance().pair_first();
}

Eta bigarrena gramoak beste kontratu adimendun batera bidaltzeko da. Metodo hau guztiz kopiatu nuen beste kontratu adimendun batetik.

() send_grams(int wc, int addr, int grams) impure {
    ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
    cell msg = begin_cell()
    ;;  .store_uint(0, 1) ;; 0 <= format indicator int_msg_info$0 
    ;;  .store_uint(1, 1) ;; 1 <= ihr disabled
    ;;  .store_uint(1, 1) ;; 1 <= bounce = true
    ;;  .store_uint(0, 1) ;; 0 <= bounced = false
    ;;  .store_uint(4, 5)  ;; 00100 <= address flags, anycast = false, 8-bit workchain
        .store_uint (196, 9)
        .store_int(wc, 8)
        .store_uint(addr, 256)
        .store_grams(grams)
        .store_uint(0, 107) ;; 106 zeroes +  0 as an indicator that there is no cell with the data.
        .end_cell(); 
    send_raw_message(msg, 3); ;; mode, 2 for ignoring errors, 1 for sender pays fees, 64 for returning inbound message value
}

Gehi ditzagun bi metodo hauek kontratu adimendunari eta idatzi logika. Lehenik eta behin, mezuaren gramo kopurua analizatzen dugu. Jarraian balantzea egiaztatzen dugu, nahikoa ez bada salbuespen bat botatzen dugu. Dena ondo badago, gramoak gordetako helbidera bidaliko ditugu eta kontagailua eguneratzen dugu.

int amount_to_send = message~load_grams();
throw_if(36, amount_to_send + 500000000 > balance());
accept_message();
send_grams(owner_wc, owner_account_id, amount_to_send);
set_data(pack_state(stored_seqno + 1, pubkey, order_seqno, number_of_wins, incoming_amount, outgoing_amount, owner_wc, owner_account_id, orders));

Horrela kontratu adimenduna dirudi momentu honetan. Egin ditzagun probak eta ziurtatu gainditzen direla.

Bide batez, komisio bat kentzen zaio kontratu adimendunetik prozesatutako mezu baterako. Kontratu adimendunaren mezuek eskaera gauzatu ahal izateko, oinarrizko egiaztapenen ondoren deitu behar duzu accept_message().

Orain barruko mezuetara pasa gaitezen. Izan ere, gramoak bakarrik onartuko ditugu eta irabazten badu jokalariari kopuru bikoitza itzuliko diogu eta jabeari hirugarren bat galtzen badu.

Lehenik eta behin, idatz dezagun proba sinple bat. Horretarako, kontratu adimendunaren proba-helbide bat behar dugu, zeinetatik ustez gramoak bidaltzen ditugun kontratu adimendunera.

Kontratu adimendunaren helbidea bi zenbakiz osatuta dago, lan-katearen ardura duen 32 biteko zenbaki oso bat eta lan-kate honetako 256 biteko zenbaki oso negatibo ez den kontu bakarra. Adibidez, -1 eta 12345, hau da fitxategi batean gordeko dugun helbidea.

Helbidea gordetzeko funtzioa kopiatu dut TonUtil.fif.

// ( wc addr fname -- )  Save address to file in 36-byte format
{ -rot 256 u>B swap 32 i>B B+ swap B>file } : save-address

Ikus dezagun funtzioak nola funtzionatzen duen, honek Fift-ek nola funtzionatzen duen ulertuko du. Abiarazi Fift modu interaktiboan.

~/TON/build/crypto/fift -i 

Lehenik eta behin -1, 12345 eta etorkizuneko "sender.addr" fitxategiaren izena pilara jartzen ditugu:

-1 12345 "sender.addr" 

Hurrengo urratsa funtzioa exekutatzea da -rot, eta horrek pilaren goialdean kontratu adimendun zenbaki esklusibo bat dagoen moduan mugitzen du:

"sender.addr" -1 12345

256 u>B 256 biteko zenbaki oso ez negatiboa byte bihurtzen du.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap pilaren goiko bi elementuak trukatzen ditu.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B 32 biteko zenbaki oso bat byte bihurtzen du.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ bi byte sekuentzia lotzen ditu.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

berriro swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Eta azkenik, byteak fitxategian idazten dira B>file. Honen ondoren gure pila hutsik dago. Gelditzen gara Fift. Fitxategi bat sortu da uneko karpetan sender.addr. Muga dezagun fitxategia sortutako karpetara test/addresses/.

Idatz dezagun gramoak kontratu adimendun batera bidaliko dituen proba sinple bat. Hona hemen konpromisoa.

Ikus dezagun orain loteriaren logika.

Egiten dugun lehenengo gauza mezua egiaztatzea da bounced edo ez bada bounced, orduan alde batera uzten dugu. bounced esan nahi du kontratuak gramoak itzuliko dituela erroreren bat gertatzen bada. Ez dugu gramorik itzuliko bat-batean erroreren bat gertatzen bada.

Egiaztatzen dugu, saldoa gramo erdia baino txikiagoa bada, orduan mezua onartu eta ez ikusiarena egiten dugu.

Ondoren, mezua atera den kontratu adimendunaren helbidea aztertuko dugu.

Biltegiko datuak irakurtzen ditugu eta, gero, apustu zaharrak historiatik ezabatzen ditugu, hogei baino gehiago badira. Erosotasunerako, hiru funtzio gehigarri idatzi ditut pack_order(), unpack_order(), remove_old_orders().

Jarraian, saldoa ordainketarako nahikoa ez den aztertzen dugu, orduan hau ez dela apustu bat, birjartzea baizik eta gordeko dugu. orders.

Ondoren, azkenik, kontratu adimendunaren funtsa.

Lehenik eta behin, jokalariak galtzen badu, apustuen historian gordetzen dugu eta zenbatekoa 3 gramotik gorakoa bada, 1/3 bidaltzen diogu smart contractaren jabeari.

Jokalariak irabazten badu, bikoitza bidaltzen dugu jokalariaren helbidera eta gero apustuari buruzko informazioa historian gordetzen dugu.

() recv_internal(int order_amount, cell in_msg_cell, slice in_msg) impure {
    var cs = in_msg_cell.begin_parse();
    int flags = cs~load_uint(4);  ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
    if (flags & 1) { ;; ignore bounced
        return ();
    }
    if (order_amount < 500000000) { ;; just receive grams without changing state 
        return ();
    }
    slice src_addr_slice = cs~load_msg_addr();
    (int src_wc, int src_addr) = parse_std_addr(src_addr_slice);
    (int stored_seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) = unpack_state();
    orders = remove_old_orders(orders, order_seqno);
    if (balance() < 2 * order_amount + 500000000) { ;; not enough grams to pay the bet back, so this is re-fill
        builder order = pack_order(order_seqno, 1, now(), order_amount, src_wc, src_addr);
        orders~udict_set_builder(32, order_seqno, order);
        set_data(pack_state(stored_seqno, pubkey, order_seqno + 1, number_of_wins, incoming_amount + order_amount, outgoing_amount, owner_wc, owner_account_id, orders));
        return ();
    }
    if (rand(10) >= 4) {
        builder order = pack_order(order_seqno, 3, now(), order_amount, src_wc, src_addr);
        orders~udict_set_builder(32, order_seqno, order);
        set_data(pack_state(stored_seqno, pubkey, order_seqno + 1, number_of_wins, incoming_amount + order_amount, outgoing_amount, owner_wc, owner_account_id, orders));
        if (order_amount > 3000000000) {
            send_grams(owner_wc, owner_account_id, order_amount / 3);
        }
        return ();
    }
    send_grams(src_wc, src_addr, 2 * order_amount);
    builder order = pack_order(order_seqno, 2, now(), order_amount, src_wc, src_addr);
    orders~udict_set_builder(32, order_seqno, order);
    set_data(pack_state(stored_seqno, pubkey, order_seqno + 1, number_of_wins + 1, incoming_amount, outgoing_amount + 2 * order_amount, owner_wc, owner_account_id, orders));
}

Dela. Dagokion konpromisoa.

Orain geratzen dena sinplea da, sor ditzagun get-metodoak, kontratuaren egoerari buruzko informazioa kanpotik lortu ahal izateko (hain zuzen ere, irakurri haien kontratu adimendunen biltegiko datuak).

Gehi ditzagun get metodoak. Jarraian idatziko dugu kontratu adimendun bati buruzko informazioa nola jaso.

Kontratu adimendun bat argitaratzean gertatzen den lehenengo eskaera prozesatuko duen kodea gehitzea ere ahaztu zait. Dagokion konpromisoa. Eta harago zuzenduta akatsa jabearen kontura zenbatekoaren 1/3 bidaltzearekin.

Hurrengo urratsa kontratu adimenduna argitaratzea da. Sor dezagun karpeta bat requests.

Argitalpen kodea hartu nuen oinarritzat simple-wallet-code.fc bertan aurki dezake biltegi ofizialean.

Arreta ematea merezi duen zerbait. Kontratu adimendunen biltegiratze bat eta sarrera-mezu bat sortzen ditugu. Horren ostean, kontratu adimendunaren helbidea sortzen da, hau da, helbidea TON-en argitaratu aurretik ere ezagutzen da. Ondoren, hainbat gramo bidali behar dituzu helbide honetara, eta horren ondoren bakarrik fitxategi bat bidali behar duzu kontratu adimendunarekin berarekin, sareak kontratu adimenduna eta eragiketak bertan gordetzeko komisioa hartzen baitu (kontratu adimendunak gordetzen eta exekutatzen dituzten balioztatzaileak). ). Kodea hemen ikus daiteke.

Ondoren, argitalpen-kodea exekutatu eta lortuko dugu lottery-query.boc smart contract fitxategia eta helbidea.

~/TON/build/crypto/fift -s requests/new-lottery.fif 0

Ez ahaztu sortutako fitxategiak gordetzea: lottery-query.boc, lottery.addr, lottery.pk.

Besteak beste, exekuzio erregistroetan kontratu adimendunaren helbidea ikusiko dugu.

new wallet address = 0:044910149dbeaf8eadbb2b28722e7d6a2dc6e264ec2f1d9bebd6fb209079bc2a 
(Saving address to file lottery.addr)
Non-bounceable address (for init): 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd
Bounceable address (for later access): kQAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KpFY

Ondo pasatzeko, egin diezaiogun eskaera TON-i

$ ./lite-client/lite-client -C ton-lite-client-test1.config.json 
getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Eta helbide hau duen kontua hutsik dagoela ikusiko dugu.

account state is empty

Helbidera bidaltzen dugu 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 Gram eta segundo batzuk igaro ondoren komando bera exekutatzen dugu. Erabiltzen ditudan gramoak bidaltzeko zorro ofiziala, eta txatetik norbaiti eskatu diezaiokezu probako gramoak, artikuluaren amaieran hitz egingo dudanez.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Hasierarik gabeko bat dirudi (state:account_uninit) helbide bereko eta 1 nanogramoko saldoa duen kontratu adimenduna.

account state is (account
  addr:(addr_std
    anycast:nothing workchain_id:0 address:x044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A)
  storage_stat:(storage_info
    used:(storage_used
      cells:(var_uint len:1 value:1)
      bits:(var_uint len:1 value:103)
      public_cells:(var_uint len:0 value:0)) last_paid:1583257959
    due_payment:nothing)
  storage:(account_storage last_trans_lt:3825478000002
    balance:(currencies
      grams:(nanograms
        amount:(var_uint len:4 value:2000000000))
      other:(extra_currencies
        dict:hme_empty))
    state:account_uninit))
x{C00044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A20259C2F2F4CB3800000DEAC10776091DCD650004_}
last transaction lt = 3825478000001 hash = B043616AE016682699477FFF01E6E903878CDFD6846042BA1BFC64775E7AC6C4
account balance is 2000000000ng

Orain argitara dezagun kontratu adimenduna. Abiarazi dezagun lite-client eta exekutatu.

> sendfile lottery-query.boc
[ 1][t 2][1583008371.631410122][lite-client.cpp:966][!testnode] sending query from file lottery-query.boc
[ 3][t 1][1583008371.828550100][lite-client.cpp:976][!query]    external message status is 1 

Egiazta dezagun kontratua argitaratu dela.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Besteak beste, lortzen dugu.

  storage:(account_storage last_trans_lt:3825499000002
    balance:(currencies
      grams:(nanograms
        amount:(var_uint len:4 value:1987150999))
      other:(extra_currencies
        dict:hme_empty))
    state:(account_active

Hori ikusten dugu account_active.

Aldaketei dagokien konpromisoa hemen.

Sor ditzagun orain kontratu adimendunarekin elkarreragiteko eskaerak.

Zehatzago esanda, lehenengoa helbidea aldatzeko lan independente gisa utziko dugu, eta bigarrena jabearen helbidera gramoak bidaltzeko egingo dugu. Izan ere, gramoak bidaltzeko proban egiten den gauza bera egin beharko dugu.

Hau da kontratu adimendunari bidaliko diogun mezua, non msg_seqno 165, action 2 eta 9.5 gramo bidaltzeko.

<b 165 32 u, 2 7 u, 9500000000 Gram, b>

Ez ahaztu mezua zure gako pribatuarekin sinatzea lottery.pk, kontratu adimenduna sortzean lehenago sortu zena. Hona hemen dagokion konpromisoa.

Kontratu adimendun batetik informazioa jasotzea get metodoak erabiliz

Orain ikus dezagun nola exekutatu smart contract get metodoak.

korrika lite-client eta exekutatu idatzi ditugun get metodoak.

$ ./lite-client/lite-client -C ton-lite-client-test1.config.json
> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd balance
arguments:  [ 104128 ] 
result:  [ 64633878952 ] 
...

Π’ result funtzioak itzultzen duen balioa dauka balance() gure kontratu adimendunetik.
Gauza bera egingo dugu beste hainbat metodorekin.

> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_seqno
...
arguments:  [ 77871 ] 
result:  [ 1 ] 

Eskatu dezagun zure apustuen historia.

> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_orders
...
arguments:  [ 67442 ] 
result:  [ ([0 1 1583258284 10000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308] [1 3 1583258347 4000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308] [2 1 1583259901 50000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308]) ] 

Lite-client erabiliko dugu eta kontratu adimendunari buruzko informazioa webgunean bistaratzeko metodoak lortuko ditugu.

Kontratu adimendunen datuak webgunean bistaratzea

Webgune sinple bat idatzi nuen Python-en kontratu adimendunaren datuak modu erosoan bistaratzeko. Hemen ez naiz zehatz-mehatz luzatuko eta gunea argitaratuko dut konpromiso batean.

TON-ri egindako eskaerak Python laguntzarekin lite-client. Erosotasunerako, gunea Docker-en bilduta dago eta Google Cloud-en argitaratzen da. Esteka.

Saiatzen

Orain saia gaitezen gramoak hortik hornitzeko bidaltzen poltsan. 40 gramo bidaliko ditugu. Eta egin ditzagun apustu pare bat argitasuna lortzeko. Ikusten dugu webguneak apustuen historia, egungo irabazi-ehunekoa eta beste informazio erabilgarria erakusten duela.

Ikusten dugulehenengoa irabazi genuela, bigarrena galdu.

afterword

Artikulua espero nuena baino askoz luzeagoa izan da, agian laburragoa izan zitekeen, edo agian TON-i buruz ezer ez dakien eta kontratu adimendun ez hain sinple bat idatzi eta argitaratu nahi duen pertsona batentzat, elkarrekin harremanetan jartzeko gaitasuna duena. hura. Agian gauza batzuk modu sinpleagoan azaldu zitezkeen.

Agian inplementazioaren alderdi batzuk eraginkorrago eta dotoreago egin zitezkeen, baina orduan are denbora gehiago beharko zen artikulua prestatzeko. Baliteke ere nonbait akats bat egitea edo zerbait ulertu ez izatea, beraz, zerbait serioa egiten ari bazara, dokumentazio ofizialean edo TON kodea duen biltegi ofizialean oinarritu behar duzu.

Kontuan izan behar da TON bera oraindik garapen fase aktiboan dagoenez, artikulu honetako edozein urrats hautsiko duten aldaketak gerta daitezkeela (idazten ari nintzen bitartean gertatu zena, dagoeneko zuzenduta dago), baina ikuspegi orokorra da. nekez aldatuko da.

Ez dut TONen etorkizunaz hitz egingo. Agian plataforma zerbait handia bihurtuko da eta denbora pasa beharko genuke aztertzen eta nitxo bat bete gure produktuekin orain.

Facebook-eko Libra ere badago, TON baino erabiltzaileen audientzia potentziala duena. Librari buruz ia ezer ez dakit, foroaren arabera TON komunitatean baino jarduera gehiago dago bertan. TON-en garatzaileak eta komunitateak lurpeko antzekoak diren arren, hori ere polita da.

Erreferentziak

  1. TON dokumentazio ofiziala: https://test.ton.org
  2. TON biltegi ofiziala: https://github.com/ton-blockchain/ton
  3. Plataforma desberdinetarako zorro ofiziala: https://wallet.ton.org
  4. Artikulu honetako kontratu adimendunen biltegia: https://github.com/raiym/astonished
  5. Esteka smart contract webgunera: https://ton-lottery.appspot.com
  6. Visual Studio Code for FunCrako luzapenaren biltegia: https://github.com/raiym/func-visual-studio-plugin
  7. Txateatu TON-i buruz Telegram-en, eta horrek benetan lagundu zuen hasierako fasean. Uste dut ez dela akats bat izango TON-entzat zerbait idatzi duten guztiak hor daudela esaten badut. Test gramoak ere eska ditzakezu bertan. https://t.me/tondev_ru
  8. TON-i buruzko beste berriketa bat non informazio baliagarria aurkitu nuen: https://t.me/TONgramDev
  9. Lehiaketaren lehen fasea: https://contest.com/blockchain
  10. Lehiaketaren bigarren fasea: https://contest.com/blockchain-2

Iturria: www.habr.com

Gehitu iruzkin berria