Artikel ini adalah terjemahan dari dokumen yang diterbitkan pada halaman TON blockchain: smc-guidelines.txt . Mungkin ini akan membantu seseorang untuk mengambil langkah menuju pengembangan untuk blockchain ini. Juga, pada akhirnya saya membuat ringkasan singkat.
Pesan internal
Kontrak pintar saling berinteraksi dengan mengirimkan apa yang disebut pesan internal. Ketika pesan internal mencapai tujuan yang ditentukan, transaksi reguler dibuat atas nama akun tujuan, dan pesan internal diproses sesuai dengan kode yang ditentukan dan data konstan dari akun ini (kontrak pintar). Secara khusus, transaksi pemrosesan dapat membuat satu atau lebih pesan internal, beberapa di antaranya mungkin ditujukan ke alamat sumber dari pesan internal yang sedang diproses. Ini dapat digunakan untuk membuat "aplikasi klien-server" sederhana ketika permintaan tertanam (dienkapsulasi) dalam pesan internal dan dikirim ke kontrak pintar lain yang memproses permintaan dan mengirimkan respons kembali, lagi sebagai pesan internal.
Pendekatan ini mengarah pada kebutuhan untuk membedakan antara pesan internal dengan "permintaan" dan "respons" (sebagai "kueri" atau sebagai "respons"), atau tidak memerlukan pemrosesan tambahan (seperti transfer uang sederhana). Selain itu, ketika jawaban datang, harus ada cara untuk memahami permintaan mana yang terkait.
Untuk mencapai tujuan ini, disarankan untuk menggunakan templat pesan internal berikut (ingat bahwa blockchain TON tidak memberlakukan batasan pada badan pesan, yaitu, itu hanya rekomendasi):
0) Badan pesan dapat tertanam dalam pesan itu sendiri, atau dapat disimpan dalam sel terpisah (sel *), yang dirujuk dalam pesan, seperti yang ditunjukkan dalam fragmen TL-B diagram (dalam bahasa Inggris lebih mudah dimengerti: atau disimpan dalam terpisah sel yang dirujuk dari pesan, seperti yang ditunjukkan oleh fragmen skema TL-B):
message$_ {X:Type} ... body:(Either X ^X) = Message X;
( https://core.telegram.org/mtproto - di sini Anda dapat membaca tentang skema TL)
Kontrak pintar penerima harus menerima setidaknya pesan internal dengan badan pesan tertanam (bahkan jika mereka ditempatkan di sel yang berisi pesan - setiap kali mereka masuk ke dalam sel yang mengandung pesan - tidak jelas apa artinya ini, oleh karena itu, lampirkan teks asli). Jika kontrak menerima badan pesan dalam sel yang terpisah (menggunakan konstruktor "kanan" (Either X ^X)
), pemrosesan pesan yang masuk tidak harus bergantung pada metode tertentu dalam menyematkan badan pesan. Di sisi lain, sangat sah untuk tidak mendukung badan pesan di sel yang terpisah sama sekali untuk menyederhanakan permintaan dan tanggapan.
1) Badan pesan biasanya dimulai dengan bidang-bidang berikut:
- op - integer unsigned 32-bit (big-endian) yang mengidentifikasi operasi yang akan dijalankan, atau metode kontrak pintar untuk memanggil.
- query_id adalah integer unsigned 64-bit (big-endian) yang digunakan dalam semua pesan pertanyaan dan jawaban internal untuk mengidentifikasi hubungan respons terhadap permintaan ( query_id dari respons harus sama dengan query_id dari permintaan yang sesuai). Jika op bukan metode respons permintaan (ia memanggil metode yang tidak diharapkan responsnya), maka kueri_id dapat dihilangkan.
- sisa isi pesan dikhususkan untuk setiap nilai parameter op yang didukung
2) Jika op adalah nol, maka pesannya adalah pesan transfer sederhana dengan komentar. Komentar terkandung dalam sisa pesan (tanpa query_id dan seterusnya, yaitu, dimulai dari byte ke-5 (penjelasan: jika query_id tidak, maka bidang op mengambil 4 byte pertama)). Jika tidak dimulai dengan byte 0xff, komentar adalah teks satu;); itu dapat ditampilkan kepada pengguna akhir dompet "apa adanya" (setelah memfilter karakter yang tidak valid dan mengontrol dan memeriksa bahwa ini adalah string UTF-8 yang valid). Misalnya, pengguna dapat menentukan tujuan transfer sederhana dari dompet mereka ke dompet pengguna lain di bidang ini. Di sisi lain, jika komentar dimulai dengan byte 0xff, sisa pesan adalah "komentar biner" yang tidak boleh ditampilkan kepada pengguna akhir sebagai teks (hanya sebagai hex dump jika perlu). Usulan penggunaan komentar biner, misalnya, berisi pengidentifikasi pembayaran untuk pembayaran di toko, dan secara otomatis dihasilkan dan diproses oleh perangkat lunak toko.
Sebagian besar kontrak pintar tidak harus melakukan tindakan nontrivial atau menolak pesan masuk ketika mereka menerima "pesan transfer sederhana". Jadi, ketika op ternyata nol, fungsi kontrak pintar untuk memproses pesan internal yang masuk (biasanya disebut recv_internal()
) harus segera keluar dengan kode 0, menunjukkan keberhasilan (misalnya, melempar pengecualian 0 jika handler kustom tidak dipasang dalam kontrak pintar pengecualian). Ini akan mengarah pada fakta bahwa jumlah yang ditransfer oleh pesan akan dikreditkan ke akun penerima tanpa efek lebih lanjut.
3) "Transfer pesan sederhana tanpa komentar" memiliki badan kosong (bahkan tanpa bidang op ). Pertimbangan di atas berlaku untuk pesan tersebut. Harap dicatat bahwa pesan tersebut harus memiliki tubuh mereka sendiri yang tertanam di dalam sel pesan.
4) Kami berharap bahwa bidang op pesan permintaan akan memiliki bit pertama ("bit orde tinggi", diterjemahkan sebagai yang pertama, ini mungkin salah, tetapi seperti yang dijelaskan kemudian menjadi jelas) kosong, artinya nilai bidang harus berada dalam kisaran 1 .. 2^31-1
, dan untuk pesan respon bit pertama (tingkat tinggi) harus sama dengan 1, yaitu, nilai bidang dalam kisaran 2^31 .. 2^32-1
. Jika pesan tersebut bukan permintaan atau respons (badan tidak mengandung parameter query_id ), maka itu harus berisi parameter op dalam rentang seperti dalam pesan permintaan: 1 .. 2^31 - 1
.
5) Ada beberapa pesan respons "standar" yang opnya 0xffffffff dan 0xffffffffe. Secara umum, nilai op dari 0xfffffff0 hingga 0xffffffff dicadangkan untuk jawaban standar tersebut.
- op = 0xffffffff berarti "operasi tidak didukung." Itu diikuti oleh 64-bit query_id diekstraksi dari permintaan asli, dan op 32-bit dari permintaan asli. Semua kecuali kontrak pintar yang paling sederhana akan mengembalikan kesalahan ini ketika mereka menerima permintaan dengan op yang tidak dikenal di kisaran 1 ... 2 ^ 31-1.
- op = 0xfffffffe berarti "operasi tidak diizinkan." Itu diikuti oleh query_id 64-bit dari permintaan asli, dan kemudian op 32-bit diekstraksi dari permintaan asli.
Perhatikan bahwa "jawaban" yang tidak diketahui (dengan op dalam kisaran 2 ^ 31 ... 2 ^ 32-1) harus diabaikan (khususnya, Anda tidak boleh menghasilkan respons dengan op sama dengan 0xffffffff), serta pengembalian yang tidak terduga ( bouncing) -messages (dengan set flag "bouncing").
Pembayaran untuk memproses permintaan dan mengirim balasan
Secara umum, jika kontrak pintar ingin mengirim permintaan ke kontrak pintar lain, ia harus membayar untuk mengirim pesan internal ke kontrak pintar target (biaya penerusan pesan), untuk memproses pesan ini di tempat tujuan (biaya gas: biaya gas) dan untuk mengirim respons jika diperlukan (biaya penerusan pesan).
Dalam kebanyakan kasus, pengirim akan melampirkan sejumlah kecil gram ke pesan internal (misalnya, 1 gram) (cukup untuk membayar memproses pesan ini) dan menetapkan bendera "bouncing" di atasnya (artinya, ia akan mengirim pesan internal yang terpental); penerima akan mengembalikan bagian yang tidak terpakai dari nilai yang diterima dengan jawabannya (mengurangi biaya untuk mengirim pesan darinya). Ini biasanya dicapai dengan memanggil SENDRAWMSG dengan mode = 64 (lih. Lampiran A untuk dokumentasi TON VM).
Jika penerima tidak dapat memproses pesan yang diterima dan eksekusi berakhir dengan kode keluar yang tidak nol (misalnya, karena deserialisasi sel yang tidak tertangani), pesan akan secara otomatis "dipantulkan" kembali ke pengirim, dan bendera "pantulan" akan dicentang dan disetel. bendera "memantul". Isi pesan yang dipantulkan akan sama dengan pesan aslinya; Oleh karena itu, penting untuk memeriksa bendera "bouncing" dari pesan internal yang masuk sebelum menguraikan bidang op dalam kontrak pintar dan memproses permintaan yang sesuai (jika tidak ada risiko bahwa permintaan yang terkandung dalam pesan bouncing akan diproses oleh pengirim aslinya sebagai permintaan terpisah yang baru). Jika bendera "bouncing" disetel, kode khusus dapat memahami permintaan mana yang gagal (misalnya, dengan membatalkan operasi op dan query_id dari pesan bouncing ) dan mengambil tindakan yang sesuai. Kontrak pintar yang lebih sederhana dapat dengan mudah mengabaikan semua pesan yang dikembalikan (diakhiri dengan kode keluar nol jika bendera "terpental" disetel).
Di sisi lain, penerima berhasil mem-parsing permintaan masuk dan menemukan bahwa metode operasi yang diminta tidak didukung atau bahwa kondisi kesalahan lain telah terpenuhi. Maka respons dengan op sama dengan 0xffffffff atau nilai lain yang sesuai harus dikirim kembali menggunakan SENDRAWMSG dengan mode = 64, seperti yang disebutkan di atas.
Dalam beberapa situasi, pengirim ingin mentransfer sejumlah uang pada saat yang sama? ke pengirim? (di sini, tampaknya, kesalahan, dan dimaksudkan untuk "penerima") dan menerima konfirmasi atau pesan kesalahan. Misalnya, kontrak pintar pemilihan validator menerima permintaan untuk berpartisipasi dalam pemilihan bersama dengan tawaran sebagai nilai tambah. Dalam kasus seperti itu, masuk akal untuk melampirkan, katakanlah, satu gram tambahan ke nilai estimasi [biaya] (Di sini kata value digunakan di mana-mana, dalam arti pembayaran untuk beberapa tindakan, jadi saya menggunakan kata "biaya"). Jika kesalahan terjadi (misalnya, tawaran tidak dapat diterima karena alasan apa pun), jumlah penuh yang diterima (dikurangi biaya pemrosesan) harus dikembalikan ke pengirim bersama dengan pesan kesalahan (misalnya, menggunakan SENDRAWMSG dengan mode = 64, seperti yang dijelaskan di atas). Jika berhasil, pesan konfirmasi dibuat dan tepat satu gram dikirim kembali (biaya transfer pesan dikurangi dari nilai ini; ini adalah mode = 1 dari SENDRAWMSG).
Menggunakan pesan yang tidak dapat di-bouncing
Hampir semua pesan internal yang dikirim antara kontrak pintar harus dikembalikan (Anda dapat menerjemahkannya sebagai "memantul", tetapi agar tidak bingung, lebih mudah menggunakan terminologi ini), yaitu, mereka harus memiliki "pantulan" sedikit tidak kosong. Kemudian, jika target kontrak pintar tidak ada, atau jika ia menciptakan pengecualian yang tidak tertangani saat memproses pesan ini, pesan tersebut akan “dikembalikan” kembali, dengan menanggung sisa biaya awal (nilai) (dikurangi semua biaya untuk pengiriman pesan dan gas). Pesan yang dikembalikan akan memiliki badan yang sama, tetapi dengan bendera "bouncing" dihapus dan set "bouncing" bendera. Oleh karena itu, semua kontrak pintar harus memeriksa bendera "terpental" dari semua pesan yang masuk dan menerimanya dengan diam-diam (segera diakhiri dengan kode keluar nol) atau melakukan beberapa pemrosesan khusus untuk menentukan permintaan keluar mana yang gagal. Permintaan yang terkandung dalam isi pesan yang dikembalikan tidak boleh dieksekusi.
Dalam beberapa kasus, pesan internal yang tidak dapat dipantulkan harus digunakan. Misalnya, akun baru tidak dapat dibuat tanpa setidaknya satu pesan internal yang tidak dapat dibatalkan dikirim ke sana. Jika pesan ini tidak mengandung StateInit dengan kode dan data kontrak pintar yang baru, tidak masuk akal untuk memiliki badan yang tidak kosong dalam pesan internal yang tidak kembali.
Merupakan ide yang baik untuk mencegah pengguna akhir (mis., Dompet) mengirim pesan yang tidak dapat dibatalkan yang membawa jumlah besar (mis., Lebih dari lima gram), atau setidaknya memperingatkan mereka jika mereka mencoba melakukan ini. Lebih baik mengirim sejumlah kecil dulu, lalu buat kontrak pintar baru, lalu kirim jumlah lebih besar.
Pesan Eksternal
Pesan eksternal dikirim secara eksternal ke kontrak pintar yang berada di blockchain TON untuk memaksa mereka melakukan tindakan tertentu. Misalnya, kontrak pintar dompet mengharapkan untuk menerima pesan eksternal yang berisi pesanan (misalnya, pesan internal yang akan dikirim dari kontrak pintar dompet) yang ditandatangani oleh pemilik dompet; ketika pesan eksternal tersebut diterima oleh kontrak pintar dompet, ia pertama-tama memverifikasi tanda tangan, kemudian menerima pesan (dengan meluncurkan TVM ACCEPT primitive), dan kemudian melakukan semua tindakan yang diperlukan.
Harap dicatat bahwa semua pesan eksternal harus dilindungi dari serangan replay. Validator biasanya menghapus pesan eksternal dari kumpulan pesan eksternal yang diusulkan (diterima dari jaringan); Namun, dalam beberapa situasi, validator lain dapat memproses pesan eksternal yang sama dua kali (sehingga menciptakan transaksi kedua untuk pesan eksternal yang sama, yang mengarah pada duplikasi tindakan asli). Lebih buruk lagi, penyerang dapat mengekstrak pesan eksternal dari blok yang berisi transaksi pemrosesan dan mengirimnya kembali nanti. Ini dapat menyebabkan, misalnya, kontrak dompet pintar untuk mengulangi pembayaran.
Cara termudah untuk melindungi kontrak pintar dari mengendus serangan yang terkait dengan pesan eksternal adalah dengan menyimpan penghitung cur-seqno 32-bit dalam data konstan kontrak pintar dan menunggu nilai req-seqno di (bagian yang ditandatangani) dari setiap pesan eksternal yang masuk. Kemudian pesan eksternal diterima (DITERIMA - petunjuk dari DITERIMA primitif) hanya jika tanda tangan valid dan req-seqno sama dengan cur-seqno . Setelah pemrosesan berhasil, nilai cur-seqno dalam data persisten meningkat satu, sehingga pesan eksternal yang sama tidak akan pernah diterima lagi.
Anda juga bisa memasukkan bidang kedaluwarsa di dalam pesan eksternal dan menerima pesan hanya jika waktu Unix saat ini kurang dari nilai bidang ini. Pendekatan ini dapat digunakan dalam kombinasi dengan seqno ; sebagai alternatif, kontrak pintar penerima dapat menyimpan set (hashes) dari semua pesan eksternal yang terakhir (tidak kedaluwarsa) yang diterima dalam data permanennya dan menolak pesan eksternal baru jika itu merupakan duplikat dari salah satu pesan yang disimpan. Anda juga harus menerapkan pengumpulan dan penghapusan pesan kadaluwarsa dalam set ini untuk menghindari pertumbuhan data persisten yang tidak terbatas.
Biasanya, pesan eksternal dimulai dengan tanda tangan 256-bit (jika perlu), 32-bit req-seqno (jika perlu), 32-bit berakhir pada (jika perlu), dan mungkin op 32-bit dan parameter lain yang diperlukan di tergantung pada op . Templat pesan eksternal tidak harus distandarisasi sebagai templat pesan internal, karena pesan eksternal tidak digunakan untuk interaksi antara berbagai kontrak pintar (ditulis oleh pengembang yang berbeda dan dikelola oleh pemilik yang berbeda).
Dapatkan metode
Beberapa kontrak cerdas diharapkan untuk menerapkan metode-metode tertentu yang terdefinisi dengan baik. Sebagai contoh, setiap kontrak pintar resolver dns untuk TON DNS diharapkan untuk mengimplementasikan metode get dnsresolve. Kontrak pintar khusus dapat menentukan metode get spesifik mereka. Satu-satunya rekomendasi umum kami saat ini adalah untuk mengimplementasikan metode get "seqno" (tanpa parameter), yang mengembalikan seqno saat ini dari kontrak pintar, yang menggunakan nomor urut untuk mencegah serangan pemutaran yang terkait dengan metode eksternal yang masuk setiap kali metode tersebut memiliki artinya
Kamus:
- Sel - Sel TVM terdiri dari paling banyak 1023 bit data, dan paling banyak empat referensi ke sel lain. Semua data persisten (termasuk kode TVM) di TON Blockchain direpresentasikan sebagai kumpulan sel TVM (lih. [1, 2.5.14]). - sel TVM terdiri dari tidak lebih dari 1023 bit data dan tidak lebih dari empat tautan ke sel lain. Semua data persisten (termasuk kode TVM) di TON blockchain disajikan sebagai satu set sel TVM (lih. [1, 2.5.14]). - kutipan dari deskripsi TON Virtual Machine ( https://test.ton.org/tvm.pdf )
Kesimpulan apa yang bisa ditarik berdasarkan apa yang saya baca?
- Anda dapat mengirim pesan eksternal ke kontrak untuk memicu tindakan.
- Serangan - ada, misalnya, serangan replay
- Layak membuat metode seqno untuk melindungi dari serangan replay.
- Penyelesai Dns memiliki metode dnsresolve
- Anda dapat menyimpan hash pesan eksternal untuk melindungi dari serangan, tetapi Anda harus menghapusnya tepat waktu, karena ini ada baiknya menggunakan bidang expired_at untuk pesan eksternal
- Pesan tidak-kembali hanya diperlukan untuk membuat kontrak, jika tidak, semua pesan internal akan dikembalikan
- Pesan respons-permintaan harus berisi bidang-bidang berikut: op, query_id - opsional, dan beberapa lainnya tergantung pada nilai op
- Anda dapat melampirkan komentar teks dalam format UTF-8 untuk orang-orang dan "komentar biner" untuk dibaca dan diproses secara otomatis oleh perangkat lunak pihak ketiga.
- Ada baiknya menangani pengecualian dan melakukannya dengan bijak
- "Pesan sederhana tanpa komentar" - harus memiliki badan kosong
- Sedikit pesan permintaan respons tingkat tinggi mengambil nilai 0 untuk pesan permintaan, dan nilai 1 untuk pesan respons
- Ada nilai op standar untuk pesan respons untuk mengidentifikasi kesalahan
- Jika pesan respons diterima dengan op yang tidak dikenal, itu harus diabaikan, yaitu, eksekusi lengkap dengan kode 0
- Anda harus membayar untuk mengirim pesan, untuk bensin dan untuk mengirim tanggapan. Pada saat yang sama, jika dia mengirim lebih dari yang diperlukan, kelebihannya akan kembali pada jawabannya.
- Saat menerima pesan, selalu perlu memeriksa bendera yang dipantulkan terlebih dahulu.
Terima kasih atas perhatian Anda, saya akan dengan senang hati memberikan tanggapan yang membangun!