电报注册网络代理

这篇文章是关于什么的? (What is this article about?)

In this article, I will tell about my participation in the first (out of two so far) Telegram blockchain contest. I didn't win any prize. However, decided to combine and share the unique experience from the start to finish line, so my observations could help anyone who is interested.

在本文中,我将介绍我参加的第一届(迄今为止,两次)电报区块链竞赛。 我没有获奖。 但是,决定从头到尾结合并分享独特的经验,因此我的观察可以对任何感兴趣的人有所帮助。

Since I didn't want to write some abstract code, instead make something useful. I created instant lottery smart-contract and website which shows smart-contract data directly from Telegram Open Network (TON) avoiding transitional storage.

由于我不想编写一些抽象代码,因此可以做一些有用的事情。 我创建了即时彩票智能合约和网站,该网站直接从Telegram开放网络(TON)显示智能合约数据,从而避免了过渡存储。

The article will be particularly useful for those, who want to write their first smart-contract in TON but has difficulties where to start.

对于那些想用TON编写第一个智能合约但从哪里开始有困难的人来说,这篇文章特别有用。

Using the lottery smart-contract as an example, I will show the path from the environment set up till publication of the smart-contract and its interaction. Moreover, I will create a website that will show smart-contract data.

以彩票智能合约为例,我将展示从环境设置到智能合约及其交互发布的路径。 此外,我将创建一个显示智能合约数据的网站。

关于参加比赛 (About participation in the contest)

In October 2019 Telegram organized a blockchain contest with new programming languages Fift and FunC. The developers were asked to write any smart-contract out of five suggested. I thought that it would be interesting to do something out of the ordinary: learn a new language and create smart-contract even if it can be the last time using this language. Besides this topic is in trend nowadays.

在2019年10月,Telegram用新的编程语言FiftFunC举办了一次Fift链竞赛。 要求开发人员写出五份建议中的任何智能合约。 我认为做一些与众不同的事情会很有趣:学习一种新语言并创建智能合约,即使这可能是最后一次使用这种语言。 除此之外,当今这个话题正在流行。

It has to be mentioned that I have never had any sort of experience in writing the smart-contracts for blockchain networks.

必须提到的是,我在编写用于区块链网络的智能合约方面从未有过任何经验。

My plan was to participate until the very end. Then I was going to write a summary article, but I was not selected further than the first stage of the competition, where I have submitted workable version of multi-signature wallet written in FunC. Smart-contract was based on Solidity for Ethereum Network.

我的计划是一直参加到最后。 然后,我打算写一篇总结文章,但是除了进入竞赛的第一阶段之外,我没有其他人被选中,在那里我提交了用FunC编写的可行的多签名钱包版本。 智能合同是基于密实度的复仇网络。

I imagined that should make it, at this level of the competition my work should be enough to gain a prize-winning spot. However, about 40 out of 60 participants happened to win and that excluded me. Basically, that's okay, but one thing bothered me: the review along with the test for my contract was not done, upon the announcement of the results. Thus, I asked other participants in the chat, if anyone else faced the same situation, there were none.

我想应该做到这一点,在这个比赛水平上,我的作品应该足以获得一个获奖的位置。 但是,在60位参与者中,大约有40位碰巧赢得了比赛,这不包括我。 基本上没关系,但是有一件事困扰着我:在结果公布后,我的合同的审查和测试都没有完成。 因此,我问聊天中的其他参与者,是否还有其他人遇到相同的情况,没有。

I believe, my messages draw some attention and after two days judges published several comments. I still did not get it: did they skipped my smart-contract by accident during the evaluation period? Or was it such a bad work that they decided it does not worth any comment at all? I asked these questions on the contest page, unfortunately questions were ignored as well. Although, it is not a secret who the judge was, nevertheless writing a private messages would have been too much to ask.

我相信,我的信息引起了大家的注意,两天后,法官发表了一些评论。 我仍然不明白:在评估期间,他们是否偶然跳过了我的智能合约? 还是那么糟糕的工作以至于他们认为它根本不值得一提? 我在比赛页面上问了这些问题,不幸的是,这些问题也被忽略了。 尽管法官的身份不是秘密,但是写私人信息实在是太多了。

That being said it was decided to write this article that explains the subject in detail, since I have already spent plenty of time to understand how things work. In general, there is lack of information provided, so this article will save time for everyone who is interested.

话虽这么说,所以决定写这篇文章来详细解释该主题,因为我已经花了很多时间来了解事情是如何工作的。 通常,缺少提供的信息,因此本文将为感兴趣的每个人节省时间。

TON中的智能合约概念 (The concept of smart-contract in TON)

Before writing the first smart-contract we need to understand how to approach this thing in general. Therefore, now I will describe the complete set of the system, or more precisely which parts we should know in order to write at least some kind of functioning smart-contract.

在编写第一个智能合约之前,我们需要了解一般如何处理这个问题。 因此,现在我将描述系统的完整集合,或更准确地描述我们为了编写至少某种有效的智能合约而应该了解的部分。

We will focus on writing smart-contract using FunC and Fift, which will be compiled into Fift-assembler and then will be executed in TON Virtual Machine (TVM). Therefore, the article is more like a description of the development of a regular program. We will not dwell on how the platform works.

我们将专注于使用FunCFift编写智能合约,这些合约将被编译为Fift-assembler ,然后将在TON Virtual Machine (TVM) 。 因此,本文更像是对常规程序开发的描述。 我们将不讨论平台的工作原理。

How Fift and TVM work is well described in the official documentation. During the contest and now while writing current smart-contract I made a use of these docs.

FiftTVM工作方式在官方文档中有很好的描述。 在比赛期间,以及现在编写当前的智能合约时,我都使用了这些文档。

The main language for writing smart-contract is FunC. Currently there is lack of documentation on this language, therefore in order to develop something, we need to study the existing examples in the official repository, you may also find language implementation itself there and other participants' submissions from previous two contests. References are at the end of the article.

编写智能合约的主要语言是FunC 。 当前缺少有关该语言的文档,因此,为了开发某些语言,我们需要研究官方存储库中的现有示例,您可能还发现那里的语言实现本身以及之前两次竞赛的其他参与者的陈述。 参考资料位于本文的结尾。

Let's say we have written the smart-contract using FunC after that we compile FunC code into Fift-assembler.

假设我们使用FunC编写了智能合约,然后将FunC代码编译到Fift-assembler

The compiled code should be published in TON. In order to be able to do that we should write Fift code, that takes as arguments smart-contract code and several other parameters and generates .boc file (which stands for "bag of cells") and depending on the way this code is written private key and address can be generated based on the code of smart-contract.

编译后的代码应在TON中发布。 为了能够做到这一点,我们应该编写Fift代码,该代码将智能合约代码和其他几个参数作为参数,并生成.boc文件(代表“袋装电池”),并取决于编写此代码的方式可以根据智能合约的代码生成私钥和地址。

We can send grams to the generated address even if the smart-contract is not published yet.

即使尚未发布智能合约,我们也可以将克发送到生成的地址。

Since TON charges a fee for storage and transactions, before publishing a smart contract, you need to transfer grams to the generated address, otherwise, it will not be published.

由于TON会收取一定的存储和交易费用,因此在发布智能合约之前,您需要将g转移到生成的地址,否则它将不会发布。

As s means to publish smart-contract, we should send generated .boc file in TON using lite-client (details are below). After that, we can interact with the smart-contract by sending external messages (e.g.: using lite-client) or internal messages (e.g.: when one smart-contract sends a message to another).

作为发布智能合约的手段,我们应该使用lite-client将生成的.boc文件发送到TON(详细信息如下)。 之后,我们可以通过发送外部消息(例如:使用lite-client )或内部消息(例如:当一个智能合约将消息发送给另一智能合约)来与智能合约进行交互。

Now, when we understand the process of publishing thr code, it becomes easier. We already have an idea of what exactly we want to create and how it should work. During the writing activity we can use existing smart-contracts or check the implementation of Fift and FunC in the official repository and documentations.

现在,当我们了解了发布代码的过程时,它变得更加容易。 我们已经对要创建什么以及它应该如何工作有了一个想法。 在写作活动中,我们可以使用现有的智能合约,或在官方存储库和文档中检查FiftFunC的实现。

Successively, I used Telegram-chat as a sourse searcging by keywords, where during the contest all participants gathered along with Telegram employees and chatted about Fift and FunC. Link is in the end.

后来,我使用Telegram-chat作为关键字的源头搜索,在比赛期间,所有参与者都与Telegram员工们聚集在一起,聊起了FiftFunC 。 链接在最后。

Let's move to implementation.

让我们开始实施。

准备在TON内工作的环境 (Preparing the environment for working within TON)

Everything described below I have done in MacOS and re-tested in Ubuntu 18.04 LTS with Docker.

我在MacOS中已完成以下所有操作,并在带有Docker的Ubuntu 18.04 LTS中进行了重新测试。

The first thing we need to do is to install lite-client that will allow us to send requests to TON.

我们需要做的第一件事是安装lite-client ,它将允许我们向TON发送请求。

Instruction on the website explains the process of the installation pretty clear. Here we just follow the instruction and install missing dependencies. I didn't compile each library separately instead installed them from the official Ubuntu repository (used brew on MacOS).

网站上的说明非常清楚地说明了安装过程。 在这里,我们只按照说明进行操作并安装缺少的依赖项。 我并没有单独编译每个库,而是从官方Ubuntu存储库(在MacOS上使用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

Once all dependencies installed we install lite-client, Fift and FunC.

安装所有依赖项后,我们将安装lite-clientFiftFunC

Let's clone the TON repository together with its dependencies. For convenience, we will process everything in ~/TON folder.

让我们克隆TON存储库及其依赖项。 为了方便起见,我们将处理~/TON文件夹中的所有内容。

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

The repository also contains Fift and FunC implementations.

该存储库还包含FiftFunC实现。

Now we are ready to put together the project. We cloned the repository into ~/TON/ton folder. In ~/TON we should create build folder and execute the following.

现在我们准备将项目放在一起。 我们将存储库克隆到~/TON/ton文件夹中。 在~/TON我们应该创建build文件夹并执行以下命令。

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

Since we are going to write smart-contract we need more than just lite-client meaning: Fift and FunC too, therefore, we compile them as well.

由于我们要编写智能合约,因此我们不仅需要lite-client含义: FiftFunC也因此需要编译它们。

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

Next, we should download a configuration file that contains information about TON node to which lite-client will connect.

接下来,我们应该下载一个配置文件,其中包含有关lite-client将连接到的TON节点的信息。

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

TON中的第一个请求 (First requests in TON)

Now let's run installed lite-client.

现在,让我们运行已安装的lite-client

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

If the installation was successful we will see lite-client connection logs.

如果安装成功,我们将看到lite-client连接日志。

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

We can execute help and see available commands.

我们可以执行help并查看可用命令。

help

Let's list the commands that will be used in this article.

让我们列出本文将要使用的命令。

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

Now we are ready to start coding the smart-contract.

现在我们准备开始编写智能合约了。

实作 (Implementation)

理念 (Idea)

As I described above the smart-contract that we are going to write is a lottery.

如前所述,我们要编写的智能合约是彩票。

Moreover, it is not just an ordinary lottery, where there is a necessity to wait an hour, a day and etc., it is an instant lottery, in which the player transfers N Grams to the given address and immediately receives back 2 * N Grams or loses. Probability of winning we will set around 40%. If smart-contract do not have enough Grams to pay, we consider a top-up transaction.

而且,这不仅仅是普通的彩票,需要等待一个小时,一天等,它是一种即时彩票,在这种彩票中,玩家将N克转移到给定的地址并立即收到2 * N克或输。 我们获胜的可能性定在40%左右。 如果智能合约没有足够的克朗支付,我们会考虑充值交易。

It is extremely important to see the bets in a real-time and in a convenient output so that the players could easily understand if they won or lost. To solve this we will create a website that will show bets' history directly from TON.

实时且方便地查看投注非常重要,这样玩家就可以轻松了解输赢。 为了解决这个问题,我们将创建一个网站,直接从TON显示投注历史。

编写智能合约 (Writing the smart-contract)

For convenience, I created a syntax highlighter for FunC language, the plugin could be found in the Visual Studio Code plugin search and code available on GitHub. There is also a plugin for Fift language available and can be installed in the VSC.

为方便起见,我为FunC语言创建了一个语法突出显示工具,该​​插件可以在Visual Studio Code插件搜索中找到,以及GitHub上可用的代码。 还有一个适用于Fift语言的插件,可以在VSC中安装。

Right away we can create a git repository and commit interim results.

我们可以立即创建一个git存储库并提交临时结果。

To simplify our lives, we will write and test locally until the smart-contract is ready. Then once it is all set, we will publish it in TON.

为了简化我们的生活,我们将在本地编写和测试,直到智能合约准备就绪为止。 然后,一旦设置好,我们将在TON中发布。

Smart-contract has two external ways that can be approached. First is recv_external() this method is used when someone sends a request from external network (not within TON), for example when we send a message with lite-client. Second is recv_internal() this way can be applied when one contract sends message to another within TON environment. In both cases, we can provide arbitrary parameters into methods.

智能合约有两种可以采用的外部方式。 首先是recv_external()当有人从外部网络(不在TON内)发送请求时,例如当我们使用lite-client发送消息时,使用此方法。 其次是recv_internal()当一个合同在TON环境中向另一个合同发送消息时,可以采用这种方式。 在这两种情况下,我们都可以将任意参数提供给方法。

Let's start with a simple example that will still work upon publishing, however will not carry actual load.

让我们从一个简单的示例开始,该示例在发布时仍然可以使用,但是不会实际加载。

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

Here we should explain what is the slice. All data stored in TON Blockchain is the collection of TVM cells or cell for short, in such cell you can store up to 1023 bits of information and up to 4 references to other cells.

在这里,我们应该解释什么是slice 。 TON区块链中存储的所有数据都是TVM cells或简称cell的集合,在此类单元中,您最多可以存储1023位信息和最多4个对其他单元的引用。

TVM cell slice or slice is part of the cell that is used for parsing the data, it will be more understandable later. Most important for us is that we can pass slice into smart-contract. In addition to that depending on the message itself we can use recv_internal() and recv_external() methods.

TVM cell sliceslice是用于解析数据的cell一部分,稍后将更易于理解。 对我们来说最重要的是,我们可以将slice传递到智能合约中。 除此之外,根据消息本身,我们可以使用recv_internal()recv_external()方法。

impure — keyword indicates that a method changes data in the smart-contract storage.

impure —关键字表示方法更改了智能合约存储中的数据。

We should save contract code in lottery-code.fc file and compile.

我们应该将合同代码保存在lottery-code.fc文件中并进行编译。

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

The meaning of flags can be checked with help command.

标记的含义可以通过help命令检查。

~/TON/build/crypto/func -help

Now we have compiled Fift-assembler code in a file named lottery-compiled.fif.

现在,我们已经在名为lottery-compiled.fif的文件中编译了Fift汇编程序代码。

// lottery-compiled.fif"Asm.fif" include
// automatically generated from `/Users/rajymbekkapisev/TON/ton/crypto/smartcont/stdlib.fc` `./lottery-code.fc`
PROGRAM{DECLPROC recv_internalDECLPROC recv_externalrecv_internal PROC:<{//  in_msgDROP    // }>recv_external PROC:<{//  in_msgDROP    // }>
}END>c

We can run it locally. Let's configure the environment first.

我们可以在本地运行它。 让我们先配置环境。

Note, that first line includes Asm.fif file, which contains an implementation of Fift-assembler using Fift language.

注意,第一行包括Asm.fif文件,该文件包含使用Fift语言实现的Fift汇编程序。

Since we want to execute and test smart-contract locally we create lottery-test-suite.fif and copy compiled code and change the last line, write smart-contract code into constant code than only we will be able to pass it to TON Virtual Machine.

因为我们要在本地执行和测试智能合约,所以我们创建lottery-test-suite.fif并复制已编译的代码并更改最后一行,将智能合约代码编写为常量code而不仅仅是我们将其传递给TON Virtual Machine

"TonUtil.fif" include
"Asm.fif" includePROGRAM{DECLPROC recv_internalDECLPROC recv_externalrecv_internal PROC:<{//  in_msgDROP    // }>recv_external PROC:<{//  in_msgDROP    // }>
}END>s constant code

So far it seems to be clear, now we will add the code to the same file that we will use to start TVM.

到目前为止,看起来似乎很清楚,现在我们将代码添加到用于启动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 c70 constant recv_internal // to run recv_internal()
-1 constant recv_external // to invoke recv_external()

In c7 constant we should write context with which TVM will be launched (or network status). During the contest, one developer showed how c7 being formed and I just copied. In this article, we also might need to change rand_seed because it is used to generate random number and if the number will remain same during the tests, it will always return the same number.

c7 constant中,我们应该编写将启动TVM的上下文(或网络状态)。 在比赛中,一位开发人员展示了c7的形成方式,而我只是复制了一下。 在本文中,我们还可能需要更改rand_seed因为它用于生成随机数,并且如果该数字在测试期间保持不变,则它将始终返回相同的数字。

recv_internal and recv_external are just constants 0 and -1 that will be responsible for executing the corresponding functions of the smart-contract.

recv_internalrecv_external只是常量0-1 ,它们将负责执行智能合约的相应功能。

Now we are ready to write the first test for our empty smart-contract. For clarity, we will add all tests into the same file lottery-test-suite.fif.

现在,我们准备为空的智能合约编写第一个测试。 为了清楚起见,我们将所有测试添加到同一文件lottery-test-suite.fif

Let's create variable storage and write empty cell in it, storage will be the permanent storage of the smart-contract.

让我们创建变量storage并在其中写入空cell ,该storage将是智能合约的永久存储。

message is the variable that we will pass to the smart-contract from the external environment (or as per documentation from "nowhere"). Let's make message empty for now.

message是我们将从外部环境(或根据“无处”的文档)传递给智能合约的变量。 让我们暂时将message设为空。

variable storage
<b b> storage ! variable message
<b b> message !

Now, after we have prepared constants and variables we can run TVM with runvmctx and pass created parameters.

现在,在准备好常量和变量之后,我们可以使用runvmctx运行TVM并传递创建的参数。

message @
recv_external
code
storage @
c7
runvmctx

As a result, we have this interim Fift code.

结果,我们有了这个临时的Fift代码。

And now let's run following code.

现在,让我们运行以下代码。

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

The code should be executde without errors and we should be able to see the following logs.

该代码应正确执行,并且我们应该能够看到以下日志。

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

Great, we have just wrote the first workable version of the smart-contract along with the test.

太好了,我们已经与测试一起编写了智能合约的第一个可行版本。

处理外部消息到智能合约 (Processing external messages to a smart-contract)

Now let's start adding functionality. Let's start with processing messages from "nowhere" to recv_external().

现在让我们开始添加功能。 让我们开始处理从“ nowhere”到recv_external()

How to structure the message is up to the developer. But usually,

如何构造消息取决于开发人员。 但是通常

  • At first, we want to protect smart-contract from the outside world and process messages only sent by the owner. 首先,我们要保护智能合约免受外界影响,并处理仅由所有者发送的消息。
  • Secondly, when we send a valid message we want the smart-contract to process it only once even if we will send the same message more than once. Also known as "replay attack". 其次,当我们发送有效消息时,我们希望智能合约只处理一次,即使我们多次发送同一消息也是如此。 也称为“重播攻击”。

Therefore, almost every smart-contract solves these two problems. Since our smart-contract will receive external messages we should take care of this. We will do it in the reverse order, we will solve the second issue and then move to the first.

因此,几乎每一个智能合约都解决了这两个问题。 由于我们的智能合约将收到外部消息,因此我们应该注意这一点。 我们将以相反的顺序进行操作,我们将解决第二个问题,然后移至第一个。

There are different ways to work out a replay attack problem. This is one of the options: in smart-contract, we will initialize the counter with 0 that will count the number of received messages. In each message, we will send to smart-contract other parameters including current existing counter value. If the counter value in the message doesn't match the counter value in the smart-contract we will reject such message. When it will match, we will process the message and increase the smart-contract counter by one.

有多种解决重播攻击问题的方法。 这是选项之一:在智能合约中,我们将计数器初始化为0 ,该计数器将对接收到的消息数进行计数。 在每条消息中,我们将向其他智能合约发送其他参数,包括当前现有计数器值。 如果消息中的计数器值与智能合约中的计数器值不匹配,我们将拒绝此类消息。 匹配时,我们将处理该消息,并将智能合约计数器增加一。

Let's go back to lottery-test-suite.fifand add the second test. We should send the wrong counter number and code must throw an exception. For example, smart-contract counter will be 166 and we will send 165.

让我们回到lottery-test-suite.fif并添加第二个测试。 我们应该发送错误的计数器号,并且代码必须引发异常。 例如,智能合约计数器为166,我们将发送165。

<b 166 32 u, b> storage !
<b 165 32 u, b> message !message @
recv_external
code
storage @
c7
runvmctxdrop
exit_code !
."Exit code " exit_code @ . cr
exit_code @ 33 - abort"Test #2 Not passed"

Let's execute.

让我们执行。

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

And observe that test fails.

并观察到测试失败。

[ 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

At this point lottery-test-suite.fif should be like this.

此时, lottery-test-suite.fif应该像这样 。

Now let's add counter into the smart-contract in lottery-code.fc.

现在,让我们在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);
}

Message sent to the smart-contract will be stored in slice in_msg.

发送到智能合约的消息将存储在slice in_msg

First, we should check if a message is empty, if so, we just exit execution.

首先,我们应该检查消息是否为空,如果是,则退出执行。

Next, we should start parsing the message. in_msg~load_uint(32) that loads 32 bits from the message unsigned integer number 165.

接下来,我们应该开始解析消息。 in_msg~load_uint(32)从消息无符号整数165加载32位。

Next, we should load 32 bits from the smart-contract storage. Then checking if these two numbers are the same, if not exception should be thrown. In our case, since we have sent not matching counter, the exception figure will be thrown.

接下来,我们应该从智能合约存储中加载32位。 然后检查这两个数字是否相同,否则请抛出异常。 在我们的例子中,由于我们发送了不匹配的计数器,因此将抛出异常图。

Let's compile.

让我们编译。

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

Let's copy outcome into Fift-assembler code, considering to change the last line. Monitor, if the test is passed successfully.

考虑更改最后一行,让我们将结果复制到Fift-assembler代码中。 监视,如果测试成功通过。

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

Here is the commit with the current results.

这是当前结果的提交。

Note that every time we change the smart-contract code we need to copy compiled code into lottery-test-suite.fif which is inconvenient. Thus, we will create a small script, which will write compiled code into constant file. We will just have to include this file in lottery-test-suite.fif.

请注意,每次更改智能合约代码时,都需要将编译后的代码复制到lottery-test-suite.fif ,这lottery-test-suite.fif方便。 因此,我们将创建一个小脚本,该脚本将已编译的代码写入常量文件。 我们只需要将此文件includelottery-test-suite.fif

In the project folder create build.sh file with the following code.

在项目文件夹中,使用以下代码创建build.sh文件。

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

Make it executable.

使它可执行。

chmod +x ./build.sh

Now in order to compile the smart-contract we just need to run build.sh and it will generate lottery-compiled.fif. Besides that we need to write that into constant code.

现在,为了编译智能合约,我们只需要运行build.sh ,它将生成lottery-compiled.fif 。 除此之外,我们需要将其写入常量code

Let's add a script which will copy compiled lottery-compiled.fif file then change the last line, like we did manually before. write the content into lottery-compiled-for-test.fif.

让我们添加一个脚本,该脚本将复制已编译的lottery-compiled.fif文件,然后更改最后一行,就像我们之前手动进行的一样。 将内容写入lottery-compiled-for-test.fif

# 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

Now let's execute resulting script and will get lottery-compiled-for-test.fif, which we will include in lottery-test-suite.fif.

现在,让我们执行生成的脚本,并将获得lottery-compiled-for-test.fif ,我们将其包含在lottery-test-suite.fif

In lottery-test-suite.fif remove contract code and add "lottery-compiled-for-test.fif" include line.

lottery-test-suite.fif删除合同代码并添加"lottery-compiled-for-test.fif" include行。

Run the tests to confirm that they are passing.

运行测试以确认它们通过了。

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

Great, now lets automate test execution by creating test.sh, which will firstly execute build.sh and then will run tests.

太好了,现在通过创建test.sh使测试执行自动化,该方法将首先执行build.sh然后运行测试。

touch test.sh
chmod +x test.sh

Write inside test.sh.

test.sh内编写。

./build.sh echo "\nCompilation completed\n"export FIFTPATH=~/TON/ton/crypto/fift/lib
~/TON/build/crypto/fift -s lottery-test-suite.fif

Run to confirm that the code compiles and tests are still passing.

运行以确认代码编译和测试仍在通过。

./test.sh

Excellent, now every time we will run test.sh, we will compile smart-contract and run all the tests. Here is the link to commit.

太好了,现在每次我们运行test.sh ,我们都将编译智能合约并运行所有测试。 这是提交的链接 。

Before we continue let's make one more thing.

在继续之前,让我们再做一件事。

Create a folder named build in which we will keep lottery-compiled.fif and lottery-compiled-for-test.fif. In addition to that we should create test folder where we will keep lottery-test-suite.fif and other supporting files. Link to the corresponding commit.

创建一个名为build的文件夹,在其中我们将保留lottery-compiled.fiflottery-compiled-for-test.fif 。 除此之外,我们应该创建test文件夹,其中将保留lottery-test-suite.fif和其他支持文件。 链接到相应的commit 。

Let's continue smart-contract development.

让我们继续智能合约开发。

Next thing that needs to be done is another test that sends the correct counter to the smart-contract and we should check it then save the updated counter. We will get back to this a bit later.

接下来需要做的是另一个测试,它将正确的计数器发送到智能合约,我们应该检查它,然后保存更新的计数器。 我们待会儿再讲。

Now let's think about the data structure and what type of data should be kept in the storage of the smart-contract. I will describe what we store.

现在,让我们考虑一下数据结构以及智能合约的存储中应保留什么类型的数据。 我将描述我们存储的内容。

`seqno` 32 bits unsigned integer as a counter. `pubkey` 256 bits unsigned integer for storing a public key, with this key we will check the signature of the sent message, explanation follows. `order_seqno` 32 bits unsigned integer stores number of bets. `number_of_wins` 32 bits unsigned integer stores number of wins. `incoming_amount` Gram type, first 4 bits describe the length of number, remaining is the number of Grams itself, stores the number of received Grams. `outgoing_amount` Gram type, stores the number of Grams sent to winners. `owner_wc` work chain id, 32 bits (in some places said that 8 bits) integer. Currently only two work chains available 0 and -1`owner_account_id` 256 bits unsigned integer, smart-contract address in current work chain. `orders` stores dictionary type, stores 20 recent bets.

Then, we should write two convenience functions. First one we will name as pack_state(), which will pack data for saving it in the smart-contract storage. Second one will be named as unpack_state(), which will read and parse data from storage.

然后,我们应该编写两个便利函数。 首先,我们将其命名为pack_state() ,它将打包数据以将其保存在智能合约存储中。 第二个将被命名为unpack_state() ,它将从存储中读取和解析数据。

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

Add these codes at the beginning of the smart-contract. At this stage, our FunC code should be like this.

在智能合约的开头添加这些代码。 在这个阶段,我们的FunC代码应该像这样 。

In order to save the data we should call built-inset_data() function and it will save the data from pack_state() into smart-contract storage.

为了保存数据,我们应该调用内置的set_data()函数,它将把数据pack_state()保存到智能合约存储中。

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

Now when we have convenient functions to read and write the data, let's move on.

现在,当我们拥有方便的功能来读写数据时,让我们继续。

We need to verify that the external message received by our smart-contract is signed by the private key holder(s).

我们需要验证我们的智能合约收到的外部消息是否已由私钥持有者签名。

When we are publishing the smart-contract we can initialize it with pre-populated data in the storage. We will pre-populate it with the public key so that it could verify that the received message signed by the corresponding private key.

发布智能合约时,我们可以使用存储中的预填充数据对其进行初始化。 我们将使用公钥预填充它,以便它可以验证接收到的消息是否由相应的私钥签名。

Before we continue let's create private/public key pair. We will save the private key in test/keys/owner.pk. For this matter let's run Fift in interactive mode and execute four following commands.

在继续之前,让我们创建私钥/公钥对。 我们将私钥保存在test/keys/owner.pk 。 为此,让我们在交互模式下运行Fift并执行以下四个命令。

`newkeypair` generates public and private key pair and places them on stack. `drop` removes the first element from the stack. `.s` just shows all stack elements. `"owner.pk" B>file` writes the first element of the stack (in our case private key) into "owner.pk" file. `bye` closes interactive mode.

In test folder we should create a folder named keys and put generated private key there.

test文件夹中,我们应该创建一个名为keys的文件夹,并将生成的私钥放在该文件夹中。

mkdir test/keys
cd test/keys
~/TON/build/crypto/fift -i
newkeypairok
.s
BYTES:128DB222CEB6CF5722021C3F21D4DF391CE6D5F70C874097E28D06FCE9FD6917 BYTES:DD0A81AAF5C07AAAA0C7772BB274E494E93BB0123AA1B29ECE7D42AE45184128
drop ok
"owner.pk" B>fileok
bye

Confirm that we can observe created owner.pk file. Note: we have just dropped public key from the stack, because whenever we might need it, the public key can be generated from the private key.

确认我们可以观察创建的owner.pk文件。 注意:我们刚刚从堆栈中删除了公钥,因为只要我们需要它,便可以从私钥中生成公钥。

Now we need to write signature verification functionality. Let's begin with the test. First of all we should read private key from the file with file>B and assign it to owner_private_key variable. Secondly we should convert the private into the public key using priv>pub function and assign it to owner_public_key variable.

现在我们需要编写签名验证功能。 让我们从测试开始。 首先,我们应该从file>B读取文件的私钥,并将其分配给owner_private_key变量。 其次,我们应该使用priv>pub函数将私钥转换为公钥,并将其分配给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 !

We will need both keys.

我们需要两个键。

Let's initialize the smart-contract storage. We will fill storage variable with arbitrary data in the same sequence as in pack_state().

让我们初始化智能合约存储。 我们将使用与pack_state()相同的顺序的任意数据填充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 !

Next, let's form a signed message that will consist of signature and counter value.

接下来,让我们形成一个包含签名和计数器值的签名消息。

In the first place we should create the data that we want to send, then it should be signed with the private key and last but not least put together the signed message.

首先,我们应该创建要发送的数据,然后使用私钥对其进行签名,最后但并非最不重要的是将签名后的消息放在一起。

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 !

The message which we are sending to the smart-contract is assigned to message_to_send variable, about hashu, ed25519_sign_uint functions we can read in Fift documentation.

我们正在发送到智能合约的消息已分配给message_to_send变量, hashu变量关于我们可以在Fift文档中阅读的hashued25519_sign_uint函数。

To run the test.

运行测试。

message_to_send @
recv_external
code
storage @
c7
runvmctx

Interim resuls commited here.

临时结果在这里提交。

We can run the test and it will fail, therefore we will change the smart-contract, so it could receive this type of message and verify the signature.

我们可以运行测试,并且测试将失败,因此我们将更改智能合约,以便它可以接收此类消息并验证签名。

Firstly we should count 512 bits signature and write it into variable, next we should count 32 bits variable counter.

首先,我们应该对512位签名进行计数并将其写入变量,接下来我们应该对32位变量计数器进行计数。

Since we already have used earlier written unpack_state() to parse storage data, we will use it.

由于我们已经使用了先前编写的unpack_state()来解析存储数据,因此我们将使用它。

Next, we should check the sent counter with the stored and the signature verification. If something does not match we must throw an exception accordingly.

接下来,我们应该检查已发送的计数器并保存并签名验证。 如果不匹配,我们必须相应地抛出异常。

var signature = in_msg~load_bits(512);
var message = in_msg;
int msg_seqno = message~load_uint(32);
(int stored_seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) = unpack_state();
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, check_signature(slice_hash(in_msg), signature, pubkey));

Commit with current changes here.

在此处提交当前更改。

We can run the test and see that the second one fails. Well, there are two reasons why code is failing during parsing: lack of bits passed and lack of it in the storage. Moreover, we need to copy the storage structure from the third test.

我们可以运行测试,看看第二个测试失败了。 嗯,代码在解析过程中失败有两个原因:缺少传递的位以及存储中缺少位。 此外,我们需要从第三次测试中复制存储结构。

During the second test run we will change the signature and the storage.The current state of tests' file can be found here.

在第二次测试运行中,我们将更改签名和存储。可在此处找到测试文件的当前状态。

Let's write the fourth test, in which we will send a message signed by someone else's private key. A new private key should be generated and saved in not-owner.pk. We will sign the message with this key. It's time to run the tests and let's make sure that tests come off. Commit with corresponding changes.

让我们编写第四个测试,在该测试中,我们将发送由其他人的私钥签名的消息。 应该生成一个新的私钥并将其保存在not-owner.pk 。 我们将使用此密钥在消息上签名。 现在该运行测试了,让我们确保测试能够成功进行。 做出相应的更改 。

Finally, we can start implementing business logic. In recv_external() we will process two types of messages.

最后,我们可以开始实现业务逻辑。 在recv_external()我们将处理两种类型的消息。

Due to the fact that our smart-contract will accumulate Grams of participants, these Grams need to be sent to the lottery owner. The lottery owner's address should be saved in the storage when the smart-contract is initialized. Yet, we need to have the option to change this address, in case it will be required.

由于我们的智能合约会累积参与者的克数,因此需要将这些克数发送给彩票所有者。 初始化智能合约时,彩票所有者的地址应保存在存储器中。 但是,如果需要,我们需要选择更改此地址。

Let's start by changing the owner's address. We will write a test to check that when the message received, a new address will be saved in the smart-contract storage. Note that in addition to the counter and a new address value the message we should include action 7 bits unsigned integer, and depending on the value of it we will choose how to process the message.

让我们从更改所有者的地址开始。 我们将编写一个测试,以检查收到消息时是否将新地址保存在智能合约存储中。 请注意,除了计数器和新的地址值外,消息还应包括action 7位无符号整数,并且根据其值,我们将选择如何处理消息。

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

In the test implementation, we can observe how deserialization of the smart-contract storage happens. The implemented test you can see in this commit. Run the test to confirm its failure.

在测试实现中,我们可以观察到智能合约存储的反序列化是如何发生的。 您可以在此commit中看到已实施的测试。 运行测试以确认其失败。

Now let's apply the logic of changing the owner's address so that the test will pass. In smart-contract, we continue to parse message and read action number. Reminder that we will have two actions: owner's address change and the ability to send grams to the owner. Then we should read the new smart-contract address of the owner and save it in the storage.

现在,让我们应用更改所有者地址的逻辑,以便测试能够通过。 在智能合约中,我们继续解析message并读取action编号。 提醒我们,我们将执行两个action :更改所有者的地址,以及将克发送给所有者的功能。 然后,我们应该阅读所有者的新智能合约地址并将其保存在存储中。

Run the tests and observe that the third one is failing. It is happening because smart-contract code now parses additional 7 bits from the message, which is not included in the message that we have sent in the third test. Let's add missing action in the message. After that we can run the tests and confirm that all of them are passing. Here is the commit with described changes. Great.

运行测试,并观察到第三个失败。 之所以会发生这种情况,是因为智能合约代码现在解析了消息中的其他7位,这不包括在我们在第三次测试中发送的消息中。 让我们在消息中添加缺少的action 。 之后,我们可以运行测试并确认所有测试都通过了。 这是带有描述的更改的提交。 大。

Now let's write the logic of sending a requested number of Grams to the owner's address. We will write two tests. The first one is when the balance of the lottery is not enough to make Gram transfer, the second is the opposite when the amount is enough. Here is the commit with updated tests.

现在,让我们编写将请求数量的克发送到所有者地址的逻辑。 我们将编写两个测试。 第一个是当彩票的余额不足以进行革兰氏转移时,第二个是当金额足够时相反。 这是带有更新测试的提交。

Let's add code. We will start by adding two methods for convenience. The first one is the get-method that displays the remaining balance of the smart-contract.

让我们添加代码。 为了方便起见,我们将添加两种方法。 第一个是获取方法,该方法显示智能合约的剩余余额。

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

The second method is regarding Grams transfer to an arbitrary address. I copied this method from another smart-contract.

第二种方法是将Grams转移到任意地址。 我从另一个智能合约复制了此方法。

() send_grams(int wc, int addr, int grams) impure {;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000cell 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
}

We should add these two methods in the smart-contract and write a business logic. Firstly we should parse the number of Grams from the message. Secondly we should check remaining balance, if not enough throw the exception. In case if everything is on track, then Grams should be sent to the saved address and update counter value.

我们应该在智能合约中添加这两种方法并编写业务逻辑。 首先,我们应该从消息中解析出克数。 其次,我们应该检查剩余余额,如果不够的话抛出异常。 如果一切都按计划进行,则应将Grams发送到保存的地址并更新计数器值。

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

Here is the commit with described changes. let's run the tests to confirm the successful outcome.

这是带有描述的更改的提交。 让我们运行测试以确认成功的结果。

By the way, for processing the accepted message (storage and computing power) each time the smart-contract pays a fee in Grams. In order to fully process the valid external message, after basic checks, we should call accept_message().

顺便说一下,每次智能合约以Grams支付费用时,为了处理接受的消息(存储和计算能力)。 为了完全处理有效的外部消息,在进行基本检查之后,我们应该调用accept_message()

处理内部消息到智能合约 (Processing internal messages to the smart-contract)

Now let's work on internal messages. In fact, we will receive Grams and send back twice the amount if the player wins or the 1/3 of the amount to the owner if the player loses.

现在让我们处理内部消息。 实际上,如果玩家获胜,我们将收到Grams并退回两倍的金额;如果玩家输了,我们将退回金额的1/3给所有者。

Let's write a simple test. To do this, we need the test address of the smart-contract, out of which we would be sending grams to the lottery smart-contract.

让我们编写一个简单的测试。 为此,我们需要智能合约的测试地址,将从中将克发送给彩票智能合约。

Any smart-contract consists of two parts, 32 bits integer number responsible for work chain and 256 bits unsigned integer is a unique account number in this work chain. For example, -1 and 12345, this address we will save in a file.

任何智能合约都由两部分组成,负责工作链的32位整数和该工作链中的唯一帐号256位无符号整数。 例如-1和12345,我们会将这个地址保存在文件中。

I have copied the function for saving address in a file from TonUtil.fif.

我已经从TonUtil.fif复制了将地址保存在文件中的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

Let's figure out how this function works, it will help us understand Fift. Run Fift in interactive mode.

让我们弄清楚这个函数是如何工作的,它将帮助我们理解Fift。 在交互模式下运行Fift。

~/TON/build/crypto/fift -i

Firstly we should put on the stack numbers -1, 12345 and the string "sender.addr".

首先,我们应该放置堆栈号-1、12345和字符串“ sender.addr”。

-1 12345 "sender.addr"

Next, we should run -rot, which moves elements in the stack to the right, allowing the unique number of the smart-contract appear above the stack.

接下来,我们应该运行-rot ,它将堆栈中的元素向右移动,从而使智能合约的唯一编号出现在堆栈上方。

"sender.addr" -1 12345

256 u>B converts 256 bits unsigned integer into bytes.

256 u>B将256位无符号整数转换为字节。

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap swaps top two elements of the stack.

swap交换堆栈的前两个元素。

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B converts 32-bits integer into bytes.

32 i>B将32位整数转换为字节。

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ concatenates two bytes sequences into one.

B+将两个字节序列连接为一个。

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

Again swap.

再次swap

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr"

Finally B>file receives two parameters, bytes and string that would be a file name. Function writes bytes into a file and names the file as sender.addr. In current folder file has been created. We should move it into test/addresses/.

最后, B>file接收两个参数,字节和字符串,它们将是文件名。 函数将字节写入文件,并将文件命名为sender.addr 。 在当前文件夹中已创建文件。 我们应该将其移到test/addresses/

Let's write a test which emulates sending Grams from the address that we have just created. Here is the commit.

让我们写一个测试,模拟从我们刚创建的地址发送Grams。 这是提交。

Now let's work on the business logic of the internal message of the lottery.

现在,让我们研究彩票内部消息的业务逻辑。

First of all we should check if the received message bounced or not, if bounced we can ignore this message. bounced means that the smart-contract will return Grams if an error happens during the execution. However, we will not return Grams if an error happens.

首先我们应该检查是否收到的消息bounced与否,如果bounced我们可以忽略此消息。 bounced意味着如果执行期间发生错误,智能合约将返回Grams。 但是,如果发生错误,我们将不会返回Grams。

Next, we must check the amount of sent Grams if it is less than half of the Grams, we can simply accept the message without doing anything else.

接下来,我们必须检查已发送的Grams的数量(如果少于Grams的一半),我们可以简单地接受该消息而无需执行其他任何操作。

Then we parse the address of the smart-contract where the message came from.

然后,我们解析消息来自的智能合约的地址。

Read data from storage and then we should remove old orders from the history, if there are more than twenty items. For convenience, I wrote three additional functions pack_order(), unpack_order(), remove_old_orders().

从存储中读取数据,然后,如果有二十多个项目,则应从历史记录中删除旧订单。 为了方便起见,我编写了三个附加函数pack_order()unpack_order()remove_old_orders()

Next, we should check the remaining smart-contract balance and if there are not enough Grams to pay, we should not consider it as a bet but as a top-up and save it in orders.

接下来,我们应该检查剩余的智能合约余额,如果没有足够的Grams支付,我们不应该将其视为下注,而应将其视为充值并保存在orders

Finally, the heart of the smart-contract.

最后,是智能合约的心脏。

If the player lost, we should save the order in the history of bets, moreover if the order amount is higher than 3 Grams, we must send 1/3 to the owner of the lottery.

如果玩家输了,我们应该将订单保存在投注历史中,此外,如果订单金额高于3克,我们必须将1/3发送给彩票所有者。

If the player won, then we should send a double amount back to the player and save the order in history.

如果玩家获胜,那么我们应该将双倍的金额返还给玩家,并将订单保存在历史记录中。

() 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:Boolif (flags & 1) { ;; ignore bouncedreturn ();}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-fillbuilder 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));
}

That is all. Corresponding commit.

就这些。 相应的提交 。

编写获取方法 (Writing get-methods)

Let's create the get-methods that will allow us to request information about the smart-contract's state from the external world (in fact, these methods will parse and return storage data).

让我们创建一个get方法,使我们可以从外部世界请求有关智能合约状态的信息(实际上,这些方法将解析并返回存储数据)。

Here is the commit with added get-methods. Later on we will see how these methods are being used.

这是添加了get方法的提交。 稍后我们将看到如何使用这些方法。

I forgot to add code, that will process the very first request when we will publish the smart-contract. Corresponding commit. I also fixed the bug regarding sending 1/3 Grams to the owner.

我忘记添加代码,它将在我们发布智能合约时处理第一个请求。 相应的提交 。 我还修复了有关向所有者发送1/3克的错误。

将智能合约发布到TON (Publishing smart-contract to the TON)

Now we need to publish created smart-contract. Let's create folder requests in the project root folder.

现在我们需要发布创建的智能合约。 让我们在项目根文件夹中创建文件夹requests

As a base code for publishing the smart-contract, I took a simple wallet publishing code in the official repository and altered it a bit.

作为发布智能合约的基本代码,我在官方存储库中使用了一个简单的钱包发布代码 ,并对其进行了一些改动。

Here is what we should pay attention to: we should form the smart-contract storage and entry message. After that address of the smart-contract is generated, so it is known even before deploying in TON. Next, we need to transfer several Grams to the generated address. And only after that we can deploy generated .boc file with the smart-contract code. Because as we already mentioned network charges a fee for the storage and processing time. Here is deploy code.

这里是我们应该注意的:我们应该形成智能合约的存储和输入消息。 在生成智能合约的地址之后,因此即使在TON部署之前也知道该地址。 接下来,我们需要将几克转移到生成的地址。 而且只有在此之后,我们才能使用智能合约代码部署生成的.boc文件。 因为正如我们已经提到的,网络会为存储和处理时间收取一定的费用。 这是部署代码 。

Then we can run new-lottery.fif and generate lottery-query.boc file and lottery.addr.

然后,我们可以运行new-lottery.fif并生成lottery-query.boc文件和lottery.addr

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

Let's not forget to save lottery.pk and lottery.addr.

我们不要忘记保存lottery.pklottery.addr

Also, among other things, we will see the smart-contract address.

此外,除其他外,我们还将看到智能合约地址。

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

For the sake of interest let's make a request to TON.

为了感兴趣,让我们向TON提出请求。

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

And observe that account with this address is empty.

并注意此地址的帐户为空。

account state is empty

Now let's transfer 2 Grams to our smart-contract address 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd and after a few seconds make the same request. I have used official TON wallet and test Grams could be requested in the group chat, a link will be shared at the end of this article.

现在,让我们将2克转移到我们的智能合约地址0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd并在几秒钟后发出相同的请求。 我使用了TON官方钱包 ,可以在群聊中测试Grams,一个链接将在本文结尾处共享。

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Observe that the smart-contract has changed its status from empty to uninitialized (state:account_uninit) with a balance of 2 000 000 000 nanograms.

观察到智能合约的状态已从空更改为未初始化( state:account_uninit ),余额为2000000000000纳克。

account state is (accountaddr:(addr_stdanycast:nothing workchain_id:0 address:x044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A)storage_stat:(storage_infoused:(storage_usedcells:(var_uint len:1 value:1)bits:(var_uint len:1 value:103)public_cells:(var_uint len:0 value:0)) last_paid:1583257959due_payment:nothing)storage:(account_storage last_trans_lt:3825478000002balance:(currenciesgrams:(nanogramsamount:(var_uint len:4 value:2000000000))other:(extra_currenciesdict:hme_empty))state:account_uninit))
x{C00044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A20259C2F2F4CB3800000DEAC10776091DCD650004_}
last transaction lt = 3825478000001 hash = B043616AE016682699477FFF01E6E903878CDFD6846042BA1BFC64775E7AC6C4
account balance is 2000000000ng

Now we can deploy the smart-contract. Let's run lite-client and execute the following.

现在我们可以部署智能合约了。 让我们运行lite-client并执行以下命令。

> 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

Confirm that the smart-contract has been published.

确认已发布智能合约。

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

We can observe other things in the log.

我们可以在日志中观察其他情况。

storage:(account_storage last_trans_lt:3825499000002balance:(currenciesgrams:(nanogramsamount:(var_uint len:4 value:1987150999))other:(extra_currenciesdict:hme_empty))state:(account_active

Finally, we can see account_active. The corresponding commit is here.

最后,我们可以看到account_active 。 相应的提交在这里 。

发送外部消息 (Sending external messages)

Now let's create requests to interact with the smart-contract. We support two: sending grams to the owner and changing owner's smart-contract address. We need to make the same request as in the test #6.

现在,让我们创建与智能合约进行交互的请求。 我们支持两种方法:将克发送给所有者,以及更改所有者的智能合约地址。 我们需要做出与测试6中相同的请求。

This is the message that we will be sending to the smart-contract, where msg_seqno 165, action 2 and 9.5 Gram to be sent.

这是我们将发送到智能合约的消息,将在其中发送msg_seqno 165, action 2和9.5 Gram。

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

We should remember to sign a message with the private key lottery.pk, which has been generated earlier. Here is the corresponding commit.

我们应该记住用早先生成的私钥lottery.pk签名消息。 这是相应的提交 。

使用get方法从智能合约中获取信息 (Getting the information from a smart-contract using the get-methods)

Now let's see how to run the get-methods of a smart contract.

现在,让我们看看如何运行智能合约的获取方法。

Run lite-client and invoke runmethod with the smart-contract address and preferred get-method.

运行lite-client并使用智能合约地址和首选get-method调用runmethod

Getting sequence number.

获取序列号。

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

And orders' history.

和订单的历史。

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

We will use lite-client and the get-methods to show the data from the smart-contract.

我们将使用lite-client和get-method显示来自智能合约的数据。

在网站上显示智能合约的数据 (Showing the data of the smart-contract on the website)

I have written a website using Python to show information from the smart-contract in a convenient layout. Here I will not dwell on it in details and make one commit with changes.

我已经使用Python编写了一个网站,以方便的布局显示来自智能合约的信息。 在这里,我将不作详细介绍,而只做更改。

Requests to TON are made using lite-client via Python code. For convenience, everything packed in Docker and deployed at Google Cloud. Website link.

使用lite-client通过Python代码向TON发送请求。 为了方便起见,所有内容都打包在Docker中并部署在Google Cloud中。 网站链接 。

下注 (Making a bet)

Now let's transfer 64 Grams to our lottery to top it up using the official wallet. And make some bets for clarity. We can see that information on the website is updating and we can observe orders' history, current winning rate, and other useful information directly from the smart-contract.

现在,让我们将64克转移到我们的彩票中,以使用官方钱包充值。 并做出一些赌注以求清楚。 我们可以看到网站上的信息正在更新,我们可以直接从智能合约中观察订单的历史记录,当前中奖率以及其他有用信息。

后记 (Afterword)

The article is way longer than I expected it to be. Maybe it could be shorter, or maybe it is just for a person who does not know anything about TON and wants to write and publish not the easiest smart-contract with the ability to interact with it.

这篇文章比我预期的更长。 也许它可能更短,或者它只是针对一个对TON一无所知并且想要编写和发布而不具有交互能力的最简单的智能合约的人。

Maybe some things could be explained easier. Some moments could be implemented more effectively, for example, we could parse orders' history from the blockchain itself and do not need to store it inside the smart-contract. But then we couldn't be able to show how a FunC dictionary works.

也许有些事情可以更容易地解释。 某些时刻可以更有效地实施,例如,我们可以从区块链本身解析订单的历史记录,而无需将其存储在智能合约中。 但是后来我们无法显示FunC词典的工作原理。

Since I could make a mistake somewhere or understand something incorrectly, you also need to rely on official documentation and official TON code repository.

由于我可能会在某个地方犯错误或理解不正确,因此您还需要依赖官方文档和官方TON代码存储库。

Need to mention, that TON still in an active development state and incompatible changes could be made that would break some of the steps in this article (which happened and has been fixed).

值得一提的是,TON仍处于活跃的开发状态,并且可能进行不兼容的更改,从而破坏了本文中的某些步骤(已发生且已修复)。

I will not talk about the TON's future here. Maybe it will become something really big and we might need to spend some time to learn and start creating TON-based products or maybe not.

我不会在这里谈论TON的未来。 也许它将变得非常重要,我们可能需要花一些时间学习并开始创建基于TON的产品。

There is also Libra by Facebook which has potential audience of users that is even greater than that is of TON's. I don't know anything about Libra, judging by the official community it is more active than TON-community. TON developers and the community are more like an underground movement, which is also cool.

Facebook也有天秤座,它的潜在潜在用户甚至比TON的潜在用户还要多。 我对天秤座一无所知,根据官方社区的判断,它比TON社区更加活跃。 TON开发人员和社区更像是地下运动,这也很酷。

链接 (Links)

  1. Official TON website: https://test.ton.org

    TON官方网站: https : //test.ton.org

  2. Official TON repository: https://github.com/ton-blockchain/ton

    官方TON资料库: https : //github.com/ton-blockchain/ton

  3. Official TON wallet for different platforms: https://wallet.ton.org

    适用于不同平台的TON官方钱包: https : //wallet.ton.org

  4. Lottery smart-contract discussed in this article: https://github.com/raiym/astonished

    本文讨论了彩票智能合约: https : //github.com/raiym/astonished

  5. Website of the lottery: https://ton-lottery.appspot.com

    彩票网站: https : //ton-lottery.appspot.com

  6. Visual Studio Code syntax highlighter for FunC: https://github.com/raiym/func-visual-studio-plugin

    FunC的Visual Studio代码语法突出显示器: https : //github.com/raiym/func-visual-studio-plugin

  7. Link to Telegram chat devoted to TON: https://t.me/tondev_en

    链接到专用于TON的电报聊天: https : //t.me/tondev_zh

  8. The first stage of the contest: https://contest.com/blockchain

    The first stage of the contest: https://contest.com/blockchain

  9. The second stage of the contest: https://contest.com/blockchain-2

    The second stage of the contest: https://contest.com/blockchain-2

July 7, 2020: Sadly, reflecting on the latest Telegram statement about test TON network servers discontinuing I decided to stop supporting the website with smart contract data. Smart-contract along with server code still accessible on the GitHub.

July 7, 2020: Sadly, reflecting on the latest Telegram statement about test TON network servers discontinuing I decided to stop supporting the website with smart contract data. Smart-contract along with server code still accessible on the GitHub.

翻译自: https://habr.com/en/post/494528/

电报注册网络代理

电报注册网络代理_如何在电报开放网络(TON)中开发和发布智能合约相关推荐

  1. android区块链 钱包_区块链研究实验室|从Android调用区块链智能合约

    本教程将展示如何直接从Android开发环境-Android Studio中调用智能合约. 环境要求 我们将使用Tezos区块链.为了使我们的Android java代码与现有的.部署的智能合约交互, ...

  2. python智能合约编程_如何用Python Flask开发以太坊智能合约

    将数据存储在数据库中是任何软件应用程序不可或缺的一部分.无论如何控制该数据库都有一个该数据的主控.区块链技术将数据存储到区块链网络内的区块中.因此,只要某个节点与网络同步,它们就会获得区块中数据的副本 ...

  3. 【以太坊】在测试网络上发布智能合约

    一.准备工作 1.本地测试网络搭建完成,对以太坊的了解已经达到基础水平.(可以参考我之前的关于以太坊的文章). 2.有翻墙的能力(发布合约的时候,有不少网站都是需要翻墙操作的) 3.本地浏览器安装Me ...

  4. debian 网络代理_在Ubuntu 18.04/Debian 10上设置系统范围的Proxy(代理)

    本文介绍在Ubuntu 18.04/Debian 10上设置系统范围的Proxy(代理)的方法,代理服务器在其基本级别上是一个计算机系统,其服务位于发出Web请求的计算机(客户端)和包含所请求内容的服 ...

  5. 网络代理【1】什么是网络代理

    一.什么是网络代理 你吃外卖,小哥帮你带. 二.网络转发 与 网络代理 网络转发: 路由器对报文进行转发操作,转发过程中修改数据包.场景: 局域网访问公网 网络代理: 饭友请求X团,外卖小哥帮你带饭给 ...

  6. 基于HTTP网络代理的校园网(局域网)网络共享

    问题描述: 嗯,校园网欠费了,但是宿舍这里无法登陆,显示欠费,实验室的电脑使用的网关是可以登陆正常上网的!不显示欠费!内网远程实验室桌面,没问题,是不是有点麻烦?既然能远程,那说明可以通过代理实现网络 ...

  7. 电子网络发票应用系统网络不通_深度强化学习在典型网络系统中的应用综述

    作  者:郑莹,段庆洋,林利祥,游新宇,徐跃东,王新 摘  要:近几年来,以深度强化学习(Deep Reinforcement Learning,DRL)为代表的人工智能技术被引入计算机网络系统设计中 ...

  8. 找不到网络名_书荒粮草,网络小说推荐大合集:文笔一流,拒绝套路

    网络小说走过这么多年,已经成为大家日常生活离不开的陪伴,也涌现出了众多经典.但是网文看多了,难免会感到书荒,特别是对满满的套路文和小白文丧失了兴趣. 关注小编,每天给书荒的朋友提供粮草.今天给大家介绍 ...

  9. 连接游戏服务器网络延迟高,网络延迟_连wifi玩游戏网络延迟高-太平洋IT百科

    网络延迟是游戏玩家最不能忍的,为了解决网络延迟的问题,玩家们装了光纤,换了路由,然而"为你我用了半年的积蓄,玩个农药还跳ping."开局两分钟,延迟400ms,网络延迟在王者荣耀中 ...

最新文章

  1. Linux文件压缩与解压缩
  2. 突然发现缓存这么好用
  3. C++ exception 类继承结构图
  4. js 延迟几秒执行ifarme_Node.js调试之llnode篇
  5. Mysql学习总结(19)——Mysql无法创建外键的原因
  6. Leetcode 76.最小覆盖子串
  7. 基于Spring的医院药品管理系统的设计与实现
  8. Java模拟ssh交互_java模拟ssh执行shell命令
  9. 【数据结构与算法】——一文带你理清线性表的基础(java代码演示)
  10. 为什么计算机关机慢,XP系统为什么电脑关机慢?
  11. wps2016向程序发送命令_word文档打开,提示“向程序发送命令时出现问题”的原因...
  12. 可视化GDP动态图表EXCEL也能搞定,如何用Excel做动态图表
  13. 大乐斗2服务器维护,《QQ宠物》和《乐斗II》停止运营
  14. 百度地图定位功能的错误has leaked ServiceConnection 解决
  15. java文件压缩与解压_Java实现文件压缩与解压
  16. python getsize函数,Python getsize函数
  17. 如何查询主机IP地址
  18. 每日一道SQL题(第N高的薪水)
  19. finalize()的真正用法及作用
  20. 密码学之对称加密体系(1):AES、SM4的 S 盒有限域与复合域算法实现原理

热门文章

  1. Arduino循迹小车教程一----材料篇
  2. type在python是什么意思_python中type()是什么意思
  3. 房天下二手交易平台房源数据采集
  4. 六轴机器人轨迹规划之五段位置s曲线插补
  5. opencv之图像平移
  6. Linux mint 双硬盘,记一次装机——笔记本UEFI双硬盘(SSD+机械)双系统(Win10+Linux Mint)...
  7. linux之awk基础
  8. win11家庭版设置远程桌面
  9. 解决Could not resolve XXX
  10. SQL中可以用来算时间差的函数