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

SĂ„ skriver och publicerar du ett smart kontrakt i TON

Vad handlar denna artikel om?

I artikeln kommer jag att berÀtta om hur jag deltog i den första (av tvÄ) Telegram-tÀvlingen om blockchain, jag erhöll ingen prisplats och beslutade att dokumentera min erfarenhet i artikeln sÄ att den inte skulle gÄ förlorad och kanske hjÀlpa nÄgon.

Eftersom jag inte ville skriva en abstrakt kod utan göra nÄgot fungerande, skrev jag för artikeln ett smart kontrakt för en snabblott och en webbplats som visar data frÄn det smarta kontraktet direkt frÄn TON utan att anvÀnda mellanlagringslösningar.

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

Med hjÀlp av lotteriet kommer jag att gÄ igenom allt frÄn installation av miljön till publicering av det smarta kontraktet, interaktion med det och skriva en webbplats för att hÀmta och publicera data.

Om deltagande i tÀvlingen

I oktober förra Äret tillkÀnnagav Telegram en blockchain-tÀvling med nya sprÄk Fift och FunC. Man behövde vÀlja och skriva nÄgot av de fem föreslagna smarta kontrakten. Jag ansÄg att det skulle vara kul att syssla med nÄgot ovanligt, lÀra mig sprÄket och göra nÄgot, Àven om jag inte skulle behöva skriva nÄgot mer i framtiden. Dessutom Àr Àmnet stÀndigt aktuellt.

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

Jag planerade att delta Ànda tills det inte gick lÀngre och skriva en översiktsartikel efterÄt, men jag misslyckades direkt i början. Jag skrev en plÄnbok med flera signaturer pÄ FunC och den fungerade generellt. Jag tog ett smart kontrakt pÄ Solidity som grund..

Vid den tiden ansÄg jag att detta definitivt skulle rÀcka för att Ätminstone fÄ nÄgon prisplats. Slutligen blev cirka 40 av 60 deltagare pristagare och jag var inte bland dem. I allmÀnhet Àr det inget att oroa sig för, men en sak gjorde mig nervös. Vid tidpunkten för resultatets tillkÀnnagivande hade ingen granskning med test av mitt kontrakt gjorts, jag frÄgade deltagarna i chatten om det fanns nÄgon annan som inte hade det, men det fanns det inte.

Tydligen, efter att ha uppmĂ€rksammat mina meddelanden, publicerade domarna en kommentar efter tvĂ„ dagar och jag förstod aldrig om de av misstag hade missat mitt smarta kontrakt under bedömningen eller bara tyckte att det var sĂ„ dĂ„ligt att det inte behövde nĂ„gon kommentar. Jag stĂ€llde en frĂ„ga pĂ„ sidan, men fick inget svar. Även om vem som bedömde — Ă€r ingen hemlighet, tyckte jag att det var överflödigt att skriva privata meddelanden.

Mycket tid har lagts pÄ förstÄelsen, sÄ det beslutades att skriva en artikel. Eftersom informationen fortfarande Àr ganska begrÀnsad, kommer artikeln att hjÀlpa alla intresserade att spara tid.

Konceptet för smarta kontrakt i TON

Innan man skriver nÄgot mÄste man förstÄ hur man ska angripa denna sak. DÀrför kommer jag nu att berÀtta vilka delar systemet bestÄr av. NÀrmare bestÀmt vilka delar man behöver veta för att skriva Ätminstone ett fungerande kontrakt.

Vi kommer att fokusera pÄ att skriva ett smart kontrakt och arbeta med TON Virtual Machine (TVM), Fift och FunC, sÄ artikeln liknar mer en beskrivning av utvecklingen av ett vanligt program. Vi kommer inte att stanna vid hur sjÀlva plattformen fungerar.

Generellt sett finns det en bra officiell dokumentation om hur TVM och sprÄket Fift Àr utformad. Under deltagandet i tÀvlingen och nu under skrivandet av det aktuella kontraktet har jag ofta refererat till den.

Det huvudsakliga sprÄket för att skriva smarta kontrakt Àr FunC. För tillfÀllet finns det ingen dokumentation, sÄ för att skriva nÄgot mÄste man studera exempel pÄ smarta kontrakt frÄn det officiella arkivet och sjÀlva implementationen av sprÄket dÀr, plus man kan titta pÄ exempel pÄ smarta kontrakt frÄn de tvÄ senaste tÀvlingarna. LÀnkar finns i slutet av artikeln.

Anta att vi redan har skrivit ett smart kontrakt i FunC, efter detta kompilerar vi koden till Fift-assembler.

Det kompilerade smarta kontraktet ÄterstÄr att publicera. För detta behöver man skriva en funktion i Fift, som tar in koden för det smarta kontraktet och nÄgra ytterligare parametrar, och som ger ut en fil med tillÀgget .boc (vilket betyder "bag of cells"), och beroende pÄ hur vi skriver, den privata nyckeln och adressen som genereras baserat pÄ koden för det smarta kontraktet. Till adressen för det smarta kontraktet, som Ànnu inte har publicerats, kan man redan skicka gram.

För att publicera det smarta kontraktet i TON mÄste den erhÄllna .boc filen skickas till blockchainen med hjÀlp av en lÀttklient (om detta nedan). Men innan publiceringen mÄste man överföra gram till den genererade adressen, annars kommer det smarta kontraktet inte att publiceras. Efter publiceringen kan man interagera med det smarta kontraktet genom att skicka meddelanden till det utifrÄn (till exempel via en lÀttklient) eller inifrÄn (till exempel att ett smart kontrakt skickar ett meddelande till ett annat inom TON).

NÀr vi vÀl förstod hur koden publiceras blir det enklare. Vi har en ungefÀrlig idé om vad vi vill skriva och hur vÄrt program kommer att fungera. Under skrivprocessen söker vi efter hur detta redan har implementerats i befintliga smarta kontrakt, eller kollar i implementationskoden. Fift och FunC i det officiella arkivet, eller ser pÄ den officiella dokumentationen.

Jag har ofta sökt efter nyckelord i Telegram-chatten dÀr alla deltagare i tÀvlingen och anstÀllda pÄ Telegram var samlade. Det visade sig att under tÀvlingen samlades alla dÀr för att diskutera Fift och FunC. LÀnken finns i slutet av artikeln.

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

Förberedelse av miljön för att arbeta med TON.

Allt som beskrivs i artikeln utförde jag pÄ MacOS och verifierade Àven i en ren Ubuntu 18.04 LTS pÄ Docker.

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

Instruktionen pÄ den officiella webbplatsen beskriver installationsprocessen ganska detaljerat och hoppar över vissa detaljer. HÀr följer vi instruktionerna samtidigt som vi installerar de saknade beroendena. Jag valde att inte sjÀlv kompilera varje projekt utan installerade frÄn det officiella Ubuntu-arkivet (pÄ MacOS anvÀnde jag 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 vi installera. lite-client, Fift, FunC.

Först klonar vi TON-arkivet tillsammans med dess beroenden. För smidighetens skull gör vi allt i mappen ~\/TON.

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

I arkivet finns ocksÄ implementationer. Fift och FunC.

Nu Àr vi redo att bygga projektet. Koden frÄn arkivet har klonats till mappen ~\/TON\/ton. I ~\/TON vi skapar en mapp build och bygger projektet i den.

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

Eftersom vi planerar att skriva ett smart kontrakt behöver vi inte bara lite-clientoch Àven Fift med FunC, sÄ vi kompilerar allt. Det Àr inte en snabb process, sÄ vi vÀntar.

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

DÀrefter laddar vi ner konfigurationsfilen som innehÄller information om noden som lite-client kommer att kopplas upp.

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

Vi gör vÄra första förfrÄgningar till TON.

Nu startar vi. lite-client.

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

Om bygget lyckades kommer du efter att ha startat att se loggen för den lÀtta klientens anslutning 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 Àr tillgÀngliga.

help

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

lista över tillgÀngliga kommandon:
last    HÀmtar senaste blocket och statusinformation frÄn servern
sendfile  Laddar en serialiserad meddelande frÄn  och skickar det till servern
getaccount  [<block-id-ext>]  Laddar det mest aktuella tillstÄndet av angiven konto;  Àr i [<workchain>:]<hex-or-base64-addr> format
runmethod  [<block-id-ext>] <method-id> <params>...   Kör GET-metod <method-id> av konto <addr> med angivna parametrar

last hÀmtar det senaste skapade blocket frÄn servern.

sendfile  skickar en fil med meddelandet till TON, med denna kommandopubliceras smartkontrakt och förfrÄgningar till det.

getaccount  laddar det aktuella tillstÄndet av smartkontraktet med den angivna adressen.

runmethod  [<block-id-ext>] <method-id> <params>  kör GET-metoder av smartkontraktet. 

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

Implementering

Tanken

Som jag nÀmnde tidigare, det smartkontrakt som vi skriver Àr en lotteri.

Det Àr inte en lotteri dÀr du mÄste köpa en biljett och vÀnta en timme, dag eller mÄnad, utan en omedelbar dÀr anvÀndaren skickar gram till kontraktadressen N och fÄr omedelbart tillbaka 2 * N gram eller förlorar. Vi sÀtter vinstchanserna till omkring 40%. Om det inte finns tillrÀckligt med gram för utbetalning, kommer vi att betrakta transaktionen som en pÄfyllning.

Det Àr viktigt att satsningarna kan ses i realtid och pÄ ett bekvÀmt sÀtt sÄ att anvÀndaren omedelbart kan förstÄ om de vunnit eller förlorat. DÀrför behöver vi skapa en webbplats som visar satsningarna och resultaten direkt frÄn TON.

Skrivande av smartkontrakt

För enkelhetens skull har jag skapat syntaktisk markering för FunC, plugin kan hittas och installeras i Visual Studio Code-sökningen. Om nÄgon vill lÀgga till nÄgot sÄ har jag gjort pluginen tillgÀnglig offentligt. Tidigare har Àven nÄgon gjort en plugin för Fift, som ocksÄ kan hittas och installeras i VSC.

LÄt oss genast skapa ett repository dit vi kommer att commita mellanresultaten.

För att underlÀtta livet kommer vi att skriva smartkontraktet och testa lokalt tills det Àr klart. Endast efter det publicerar vi det i TON.

Smartkontraktet har tvÄ externa metoder som man kan anropa. Första, recv_external() denna funktion utförs nÀr en begÀran till kontraktet kommer frÄn den externa vÀrlden, alltsÄ inte frÄn TON, till exempel nÀr vi sjÀlva formar meddelandet och skickar det genom lite-client. Andra, recv_internal() det Àr nÀr nÄgon kontrakt inuti TON anropar vÄrt. I bÄda fallen kan parametrar skickas till funktionen.

LÄt oss börja med ett enkelt exempel som kommer att fungera nÀr det publiceras, men som inte har nÄgon funktionell belastning.

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

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

Det hÀr behöver förklaras vad som Àr slice. All data som lagras i TON Blockchain Àr en samling TVM cell eller helt enkelt cell, i en sÄdan cell kan man lagra upp till 1023 bit data och upp till 4 lÀnkar till andra celler.

TVM cell slice eller slice Àr en del av en befintlig cell anvÀnds för att parsa den, och senare kommer det att bli tydligt. Huvudsaken för oss Àr att vi kan skicka slice och beroende pÄ typ av meddelande hantera data i recv_external() eller recv_internal().

impure – ett nyckelord som anger att funktionen Ă€ndrar datan i smartkontraktet.

Vi kommer att spara kontraktskoden i lottery-code.fc och kompilera den.

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

VĂ€rdet av flaggorna kan kontrolleras med kommandot

~\/TON\/build\/crypto\/func -help

Vi har fÄtt en kompilerad Fift-assemblerkod i 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

Den kan köras lokalt, sÄ vi förbereder miljön.

Vi noterar att den första raden inkluderar Asm.fif, detta Àr kod skriven i Fift för Fift-assembler.

Eftersom vi vill köra och testa smartkontraktet lokalt skapar vi en fil lottery-test-suite.fif och kopierar dit den kompilerade koden, och byter ut sista raden som skriver smartkontraktkoden till en konstant code, för att senare skicka 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

Det verkar klart hittills, nu lÀgger vi till kod i samma fil som vi ska anvÀnda för att köra TVM.

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 \/\/ för att köra recv_internal() 
-1 constant recv_external \/\/ för att anropa recv_external()

I c7 vi skriver ner kontexten, det vill sÀga data som TVM kommer att köras med (eller nÀtverksstatus). Redan under tÀvlingen visade en av utvecklarna hur man skapar c7 och jag kopierade det. I denna artikel kan vi behöva Àndra pÄ rand_seed eftersom det pÄverkar genereringen av ett slumpmÀssigt tal och inte ska Àndras, sÄ kommer samma tal alltid att returneras.

recv_internal och recv_external konstanter med vÀrdena 0 och -1 kommer att svara för anropet av motsvarande funktioner i smartkontraktet.

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

Skapa en variabel lagring och skriv ett tomt vÀrde i den. cell, detta kommer att vara lagringsutrymmet för smartkontraktet.

meddelande detta Àr meddelandet som vi kommer att skicka till smartkontraktet utifrÄn. Vi kommer ocksÄ att göra det tomt för tillfÀllet.

variabel lagring 
<b b> lagring ! 

variabel meddelande 
<b b> meddelande ! 

Efter att vi har förberett konstanterna och variablerna startar vi TVM med kommandot runvmctx och överför de skapade parametrarna som indata.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

Resultatet blir en sÄdan hÀr intermediÀr kod pÄ Fift.

Nu kan vi köra den resulterande koden.

export FIFTPATH=~\/TON\/ton\/crypto\/fift\/lib \/\/ körs en gÄng för bekvÀmlighet 
~\/TON\/build\/crypto\/fift -s lottery-test-suite.fif 

Programmet ska fungera utan fel och i utskriften ser vi exekveringsloggen:

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

UtmÀrkt, vi har skrivit den första fungerande versionen av smartkontraktet.

Nu mÄste vi lÀgga till funktionalitet. Först ska vi hantera meddelandena som kommer frÄn den externa vÀrlden till recv_external()

Utvecklaren vÀljer sjÀlv meddelandeformatet som kontraktet kan ta emot.

Men vanligtvis,

  • För det första vill vi skydda vĂ„rt kontrakt frĂ„n den externa vĂ€rlden och se till att endast kontraktets Ă€gare kan skicka externa meddelanden till det.
  • För det andra, nĂ€r vi skickar ett giltigt meddelande till TON vill vi att detta intrĂ€ffar exakt en gĂ„ng och att smartkontraktet avvisar samma meddelande om det skickas igen.

DÀrför hanteras nÀstan alltid dessa tvÄ problem i varje kontrakt; eftersom vÄrt kontrakt tar emot externa meddelanden, mÄste vi ocksÄ ta hand om detta.

Vi ska göra det i omvÀnd ordning. Först löser vi problemet med upprepning: om kontraktet redan har mottagit ett sÄdant meddelande och behandlat det, kommer det inte att utföra det en andra gÄng. Och dÀrefter löser vi problemet med att endast en viss krets personer kan skicka meddelanden till smartkontraktet.

Det finns olika sÀtt att lösa problemet med Äterkommande meddelanden. Vi gör sÄ hÀr. I smartkontraktet initierar vi en rÀknare för mottagna meddelanden med ett ursprungligt vÀrde pÄ 0. I varje meddelande lÀgger vi till det aktuella vÀrdet pÄ rÀknaren i smartkontraktet. Om vÀrdet pÄ rÀknaren i meddelandet inte stÀmmer överens med vÀrdet i smartkontraktet kommer vi inte att bearbeta det, om det stÀmmer överens kommer vi att bearbeta det och öka rÀknaren i smartkontraktet med 1.

Vi ÄtervÀnder till lottery-test-suite.fif och lÀgger till ett andra test. Vi skickar ett felaktigt nummer, koden ska kasta ett undantag. LÄt oss sÀga att kontraktsdata lagrar 166, men vi skickar 165.

<b 166 32 u, b> lagring !
<b 165 32 u, b> meddelande !

meddelande @ 
recv_external 
kod 
lagring @ 
c7 
runvmctx

tappa 
exit_code ! 
."Utgångskod " exit_code @ . cr 
exit_code @ 33 - abort"Test #2 Godkänd"

Vi kör.

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

Och vi ser att testet körs med ett fel.

[ 1][t 0][1582283084.210902214][words.cpp:3046] lottery-test-suite.fif:67: abort": Test #2 Inte passerat
[ 1][t 0][1582283084.210941076][fift-main.cpp:196]      Fel vid tolkning av fil `lottery-test-suite.fif`: fel vid tolkning av inkluderad fil `lottery-test-suite.fif` : lottery-test-suite.fif:67: abort": Test #2 Inte passerat

I detta skede lottery-test-suite.fif borde se ut som via lÀnken.

Nu lÄt oss lÀgga till logiken för rÀknaren i smartkontraktet i 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);
}

I slice in_msg innehÄller meddelandet som vi skickar.

Det första vi gör Àr att kontrollera om det finns data i meddelandet; om inte, gÄr vi helt enkelt ut.

DÀrefter parser vi meddelandet. in_msg~load_uint(32) laddar talet 165, 32-bitars unsigned int frÄn det mottagna meddelandet.

DÀrefter laddar vi 32 bitar frÄn smartkontraktets lagring. Vi kontrollerar att det laddade talet stÀmmer överens med det skickade; om inte kastar vi ett undantag. I vÄrt fall, eftersom vi skickar nÄgot som inte stÀmmer, ska ett undantag kastas.

Nu kompilera vi.

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

Vi kopierar den resulterande koden till lottery-test-suite.fif, utan att glömma att ersÀtta sista raden.

Kontrollera att testet gÄr igenom:

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

HĂ€r kan du se den aktuella commit med resultaten.

Vi noterar att det Àr besvÀrligt att stÀndigt kopiera den kompilerade koden för smartkontraktet till testfilen, sÄ lÄt oss skriva ett skript som automatiskt skriver koden till en konstant för oss, och vi kopplar enkelt in den kompilerade koden i vÄra tester med hjÀlp av "include".

I projektmappen skapar vi filen 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

Vi gör den körbar.

chmod +x .\/build.sh

Nu behöver vi bara köra vÄrt skript för att kompilera kontraktet. Men bortsett frÄn det mÄste vi spara det i en konstant. code. DÀrför kommer vi att skapa en ny fil. lotter-compiled-for-test.fif, som vi kommer att inkludera i filen. lottery-test-suite.fif.

LÀgg till kod i sh-skriptet som helt enkelt duplicerar den kompilerade filen i lotter-compiled-for-test.fif och Àndrar 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, kör vi det resulterande skriptet och en fil kommer att genereras för oss. lottery-compiled-for-test.fif, som vi kommer att inkludera i vÄr lottery-test-suite.fif

I lottery-test-suite.fif tar bort kontraktskoden och lÀgger till raden "lottery-compiled-for-test.fif" include.

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

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

UtmÀrkt, nu för att automatisera körningen av testerna skapar vi filen test.sh, som först kommer att köra build.sh, och sedan starta testerna.

touch test.sh
chmod +x test.sh

Inuti skriver vi

.\/build.sh 

echo "nKompileringen avslutadn"

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öra för att sÀkerstÀlla att testerna fungerar.

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

Vi kontrollerar att kontraktet kompileras och att testerna körs.

UtmÀrkt, nu kommer kompilering och körning av tester att ske direkt. test.sh HÀr Àr lÀnken till commit.

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

Vi skapar en mapp build dÀr vi kommer att lagra det kompilerade kontraktet och dess klon sparad i en konstant. lottery-compiled.fif, lottery-compiled-for-test.fif. Vi skapar ocksÄ en mapp test dÀr filen med testerna kommer att lagras lottery-test-suite.fif och potentiellt andra hjÀlpfiler. LÀnk till relevanta Àndringar.

Vi fortsÀtter utvecklingen av smartkontraktet.

NÀsta steg bör vara ett test som kontrollerar att meddelandet tas emot och att rÀknaren uppdateras i lagret nÀr vi skickar rÀtt nummer. Men vi gör det senare.

Nu tÀnker vi pÄ vilken databasstruktur och vilka data som behöver sparas i smartkontraktet.

Jag kommer att beskriva allt vi sparar.

`seqno` 32-bit heltal som rÀknare. 

`pubkey` 256-bit heltal, den offentliga nyckeln vi kommer att anvÀnda för att verifiera signaturen av det externa meddelandet, som nÀmns nedan. 

`order_seqno` 32-bit heltal som lagrar rÀknaren för antalet bud. 

`number_of_wins` 32-bit heltal som lagrar antalet vinster. 

`incoming_amount` datatyp Gram (de första 4 biterna representerar lÀngden), lagrar det totala antalet gram som har skickats till kontraktet. 

`outgoing_amount` det totala antalet gram som har skickats till vinnarna. 

`owner_wc` nummer för arbetskedjan, 32-bit (i vissa fall anges det som 8-bit) heltal. För nÀrvarande finns det bara tvÄ -1 och 0. 

`owner_account_id` 256-bit heltal, adressen för kontraktet i den aktuella arbetskedjan. 

`orders` variabel av typ ordbok, lagrar de senaste tjugo buden. 

NÀsta steg Àr att skriva tvÄ funktioner. Den första kommer vi att kalla pack_state(), som kommer att paketera data för senare lagring i smartkontraktet. Den andra, vi kommer att kalla unpack_state() kommer att lÀsa och returnera data frÄn lagringen.

_ 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 smartkontraktet. Resultatet blir en sÄdan hÀr ett mellanresultat.

För att spara data behöver vi anropa den inbyggda funktionen set_data() och den kommer att spara data frÄn pack_state() till lagringen av smartkontraktet.

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 behöver kontrollera att det inkommande externa meddelandet Àr signerat av kontraktets Àgare (eller en annan anvÀndare som har tillgÄng till den privata nyckeln).

NÀr vi publicerar ett smart kontrakt kan vi initiera det med de data vi behöver i lagringen, som kommer att sparas för framtida anvÀndning. Vi kommer att skriva dit den offentliga nyckeln sÄ att vi kan verifiera att signaturen av det inkommande meddelandet skapades av den motsvarande privata nyckeln.

Innan vi fortsÀtter skapar vi en privat nyckel och skriver den i test/keys/owner.pk. För detta startar vi Fift i interaktivt lÀge och utför fyra kommandon.

`newkeypair` genererar en offentlig och privat nyckel och skriver dem i stacken. 

`drop` tar bort det översta elementet frÄn stacken (i detta fall den offentliga nyckeln)  

`.s` visar bara vad som ligger i stacken för tillfÀllet 

`"owner.pk" B>file` skriver den privata nyckeln till en fil med namnet `owner.pk`. 

`bye` avslutar arbetet med Fift. 

Vi skapar en mapp nycklar inuti mappen test och dit skriver vi den privata nyckeln.

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 i den aktuella mappen en fil owner.pk.

Vi tar bort den offentliga nyckeln frÄn stacken, nÀr vi behöver kan vi hÀmta den frÄn den privata.

Nu behöver vi skriva en signaturkontroll. Vi börjar med ett test. Först lÀser vi in den privata nyckeln frÄn filen med hjÀlp av funktionen file>B och skriver den till variabeln owner_private_key, sedan med hjÀlp av funktionen priv>pub konverterar vi den privata nyckeln till en offentlig och skriver resultatet i owner_public_key.

variabel owner_private_key
variabel owner_public_key 

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

BÄda nycklarna behövs.

Vi initierar lagringen av det smarta kontraktet med godtyckliga data i samma sekvens som i funktionen pack_state()och skriver till variabeln lagring.

variabel ägare_privata_nyckel
variabel ägare_offentliga_nyckel
variabel beställningar
variabel ägare_wc
variabel ägare_konto_id

"./nycklar/ägare.pk" fil&gt;B ägare_privata_nyckel !
ägare_privata_nyckel @ priv&gt;pub ägare_offentliga_nyckel !
dictnew beställningar !
0 ägare_wc !
0 ägare_konto_id !

<b 0 32 u, owner_public_key @ b, gram, owner_wc i, owner_account_id 256 orders dict, b> lagring !

DÀrefter kommer vi att skapa ett signerat meddelande som bara kommer att innehÄlla signaturen och vÀrdet av rÀknaren.

Först skapar vi datan som vi vill överföra, sedan signerar vi dem med den privata nyckeln och slutligen formulerar vi det signerade meddelandet.

variabel meddelande_att_signera
variabel meddelande_att_skicka
variabel signatur
<b 0 32 u, b> meddelande_att_signera !
meddelande_att_signera @ hashägare_privata_nyckel @ ed25519_signera_uint signatur !
<b signature @ b, 0 32 u, b> &lt;s !  

Resultatet, meddelandet som vi kommer att skicka till det smarta kontraktet, Àr skrivet i variabeln message_to_send, angÄende funktionerna hashu, ed25519_sign_uint kan lÀsas i dokumentationen för Fift.

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

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

SÄ hÀr bör filen med tester se ut vid detta stadium.

Vi kommer att köra testet och det kommer att misslyckas, sÄ vi Àndrar det smarta kontraktet sÄ att det kan ta emot meddelanden av detta format och verifiera signaturen.

Först lÀser vi 512 bitar av signaturen frÄn meddelandet och skriver det i en variabel, dÀrefter lÀser vi 32 bitar av rÀknarvariabeln.

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

Vidare verifierar vi rÀknaren som skickas med lagringen och kontrollerar signaturen. Om nÄgot inte stÀmmer, kastar 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 commit hÀr.

Vi kör testerna och ser att det andra testet misslyckas. Av tvÄ anledningar: brist pÄ bitar i meddelandet och brist pÄ bitar i lagringen, sÄ koden misslyckas vid parsing. Vi mÄste lÀgga till signaturen av meddelandet som vi skickar och kopiera lagringen frÄn den senaste testen.

I den andra testen lÀgger vi till signaturen av meddelandet och Àndrar smartkontraktets lagring. SÄ hÀr sÄ hÀr ser testfilen ut just nu.

LÄt oss skriva det fjÀrde testet, dÀr vi skickar ett meddelande som Àr signerat med en annan persons privata nyckel. Vi skapar ytterligare en privat nyckel och sparar den i en fil not-owner.pk. Vi signerar meddelandet med denna privata nyckel. Vi kör testerna och ser till att alla tester passerar. Commit just nu.

Nu kan vi Àntligen gÄ vidare till implementeringen av smartkontraktets logik.
I recv_external() Vi kommer att ta emot tvÄ typer av meddelanden.

Eftersom vÄrt kontrakt kommer att ackumulera spelarens förluster, mÄste dessa pengar överföras till skaparen av lotteriet. Adressen till skaparen av lotteriet skrivs in i lagringen nÀr kontraktet skapas.

För sÀkerhets skull behöver vi möjligheten att Àndra adressen dit vi ska skicka förlorade gram. Vi mÄste ocksÄ ha möjlighet att skicka gram frÄn lotteriet till Àgarens adress.

LÄt oss börja med den första. Vi skriver först ett test som kontrollerar att efter att meddelandet skickats har smartkontraktet sparat den nya adressen i lagringen. Vi noterar att vi förutom rÀknaren och den nya adressen ocksÄ skickar ÄtgÀrd ett 7-bitars heltal som Àr icke-negativt, beroende pÄ det kommer vi att vÀlja hur vi bearbetar meddelandet i smartkontraktet.

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

I testet kan vi se hur deserialisering av smartkontraktets lagring gÄr till. lagring I Fift. Deserialisering av variabler beskrivs i dokumentationen för Fift.

LĂ€nk till commit med en ny test.

Vi kör testet och ser till att det misslyckas. Nu lÀgger vi till logik för att Àndra Àgarens adress till lotteriet.

I smartkontraktet fortsÀtter vi att parsa meddelande, lÀser in ÄtgÀrd. LÄt oss pÄminna om att vi kommer att ha tvÄ ÄtgÀrd: Àndring av adress och överföring av gram.

Sedan lÀser vi in den nya Àgarens adress och sparar i lagringen.
Vi kör testerna och ser att det tredje testet misslyckas. Det misslyckas eftersom kontraktet nu ocksÄ parserar 7 bitar frÄn meddelandet som saknas i testet. LÄt oss lÀgga till ett icke-existerande ÄtgÀrd. Vi kör testerna och ser att alla gÄr igenom. HÀr commit för Àndringar. UtmÀrkt.

Nu skriver vi logik för att skicka det angivna antalet gram till den tidigare sparade adressen.

Först skriver vi ett test. Vi kommer att skriva tvÄ tester, ett nÀr saldot Àr otillrÀckligt och ett nÀr allt ska gÄ igenom. Tester kan ses i denna commit.

Nu skriver vi klart koden. Först ska vi skriva tvÄ hjÀlpfunktioner. Den första Àr en get-funktion för att fÄ veta det aktuella saldot av smartkontraktet.

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

Och den andra för att skicka gram till ett annat smartkontrakt. Den hÀr metoden har jag helt kopierat frÄn ett annat smartkontrakt.

() 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 <= formatindikator int_msg_info$0 
    ;;  .store_uint(1, 1) ;; 1 <= ihr avstÀngd
    ;;  .store_uint(1, 1) ;; 1 <= studs = true
    ;;  .store_uint(0, 1) ;; 0 <= studs = false
    ;;  .store_uint(4, 5)  ;; 00100 <= adressflaggor, anycast = false, 8-bit arbetskedja
        .store_uint (196, 9)
        .store_int(wc, 8)
        .store_uint(addr, 256)
        .store_grams(grams)
        .store_uint(0, 107) ;; 106 nollor +  0 som en indikator pÄ att det inte finns nÄgon cell med data.
        .end_cell(); 
    send_raw_message(msg, 3); ;; lÀge, 2 för att ignorera fel, 1 för att avsÀndaren betalar avgifter, 64 för att returnera vÀrdet av inkommande meddelande
}

Vi lÀgger till dessa tvÄ metoder i smartkontraktet och skriver logiken. Först parserar vi antalet gram frÄn meddelandet. Sedan kontrollerar vi saldot, om det inte rÀcker kastar vi ett undantag. Om allt gÄr bra, skickar vi gram 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));

SÄ hÀr ser smartkontraktet ut vid denna tidpunkt. Vi kör testerna och kontrollerar att de gÄr igenom.

För övrigt debiteras en avgift för varje bearbetat meddelande av smartkontraktet. För att meddelanden ska kunna utföra begÀran i smartkontraktet mÄste man efter de grundlÀggande kontrollerna kalla pÄ accept_message().

Nu ska vi hantera interna meddelanden. Faktum Àr att vi bara kommer att ta emot gram och skicka tillbaka dubbel summa till spelaren vid vinst och en tredjedel till Àgaren vid förlust.

LÄt oss börja med att skriva ett enkelt test. För detta behöver vi en testadress för smartkontraktet frÄn vilket vi skickar gram till smartkontraktet.

Adressen till smartkontraktet bestÄr av tvÄ nummer, ett 32-bitars heltal som representerar workchain och ett unikt 256-bitars positivt heltal som Àr kontonumret i denna workchain. Till exempel, -1 och 12345, den hÀr adressen sparar vi 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 gÄ igenom hur funktionen fungerar, detta kommer att ge en förstÄelse för hur Fift fungerar. Vi kör 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 köra funktionen -rot, som skiftar stacken sÄ att det unika numret för smartkontraktet hamnar högst upp i stacken:

"sender.addr" -1 12345

256 u>B konverterar det 256-bitars positiva heltalet till bytes.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap byter plats pÄ de tvÄ översta elementen i stacken.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B konverterar det 32-bitars heltalet till bytes.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ kombinerar tvÄ sekvenser av bytes.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

Återigen swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Och slutligen skrivs bytes till filen B>file. Efter detta Àr vÄr stack tom. Vi stoppar Fift. En fil har skapats i den aktuella mappen sender.addr. Vi flyttar filen till den skapade mappen test\/addresses\/.

Vi skriver ett enkelt test som ska skicka gram till smartkontraktet. HÀr Àr committen.

Nu ska vi fokusera pÄ logiken för lotteriet.

Det första vi gör Àr att kontrollera meddelandet bounced eller inte, om bounced, sÄ ignorerar vi det. bounced Det betyder att kontraktet kommer att returnera gram om det uppstÄr nÄgot fel. Vi kommer inte att ÄtervÀnda gram ifall ett fel skulle uppstÄ.

Vi kontrollerar balansen; om den Àr mindre Àn ett halvt gram accepterar vi helt enkelt meddelandet och ignorerar det.

Vi parser adressen för smartkontraktet frÄn vilket meddelandet kom.

Vi lÀser in data frÄn lagringen och tar bort gamla insatser frÄn historiken om det finns mer Àn tjugo. För att underlÀtta har jag skrivit tre ytterligare funktioner. pack_order(), unpack_order(), remove_old_orders().

Sedan kontrollerar vi om saldot inte rÀcker till för utbetalningen, dÄ betraktar vi det som en insÀttning och sparar insÀttningen i orders.

DÀrefter, Àntligen sjÀlva essensen av smartkontraktet.

Först, om spelaren förlorar sparar vi dem i historiken för insatser och om beloppet Àr större Àn 3 gram skickar vi 1/3 till Àgaren av smartkontraktet.

Om spelaren vinner skickar vi det dubbla beloppet till spelarens adress och sparar sedan informationen om insatsen 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) { ;; ignorera studsad
        return ();
    }
    if (order_amount < 500000000) { ;; bara ta emot gram utan att Àndra tillstÄnd 
        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() = 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. Den motsvarande commit.

Nu ÄterstÄr det enkla, vi skapar get-metoder sÄ att vi kan fÄ information om tillstÄndet för kontraktet frÄn den externa vÀrlden (i praktiken lÀsa data frÄn deras lagring i smartkontraktet).

LÄt oss lÀgga till get-metoder. Hur man fÄr information om smartkontraktet kommer vi att skriva om nedan.

Jag glömde ocksÄ att lÀgga till koden som kommer att hantera den allra första begÀran som görs vid publiceringen av smarta kontrakt. Den motsvarande commit. Och dessutom ÄtgÀrdat felet med att skicka 1/3 av summan till Àgarens konto.

För att gÄ vidare behöver vi publicera det smarta kontraktet. LÄt oss skapa en mapp requests.

Jag baserade koden för publiceringen pÄ simple-wallet-code.fc som gÄr det att hitta finns i det officiella arkivet.

Det Àr viktigt att notera att vi formar lagringen för det smarta kontraktet och meddelandet till ingÄngen. Efter det genereras adressen för det smarta kontraktet, vilket innebÀr att adressen Àr kÀnd redan innan publiceringen i TON. DÀrefter mÄste vi skicka nÄgra gram till denna adress och först efter det skicka filen med det smarta kontraktet, eftersom nÀtverket tar en avgift för lagring och operationer i det smarta kontraktet (validatorer som lagrar och utför smarta kontrakt). Koden kan ses hÀr.

Sedan kör vi publiceringskoden och fÄr lottery-query.boc filen och adressen till 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.

Bland annat i körningsloggarna ser vi adressen till det smarta kontraktet.

new wallet address = 0:044910149dbeaf8eadbb2b28722e7d6a2dc6e264ec2f1d9bebd6fb209079bc2a 
(Sparar adress till fil lottery.addr)
Icke-bounceable adress (för init): 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd
Bounceable adress (för senare Ätkomst): kQAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KpFY

Av intresse kommer vi att göra en begÀran till TON

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

Och vi ser att kontot med denna adress Àr tomt.

konto innehÄll Àr tomt

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 den officiella plÄnboken, och testgram kan man be om frÄn nÄgon i chatten, som jag kommer att nÀmna i slutet av artikeln.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

ser att det finns ett icke-initierat (state:account_uninit) smartkontrakt med denna adress och en balans pÄ 1 000 000 000 nanogram.

kontostatus Àr (konto
  addr:(addr_std
    anycast:ingenting workchain_id:0 address:x044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A)
  lagringsstatistik:(storage_info
    anvÀnt:(storage_used
      celler:(var_uint len:1 value:1)
      bitar:(var_uint len:1 value:103)
      offentliga_celler:(var_uint len:0 value:0)) senast_betald:1583257959
    förfallen_betalning:ingenting)
  lagring:(account_storage last_trans_lt:3825478000002
    balans:(valutor
      gram:(nanogram
        belopp:(var_uint len:4 value:2000000000))
      annat:(extra_currencies
        dict:hme_empty))
    status:account_uninit))
x{C00044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A20259C2F2F4CB3800000DEAC10776091DCD650004_}
senaste transaktion lt = 3825478000001 hash = B043616AE016682699477FFF01E6E903878CDFD6846042BA1BFC64775E7AC6C4
konto balans Àr 2000000000ng

Nu publicerar vi smartkontraktet. Vi kör igÄng lite-klienten och utför.

> sendfile lottery-query.boc
[ 1][t 2][1583008371.631410122][lite-client.cpp:966][!testnode] skickar förfrÄgan frÄn fil lottery-query.boc
[ 3][t 1][1583008371.828550100][lite-client.cpp:976][!query]    extern meddelandestatus Àr 1 

Vi kontrollerar att kontraktet Àr publicerat.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Bland annat fÄr vi.

  lagring:(account_storage last_trans_lt:3825499000002
    balans:(valutor
      gram:(nanogram
        belopp:(var_uint len:4 value:1987150999))
      annat:(extra_currencies
        dict:hme_empty))
    status:(account_active

Vi ser att account_active.

Den motsvarande commit med Àndringar hÀr.

Nu skapar vi förfrÄgningar för att interagera med smartkontraktet.

Mer specifikt, den första för att Àndra adressen lÀmnar vi som sjÀlvstÀndigt arbete, och den andra för att skicka gram till Àgarens adress gör vi. I praktiken behöver vi göra samma sak som i testet för att skicka gram.

SÄ hÀr meddelande kommer vi att skicka till smartkontraktet, dÀr msg_seqno 165, ÄtgÀrd 2 och 9.5 gram att skicka.

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

Kom ihÄg att underteckna meddelandet med den privata nyckeln lottery.pk, som genererades tidigare vid skapandet av smartkontraktet. HÀr Àr den motsvarande commit.

Vi fÄr information frÄn smartkontraktet med hjÀlp av get-metoder

Nu ska vi se hur man kör get-metoder i smartkontraktet.

Starta lite-client och vi kör get-metoderna som vi har skrivit.

$ .\/lite-client\/lite-client -C ton-lite-client-test1.config.json
> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd balans
argument:  [ 104128 ] 
resultat:  [ 64633878952 ] 
...

I result innehÄller vÀrdet som funktionen balance() frÄn vÄrt smartkontrakt.
Samma sak kommer vi utföra för ytterligare nÄgra metoder.

> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_seqno
...
argument:  [ 77871 ] 
resultat:  [ 1 ] 

Vi begÀr historik över satsningar.

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

Vi kommer att anvÀnda lite-client och get-metoder för att visa information om smartkontraktet pÄ webbplatsen.

Vi visar data frÄn smartkontraktet pÄ webbplatsen.

Jag har skrivit en enkel webbplats i Python för att visa data frÄn smartkontraktet pÄ ett anvÀndarvÀnligt sÀtt. HÀr kommer jag inte att gÄ in pÄ detaljer och publicera webbplatsen. med en commit..

FörfrÄgningar till TON görs frÄn Python med hjÀlp av lite-client. För bekvÀmlighetens skull packas webbplatsen i Docker och publiceras pÄ Google Cloud. LÀnk till webbplatsen.

Vi testar

Nu ska vi försöka skicka gram för pÄfyllning frÄn plÄnboken.Vi kommer att skicka 40 gram. Och göra nÄgra insatser för tydlighetens skull. Vi ser att webbplatsen visar insatshistorik, aktuell vinstprocent och annan nyttig information.

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

Epilog

Artikeln blev mycket lÀngre Àn jag hade förvÀntat mig, kanske kunde den ha varit kortare, eller sÄ Àr den just right för nÄgon som inte vet nÄgot om TON och vill skriva och publicera ett inte sÄ enkelt smartkontrakt med möjlighet att interagera med det. Kanske vissa saker kunde ha förklarats enklare.

Det Àr möjligt att vissa aspekter av implementeringen kunde ha gjorts mer effektivt och elegant, men dÄ skulle förberedelsen av artikeln ha tagit Ànnu mer tid. Dessutom kan det hÀnda att jag har gjort nÄgot fel eller missförstÄtt nÄgot, sÄ om du gör nÄgot seriöst bör du förlita dig pÄ den officiella dokumentationen eller det officiella kodarkivet för TON.

Det Àr vÀrt att notera att eftersom TON fortfarande Àr under aktiv utveckling kan förÀndringar intrÀffa som bryter nÄgon av stegen i denna artikel (vilket redan har hÀnt medan jag skrev, och jag har redan ÄtgÀrdat det), men den övergripande metoden kommer sannolikt inte att förÀndras.

Jag kommer inte att spekulera om TON:s framtid. Kanske blir plattformen nÄgot större och det Àr vÀrt att investera tid i att studera den och ta en nisch med vÄra produkter redan nu.

Det finns ocksĂ„ Libra frĂ„n Facebook, som har en potentiell anvĂ€ndarpublik större Ă€n TON. Jag vet nĂ€stan inget om Libra, men enligt forumet Ă€r aktiviteten dĂ€r mycket större Ă€n i TON-gemenskapen. Även om utvecklarna och TON-gemenskapen mer liknar underground, vilket ocksĂ„ Ă€r coolt.

LĂ€nkar

  1. Officiell dokumentation om TON: https://test.ton.org
  2. Officiellt arkiv för TON: https://github.com/ton-blockchain/ton
  3. Officiell plÄnbok för olika plattformar: https://wallet.ton.org
  4. Arkiv för smartkontraktet frÄn denna artikel: https://github.com/raiym/astonished
  5. LÀnk till webbplatsen för smartkontraktet: https://ton-lottery.appspot.com
  6. Arkiv för extension för Visual Studio Code för FunC: https://github.com/raiym/func-visual-studio-plugin
  7. En chat om TON pÄ Telegram som har hjÀlpt mig mycket i början. Jag tror inte det Àr fel att sÀga att alla som har skrivit nÄgot för TON finns dÀr. DÀr kan man ocksÄ be om testgram. https://t.me/tondev_ru
  8. En annan chat om TON dÀr jag har hittat nyttig 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: habr.com

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