Telegram Açık Ağı'nda (TON) akıllı sözleşmenin nasıl yazılacağı ve yayınlanacağı hakkında

TON'da akıllı sözleşmenin nasıl yazılacağı ve yayınlanacağı hakkında

Bu makale ne hakkında?

Makalede, ilk (iki) Telegram blockchain yarışmasına nasıl katıldığımı, ödül almadığımı ve deneyimimi unutulmaması ve belki de yardımcı olması için bir makalede kaydetmeye karar verdiğimi anlatacağım. birisi.

Soyut kod yazmak istemediğim, ancak işe yarayan bir şey yapmak istediğim için, makale için anlık bir piyango için akıllı bir sözleşme ve akıllı sözleşme verilerini ara depolama kullanmadan doğrudan TON'dan gösteren bir web sitesi yazdım.

Makale, TON'da ilk akıllı sözleşmesini yapmak isteyen ancak nereden başlayacağını bilmeyenler için faydalı olacaktır.

Piyangoyu örnek olarak kullanarak, ortamı kurmaktan akıllı bir sözleşme yayınlamaya, onunla etkileşim kurmaya ve veri almak ve yayınlamak için bir web sitesi yazmaya kadar gideceğim.

Yarışmaya katılım hakkında

Geçen Ekim ayında Telegram, yeni dillerle bir blockchain yarışmasını duyurdu Fift и FunC. Önerilen beş akıllı sözleşmeden herhangi birini yazmaktan seçim yapmak gerekiyordu. İleride başka bir şey yazmak zorunda kalmasam bile farklı bir şeyler yapmanın, dil öğrenip bir şeyler yapmanın güzel olacağını düşündüm. Üstelik konu sürekli gündemde.

Akıllı sözleşmeler geliştirme konusunda hiçbir deneyimim olmadığını söylemekte fayda var.

Yapabildiğim kadar sonuna kadar katılmayı ve ardından bir inceleme yazısı yazmayı planladım, ancak ilkinde hemen başarısız oldum. BEN bir cüzdan yazdım çoklu imza açıkken FunC ve genellikle işe yaradı. bunu esas aldım Solidity'de akıllı sözleşme.

O zamanlar bunun en azından bir miktar ödül almak için kesinlikle yeterli olduğunu düşünmüştüm. Sonuç olarak 40 katılımcıdan yaklaşık 60'ı ödül almaya hak kazandı ve ben onların arasında değildim. Genel olarak bunda yanlış bir şey yok ama bir şey beni rahatsız etti. Sonuçların açıklandığı tarihte sözleşmemin test incelemesi henüz yapılmamıştı, sohbette katılımcılara sahip olmayan var mı diye sordum, yoktu.

Görünüşe göre mesajlarıma dikkat ettim, iki gün sonra jüri bir yorum yayınladı ve ben hâlâ akıllı sözleşmemi yargılama sırasında kazara mı kaçırdıklarını yoksa sadece yoruma gerek kalmayacak kadar kötü olduğunu mu düşündüklerini anlamıyorum. Sayfada bir soru sordum ancak yanıt alamadım. Kimin yargıladığı sır olmasa da kişisel mesaj yazmanın gereksiz olduğunu düşündüm.

Anlamak için çok zaman harcandı, bu yüzden bir makale yazmaya karar verildi. Henüz çok fazla bilgi olmadığından bu makale ilgilenen herkesin zamandan tasarruf etmesine yardımcı olacaktır.

TON'da akıllı sözleşmeler kavramı

Herhangi bir şey yazmadan önce bu konuya hangi taraftan yaklaşacağınızı bulmanız gerekir. Bu nedenle şimdi size sistemin hangi parçalardan oluştuğunu anlatacağım. Daha doğrusu, en azından bir tür çalışma sözleşmesi yazmak için hangi kısımları bilmeniz gerekir.

Akıllı bir sözleşme yazmaya ve birlikte çalışmaya odaklanacağız. TON Virtual Machine (TVM), Fift и FunCyani makale daha çok normal bir programın geliştirilmesinin bir açıklamasına benziyor. Burada platformun nasıl çalıştığı üzerinde durmayacağız.

Genel olarak nasıl çalıştığı hakkında TVM ve dil Fift iyi resmi belgeler var. Yarışmaya katılırken ve şimdi mevcut sözleşmeyi yazarken sık sık ona başvurdum.

Akıllı sözleşmelerin yazıldığı ana dil FunC. Şu anda bununla ilgili bir belge yok, dolayısıyla bir şeyler yazmak için resmi depodaki akıllı sözleşme örneklerini ve orada dilin uygulanmasını incelemeniz gerekiyor, ayrıca son ikisinden akıllı sözleşme örneklerine bakabilirsiniz. yarışmalar. Bağlantılar yazının sonunda.

Diyelim ki zaten bir akıllı sözleşme yazdık. FunCBundan sonra kodu Fift assembler'a derliyoruz.

Derlenen akıllı sözleşme henüz yayınlanmayı bekliyor. Bunu yapmak için bir fonksiyon yazmanız gerekir. Fiftakıllı sözleşme kodunu ve diğer bazı parametreleri girdi olarak alacak ve çıktı, uzantılı bir dosya olacaktır. .boc ("hücre torbası" anlamına gelir) ve onu nasıl yazdığımıza bağlı olarak akıllı sözleşme koduna göre oluşturulan özel bir anahtar ve adres. Henüz yayınlanmamış bir akıllı sözleşmenin adresine zaten gram gönderebilirsiniz.

Alınan TON cinsinden bir akıllı sözleşme yayınlamak için .boc dosyanın hafif bir istemci kullanılarak blok zincirine gönderilmesi gerekecektir (bununla ilgili daha fazla bilgi aşağıdadır). Ancak yayınlamadan önce gramları oluşturulan adrese aktarmanız gerekiyor, aksi takdirde akıllı sözleşme yayınlanmayacaktır. Yayınlandıktan sonra, dışarıdan (örneğin, hafif istemci kullanarak) veya içeriden (örneğin, bir akıllı sözleşmenin TON içinde diğerine bir mesaj göndermesi) mesajlar göndererek akıllı sözleşmeyle etkileşime girebilirsiniz.

Kodun nasıl yayınlandığını anladığımızda işimiz kolaylaşır. Ne yazmak istediğimizi ve programımızın nasıl çalışacağını kabaca biliyoruz. Ve yazarken bunun mevcut akıllı sözleşmelerde zaten nasıl uygulandığına bakıyoruz veya uygulama koduna bakıyoruz Fift и FunC resmi depoda veya resmi belgelere bakın.

Tüm yarışma katılımcılarının ve Telegram çalışanlarının bir araya geldiği Telegram sohbetinde sıklıkla anahtar kelimeler aradım ve öyle oldu ki yarışma sırasında herkes orada toplandı ve Fift ve FunC'yi tartışmaya başladı. Bağlantı makalenin sonunda.

Teoriden pratiğe geçmenin zamanı geldi.

TON ile çalışmaya ortamı hazırlamak

MacOS'taki makalede anlatılacak her şeyi yaptım ve Docker'daki temiz Ubuntu 18.04 LTS'de iki kez kontrol ettim.

Yapmanız gereken ilk şey indirip yüklemek lite-client TON'a istek gönderebilirsiniz.

Resmi web sitesindeki talimatlar, kurulum sürecini oldukça ayrıntılı ve net bir şekilde açıklıyor ve bazı ayrıntıları atlıyor. Burada talimatları takip ederek eksik bağımlılıkları yol boyunca kuruyoruz. Her projeyi kendim derlemedim ve resmi Ubuntu deposundan yüklemedim (MacOS'ta kullandım) 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 

Tüm bağımlılıklar yüklendikten sonra yükleyebilirsiniz lite-client, Fift, FunC.

Öncelikle TON deposunu bağımlılıklarıyla birlikte klonlarız. Kolaylık sağlamak için her şeyi bir klasörde yapacağız ~/TON.

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

Depo aynı zamanda uygulamaları da saklar Fift и FunC.

Artık projeyi birleştirmeye hazırız. Depo kodu bir klasöre kopyalanır ~/TON/ton. ~/TON bir klasör oluştur build ve projeyi içinde toplayın.

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

Akıllı bir sözleşme yazacağımız için sadece ihtiyacımız yok lite-clientama Fift с FunC, o halde her şeyi derleyelim. Hızlı bir süreç değil, o yüzden bekliyoruz.

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

Daha sonra, bağlanacağı düğümle ilgili verileri içeren yapılandırma dosyasını indirin. lite-client bağlanacak.

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

TON'a ilk talepler yapıldı

Şimdi başlayalım lite-client.

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

Derleme başarılıysa, başlattıktan sonra hafif istemcinin düğüme bağlantısının bir günlüğünü göreceksiniz.

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

Komutu çalıştırabilirsiniz help ve hangi komutların mevcut olduğunu görün.

help

Bu yazımızda kullanacağımız komutları listeleyelim.

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

last получает последний созданный блок с сервера. 

sendfile <filename> отправляет в TON файл с сообщением, именно с помощью этой команды публикуется смарт-контракт и запрсосы к нему. 

getaccount <addr> загружает текущее состояние смарт-контракта с указанным адресом. 

runmethod <addr> [<block-id-ext>] <method-id> <params>  запускает get-методы смартконтракта. 

Artık sözleşmenin kendisini yazmaya hazırız.

uygulama

Fikir

Yukarıda da yazdığım gibi yazdığımız akıllı sözleşme bir piyangodur.

Üstelik bu, bilet alıp bir saat, gün veya ay beklemeniz gereken bir piyango değil, kullanıcının sözleşme adresine transfer yaptığı anlık bir piyangodur. N gram alır ve anında geri alır 2 * N gram veya kaybeder. Kazanma olasılığını %40 civarında yapacağız. Ödeme için yeterli gram yoksa işlemi yükleme olarak değerlendireceğiz.

Üstelik bahislerin gerçek zamanlı ve uygun bir biçimde görülebilmesi önemlidir, böylece kullanıcı kazanıp kazanmadığını hemen anlayabilir. Bu nedenle bahisleri ve sonuçları doğrudan TON'dan gösterecek bir web sitesi yapmanız gerekir.

Akıllı sözleşme yazmak

Kolaylık sağlamak için FunC kodunu vurguladım; eklenti Visual Studio Code aramasında bulunabilir ve kurulabilir; aniden bir şey eklemek isterseniz eklentiyi herkese açık hale getirdim. Ayrıca birisi daha önce Fift ile çalışmak için bir eklenti yapmıştı, onu da yükleyebilir ve VSC'de bulabilirsiniz.

Hemen ara sonuçları işleyeceğimiz bir depo oluşturalım.

Hayatımızı kolaylaştırmak için akıllı bir sözleşme yazacağız ve hazır olana kadar yerel olarak test edeceğiz. Ancak bundan sonra TON'da yayınlayacağız.

Akıllı sözleşmenin erişilebilecek iki harici yöntemi vardır. Birinci, recv_external() bu işlev, sözleşmeye yönelik bir talep dış dünyadan geldiğinde, yani TON'dan değil, örneğin kendimiz bir mesaj oluşturup onu lite-istemci aracılığıyla gönderdiğimizde gerçekleştirilir. Saniye, recv_internal() bu, TON'un kendi içinde herhangi bir sözleşmenin bizim sözleşmemize atıfta bulunduğu zamandır. Her iki durumda da fonksiyona parametreler iletebilirsiniz.

Yayınlanırsa işe yarayacak, ancak içinde işlevsel bir yük bulunmayan basit bir örnekle başlayalım.

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

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

Burada ne olduğunu açıklamamız gerekiyor. slice. TON Blockchain'de saklanan tüm veriler bir koleksiyondur TVM cell veya basitçe cell, böyle bir hücrede 1023 bit'e kadar veri ve diğer hücrelere en fazla 4 bağlantı depolayabilirsiniz.

TVM cell slice veya slice bu mevcut olanın bir parçası cell ayrıştırmak için kullanılır, daha sonra netleşecektir. Bizim için önemli olan transfer yapabilmemiz. slice ve mesajın türüne bağlı olarak verileri işleyin recv_external() veya recv_internal().

impure — işlevin akıllı sözleşme verilerini değiştirdiğini belirten bir anahtar kelime.

Sözleşme kodunu kaydedelim lottery-code.fc ve derleyin.

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

Bayrakların anlamları şu komut kullanılarak görüntülenebilir:

~/TON/build/crypto/func -help

Fift assembler kodunu derledik. 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

Yerel olarak da devreye alınabilir, bunun için ortamı hazırlayacağız.

İlk satırın bağlandığını unutmayın Asm.fifBu, Fift for the Fift derleyicisinde yazılmış koddur.

Akıllı sözleşmeyi yerel olarak çalıştırmak ve test etmek istediğimiz için bir dosya oluşturacağız. lottery-test-suite.fif ve akıllı sözleşme kodunu sabit bir değere yazan son satırı değiştirerek derlenmiş kodu buraya kopyalayın. codedaha sonra sanal makineye aktarmak için:

"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

Buraya kadar açık görünüyor, şimdi aynı dosyaya TVM'yi başlatmak için kullanacağımız kodu ekleyelim.

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

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

В c7 bağlamı, yani TVM'nin (veya ağ durumunun) başlatılacağı verileri kaydederiz. Yarışma sırasında bile geliştiricilerden biri nasıl yaratılacağını gösterdi c7 ve kopyaladım. Bu yazıda değişiklik yapmamız gerekebilir rand_seed Rastgele bir sayının üretilmesi buna bağlı olduğundan ve değiştirilmediği takdirde her defasında aynı sayı döndürülecektir.

recv_internal и recv_external 0 ve -1 değerlerine sahip sabitler, akıllı sözleşmedeki ilgili işlevlerin çağrılmasından sorumlu olacaktır.

Artık boş akıllı sözleşmemiz için ilk testi oluşturmaya hazırız. Anlaşılır olması açısından şimdilik tüm testleri aynı dosyaya ekleyeceğiz lottery-test-suite.fif.

Bir değişken oluşturalım storage ve içine boş bir tane yaz cell, bu akıllı sözleşme depolaması olacak.

message Dışarıdan akıllı kontağa ileteceğimiz mesaj budur. Burayı da şimdilik boş yapacağız.

variable storage 
<b b> storage ! 

variable message 
<b b> message ! 

Sabitleri ve değişkenleri hazırladıktan sonra şu komutu kullanarak TVM'yi başlatıyoruz. runvmctx ve oluşturulan parametreleri girişe iletin.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

Sonunda başaracağız burada bir için ara kod Fift.

Artık ortaya çıkan kodu çalıştırabiliriz.

export FIFTPATH=~/TON/ton/crypto/fift/lib // выполняем один раз для удобства 
~/TON/build/crypto/fift -s lottery-test-suite.fif 

Program hatasız çalışmalıdır ve çıktıda yürütme günlüğünü göreceğiz:

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

Harika, akıllı sözleşmenin ilk çalışan versiyonunu yazdık.

Şimdi işlevsellik eklememiz gerekiyor. Öncelikle dış dünyadan gelen mesajları ele alalım. recv_external()

Geliştirici, sözleşmenin kabul edebileceği mesaj formatını kendisi seçer.

Ama genellikle

  • Öncelikle sözleşmemizi dış dünyadan korumak ve ona yalnızca sözleşme sahibinin harici mesaj gönderebilmesini sağlamak istiyoruz.
  • İkincisi, TON'a geçerli bir mesaj gönderdiğimizde bunun tam olarak bir kez olmasını istiyoruz ve aynı mesajı tekrar gönderdiğimizde akıllı sözleşme bunu reddediyor.

Yani neredeyse her sözleşme bu iki sorunu çözüyor, sözleşmemiz dışarıdan gelen mesajları kabul ettiği için buna da dikkat etmemiz gerekiyor.

Bunu ters sırayla yapacağız. Öncelikle sorunu tekrarlayarak çözelim; eğer sözleşme zaten böyle bir mesaj almışsa ve bunu işlemişse ikinci kez çalıştırmayacaktır. Ardından akıllı sözleşmeye yalnızca belirli bir grup insanın mesaj gönderebilmesi için sorunu çözeceğiz.

Yinelenen iletilerle ilgili sorunu çözmenin farklı yolları vardır. Bunu nasıl yapacağımız aşağıda açıklanmıştır. Akıllı sözleşmede alınan mesajların sayacını başlangıç ​​değeri 0 ile başlatıyoruz. Akıllı sözleşmeye gönderilen her mesaja mevcut sayaç değerini ekleyeceğiz. Mesajdaki sayaç değeri akıllı sözleşmedeki değerle eşleşmiyorsa işleme almayız, eşleşiyorsa işleyip akıllı sözleşmedeki sayacı 1 artırırız.

Hadi geri dönelim lottery-test-suite.fif ve buna ikinci bir test ekleyin. Yanlış bir numara gönderirsek kodun bir istisna atması gerekir. Örneğin, sözleşme verilerinin 166'yı saklamasına izin verin, biz de 165'i göndereceğiz.

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

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx

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

Hadi başlayalım.

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

Ve testin hatalı yürütüldüğünü göreceğiz.

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

Bu aşamada lottery-test-suite.fif şöyle görünmeli по ссылке.

Şimdi sayaç mantığını akıllı sözleşmeye ekleyelim. lottery-code.fc.

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

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

В slice in_msg gönderdiğimiz mesaj yatıyor.

Yaptığımız ilk şey mesajın veri içerip içermediğini kontrol etmek, eğer değilse, sonra çıkıyoruz.

Daha sonra mesajı ayrıştırıyoruz. in_msg~load_uint(32) 165 sayısını yükler, 32 bit unsigned int iletilen mesajdan.

Daha sonra akıllı sözleşme deposundan 32 bit yüklüyoruz. Yüklenen sayının iletilen sayıyla eşleşip eşleşmediğini kontrol ederiz; değilse bir istisna atarız. Bizim durumumuzda eşleşmeyen bir durumu geçtiğimiz için bir istisna atılmalıdır.

Şimdi derleyelim.

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

Ortaya çıkan kodu kopyalayın lottery-test-suite.fif, son satırı değiştirmeyi unutmamak.

Testin geçtiğini kontrol ediyoruz:

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

Burada Mevcut sonuçlarla ilgili taahhüdü görebilirsiniz.

Akıllı sözleşmenin derlenmiş kodunu sürekli olarak testlerin bulunduğu bir dosyaya kopyalamanın sakıncalı olduğunu unutmayın, bu nedenle kodu bizim için bir sabite yazacak bir komut dosyası yazacağız ve derlenmiş kodu kullanarak testlerimize bağlayacağız. "include".

Proje klasöründe bir dosya oluşturun build.sh aşağıdaki içerikle.

#!/bin/bash

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

Çalıştırılabilir hale getirelim.

chmod +x ./build.sh

Şimdi sözleşmeyi derlemek için betiğimizi çalıştırın. Ama bunun yanında bunu bir sabite yazmamız gerekiyor. code. Böylece yeni bir dosya oluşturacağız lotter-compiled-for-test.fif, dosyaya dahil edeceğimiz lottery-test-suite.fif.

Derlenmiş dosyayı basitçe çoğaltacak sh'ye etek kodu ekleyelim. lotter-compiled-for-test.fif ve içindeki son satırı değiştirin.

# 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

Şimdi kontrol etmek için ortaya çıkan betiği çalıştıralım ve bir dosya oluşturulacak lottery-compiled-for-test.fifprogramımıza dahil edeceğimiz lottery-test-suite.fif

В lottery-test-suite.fif sözleşme kodunu silin ve satırı ekleyin "lottery-compiled-for-test.fif" include.

Geçtiklerini kontrol etmek için testler yapıyoruz.

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

Harika, şimdi testlerin başlatılmasını otomatikleştirmek için bir dosya oluşturalım test.sh, ilk olarak yürütülecek olan build.shve ardından testleri çalıştırın.

touch test.sh
chmod +x test.sh

İçeriye yazıyoruz

./build.sh 

echo "nCompilation completedn"

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

Biz yapmak test.sh ve testlerin çalıştığından emin olmak için çalıştırın.

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

Sözleşmenin derlendiğini ve testlerin yürütüldüğünü kontrol ediyoruz.

Harika, şimdi başlangıçta test.sh Testler derhal derlenecek ve çalıştırılacaktır. İşte bağlantı işlemek.

Tamam, devam etmeden önce kolaylık olması açısından bir şey daha yapalım.

Bir klasör oluşturalım build kopyalanan sözleşmeyi ve onun sabit bir dosyaya yazılan kopyasını nerede saklayacağız? lottery-compiled.fif, lottery-compiled-for-test.fif. Ayrıca bir klasör oluşturalım test test dosyası nerede saklanacak? lottery-test-suite.fif ve potansiyel olarak diğer destekleyici dosyalar. İlgili değişikliklere bağlantı.

Akıllı sözleşmeyi geliştirmeye devam edelim.

Daha sonra mesajın alındığını ve doğru numarayı gönderdiğimizde mağazadaki sayacın güncellendiğini kontrol eden bir test yapılmalıdır. Ama bunu daha sonra yapacağız.

Şimdi akıllı sözleşmede hangi veri yapısının ve hangi verilerin saklanması gerektiğini düşünelim.

Sakladığımız her şeyi anlatacağım.

`seqno` 32-х битное целое положительное число счетчик. 

`pubkey` 256-ти битное целое положительное число публичный ключ, с помощью которого, мы будем проверять подпись отправленного извне сообщения, о чем ниже. 

`order_seqno` 32-х битное целое положительное число хранит счетчик количества ставок. 

`number_of_wins` 32-х битное целое положительное число хранит  количество побед. 

`incoming_amount` тип данных Gram (первые 4 бита отвечает за длину), хранит общее количество грамов, которые были отправлены на контртакт. 

`outgoing_amount` общее количество грамов, которое было отправлено победителям. 

`owner_wc` номер воркчейна, 32-х битное (в некоторых местах написано, что 8-ми битное) целое число. В данный момент всего два -1 и 0. 

`owner_account_id` 256-ти битное целое положительное число, адрес контракта в текущем воркчейне. 

`orders` переменная типа словарь, хранит последние двадцать ставок. 

Daha sonra iki fonksiyon yazmanız gerekir. İlkini arayalım pack_state(), akıllı sözleşme depolama alanına daha sonra kaydedilmek üzere verileri paketleyecektir. İkinciyi arayalım unpack_state() depolama alanındaki verileri okuyacak ve döndürecektir.

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

Bu iki fonksiyonu akıllı sözleşmenin başına ekliyoruz. Bu işe yarayacak burada bir ara sonuç.

Verileri kaydetmek için yerleşik işlevi çağırmanız gerekecektir. set_data() ve buradan veri yazacak pack_state() akıllı sözleşme deposunda.

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

Artık veri yazma ve okuma için kullanışlı işlevlere sahip olduğumuza göre devam edebiliriz.

Dışarıdan gelen mesajın sözleşme sahibi (veya özel anahtara erişimi olan başka bir kullanıcı) tarafından imzalanıp imzalanmadığını kontrol etmemiz gerekiyor.

Akıllı bir sözleşme yayınladığımızda, onu gelecekte kullanmak üzere saklanacak olan depolama alanında ihtiyaç duyduğumuz verilerle başlatabiliriz. Gelen mesajın ilgili özel anahtarla imzalandığını doğrulayabilmemiz için genel anahtarı buraya kaydedeceğiz.

Devam etmeden önce özel bir anahtar oluşturalım ve onu yazalım. test/keys/owner.pk. Bunu yapmak için Fift'i etkileşimli modda başlatalım ve dört komutu çalıştıralım.

`newkeypair` генерация публичного и приватного ключа и запись их в стек. 

`drop` удаления из стека верхнего элемента (в данном случае публичный ключ)  

`.s` просто посмотреть что лежит в стеке в данный момент 

`"owner.pk" B>file` запись приватного ключа в файл с именем `owner.pk`. 

`bye` завершает работу с Fift. 

Bir klasör oluşturalım keys klasörün içinde test ve özel anahtarı oraya yazın.

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

Geçerli klasörde bir dosya görüyoruz owner.pk.

Genel anahtarı yığından kaldırıyoruz ve gerektiğinde onu özel olandan alabiliyoruz.

Şimdi bir imza doğrulaması yazmamız gerekiyor. Testle başlayalım. İlk önce fonksiyonu kullanarak dosyadaki özel anahtarı okuyoruz. file>B ve bunu bir değişkene yaz owner_private_key, ardından işlevi kullanarak priv>pub özel anahtarı genel anahtara dönüştürün ve sonucu yazın owner_public_key.

variable owner_private_key
variable owner_public_key 

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

Her iki anahtara da ihtiyacımız olacak.

Akıllı sözleşme depolamasını isteğe bağlı verilerle işlevdekiyle aynı sırayla başlatıyoruz pack_state()ve bunu bir değişkene yaz storage.

variable owner_private_key
variable owner_public_key 
variable orders
variable owner_wc
variable owner_account_id

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

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

Daha sonra imzalı bir mesaj oluşturacağız, bu mesaj yalnızca imzayı ve sayaç değerini içerecek.

Öncelikle iletmek istediğimiz veriyi oluşturuyoruz, ardından onu özel anahtarla imzalıyoruz ve son olarak imzalı bir mesaj oluşturuyoruz.

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

Sonuç olarak akıllı sözleşmeye göndereceğimiz mesaj bir değişkene kaydedilmektedir. message_to_send, işlevler hakkında hashu, ed25519_sign_uint okuyabilir Beşinci belgelerde.

Ve testi çalıştırmak için tekrar çağırıyoruz.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

Böyle Testlerin bulunduğu dosya bu aşamada bu şekilde görünmelidir.

Testi çalıştıralım ve başarısız olacak, bu nedenle akıllı sözleşmeyi bu formattaki mesajları alabilecek ve imzayı doğrulayabilecek şekilde değiştireceğiz.

Öncelikle mesajdaki imzanın 512 bitini sayıp bir değişkene yazıyoruz, ardından sayaç değişkeninin 32 bitini sayıyoruz.

Akıllı sözleşme deposundan veri okumaya yönelik bir fonksiyonumuz olduğundan onu kullanacağız.

Daha sonra depoya aktarılan sayacın kontrol edilmesi ve imzanın kontrol edilmesi gelir. Bir şey eşleşmezse uygun kodla bir istisna atarız.

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

İlgili taahhüt burada.

Testleri çalıştıralım ve ikinci testin başarısız olduğunu görelim. İki nedenden dolayı, mesajda yeterli bit yok ve depolamada da yeterli bit yok, dolayısıyla kod ayrıştırma sırasında çöküyor. Gönderdiğimiz mesaja imza eklememiz ve son testteki depolamayı kopyalamamız gerekiyor.

İkinci testimizde mesaj imzası ekleyeceğiz ve akıllı sözleşme depolama alanını değiştireceğiz. Böyle testlerin bulunduğu dosya şu anki gibi görünüyor.

Başka birinin özel anahtarıyla imzalanmış bir mesaj göndereceğimiz dördüncü bir test yazalım. Başka bir özel anahtar oluşturup onu bir dosyaya kaydedelim not-owner.pk. Mesajı bu özel anahtarla imzalayacağız. Testleri çalıştıralım ve tüm testlerin geçtiğinden emin olalım. İşlemek şu anda.

Artık nihayet akıllı sözleşme mantığını uygulamaya geçebiliriz.
В recv_external() iki tür mesajı kabul edeceğiz.

Sözleşmemiz oyuncuların kayıplarını biriktireceğinden bu paranın piyangoyu düzenleyen kişiye aktarılması gerekmektedir. Piyango yaratıcısının cüzdan adresi, sözleşme oluşturulduğunda depoya kaydedilir.

Her ihtimale karşı, kaybedenlerin gramlarını gönderdiğimiz adresi değiştirme yeteneğine ihtiyacımız var. Ayrıca piyangodan gramları sahibinin adresine gönderebilmeliyiz.

İlkiyle başlayalım. Öncelikle mesajı gönderdikten sonra akıllı sözleşmenin yeni adresi depoya kaydedip kaydetmediğini kontrol edecek bir test yazalım. Lütfen mesajda sayaç ve yeni adresin yanı sıra aynı zamanda bilgileri de ilettiğimizi unutmayın. action 7 bitlik negatif olmayan bir tam sayı, buna bağlı olarak akıllı sözleşmede mesajın nasıl işleneceğini seçeceğiz.

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

Testte akıllı sözleşme depolamasının nasıl seri durumdan çıkarıldığını görebilirsiniz storage Beşli'de. Değişkenlerin seri durumdan çıkarılması, Fift belgelerinde açıklanmaktadır.

Bağlantıyı kaydet hamur ilavesiyle.

Testi çalıştıralım ve başarısız olduğundan emin olalım. Şimdi piyango sahibinin adresini değiştirme mantığını ekleyelim.

Akıllı sözleşmede ayrıştırmaya devam ediyoruz message, oku action. İki tane olacağımızı hatırlatalım. action: adresi değiştirin ve gramı gönderin.

Daha sonra sözleşme sahibinin yeni adresini okuyup depoya kaydediyoruz.
Testleri yapıyoruz ve üçüncü testin başarısız olduğunu görüyoruz. Sözleşmenin artık ek olarak mesajdan testte eksik olan 7 biti ayrıştırması nedeniyle çöküyor. Mesaja var olmayan bir tane ekleyin action. Testleri yapalım ve her şeyin geçtiğini görelim. Burada değişiklikleri taahhüt edin. Harika.

Şimdi belirtilen sayıda gramın daha önce kaydettiğimiz adrese gönderilmesinin mantığını yazalım.

Öncelikle bir test yazalım. Biri yeterli bakiye olmadığında, ikincisi her şeyin başarılı bir şekilde geçmesi gerektiğinde olmak üzere iki test yazacağız. Testler görüntülenebilir bu taahhütte.

Şimdi kodu ekleyelim. Öncelikle iki yardımcı yöntem yazalım. İlk get yöntemi akıllı sözleşmenin mevcut bakiyesini bulmaktır.

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

İkincisi ise gramları başka bir akıllı sözleşmeye göndermek içindir. Bu yöntemi başka bir akıllı sözleşmeden tamamen kopyaladım.

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

Bu iki yöntemi akıllı sözleşmeye ekleyelim ve mantığını yazalım. Öncelikle mesajdaki gram sayısını ayrıştırıyoruz. Daha sonra dengeyi kontrol ediyoruz, yeterli değilse bir istisna atıyoruz. Her şey yolundaysa gramları kayıtlı adrese gönderip sayacı güncelliyoruz.

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

Böyle şu anda akıllı sözleşmeye benziyor. Testleri yapalım ve geçtiklerinden emin olalım.

Bu arada, işlenen bir mesaj için akıllı sözleşmeden her defasında bir komisyon düşülüyor. Akıllı sözleşme mesajlarının isteği yerine getirebilmesi için temel kontrollerden sonra aramanız gerekir. accept_message().

Şimdi dahili mesajlara geçelim. Aslında sadece gram kabul edeceğiz ve kazanırsa miktarın iki katını oyuncuya, kaybederse sahibine üçte birini geri göndereceğiz.

Öncelikle basit bir test yazalım. Bunu yapmak için, akıllı sözleşmeye gram göndereceğimiz varsayılan akıllı sözleşmenin bir test adresine ihtiyacımız var.

Akıllı sözleşme adresi, çalışma zincirinden sorumlu 32 bitlik bir tam sayı ve bu çalışma zincirindeki 256 bitlik negatif olmayan tam sayı benzersiz hesap numarası olmak üzere iki sayıdan oluşur. Örneğin -1 ve 12345 bir dosyaya kaydedeceğimiz adrestir.

Adresi kaydetme işlevini kopyaladım 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

Şimdi fonksiyonun nasıl çalıştığına bakalım, bu Fift'in nasıl çalıştığına dair bir fikir verecektir. Fift'i etkileşimli modda başlatın.

~/TON/build/crypto/fift -i 

İlk önce -1, 12345'i ve gelecekteki "sender.addr" dosyasının adını yığına basıyoruz:

-1 12345 "sender.addr" 

Bir sonraki adım işlevi yürütmektir -rotyığını, yığının en üstünde benzersiz bir akıllı sözleşme numarası olacak şekilde kaydırır:

"sender.addr" -1 12345

256 u>B 256 bit negatif olmayan bir tam sayıyı bayta dönüştürür.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap yığının en üstteki iki elemanını değiştirir.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B 32 bitlik bir tam sayıyı bayta dönüştürür.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ iki bayt dizisini birbirine bağlar.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

tekrar swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Ve son olarak baytlar dosyaya yazılır B>file. Bundan sonra yığınımız boş. Durduk Fift. Geçerli klasörde bir dosya oluşturuldu sender.addr. Dosyayı oluşturulan klasöre taşıyalım test/addresses/.

Akıllı bir sözleşmeye gram gönderecek basit bir test yazalım. İşte taahhüt.

Şimdi piyango mantığına bakalım.

Yaptığımız ilk şey mesajı kontrol etmek bounced ya da değilse bouncedsonra görmezden geliriz. bounced bir hata oluşması durumunda sözleşmenin gram döndüreceği anlamına gelir. Aniden bir hata meydana gelirse gramı iade etmeyeceğiz.

Bakiyenin yarım gramdan az olup olmadığını kontrol ediyoruz, ardından mesajı kabul edip görmezden geliyoruz.

Daha sonra mesajın geldiği akıllı sözleşmenin adresini ayrıştırıyoruz.

Verileri depodan okuruz ve ardından yirmiden fazla varsa eski bahisleri geçmişten sileriz. Kolaylık sağlamak için üç ek işlev yazdım pack_order(), unpack_order(), remove_old_orders().

Daha sonra, bakiyenin ödeme için yeterli olup olmadığına bakarız, ardından bunun bir bahis değil, bir yenileme olduğunu düşünürüz ve yenilemeyi kaydederiz. orders.

Ve son olarak akıllı sözleşmenin özü.

Öncelikle oyuncu kaybederse bahis geçmişine kaydediyoruz ve miktar 3 gramdan fazla ise akıllı sözleşme sahibine 1/3'ünü gönderiyoruz.

Oyuncu kazanırsa, tutarın iki katını oyuncunun adresine gönderiyoruz ve ardından bahisle ilgili bilgileri geçmişe kaydediyoruz.

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

Hepsi bu kadar. İlgili taahhüt.

Şimdi geriye kalan tek şey basit, hadi dış dünyadan sözleşmenin durumu hakkında bilgi alabilmemiz için (aslında akıllı sözleşme deposundaki verileri okuyabilmemiz için) get-yöntemleri oluşturalım.

Get metodlarını ekleyelim. Akıllı sözleşme hakkında nasıl bilgi alınacağını aşağıda yazacağız.

Ayrıca akıllı sözleşme yayınlarken ortaya çıkan ilk isteği işleyecek kodu da eklemeyi unuttum. İlgili taahhüt. Ve ilerisi düzeltilmiş Tutarın 1/3'ünün sahibinin hesabına gönderilmesiyle ilgili hata.

Bir sonraki adım akıllı sözleşmeyi yayınlamaktır. Bir klasör oluşturalım requests.

Yayın kodunu esas aldım simple-wallet-code.fc hangi mevcut resmi depoda.

Dikkat etmeye değer bir şey. Akıllı bir sözleşme depolama alanı ve bir giriş mesajı oluşturuyoruz. Bundan sonra akıllı sözleşmenin adresi oluşturulur, yani adres TON'da yayınlanmadan önce bile bilinir. Daha sonra, bu adrese birkaç gram göndermeniz gerekir ve ancak bundan sonra akıllı sözleşmenin kendisini içeren bir dosya göndermeniz gerekir, çünkü ağ, akıllı sözleşmeyi ve içindeki işlemleri depolamak için bir komisyon alır (akıllı sözleşmeyi saklayan ve yürüten doğrulayıcılar) sözleşmeler). Kod burada görülebilir.

Daha sonra yayınlama kodunu çalıştırıyoruz ve lottery-query.boc akıllı sözleşme dosyası ve adresi.

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

Oluşturulan dosyaları kaydetmeyi unutmayın: lottery-query.boc, lottery.addr, lottery.pk.

Diğer şeylerin yanı sıra akıllı sözleşmenin adresini yürütme günlüklerinde göreceğiz.

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

Sırf eğlence olsun diye TON'a bir istekte bulunalım

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

Ve bu adrese sahip hesabın boş olduğunu göreceğiz.

account state is empty

Adrese gönderiyoruz 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 Gram ve birkaç saniye sonra aynı komutu uyguluyoruz. Gram göndermek için kullanıyorum resmi cüzdan, ve yazının sonunda bahsedeceğim test gramlarını sohbetten birinden isteyebilirsiniz.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Başlatılmamış gibi görünüyor (state:account_uninit) aynı adrese ve 1 nanogram bakiyeye sahip bir akıllı sözleşme.

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

Şimdi akıllı sözleşmeyi yayınlayalım. Lite-client'ı başlatalım ve çalıştıralım.

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

Sözleşmenin yayınlanıp yayınlanmadığını kontrol edelim.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Aldığımız diğer şeylerin yanı sıra.

  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

bunu görüyoruz account_active.

Değişikliklerle karşılık gelen taahhüt burada.

Şimdi akıllı sözleşmeyle etkileşime geçmek için istekler oluşturalım.

Daha doğrusu ilkini adres değiştirmek için bağımsız bir çalışma olarak bırakacağız, ikincisini ise gramın sahibinin adresine gönderilmesi için yapacağız. Aslında gram gönderme testindeki işlemin aynısını yapmamız gerekecek.

Akıllı sözleşmeye göndereceğimiz mesaj budur. msg_seqno 165, action Göndermek için 2 ve 9.5 gram.

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

Mesajı özel anahtarınızla imzalamayı unutmayın lottery.pk, daha önce akıllı sözleşme oluşturulurken oluşturulmuştu. İşte karşılık gelen taahhüt.

Get yöntemlerini kullanarak akıllı bir sözleşmeden bilgi alma

Şimdi akıllı sözleşme get yöntemlerinin nasıl çalıştırılacağına bakalım.

koşmak lite-client ve yazdığımız get metodlarını çalıştırın.

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

В result işlevin döndürdüğü değeri içerir balance() akıllı sözleşmemizden.
Aynısını birkaç yöntem için daha yapacağız.

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

Bahis geçmişinizi soralım.

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

Sitede akıllı sözleşmeyle ilgili bilgileri görüntülemek için lite-client'i kullanacağız ve yöntemler alacağız.

Akıllı sözleşme verilerinin web sitesinde görüntülenmesi

Akıllı sözleşmedeki verileri uygun bir şekilde görüntülemek için Python'da basit bir web sitesi yazdım. Burada bunun üzerinde ayrıntılı olarak durmayacağım ve siteyi yayınlayacağım tek bir taahhütte.

TON'a talepler şu adresten yapılır: Python üzerinden lite-client. Kolaylık sağlamak için site Docker'da paketlenmiştir ve Google Cloud'da yayınlanmıştır. Bağlantı.

denemek

Şimdi ikmal için oraya gram göndermeye çalışalım. cüzdan. 40 gram göndereceğiz. Ve netlik sağlamak için birkaç bahis yapalım. Sitenin bahis geçmişini, mevcut kazanma yüzdesini ve diğer faydalı bilgileri gösterdiğini görüyoruz.

Görürüzbirinciyi kazandık, ikinciyi kaybettik.

Послесловие

Makalenin beklediğimden çok daha uzun olduğu ortaya çıktı, belki daha kısa olabilirdi veya belki sadece TON hakkında hiçbir şey bilmeyen ve etkileşime girme yeteneğine sahip, o kadar da basit olmayan bir akıllı sözleşme yazıp yayınlamak isteyen bir kişi için olabilirdi. BT. Belki bazı şeyler daha basit anlatılabilirdi.

Belki uygulamanın bazı yönleri daha verimli ve zarif bir şekilde yapılabilirdi, ancak o zaman makaleyi hazırlamak daha da fazla zaman alırdı. Bir yerde hata yapmış olmam veya bir şeyi anlamamış olmam da mümkündür, bu nedenle ciddi bir şey yapıyorsanız, resmi belgelere veya TON kodunun bulunduğu resmi depoya güvenmeniz gerekir.

TON'un kendisi hala aktif geliştirme aşamasında olduğundan, bu makaledeki adımlardan herhangi birini bozacak değişiklikler meydana gelebileceğini belirtmek gerekir (bu, ben yazarken oldu, zaten düzeltildi), ancak genel yaklaşım şu şekildedir: değişmesi pek mümkün değil.

TON'un geleceği hakkında konuşmayacağım. Belki de platform büyük bir şeye dönüşecek ve biz de onu incelemek için zaman harcamalı ve şimdi ürünlerimizle bir boşluk doldurmalıyız.

Ayrıca TON'dan daha büyük bir potansiyel kullanıcı kitlesine sahip olan Facebook'tan Libra da var. Libra hakkında neredeyse hiçbir şey bilmiyorum, foruma bakılırsa orada TON topluluğuna göre çok daha fazla aktivite var. Her ne kadar TON geliştiricileri ve topluluğu daha çok yeraltına benzese de, bu da harika.

referanslar

  1. Resmi TON belgeleri: https://test.ton.org
  2. Resmi TON deposu: https://github.com/ton-blockchain/ton
  3. Farklı platformlar için resmi cüzdan: https://wallet.ton.org
  4. Bu makaledeki akıllı sözleşme deposu: https://github.com/raiym/astonished
  5. Akıllı sözleşme web sitesine bağlantı: https://ton-lottery.appspot.com
  6. FunC için Visual Studio Code uzantısının deposu: https://github.com/raiym/func-visual-studio-plugin
  7. Telegram'da TON hakkında sohbet edin, bu da ilk aşamada bunu anlamanıza gerçekten yardımcı oldu. TON için bir şeyler yazan herkesin orada olduğunu söylersem sanırım hata olmaz. Oradan test gramlarını da isteyebilirsiniz. https://t.me/tondev_ru
  8. TON hakkında yararlı bilgiler bulduğum başka bir sohbet: https://t.me/TONgramDev
  9. Yarışmanın ilk aşaması: https://contest.com/blockchain
  10. Yarışmanın ikinci aşaması: https://contest.com/blockchain-2

Kaynak: habr.com

Yorum ekle