ProHoster > Blog > yönetim > Telegram Açık Ağı'nda (TON) akıllı sözleşmenin nasıl yazılacağı ve yayınlanacağı hakkında
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).
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.
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.
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.
// 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.
В 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.
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.
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.
Ş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.
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.
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.
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.
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));
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.
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.
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.
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));
}
Ş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.
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.
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.
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.
Ş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.
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.
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.
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