Come scrivere e pubblicare un contratto intelligente nella Telegram Open Network (TON)

Come scrivere e pubblicare un contratto intelligente in TON

Di cosa parla questo articolo?

In questo articolo racconterò come ho partecipato al primo (di due) concorso di Telegram sulla blockchain, non ho ottenuto un premio e ho deciso di documentare la mia esperienza in modo che non andasse perduta e, forse, potesse essere utile a qualcun altro.

Poiché non volevo scrivere codice astratto ma realizzare qualcosa di funzionante, per l'articolo ho scritto un contratto intelligente per una lotteria istantanea e un sito che mostra i dati del contratto intelligente direttamente da TON senza l'uso di archivi intermedi.

L'articolo sarà utile a chi desidera creare il proprio primo contratto intelligente in TON, ma non sa da dove cominciare.

Usando l'esempio della lotteria, passerò dall'installazione dell'ambiente alla pubblicazione del contratto intelligente, interagendo con esso e scrivendo un sito per ottenere e pubblicare i dati.

Partecipazione al concorso

Lo scorso ottobre Telegram ha annunciato un concorso sulla blockchain con nuovi linguaggi Fift e FunC. Era necessario scrivere uno qualsiasi dei cinque contratti intelligenti proposti. Ho pensato che sarebbe stato interessante dedicarmi a qualcosa di insolito, studiare il linguaggio e realizzare qualcosa, anche se in futuro non avessi dovuto scrivere altro. Inoltre, l'argomento è sempre attuale.

Vale la pena dire che non avevo esperienza nello sviluppo di contratti intelligenti.

Pianificavo di partecipare fino alla fine, se possibile, e poi scrivere un articolo di riepilogo, ma ho fallito subito nel primo tentativo. Ho scritto un portafoglio con firma multipla su FunC ed ha funzionato complessivamente. Ho preso come base un contratto intelligente su Solidity.

A quel tempo pensavo che sarebbe stato sicuramente sufficiente per ottenere un premio. Alla fine, circa 40 dei 60 partecipanti sono stati premiati e io non ero tra loro. In generale, non è nulla di grave, ma una cosa mi ha preoccupato. Al momento dell'annuncio dei risultati, non era stata effettuata una revisione con il test del mio contratto; ho chiesto ai partecipanti nella chat se qualcun altro non lo avesse, e non c'era nessuno.

Probabilmente, notando i miei messaggi, dopo due giorni i giudici hanno pubblicato un commento e non ho capito se avevano accidentalmente trascurato il mio contratto durante la valutazione oppure se lo considerassero così scarso da non richiedere commenti. Ho posto una domanda sulla pagina, ma non ho ricevuto risposta. Anche se chi giudicava non è un segreto, ho ritenuto superfluo inviare messaggi privati.

È stato speso molto tempo a capire, quindi si è deciso di scrivere un articolo. Poiché attualmente non ci sono molte informazioni, l'articolo aiuterà a risparmiare tempo a tutti gli interessati.

Il concetto di funzionamento dei contratti intelligenti in TON

Prima di scrivere qualcosa, è necessario capire da quale lato approcciare questa cosa. Quindi ora spiegherò di quali parti è composta la sistema. Più precisamente, quali parti bisogna conoscere per scrivere almeno un contratto funzionante.

Ci concentreremo sulla scrittura di un contratto intelligente e sul lavoro con TON Virtual Machine (TVM), Fift e FunC, quindi l'articolo somiglia di più a una descrizione dello sviluppo di un programma normale. Non ci fermeremo a spiegare come funziona la piattaforma stessa.

In generale su come funziona TVM e il linguaggio Fift esiste una buona documentazione ufficiale. Durante la partecipazione al concorso e ora mentre scrivo il contratto attuale, mi sono spesso riferito a essa.

Il linguaggio principale utilizzato per scrivere contratti intelligenti è FunC. Attualmente non ci sono documentazioni su di esso, quindi per scrivere qualcosa bisogna studiare esempi di contratti intelligenti dal repository ufficiale e la stessa implementazione del linguaggio, oltre a guardare gli esempi di contratti intelligenti dai due concorsi passati. I link sono alla fine dell'articolo.

Supponiamo di aver già scritto un contratto intelligente su FunC, dopodiché compiliamo il codice in assembler Fift.

Il contratto intelligente compilato deve essere pubblicato. Per fare ciò, è necessario scrivere una funzione su Fift, che accetterà come input il codice del contratto intelligente e alcuni altri parametri, e come output si otterrà un file con estensione .boc (che significa "bag of cells"), e, a seconda di come lo scriviamo, la chiave privata e l'indirizzo, che vengono generati sulla base del codice del contratto intelligente. Già si possono inviare grammi all'indirizzo del contratto intelligente che non è ancora stato pubblicato.

Per pubblicare un contratto intelligente in TON, il file ottenuto deve essere inviato alla blockchain tramite un light client (di cui parleremo più avanti). Ma prima di pubblicare, è necessario trasferire grammi all'indirizzo generato, altrimenti il contratto intelligente non sarà pubblicato. Dopo la pubblicazione, sarà possibile interagire con il contratto intelligente inviandogli messaggi dall'esterno (ad esempio, tramite il light client) o dall'interno (ad esempio, un contratto intelligente invia un messaggio a un altro all'interno di TON). .boc il file dovrà essere inviato nella blockchain tramite un light client (di cui parleremo più avanti). Ma prima di pubblicare, è necessario trasferire grammi all'indirizzo generato, altrimenti il contratto intelligente non verrà pubblicato. Dopo la pubblicazione, sarà possibile interagire con il contratto intelligente, inviando messaggi esternamente (ad esempio, tramite un light client) o internamente (ad esempio, un contratto intelligente invia un messaggio a un altro all'interno di TON).

Una volta compreso come pubblicare il codice, diventa tutto più semplice. Abbiamo un'idea di cosa vogliamo scrivere e di come funzionerà il nostro programma. Durante la scrittura, cerchiamo come è già stato implementato nei contratti smart esistenti o diamo un'occhiata al codice di implementazione. Fift e FunC nel repository ufficiale, oppure consultiamo la documentazione ufficiale.

Molto spesso cercavo parole chiave nella chat di Telegram dove erano riuniti tutti i partecipanti al concorso, inclusi i dipendenti di Telegram. Durante il concorso, tutti si sono riuniti lì e hanno iniziato a discutere di Fift e FunC. Il link è alla fine dell'articolo.

È il momento di passare dalla teoria alla pratica.

Preparazione dell'ambiente per lavorare con TON

Tutto ciò che sarà descritto nell'articolo l'ho fatto su MacOS e ho ricontrollato su una pulita Ubuntu 18.04 LTS su Docker.

La prima cosa da fare è scaricare e installare lite-client con il quale è possibile inviare richieste a TON.

Le istruzioni sul sito ufficiale descrivono il processo di installazione in modo abbastanza dettagliato e chiaro, tralasciando alcuni dettagli. Qui seguiamo le istruzioni, installando nel contempo le dipendenze mancanti. Non ho compilato ogni progetto da solo e ho installato dal repository ufficiale di Ubuntu (su MacOS ho usato 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 

Una volta installate tutte le dipendenze, possiamo installare lite-client, Fift, FunC.

Iniziamo clonando il repository TON con le dipendenze. Per comodità faremo tutto nella cartella ~\/TON.

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

Nel repository si trovano anche le implementazioni. Fift e FunC.

Ora siamo pronti a compilare il progetto. Il codice del repository è stato clonati nella cartella ~\/TON\/ton. In ~\/TON creiamo una cartella build e compiliamo il progetto in essa.

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

Poiché stiamo per scrivere un contratto smart, abbiamo bisogno non solo di lite-client, ma anche Fift con FunC, quindi compiliamo tutto. Non è un processo veloce, quindi aspettiamo.

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

Successivamente, scarichiamo il file di configurazione che contiene i dati sul nodo a cui lite-client ci si connetterà.

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

Facciamo le prime richieste a TON.

Ora avviamo lite-client.

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

Se la compilazione è andata a buon fine, dopo l'avvio vedrete il log della connessione del client leggero al nodo.

[ 1][t 2][1582054822.963129282][lite-client.h:201][!testnode]   conn ready
[ 2][t 2][1582054823.085654020][lite-client.cpp:277][!testnode] la versione del server è 1.1, capacità 7
[ 3][t 2][1582054823.085725069][lite-client.cpp:286][!testnode] il tempo del server è 1582054823 (delta 0)
...

È possibile eseguire il comando help e vedere quali comandi sono disponibili.

help

Elencare i comandi che utilizzeremo in questo articolo.

elenco dei comandi disponibili:
last    Ottieni informazioni sull'ultimo blocco e stato dal server
sendfile  Carica un messaggio serializzato da  e invialo al server
getaccount  []  Carica lo stato più recente dell'account specificato;  è nel formato []:
runmethod  []  ...   Esegue il metodo GET  dell'account  con i parametri specificati

last ottiene l'ultimo blocco creato dal server.

sendfile  invia a TON un file con un messaggio, è tramite questo comando che viene pubblicato il contratto intelligente e le relative richieste.

getaccount  carica l'attuale stato del contratto intelligente con l'indirizzo specificato.

runmethod  []   esegue i metodi get del contratto intelligente. 

Ora siamo pronti a scrivere il contratto stesso.

Implementazione

Idea

Come già accennato, il contratto intelligente che stiamo scrivendo è una lotteria.

Tuttavia, questa non è una lotteria in cui devi acquistare un biglietto e aspettare un'ora, un giorno o un mese, ma è istantanea in cui l'utente trasferisce a un indirizzo contrattuale N grammi, e riceve immediatamente indietro 2 * N grammi o perde. Rendiamo la probabilità di vincita circa il 40%. Se i grammi per il pagamento non sono sufficienti, considereremo la transazione come un aumento del saldo.

È importante che le scommesse possano essere viste in tempo reale e in modo conveniente, affinché l'utente possa capire immediatamente se ha vinto o perso. Pertanto, è necessario creare un sito web che mostri le scommesse e i risultati direttamente da TON.

Scrittura del contratto intelligente

Per comodità, ho creato la evidenziazione della sintassi per FunC, il plugin può essere trovata e installata nella ricerca di Visual Studio Code, se mai volessi aggiungere qualcosa, ho reso il plugin disponibile pubblicamente. Inoltre, in precedenza qualcuno aveva creato un plugin per lavorare con Fift, che può essere installato e trovato in VSC.

Creiamo subito un repository dove committeremo i risultati intermedi.

Per facilitare la nostra vita, scriveremo il contratto intelligente e lo testeremo localmente, finché non sarà pronto. Solo dopo lo pubblicheremo in TON.

Il contratto intelligente ha due metodi esterni a cui è possibile accedere. Primo, recv_external() questa funzione viene eseguita quando una richiesta al contratto proviene dal mondo esterno, cioè non da TON, ad esempio quando formiamo un messaggio e lo inviamo tramite lite-client. Secondo, recv_internal() è quando un contratto all'interno stesso di TON interagisce con il nostro. In entrambi i casi possiamo passare parametri alla funzione.

Iniziamo con un esempio semplice che funzionerà una volta pubblicato, ma non ha alcun carico funzionale.

() recv_internal(slice in_msg) impuro {
    ;; TODO: implementazione 
}

() recv_external(slice in_msg) impuro {
    ;; TODO: implementazione  
}

Qui è necessario spiegare cosa sia slice. Tutti i dati memorizzati nella TON Blockchain sono una collezione cella TVM o semplicemente cella, in questa cella è possibile memorizzare fino a 1023 bit di dati e fino a 4 collegamenti ad altre celle.

slice della cella TVM o slice è una parte esistente cella viene utilizzata per il parsing; più avanti sarà chiaro. La cosa principale per noi è che possiamo passare slice e a seconda del tipo di messaggio elaborare i dati in recv_external() o recv_internal().

impuro è una parola chiave che indica che la funzione modifica i dati del contratto intelligente.

Salviamo il codice del contratto in lottery-code.fc e compiliamo.

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

Puoi visualizzare i valori dei flag usando il comando

~ /TON /build /crypto /func -help

Abbiamo ottenuto il codice dell'assemblatore Fift compilato in 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

Può essere eseguito localmente, per questo prepariamo l'ambiente.

Nota che nella prima riga si include Asm.fif, questo è codice scritto in Fift per l'assemblatore Fift.

Poiché vogliamo eseguire e testare il contratto intelligente localmente, creiamo un file lottery-test-suite.fif e copiamo il codice compilato, sostituendo l'ultima riga, che scrive il codice del contratto intelligente in una costante code, in modo da poterlo poi passare alla macchina virtuale:

"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

Finora sembra chiaro, ora aggiungiamo nel medesimo file il codice che utilizzeremo per eseguire 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 , , \/\/ saldo rimanente
0 , dictnew , \/\/ contract_address, global_config
1 tuple \/\/ wrap in un'altra tupla
constant c7

0 constant recv_internal \/\/ per eseguire recv_internal() 
-1 constant recv_external \/\/ per invocare recv_external()

In c7 registriamo il contesto, cioè i dati con cui sarà eseguito TVM (o lo stato della rete). Durante il concorso, uno degli sviluppatori ha mostrato come creare c7 e io ho copiato. In questo articolo potrebbe essere necessario cambiare rand_seed Poiché da esso dipende la generazione del numero casuale e non deve essere modificato, ogni volta verrà restituito lo stesso numero.

recv_internal e recv_external Le costanti con valore 0 e -1 si occuperanno di chiamare le rispettive funzioni nel contratto intelligente.

Ora siamo pronti a creare il primo test per il nostro contratto intelligente vuoto. Per chiarezza, aggiungeremo tutti i test finora in questo stesso file. lottery-test-suite.fif.

Creiamo una variabile storage e ci scriviamo dentro un vuoto cella, questo sarà lo storage del contratto intelligente.

message Questo è il messaggio che passeremo al contratto intelligente dall'esterno. Anche questo lo faremo vuoto per ora.

memorizzazione delle variabili 
<b b> memorizzazione ! 

messaggio variabile 
<b b> messaggio ! 

Dopo aver preparato le costanti e le variabili, avviamo la TVM con il comando runvmctx e passiamo i parametri creati in ingresso.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

Alla fine otterremo questo codice intermedio in Ora possiamo eseguire il codice risultante. Fift.

export FIFTPATH=~\/TON\/ton\/crypto\/fift\/lib \/\/ eseguiamo una volta per comodità ~\/TON\/build\/crypto\/fift -s lottery-test-suite.fif

Il programma dovrebbe funzionare senza errori e nell'output vedremo il log dell'esecuzione: 

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

Ottimo, abbiamo scritto la prima versione funzionante del contratto intelligente.

Ora dobbiamo aggiungere funzionalità. Iniziamo a occuparci dei messaggi che arrivano dall'esterno in

Lo sviluppatore sceglie il formato del messaggio che il contratto può accettare. recv_external()

Ma in genere,

prima di tutto, vogliamo proteggere il nostro contratto dal mondo esterno e fare in modo che solo il proprietario del contratto possa inviare messaggi esterni.

  • In secondo luogo, quando inviamo un messaggio valido a TON, vogliamo che questo avvenga esattamente una volta e che, alla ripetizione dello stesso messaggio, il contratto intelligente lo rifiuti.
  • Pertanto, quasi ogni contratto affronta questi due problemi; poiché il nostro contratto accetta messaggi esterni, dobbiamo anche occuparcene.

Affronteremo queste questioni in ordine inverso. Prima risolveremo il problema della ripetizione, in modo che se il contratto ha già ricevuto un messaggio e l'ha elaborato, non verrà eseguito di nuovo. E poi affronteremo il problema di garantire che solo un certo numero di persone possa inviare messaggi al contratto intelligente.

Risolviamo quindi prima la questione della ripetizione, se il contratto ha già ricevuto un messaggio simile e lo ha elaborato, non lo eseguirà una seconda volta. Successivamente, ci occuperemo del problema affinché solo un gruppo specifico di persone possa inviare messaggi al contratto intelligente.

Ci sono diversi modi per risolvere il problema dei messaggi duplicati. Procederemo come segue. Inizializziamo un contatore dei messaggi ricevuti nel contratto intelligente con un valore iniziale di 0. Per ogni messaggio che inviamo al contratto intelligente, aggiungeremo il valore attuale del contatore. Se il valore del contatore nel messaggio non corrisponde al valore nel contratto intelligente, non lo elaboriamo; se corrisponde, lo elaboriamo e incrementiamo il contatore nel contratto intelligente di 1.

Torniamo a lottery-test-suite.fif e aggiungiamo un secondo test. Invieremo un numero errato, e il codice deve generare un'eccezione. Ad esempio, supponiamo che nel contratto ci sia il numero 166, mentre noi inviamo 165.

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

messaggio @ 
recv_external 
code 
storage @ 
c7 
runvmctx

drop 
exit_code ! 
."Codice di uscita " exit_code @ . cr 
exit_code @ 33 - abort"Test #2 non superato"

Avviamo.

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

E vedremo che il test viene eseguito con un errore.

[ 1][t 0][1582283084.210902214][words.cpp:3046] lottery-test-suite.fif:67: abort": Test #2 Non superato
[ 1][t 0][1582283084.210941076][fift-main.cpp:196]      Errore nell'interpretazione del file `lottery-test-suite.fif`: errore nell'interpretazione del file incluso `lottery-test-suite.fif`: lottery-test-suite.fif:67: abort": Test #2 Non superato

A questo punto lottery-test-suite.fif dovrebbe apparire come al link.

Ora aggiungiamo la logica del contatore nel contratto intelligente in lottery-code.fc.

() recv_internal(slice in_msg) impuro {
    ;; TODO: implementazione 
}

() recv_external(slice in_msg) impuro {
    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);
}

In slice in_msg contiene il messaggio che stiamo inviando.

La prima cosa che facciamo è controllare se il messaggio contiene dati; se non ci sono, usciamo semplicemente.

Dopo procediamo a fare il parsing del messaggio. in_msg~load_uint(32) carica il numero 165, 32 bit unsigned int dal messaggio inviato.

Successivamente carichiamo 32 bit dallo storage del contratto intelligente. Controlliamo che il numero caricato corrisponda a quello inviato; se non corrisponde, generiamo un'eccezione. Nel nostro caso, poiché stiamo inviando un valore non corrispondente, deve essere generata un'eccezione.

Ora compiliamo.

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

Copiamo il codice risultante in lottery-test-suite.fif, ricordando di sostituire l'ultima riga.

Controlliamo che il test venga superato:

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

Qui c'è possiamo visualizzare il commit corrispondente con i risultati attuali.

Notiamo che copiare continuamente il codice compilato del contratto intelligente in un file di test non è pratico, quindi scriviamo uno script che registrerà il codice in una costante per noi, e noi semplicemente includeremo il codice compilato nei nostri test usando "include".

Nella cartella del progetto creiamo il file build.sh con il seguente contenuto.

#!/bin/bash

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

Rendiamolo eseguibile.

chmod +x .\/build.sh

Ora, è sufficiente eseguire il nostro script per compilare il contratto. Ma oltre a questo, dobbiamo registrarlo in una costante. codeQuindi creeremo un nuovo file. lotter-compiled-for-test.fif, che includeremo nel file. lottery-test-suite.fif.

Aggiungeremo nel codice dello script sh la riga che semplicemente duplicherà il file compilato in lotter-compiled-for-test.fif e modificherà l'ultima riga.

# 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

Ora, per controllare, eseguiremo lo script generato e verrà generato un file. lottery-compiled-for-test.fif, che includeremo nel nostro. lottery-test-suite.fif

In lottery-test-suite.fif eliminiamo il codice del contratto e aggiungiamo la riga "lottery-compiled-for-test.fif" include.

Eseguiamo i test per verificare che vengano superati.

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

Ottimo, ora, per automatizzare l'esecuzione dei test, creiamo un file. test.sh, che eseguirà prima build.sh, e poi avvierà i test.

touch test.sh
chmod +x test.sh

Scriviamo dentro

.\/build.sh 

echo "nCompilazione completatan"

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

Facciamo test.sh e avviamolo per assicurarci che i test funzionino.

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

Controlliamo che il contratto si compili e che i test vengano eseguiti.

Ottimo, ora all'avvio test.sh la compilazione e l'esecuzione dei test avverranno immediatamente. Ecco il link a commit.

Ok, prima di continuare facciamo un'altra cosa per comodità.

Creiamo una cartella build dove conserveremo il contratto compilato e la sua copia registrata in una costante. lottery-compiled.fif, lottery-compiled-for-test.fifCreeremo anche una cartella test dove si troverà il file con i test lottery-test-suite.fif e potenzialmente altri file ausiliari. Link alle relative modifiche..

Continuiamo lo sviluppo del contratto intelligente.

Il passo successivo dovrebbe essere un test che verifica che il messaggio venga ricevuto e che il contatore venga aggiornato nella memoria quando inviamo il numero corretto. Ma lo faremo più tardi.

Ora pensiamo a quale sia la struttura dei dati e quali dati devono essere memorizzati nel contratto intelligente.

Descriverò tutto ciò che memorizziamo.

`seqno` numero intero positivo a 32 bit contatore. 

`pubkey` numero intero positivo a 256 bit chiave pubblica, con cui verificheremo la firma del messaggio inviato esternamente, come descritto in seguito. 

`order_seqno` numero intero positivo a 32 bit memorizza il contatore delle scommesse. 

`number_of_wins` numero intero positivo a 32 bit memorizza il numero di vittorie. 

`incoming_amount` tipo di dato Gram (i primi 4 bit rispondono per la lunghezza), memorizza il numero totale di grammi inviati al contratto. 

`outgoing_amount` numero totale di grammi inviati ai vincitori. 

`owner_wc` numero del workchain, numero intero positivo a 32 bit (in alcuni casi è indicato come a 8 bit). Attualmente ci sono solo due -1 e 0. 

`owner_account_id` numero intero positivo a 256 bit, indirizzo del contratto nell'attuale workchain. 

`orders` variabile di tipo dizionario, memorizza le ultime venti scommesse. 

Dobbiamo poi scrivere due funzioni. La prima la chiameremo pack_state(), che imballerà i dati per la successiva memorizzazione nel sistema di archiviazione del contratto intelligente. La seconda, la chiameremo unpack_state() leggerà e restituirà i dati dallo storage.

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

Aggiungiamo queste due funzioni all'inizio del contratto intelligente. Otterremo questo codice intermedio in un risultato intermedio.

Per memorizzare i dati sarà necessario chiamare la funzione integrata set_data() e essa scriverà i dati da pack_state() nello storage del contratto intelligente.

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

Ora che abbiamo comode funzioni per scrivere e leggere i dati, possiamo procedere.

Dobbiamo verificare che il messaggio in arrivo sia firmato dal proprietario del contratto (o da un altro utente che ha accesso alla chiave privata).

Quando pubblichiamo un contratto intelligente, possiamo inizializzarlo con i dati di cui abbiamo bisogno nel deposito, che verranno salvati per un uso futuro. Registrarvi la chiave pubblica, in modo da poter verificare che la firma del messaggio in arrivo sia stata effettuata con la corrispondente chiave privata.

Prima di procedere, creiamo una chiave privata e registriamola in test/keys/owner.pk. Per fare questo, avviamo Fift in modalità interattiva ed eseguiamo quattro comandi.

`newkeypair` genera la chiave pubblica e privata e le registra nello stack. 

`drop` rimuove l'elemento superiore dallo stack (in questo caso, la chiave pubblica) 

`.s` semplicemente per vedere cosa c'è attualmente nello stack 

`"owner.pk" B>file` registra la chiave privata in un file con il nome `owner.pk`. 

`bye` termina l'attività con Fift. 

Creiamo una cartella chiavi all'interno della cartella test e lì registreremo la chiave privata.

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

Vediamo nella cartella corrente il file owner.pk.

Rimuoviamo la chiave pubblica dallo stack; quando necessario, possiamo recuperarla dalla chiave privata.

Ora dobbiamo scrivere un controllo della firma. Iniziamo con un test. Prima leggiamo la chiave privata dal file usando la funzione file>B e registriamola nella variabile owner_private_key, poi con la funzione priv>pub convertiamo la chiave privata in pubblica e registriamo il risultato in owner_public_key.

variabile owner_private_key
variabile owner_public_key 

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

Entrambe le chiavi ci serviranno.

Inizializziamo il deposito del contratto intelligente con dati arbitrari nello stesso ordine della funzione pack_state()e registriamo nella variabile storage.

variabile owner_private_key
variabile owner_public_key 
variabile ordini
variabile owner_wc
variabile owner_account_id

".\/keys\/owner.pk" file&gt;B owner_private_key !
owner_private_key @ priv&gt;pub owner_public_key !
dictnew ordini !
0 owner_wc !
0 owner_account_id !

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

Poi prepariamo un messaggio firmato, che conterrà solo la firma e il valore del contatore.

Prima creiamo i dati che vogliamo inviare, poi li firmiamo con la chiave privata e infine formiamo il messaggio firmato.

variabile messaggio_da_firmare
variabile messaggio_da_inviare
variabile firma
<b 0 32 u, b> messaggio_da_firmare !
messaggio_da_firmare @ proprietario_chiave_privata @ ed25519_firma_uint firma !
<b signature @ b, 0 32 u, b> &lt;s !  

In definitiva, il messaggio che invieremo al contratto intelligente è registrato nella variabile message_to_send, riguardo alle funzioni hashu, ed25519_sign_uint si possono leggere nella documentazione di Fift.

E per avviare il test richiameremo di nuovo.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

Ecco come il file deve apparire con i test a questo punto.

Eseguiamo il test e fallirà, quindi modificheremo il contratto intelligente in modo che possa ricevere messaggi di questo formato e verificare la firma.

Iniziamo leggendo 512 bit della firma dal messaggio e registriamoli in una variabile, poi leggiamo 32 bit della variabile contatore.

Poiché abbiamo una funzione per leggere i dati dallo storage del contratto smart, la utilizzeremo.

Dopo, verifichiamo il contatore fornito con lo storage e la firma. Se qualcosa non corrisponde, lanciamo un'eccezione con il codice appropriato.

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

Commit pertinente qui.

Eseguiamo i test e vediamo che il secondo test fallisce. Per due motivi, mancanza di bit nel messaggio e mancanza di bit nello storage, quindi il codice fallisce durante il parsing. Dobbiamo aggiungere la firma del messaggio che stiamo inviando e copiare lo storage dall'ultimo test.

Nel secondo test aggiungeremo la firma del messaggio e modificheremo lo storage del contratto smart. Ecco come Ecco come appare al momento il file con i test.

Scriviamo un quarto test, in cui invieremo un messaggio firmato con una chiave privata di qualcun altro. Creeremo un'altra chiave privata e la salveremo in un file not-owner.pk. Con questa chiave privata firmeremo il messaggio. Eseguiamo i test e assicuriamoci che tutti i test passino. Commit fino ad ora.

Ora finalmente possiamo passare all'implementazione della logica del contratto smart.
In recv_external() Accetteremo due tipi di messaggi.

Poiché il nostro contratto accumulerà le perdite dei giocatori, questi fondi devono essere trasferiti al creatore della lotteria. L'indirizzo del portafoglio del creatore della lotteria viene registrato nello storage al momento della creazione del contratto.

Per ogni evenienza abbiamo bisogno della possibilità di cambiare l'indirizzo a cui inviare i grammi dei perdenti. Inoltre, dobbiamo avere la possibilità di inviare grammi dalla lotteria all'indirizzo del proprietario.

Iniziamo con il primo. Scriviamo prima un test che verificherà che dopo l'invio di un messaggio, il contratto smart abbia salvato il nuovo indirizzo nello storage. Notiamo che nel messaggio, oltre al contatore e al nuovo indirizzo, stiamo passando anche action un numero intero non negativo di 7 bit, a seconda del quale decideremo come elaborare il messaggio nel contratto smart.

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

Nel test è possibile vedere come avviene la deserializzazione dello storage del contratto smart storage in Fift. La deserializzazione delle variabili è descritta nella documentazione di Fift.

Link al commit con l'aggiunta del test.

Eseguiamo il test e assicuriamoci che fallisca. Ora aggiungiamo la logica per modificare l'indirizzo del proprietario della lotteria.

Nel contratto intelligente continuiamo a analizzare message, leggiamo in action. Ricordiamo che avremo due action: modifica dell'indirizzo e invio di grammi.

Poi leggiamo il nuovo indirizzo del proprietario del contratto e lo salviamo nello storage.
Eseguiamo i test e vediamo che il terzo test fallisce. Fallisce perché il contratto ora analizza ulteriori 7 bit dal messaggio, che mancano nel test. Aggiungiamo al messaggio un non esistente action. Eseguiamo i test e vediamo che tutti passano. Qui commit delle modifiche. Ottimo.

Ora scriviamo la logica per inviare il numero specificato di grammi all'indirizzo salvato in precedenza.

Per prima cosa scriviamo il test. Scriveremo due test: uno quando il saldo non è sufficiente e l'altro quando tutto dovrebbe andare a buon fine. I test possono essere visti in questo commit.

Ora completiamo il codice. Per prima cosa scriviamo due metodi ausiliari. Il primo è un metodo get per conoscere il saldo attuale del contratto intelligente.

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

E il secondo per inviare grammi a un altro contratto intelligente. Questo metodo l'ho copiato integralmente da un altro contratto intelligente.

() 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 <= indicatore di formato int_msg_info$0 
    ;;  .store_uint(1, 1) ;; 1 <= ihr disabilitato
    ;;  .store_uint(1, 1) ;; 1 <= bounce = true
    ;;  .store_uint(0, 1) ;; 0 <= bounced = false
    ;;  .store_uint(4, 5)  ;; 00100 <= flag degli indirizzi, anycast = false, lavoro a 8 bit
        .store_uint (196, 9)
        .store_int(wc, 8)
        .store_uint(addr, 256)
        .store_grams(grams)
        .store_uint(0, 107) ;; 106 zeri +  0 come indicatore che non ci sono celle con i dati.
        .end_cell(); 
    send_raw_message(msg, 3); ;; modalità, 2 per ignorare gli errori, 1 per il mittente paga le spese, 64 per restituire il valore del messaggio in entrata
}

Aggiungiamo questi due metodi al contratto intelligente e scriviamo la logica. Per prima cosa analizziamo la quantità di grammi dal messaggio. Poi controlliamo il saldo, se non è sufficiente lanciamo un'eccezione. Se tutto va bene, inviamo i grammi all'indirizzo salvato e aggiorniamo il contatore.

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

Ecco come il contratto intelligente al momento. Eseguiamo i test e assicuriamoci che passino.

A proposito, ogni messaggio elaborato da un contratto intelligente comporta una commissione ogni volta. Affinché il contratto intelligente esegua la richiesta, dopo le verifiche di base è necessario chiamare accept_message().

Ora ci occupiamo dei messaggi interni. In effetti, accetteremo solo gram e rimanderemo indietro al giocatore il doppio dell'importo in caso di vincita e un terzo al proprietario in caso di perdita.

Iniziamo scrivendo un semplice test. Per questo avremo bisogno di un indirizzo di contratto intelligente di prova da cui simuleremo l'invio di gram al contratto intelligente.

L'indirizzo del contratto intelligente è composto da due numeri, un numero intero di 32 bit che rappresenta la workchain e un numero intero non negativo di 256 bit che è un numero identificativo unico dell'account in questa workchain. Ad esempio, -1 e 12345, questo indirizzo lo salveremo in un file.

Ho copiato la funzione per salvare l'indirizzo da 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

Analizziamo come funziona la funzione; questo ci darà un'idea di come funziona Fift. Avviamo Fift in modalità interattiva.

~\/TON\/build\/crypto\/fift -i 

Per prima cosa mettiamo nello stack -1, 12345 e il nome del file futuro "sender.addr":

-1 12345 "sender.addr" 

Il passo successivo esegue la funzione -rot, che sposta lo stack in modo tale che il numero unico del contratto intelligente si trovi in cima allo stack:

"sender.addr" -1 12345

256 u>B converte un intero non negativo di 256 bit in byte.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap scambia le prime due posizioni dello stack.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B converte un intero di 32 bit in byte.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ unisce due sequenze di byte.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

Di nuovo swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

E infine eseguiamo la scrittura dei byte nel file B>file. Dopo questo, il nostro stack è vuoto. Fermiamo Fift. Nella cartella corrente è stato creato il file sender.addr. Spostiamo il file nella cartella creata test\/addresses\/.

Scriviamo un semplice test che invierà gram al contratto intelligente. Ecco il commit.

Ora ci occupiamo della logica della lotteria.

La prima cosa che facciamo è controllare il messaggio bounced o meno, se bounced, lo ignoriamo. bounced ciò significa che il contratto restituirà gram se si verifica un errore. Non restituiremo gram se dovesse verificarsi un errore.

Controlliamo il saldo; se è inferiore a mezzo grammo, accettiamo semplicemente il messaggio e lo ignoriamo.

Quindi analizziamo l'indirizzo del contratto smart da cui è arrivato il messaggio.

Leggiamo i dati dall'archivio e poi rimuoviamo le vecchie scommesse dalla cronologia se ce ne sono più di venti. Per comodità ho scritto tre funzioni aggiuntive pack_order(), unpack_order(), remove_old_orders().

Poi controlliamo se il saldo non è sufficiente per il pagamento, quindi consideriamo che non sia una scommessa, ma un rifornimento e salviamo il rifornimento in orders.

Poi finalmente la sostanza del contratto smart.

Inizialmente, se il giocatore perde, salviamo la sua scommessa nella cronologia delle scommesse e se la somma supera i 3 grammi, inviamo 1/3 al proprietario del contratto smart.

Se il giocatore vince, allora inviamo il doppio dell'importo all'indirizzo del giocatore e poi salviamo l'informazione sulla scommessa nella cronologia.

() recv_internal(int order_amount, cell in_msg_cell, slice in_msg) impuro {
    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) { ;; ignorare rimbalzati
        return ();
    }
    if (order_amount < 500000000) { ;; ricevi solo grammi senza cambiare stato 
        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));
}

Ecco fatto. Il commit corrispondente.

Ora resta solo da fare una cosa semplice, creiamo i metodi get, in modo che dal mondo esterno sia possibile ottenere informazioni sullo stato del contratto (in pratica, leggere i dati dal loro archivio del contratto smart).

Aggiungiamo i metodi get. Di come ottenere informazioni sul contratto smart ne parleremo più avanti.

Ho anche dimenticato di aggiungere il codice che gestirà la prima richiesta, che avviene quando si pubblica il contratto smart. Il commit corrispondente. E inoltre corretto un bug nell'invio di 1/3 dell'importo all'account del proprietario.

Dopo, bisogna pubblicare il contratto smart. Creiamo una cartella requests.

Ho preso come base il codice di pubblicazione simple-wallet-code.fc che si possono trovare si trova nel repository ufficiale.

È importante notare che formiamo lo spazio di archiviazione del contratto smart e il messaggio in ingresso. Successivamente viene generato l'indirizzo del contratto smart, cioè l'indirizzo è conosciuto anche prima della pubblicazione in TON. Dopo, bisogna inviare alcune gram a questo indirizzo e solo dopo si deve inviare il file con il contratto smart stesso, poiché per l'archiviazione del contratto smart e le operazioni in esso, la rete addebita una commissione (i validatori che archiviano ed eseguono i contratti smart). Puoi vedere il codice qui.

Eseguiamo il codice di pubblicazione e otteniamo lottery-query.boc il file e l'indirizzo del contratto smart.

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

Non dimentichiamo di salvare i file generati: lottery-query.boc, lottery.addr, lottery.pk.

Tra l'altro, nei log di esecuzione vedremo l'indirizzo del contratto smart.

new wallet address = 0:044910149dbeaf8eadbb2b28722e7d6a2dc6e264ec2f1d9bebd6fb209079bc2a 
(Salvando l'indirizzo nel file lottery.addr)
Indirizzo non bounceable (per init): 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd
Indirizzo bounceable (per accesso successivo): kQAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KpFY

Per curiosità facciamo una richiesta a TON

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

E vedremo che l'account con tale indirizzo è vuoto.

lo stato dell'account è vuoto

Inviamo all'indirizzo 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 Gram e dopo pochi secondi eseguiamo di nuovo lo stesso comando. Per inviare gram utilizzo il portafoglio ufficiale, e i gram di prova posso chiedere a qualcuno della chat che menzionerò alla fine dell'articolo.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Guarda, che in rete è apparso un contratto smart non inizializzato (state:account_uninit) con tale indirizzo e un saldo di 1.000.000.000 nanogram.

lo stato dell'account è (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
il saldo dell'account è 2000000000ng

Ora pubblicheremo il contratto intelligente. Avvieremo il lite-client e lo eseguiremo.

> sendfile lottery-query.boc
[ 1][t 2][1583008371.631410122][lite-client.cpp:966][!testnode] invio query dal file lottery-query.boc
[ 3][t 1][1583008371.828550100][lite-client.cpp:976][!query]    lo stato del messaggio esterno è 1 

Verifichiamo che il contratto sia stato pubblicato.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Tra l'altro otterremo.

  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

Vediamo che account_active.

Il commit corrispondente con le modifiche qui.

Ora creiamo richieste per interagire con il contratto intelligente.

Più precisamente, la prima per modificare l'indirizzo la lasceremo come lavoro autonomo, mentre la seconda per inviare gramm a indirizzo del proprietario la faremo. In effetti, dobbiamo fare la stessa cosa che abbiamo fatto nel test di invio di gramm.

Ecco quale messaggio invieremo al contratto intelligente, dove msg_seqno 165, action 2 e 9.5 gramm per l'invio.

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

Non dimentichiamo di firmare il messaggio con la chiave privata lottery.pk, che è stata generata in precedenza durante la creazione del contratto intelligente. Ecco il commit corrispondente.

Otteniamo informazioni dal contratto intelligente tramite metodi GET

Ora vediamo come eseguire i metodi GET del contratto intelligente.

Avviamo lite-client e avviamo i metodi GET che abbiamo scritto.

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

In risultato contiene il valore che restituisce la funzione balance() dal nostro contratto intelligente.
Faremo lo stesso anche per altri metodi.

> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_seqno
...
argomenti:  [ 77871 ] 
risultato:  [ 1 ] 

Richiediamo la storia delle scommesse.

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

Utilizzeremo il lite-client e i metodi GET per visualizzare le informazioni sul contratto intelligente sul sito.

Mostriamo i dati del contratto intelligente sul sito

Ho scritto un semplice sito web in Python per mostrare i dati del contratto intelligente in una forma conveniente. Non mi soffermerò troppo su di esso e pubblicherò il sito con un solo commit.

Le richieste a TON vengono effettuate da Python utilizzando lite-client. Per comodità, il sito è impacchettato in Docker e pubblicato su Google Cloud. Link al sito.

Proviamo

Ora proviamo a inviare lì dei grammi per il rifornimento da portafoglio. Invieremo 40 grammi e faremo un paio di scommesse per chiarezza. Possiamo vedere che il sito mostra la cronologia delle scommesse, la percentuale attuale di vincita e altre informazioni utili.

Possiamo vedere, che la prima l'abbiamo vinta, la seconda l'abbiamo persa.

Postfazione

L'articolo è risultato molto più lungo di quanto avessi presupposto, forse si poteva fare più breve, o forse è proprio adatto a una persona che non sa nulla di TON e vuole scrivere e pubblicare un contratto intelligente non così semplice con la possibilità di interagire. Forse alcune cose avrebbero potuto essere spiegate in modo più semplice.

È possibile che alcuni aspetti dell'implementazione potessero essere realizzati in modo più efficiente ed elegante, ma ciò avrebbe comportato ulteriori tempi per la preparazione dell'articolo. Inoltre, è possibile che abbia commesso degli errori o non abbia capito qualcosa, quindi se stai facendo qualcosa di serio devi basarti sulla documentazione ufficiale o sul repository ufficiale del codice TON.

Va notato che poiché il TON è ancora in fase attiva di sviluppo, potrebbero verificarsi cambiamenti che rompano alcuni dei passaggi di questo articolo (cosa che è accaduta mentre scrivevo, ho già corretto), ma l'approccio generale difficilmente cambierà.

Non parlerò del futuro di TON. Potenzialmente la piattaforma diventerà qualcosa di più grande e dovremmo dedicare tempo alla sua esplorazione e occupare una nicchia con i nostri prodotti già adesso.

C'è anche Libra di Facebook, che ha un pubblico potenziale più ampio rispetto a TON. Non so quasi nulla di Libra, a giudicare dal forum l'attività lì è molto maggiore che nella comunità TON. Anche se gli sviluppatori e la comunità TON sembrano più underground, il che è comunque interessante.

Link

  1. Documentazione ufficiale su TON: https://test.ton.org
  2. Repository ufficiale di TON: https://github.com/ton-blockchain/ton
  3. Portafoglio ufficiale per diverse piattaforme: https://wallet.ton.org
  4. Repository del contratto intelligente di questo articolo: https://github.com/raiym/astonished
  5. Link al sito del contratto intelligente: https://ton-lottery.appspot.com
  6. Repository per l'estensione di Visual Studio Code per FunC: https://github.com/raiym/func-visual-studio-plugin
  7. Chat su TON su Telegram, che mi ha molto aiutato a capire le cose all'inizio. Credo non sia un errore dire che ci sono tutti coloro che hanno scritto qualcosa per TON. Lì puoi anche chiedere dei gram test. https://t.me/tondev_ru
  8. Un altro chat su TON dove ho trovato informazioni utili: https://t.me/TONgramDev
  9. Fase uno del concorso: https://contest.com/blockchain
  10. Fase due del concorso: https://contest.com/blockchain-2

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS 🔥 Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS - ProHoster