一年多前,人们得知 Telegram Messenger 计划发布自己的去中心化网络 电报开放网络。 随后出现了一份大量的技术文件,据称是尼古拉·杜罗夫撰写的,描述了未来网络的结构。 对于那些错过的人,我建议您阅读我对这份文件的重述(
从那时起,就没有关于 TON 开发状况的重大新闻,直到几天前(在其中一篇文章中)
•
•
•
•
•
•
•
•
我再说一遍,该页面和来自 Telegram 的所有这些文件都没有得到官方确认,但这些材料的数量使它们非常可信。 启动已发布的客户端 风险自担.
构建测试客户端
首先,让我们尝试构建并运行一个测试客户端 - 幸运的是,
-
下载并解压
源档案 。 下载最新版本非常重要,因为现阶段无法保证向后兼容性。 -
确保系统上安装了最新版本的 make、cmake(版本 3.0.2 或更高版本)、OpenSSL(包括 C 头文件)、g++ 或 clang。 我不需要安装任何东西,一切都立即组合在一起。
-
我们假设源代码被解压到一个文件夹中
~/lite-client
。 除此之外,为组装的项目创建一个空文件夹(例如,~/liteclient-build
),并从中得出(cd ~/liteclient-build
)调用命令:cmake ~/lite-client cmake --build . --target test-lite-client
为了构建智能合约的 Fiveft 语言解释器(更多相关信息见下文),我们还调用cmake --build . --target fift
-
下载当前版本
配置文件 连接到测试网络并将其与组装好的客户端放在文件夹中。 -
完成,您可以启动客户端:
./test-lite-client -C ton-lite-client-test1.config.json
如果一切都正确完成,您应该看到如下内容:
正如我们所看到的,可用的命令很少:
• help
— 显示命令列表;
• quit
- 出去;
• time
— 显示服务器上的当前时间;
• status
— 显示连接和本地数据库状态;
• last
— 更新区块链的状态(下载最后一个区块)。 在发出任何请求之前运行此命令非常重要,以确保您看到网络的当前状态。
• sendfile
<filename>
— 将本地文件上传到 TON 网络。 这就是与网络交互的发生方式 - 例如,包括创建新的智能合约和请求在账户之间转移资金;
• getaccount
<address>
— 显示当前(执行命令时) last
) 指定地址的账户状态;
• privkey
<filename>
— 从本地文件加载私钥。
如果在启动客户端时,您使用选项将文件夹传输到客户端 -D
,然后他会将主链的最后一个区块添加到其中:
./test-lite-client -C ton-lite-client-test1.config.json -D ~/ton-db-dir
现在我们可以继续做更有趣的事情 - 学习 Fift 语言,尝试编译智能合约(例如,创建测试钱包),将其上传到网络并尝试在帐户之间转移资金。
语言五
来自文件
该文档相当庞大,有87页,我不会在本文的框架内详细复述其内容(至少因为我自己还没有读完:)。 我将重点关注要点并给出一些这种语言的代码示例。
从根本上来说,Fift 的语法非常简单:它的代码包括 话,通常用空格或换行符分隔(特殊情况:某些单词后面不需要分隔符)。 任何 слово 是一个区分大小写的字符序列,对应于某个特定的 决心 (粗略地说,解释器遇到这个词时应该做什么)。 如果没有单词的定义,解释器会尝试将其解析为数字并将其放入堆栈中。 顺便说一句,这里的数字突然变成了 257 位整数,而且根本没有分数,更准确地说,它们立即变成一对整数,形成有理分数的分子和分母。
单词往往与堆栈顶部的值交互。 一个单独的词类型 - 字首 — 不使用堆栈,而是使用源文件中的后续字符。 例如,这就是字符串文字的实现方式 - 引号字符 ("
) 是一个前缀词,用于查找下一个(结束)引号,并将它们之间的字符串压入堆栈。 单行代码的行为方式相同(//
) 和多行 (/*
) 评论。
这几乎是该语言的整个内部结构的终点。 其他一切(包括控制结构)都被定义为单词(或者是内部的,例如算术运算和新单词的定义;或者是在“标准库”中定义的) Fift.fif
,位于文件夹中 crypto/fift
在来源中)。
Five 中的一个简单示例程序:
{ dup =: x dup * =: y } : setxy
3 setxy x . y . x y + .
7 setxy x . y . x y + .
第一行定义一个新词 setxy
(注意前缀 {
,它在结束块之前创建一个块 }
和前缀 :
,它实际上定义了这个词)。 setxy
从堆栈顶部获取一个数字,将其定义(或重新定义)为全局的 持续的 x
,并将该数字的平方作为常数 y
(鉴于常量的值可以重新定义,我宁愿将它们称为变量,但我遵循语言中的命名约定)。
接下来的两行将一个数字放入堆栈并调用 setxy
,然后显示常量的值 x
, y
(该词用于输出 .
),两个常量都被放入堆栈中,求和,并打印结果。 结果我们会看到:
3 9 12 ok
7 49 56 ok
(解释器在交互输入模式下处理完当前行后会打印“ok”行)
好吧,完整的代码示例:
"Asm.fif" include
-1 constant wc // create a wallet in workchain -1 (masterchain)
// Create new simple wallet
<{ SETCP0 DUP IFNOTRET INC 32 THROWIF // return if recv_internal, fail unless recv_external
512 INT LDSLICEX DUP 32 PLDU // sign cs cnt
c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS // sign cs cnt cnt' pubk
s1 s2 XCPU // sign cs cnt pubk cnt' cnt
EQUAL 33 THROWIFNOT // ( seqno mismatch? )
s2 PUSH HASHSU // sign cs cnt pubk hash
s0 s4 s4 XC2PU // pubk cs cnt hash sign pubk
CHKSIGNU // pubk cs cnt ?
34 THROWIFNOT // signature mismatch
ACCEPT
SWAP 32 LDU NIP
DUP SREFS IF:<{
8 LDU LDREF // pubk cnt mode msg cs
s0 s2 XCHG SENDRAWMSG // pubk cnt cs ; ( message sent )
}>
ENDS
INC NEWC 32 STU 256 STU ENDC c4 POPCTR
}>c
// code
<b 0 32 u,
newkeypair swap dup constant wallet_pk
"new-wallet.pk" B>file
B,
b> // data
// no libraries
<b b{00110} s, rot ref, swap ref, b> // create StateInit
dup ."StateInit: " <s csr. cr
dup hash dup constant wallet_addr
."new wallet address = " wc . .": " dup x. cr
wc over 7 smca>$ type cr
256 u>B "new-wallet.addr" B>file
<b 0 32 u, b>
dup ."signing message: " <s csr. cr
dup hash wallet_pk ed25519_sign_uint rot
<b b{1000100} s, wc 8 i, wallet_addr 256 u, b{000010} s, swap <s s, b{0} s, swap B, swap <s s, b>
dup ."External message for initialization is " <s csr. cr
2 boc+>B dup Bx. cr
"new-wallet-query.boc" tuck B>file
."(Saved to file " type .")" cr
这个看起来吓人的文件用于创建智能合约 - 它将被放置在一个文件中 new-wallet-query.boc
执行后。 请注意,TON 虚拟机这里使用的是另一种汇编语言(我不会详细介绍它),其指令将放在区块链上。
因此,TVM 的汇编程序是用 Fift 编写的 - 该汇编程序的源代码位于文件中 crypto/fift/Asm.fif
和 连接在上述代码的开头。
我能说什么,显然 Nikolai Durov 只是喜欢创建新的编程语言:)
创建智能合约并与 TON 交互
因此,假设我们已经如上所述组装了 TON 客户端和 Fift 解释器,并熟悉了该语言。 现在如何创建智能合约? 文件中对此进行了描述
TON 账户
正如我在
在一个工作链中,存储了许多具有自己的 account_id 标识符的帐户。 对于主链和零工作链,它们的长度是256位。 因此,帐户标识符的写法例如如下:
-1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
这是“原始”格式:首先是工作链 ID,然后是冒号,以及十六进制表示法的账户 ID。
此外,还有一种缩短的格式——工作链号和账户地址以二进制形式编码,并添加校验和,所有这些都以 Base64 编码:
Ef+BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSkb
知道了这种记录格式,我们可以使用以下命令通过测试客户端请求帐户的当前状态
getaccount -1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
我们会得到这样的结果:
[ 3][t 2][1558746708.815218925][test-lite-client.cpp:631][!testnode] requesting account state for -1:8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D
[ 3][t 2][1558746708.858564138][test-lite-client.cpp:652][!testnode] got account state for -1:8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D with respect to blocks (-1,8000000000000000,72355):F566005749C1B97F18EDE013EBA7A054B9014961BC1AD91F475B9082919A2296:1BD5DE54333164025EE39D389ECE2E93DA2871DA616D488253953E52B50DC03F and (-1,8000000000000000,72355):F566005749C1B97F18EDE013EBA7A054B9014961BC1AD91F475B9082919A2296:1BD5DE54333164025EE39D389ECE2E93DA2871DA616D488253953E52B50DC03F
account state is (account
addr:(addr_std
anycast:nothing workchain_id:-1 address:x8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D)
storage_stat:(storage_info
used:(storage_used
cells:(var_uint len:1 value:3)
bits:(var_uint len:2 value:539)
public_cells:(var_uint len:0 value:0)) last_paid:0
due_payment:nothing)
storage:(account_storage last_trans_lt:74208000003
balance:(currencies
grams:(nanograms
amount:(var_uint len:7 value:999928362430000))
other:(extra_currencies
dict:hme_empty))
state:(account_active
(
split_depth:nothing
special:nothing
code:(just
value:(raw@^Cell
x{}
x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
))
data:(just
value:(raw@^Cell
x{}
x{0000000D}
))
library:hme_empty))))
x{CFF8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D2068086C000000000000000451C90E00DC0E35B7DB5FB8C134_}
x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
x{0000000D}
我们看到存储在指定工作链的 DHT 中的结构。 例如,在该领域 storage.balance
是经常账户余额,单位为 storage.state.code
- 智能合约代码,以及 storage.state.data
- 其当前数据。 请注意,TON 数据存储 - Cell、cells - 是树状的,每个单元格都可以有自己的数据和子单元格。 这在最后几行显示为缩进。
构建智能合约
现在让我们自己创建一个这样的结构(它被称为 BOC - 细胞袋)使用第五语言。 幸运的是,您不必自己编写智能合约 - 在文件夹中 crypto/block
源存档中有一个文件 new-wallet.fif
,这将帮助我们创建一个新的钱包。 让我们将其复制到包含已组装客户端的文件夹中(~/liteclient-build
,如果您按照上面的说明进行操作)。 我在上面引用了它的内容作为 Fift 上的代码示例。
按如下方式执行该文件:
./crypto/fift -I"<source-directory>/crypto/fift" new-wallet.fif
这是 <source-directory>
必须替换为解压源的路径(遗憾的是,此处不能使用“~”符号,需要完整路径)。 而不是使用钥匙 -I
你可以定义一个环境变量 FIFTPATH
并将这条路径放入其中。
由于我们使用文件名启动 Fift new-wallet.fif
,它将执行并退出。 如果省略文件名,则可以与解释器交互播放。
执行后,控制台中应该显示如下内容:
StateInit: x{34_}
x{FF0020DDA4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54}
x{0000000055375F730EDC2292E8CB15C42E8036EE9C25AA958EE002D2DE48A205E3A3426B}
new wallet address = -1 : 4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2
0f9PzVILj8yglrVn1zS-NSjtxr7QBfaTCp7JrBqnFPIR8nhZ
signing message: x{00000000}
External message for initialization is x{89FEE120E20C7E953E31546F64C23CD654002C1AA919ADD24DB12DDF85C6F3B58AE41198A28AD8DAF3B9588E7A629252BA3DB88F030D00BC1016110B2073359EAC3C13823C53245B65D056F2C070B940CDA09789585935C7ABA4D2AD4BED139281CFA1200000001_}
x{FF0020DDA4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54}
x{0000000055375F730EDC2292E8CB15C42E8036EE9C25AA958EE002D2DE48A205E3A3426B}
B5EE9C724104030100000000D60002CF89FEE120E20C7E953E31546F64C23CD654002C1AA919ADD24DB12DDF85C6F3B58AE41198A28AD8DAF3B9588E7A629252BA3DB88F030D00BC1016110B2073359EAC3C13823C53245B65D056F2C070B940CDA09789585935C7ABA4D2AD4BED139281CFA1200000001001020084FF0020DDA4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED5400480000000055375F730EDC2292E8CB15C42E8036EE9C25AA958EE002D2DE48A205E3A3426B6290698B
(Saved to file new-wallet-query.boc)
这意味着带有ID的钱包 -1:4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2
(或者,什么是相同的, 0f9PzVILj8yglrVn1zS-NSjtxr7QBfaTCp7JrBqnFPIR8nhZ
)创建成功。 相应的代码将在文件中 new-wallet-query.boc
,他的地址是在 new-wallet.addr
,私钥位于 new-wallet.pk
(小心 - 再次运行脚本将覆盖这些文件)。
当然,TON 网络还不知道这个钱包;它仅以这些文件的形式存储。 现在需要将其上传到网络。 然而问题是,创建智能合约需要支付佣金,而你的账户余额仍然为零。
在工作模式下,这个问题将通过在交易所购买克(或从另一个钱包转移)来解决。 嗯,在当前的测试模式下,已经创建了一个特殊的智能合约,您可以像这样索取最多 20 克的智能合约。
生成对其他人智能合约的请求
我们向智能合约发出请求,该合约像这样左右分配克。 在同一个文件夹中 crypto/block
找到文件 testgiver.fif
:
// "testgiver.addr" file>B 256 B>u@
0x8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
dup constant wallet_addr ."Test giver address = " x. cr
0x4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2
constant dest_addr
-1 constant wc
0x00000011 constant seqno
1000000000 constant Gram
{ Gram swap */ } : Gram*/
6.666 Gram*/ constant amount
// b x --> b' ( serializes a Gram amount )
{ -1 { 1+ 2dup 8 * ufits } until
rot over 4 u, -rot 8 * u, } : Gram,
// create a message (NB: 01b00.., b = bounce)
<b b{010000100} s, wc 8 i, dest_addr 256 u, amount Gram, 0 9 64 32 + + 1+ 1+ u, "GIFT" $, b>
<b seqno 32 u, 1 8 u, swap ref, b>
dup ."enveloping message: " <s csr. cr
<b b{1000100} s, wc 8 i, wallet_addr 256 u, 0 Gram, b{00} s,
swap <s s, b>
dup ."resulting external message: " <s csr. cr
2 boc+>B dup Bx. cr
"wallet-query.boc" B>file
我们还将其保存在已组装客户端的文件夹中,但我们将更正第五行 - 在“行”之前constant dest_addr
”。 让我们将其替换为您之前创建的钱包地址(完整,非缩写)。 开头不需要写“-1:”,而是在开头写“0x”。
您也可以更改线路 6.666 Gram*/ constant amount
— 这是您要求的克数(不超过 20)。 即使指定整数,也要保留小数点。
最后,您需要更正该行 0x00000011 constant seqno
。 这里的第一个数字是当前的序列号,存储在发行克的账户中。 我可以从哪里得到它? 如上所述,启动客户端并运行:
last
getaccount -1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
最后,智能合约数据将包含
...
x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
x{0000000D}
数字 0000000D(你的会更大)是必须替换为的序列号 testgiver.fif
.
就这样,保存文件并运行(./crypto/fift testgiver.fif
)。 输出将是一个文件 wallet-query.boc
。 这就是形成的样子 сообщение 到其他人的智能合约 - 请求“将这么多克转移到某个帐户”。
使用客户端,我们将其上传到网络:
> sendfile wallet-query.boc
[ 1][t 1][1558747399.456575155][test-lite-client.cpp:577][!testnode] sending query from file wallet-query.boc
[ 3][t 2][1558747399.500236034][test-lite-client.cpp:587][!query] external message status is 1
如果你现在打电话 last
,然后再次请求我们请求克的帐户的状态,然后我们应该看到它的序列号增加了 XNUMX - 这意味着它向我们的帐户发送了钱。
最后一步仍然是 - 下载我们钱包的代码(其余额已被补充,但如果没有智能合约代码,我们将无法管理它)。 我们开展 sendfile new-wallet-query.boc
- 就是这样,您在 TON 网络中拥有了自己的钱包(即使目前只是测试钱包)。
创建传出交易
要从创建的帐户余额中转账,有一个文件 crypto/block/wallet.fif
,也需要和组装好的客户端放在文件夹中。
与前面的步骤类似,您需要调整您要转账的金额、接收方地址(dest_addr)和钱包的 seqno(初始化钱包后等于 1,每次发出交易后增加 1 - 您可以通过请求您的帐户状态来查看)。 对于测试,您可以使用我的钱包 - 0x4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2
.
启动时(./crypto/fift wallet.fif
)该脚本将从文件中获取您的钱包地址(从您转账的位置)及其私钥 new-wallet.addr
и new-wallet.pk
,收到的消息将被写入 new-wallet-query.boc
.
和以前一样,要直接执行交易,请调用 sendfile new-wallet-query.boc
在客户端中。 之后,不要忘记更新区块链的状态(last
)并检查我们钱包的余额和序列号是否已更改(getaccount <account_id>
).
就这样,现在我们可以在 TON 中创建智能合约并向它们发送请求。 正如您所看到的,当前的功能已经足够,例如,制作一个带有图形界面的更友好的钱包(但是,预计它将已经作为信使的一部分提供)。
只有注册用户才能参与调查。
您有兴趣继续分析 TON、TVM、Fift 的文章吗?
-
是的,我正在等待 TON 概述系列文章的完成
-
是的,阅读有关第五语言的更多内容很有趣
-
是的,我想了解有关 TON 虚拟机及其汇编器的更多信息
-
不,这些都不有趣
39 位用户投票。 12 名用户弃权。
您对 Telegram 推出 TON 的计划有何看法?
-
我对这个项目寄予厚望
-
我只是饶有兴趣地关注着它的发展。
-
我对此表示怀疑并怀疑它的成功。
-
我倾向于认为这一举措是失败的,对于广大群众来说是不必要的
47 位用户投票。 12 名用户弃权。
来源: habr.com