Về cách viết và xuất bản hợp đồng thông minh trên Mạng mở Telegram (TON)

Về cách viết và xuất bản hợp đồng thông minh trong TON

Bài báo này viết về vấn đề gì vậy?

Trong bài viết, tôi sẽ nói về việc tôi đã tham gia (trong số hai) cuộc thi blockchain Telegram đầu tiên, không nhận giải thưởng và quyết định ghi lại trải nghiệm của mình vào một bài báo để nó không bị lãng quên và có lẽ sẽ giúp ích cho bạn. người nào đó.

Vì tôi không muốn viết mã trừu tượng mà muốn làm điều gì đó hiệu quả nên trong bài viết này tôi đã viết một hợp đồng thông minh cho xổ số tức thời và một trang web hiển thị dữ liệu hợp đồng thông minh trực tiếp từ TON mà không cần sử dụng bộ lưu trữ trung gian.

Bài viết sẽ hữu ích cho những ai muốn thực hiện hợp đồng thông minh đầu tiên bằng TON nhưng không biết bắt đầu từ đâu.

Lấy xổ số làm ví dụ, tôi sẽ đi từ cài đặt môi trường đến xuất bản hợp đồng thông minh, tương tác với nó và viết một trang web để nhận và xuất bản dữ liệu.

Về việc tham gia cuộc thi

Tháng 10 năm ngoái, Telegram đã công bố cuộc thi blockchain với các ngôn ngữ mới Fift и FunC. Cần phải chọn viết bất kỳ hợp đồng thông minh nào trong số năm hợp đồng thông minh được đề xuất. Tôi nghĩ sẽ thật tuyệt nếu làm điều gì đó khác biệt, học một ngôn ngữ và làm điều gì đó, ngay cả khi tôi không phải viết bất cứ điều gì khác trong tương lai. Thêm vào đó, chủ đề này liên tục được nhắc đến.

Điều đáng nói là tôi không có kinh nghiệm phát triển hợp đồng thông minh.

Tôi dự định tham gia đến phút cuối cùng cho đến khi có thể rồi viết bài đánh giá, nhưng tôi đã thất bại ngay ở bài đầu tiên. TÔI đã viết một chiếc ví với tính năng đa chữ ký trên FunC và nó thường hoạt động. Tôi lấy nó làm cơ sở hợp đồng thông minh trên Solidity.

Vào thời điểm đó, tôi nghĩ rằng điều này chắc chắn đủ để giành được ít nhất một giải thưởng nào đó. Kết quả là khoảng 40 trong số 60 người tham gia đã trở thành người đoạt giải và tôi không nằm trong số đó. Nói chung, điều này không có gì sai, nhưng có một điều khiến tôi bận tâm. Tại thời điểm công bố kết quả, việc xét duyệt hợp đồng của tôi vẫn chưa được thực hiện, tôi hỏi những người tham gia trò chuyện xem còn ai chưa có thì không có.

Rõ ràng để ý đến tin nhắn của tôi, hai ngày sau ban giám khảo đăng bình luận và tôi vẫn không hiểu liệu họ có vô tình bỏ sót hợp đồng thông minh của tôi trong quá trình chấm điểm hay chỉ đơn giản là nghĩ rằng nó tệ đến mức không cần bình luận. Tôi đã hỏi một câu hỏi trên trang, nhưng không nhận được câu trả lời. Mặc dù việc ai đánh giá không có gì bí mật nhưng tôi cho rằng việc viết tin nhắn cá nhân là không cần thiết.

Mất rất nhiều thời gian để tìm hiểu nên quyết định viết một bài. Vì chưa có nhiều thông tin nên bài viết này sẽ giúp tiết kiệm thời gian cho mọi người quan tâm.

Khái niệm về hợp đồng thông minh trong TON

Trước khi viết bất cứ điều gì, bạn cần tìm ra cách tiếp cận vấn đề này từ phía nào. Vì vậy, bây giờ tôi sẽ cho bạn biết hệ thống bao gồm những bộ phận nào. Chính xác hơn, những phần nào bạn cần biết để viết được ít nhất một loại hợp đồng làm việc nào đó.

Chúng tôi sẽ tập trung vào việc viết một hợp đồng thông minh và làm việc với TON Virtual Machine (TVM), Fift и FunC, vì vậy bài viết giống mô tả quá trình phát triển của một chương trình thông thường hơn. Chúng tôi sẽ không tập trung vào cách hoạt động của nền tảng ở đây.

Nói chung về cách thức hoạt động TVM và ngôn ngữ Fift có tài liệu chính thức tốt. Khi tham gia cuộc thi và bây giờ khi viết hợp đồng hiện tại, tôi thường tìm đến cô ấy.

Ngôn ngữ chính để viết hợp đồng thông minh là FunC. Hiện tại không có tài liệu nào về nó, vì vậy để viết một cái gì đó, bạn cần nghiên cứu các ví dụ về hợp đồng thông minh từ kho lưu trữ chính thức và cách triển khai ngôn ngữ ở đó, ngoài ra bạn có thể xem các ví dụ về hợp đồng thông minh từ hai kho lưu trữ trước đó. các cuộc thi. Các liên kết ở cuối bài viết.

Giả sử chúng ta đã viết một hợp đồng thông minh cho FunC, sau đó chúng tôi biên dịch mã thành trình biên dịch mã Fift.

Hợp đồng thông minh được biên soạn vẫn được xuất bản. Để làm điều này bạn cần phải viết một hàm trong Fift, sẽ lấy mã hợp đồng thông minh và một số tham số khác làm đầu vào và đầu ra sẽ là một tệp có phần mở rộng .boc (có nghĩa là “túi tế bào”) và tùy thuộc vào cách chúng tôi viết nó, khóa riêng và địa chỉ được tạo dựa trên mã hợp đồng thông minh. Bạn đã có thể gửi gram đến địa chỉ của hợp đồng thông minh chưa được công bố.

Để xuất bản một hợp đồng thông minh bằng TON đã nhận được .boc tệp sẽ cần phải được gửi đến blockchain bằng ứng dụng khách nhẹ (xem thêm thông tin bên dưới). Nhưng trước khi xuất bản, bạn cần chuyển gram đến địa chỉ đã tạo, nếu không hợp đồng thông minh sẽ không được xuất bản. Sau khi xuất bản, bạn có thể tương tác với hợp đồng thông minh bằng cách gửi tin nhắn từ bên ngoài (ví dụ: sử dụng ứng dụng khách nhẹ) hoặc từ bên trong (ví dụ: một hợp đồng thông minh gửi tin nhắn khác bên trong TON).

Khi chúng tôi hiểu cách xuất bản mã, mọi việc sẽ trở nên dễ dàng hơn. Chúng tôi đại khái biết những gì chúng tôi muốn viết và chương trình của chúng tôi sẽ hoạt động như thế nào. Và trong khi viết, chúng tôi tìm hiểu xem điều này đã được triển khai như thế nào trong các hợp đồng thông minh hiện có hoặc chúng tôi xem xét mã triển khai Fift и FunC trong kho lưu trữ chính thức hoặc xem tài liệu chính thức.

Tôi rất thường xuyên tìm kiếm các từ khóa trong cuộc trò chuyện Telegram, nơi tập trung tất cả những người tham gia cuộc thi và nhân viên Telegram, và điều đó đã xảy ra trong cuộc thi, mọi người đều tập trung ở đó và bắt đầu thảo luận về Fift và FunC. Liên kết ở cuối bài viết.

Đã đến lúc chuyển từ lý thuyết sang thực hành.

Chuẩn bị môi trường làm việc với TON

Tôi đã làm mọi thứ sẽ được mô tả trong bài viết về MacOS và kiểm tra kỹ nó trong Ubuntu 18.04 LTS sạch trên Docker.

Điều đầu tiên bạn cần làm là tải xuống và cài đặt lite-client mà bạn có thể gửi yêu cầu tới TON.

Các hướng dẫn trên trang web chính thức mô tả quá trình cài đặt khá chi tiết và rõ ràng và bỏ qua một số chi tiết. Ở đây chúng tôi làm theo hướng dẫn, cài đặt các phần phụ thuộc còn thiếu trong quá trình thực hiện. Tôi không tự mình biên dịch từng dự án và cài đặt từ kho lưu trữ Ubuntu chính thức (trên MacOS tôi đã sử dụng 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 

Khi tất cả các phụ thuộc được cài đặt, bạn có thể cài đặt lite-client, Fift, FunC.

Đầu tiên, chúng tôi sao chép kho lưu trữ TON cùng với các phần phụ thuộc của nó. Để thuận tiện, chúng tôi sẽ làm mọi thứ trong một thư mục ~/TON.

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

Kho lưu trữ cũng lưu trữ các triển khai Fift и FunC.

Bây giờ chúng ta đã sẵn sàng để lắp ráp dự án. Mã kho lưu trữ được sao chép vào một thư mục ~/TON/ton. Trong ~/TON tạo một thư mục build và thu thập dự án trong đó.

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

Vì chúng ta sẽ viết một hợp đồng thông minh nên chúng ta không chỉ cần lite-clientNhưng Fift с FunC, vì vậy hãy biên dịch mọi thứ. Đó không phải là một quá trình nhanh chóng, vì vậy chúng tôi đang chờ đợi.

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

Tiếp theo, tải xuống tệp cấu hình chứa dữ liệu về nút mà lite-client sẽ kết nối.

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

Thực hiện các yêu cầu đầu tiên tới TON

Bây giờ hãy khởi động lite-client.

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

Nếu quá trình xây dựng thành công thì sau khi khởi chạy, bạn sẽ thấy nhật ký kết nối của máy khách nhẹ với nút.

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

Bạn có thể chạy lệnh help và xem những lệnh nào có sẵn.

help

Hãy liệt kê các lệnh mà chúng tôi sẽ sử dụng trong bài viết này.

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-методы смартконтракта. 

Bây giờ chúng tôi đã sẵn sàng để viết hợp đồng.

Thực hiện

Ý tưởng

Như tôi đã viết ở trên, hợp đồng thông minh mà chúng tôi đang viết là một cuộc xổ số.

Hơn nữa, đây không phải là xổ số mà bạn cần mua vé và đợi một giờ, ngày hoặc tháng, mà là xổ số ngay lập tức trong đó người dùng chuyển đến địa chỉ hợp đồng N gram và ngay lập tức lấy lại được 2 * N gram hoặc mất. Chúng ta sẽ đặt xác suất thắng khoảng 40%. Nếu không có đủ gram để thanh toán thì chúng tôi sẽ coi giao dịch là giao dịch nạp tiền.

Hơn nữa, điều quan trọng là các cược có thể được xem trong thời gian thực và ở hình thức thuận tiện để người dùng có thể biết ngay mình thắng hay thua. Vì vậy, bạn cần tạo một trang web hiển thị cược và kết quả trực tiếp từ TON.

Viết hợp đồng thông minh

Để thuận tiện, tôi đã đánh dấu mã cho FunC; plugin này có thể được tìm thấy và cài đặt trong tìm kiếm Visual Studio Code; nếu bạn đột nhiên muốn thêm một cái gì đó, tôi đã công khai plugin này. Ngoài ra, trước đây ai đó đã tạo một plugin để làm việc với Fift, bạn cũng có thể cài đặt nó và tìm thấy nó trong VSC.

Hãy tạo ngay một kho lưu trữ nơi chúng ta sẽ cam kết các kết quả trung gian.

Để làm cho cuộc sống của chúng ta dễ dàng hơn, chúng ta sẽ viết một hợp đồng thông minh và thử nghiệm nó tại địa phương cho đến khi nó sẵn sàng. Chỉ sau đó chúng tôi mới xuất bản nó trên TON.

Hợp đồng thông minh có hai phương thức bên ngoài có thể được truy cập. Đầu tiên, recv_external() Chức năng này được thực thi khi một yêu cầu đối với hợp đồng đến từ thế giới bên ngoài, tức là không phải từ TON, chẳng hạn như khi chính chúng ta tạo một tin nhắn và gửi nó qua lite-client. Thứ hai, recv_internal() đây là khi, trong chính TON, bất kỳ hợp đồng nào đều đề cập đến hợp đồng của chúng tôi. Trong cả hai trường hợp, bạn có thể truyền tham số cho hàm.

Hãy bắt đầu với một ví dụ đơn giản sẽ hoạt động nếu được xuất bản nhưng không có tải chức năng trong đó.

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

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

Ở đây chúng ta cần giải thích nó là gì slice. Tất cả dữ liệu được lưu trữ trong TON Blockchain là một bộ sưu tập TVM cell hoặc đơn giản cell, trong một ô như vậy, bạn có thể lưu trữ tối đa 1023 bit dữ liệu và tối đa 4 liên kết đến các ô khác.

TVM cell slice hoặc slice đây là một phần của cái hiện có cell được sử dụng để phân tích nó, nó sẽ trở nên rõ ràng sau này. Điều quan trọng đối với chúng tôi là chúng tôi có thể chuyển slice và tùy thuộc vào loại tin nhắn, xử lý dữ liệu trong recv_external() hoặc recv_internal().

impure — một từ khóa chỉ ra rằng chức năng sửa đổi dữ liệu hợp đồng thông minh.

Hãy lưu mã hợp đồng vào lottery-code.fc và biên dịch.

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

Ý nghĩa của các lá cờ có thể được xem bằng lệnh

~/TON/build/crypto/func -help

Chúng tôi đã biên soạn mã trình biên dịch Fift trong 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

Nó có thể được khởi chạy cục bộ, vì điều này chúng tôi sẽ chuẩn bị môi trường.

Lưu ý rằng dòng đầu tiên kết nối Asm.fif, đây là mã được viết bằng Fift dành cho trình biên dịch mã Fift.

Vì chúng tôi muốn chạy và kiểm tra hợp đồng thông minh cục bộ nên chúng tôi sẽ tạo một tệp lottery-test-suite.fif và sao chép mã đã biên dịch ở đó, thay thế dòng cuối cùng trong đó, dòng này ghi mã hợp đồng thông minh vào một hằng số codesau đó chuyển nó sang máy ảo:

"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

Cho đến nay mọi chuyện có vẻ rõ ràng, bây giờ hãy thêm vào cùng một tệp mã mà chúng ta sẽ sử dụng để khởi chạy TVM.

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

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

В c7 chúng tôi ghi lại bối cảnh, tức là dữ liệu mà TVM (hoặc trạng thái mạng) sẽ được khởi chạy. Ngay cả trong cuộc thi, một trong những nhà phát triển đã chỉ ra cách tạo c7 và tôi đã sao chép. Trong bài viết này chúng ta có thể cần phải thay đổi rand_seed vì việc tạo một số ngẫu nhiên phụ thuộc vào nó và nếu không thay đổi, số đó sẽ được trả về mọi lúc.

recv_internal и recv_external Các hằng số có giá trị 0 và -1 sẽ chịu trách nhiệm gọi các hàm tương ứng trong hợp đồng thông minh.

Bây giờ chúng ta đã sẵn sàng tạo thử nghiệm đầu tiên cho hợp đồng thông minh trống của mình. Để rõ ràng, bây giờ chúng tôi sẽ thêm tất cả các bài kiểm tra vào cùng một tệp lottery-test-suite.fif.

Hãy tạo một biến storage và viết một cái trống vào đó cell, đây sẽ là nơi lưu trữ hợp đồng thông minh.

message Đây là thông điệp mà chúng ta sẽ truyền tải tới contact thông minh từ bên ngoài. Bây giờ chúng tôi cũng sẽ làm trống nó.

variable storage 
<b b> storage ! 

variable message 
<b b> message ! 

Sau khi đã chuẩn bị xong các hằng số và biến, chúng ta khởi chạy TVM bằng lệnh runvmctx và truyền các tham số đã tạo vào đầu vào.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

Cuối cùng chúng ta sẽ thành công cái này mã trung gian cho Fift.

Bây giờ chúng ta có thể chạy mã kết quả.

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

Chương trình sẽ chạy mà không có lỗi và ở đầu ra chúng ta sẽ thấy nhật ký thực thi:

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

Tuyệt vời, chúng tôi đã viết phiên bản hoạt động đầu tiên của hợp đồng thông minh.

Bây giờ chúng ta cần thêm chức năng. Đầu tiên hãy giải quyết những thông điệp đến từ thế giới bên ngoài tới recv_external()

Nhà phát triển tự mình chọn định dạng tin nhắn mà hợp đồng có thể chấp nhận.

Nhưng thông thường

  • Đầu tiên, chúng tôi muốn bảo vệ hợp đồng của mình khỏi thế giới bên ngoài và làm cho nó chỉ có chủ sở hữu hợp đồng mới có thể gửi tin nhắn bên ngoài tới nó.
  • thứ hai, khi chúng tôi gửi một tin nhắn hợp lệ tới TON, chúng tôi muốn điều này xảy ra đúng một lần và khi chúng tôi gửi lại cùng một tin nhắn, hợp đồng thông minh sẽ từ chối nó.

Vì vậy, hầu hết mọi hợp đồng đều giải quyết được hai vấn đề này, vì hợp đồng của chúng tôi chấp nhận các tin nhắn bên ngoài nên chúng tôi cũng cần quan tâm đến vấn đề đó.

Chúng ta sẽ làm theo thứ tự ngược lại. Đầu tiên, hãy giải quyết vấn đề với sự lặp lại; nếu hợp đồng đã nhận được một tin nhắn như vậy và xử lý nó, nó sẽ không thực hiện nó lần thứ hai. Và sau đó chúng tôi sẽ giải quyết vấn đề để chỉ một nhóm người nhất định mới có thể gửi tin nhắn đến hợp đồng thông minh.

Có nhiều cách khác nhau để giải quyết vấn đề tin nhắn trùng lặp. Đây là cách chúng tôi sẽ làm điều đó. Trong hợp đồng thông minh, chúng tôi khởi tạo bộ đếm tin nhắn đã nhận với giá trị ban đầu là 0. Trong mỗi tin nhắn vào hợp đồng thông minh, chúng tôi sẽ thêm giá trị bộ đếm hiện tại. Nếu giá trị bộ đếm trong tin nhắn không khớp với giá trị trong hợp đồng thông minh thì chúng tôi sẽ không xử lý nó; nếu đúng như vậy thì chúng tôi sẽ xử lý nó và tăng bộ đếm trong hợp đồng thông minh lên 1.

Hãy quay trở lại lottery-test-suite.fif và thêm bài kiểm tra thứ hai vào đó. Nếu chúng tôi gửi số không chính xác, mã sẽ đưa ra một ngoại lệ. Ví dụ: để dữ liệu hợp đồng lưu trữ 166 và chúng tôi sẽ gửi 165.

<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"

Hãy khởi động.

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

Và chúng ta sẽ thấy rằng bài kiểm tra được thực hiện có lỗi.

[ 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

Ở giai đoạn này lottery-test-suite.fif sẽ trông giống như по ссылке.

Bây giờ hãy thêm logic phản hồi vào hợp đồng thông minh trong 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 nói dối thông điệp chúng tôi gửi.

Việc đầu tiên chúng ta làm là kiểm tra xem tin nhắn có chứa dữ liệu hay không, nếu không thì chúng ta chỉ cần thoát ra.

Tiếp theo chúng tôi phân tích tin nhắn. in_msg~load_uint(32) tải số 165, 32-bit unsigned int từ thông điệp được truyền đi.

Tiếp theo, chúng tôi tải 32 bit từ bộ lưu trữ hợp đồng thông minh. Chúng tôi kiểm tra xem số được tải có khớp với số đã truyền hay không; nếu không, chúng tôi sẽ đưa ra một ngoại lệ. Trong trường hợp của chúng tôi, vì chúng tôi đang chuyển một kết quả không khớp nên sẽ đưa ra một ngoại lệ.

Bây giờ hãy biên dịch.

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

Sao chép mã kết quả vào lottery-test-suite.fif, không quên thay dòng cuối cùng.

Chúng tôi kiểm tra xem bài kiểm tra có vượt qua không:

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

Đây Bạn có thể thấy cam kết tương ứng với kết quả hiện tại.

Lưu ý rằng thật bất tiện khi liên tục sao chép mã đã biên dịch của hợp đồng thông minh vào một tệp có kiểm tra, vì vậy hãy viết một tập lệnh sẽ ghi mã thành một hằng số cho chúng ta và chúng ta sẽ chỉ cần kết nối mã đã biên dịch với các kiểm tra của mình bằng cách sử dụng "include".

Tạo một tập tin trong thư mục dự án build.sh với nội dung sau đây.

#!/bin/bash

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

Hãy làm cho nó có thể thực thi được.

chmod +x ./build.sh

Bây giờ, chỉ cần chạy tập lệnh của chúng tôi để biên dịch hợp đồng. Nhưng bên cạnh đó, chúng ta cần viết nó thành một hằng số code. Vì vậy chúng ta sẽ tạo một file mới lotter-compiled-for-test.fif, mà chúng tôi sẽ đưa vào tệp lottery-test-suite.fif.

Hãy thêm mã skirpt vào sh, nó sẽ chỉ sao chép tệp đã biên dịch trong lotter-compiled-for-test.fif và thay đổi dòng cuối cùng trong đó.

# 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

Bây giờ, để kiểm tra, hãy chạy tập lệnh kết quả và một tệp sẽ được tạo lottery-compiled-for-test.fif, mà chúng tôi sẽ đưa vào lottery-test-suite.fif

В lottery-test-suite.fif xóa mã hợp đồng và thêm dòng "lottery-compiled-for-test.fif" include.

Chúng tôi chạy thử nghiệm để kiểm tra xem chúng có vượt qua không.

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

Tuyệt vời, bây giờ để tự động hóa việc khởi chạy thử nghiệm, hãy tạo một tệp test.sh, đầu tiên sẽ thực thi build.sh, sau đó chạy thử nghiệm.

touch test.sh
chmod +x test.sh

Chúng tôi viết bên trong

./build.sh 

echo "nCompilation completedn"

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

Hãy làm nó test.sh và chạy nó để đảm bảo các bài kiểm tra hoạt động.

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

Chúng tôi kiểm tra xem hợp đồng có được biên dịch và các thử nghiệm được thực hiện hay không.

Tuyệt vời, bây giờ đang khởi động test.sh Các bài kiểm tra sẽ được biên dịch và chạy ngay lập tức. Đây là liên kết đến làm.

Được rồi, trước khi tiếp tục, chúng ta hãy làm một việc nữa cho thuận tiện.

Hãy tạo một thư mục build nơi chúng tôi sẽ lưu trữ hợp đồng đã sao chép và bản sao của nó được viết thành một hằng số lottery-compiled.fif, lottery-compiled-for-test.fif. Chúng ta cũng hãy tạo một thư mục test tập tin kiểm tra sẽ được lưu trữ ở đâu? lottery-test-suite.fif và các tập tin hỗ trợ tiềm năng khác. Liên kết đến những thay đổi có liên quan.

Hãy tiếp tục phát triển hợp đồng thông minh.

Tiếp theo cần có một bài kiểm tra để kiểm tra xem tin nhắn đã được nhận chưa và bộ đếm có được cập nhật trong cửa hàng khi chúng tôi gửi đúng số hay không. Nhưng chúng ta sẽ làm điều đó sau.

Bây giờ hãy suy nghĩ về cấu trúc dữ liệu nào và dữ liệu nào cần được lưu trữ trong hợp đồng thông minh.

Tôi sẽ mô tả mọi thứ mà chúng tôi lưu trữ.

`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` переменная типа словарь, хранит последние двадцать ставок. 

Tiếp theo bạn cần viết hai hàm. Hãy gọi cái đầu tiên pack_state(), sẽ đóng gói dữ liệu để lưu tiếp theo vào bộ lưu trữ hợp đồng thông minh. Hãy gọi thứ hai unpack_state() sẽ đọc và trả về dữ liệu từ bộ lưu trữ.

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

Chúng tôi thêm hai chức năng này vào phần đầu của hợp đồng thông minh. Nó sẽ làm việc bên ngoài cái này kết quả trung gian.

Để lưu dữ liệu, bạn sẽ cần gọi hàm tích hợp set_data() và nó sẽ ghi dữ liệu từ pack_state() trong kho lưu trữ hợp đồng thông minh.

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

Bây giờ chúng ta đã có các chức năng thuận tiện để ghi và đọc dữ liệu, chúng ta có thể tiếp tục.

Chúng ta cần kiểm tra xem tin nhắn đến từ bên ngoài có được ký bởi chủ sở hữu hợp đồng (hoặc người dùng khác có quyền truy cập vào khóa riêng) hay không.

Khi chúng tôi xuất bản một hợp đồng thông minh, chúng tôi có thể khởi tạo nó với dữ liệu chúng tôi cần trong bộ lưu trữ, dữ liệu này sẽ được lưu để sử dụng trong tương lai. Chúng tôi sẽ ghi lại khóa chung ở đó để có thể xác minh rằng tin nhắn đến đã được ký bằng khóa riêng tương ứng.

Trước khi tiếp tục, hãy tạo một khóa riêng và ghi nó vào test/keys/owner.pk. Để thực hiện việc này, hãy khởi chạy Fift ở chế độ tương tác và thực hiện bốn lệnh.

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

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

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

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

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

Hãy tạo một thư mục keys bên trong thư mục test và viết khóa riêng ở đó.

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

Chúng tôi thấy một tập tin trong thư mục hiện tại owner.pk.

Chúng tôi xóa khóa chung khỏi ngăn xếp và khi cần, chúng tôi có thể lấy nó từ khóa riêng.

Bây giờ chúng ta cần viết xác minh chữ ký. Hãy bắt đầu với bài kiểm tra. Đầu tiên chúng ta đọc khóa riêng từ tệp bằng hàm file>B và viết nó vào một biến owner_private_key, sau đó sử dụng hàm priv>pub chuyển đổi khóa riêng thành khóa chung và ghi kết quả vào 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 !

Chúng ta sẽ cần cả hai chìa khóa.

Chúng tôi khởi tạo bộ lưu trữ hợp đồng thông minh với dữ liệu tùy ý theo trình tự giống như trong hàm pack_state()và viết nó vào một biến 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 !

Tiếp theo, chúng ta sẽ soạn một tin nhắn đã ký, nó sẽ chỉ chứa chữ ký và giá trị bộ đếm.

Đầu tiên, chúng tôi tạo dữ liệu mà chúng tôi muốn truyền, sau đó chúng tôi ký dữ liệu đó bằng khóa riêng và cuối cùng chúng tôi tạo một tin nhắn đã ký.

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 !  

Do đó, thông báo mà chúng tôi sẽ gửi tới hợp đồng thông minh được ghi lại trong một biến message_to_send, về chức năng hashu, ed25519_sign_uint bạn có thể đọc trong tài liệu của Fif.

Và để chạy thử nghiệm, chúng tôi gọi lại.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

Ở đây Tệp có các bài kiểm tra sẽ trông như thế này ở giai đoạn này.

Hãy chạy thử nghiệm và nó sẽ thất bại, vì vậy chúng tôi sẽ thay đổi hợp đồng thông minh để nó có thể nhận các tin nhắn có định dạng này và xác minh chữ ký.

Đầu tiên, chúng ta đếm 512 bit chữ ký từ tin nhắn và ghi nó vào một biến, sau đó chúng ta đếm 32 bit của biến đếm.

Vì chúng tôi có chức năng đọc dữ liệu từ bộ lưu trữ hợp đồng thông minh nên chúng tôi sẽ sử dụng nó.

Tiếp theo là kiểm tra bộ đếm được chuyển cùng với bộ lưu trữ và kiểm tra chữ ký. Nếu có gì đó không khớp thì chúng tôi sẽ đưa ra một ngoại lệ với mã thích hợp.

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

Cam kết liên quan ngay tại đây.

Hãy chạy thử nghiệm và thấy rằng thử nghiệm thứ hai thất bại. Vì hai lý do, không có đủ bit trong tin nhắn và không có đủ bit trong bộ lưu trữ nên mã bị lỗi khi phân tích cú pháp. Chúng tôi cần thêm chữ ký vào tin nhắn chúng tôi đang gửi và sao chép bộ nhớ từ lần kiểm tra trước.

Trong thử nghiệm thứ hai, chúng tôi sẽ thêm chữ ký tin nhắn và thay đổi bộ nhớ hợp đồng thông minh. Ở đây tập tin với các bài kiểm tra trông như thế nào vào lúc này.

Hãy viết bài kiểm tra thứ tư, trong đó chúng ta sẽ gửi một tin nhắn được ký bằng khóa riêng của người khác. Hãy tạo một khóa riêng khác và lưu nó vào một tệp not-owner.pk. Chúng tôi sẽ ký tin nhắn bằng khóa riêng này. Hãy chạy thử nghiệm và đảm bảo rằng tất cả các thử nghiệm đều vượt qua. Làm hiện nay.

Bây giờ cuối cùng chúng ta cũng có thể chuyển sang triển khai logic hợp đồng thông minh.
В recv_external() chúng tôi sẽ chấp nhận hai loại tin nhắn.

Vì hợp đồng của chúng tôi sẽ cộng dồn số tiền thua lỗ của người chơi nên số tiền này phải được chuyển cho người tạo ra xổ số. Địa chỉ ví của người tạo xổ số được ghi lại trong bộ lưu trữ khi hợp đồng được tạo.

Để đề phòng, chúng tôi cần có khả năng thay đổi địa chỉ mà chúng tôi gửi gram của những người thua cuộc. Chúng tôi cũng có thể gửi gram từ xổ số đến địa chỉ của chủ sở hữu.

Hãy bắt đầu với cái đầu tiên. Trước tiên, hãy viết một bài kiểm tra để kiểm tra xem sau khi gửi tin nhắn, hợp đồng thông minh đã lưu địa chỉ mới vào bộ lưu trữ hay chưa. Xin lưu ý rằng trong tin nhắn, ngoài bộ đếm và địa chỉ mới, chúng tôi còn truyền tải action Một số nguyên không âm 7 bit, tùy theo số đó mà chúng ta sẽ chọn cách xử lý tin nhắn trong hợp đồng thông minh.

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

Trong thử nghiệm, bạn có thể thấy cách lưu trữ hợp đồng thông minh được giải tuần tự hóa storage trong Năm. Việc giải tuần tự hóa các biến được mô tả trong tài liệu Fift.

Liên kết cam kết có thêm bột.

Hãy chạy thử nghiệm và đảm bảo rằng nó thất bại. Bây giờ hãy thêm logic để thay đổi địa chỉ của chủ xổ số.

Trong hợp đồng thông minh, chúng tôi tiếp tục phân tích cú pháp message, đọc vào action. Hãy để chúng tôi nhắc nhở bạn rằng chúng tôi sẽ có hai action: thay đổi địa chỉ và gửi gram.

Sau đó chúng ta đọc địa chỉ mới của chủ hợp đồng và lưu vào kho.
Chúng tôi chạy thử nghiệm và thấy rằng thử nghiệm thứ ba thất bại. Nó gặp sự cố do hợp đồng hiện phân tích bổ sung 7 bit từ thông báo bị thiếu trong thử nghiệm. Thêm một cái không tồn tại vào tin nhắn action. Hãy chạy thử nghiệm và xem mọi thứ đều trôi qua. Đây cam kết thay đổi. Tuyệt vời.

Bây giờ hãy viết logic để gửi số gam đã chỉ định đến địa chỉ đã lưu trước đó.

Đầu tiên, hãy viết một bài kiểm tra. Chúng tôi sẽ viết hai bài kiểm tra, một bài kiểm tra khi không có đủ số dư, bài kiểm tra thứ hai khi mọi thứ sẽ vượt qua thành công. Có thể xem thử nghiệm trong cam kết này.

Bây giờ hãy thêm mã. Đầu tiên, hãy viết hai phương thức trợ giúp. Phương pháp nhận đầu tiên là tìm hiểu số dư hiện tại của hợp đồng thông minh.

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

Và cái thứ hai là gửi gram đến một hợp đồng thông minh khác. Tôi đã sao chép hoàn toàn phương pháp này từ một hợp đồng thông minh khác.

() 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
}

Hãy thêm hai phương thức này vào hợp đồng thông minh và viết logic. Đầu tiên, chúng tôi phân tích số gam từ tin nhắn. Tiếp theo, chúng tôi kiểm tra số dư, nếu không đủ, chúng tôi sẽ đưa ra một ngoại lệ. Nếu mọi thứ đều ổn thì chúng tôi sẽ gửi gram đến địa chỉ đã lưu và cập nhật bộ đếm.

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

Ở đây trông giống như hợp đồng thông minh vào lúc này. Hãy chạy thử nghiệm và đảm bảo chúng vượt qua.

Nhân tiện, hoa hồng sẽ được khấu trừ từ hợp đồng thông minh mỗi lần cho một tin nhắn được xử lý. Để các tin nhắn hợp đồng thông minh thực hiện yêu cầu, sau khi kiểm tra cơ bản, bạn cần gọi accept_message().

Bây giờ hãy chuyển sang tin nhắn nội bộ. Trên thực tế, chúng tôi sẽ chỉ nhận gram và gửi lại số tiền gấp đôi cho người chơi nếu thắng và một phần ba cho chủ sở hữu nếu thua.

Đầu tiên, hãy viết một bài kiểm tra đơn giản. Để làm điều này, chúng tôi cần một địa chỉ thử nghiệm của hợp đồng thông minh mà từ đó chúng tôi được cho là sẽ gửi gram đến hợp đồng thông minh.

Địa chỉ hợp đồng thông minh bao gồm hai số, một số nguyên 32 bit chịu trách nhiệm cho chuỗi công việc và số tài khoản duy nhất số nguyên không âm 256 bit trong chuỗi công việc này. Ví dụ: -1 và 12345, đây là địa chỉ chúng ta sẽ lưu vào một file.

Tôi đã sao chép chức năng lưu địa chỉ từ 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

Chúng ta hãy xem chức năng này hoạt động như thế nào, điều này sẽ giúp bạn hiểu cách hoạt động của Fift. Khởi chạy Fift ở chế độ tương tác.

~/TON/build/crypto/fift -i 

Đầu tiên chúng ta đẩy -1, 12345 và tên của tệp trong tương lai "sender.addr" vào ngăn xếp:

-1 12345 "sender.addr" 

Bước tiếp theo là thực hiện chức năng -rot, điều này sẽ dịch chuyển ngăn xếp sao cho ở đầu ngăn xếp có một số hợp đồng thông minh duy nhất:

"sender.addr" -1 12345

256 u>B chuyển đổi số nguyên không âm 256 bit thành byte.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap hoán đổi hai phần tử trên cùng của ngăn xếp.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B chuyển đổi số nguyên 32 bit thành byte.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ kết nối hai chuỗi byte.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

Một lần nữa swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Và cuối cùng các byte được ghi vào tập tin B>file. Sau này ngăn xếp của chúng tôi trống rỗng. Chúng tôi dừng lại Fift. Một tập tin đã được tạo trong thư mục hiện tại sender.addr. Hãy di chuyển tập tin vào thư mục đã tạo test/addresses/.

Hãy viết một bài kiểm tra đơn giản để gửi gram tới một hợp đồng thông minh. Đây là cam kết.

Bây giờ chúng ta hãy nhìn vào logic của xổ số.

Việc đầu tiên chúng ta làm là kiểm tra tin nhắn bounced hay không nếu bounced, thì chúng ta bỏ qua nó. bounced có nghĩa là hợp đồng sẽ trả lại gram nếu xảy ra lỗi. Chúng tôi sẽ không trả lại gram nếu xảy ra lỗi bất ngờ.

Chúng tôi kiểm tra, nếu số dư nhỏ hơn nửa gram thì chúng tôi chỉ cần chấp nhận tin nhắn và bỏ qua nó.

Tiếp theo, chúng tôi phân tích địa chỉ của hợp đồng thông minh mà thông báo được gửi đến.

Chúng tôi đọc dữ liệu từ bộ lưu trữ và sau đó xóa các cược cũ khỏi lịch sử nếu có hơn 20 cược trong số đó. Để thuận tiện, tôi đã viết thêm ba chức năng pack_order(), unpack_order(), remove_old_orders().

Tiếp theo, chúng tôi xem xét nếu số dư không đủ để thanh toán thì chúng tôi coi đây không phải là đặt cược mà là một khoản bổ sung và lưu số bổ sung vào orders.

Cuối cùng là bản chất của hợp đồng thông minh.

Đầu tiên, nếu người chơi thua, chúng tôi sẽ lưu nó vào lịch sử cá cược và nếu số tiền lớn hơn 3 gram, chúng tôi sẽ gửi 1/3 cho chủ sở hữu hợp đồng thông minh.

Nếu người chơi thắng thì chúng tôi gửi gấp đôi số tiền đến địa chỉ của người chơi rồi lưu thông tin đặt cược vào lịch sử.

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

Có bấy nhiêu thôi. Cam kết tương ứng.

Bây giờ tất cả những gì còn lại thật đơn giản, hãy tạo các phương thức get để chúng ta có thể lấy thông tin về trạng thái của hợp đồng từ thế giới bên ngoài (trên thực tế, là đọc dữ liệu từ bộ lưu trữ hợp đồng thông minh của họ).

Hãy thêm các phương thức get. Chúng tôi sẽ viết bên dưới về cách nhận thông tin về hợp đồng thông minh.

Tôi cũng quên thêm mã sẽ xử lý yêu cầu đầu tiên xảy ra khi xuất bản hợp đồng thông minh. Cam kết tương ứng. Và xa hơn sửa lại lỗi gửi 1/3 số tiền vào tài khoản của chủ sở hữu.

Bước tiếp theo là xuất bản hợp đồng thông minh. Hãy tạo một thư mục requests.

Tôi lấy mã xuất bản làm cơ sở simple-wallet-code.fc который có thể tìm thấy trong kho lưu trữ chính thức.

Một cái gì đó đáng chú ý đến. Chúng tôi tạo ra một kho lưu trữ hợp đồng thông minh và một thông báo đầu vào. Sau đó, địa chỉ của hợp đồng thông minh được tạo, nghĩa là địa chỉ đó đã được biết ngay cả trước khi xuất bản bằng TON. Tiếp theo, bạn cần gửi một vài gram đến địa chỉ này và chỉ sau đó bạn cần gửi một tệp có chính hợp đồng thông minh, vì mạng sẽ tính phí lưu trữ hợp đồng thông minh và các hoạt động trong đó (người xác thực lưu trữ và thực thi thông minh hợp đồng). Mã có thể được xem ở đây.

Tiếp theo chúng tôi thực thi mã xuất bản và nhận được lottery-query.boc tập tin và địa chỉ hợp đồng thông minh.

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

Đừng quên lưu các tệp được tạo: lottery-query.boc, lottery.addr, lottery.pk.

Trong số những thứ khác, chúng ta sẽ thấy địa chỉ của hợp đồng thông minh trong nhật ký thực hiện.

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

Chỉ để cho vui thôi, hãy yêu cầu TON

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

Và chúng ta sẽ thấy tài khoản có địa chỉ này trống.

account state is empty

Chúng tôi gửi đến địa chỉ 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 Gram và sau vài giây, chúng tôi thực hiện lệnh tương tự. Để gửi gram tôi sử dụng ví chính thức, và bạn có thể hỏi ai đó trong cuộc trò chuyện về số gam kiểm tra, điều mà tôi sẽ nói ở cuối bài viết.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Có vẻ như chưa được khởi tạo (state:account_uninit) một hợp đồng thông minh có cùng địa chỉ và số dư 1 nanogram.

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

Bây giờ hãy xuất bản hợp đồng thông minh. Hãy khởi chạy lite-client và thực thi.

> 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 

Hãy kiểm tra xem hợp đồng đã được công bố chưa.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Trong số những thứ khác chúng tôi nhận được.

  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

Chúng ta thấy rằng account_active.

Cam kết tương ứng với những thay đổi ngay tại đây.

Bây giờ hãy tạo yêu cầu tương tác với hợp đồng thông minh.

Chính xác hơn, chúng tôi sẽ để việc đầu tiên để thay đổi địa chỉ là một công việc độc lập và chúng tôi sẽ thực hiện việc thứ hai để gửi gram đến địa chỉ của chủ sở hữu. Trên thực tế, chúng ta sẽ cần phải làm điều tương tự như trong bài kiểm tra gửi gam.

Đây là thông điệp chúng tôi sẽ gửi đến hợp đồng thông minh, nơi msg_seqno 165, action 2 và 9.5 gram để gửi.

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

Đừng quên ký tin nhắn bằng khóa riêng của bạn lottery.pk, được tạo trước đó khi tạo hợp đồng thông minh. Đây là cam kết tương ứng.

Nhận thông tin từ hợp đồng thông minh bằng phương thức get

Bây giờ chúng ta hãy xem cách chạy các phương thức nhận hợp đồng thông minh.

Phóng lite-client và chạy các phương thức get mà chúng tôi đã viết.

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

В result chứa giá trị mà hàm trả về balance() từ hợp đồng thông minh của chúng tôi.
Chúng tôi sẽ làm tương tự cho một số phương pháp khác.

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

Hãy hỏi lịch sử đặt cược của bạn.

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

Chúng tôi sẽ sử dụng lite-client và get các phương pháp để hiển thị thông tin về hợp đồng thông minh trên trang web.

Hiển thị dữ liệu hợp đồng thông minh trên trang web

Tôi đã viết một trang web đơn giản bằng Python để hiển thị dữ liệu từ hợp đồng thông minh một cách thuận tiện. Ở đây tôi sẽ không đi sâu vào chi tiết và sẽ xuất bản trang web trong một lần cam kết.

Yêu cầu tới TON được thực hiện từ Python thông qua lite-client. Để thuận tiện, trang web được đóng gói trong Docker và xuất bản trên Google Cloud. Liên kết.

Cố gắng

Bây giờ hãy thử gửi gram đến đó để bổ sung từ . Chúng tôi sẽ gửi 40 gram. Và hãy đặt cược một vài lần cho rõ ràng. Chúng tôi thấy rằng trang web hiển thị lịch sử đặt cược, tỷ lệ thắng hiện tại và các thông tin hữu ích khác.

Chúng tôi thấyrằng chúng tôi thắng trận đầu tiên, thua trận thứ hai.

bạt

Bài viết hóa ra dài hơn tôi mong đợi rất nhiều, có thể nó có thể ngắn hơn, hoặc có thể chỉ dành cho một người không biết gì về TON và muốn viết và xuất bản một hợp đồng thông minh không hề đơn giản với khả năng tương tác với Nó. Có lẽ một số điều có thể được giải thích đơn giản hơn.

Có lẽ một số khía cạnh của việc thực hiện có thể đã được thực hiện hiệu quả và tinh tế hơn, nhưng khi đó việc chuẩn bị bài viết sẽ còn mất nhiều thời gian hơn. Cũng có thể tôi đã mắc lỗi ở đâu đó hoặc không hiểu điều gì đó, vì vậy nếu bạn đang làm điều gì đó nghiêm trọng, bạn cần dựa vào tài liệu chính thức hoặc kho lưu trữ chính thức có mã TON.

Cần lưu ý rằng vì bản thân TON vẫn đang trong giai đoạn phát triển tích cực nên những thay đổi có thể xảy ra sẽ phá vỡ bất kỳ bước nào trong bài viết này (điều xảy ra khi tôi đang viết và đã được sửa), nhưng cách tiếp cận chung là khó có thể thay đổi.

Tôi sẽ không nói về tương lai của TON. Có lẽ nền tảng này sẽ trở thành một thứ gì đó lớn lao và chúng ta nên dành thời gian nghiên cứu nó và lấp đầy một vị trí thích hợp bằng các sản phẩm của mình ngay bây giờ.

Ngoài ra còn có Libra của Facebook, nơi có lượng người dùng tiềm năng lớn hơn TON. Tôi hầu như không biết gì về Libra, theo đánh giá của diễn đàn thì ở đó có nhiều hoạt động hơn so với cộng đồng TON. Mặc dù các nhà phát triển và cộng đồng của TON giống với hoạt động ngầm hơn, nhưng điều này cũng rất thú vị.

tài liệu tham khảo

  1. Tài liệu TON chính thức: https://test.ton.org
  2. Kho TON chính thức: https://github.com/ton-blockchain/ton
  3. Ví chính thức cho các nền tảng khác nhau: https://wallet.ton.org
  4. Kho lưu trữ hợp đồng thông minh từ bài viết này: https://github.com/raiym/astonished
  5. Liên kết đến trang web hợp đồng thông minh: https://ton-lottery.appspot.com
  6. Kho lưu trữ tiện ích mở rộng cho Visual Studio Code cho FunC: https://github.com/raiym/func-visual-studio-plugin
  7. Trò chuyện về TON trên Telegram, điều này thực sự đã giúp tìm ra điều đó ở giai đoạn đầu. Tôi nghĩ sẽ không sai nếu tôi nói rằng tất cả những người viết gì đó cho TON đều ở đó. Bạn cũng có thể yêu cầu gram kiểm tra ở đó. https://t.me/tondev_ru
  8. Một cuộc trò chuyện khác về TON mà tôi tìm thấy thông tin hữu ích: https://t.me/TONgramDev
  9. Giai đoạn đầu tiên của cuộc thi: https://contest.com/blockchain
  10. Giai đoạn thứ hai của cuộc thi: https://contest.com/blockchain-2

Nguồn: www.habr.com

Thêm một lời nhận xét