Om hur man skriver och publicerar ett smart kontrakt i Telegram Open Network (TON)

Hur man skriver och publicerar ett smart kontrakt i TON

Vad handlar den hÀr artikeln om?

I den hÀr artikeln kommer jag att berÀtta om hur jag deltog i den första (av tvÄ) Telegram blockchain-tÀvling, inte vann nÄgot pris och bestÀmde mig för att spela in upplevelsen i en artikel sÄ att den inte skulle sjunka i glömska och kanske hjÀlpa nÄgon.

Eftersom jag inte ville skriva abstrakt kod, utan för att fÄ nÄgot att fungera, skrev jag till artikeln ett smart kontrakt direktlotteri och en hemsida som visar smart kontraktsdata direkt frÄn TON utan att anvÀnda mellanlagring.

Artikeln kommer att vara anvÀndbar för dem som vill göra sitt första smarta kontrakt i TON, men inte vet var de ska börja.

Med lotteriet som exempel kommer jag att gÄ frÄn att installera miljön till att publicera ett smart kontrakt, interagera med det och skriva en hemsida för att ta emot och publicera data.

Om deltagande i tÀvlingen

I oktober förra Äret utlyste Telegram en blockchain-tÀvling med nya sprÄk Fift О FunC. Det var nödvÀndigt att vÀlja att skriva nÄgot av de fem föreslagna smarta kontrakten. Jag tÀnkte att det skulle vara trevligt att göra nÄgot annorlunda, lÀra mig ett sprÄk och göra nÄgot, Àven om jag inte skulle behöva skriva nÄgot annat i framtiden. Dessutom Àr Àmnet stÀndigt pÄ allas lÀppar.

Det Àr vÀrt att sÀga att jag inte hade nÄgon erfarenhet av att utveckla smarta kontrakt.

Jag planerade att delta Ànda till slutet medan jag kunde och sedan skriva en recensionsartikel, men jag misslyckades direkt vid den första. jag skrev plÄnbok med multisignatur pÄ FunC och i allmÀnhet fungerade det. Jag tog det som grund smart kontrakt om soliditet.

I det ögonblicket tÀnkte jag att detta definitivt rÀckte för att ta Ätminstone nÄgon prisplats. Till sist blev cirka 40 av 60 deltagare pristagare och jag var inte bland dem. I allmÀnhet Àr det inget fel med det hÀr, men en sak störde mig. Vid den tidpunkt dÄ beskedet om resultatet av granskningen med testet för mitt kontrakt inte hade gjorts frÄgade jag deltagarna i chatten om det var nÄgon annan som inte hade det, det fanns inga.

Tydligen, efter att ha lagt mĂ€rke till mina meddelanden, publicerade domarna en kommentar tvĂ„ dagar senare och jag förstĂ„r fortfarande inte om de av misstag missade mitt smarta kontrakt under bedömningen eller helt enkelt tyckte att det var sĂ„ dĂ„ligt att det inte behövde en kommentar. Jag stĂ€llde en frĂ„ga pĂ„ sidan men fick inget svar. Även om det inte Ă€r nĂ„gon hemlighet vem som dömde, ansĂ„g jag att det var onödigt att skriva personliga meddelanden.

Det tog mycket tid att förstÄ, sÄ det beslöts att skriva en artikel. Eftersom det inte finns mycket information Ànnu, kommer den hÀr artikeln att spara tid för alla intresserade.

Konceptet med smarta kontrakt i TON

Innan du skriver nÄgot mÄste du ta reda pÄ frÄn vilken sida du ska nÀrma dig den hÀr saken. SÄ nu ska jag berÀtta vilka delar systemet bestÄr av. Mer exakt, vilka delar behöver du veta för att kunna skriva Ätminstone nÄgot slags arbetskontrakt.

Vi kommer fokusera pÄ att skriva ett smart kontrakt och jobba med TON Virtual Machine (TVM), Fift О FunC, sÄ artikeln Àr mer som en beskrivning av utvecklingen av ett vanligt program. Vi kommer inte att uppehÄlla oss vid hur sjÀlva plattformen fungerar hÀr.

Generellt om hur det fungerar TVM och sprÄk Fift Det finns bra officiell dokumentation. Under mitt deltagande i tÀvlingen och nu nÀr jag skrev det nuvarande kontraktet vÀnde jag mig ofta till henne.

HuvudsprÄket som smarta kontrakt skrivs pÄ Àr FunC. Det finns för nÀrvarande ingen dokumentation pÄ det, sÄ för att skriva nÄgot behöver du studera exempel pÄ smarta kontrakt frÄn det officiella förrÄdet och implementeringen av sjÀlva sprÄket dÀr, plus att du kan titta pÄ exempel pÄ smarta kontrakt frÄn de tvÄ senaste tÀvlingarna. LÀnkar i slutet av artikeln.

LÄt oss sÀga att vi redan har skrivit ett smart kontrakt pÄ FunC, efter det kompilerar vi koden till Fift assembler.

Det sammanstÀllda smarta kontraktet ÄterstÄr att publiceras. För att göra detta mÄste du skriva en funktion i Fift, som kommer att acceptera den smarta kontraktskoden och nÄgra andra parametrar som indata, och utdata kommer att vara en fil med tillÀgget .boc (vilket betyder "pÄse med celler"), och, beroende pÄ hur vi skriver det, en privat nyckel och en adress som genereras baserat pÄ den smarta kontraktskoden. Du kan redan skicka gram till adressen till ett smart kontrakt som Ànnu inte har publicerats.

För att publicera ett smart kontrakt i TON mottagits .boc filen kommer att behöva skickas till blockkedjan med en lÀtt klient (mer om det nedan). Men innan du publicerar mÄste du överföra gram till den genererade adressen, annars kommer det smarta kontraktet inte att publiceras. NÀr det vÀl har publicerats kan det interagera med det smarta kontraktet genom att skicka meddelanden till det frÄn utsidan (t.ex. med hjÀlp av en lÀtt klient) eller frÄn insidan (t.ex. ett smart kontrakt skickar ett annat meddelande inuti TON).

NÀr vi vÀl förstÄr hur kod publiceras blir det lÀttare. Vi vet ungefÀr vad vi vill skriva och hur vÄrt program kommer att fungera. Och medan vi skriver letar vi efter hur detta redan Àr implementerat i befintliga smarta kontrakt, eller sÄ tittar vi pÄ implementeringskoden Fift О FunC i det officiella arkivet, eller titta i den officiella dokumentationen.

Mycket ofta sökte jag pÄ nyckelord i Telegram-chatten dÀr alla deltagare i tÀvlingen och Telegram-anstÀllda samlades, det hÀnde sig att under tÀvlingen samlades alla dÀr och började diskutera Fift och FunC. LÀnk i slutet av artikeln.

Det Àr dags att gÄ frÄn teori till praktik.

Förbereda miljön för att arbeta med TON

Jag gjorde allt som kommer att beskrivas i artikeln om MacOS och dubbelkollade det i en ren version. Ubuntu 18.04 LTS pÄ Docker.

Det första du behöver göra Àr att ladda ner och installera lite-client med vilken du kan skicka förfrÄgningar till TON.

Instruktionerna pÄ den officiella webbplatsen beskriver installationsprocessen ganska utförligt och tydligt, med vissa detaljer utelÀmnade. HÀr följer vi instruktionerna och installerar eventuella saknade beroenden lÀngs vÀgen. Jag kompilerade inte varje projekt sjÀlv och installerade frÄn det officiella arkivet. Ubuntu (pÄ MacOS jag anvÀnde 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 

NÀr alla beroenden Àr installerade kan du installera lite-client, Fift, FunC.

LÄt oss först klona TON-förvaret tillsammans med dess beroenden. För enkelhetens skull gör vi allt i en pÀrm. ~/TON.

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

Förvaret lagrar Àven implementeringar. Fift О FunC.

Nu Àr vi redo att montera projektet. Förvarskoden klonas in i mappen ~/TON/ton. I ~/TON skapa en mapp build och vi samlar projektet i den.

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

Eftersom vi ska skriva ett smart kontrakt behöver vi inte bara lite-clientMen Fift с FunC, sÄ vi sammanstÀller allt. Det Àr ingen snabb process, sÄ vi avvaktar.

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

DÀrefter laddar vi ner konfigurationsfilen, som innehÄller data om den nod till vilken lite-client kommer att ansluta.

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

Gör de första förfrÄgningarna till TON

LÄt oss nu starta lite-client.

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

Om bygget lyckades kommer du efter lanseringen att se en logg över anslutningen av light-klienten till noden.

[ 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)
...

Du kan utföra kommandot help och se vilka kommandon som finns tillgÀngliga.

help

LÄt oss lista kommandona som vi kommer att anvÀnda i den hÀr artikeln.

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-ĐŒĐ”Ń‚ĐŸĐŽŃ‹ ŃĐŒĐ°Ń€Ń‚ĐșĐŸĐœŃ‚Ń€Đ°Đșта. 

Nu Àr vi redo att skriva sjÀlva kontraktet.

genomförande

Idé

Som jag skrev ovan Àr det smarta kontraktet vi skriver ett lotteri.

Dessutom Àr detta inte ett lotteri dÀr du behöver köpa en biljett och vÀnta en timme, en dag eller en mÄnad, utan ett ögonblick dÀr anvÀndaren överför till kontraktsadressen N gram och fÄr den omedelbart tillbaka 2 * N gram eller tappar. Vi kommer att göra sannolikheten för att vinna cirka 40%. Om det inte finns tillrÀckligt med gram för betalning, kommer vi att betrakta transaktionen som en pÄfyllning.

Dessutom Àr det viktigt att vaden kan ses i realtid och i en bekvÀm form, sÄ att anvÀndaren omedelbart kan förstÄ om han har vunnit eller förlorat. DÀrför mÄste vi göra en webbplats som visar priser och resultat direkt frÄn TON.

Att skriva ett smart kontrakt

För enkelhetens skull gjorde jag en kodmarkering för FunC, pluginet kan hittas och installeras i Visual Studio Code-sökningen, om du plötsligt vill lÀgga till nÄgot sÄ har jag gjort pluginen allmÀnt tillgÀnglig. Dessutom har nÄgon tidigare gjort ett plugin för att arbeta med Fift, du kan ocksÄ installera det och hitta det i VSC.

LÄt oss omedelbart skapa ett arkiv dÀr vi kommer att begÄ mellanliggande resultat.

För att göra livet enklare kommer vi att skriva ett smart kontrakt och testa det lokalt tills det Àr klart. Först efter det kommer vi att publicera den i TON.

Det smarta kontraktet har tvÄ externa metoder som kan anropas. Första, recv_external() Denna funktion exekveras nÀr en förfrÄgan till kontraktet kommer frÄn omvÀrlden, det vill sÀga inte frÄn exempelvis TON, nÀr vi sjÀlva genererar ett meddelande och skickar det via lite-klient. Andra, recv_internal() det Àr nÀr inom TON sjÀlvt nÄgot kontrakt hÀnvisar till vÄrt. I bÄda fallen kan du skicka parametrar till funktionen.

LÄt oss börja med ett enkelt exempel som fungerar om det publiceras, men det ger ingen funktionalitet.

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

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

HÀr mÄste vi förklara vad detta Àr slice. All data som lagras i TON Blockchain Àr en samling TVM cell eller helt enkelt cell, kan en sÄdan cell lagra upp till 1023 bitar av data och upp till 4 referenser till andra celler.

TVM cell slice eller slice detta Àr en del av det befintliga cell anvÀnds för sin analys, kommer det att bli klart senare. Huvudsaken för oss Àr att vi kan överföra till ett smart kontrakt slice och beroende pÄ typen av meddelande, bearbeta data in recv_external() eller recv_internal().

impure — ett nyckelord som indikerar att funktionen modifierar smart kontraktsdata.

LÄt oss spara kontraktskoden lottery-code.fc och kompilera.

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

Betydelsen av flaggorna kan ses med kommandot

~/TON/build/crypto/func -help

Vi fick in den kompilerade Fift assembler-koden 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

Det kan drivas lokalt, för detta kommer vi att förbereda miljön.

Observera att den första raden ansluter Asm.fif, detta Àr kod skriven i Fift för Fift assembler.

Eftersom vi vill köra och testa det smarta kontraktet lokalt kommer vi att skapa en fil lottery-test-suite.fif och kopiera den kompilerade koden dit, ersÀtt den sista raden i den, som skriver den smarta kontraktskoden till en konstant code, sÄ att du sedan kan överföra den till den virtuella maskinen:

"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

Hittills verkar det klart, lÄt oss nu lÀgga till koden som vi kommer att anvÀnda för att starta TVM i samma fil.

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 vi skriver sammanhanget, det vill sĂ€ga de data som TVM kommer att lanseras med (eller nĂ€tverkstillstĂ„ndet). Redan under tĂ€vlingen visade en av utvecklarna hur den skapas c7 och jag kopierade det. I den hĂ€r artikeln kan vi behöva Ă€ndra rand_seed eftersom genereringen av ett slumptal beror pĂ„ det och inte kan Ă€ndras, kommer samma nummer att returneras varje gĂ„ng.

recv_internal О recv_external konstanter med vÀrdena 0 och -1 kommer att ansvara för att anropa motsvarande funktioner i det smarta kontraktet.

Nu Àr vi redo att skapa det första testet för vÄrt tomma smarta kontrakt. För tydlighetens skull kommer vi för nÀrvarande att lÀgga till alla tester i samma fil. lottery-test-suite.fif.

LÄt oss skapa en variabel storage och skriv en tom i den cell, kommer detta att vara lagringen av det smarta kontraktet.

message Detta Àr budskapet som vi kommer att överföra till den smarta kontakten utifrÄn. Vi lÀmnar det ocksÄ tomt för tillfÀllet.

variable storage 
<b b> storage ! 

variable message 
<b b> message ! 

Efter att vi har förberett konstanterna och variablerna kör vi TVM med kommandot runvmctx och skicka de skapade parametrarna till ingÄngen.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

I slutÀndan kommer vi att lyckas sÄ hÀr mellankod pÄ Fift.

Nu kan vi köra den resulterande koden.

export FIFTPATH=~/TON/ton/crypto/fift/lib // ĐČŃ‹ĐżĐŸĐ»ĐœŃĐ”ĐŒ ĐŸĐŽĐžĐœ раз ĐŽĐ»Ń ŃƒĐŽĐŸĐ±ŃŃ‚ĐČа 
~/TON/build/crypto/fift -s lottery-test-suite.fif 

Programmet bör köras utan fel och vi kommer att se exekveringsloggen i utgÄngen:

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

Bra, vi har skrivit den första fungerande versionen av ett smart kontrakt.

Nu mÄste vi lÀgga till funktionalitet. LÄt oss först ta itu med meddelanden som kommer frÄn omvÀrlden. recv_external()

Byggherren vÀljer sjÀlv vilket meddelandeformat som kontraktet kan acceptera.

Men vanligtvis,

  • För det första vill vi skydda vĂ„rt kontrakt frĂ„n omvĂ€rlden och göra det sĂ„ att endast Ă€garen av kontraktet kan skicka externa meddelanden till det.
  • för det andra, nĂ€r vi skickar ett giltigt meddelande till TON vill vi att det ska hĂ€nda exakt en gĂ„ng, och nĂ€r vi skickar samma meddelande igen avvisar det smarta kontraktet det.

SÄ nÀstan varje kontrakt handlar om dessa tvÄ frÄgor, eftersom vÄrt kontrakt fÄr externa meddelanden mÄste vi ta hand om det ocksÄ.

Vi gör det i omvÀnd ordning. LÄt oss först lösa problemet med upprepning: om kontraktet redan har fÄtt ett sÄdant meddelande och bearbetat det, kommer det inte att utföras en andra gÄng. Och dÄ ska vi lösa problemet sÄ att bara en viss krets av mÀnniskor kan skicka meddelanden till det smarta kontraktet.

Det finns olika sÀtt att lösa problemet med dubbletter av meddelanden. SÄ hÀr gör vi. I det smarta kontraktet initierar vi rÀknaren för mottagna meddelanden med startvÀrdet 0. I varje meddelande till det smarta kontraktet kommer vi att lÀgga till det aktuella vÀrdet pÄ rÀknaren. Om rÀknarvÀrdet i meddelandet inte stÀmmer överens med vÀrdet i det smarta kontraktet sÄ bearbetar vi det inte, om det stÀmmer överens sÄ bearbetar vi det och ökar rÀknaren i det smarta kontraktet med 1.

Vi Ă„tervĂ€nder till lottery-test-suite.fif och vi lĂ€gger till det andra testet. Om vi ​​skickar ett ogiltigt nummer bör koden ge ett undantag. LĂ„t oss till exempel sĂ€ga att kontraktsdata lagrar 166 och vi skickar 165.

<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"

LÄt oss starta.

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

Och vi kommer att se att testet utförs med ett fel.

[ 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

I detta skede lottery-test-suite.fif ska se ut ĐżĐŸ ссылĐșĐ”.

LÄt oss nu lÀgga till motlogiken till det smarta kontraktet 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 ligger budskapet vi skickar.

Det första vi gör Àr att kontrollera om det finns data i meddelandet, om inte sÄ avslutar vi bara.

DÀrefter analyserar vi meddelandet. in_msg~load_uint(32) laddar numret 165, 32-bitars unsigned int frÄn det överförda meddelandet.

DÀrefter laddar vi 32 bitar frÄn den smarta kontraktslagringen. Vi kontrollerar att det laddade numret matchar det överförda, om inte gör vi ett undantag. I vÄrt fall, eftersom vi passerar en missmatchning, bör ett undantag göras.

LÄt oss nu kompilera.

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

Kopiera den resulterande koden till lottery-test-suite.fif, glöm inte att ersÀtta den sista raden.

LÄt oss kontrollera att testet klarar:

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

Precis hÀr Du kan se motsvarande commit med de aktuella resultaten.

Observera att det Àr obekvÀmt att stÀndigt kopiera den kompilerade koden för det smarta kontraktet till filen med tester, sÄ vi kommer att skriva ett skript som skriver koden till en konstant för oss, och vi kommer helt enkelt att koppla den kompilerade koden till vÄra tester med hjÀlp av "include".

Skapa en fil i projektmappen build.sh med följande innehÄll.

#!/bin/bash

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

LÄt oss göra det körbart.

chmod +x ./build.sh

Nu rÀcker det med att köra vÄrt skript för att kompilera kontraktet. Men förutom detta mÄste vi skriva in det i en konstant code. SÄ vi kommer att skapa en ny fil lotter-compiled-for-test.fif, som vi kommer att inkludera i filen lottery-test-suite.fif.

LÄt oss lÀgga till lite kod till sh-skriptet som helt enkelt kommer att duplicera den kompilerade filen in lotter-compiled-for-test.fif och Àndra den sista raden i den.

# 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

Nu, för att kontrollera, lÄt oss köra det resulterande skriptet och en fil kommer att genereras lottery-compiled-for-test.fif, som vi kommer att ta med i vÄr lottery-test-suite.fif

В lottery-test-suite.fif radera kontraktskoden och lĂ€gg till en rad "lottery-compiled-for-test.fif" include.

Vi kör testerna för att kontrollera att de klarar.

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

Bra, nu skapar vi en fil för att automatisera körningen av tester test.sh, som först kommer att upptrÀda build.sh, och kör sedan testerna.

touch test.sh
chmod +x test.sh

Vi skriver inuti

./build.sh 

echo "nCompilation completedn"

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

LÄt oss göra test.sh och kör det för att se till att testerna fungerar.

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

Vi kontrollerar att kontraktet sammanstÀlls och att testerna körs.

Bra, nu pÄ start test.sh testerna kommer att kompileras och köras omedelbart. HÀr Àr lÀnken till begÄ.

Okej, innan vi fortsÀtter, lÄt oss göra en sak till för bekvÀmlighets skull.

LÄt oss skapa en mapp build dÀr vi kommer att lagra det kompilerade kontraktet och dess klon skriven till konstanten lottery-compiled.fif, lottery-compiled-for-test.fif. Vi kommer ocksÄ att skapa en mapp test dÀr testfilen kommer att lagras lottery-test-suite.fif och eventuellt andra stödfiler. LÀnk till relevanta Àndringar.

LÄt oss fortsÀtta utveckla det smarta kontraktet.

HÀrnÀst ska det vara ett test som kontrollerar att meddelandet tas emot och rÀknaren Àr uppdaterad i butiken nÀr vi skickar rÀtt nummer. Men det gör vi senare.

LÄt oss nu fundera pÄ vilken datastruktur och vilken data som behöver lagras i ett smart kontrakt.

Jag kommer att beskriva allt som vi lagrar.

`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` ĐżĐ”Ń€Đ”ĐŒĐ”ĐœĐœĐ°Ń топа ŃĐ»ĐŸĐČарь, Ń…Ń€Đ°ĐœĐžŃ‚ ĐżĐŸŃĐ»Đ”ĐŽĐœĐžĐ” ĐŽĐČаЮцать стаĐČĐŸĐș. 

DÀrefter mÄste du skriva tvÄ funktioner. Vi ringer den första pack_state(), som kommer att paketera data för efterföljande lagring i den smarta kontraktslagringen. Den andra ska vi kalla den unpack_state() kommer att lÀsa och returnera data frÄn lagring.

_ 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;
}

Vi lÀgger till dessa tvÄ funktioner i början av det smarta kontraktet. Det kommer att lösa sig sÄ hÀr mellanresultat.

För att spara data mÄste du anropa den inbyggda funktionen set_data() och det kommer att spela in data frÄn pack_state() i den smarta kontraktslagringen.

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

Nu nÀr vi har bekvÀma funktioner för att skriva och lÀsa data kan vi gÄ vidare.

Vi mÄste kontrollera att det inkommande meddelandet utifrÄn Àr undertecknat av kontraktets Àgare (eller annan anvÀndare som har tillgÄng till den privata nyckeln).

NÀr vi publicerar ett smart kontrakt kan vi initialisera det med den data vi behöver i lagringen, som kommer att sparas för framtida bruk. Vi kommer att skriva den publika nyckeln dÀr sÄ att vi kan verifiera att signaturen för det inkommande meddelandet gjordes med motsvarande privata nyckel.

Innan vi fortsÀtter, lÄt oss skapa en privat nyckel och skriva ner den test/keys/owner.pk. För att göra detta, lÄt oss köra Fift i interaktivt lÀge och köra fyra kommandon.

`newkeypair` ĐłĐ”ĐœĐ”Ń€Đ°Ń†ĐžŃ ĐżŃƒĐ±Đ»ĐžŃ‡ĐœĐŸĐłĐŸ Đž проĐČĐ°Ń‚ĐœĐŸĐłĐŸ Đșлюча Đž Đ·Đ°ĐżĐžŃŃŒ ох ĐČ ŃŃ‚Đ”Đș. 

`drop` ŃƒĐŽĐ°Đ»Đ”ĐœĐžŃ Оз стДĐșа ĐČĐ”Ń€Ń…ĐœĐ”ĐłĐŸ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Đ° (ĐČ ĐŽĐ°ĐœĐœĐŸĐŒ ŃĐ»ŃƒŃ‡Đ°Đ” ĐżŃƒĐ±Đ»ĐžŃ‡ĐœŃ‹Đč Đșлюч)  

`.s` ĐżŃ€ĐŸŃŃ‚ĐŸ ĐżĐŸŃĐŒĐŸŃ‚Ń€Đ”Ń‚ŃŒ Ń‡Ń‚ĐŸ лДжОт ĐČ ŃŃ‚Đ”ĐșĐ” ĐČ ĐŽĐ°ĐœĐœŃ‹Đč ĐŒĐŸĐŒĐ”ĐœŃ‚ 

`"owner.pk" B>file` Đ·Đ°ĐżĐžŃŃŒ проĐČĐ°Ń‚ĐœĐŸĐłĐŸ Đșлюча ĐČ Ń„Đ°ĐčĐ» с ĐžĐŒĐ”ĐœĐ”ĐŒ `owner.pk`. 

`bye` заĐČĐ”Ń€ŃˆĐ°Đ”Ń‚ Ń€Đ°Đ±ĐŸŃ‚Ńƒ с Fift. 

LÄt oss skapa en mapp keys inuti mappen test och vi skriver den privata nyckeln dÀr.

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

Vi ser filen i den aktuella mappen owner.pk.

Vi tar bort den publika nyckeln frÄn stacken och kan hÀmta den frÄn den privata vid behov.

Nu mÄste vi skriva en signaturkontroll. LÄt oss börja med testet. Först lÀser vi den privata nyckeln frÄn filen med hjÀlp av funktionen file>B och skriv det i en variabel owner_private_keyoch sedan anvÀnda funktionen priv>pub konvertera den privata nyckeln till en offentlig nyckel och skriv resultatet till 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 !

Vi kommer att behöva bÄda nycklarna.

Initiera den smarta kontraktslagringen med godtyckliga data i samma sekvens som i funktionen pack_state()och skriv det i en variabel 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 !

DÀrefter kommer vi att komponera ett signerat meddelande, det kommer bara att innehÄlla signaturen och rÀknarvÀrdet.

Först skapar vi den data vi vill överföra, sedan signerar vi den med en privat nyckel och slutligen bildar vi ett signerat meddelande.

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 !  

Som ett resultat skrivs meddelandet som vi skickar till det smarta kontraktet in i en variabel message_to_send, om funktioner hashu, ed25519_sign_uint du kan lÀsa i Fift-dokumentationen.

Och för att köra testet kallar vi det igen.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

HÀr sÄ testfilen ska se ut sÄ hÀr i detta skede.

LÄt oss köra testet och det kommer att misslyckas, sÄ lÄt oss Àndra det smarta kontraktet sÄ att det kan ta emot meddelanden i detta format och kontrollera signaturen.

Först lÀser vi 512 bitar av signaturen frÄn meddelandet och skriver dem till en variabel, sedan lÀser vi 32 bitar av rÀknarvariabeln.

Eftersom vi har en funktion för att lÀsa data frÄn den smarta kontraktslagringen kommer vi att anvÀnda den.

Kontrollera sedan rÀknaren som överförts med lagringen och kontrollera signaturen. Om nÄgot inte matchar, slÀnger vi ett undantag med motsvarande kod.

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));

Motsvarande Ätagande just hÀr.

LÄt oss köra testerna och se att det andra testet misslyckas. Av tvÄ skÀl, inte tillrÀckligt med bitar i meddelandet och inte tillrÀckligt med bitar i lagringen, sÄ koden kraschar vid analys. Vi mÄste lÀgga till en signatur i meddelandet vi skickar och kopiera lagringen frÄn det senaste testet.

I det andra testet kommer vi att lÀgga till en meddelandesignatur och Àndra den smarta kontraktslagringen. HÀr sÄ sÄ hÀr ser testfilen ut för tillfÀllet.

LÄt oss skriva ett fjÀrde test dÀr vi skickar ett meddelande signerat med nÄgon annans privata nyckel. LÄt oss skapa en annan privat nyckel och spara den i en fil not-owner.pk. Vi kommer att signera meddelandet med denna privata nyckel. LÄt oss köra testerna och se till att alla tester klarar. BegÄ just nu.

Nu kan vi Àntligen gÄ vidare till att implementera den smarta kontraktslogiken.
В recv_external() Vi accepterar tvĂ„ typer av meddelanden.

Eftersom vÄrt kontrakt kommer att ackumulera spelarnas förluster mÄste dessa pengar överföras till skaparen av lotteriet. Lotteriskaparens plÄnboksadress skrivs in i förrÄdet nÀr kontraktet skapas.

För sÀkerhets skull behöver vi möjligheten att Àndra adressen som vi ska skicka förlorarnas gram till. Vi ska Àven kunna skicka lottogram till Àgarens adress.

LÄt oss börja med den första. LÄt oss först skriva ett test som kontrollerar att det smarta kontraktet efter att ha skickat ett meddelande har sparat den nya adressen i lagringen. Observera att vi förutom disken och den nya adressen Àven sÀnder i meddelandet action 7-bitars icke-negativt heltal, beroende pÄ det kommer vi att vÀlja hur vi ska behandla meddelandet i det smarta kontraktet.

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

I testet kan du se hur deserialiseringen av den smarta kontraktslagringen sker storage i femte. Deserialisering av variabler beskrivs i Fift-dokumentationen.

LÀnk för att begÄ med tillsats av deg.

LÄt oss köra testet och se till att det misslyckas. LÄt oss nu lÀgga till logik för att Àndra lotteriÀgarens adress.

I det smarta kontraktet fortsÀtter vi att analysera message, lÀser vi in action. LÄt oss pÄminna dig om att vi kommer att ha tvÄ action: adressÀndring och skicka gram.

Sedan lÀser vi den nya adressen till kontraktsÀgaren och sparar den i förrÄdet.
Vi kör testerna och ser att det tredje testet misslyckas. Det kraschar eftersom kontraktet nu dessutom analyserar 7 bitar frÄn meddelandet, som saknas i testet. LÄt oss lÀgga till en icke-existerande i meddelandet action. LÄt oss köra testerna och se att de alla klarar sig. HÀr Äta sig förÀndringar. Stor.

LÄt oss nu skriva logiken för att skicka det angivna antalet gram till den tidigare sparade adressen.

LÄt oss skriva ett test först. Vi kommer att skriva tvÄ test, ett nÀr balansen inte rÀcker till, det andra nÀr allt ska gÄ bra. Testerna kan ses i detta Ätagande.

LÄt oss nu skriva klart koden. LÄt oss först skriva tvÄ hjÀlpmetoder. Den första get-metoden Àr att ta reda pÄ det aktuella saldot för ett smart kontrakt.

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

Och den andra Àr för att skicka gram till ett annat smart kontrakt. Jag kopierade den hÀr metoden helt frÄn ett annat smart kontrakt.

() 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
}

LÄt oss lÀgga till dessa tvÄ metoder till det smarta kontraktet och skriva logiken. Först analyserar vi antalet gram frÄn meddelandet. DÀrefter kontrollerar vi saldot, om det inte finns tillrÀckligt gör vi ett undantag. Om allt Àr bra skickar vi grammen till den sparade adressen och uppdaterar rÀknaren.

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));

HÀr sÄ hur ett smart kontrakt ser ut för tillfÀllet. LÄt oss köra testerna och se till att de blir godkÀnda.

Förresten, en provision debiteras det smarta kontraktet varje gÄng ett meddelande behandlas. För att det smarta kontraktet ska uppfylla begÀran mÄste du efter grundlÀggande kontroller ringa accept_message().

LÄt oss nu gÄ vidare till interna meddelanden. Faktum Àr att vi bara accepterar gram och skickar tillbaka dubbla beloppet till spelaren om han vinner och en tredjedel till Àgaren om han förlorar.

LÄt oss först skriva ett enkelt test. För att göra detta behöver vi en testadress för det smarta kontraktet frÄn vilket vi skickar gram till det smarta kontraktet.

Den smarta kontraktsadressen bestÄr av tvÄ siffror, ett 32-bitars heltal som ansvarar för arbetskedjan och ett 256-bitars icke-negativt heltals unikt kontonummer i denna arbetskedja. Till exempel, -1 och 12345, kommer vi att spara denna adress i en fil.

Jag kopierade funktionen för att spara adressen frÄn 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

LÄt oss ta en titt pÄ hur funktionen fungerar, detta kommer att ge oss en förstÄelse för hur Fift fungerar. LÄt oss starta Fift i interaktivt lÀge.

~/TON/build/crypto/fift -i 

Först lÀgger vi -1, 12345 och namnet pÄ den framtida filen "sender.addr" i stacken:

-1 12345 "sender.addr" 

NÀsta steg Àr att utföra funktionen -rot, som skiftar stacken sÄ att det unika smarta kontraktsnumret Àr överst i stacken:

"sender.addr" -1 12345

256 u>B Konverterar ett 256-bitars icke-negativt heltal till byte.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap byter ut de tvÄ översta elementen i stacken.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B Konverterar ett 32-bitars heltal till byte.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ sammanfogar tvÄ sekvenser av byte.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

igen swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Och slutligen skrivs byten till filen. B>file. Efter detta Àr vÄr stack tom. Vi slutar Fift. En fil har skapats i den aktuella mappen. sender.addr. LÄt oss flytta filen till den skapade mappen test/addresses/.

LÄt oss skriva ett enkelt test som skickar gram till ett smart kontrakt. HÀr Àr engagemanget.

LÄt oss nu gÄ ner till lotteriets logik.

Det första vi gör Àr att kontrollera meddelandet bounced eller inte, om bounced, dÄ ignorerar vi det. bounced innebÀr att kontraktet kommer att returnera gram om nÄgot fel uppstÄr. Vi returnerar inte gram om ett fel uppstÄr.

Vi kontrollerar, om saldot Àr mindre Àn ett halvt gram, accepterar vi helt enkelt meddelandet och ignorerar det.

DÀrefter analyserar vi adressen till det smarta kontraktet som meddelandet kom frÄn.

Vi lÀser data frÄn lagringen och raderar sedan gamla insatser frÄn historiken om det finns fler Àn tjugo av dem. För enkelhetens skull skrev jag ytterligare tre funktioner pack_order(), unpack_order(), remove_old_orders().

DÀrefter tittar vi, om saldot inte rÀcker för betalningen, anser vi att detta inte Àr en satsning, utan en pÄfyllning och vi sparar pÄfyllningen i orders.

Nu, Àntligen, kÀrnan i ett smart kontrakt.

Först, om spelaren förlorar, sparar vi det i spelhistoriken och om beloppet Àr mer Àn 3 gram skickar vi 1/3 till Àgaren av det smarta kontraktet.

Om spelaren vinner skickar vi det dubbla beloppet till spelarens adress och sparar sedan insatsinformationen i historiken.

() 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));
}

Det var allt. Motsvarande Ätagande.

Nu Àr det enkelt: lÄt oss skapa get-metoder sÄ att vi kan fÄ information om kontraktets tillstÄnd frÄn omvÀrlden (lÀs faktiskt data frÄn den smarta kontraktslagringen).

LÄt oss lÀgga till get-metoder. Vi kommer att skriva nedan om hur du fÄr information om ett smart kontrakt.

Jag glömde ocksÄ lÀgga till koden som ska hantera den allra första förfrÄgan som uppstÄr nÀr det smarta kontraktet publiceras. Motsvarande Ätagande. Och en sak till rÀttad bugg med att skicka 1/3 av beloppet till Àgarens konto.

Allt som ÄterstÄr Àr att publicera det smarta kontraktet. LÄt oss skapa en mapp requests.

Jag tog publiceringskoden som grund simple-wallet-code.fc ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč kan hittas i det officiella arkivet.

HÀr Àr nÄgra saker vÀrda att uppmÀrksamma. Vi bildar en smart avtalslagring och ett ingÄngsmeddelande. Efter detta genereras den smarta kontraktsadressen, det vill sÀga adressen Àr kÀnd redan innan publicering i TON. DÀrefter mÄste du skicka nÄgra gram till den hÀr adressen och först efter det behöver du skicka filen med sjÀlva det smarta kontraktet, eftersom nÀtverket tar en provision för att lagra det smarta kontraktet och operationer i det (validatorer som lagrar och utför smarta kontrakt). Koden kan ses hÀr.

DÀrefter kör vi publiceringskoden och hÀmtar lottery-query.boc fil och adress för det smarta kontraktet.

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

Glöm inte att spara de genererade filerna: lottery-query.boc, lottery.addr, lottery.pk.

Vi kommer bland annat att se den smarta kontraktsadressen i exekveringsloggarna.

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

LÄt oss för skojs skull göra en förfrÄgan till TON

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

Och vi kommer att se att kontot med den hÀr adressen Àr tomt.

account state is empty

Vi skickar till adressen 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 gram och efter nÄgra sekunder kör vi samma kommando. För att skicka gram anvÀnder jag officiell plÄnbok, och du kan be om testgram frÄn nÄgon frÄn chatten, vilket jag kommer att prata om i slutet av artikeln.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Ser ut som en oinitierad ( har dykt upp pÄ nÀtverketstate:account_uninit) ett smart kontrakt med en sÄdan adress och ett saldo pÄ 1 000 000 000 nanogram.

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

LÄt oss nu publicera det smarta kontraktet. LÄt oss starta lite-klient och köra den.

> 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 

LÄt oss kontrollera att kontraktet Àr publicerat.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Vi ska bland annat ta emot.

  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

Vi ser det account_active.

Motsvarande Ätagande med Àndringar just hÀr.

LÄt oss nu skapa förfrÄgningar för att interagera med det smarta kontraktet.

NÀrmare bestÀmt kommer vi att lÀmna den första för att Àndra adressen som ett oberoende verk, och vi kommer att göra den andra för att skicka gram till Àgarens adress. Faktum Àr att vi mÄste göra samma sak som i testet för att skicka gram.

Detta Àr meddelandet vi kommer att skicka till det smarta kontraktet, dÀr msg_seqno 165 action 2 och 9.5 gram för frakt.

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

Glöm inte att signera meddelandet med en privat nyckel lottery.pk, som genererades tidigare nÀr det smarta kontraktet skapades. HÀr Àr motsvarande kommitté.

FÄ information frÄn ett smart kontrakt med get-metoder

LÄt oss nu titta pÄ hur man kör smarta kontrakt fÄ metoder.

Vi lanserar lite-client och kör get-metoderna vi skrev.

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

В result innehĂ„ller vĂ€rdet som funktionen returnerar balance() frĂ„n vĂ„rt smarta kontrakt.
Vi kommer att göra detsamma för flera fler metoder.

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

LÄt oss begÀra vadslagningshistoriken.

> 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]) ] 

Vi kommer att anvÀnda lite-klient och get-metoder för att visa information om det smarta kontraktet pÄ sajten.

Visar smart kontraktsdata pÄ webbplatsen

Jag skrev en enkel Python-webbplats för att visa smarta kontraktsdata pÄ ett bekvÀmt sÀtt. Jag kommer inte att gÄ in i detalj om det hÀr och kommer att publicera sidan i ett Ätagande.

FörfrÄgningar till TON görs frÄn Python via lite-client. För enkelhetens skull Àr webbplatsen förpackad i Docker och publicerad pÄ Google Cloud. LÀnk.

LÄt oss försöka

LÄt oss nu försöka skicka gram dit att fylla pÄ frÄn handvÀska. Vi skickar 40 gram. Och lÄt oss göra ett par satsningar för tydlighetens skull. Vi ser att sidan visar historiken för vad, den aktuella vinstprocenten och annan anvÀndbar information.

Vi ser, att vi vann den första och förlorade den andra.

efterordet

Artikeln visade sig vara mycket lÀngre Àn jag förvÀntade mig, kanske kunde den ha varit kortare, eller sÄ kanske den Àr helt rÀtt för en person som inte vet nÄgot om TON och vill skriva och publicera ett inte sÄ enkelt smart kontrakt med möjligheten att interagera med det. Vissa saker kunde kanske ha förklarats enklare.

Kanske kunde vissa aspekter av implementeringen ha gjorts mer effektivt och elegant, men dÄ hade det tagit Ànnu mer tid att förbereda artikeln. Det Àr ocksÄ möjligt att jag gjorde ett misstag nÄgonstans eller inte förstod nÄgot, sÄ om du gör nÄgot allvarligt mÄste du lita pÄ den officiella dokumentationen eller det officiella förrÄdet med TON-koden.

Det bör noteras att eftersom TON sjÀlv fortfarande Àr i det aktiva utvecklingsstadiet kan förÀndringar intrÀffa som kommer att bryta nÄgot av stegen i den hÀr artikeln (vilket Àr vad som hÀnde medan jag skrev, jag har redan korrigerat det), men det generella tillvÀgagÄngssÀttet kommer sannolikt inte att förÀndras.

Jag kommer inte att prata om framtiden för TON. Kanske kommer plattformen att bli nÄgot större och vi borde lÀgga tid pÄ att studera den och ockupera nischen med vÄra produkter nu.

Det finns ocksĂ„ Libra frĂ„n Facebook, som har en potentiell publik av anvĂ€ndare större Ă€n TON. Jag vet nĂ€stan ingenting om VĂ„gen, av forumet att döma Ă€r det mycket mer aktivitet dĂ€r Ă€n i TON-communityt. Även om utvecklarna och TON-communityt Ă€r mer som underground, vilket ocksĂ„ Ă€r coolt.

referenser

  1. Officiell TON-dokumentation: https://test.ton.org
  2. Officiellt TON-förrÄd: https://github.com/ton-blockchain/ton
  3. Officiell plÄnbok för olika plattformar: https://wallet.ton.org
  4. Smart kontraktsförrÄd frÄn denna artikel: https://github.com/raiym/astonished
  5. LÀnk till webbplatsen för smart kontrakt: https://ton-lottery.appspot.com
  6. Visual Studio Code tillÀggsförrÄd för FunC: https://github.com/raiym/func-visual-studio-plugin
  7. En chatt om TON i Telegram, som verkligen hjÀlpte mig att ta reda pÄ det i det inledande skedet. Jag tror att det inte kommer att vara ett misstag om jag sÀger att alla som skrivit nÄgot för TON Àr dÀr. Du kan ocksÄ be om provgram dÀr. https://t.me/tondev_ru
  8. En annan chatt om TON dÀr jag hittade anvÀndbar information: https://t.me/TONgramDev
  9. Första etappen av tÀvlingen: https://contest.com/blockchain
  10. Andra etappen av tÀvlingen: https://contest.com/blockchain-2

KĂ€lla: will.com

Köp pĂ„litlig hosting för webbplatser med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster